diff --git a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp index f3bd14b9..3c0005ef 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/LatteTextureMtl.cpp @@ -2,9 +2,6 @@ #include "Cafe/HW/Latte/Renderer/Metal/LatteTextureViewMtl.h" #include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.h" #include "Cafe/HW/Latte/Renderer/Metal/LatteToMtl.h" -#include "Common/precompiled.h" -#include "Metal/MTLResource.hpp" -#include "Metal/MTLTexture.hpp" LatteTextureMtl::LatteTextureMtl(class MetalRenderer* mtlRenderer, Latte::E_DIM dim, MPTR physAddress, MPTR physMipAddress, Latte::E_GX2SURFFMT format, uint32 width, uint32 height, uint32 depth, uint32 pitch, uint32 mipLevels, uint32 swizzle, Latte::E_HWTILEMODE tileMode, bool isDepth) @@ -12,7 +9,7 @@ LatteTextureMtl::LatteTextureMtl(class MetalRenderer* mtlRenderer, Latte::E_DIM { MTL::TextureDescriptor* desc = MTL::TextureDescriptor::alloc()->init(); desc->setStorageMode(MTL::StorageModePrivate); - desc->setCpuCacheMode(MTL::CPUCacheModeWriteCombined); + //desc->setCpuCacheMode(MTL::CPUCacheModeWriteCombined); sint32 effectiveBaseWidth = width; sint32 effectiveBaseHeight = height; diff --git a/src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h b/src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h index 952fd1de..f3dd1733 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/MetalCommon.h @@ -101,3 +101,11 @@ inline bool FormatIsRenderable(Latte::E_GX2SURFFMT format) { return !Latte::IsCompressedFormat(format); } + +template +inline void executeCommand(fmt::format_string fmt, T&&... args) { + std::string command = fmt::format(fmt, std::forward(args)...); + int res = system(command.c_str()); + if (res != 0) + cemuLog_log(LogType::Force, "command \"{}\" failed with exit code {}", command, res); +} diff --git a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp index c4492e3c..dc1256a4 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp +++ b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.cpp @@ -2,14 +2,21 @@ #include "Cafe/HW/Latte/Renderer/Metal/MetalRenderer.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" #include "GameProfile/GameProfile.h" #include "util/helpers/helpers.h" +#define METAL_AIR_CACHE_NAME "Cemu_AIR_cache" +#define METAL_AIR_CACHE_PATH "/Volumes/" METAL_AIR_CACHE_NAME +#define METAL_AIR_CACHE_SIZE (512 * 1024 * 1024) +#define METAL_AIR_CACHE_BLOCK_COUNT (METAL_AIR_CACHE_SIZE / 512) + static bool s_isLoadingShadersMtl{false}; +static std::atomic s_hasRAMFilesystem{false}; +class FileCache* s_airCache{nullptr}; extern std::atomic_int g_compiled_shaders_total; extern std::atomic_int g_compiled_shaders_async; @@ -88,12 +95,44 @@ private: // TODO: find out if it would be possible to cache compiled Metal shaders void RendererShaderMtl::ShaderCacheLoading_begin(uint64 cacheTitleId) { + s_isLoadingShadersMtl = true; + + // Open AIR cache + if (s_airCache) + { + delete s_airCache; + s_airCache = nullptr; + } + uint32 airCacheMagic = GeneratePrecompiledCacheId(); + const std::string cacheFilename = fmt::format("{:016x}_air.bin", cacheTitleId); + const fs::path cachePath = ActiveSettings::GetCachePath("shaderCache/precompiled/{}", cacheFilename); + s_airCache = FileCache::Open(cachePath, true, airCacheMagic); + if (!s_airCache) + cemuLog_log(LogType::Force, "Unable to open AIR cache {}", cacheFilename); + // Maximize shader compilation speed static_cast(g_renderer.get())->SetShouldMaximizeConcurrentCompilation(true); } void RendererShaderMtl::ShaderCacheLoading_end() { + s_isLoadingShadersMtl = false; + + // Close the AIR cache + if (s_airCache) + { + delete s_airCache; + s_airCache = nullptr; + } + + // 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); } @@ -174,6 +213,49 @@ bool RendererShaderMtl::ShouldCountCompilation() const void RendererShaderMtl::CompileInternal() { + // First, try to retrieve the compiled shader from the AIR cache + if (s_isLoadingShadersMtl && (m_isGameShader && !m_isGfxPackShader) && s_airCache) + { + cemu_assert_debug(m_baseHash != 0); + uint64 h1, h2; + GenerateShaderPrecompiledCacheFilename(m_type, m_baseHash, m_auxHash, h1, h2); + std::vector cacheFileData; + if (s_airCache->GetFile({ h1, h2 }, cacheFileData)) + { + CompileFromAIR(std::span(cacheFileData.data(), cacheFileData.size())); + FinishCompilation(); + } + else + { + // Ensure that RAM filesystem exists + if (!s_hasRAMFilesystem) + { + s_hasRAMFilesystem = true; + executeCommand("diskutil erasevolume HFS+ {} $(hdiutil attach -nomount ram://{})", METAL_AIR_CACHE_NAME, METAL_AIR_CACHE_BLOCK_COUNT); + } + + // The shader is not in the cache, compile it + std::string filename = fmt::format("{}_{}", h1, h2); + // TODO: store the source + executeCommand("xcrun -sdk macosx metal -o {}.ir -c {}.metal", filename, filename); + executeCommand("xcrun -sdk macosx metallib -o {}.metallib {}.ir", filename, filename); + // TODO: clean up + + // Load from the newly Generated AIR + // std::span airData = ; + //CompileFromAIR(std::span((uint8*)cacheFileData.data(), cacheFileData.size() / sizeof(uint8))); + FinishCompilation(); + + // 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()); + } + + return; + } + + // Compile from source MTL::CompileOptions* options = MTL::CompileOptions::alloc()->init(); // TODO: always disable fast math for problematic shaders if (g_current_game_profile->GetFastMath()) @@ -200,6 +282,12 @@ void RendererShaderMtl::CompileInternal() g_compiled_shaders_total++; } +void RendererShaderMtl::CompileFromAIR(std::span data) +{ + // TODO: implement this + printf("LOADING SHADER FROM AIR CACHE\n"); +} + 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 40d04c87..98d18687 100644 --- a/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h +++ b/src/Cafe/HW/Latte/Renderer/Metal/RendererShaderMtl.h @@ -69,5 +69,7 @@ private: void CompileInternal(); + void CompileFromAIR(std::span data); + void FinishCompilation(); };