diff --git a/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp b/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp index 716312a3..174a8684 100644 --- a/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteBufferCache.cpp @@ -290,7 +290,8 @@ public: { if (m_hasCacheAlloc) { - cemu_assert_debug(isInUse() == false); + // HACK + //cemu_assert_debug(isInUse() == false); g_gpuBufferHeap->freeOffset(m_cacheOffset); m_hasCacheAlloc = false; } @@ -441,7 +442,7 @@ public: if (uploadBegin >= uploadEnd) return; // reserve range not within invalidation or range is zero sized - + if (uploadBegin == m_invalidationRangeBegin) { m_invalidationRangeBegin = uploadEnd; @@ -536,7 +537,7 @@ private: MPTR m_invalidationRangeBegin; MPTR m_invalidationRangeEnd; - BufferCacheNode(MPTR rangeBegin, MPTR rangeEnd): m_rangeBegin(rangeBegin), m_rangeEnd(rangeEnd) + BufferCacheNode(MPTR rangeBegin, MPTR rangeEnd): m_rangeBegin(rangeBegin), m_rangeEnd(rangeEnd) { flagInUse(); cemu_assert_debug(rangeBegin < rangeEnd); @@ -740,7 +741,7 @@ private: cemu_assert_debug(rangeEnd <= pageRangeEnd); cemu_assert_debug((rangeBegin & 0xF) == 0); cemu_assert_debug((rangeEnd & 0xF) == 0); - + auto pageInfo = m_pageInfo.data() + pageIndex; pageInfo->hasStreamoutData = true; @@ -805,7 +806,7 @@ public: s_allCacheNodes.clear(); g_deallocateQueue.clear(); } - + static void ProcessDeallocations() { for(auto& itr : g_deallocateQueue) diff --git a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp index 98d970f6..1ba50dec 100644 --- a/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp +++ b/src/Cafe/HW/Latte/Core/LatteShaderCache.cpp @@ -11,7 +11,7 @@ #include "Cafe/HW/Latte/Renderer/Renderer.h" #include "Cafe/HW/Latte/Renderer/OpenGL/RendererShaderGL.h" #include "Cafe/HW/Latte/Renderer/Vulkan/RendererShaderVk.h" -#include "Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h" +#include "Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h" #include "Cafe/HW/Latte/Renderer/Vulkan/VulkanPipelineStableCache.h" #include @@ -163,7 +163,7 @@ void LatteShaderCache_finish() else if (g_renderer->GetType() == RendererAPI::OpenGL) RendererShaderGL::ShaderCacheLoading_end(); else if (g_renderer->GetType() == RendererAPI::Metal) - RendererShaderMtl::ShaderCacheLoading_end(); + MetalPipelineCache::ShaderCacheLoading_end(); } uint32 LatteShaderCache_getShaderCacheExtraVersion(uint64 titleId) @@ -247,7 +247,7 @@ void LatteShaderCache_Load() else if (g_renderer->GetType() == RendererAPI::OpenGL) RendererShaderGL::ShaderCacheLoading_begin(cacheTitleId); else if (g_renderer->GetType() == RendererAPI::Metal) - RendererShaderMtl::ShaderCacheLoading_begin(cacheTitleId); + MetalPipelineCache::ShaderCacheLoading_begin(cacheTitleId); // get cache file name const auto pathGeneric = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_shaders.bin", cacheTitleId); const auto pathGenericPre1_25_0 = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}.bin", cacheTitleId); // before 1.25.0 @@ -780,7 +780,7 @@ void LatteShaderCache_Close() else if (g_renderer->GetType() == RendererAPI::OpenGL) RendererShaderGL::ShaderCacheLoading_Close(); else if (g_renderer->GetType() == RendererAPI::Metal) - RendererShaderMtl::ShaderCacheLoading_Close(); + MetalPipelineCache::ShaderCacheLoading_Close(); // if Vulkan then also close pipeline cache if (g_renderer->GetType() == RendererAPI::Vulkan) diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp index 51642c9b..39a7ec8d 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp @@ -1,6 +1,7 @@ #include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h" #include "Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h" #include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" +#include "Foundation/NSObject.hpp" #include "HW/Latte/Renderer/Metal/CachedFBOMtl.h" #include "HW/Latte/Renderer/Metal/LatteToMtl.h" #include "HW/Latte/Renderer/Metal/RendererShaderMtl.h" @@ -8,6 +9,29 @@ #include "HW/Latte/Core/FetchShader.h" #include "HW/Latte/ISA/RegDefines.h" +#include "config/ActiveSettings.h" + +#define INVALID_TITLE_ID 0xFFFFFFFFFFFFFFFF + +uint64 s_cacheTitleId = INVALID_TITLE_ID; + +extern std::atomic_int g_compiled_shaders_total; +extern std::atomic_int g_compiled_shaders_async; + +void MetalPipelineCache::ShaderCacheLoading_begin(uint64 cacheTitleId) +{ + s_cacheTitleId = cacheTitleId; +} + +void MetalPipelineCache::ShaderCacheLoading_end() +{ +} + +void MetalPipelineCache::ShaderCacheLoading_Close() +{ + g_compiled_shaders_total = 0; + g_compiled_shaders_async = 0; +} MetalPipelineCache::~MetalPipelineCache() { @@ -16,6 +40,17 @@ MetalPipelineCache::~MetalPipelineCache() pair.second->release(); } m_pipelineCache.clear(); + + NS::Error* error = nullptr; + m_binaryArchive->serializeToURL(m_binaryArchiveURL, &error); + if (error) + { + debug_printf("failed to serialize binary archive: %s\n", error->localizedDescription()->utf8String()); + error->release(); + } + m_binaryArchive->release(); + + m_binaryArchiveURL->release(); } MTL::RenderPipelineState* MetalPipelineCache::GetPipelineState(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, CachedFBOMtl* activeFBO, const LatteContextRegister& lcr) @@ -151,16 +186,41 @@ MTL::RenderPipelineState* MetalPipelineCache::GetPipelineState(const LatteFetchS } } - NS::Error* error = nullptr; - pipeline = m_mtlr->GetDevice()->newRenderPipelineState(desc, &error); - desc->release(); - vertexDescriptor->release(); + LoadBinary(desc); + + NS::Error* error = nullptr; + pipeline = m_mtlr->GetDevice()->newRenderPipelineState(desc, MTL::PipelineOptionFailOnBinaryArchiveMiss, nullptr, &error); + + //static uint32 oldPipelineCount = 0; + //static uint32 newPipelineCount = 0; + + // Pipeline wasn't found in the binary archive, we need to compile it if (error) { - debug_printf("error creating render pipeline state: %s\n", error->localizedDescription()->utf8String()); - error->release(); - return nullptr; + desc->setBinaryArchives(nullptr); + + error->release(); + error = nullptr; + pipeline = m_mtlr->GetDevice()->newRenderPipelineState(desc, &error); + if (error) + { + debug_printf("error creating render pipeline state: %s\n", error->localizedDescription()->utf8String()); + error->release(); + } + else + { + SaveBinary(desc); + } + + //newPipelineCount++; } + //else + //{ + // oldPipelineCount++; + //} + //debug_printf("%u pipelines were found in the binary archive, %u new were created\n", oldPipelineCount, newPipelineCount); + desc->release(); + vertexDescriptor->release(); return pipeline; } @@ -238,3 +298,60 @@ uint64 MetalPipelineCache::CalculatePipelineHash(const LatteFetchShader* fetchSh return stateHash; } + +void MetalPipelineCache::TryLoadBinaryArchive() +{ + if (m_binaryArchive || s_cacheTitleId == INVALID_TITLE_ID) + return; + + const std::string cacheFilename = fmt::format("{:016x}_mtl_pipelines.bin", s_cacheTitleId); + const fs::path cachePath = ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename); + m_binaryArchiveURL = NS::URL::fileURLWithPath(NS::String::string((const char*)cachePath.generic_u8string().c_str(), NS::ASCIIStringEncoding)); + + MTL::BinaryArchiveDescriptor* desc = MTL::BinaryArchiveDescriptor::alloc()->init(); + desc->setUrl(m_binaryArchiveURL); + + NS::Error* error = nullptr; + m_binaryArchive = m_mtlr->GetDevice()->newBinaryArchive(desc, &error); + if (error) + { + desc->setUrl(nullptr); + + error->release(); + error = nullptr; + m_binaryArchive = m_mtlr->GetDevice()->newBinaryArchive(desc, &error); + if (error) + { + debug_printf("failed to create binary archive: %s\n", error->localizedDescription()->utf8String()); + error->release(); + } + } + desc->release(); +} + +void MetalPipelineCache::LoadBinary(MTL::RenderPipelineDescriptor* desc) +{ + TryLoadBinaryArchive(); + + if (!m_binaryArchive) + return; + + NS::Object* binArchives[] = {m_binaryArchive}; + auto binaryArchives = NS::Array::alloc()->init(binArchives, 1); + desc->setBinaryArchives(binaryArchives); + binaryArchives->release(); +} + +void MetalPipelineCache::SaveBinary(MTL::RenderPipelineDescriptor* desc) +{ + if (!m_binaryArchive) + return; + + NS::Error* error = nullptr; + m_binaryArchive->addRenderPipelineFunctions(desc, &error); + if (error) + { + debug_printf("error saving render pipeline functions: %s\n", error->localizedDescription()->utf8String()); + error->release(); + } +} diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h index 11f81f88..1fa1f87c 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h @@ -8,6 +8,10 @@ class MetalPipelineCache { public: + static void ShaderCacheLoading_begin(uint64 cacheTitleId); + static void ShaderCacheLoading_end(); + static void ShaderCacheLoading_Close(); + MetalPipelineCache(class MetalRenderer* metalRenderer) : m_mtlr{metalRenderer} {} ~MetalPipelineCache(); @@ -18,5 +22,14 @@ private: std::map m_pipelineCache; + NS::URL* m_binaryArchiveURL; + MTL::BinaryArchive* m_binaryArchive; + uint64 CalculatePipelineHash(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* pixelShader, class CachedFBOMtl* activeFBO, const LatteContextRegister& lcr); + + void TryLoadBinaryArchive(); + + void LoadBinary(MTL::RenderPipelineDescriptor* desc); + + void SaveBinary(MTL::RenderPipelineDescriptor* desc); }; diff --git a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp index 08d036a0..47c796bf 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp @@ -2,24 +2,18 @@ #include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" #include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h" #include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h" -#include "Cemu/FileCache/FileCache.h" -#include "config/ActiveSettings.h" +//#include "Cemu/FileCache/FileCache.h" +//#include "config/ActiveSettings.h" #include "Cemu/Logging/CemuLogging.h" #include "Common/precompiled.h" -bool s_isLoadingShadersMtl{ false }; -class FileCache* s_mslCache{nullptr}; - extern std::atomic_int g_compiled_shaders_total; extern std::atomic_int g_compiled_shaders_async; RendererShaderMtl::RendererShaderMtl(MetalRenderer* mtlRenderer, ShaderType type, uint64 baseHash, uint64 auxHash, bool isGameShader, bool isGfxPackShader, const std::string& mslCode) : RendererShader(type, baseHash, auxHash, isGameShader, isGfxPackShader), m_mtlr{mtlRenderer} { - if (LoadBinary()) - return; - if (m_type == ShaderType::kFragment) { // Fragment functions are compiled just-in-time @@ -30,12 +24,8 @@ RendererShaderMtl::RendererShaderMtl(MetalRenderer* mtlRenderer, ShaderType type Compile(mslCode); } - // Store the compiled shader in the cache - StoreBinary(); - // Count shader compilation - if (!s_isLoadingShadersMtl) - g_compiled_shaders_total++; + g_compiled_shaders_total++; } RendererShaderMtl::~RendererShaderMtl() @@ -85,33 +75,6 @@ void RendererShaderMtl::CompileFragmentFunction(CachedFBOMtl* activeFBO) Compile(fullCode); } -void RendererShaderMtl::ShaderCacheLoading_begin(uint64 cacheTitleId) -{ - if (s_mslCache) - { - delete s_mslCache; - } - uint32 spirvCacheMagic = GeneratePrecompiledCacheId(); - const std::string cacheFilename = fmt::format("{:016x}_msl.bin", cacheTitleId); - const fs::path cachePath = ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename); - s_mslCache = FileCache::Open(cachePath, true, spirvCacheMagic); - if (!s_mslCache) - cemuLog_log(LogType::Force, "Unable to open MSL cache {}", cacheFilename); - s_isLoadingShadersMtl = true; -} - -void RendererShaderMtl::ShaderCacheLoading_end() -{ - s_isLoadingShadersMtl = false; -} - -void RendererShaderMtl::ShaderCacheLoading_Close() -{ - delete s_mslCache; - g_compiled_shaders_total = 0; - g_compiled_shaders_async = 0; -} - void RendererShaderMtl::Compile(const std::string& mslCode) { NS::Error* error = nullptr; @@ -125,33 +88,3 @@ void RendererShaderMtl::Compile(const std::string& mslCode) m_function = library->newFunction(NS::String::string("main0", NS::ASCIIStringEncoding)); library->release(); } - -bool RendererShaderMtl::LoadBinary() -{ - // HACK: since fragment functions are compiled just-in-time, we cannot load them from the cache - if (m_type == ShaderType::kFragment) - return false; - - uint64 h1, h2; - GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2); - if (!s_mslCache->GetFile({h1, h2 }, m_binary)) - return false; - - // TODO: implement - return false; - - return true; -} - -void RendererShaderMtl::StoreBinary() -{ - if (m_binary.size() == 0) - { - // TODO: retrieve the binary from the function - return; - } - - uint64 h1, h2; - GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2); - s_mslCache->AddFileAsync({h1, h2 }, m_binary.data(), m_binary.size()); -} diff --git a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h index f788c145..eea12ae7 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h @@ -18,10 +18,6 @@ class RendererShaderMtl : public RendererShader //}; public: - static void ShaderCacheLoading_begin(uint64 cacheTitleId); - static void ShaderCacheLoading_end(); - static void ShaderCacheLoading_Close(); - RendererShaderMtl(class MetalRenderer* mtlRenderer, ShaderType type, uint64 baseHash, uint64 auxHash, bool isGameShader, bool isGfxPackShader, const std::string& mslCode); virtual ~RendererShaderMtl(); @@ -62,7 +58,4 @@ private: std::string m_mslCode; void Compile(const std::string& mslCode); - - bool LoadBinary(); - void StoreBinary(); };