From 7733d59ed16d64d3f95619efbca932eb0eda2fa8 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Thu, 9 Apr 2026 23:44:01 +0800 Subject: [PATCH] Fix OpenGL shader temp file collisions --- .../ShaderCompiler/SpirvShaderCompiler.cpp | 48 +++++++++-- .../RHI/OpenGL/unit/test_backend_specific.cpp | 85 +++++++++++++++++++ 2 files changed, 125 insertions(+), 8 deletions(-) diff --git a/engine/src/RHI/ShaderCompiler/SpirvShaderCompiler.cpp b/engine/src/RHI/ShaderCompiler/SpirvShaderCompiler.cpp index 1ca123d7..53646b27 100644 --- a/engine/src/RHI/ShaderCompiler/SpirvShaderCompiler.cpp +++ b/engine/src/RHI/ShaderCompiler/SpirvShaderCompiler.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -72,6 +73,7 @@ bool TryResolveShaderTypeFromTarget(const char* target, ShaderType& type) { constexpr uint32_t kSpirvMagic = 0x07230203u; constexpr uint16_t kSpirvOpEntryPoint = 15; +std::atomic g_temporaryPathCounter = 0; ShaderType ToShaderType(uint32_t executionModel) { switch (executionModel) { @@ -483,19 +485,49 @@ const char* SpirvTargetName(SpirvTargetEnvironment targetEnvironment) { bool CreateTemporaryPath(const wchar_t* extension, std::wstring& outPath) { wchar_t tempDirectory[MAX_PATH] = {}; - if (GetTempPathW(MAX_PATH, tempDirectory) == 0) { + const DWORD tempDirectoryLength = GetTempPathW(MAX_PATH, tempDirectory); + if (tempDirectoryLength == 0 || tempDirectoryLength >= MAX_PATH) { return false; } - wchar_t tempFile[MAX_PATH] = {}; - if (GetTempFileNameW(tempDirectory, L"XCV", 0, tempFile) == 0) { - return false; + const std::wstring extensionSuffix = extension != nullptr ? extension : L""; + const DWORD processId = GetCurrentProcessId(); + + for (uint32_t attempt = 0; attempt < 256u; ++attempt) { + const uint64_t counter = + g_temporaryPathCounter.fetch_add(1u, std::memory_order_relaxed) + 1u; + + wchar_t fileName[128] = {}; + swprintf_s( + fileName, + L"XCE_%08X_%016llX%ls", + processId, + static_cast(counter), + extensionSuffix.c_str()); + + const std::filesystem::path candidatePath = + std::filesystem::path(tempDirectory) / fileName; + const HANDLE handle = CreateFileW( + candidatePath.c_str(), + GENERIC_READ | GENERIC_WRITE, + 0, + nullptr, + CREATE_NEW, + FILE_ATTRIBUTE_TEMPORARY, + nullptr); + if (handle != INVALID_HANDLE_VALUE) { + CloseHandle(handle); + outPath = candidatePath.wstring(); + return true; + } + + const DWORD error = GetLastError(); + if (error != ERROR_ALREADY_EXISTS && error != ERROR_FILE_EXISTS) { + return false; + } } - DeleteFileW(tempFile); - outPath = tempFile; - outPath += extension; - return true; + return false; } bool WriteBinaryFile(const std::filesystem::path& path, const void* data, size_t size) { diff --git a/tests/RHI/OpenGL/unit/test_backend_specific.cpp b/tests/RHI/OpenGL/unit/test_backend_specific.cpp index 41c5d9ad..6e2e02e7 100644 --- a/tests/RHI/OpenGL/unit/test_backend_specific.cpp +++ b/tests/RHI/OpenGL/unit/test_backend_specific.cpp @@ -16,6 +16,7 @@ #include "XCEngine/RHI/RHIShader.h" #include "XCEngine/RHI/RHISampler.h" #include "XCEngine/RHI/RHISwapChain.h" +#include "XCEngine/RHI/ShaderCompiler/SpirvShaderCompiler.h" #include "XCEngine/RHI/RHITexture.h" #include @@ -23,6 +24,9 @@ #include #include #include +#include +#include +#include #include using namespace XCEngine::RHI; @@ -118,6 +122,87 @@ VSOutput MainVS(VSInput input) { delete shader; } +TEST(OpenGLShaderCompiler_Test, ParallelSpirvToolInvocationsDoNotCollideOnTemporaryFiles) { + if (!SupportsOpenGLHlslToolchainForTests()) { + GTEST_SKIP() << "glslangValidator.exe or spirv-cross.exe was not found."; + } + + static const char* vertexSource = R"( +struct VSInput { + float4 position : POSITION; +}; + +struct VSOutput { + float4 position : SV_POSITION; +}; + +VSOutput MainVS(VSInput input) { + VSOutput output; + output.position = input.position; + return output; +} +)"; + + ShaderCompileDesc shaderDesc = {}; + shaderDesc.source.assign(vertexSource, vertexSource + std::strlen(vertexSource)); + shaderDesc.sourceLanguage = ShaderLanguage::HLSL; + shaderDesc.entryPoint = L"MainVS"; + shaderDesc.profile = L"vs_5_0"; + + constexpr int kWorkerCount = 4; + constexpr int kIterationsPerWorker = 4; + + std::mutex failureMutex; + std::vector failures; + std::vector workers; + workers.reserve(kWorkerCount); + + for (int workerIndex = 0; workerIndex < kWorkerCount; ++workerIndex) { + workers.emplace_back([&, workerIndex]() { + for (int iteration = 0; iteration < kIterationsPerWorker; ++iteration) { + CompiledSpirvShader compiledShader = {}; + std::string errorMessage; + if (!CompileSpirvShader( + shaderDesc, + SpirvTargetEnvironment::Vulkan, + compiledShader, + &errorMessage)) { + std::lock_guard lock(failureMutex); + failures.push_back( + "CompileSpirvShader failed for worker " + + std::to_string(workerIndex) + + ", iteration " + + std::to_string(iteration) + + ": " + + errorMessage); + return; + } + + std::string glslSource; + errorMessage.clear(); + if (!TranspileSpirvToOpenGLGLSL(compiledShader, glslSource, &errorMessage) || + glslSource.empty()) { + std::lock_guard lock(failureMutex); + failures.push_back( + "TranspileSpirvToOpenGLGLSL failed for worker " + + std::to_string(workerIndex) + + ", iteration " + + std::to_string(iteration) + + ": " + + errorMessage); + return; + } + } + }); + } + + for (std::thread& worker : workers) { + worker.join(); + } + + ASSERT_TRUE(failures.empty()) << failures.front(); +} + TEST_F(OpenGLTestFixture, Device_CreatePipelineState_HlslGraphicsShaders_UsesTranspiledHlslPath) { ASSERT_TRUE(GetDevice()->MakeContextCurrent()); if (!SupportsOpenGLHlslToolchainForTests()) {