mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-05 22:41:18 +12:00
compile shaders to AIR at runtime
This commit is contained in:
parent
5af904b5e2
commit
0b1932c206
2 changed files with 101 additions and 73 deletions
|
@ -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();
|
||||
|
|
|
@ -73,5 +73,7 @@ private:
|
|||
|
||||
void CompileInternal();
|
||||
|
||||
void CompileToAIR();
|
||||
|
||||
void FinishCompilation();
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue