diff --git a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp index 0ef4c5b7..09a25499 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp @@ -15,7 +15,7 @@ #define METAL_AIR_CACHE_BLOCK_COUNT (METAL_AIR_CACHE_SIZE / 512) static bool s_isLoadingShadersMtl{false}; -static std::atomic s_hasRAMFilesystem{false}; +static bool s_hasRAMFilesystem{false}; class FileCache* s_airCache{nullptr}; extern std::atomic_int g_compiled_shaders_total; @@ -28,10 +28,14 @@ public: { if (m_threadsActive.exchange(true)) return; - // create thread pool + + // Create thread pool const uint32 threadCount = 2; for (uint32 i = 0; i < threadCount; ++i) s_threads.emplace_back(&ShaderMtlThreadPool::CompilerThreadFunc, this); + + // Create AIR cache thread + s_airCacheThread = new std::thread(&ShaderMtlThreadPool::AIRCacheThreadFunc, this); } void StopThreads() @@ -43,6 +47,9 @@ public: for (auto& it : s_threads) it.join(); s_threads.clear(); + + s_airCacheThread->join(); + delete s_airCacheThread; } ~ShaderMtlThreadPool() @@ -79,15 +86,48 @@ public: } } + void AIRCacheThreadFunc() + { + SetThreadName("mtlAIRCache"); + while (m_threadsActive.load(std::memory_order::relaxed)) + { + s_airCacheQueueCount.decrementWithWait(); + s_airCacheQueueMutex.lock(); + if (s_airCacheQueue.empty()) + { + s_airCacheQueueMutex.unlock(); + continue; + } + + // Create RAM filesystem + if (!s_hasRAMFilesystem) + { + executeCommand("diskutil erasevolume HFS+ {} $(hdiutil attach -nomount ram://{})", METAL_AIR_CACHE_NAME, METAL_AIR_CACHE_BLOCK_COUNT); + s_hasRAMFilesystem = true; + } + + RendererShaderMtl* job = s_airCacheQueue.front(); + s_airCacheQueue.pop_front(); + s_airCacheQueueMutex.unlock(); + // compile + job->CompileToAIR(); + } + } + bool HasThreadsRunning() const { return m_threadsActive; } public: std::vector s_threads; + std::thread* s_airCacheThread{nullptr}; std::deque s_compilationQueue; CounterSemaphore s_compilationQueueCount; std::mutex s_compilationQueueMutex; + std::deque s_airCacheQueue; + CounterSemaphore s_airCacheQueueCount; + std::mutex s_airCacheQueueMutex; + private: std::atomic m_threadsActive; } shaderMtlThreadPool; @@ -118,6 +158,12 @@ void RendererShaderMtl::ShaderCacheLoading_end() { s_isLoadingShadersMtl = false; + // Reset shader compilation speed + static_cast(g_renderer.get())->SetShouldMaximizeConcurrentCompilation(false); +} + +void RendererShaderMtl::ShaderCacheLoading_Close() +{ // Close the AIR cache if (s_airCache) { @@ -127,18 +173,7 @@ void RendererShaderMtl::ShaderCacheLoading_end() // Close RAM filesystem if (s_hasRAMFilesystem) - { executeCommand("diskutil eject {}", METAL_AIR_CACHE_PATH); - s_hasRAMFilesystem = false; - } - - // Reset shader compilation speed - static_cast(g_renderer.get())->SetShouldMaximizeConcurrentCompilation(false); -} - -void RendererShaderMtl::ShaderCacheLoading_Close() -{ - // Do nothing } void RendererShaderMtl::Initialize() @@ -215,7 +250,6 @@ MTL::Library* RendererShaderMtl::LibraryFromSource() { // Compile from source MTL::CompileOptions* options = MTL::CompileOptions::alloc()->init(); - // TODO: always disable fast math for problematic shaders if (g_current_game_profile->GetFastMath()) options->setFastMathEnabled(true); if (g_current_game_profile->GetPositionInvariance()) @@ -224,7 +258,6 @@ MTL::Library* RendererShaderMtl::LibraryFromSource() NS::Error* error = nullptr; MTL::Library* library = m_mtlr->GetDevice()->newLibrary(ToNSString(m_mslCode), options, &error); options->release(); - FinishCompilation(); if (error) { cemuLog_log(LogType::Force, "failed to create library from source: {} -> {}", error->localizedDescription()->utf8String(), m_mslCode.c_str()); @@ -240,7 +273,6 @@ MTL::Library* RendererShaderMtl::LibraryFromAIR(std::span data) NS::Error* error = nullptr; MTL::Library* library = m_mtlr->GetDevice()->newLibrary(dispatchData, &error); - FinishCompilation(); if (error) { cemuLog_log(LogType::Force, "failed to create library from AIR: {}", error->localizedDescription()->utf8String()); @@ -252,7 +284,7 @@ MTL::Library* RendererShaderMtl::LibraryFromAIR(std::span data) void RendererShaderMtl::CompileInternal() { - MTL::Library* library; + MTL::Library* library = nullptr; // First, try to retrieve the compiled shader from the AIR cache if (s_isLoadingShadersMtl && (m_isGameShader && !m_isGfxPackShader) && s_airCache) @@ -264,68 +296,24 @@ void RendererShaderMtl::CompileInternal() if (s_airCache->GetFile({ h1, h2 }, cacheFileData)) { library = LibraryFromAIR(std::span(cacheFileData.data(), cacheFileData.size())); - } - else - { - // Ensure that RAM filesystem exists - static std::atomic s_creatingRAMFilesystem{false}; - if (!s_hasRAMFilesystem) - { - if (s_creatingRAMFilesystem) - { - while (!s_hasRAMFilesystem) - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - } - else - { - s_creatingRAMFilesystem = true; - executeCommand("diskutil erasevolume HFS+ {} $(hdiutil attach -nomount ram://{})", METAL_AIR_CACHE_NAME, METAL_AIR_CACHE_BLOCK_COUNT); - s_creatingRAMFilesystem = false; - } - s_hasRAMFilesystem = true; - } - - // The shader is not in the cache, compile it - std::string baseFilename = fmt::format("{}/{}_{}", METAL_AIR_CACHE_PATH, h1, h2); - - // Source - std::ofstream mslFile; - mslFile.open(fmt::format("{}.metal", baseFilename)); - mslFile << m_mslCode; - mslFile.close(); - - // Compile - if (!executeCommand("xcrun -sdk macosx metal -o {}.ir -c {}.metal -w", baseFilename, baseFilename)) - return; - if (!executeCommand("xcrun -sdk macosx metallib -o {}.metallib {}.ir", baseFilename, baseFilename)) - return; - - // Clean up - executeCommand("rm {}.metal", baseFilename); - executeCommand("rm {}.ir", baseFilename); - - // Load from the newly generated AIR - MemoryMappedFile airFile(fmt::format("{}.metallib", baseFilename)); - std::span airData = std::span(airFile.data(), airFile.size()); - library = LibraryFromAIR(std::span(airData.data(), airData.size())); - - // Store in the cache - uint64 h1, h2; - GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2); - s_airCache->AddFile({ h1, h2 }, airData.data(), airData.size()); - - // Clean up - executeCommand("rm {}.metallib", baseFilename); + FinishCompilation(); } } - else + + // Not in the cache, compile from source + if (!library) { // Compile from source library = LibraryFromSource(); - } + if (!library) + return; - if (!library) - return; + // Store in the AIR cache + shaderMtlThreadPool.s_airCacheQueueMutex.lock(); + shaderMtlThreadPool.s_airCacheQueue.push_back(this); + shaderMtlThreadPool.s_airCacheQueueCount.increment(); + shaderMtlThreadPool.s_airCacheQueueMutex.unlock(); + } m_function = library->newFunction(ToNSString("main0")); library->release(); @@ -335,6 +323,44 @@ void RendererShaderMtl::CompileInternal() g_compiled_shaders_total++; } +void RendererShaderMtl::CompileToAIR() +{ + uint64 h1, h2; + GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2); + + // The shader is not in the cache, compile it + std::string baseFilename = fmt::format("{}/{}_{}", METAL_AIR_CACHE_PATH, h1, h2); + + // Source + std::ofstream mslFile; + mslFile.open(fmt::format("{}.metal", baseFilename)); + mslFile << m_mslCode; + mslFile.close(); + + // Compile + if (!executeCommand("xcrun -sdk macosx metal -o {}.ir -c {}.metal -w", baseFilename, baseFilename)) + return; + if (!executeCommand("xcrun -sdk macosx metallib -o {}.metallib {}.ir", baseFilename, baseFilename)) + return; + + // Clean up + executeCommand("rm {}.metal", baseFilename); + executeCommand("rm {}.ir", baseFilename); + + // Load from the newly generated AIR + MemoryMappedFile airFile(fmt::format("{}.metallib", baseFilename)); + std::span airData = std::span(airFile.data(), airFile.size()); + //library = LibraryFromAIR(std::span(airData.data(), airData.size())); + + // Store in the cache + s_airCache->AddFile({ h1, h2 }, airData.data(), airData.size()); + + // Clean up + executeCommand("rm {}.metallib", baseFilename); + + FinishCompilation(); +} + void RendererShaderMtl::FinishCompilation() { m_mslCode.clear(); diff --git a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h index 98973a0e..a749253e 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h @@ -73,5 +73,7 @@ private: void CompileInternal(); + void CompileToAIR(); + void FinishCompilation(); };