From ac42f7b90266a835f34ca79915acb4751fcb8722 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Tue, 22 Oct 2024 07:54:03 +0200 Subject: [PATCH 1/5] save and load binary archives --- src/Cafe/CMakeLists.txt | 2 + .../LatteDecompilerEmitMSL.cpp | 1 + .../Renderer/Metal/MetalBinaryArchive.cpp | 173 ++++++++++++++++++ .../Latte/Renderer/Metal/MetalBinaryArchive.h | 52 ++++++ .../HW/Latte/Renderer/Metal/MetalCommon.h | 9 + .../Renderer/Metal/MetalPipelineCache.cpp | 14 +- .../Latte/Renderer/Metal/MetalPipelineCache.h | 4 +- .../Renderer/Metal/MetalPipelineCompiler.cpp | 166 ++--------------- .../Renderer/Metal/MetalPipelineCompiler.h | 2 +- 9 files changed, 268 insertions(+), 155 deletions(-) create mode 100644 src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp create mode 100644 src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index b30f8efef..f8611f548 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -565,6 +565,8 @@ if(ENABLE_METAL) HW/Latte/Renderer/Metal/MetalOutputShaderCache.h HW/Latte/Renderer/Metal/MetalPipelineCompiler.cpp HW/Latte/Renderer/Metal/MetalPipelineCompiler.h + HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp + HW/Latte/Renderer/Metal/MetalBinaryArchive.h HW/Latte/Renderer/Metal/MetalPipelineCache.cpp HW/Latte/Renderer/Metal/MetalPipelineCache.h HW/Latte/Renderer/Metal/MetalDepthStencilCache.cpp diff --git a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp index 13f7bccff..0274e69cd 100644 --- a/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp +++ b/src/Cafe/HW/Latte/LegacyShaderDecompiler/LatteDecompilerEmitMSL.cpp @@ -12,6 +12,7 @@ #include "Cafe/HW/Latte/Renderer/Renderer.h" #include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h" #include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h" + #include "config/ActiveSettings.h" #include "util/helpers/StringBuf.h" diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp new file mode 100644 index 000000000..93fdfda3f --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp @@ -0,0 +1,173 @@ +#include "Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h" +#include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" + +#include "config/ActiveSettings.h" + +MetalBinaryArchive::MetalBinaryArchive(class MetalRenderer* metalRenderer) : m_mtlr{metalRenderer} +{ + const char* deviceNameSrc = m_mtlr->GetDevice()->name()->utf8String(); + std::string deviceName; + deviceName.assign(deviceNameSrc); + + // Replace spaces with underscores + for (auto& c : deviceName) + { + if (c == ' ') + c = '_'; + } + + // OS version + auto osVersion = NS::ProcessInfo::processInfo()->operatingSystemVersion(); + + // Precompiled binaries cannot be shared between different devices or OS versions + m_archiveDir = ActiveSettings::GetCachePath("shaderCache/precompiled/{}/{}-{}-{}/", deviceName, osVersion.majorVersion, osVersion.minorVersion, osVersion.patchVersion); + fs::create_directories(m_archiveDir); + + // Directory for temporary archives + m_tmpArchiveDir = m_archiveDir / "tmp"; + fs::create_directories(m_tmpArchiveDir); +} + +MetalBinaryArchive::~MetalBinaryArchive() +{ + SerializeOldArchive(); + + // TODO: combine new archives into a single one +} + +void MetalBinaryArchive::SetTitleId(uint64 titleId) +{ + m_titleId = titleId; + + const std::string archiveFilename = fmt::format("{:016x}_mtl_pipelines.bin", titleId); + m_finalArchivePath = m_archiveDir / archiveFilename; +} + +void MetalBinaryArchive::LoadSerializedArchive() +{ + m_isLoading = true; + + if (!std::filesystem::exists(m_finalArchivePath)) + { + // TODO: create a save archive + return; + } + + // Load the binary archive + MTL::BinaryArchiveDescriptor* desc = MTL::BinaryArchiveDescriptor::alloc()->init(); + NS::URL* url = ToNSURL(m_finalArchivePath); + desc->setUrl(url); + url->release(); + + NS::Error* error = nullptr; + m_loadArchive = m_mtlr->GetDevice()->newBinaryArchive(desc, &error); + if (error) + { + cemuLog_log(LogType::Force, "failed to load binary archive: {}", error->localizedDescription()->utf8String()); + error->release(); + } + desc->release(); + + // Create an array for the archive + NS::Object* binArchives[] = {m_loadArchive}; + m_loadArchiveArray = NS::Array::alloc()->init(binArchives, 1); +} + +void MetalBinaryArchive::CloseSerializedArchive() +{ + if (m_loadArchive) + m_loadArchive->release(); + if (m_loadArchiveArray) + m_loadArchiveArray->release(); + + m_isLoading = false; +} + +void MetalBinaryArchive::LoadPipeline(MTL::RenderPipelineDescriptor* renderPipelineDescriptor) { + if (m_isLoading) + renderPipelineDescriptor->setBinaryArchives(m_loadArchiveArray); +} + +// TODO: should be available since macOS 15.0 +/* +void MetalBinaryArchive::LoadPipeline(MTL::MeshRenderPipelineDescriptor* renderPipelineDescriptor) { + if (m_isLoading) + renderPipelineDescriptor->setBinaryArchives(m_loadArchiveArray); +} +*/ + +void MetalBinaryArchive::SavePipeline(MTL::RenderPipelineDescriptor* renderPipelineDescriptor) { + LoadSaveArchive(); + + if (m_saveArchive) + { + NS::Error* error = nullptr; + m_saveArchive->addRenderPipelineFunctions(renderPipelineDescriptor, &error); + if (error) + { + cemuLog_log(LogType::Force, "error saving render pipeline functions: {}", error->localizedDescription()->utf8String()); + error->release(); + } + m_pipelinesSerialized++; + } +} + +// TODO: should be available since macOS 15.0 +/* +void MetalBinaryArchive::SavePipeline(MTL::MeshRenderPipelineDescriptor* renderPipelineDescriptor) { + LoadSaveArchive(); + + if (m_saveArchive) + { + NS::Error* error = nullptr; + m_saveArchive->addMeshRenderPipelineFunctions(renderPipelineDescriptor, &error); + if (error) + { + cemuLog_log(LogType::Force, "error saving mesh pipeline functions: {}", error->localizedDescription()->utf8String()); + error->release(); + } + m_pipelinesSerialized++; + } +} +*/ + +void MetalBinaryArchive::SerializeOldArchive() +{ + if (!m_saveArchive) + return; + + NS::Error* error = nullptr; + NS::URL* url = ToNSURL(GetTmpArchivePath(m_archiveIndex)); + m_saveArchive->serializeToURL(url, &error); + url->release(); + if (error) + { + cemuLog_log(LogType::Force, "failed to serialize binary archive: {}", error->localizedDescription()->utf8String()); + error->release(); + } + m_saveArchive->release(); + + m_archiveIndex++; + m_pipelinesSerialized = 0; +} + +void MetalBinaryArchive::LoadSaveArchive() +{ + if (m_isLoading || (m_saveArchive && m_pipelinesSerialized < SERIALIZE_TRESHOLD)) + return; + + // First, save the old archive to disk + SerializeOldArchive(); + + // Create a new archive + MTL::BinaryArchiveDescriptor* desc = MTL::BinaryArchiveDescriptor::alloc()->init(); + + NS::Error* error = nullptr; + m_saveArchive = m_mtlr->GetDevice()->newBinaryArchive(desc, &error); + if (error) + { + cemuLog_log(LogType::Force, "failed to create save binary archive: {}", error->localizedDescription()->utf8String()); + error->release(); + } + desc->release(); +} diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h new file mode 100644 index 000000000..a8136edd3 --- /dev/null +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h @@ -0,0 +1,52 @@ +#pragma once + +#include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h" + +class MetalBinaryArchive +{ +public: + static constexpr uint32 SERIALIZE_TRESHOLD = 64; + + MetalBinaryArchive(class MetalRenderer* metalRenderer); + ~MetalBinaryArchive(); + + void SetTitleId(uint64 titleId); + + void LoadSerializedArchive(); + void CloseSerializedArchive(); + + // For pipeline compiler + void LoadPipeline(MTL::RenderPipelineDescriptor* renderPipelineDescriptor); + //void LoadPipeline(MTL::MeshRenderPipelineDescriptor* renderPipelineDescriptor); + void SavePipeline(MTL::RenderPipelineDescriptor* renderPipelineDescriptor); + //void SavePipeline(MTL::MeshRenderPipelineDescriptor* renderPipelineDescriptor); + +private: + class MetalRenderer* m_mtlr; + + uint64 m_titleId; + + bool m_isLoading = false; + + // Paths + fs::path m_archiveDir; + fs::path m_tmpArchiveDir; + fs::path m_finalArchivePath; + + // Binary archives + MTL::BinaryArchive* m_loadArchive = nullptr; + NS::Array* m_loadArchiveArray = nullptr; + MTL::BinaryArchive* m_saveArchive = nullptr; + + uint32 m_pipelinesSerialized = 0; + uint32 m_archiveIndex = 0; + + void SerializeOldArchive(); + void createSaveArchive(); + void LoadSaveArchive(); + + fs::path GetTmpArchivePath(uint32 index) + { + return m_tmpArchiveDir / fs::path("archive" + std::to_string(index) + ".bin"); + } +}; diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h index a1fe7f826..32e3af769 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h @@ -58,11 +58,20 @@ inline NS::String* ToNSString(const std::string& str) return ToNSString(str.c_str()); } +// Cast from fs::path to NS::URL* +inline NS::URL* ToNSURL(const fs::path& path) +{ + return NS::URL::fileURLWithPath(ToNSString(_pathToUtf8(path))); +} + +// Get a label for a resource inline NS::String* GetLabel(const std::string& label, const void* identifier) { return ToNSString(label + " (" + std::to_string(reinterpret_cast(identifier)) + ")"); } +// TODO: make a helper function to set the label for a resource + constexpr MTL::RenderStages ALL_MTL_RENDER_STAGES = MTL::RenderStageVertex | MTL::RenderStageObject | MTL::RenderStageMesh | MTL::RenderStageFragment; inline bool IsValidDepthTextureType(Latte::E_DIM dim) diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp index 9e49959c9..8c6cf5eb8 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.cpp @@ -27,7 +27,7 @@ MetalPipelineCache& MetalPipelineCache::GetInstance() return *g_mtlPipelineCache; } -MetalPipelineCache::MetalPipelineCache(class MetalRenderer* metalRenderer) : m_mtlr{metalRenderer} +MetalPipelineCache::MetalPipelineCache(class MetalRenderer* metalRenderer) : m_mtlr{metalRenderer}, m_binaryArchive(metalRenderer) { g_mtlPipelineCache = this; } @@ -51,7 +51,7 @@ MTL::RenderPipelineState* MetalPipelineCache::GetRenderPipelineState(const Latte bool fbosMatch; compiler.InitFromState(fetchShader, vertexShader, geometryShader, pixelShader, lastUsedAttachmentsInfo, activeAttachmentsInfo, lcr, fbosMatch); bool attemptedCompilation = false; - MTL::RenderPipelineState* pipeline = compiler.Compile(false, true, true, attemptedCompilation); + MTL::RenderPipelineState* pipeline = compiler.Compile(m_binaryArchive, false, true, true, attemptedCompilation); // If FBOs don't match, it wouldn't be possible to reconstruct the pipeline from the cache if (pipeline && fbosMatch) @@ -176,6 +176,10 @@ struct uint32 MetalPipelineCache::BeginLoading(uint64 cacheTitleId) { + // Notify the binary archive + m_binaryArchive.SetTitleId(cacheTitleId); + m_binaryArchive.LoadSerializedArchive(); + std::error_code ec; fs::create_directories(ActiveSettings::GetCachePath("shaderCache/transferable"), ec); const auto pathCacheFile = ActiveSettings::GetCachePath("shaderCache/transferable/{:016x}_mtlpipeline.bin", cacheTitleId); @@ -216,6 +220,7 @@ uint32 MetalPipelineCache::BeginLoading(uint64 cacheTitleId) s_cache->UseCompression(false); g_mtlCacheState.pipelineMaxFileIndex = s_cache->GetMaximumFileIndex(); } + return s_cache->GetFileCount(); } @@ -253,6 +258,9 @@ bool MetalPipelineCache::UpdateLoading(uint32& pipelinesLoadedTotal, uint32& pip void MetalPipelineCache::EndLoading() { + // Notify the binary archive + m_binaryArchive.CloseSerializedArchive(); + // shut down compilation threads uint32 threadCount = m_numCompilationThreads; m_numCompilationThreads = 0; // signal thread shutdown @@ -375,7 +383,7 @@ void MetalPipelineCache::LoadPipelineFromCache(std::span fileData) // return; //} bool attemptedCompilation = false; - pipeline = pp.Compile(true, true, false, attemptedCompilation); + pipeline = pp.Compile(m_binaryArchive, true, true, false, attemptedCompilation); cemu_assert_debug(attemptedCompilation); // destroy pp early } diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h index be26bdee0..6875ec07e 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCache.h @@ -1,10 +1,10 @@ #pragma once #include "Cafe/HW/Latte/Renderer/Metal/MetalPipelineCompiler.h" +#include "Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h" #include "util/helpers/ConcurrentQueue.h" #include "util/helpers/fspinlock.h" -// TODO: binary archives class MetalPipelineCache { public: @@ -28,6 +28,8 @@ class MetalPipelineCache private: class MetalRenderer* m_mtlr; + MetalBinaryArchive m_binaryArchive; + std::map m_pipelineCache; FSpinlock m_pipelineCacheLock; diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCompiler.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCompiler.cpp index a8bce2913..5c95786ba 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCompiler.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCompiler.cpp @@ -5,6 +5,7 @@ #include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h" #include "Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h" #include "Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.h" +#include "Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h" #include "Cafe/HW/Latte/Core/FetchShader.h" #include "Cafe/HW/Latte/ISA/RegDefines.h" @@ -184,8 +185,6 @@ static RendererShaderMtl* rectsEmulationGS_generate(MetalRenderer* metalRenderer #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; @@ -267,24 +266,6 @@ void SetFragmentState(T* desc, const MetalAttachmentsInfo& lastUsedAttachmentsIn MetalPipelineCompiler::~MetalPipelineCompiler() { - /* - for (auto& pair : m_pipelineCache) - { - pair.second->release(); - } - m_pipelineCache.clear(); - - NS::Error* error = nullptr; - m_binaryArchive->serializeToURL(m_binaryArchiveURL, &error); - if (error) - { - cemuLog_log(LogType::Force, "error serializing binary archive: {}", error->localizedDescription()->utf8String()); - error->release(); - } - m_binaryArchive->release(); - - m_binaryArchiveURL->release(); - */ m_pipelineDescriptor->release(); } @@ -327,7 +308,7 @@ void MetalPipelineCompiler::InitFromState(const LatteFetchShader* fetchShader, c InitFromStateRender(fetchShader, vertexShader, lastUsedAttachmentsInfo, activeAttachmentsInfo, lcr, fbosMatch); } -MTL::RenderPipelineState* MetalPipelineCompiler::Compile(bool forceCompile, bool isRenderThread, bool showInOverlay, bool& attemptedCompilation) +MTL::RenderPipelineState* MetalPipelineCompiler::Compile(class MetalBinaryArchive& binaryArchive, bool forceCompile, bool isRenderThread, bool showInOverlay, bool& attemptedCompilation) { if (forceCompile) { @@ -365,10 +346,17 @@ MTL::RenderPipelineState* MetalPipelineCompiler::Compile(bool forceCompile, bool if (m_rasterizationEnabled) desc->setFragmentFunction(m_pixelShaderMtl->GetFunction()); + // Binary archive + //binaryArchive.LoadPipeline(desc); + #ifdef CEMU_DEBUG_ASSERT desc->setLabel(GetLabel("Mesh render pipeline state", desc)); #endif pipeline = m_mtlr->GetDevice()->newRenderPipelineState(desc, MTL::PipelineOptionNone, nullptr, &error); + + // Serialize + //if (pipeline) + // binaryArchive.SavePipeline(desc); } else { @@ -379,10 +367,17 @@ MTL::RenderPipelineState* MetalPipelineCompiler::Compile(bool forceCompile, bool if (m_rasterizationEnabled) desc->setFragmentFunction(m_pixelShaderMtl->GetFunction()); + // Binary archive + binaryArchive.LoadPipeline(desc); + #ifdef CEMU_DEBUG_ASSERT desc->setLabel(GetLabel("Render pipeline state", desc)); #endif pipeline = m_mtlr->GetDevice()->newRenderPipelineState(desc, MTL::PipelineOptionNone, nullptr, &error); + + // Serialize + if (pipeline) + binaryArchive.SavePipeline(desc); } auto end = std::chrono::high_resolution_clock::now(); @@ -487,62 +482,6 @@ void MetalPipelineCompiler::InitFromStateRender(const LatteFetchShader* fetchSha SetFragmentState(desc, lastUsedAttachmentsInfo, activeAttachmentsInfo, m_rasterizationEnabled, lcr, fbosMatch); m_pipelineDescriptor = desc; - - //TryLoadBinaryArchive(); - - // Load binary - /* - if (m_binaryArchive) - { - NS::Object* binArchives[] = {m_binaryArchive}; - auto binaryArchives = NS::Array::alloc()->init(binArchives, 1); - desc->setBinaryArchives(binaryArchives); - binaryArchives->release(); - } - */ - - /* - NS::Error* error = nullptr; -#ifdef CEMU_DEBUG_ASSERT - desc->setLabel(GetLabel("Cached render pipeline state", desc)); -#endif - pipeline = m_mtlr->GetDevice()->newRenderPipelineState(desc, MTL::PipelineOptionFailOnBinaryArchiveMiss, nullptr, &error); - - // Pipeline wasn't found in the binary archive, we need to compile it - if (error) - { - desc->setBinaryArchives(nullptr); - - error->release(); - error = nullptr; -#ifdef CEMU_DEBUG_ASSERT - desc->setLabel(GetLabel("New render pipeline state", desc)); -#endif - pipeline = m_mtlr->GetDevice()->newRenderPipelineState(desc, &error); - if (error) - { - cemuLog_log(LogType::Force, "error creating render pipeline state: {}", error->localizedDescription()->utf8String()); - error->release(); - } - else - { - // Save binary - if (m_binaryArchive) - { - NS::Error* error = nullptr; - m_binaryArchive->addRenderPipelineFunctions(desc, &error); - if (error) - { - cemuLog_log(LogType::Force, "error saving render pipeline functions: {}", error->localizedDescription()->utf8String()); - error->release(); - } - } - } - } - desc->release(); - - return pipeline; - */ } void MetalPipelineCompiler::InitFromStateMesh(const LatteFetchShader* fetchShader, const MetalAttachmentsInfo& lastUsedAttachmentsInfo, const MetalAttachmentsInfo& activeAttachmentsInfo, const LatteContextRegister& lcr, bool& fbosMatch) @@ -553,77 +492,4 @@ void MetalPipelineCompiler::InitFromStateMesh(const LatteFetchShader* fetchShade SetFragmentState(desc, lastUsedAttachmentsInfo, activeAttachmentsInfo, m_rasterizationEnabled, lcr, fbosMatch); m_pipelineDescriptor = desc; - - //TryLoadBinaryArchive(); - - // Load binary - // TODO: no binary archives? :( - - /* - NS::Error* error = nullptr; -#ifdef CEMU_DEBUG_ASSERT - desc->setLabel(GetLabel("Mesh pipeline state", desc)); -#endif - pipeline = m_mtlr->GetDevice()->newRenderPipelineState(desc, MTL::PipelineOptionNone, nullptr, &error); - desc->release(); - if (error) - { - cemuLog_log(LogType::Force, "error creating mesh render pipeline state: {}", error->localizedDescription()->utf8String()); - error->release(); - } - - return pipeline; - */ -} - -/* -void MetalPipelineCache::TryLoadBinaryArchive() -{ - if (m_binaryArchive || s_cacheTitleId == INVALID_TITLE_ID) - return; - - // GPU name - const char* deviceName1 = m_mtlr->GetDevice()->name()->utf8String(); - std::string deviceName; - deviceName.assign(deviceName1); - - // Replace spaces with underscores - for (auto& c : deviceName) - { - if (c == ' ') - c = '_'; - } - - // OS version - auto osVersion = NS::ProcessInfo::processInfo()->operatingSystemVersion(); - - // Precompiled binaries cannot be shared between different devices or OS versions - const std::string cacheFilename = fmt::format("{:016x}_mtl_pipelines.bin", s_cacheTitleId); - const fs::path cachePath = ActiveSettings::GetCachePath("shaderCache/precompiled/{}/{}-{}-{}/{}", deviceName, osVersion.majorVersion, osVersion.minorVersion, osVersion.patchVersion, cacheFilename); - - // Create the directory if it doesn't exist - std::filesystem::create_directories(cachePath.parent_path()); - - m_binaryArchiveURL = NS::URL::fileURLWithPath(ToNSString((const char*)cachePath.generic_u8string().c_str())); - - 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) - { - cemuLog_log(LogType::Force, "failed to create binary archive: {}", error->localizedDescription()->utf8String()); - error->release(); - } - } - desc->release(); } -*/ diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCompiler.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCompiler.h index e40675559..0247012b9 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCompiler.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalPipelineCompiler.h @@ -13,7 +13,7 @@ class MetalPipelineCompiler void InitFromState(const LatteFetchShader* fetchShader, const LatteDecompilerShader* vertexShader, const LatteDecompilerShader* geometryShader, const LatteDecompilerShader* pixelShader, const class MetalAttachmentsInfo& lastUsedAttachmentsInfo, const class MetalAttachmentsInfo& activeAttachmentsInfo, const LatteContextRegister& lcr, bool& fbosMatch); - MTL::RenderPipelineState* Compile(bool forceCompile, bool isRenderThread, bool showInOverlay, bool& attemptedCompilation); + MTL::RenderPipelineState* Compile(class MetalBinaryArchive& binaryArchive, bool forceCompile, bool isRenderThread, bool showInOverlay, bool& attemptedCompilation); private: class MetalRenderer* m_mtlr; From d4ed954ea8ccb62e1e949f1ea064b5a21dc92971 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 24 Oct 2024 17:15:24 +0200 Subject: [PATCH 2/5] fix: incorrect texture usages --- src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp | 5 +---- src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp | 3 ++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp index 142870501..c6a5012bd 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp @@ -65,7 +65,7 @@ LatteTextureMtl::LatteTextureMtl(class MetalRenderer* mtlRenderer, Latte::E_DIM } else if (textureType == MTL::TextureTypeCube) { - // Do notjing + // Do nothing } else if (textureType == MTL::TextureTypeCubeArray) { @@ -81,13 +81,10 @@ LatteTextureMtl::LatteTextureMtl(class MetalRenderer* mtlRenderer, Latte::E_DIM MTL::TextureUsage usage = MTL::TextureUsageShaderRead | MTL::TextureUsagePixelFormatView; if (!Latte::IsCompressedFormat(format)) - { usage |= MTL::TextureUsageRenderTarget; - } desc->setUsage(usage); m_texture = mtlRenderer->GetDevice()->newTexture(desc); - desc->release(); } diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp index 2b420e6e2..e560c2c33 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalRenderer.cpp @@ -23,7 +23,6 @@ #include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h" #include "Cafe/HW/Latte/Renderer/Metal/MetalLayerHandle.h" #include "Cafe/HW/Latte/Renderer/Renderer.h" -#include "HW/Latte/Renderer/Metal/MetalAttachmentsInfo.h" #include "config/CemuConfig.h" #define IMGUI_IMPL_METAL_CPP @@ -70,6 +69,7 @@ MetalRenderer::MetalRenderer() MTL::TextureDescriptor* textureDescriptor = MTL::TextureDescriptor::alloc()->init(); textureDescriptor->setTextureType(MTL::TextureType1D); textureDescriptor->setWidth(1); + textureDescriptor->setUsage(MTL::TextureUsageShaderRead); m_nullTexture1D = m_device->newTexture(textureDescriptor); #ifdef CEMU_DEBUG_ASSERT m_nullTexture1D->setLabel(GetLabel("Null texture 1D", m_nullTexture1D)); @@ -77,6 +77,7 @@ MetalRenderer::MetalRenderer() textureDescriptor->setTextureType(MTL::TextureType2D); textureDescriptor->setHeight(1); + textureDescriptor->setUsage(MTL::TextureUsageShaderRead | MTL::TextureUsageRenderTarget); m_nullTexture2D = m_device->newTexture(textureDescriptor); #ifdef CEMU_DEBUG_ASSERT m_nullTexture2D->setLabel(GetLabel("Null texture 2D", m_nullTexture2D)); From d184f045139892f2ae5a5995cdbf44bcc7a2d07c Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 24 Oct 2024 17:37:25 +0200 Subject: [PATCH 3/5] archive cached pipelines if needed --- .../Renderer/Metal/MetalBinaryArchive.cpp | 54 ++++++++++++------- .../Latte/Renderer/Metal/MetalBinaryArchive.h | 6 ++- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp index 93fdfda3f..9cedc7259 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp @@ -30,7 +30,7 @@ MetalBinaryArchive::MetalBinaryArchive(class MetalRenderer* metalRenderer) : m_m MetalBinaryArchive::~MetalBinaryArchive() { - SerializeOldArchive(); + SerializeOldSaveArchive(); // TODO: combine new archives into a single one } @@ -49,7 +49,7 @@ void MetalBinaryArchive::LoadSerializedArchive() if (!std::filesystem::exists(m_finalArchivePath)) { - // TODO: create a save archive + CreateSaveArchive(); return; } @@ -80,18 +80,24 @@ void MetalBinaryArchive::CloseSerializedArchive() if (m_loadArchiveArray) m_loadArchiveArray->release(); + if (m_saveArchive) + { + SerializeArchive(m_saveArchive, m_finalArchivePath); + m_saveArchive = nullptr; + } + m_isLoading = false; } void MetalBinaryArchive::LoadPipeline(MTL::RenderPipelineDescriptor* renderPipelineDescriptor) { - if (m_isLoading) + if (m_loadArchiveArray) renderPipelineDescriptor->setBinaryArchives(m_loadArchiveArray); } // TODO: should be available since macOS 15.0 /* void MetalBinaryArchive::LoadPipeline(MTL::MeshRenderPipelineDescriptor* renderPipelineDescriptor) { - if (m_isLoading) + if (m_loadArchiveArray) renderPipelineDescriptor->setBinaryArchives(m_loadArchiveArray); } */ @@ -131,35 +137,33 @@ void MetalBinaryArchive::SavePipeline(MTL::MeshRenderPipelineDescriptor* renderP } */ -void MetalBinaryArchive::SerializeOldArchive() +void MetalBinaryArchive::SerializeArchive(MTL::BinaryArchive* archive, const fs::path& path) { - if (!m_saveArchive) - return; - NS::Error* error = nullptr; - NS::URL* url = ToNSURL(GetTmpArchivePath(m_archiveIndex)); - m_saveArchive->serializeToURL(url, &error); + NS::URL* url = ToNSURL(path); + archive->serializeToURL(url, &error); url->release(); if (error) { cemuLog_log(LogType::Force, "failed to serialize binary archive: {}", error->localizedDescription()->utf8String()); error->release(); } - m_saveArchive->release(); - - m_archiveIndex++; - m_pipelinesSerialized = 0; + archive->release(); } -void MetalBinaryArchive::LoadSaveArchive() +void MetalBinaryArchive::SerializeOldSaveArchive() { - if (m_isLoading || (m_saveArchive && m_pipelinesSerialized < SERIALIZE_TRESHOLD)) + if (!m_saveArchive) return; - // First, save the old archive to disk - SerializeOldArchive(); + SerializeArchive(m_saveArchive, GetTmpArchivePath(m_archiveIndex)); - // Create a new archive + m_archiveIndex++; + m_pipelinesSerialized = 0; +} + +void MetalBinaryArchive::CreateSaveArchive() +{ MTL::BinaryArchiveDescriptor* desc = MTL::BinaryArchiveDescriptor::alloc()->init(); NS::Error* error = nullptr; @@ -171,3 +175,15 @@ void MetalBinaryArchive::LoadSaveArchive() } desc->release(); } + +void MetalBinaryArchive::LoadSaveArchive() +{ + if (m_isLoading || (m_saveArchive && m_pipelinesSerialized < SERIALIZE_TRESHOLD)) + return; + + // First, save the old archive to disk + SerializeOldSaveArchive(); + + // Create a new archive + CreateSaveArchive(); +} diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h index a8136edd3..4520e3954 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h @@ -1,6 +1,7 @@ #pragma once #include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h" +#include "Metal/MTLBinaryArchive.hpp" class MetalBinaryArchive { @@ -41,8 +42,9 @@ class MetalBinaryArchive uint32 m_pipelinesSerialized = 0; uint32 m_archiveIndex = 0; - void SerializeOldArchive(); - void createSaveArchive(); + void SerializeArchive(MTL::BinaryArchive* archive, const fs::path& path); + void SerializeOldSaveArchive(); + void CreateSaveArchive(); void LoadSaveArchive(); fs::path GetTmpArchivePath(uint32 index) From c2c2fcfdf23395c93a4bf3727bf9cd3b4050dd89 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 24 Oct 2024 17:45:12 +0200 Subject: [PATCH 4/5] handle multithreading currectly with archives --- src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp | 6 ++++++ src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp index 9cedc7259..138f6cdcc 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp @@ -80,11 +80,13 @@ void MetalBinaryArchive::CloseSerializedArchive() if (m_loadArchiveArray) m_loadArchiveArray->release(); + m_lock.lock(); if (m_saveArchive) { SerializeArchive(m_saveArchive, m_finalArchivePath); m_saveArchive = nullptr; } + m_lock.unlock(); m_isLoading = false; } @@ -103,6 +105,8 @@ void MetalBinaryArchive::LoadPipeline(MTL::MeshRenderPipelineDescriptor* renderP */ void MetalBinaryArchive::SavePipeline(MTL::RenderPipelineDescriptor* renderPipelineDescriptor) { + m_lock.lock(); + LoadSaveArchive(); if (m_saveArchive) @@ -116,6 +120,8 @@ void MetalBinaryArchive::SavePipeline(MTL::RenderPipelineDescriptor* renderPipel } m_pipelinesSerialized++; } + + m_lock.unlock(); } // TODO: should be available since macOS 15.0 diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h index 4520e3954..90965d18d 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.h @@ -1,7 +1,7 @@ #pragma once #include "Cafe/HW/Latte/Renderer/Metal/MetalCommon.h" -#include "Metal/MTLBinaryArchive.hpp" +#include "util/helpers/fspinlock.h" class MetalBinaryArchive { @@ -42,6 +42,8 @@ class MetalBinaryArchive uint32 m_pipelinesSerialized = 0; uint32 m_archiveIndex = 0; + FSpinlock m_lock; + void SerializeArchive(MTL::BinaryArchive* archive, const fs::path& path); void SerializeOldSaveArchive(); void CreateSaveArchive(); From 07727dba15296dbc6aa6a10740a5feb82719ab81 Mon Sep 17 00:00:00 2001 From: Samuliak Date: Thu, 24 Oct 2024 18:05:39 +0200 Subject: [PATCH 5/5] set load archive to nullptr after releasing --- src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp index 138f6cdcc..6ff9edc4f 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalBinaryArchive.cpp @@ -76,9 +76,15 @@ void MetalBinaryArchive::LoadSerializedArchive() void MetalBinaryArchive::CloseSerializedArchive() { if (m_loadArchive) + { m_loadArchive->release(); + m_loadArchive = nullptr; + } if (m_loadArchiveArray) + { m_loadArchiveArray->release(); + m_loadArchiveArray = nullptr; + } m_lock.lock(); if (m_saveArchive)