compile shaders to AIR at runtime

This commit is contained in:
Samuliak 2025-01-16 15:53:51 +01:00
parent 5af904b5e2
commit 0b1932c206
No known key found for this signature in database
2 changed files with 101 additions and 73 deletions

View file

@ -15,7 +15,7 @@
#define METAL_AIR_CACHE_BLOCK_COUNT (METAL_AIR_CACHE_SIZE / 512)
static bool s_isLoadingShadersMtl{false};
static std::atomic<bool> 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<std::thread> s_threads;
std::thread* s_airCacheThread{nullptr};
std::deque<RendererShaderMtl*> s_compilationQueue;
CounterSemaphore s_compilationQueueCount;
std::mutex s_compilationQueueMutex;
std::deque<RendererShaderMtl*> s_airCacheQueue;
CounterSemaphore s_airCacheQueueCount;
std::mutex s_airCacheQueueMutex;
private:
std::atomic<bool> m_threadsActive;
} shaderMtlThreadPool;
@ -118,6 +158,12 @@ void RendererShaderMtl::ShaderCacheLoading_end()
{
s_isLoadingShadersMtl = false;
// Reset shader compilation speed
static_cast<MetalRenderer*>(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<MetalRenderer*>(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<uint8> 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<uint8> 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<uint8>(cacheFileData.data(), cacheFileData.size()));
}
else
{
// Ensure that RAM filesystem exists
static std::atomic<bool> 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<uint8> airData = std::span<uint8>(airFile.data(), airFile.size());
library = LibraryFromAIR(std::span<uint8>(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<uint8> airData = std::span<uint8>(airFile.data(), airFile.size());
//library = LibraryFromAIR(std::span<uint8>(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();

View file

@ -73,5 +73,7 @@ private:
void CompileInternal();
void CompileToAIR();
void FinishCompilation();
};