From 4bf562589565636382b81313c2de0482e45743c0 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 20 Jul 2017 15:25:31 +1000 Subject: D3D: Uber shader support --- Source/Core/VideoBackends/D3D/PixelShaderCache.cpp | 285 +++++++++++++++++---- 1 file changed, 242 insertions(+), 43 deletions(-) (limited to 'Source/Core/VideoBackends/D3D/PixelShaderCache.cpp') diff --git a/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp b/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp index 6bb02b890a..f173551da3 100644 --- a/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp +++ b/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp @@ -8,12 +8,15 @@ #include "Common/CommonTypes.h" #include "Common/FileUtil.h" #include "Common/LinearDiskCache.h" +#include "Common/MsgHandler.h" #include "Common/StringUtil.h" #include "Core/ConfigManager.h" +#include "Core/Host.h" #include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DShader.h" +#include "VideoBackends/D3D/D3DState.h" #include "VideoBackends/D3D/PixelShaderCache.h" #include "VideoCommon/Debugger.h" @@ -25,10 +28,15 @@ namespace DX11 { PixelShaderCache::PSCache PixelShaderCache::PixelShaders; +PixelShaderCache::UberPSCache PixelShaderCache::UberPixelShaders; const PixelShaderCache::PSCacheEntry* PixelShaderCache::last_entry; +const PixelShaderCache::PSCacheEntry* PixelShaderCache::last_uber_entry; PixelShaderUid PixelShaderCache::last_uid; +UberShader::PixelShaderUid PixelShaderCache::last_uber_uid; LinearDiskCache g_ps_disk_cache; +LinearDiskCache g_uber_ps_disk_cache; +extern std::unique_ptr g_async_compiler; ID3D11PixelShader* s_ColorMatrixProgram[2] = {nullptr}; ID3D11PixelShader* s_ColorCopyProgram[2] = {nullptr}; @@ -429,10 +437,8 @@ ID3D11PixelShader* PixelShaderCache::GetDepthResolveProgram() return s_DepthResolveProgram; } -ID3D11Buffer*& PixelShaderCache::GetConstantBuffer() +static void UpdateConstantBuffers() { - // TODO: divide the global variables of the generated shaders into about 5 constant buffers to - // speed this up if (PixelShaderManager::dirty) { D3D11_MAPPED_SUBRESOURCE map; @@ -443,14 +449,20 @@ ID3D11Buffer*& PixelShaderCache::GetConstantBuffer() ADDSTAT(stats.thisFrame.bytesUniformStreamed, sizeof(PixelShaderConstants)); } +} + +ID3D11Buffer* PixelShaderCache::GetConstantBuffer() +{ + UpdateConstantBuffers(); return pscbuf; } // this class will load the precompiled shaders into our cache -class PixelShaderCacheInserter : public LinearDiskCacheReader +template +class PixelShaderCacheInserter : public LinearDiskCacheReader { public: - void Read(const PixelShaderUid& key, const u8* value, u32 value_size) + void Read(const UidType& key, const u8* value, u32 value_size) { PixelShaderCache::InsertByteCode(key, value, value_size); } @@ -499,22 +511,34 @@ void PixelShaderCache::Init() if (g_ActiveConfig.bShaderCache) LoadShaderCache(); + + if (g_ActiveConfig.CanPrecompileUberShaders()) + PrecompileUberShaders(); } void PixelShaderCache::LoadShaderCache() { - PixelShaderCacheInserter inserter; + PixelShaderCacheInserter inserter; g_ps_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "PS", true, true), inserter); + + PixelShaderCacheInserter uber_inserter; + g_uber_ps_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "UberPS", false, true), + uber_inserter); } void PixelShaderCache::Reload() { g_ps_disk_cache.Sync(); g_ps_disk_cache.Close(); + g_uber_ps_disk_cache.Sync(); + g_uber_ps_disk_cache.Close(); Clear(); if (g_ActiveConfig.bShaderCache) LoadShaderCache(); + + if (g_ActiveConfig.CanPrecompileUberShaders()) + PrecompileUberShaders(); } // ONLY to be used during shutdown. @@ -522,10 +546,15 @@ void PixelShaderCache::Clear() { for (auto& iter : PixelShaders) iter.second.Destroy(); + for (auto& iter : UberPixelShaders) + iter.second.Destroy(); PixelShaders.clear(); + UberPixelShaders.clear(); last_entry = nullptr; + last_uber_entry = nullptr; last_uid = {}; + last_uber_uid = {}; } // Used in Swap() when AA mode has changed @@ -558,82 +587,252 @@ void PixelShaderCache::Shutdown() Clear(); g_ps_disk_cache.Sync(); g_ps_disk_cache.Close(); + g_uber_ps_disk_cache.Sync(); + g_uber_ps_disk_cache.Close(); } bool PixelShaderCache::SetShader() { - PixelShaderUid uid = GetPixelShaderUid(); - - // Check if the shader is already set - if (last_entry) + if (g_ActiveConfig.CanUseUberShaders() && + (g_ActiveConfig.bDisableSpecializedShaders || g_ActiveConfig.bForcePixelUberShaders)) { - if (uid == last_uid) - { - GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - return (last_entry->shader != nullptr); - } + return SetUberShader(); } - last_uid = uid; + PixelShaderUid uid = GetPixelShaderUid(); + if (last_entry && uid == last_uid) + { + if (last_entry->pending) + return SetUberShader(); + + if (!last_entry->shader) + return false; + + D3D::stateman->SetPixelShader(last_entry->shader); + return true; + } // Check if the shader is already in the cache - PSCache::iterator iter; - iter = PixelShaders.find(uid); + auto iter = PixelShaders.find(uid); if (iter != PixelShaders.end()) { const PSCacheEntry& entry = iter->second; + if (entry.pending) + return SetUberShader(); + + last_uid = uid; last_entry = &entry; GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - return (entry.shader != nullptr); + if (!last_entry->shader) + return false; + + D3D::stateman->SetPixelShader(last_entry->shader); + return true; + } + + // Background compiling? + if (g_ActiveConfig.CanBackgroundCompileShaders()) + { + // Create a pending entry + PSCacheEntry entry; + entry.pending = true; + PixelShaders[uid] = entry; + + // Queue normal shader compiling and use ubershader + g_async_compiler->QueueWorkItem( + g_async_compiler->CreateWorkItem(uid)); + return SetUberShader(); } // Need to compile a new shader + D3DBlob* bytecode = nullptr; ShaderCode code = GeneratePixelShaderCode(APIType::D3D, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - - D3DBlob* pbytecode; - if (!D3D::CompilePixelShader(code.GetBuffer(), &pbytecode)) + D3D::CompilePixelShader(code.GetBuffer(), &bytecode); + if (!InsertByteCode(uid, bytecode->Data(), bytecode->Size())) { - GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true); + SAFE_RELEASE(bytecode); return false; } - // Insert the bytecode into the caches - g_ps_disk_cache.Append(uid, pbytecode->Data(), pbytecode->Size()); + g_ps_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); + return SetShader(); +} - bool success = InsertByteCode(uid, pbytecode->Data(), pbytecode->Size()); - pbytecode->Release(); +bool PixelShaderCache::SetUberShader() +{ + UberShader::PixelShaderUid uid = UberShader::GetPixelShaderUid(); - GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - return success; + if (last_uber_entry && last_uber_uid == uid) + { + if (!last_uber_entry->shader) + return false; + + D3D::stateman->SetPixelShader(last_uber_entry->shader); + return true; + } + + auto iter = UberPixelShaders.find(uid); + if (iter != UberPixelShaders.end()) + { + const PSCacheEntry& entry = iter->second; + last_uber_uid = uid; + last_uber_entry = &entry; + + GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); + if (!last_uber_entry->shader) + return false; + + D3D::stateman->SetPixelShader(last_uber_entry->shader); + return true; + } + + D3DBlob* bytecode = nullptr; + ShaderCode code = + UberShader::GenPixelShader(APIType::D3D, ShaderHostConfig::GetCurrent(), uid.GetUidData()); + D3D::CompilePixelShader(code.GetBuffer(), &bytecode); + if (!InsertByteCode(uid, bytecode->Data(), bytecode->Size())) + { + SAFE_RELEASE(bytecode); + return false; + } + + // Lookup map again. + g_uber_ps_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); + bytecode->Release(); + return SetUberShader(); } -bool PixelShaderCache::InsertByteCode(const PixelShaderUid& uid, const void* bytecode, - unsigned int bytecodelen) +bool PixelShaderCache::InsertByteCode(const PixelShaderUid& uid, const u8* data, size_t len) { - ID3D11PixelShader* shader = D3D::CreatePixelShaderFromByteCode(bytecode, bytecodelen); - if (shader == nullptr) + ID3D11PixelShader* shader = data ? D3D::CreatePixelShaderFromByteCode(data, len) : nullptr; + if (!InsertShader(uid, shader)) + { + SAFE_RELEASE(shader); return false; + } - // TODO: Somehow make the debug name a bit more specific - D3D::SetDebugObjectName((ID3D11DeviceChild*)shader, "a pixel shader of PixelShaderCache"); - - // Make an entry in the table - PSCacheEntry newentry; - newentry.shader = shader; - PixelShaders[uid] = newentry; - last_entry = &PixelShaders[uid]; + return true; +} - if (!shader) +bool PixelShaderCache::InsertByteCode(const UberShader::PixelShaderUid& uid, const u8* data, + size_t len) +{ + ID3D11PixelShader* shader = data ? D3D::CreatePixelShaderFromByteCode(data, len) : nullptr; + if (!InsertShader(uid, shader)) { - // INCSTAT(stats.numPixelShadersFailed); + SAFE_RELEASE(shader); return false; } + return true; +} + +bool PixelShaderCache::InsertShader(const PixelShaderUid& uid, ID3D11PixelShader* shader) +{ + auto iter = PixelShaders.find(uid); + if (iter != PixelShaders.end() && !iter->second.pending) + return false; + + PSCacheEntry& newentry = PixelShaders[uid]; + newentry.pending = false; + newentry.shader = shader; + INCSTAT(stats.numPixelShadersCreated); SETSTAT(stats.numPixelShadersAlive, PixelShaders.size()); + return (shader != nullptr); +} + +bool PixelShaderCache::InsertShader(const UberShader::PixelShaderUid& uid, + ID3D11PixelShader* shader) +{ + auto iter = UberPixelShaders.find(uid); + if (iter != UberPixelShaders.end() && !iter->second.pending) + return false; + + PSCacheEntry& newentry = UberPixelShaders[uid]; + newentry.pending = false; + newentry.shader = shader; + return (shader != nullptr); +} + +void PixelShaderCache::PrecompileUberShaders() +{ + UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& uid) { + if (UberPixelShaders.find(uid) != UberPixelShaders.end()) + return; + + g_async_compiler->QueueWorkItem( + g_async_compiler->CreateWorkItem(uid)); + }); + + g_async_compiler->WaitUntilCompletion([](size_t completed, size_t total) { + Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(), + static_cast(completed), static_cast(total)); + }); + g_async_compiler->RetrieveWorkItems(); + Host_UpdateProgressDialog("", -1, -1); +} + +PixelShaderCache::PixelShaderCompilerWorkItem::PixelShaderCompilerWorkItem( + const PixelShaderUid& uid) +{ + std::memcpy(&m_uid, &uid, sizeof(uid)); +} + +PixelShaderCache::PixelShaderCompilerWorkItem::~PixelShaderCompilerWorkItem() +{ + SAFE_RELEASE(m_bytecode); +} + +bool PixelShaderCache::PixelShaderCompilerWorkItem::Compile() +{ + ShaderCode code = + GeneratePixelShaderCode(APIType::D3D, ShaderHostConfig::GetCurrent(), m_uid.GetUidData()); + + if (D3D::CompilePixelShader(code.GetBuffer(), &m_bytecode)) + m_shader = D3D::CreatePixelShaderFromByteCode(m_bytecode); + + return true; +} + +void PixelShaderCache::PixelShaderCompilerWorkItem::Retrieve() +{ + if (InsertShader(m_uid, m_shader)) + g_ps_disk_cache.Append(m_uid, m_bytecode->Data(), m_bytecode->Size()); + else + SAFE_RELEASE(m_shader); +} + +PixelShaderCache::UberPixelShaderCompilerWorkItem::UberPixelShaderCompilerWorkItem( + const UberShader::PixelShaderUid& uid) +{ + std::memcpy(&m_uid, &uid, sizeof(uid)); +} + +PixelShaderCache::UberPixelShaderCompilerWorkItem::~UberPixelShaderCompilerWorkItem() +{ + SAFE_RELEASE(m_bytecode); +} + +bool PixelShaderCache::UberPixelShaderCompilerWorkItem::Compile() +{ + ShaderCode code = + UberShader::GenPixelShader(APIType::D3D, ShaderHostConfig::GetCurrent(), m_uid.GetUidData()); + + if (D3D::CompilePixelShader(code.GetBuffer(), &m_bytecode)) + m_shader = D3D::CreatePixelShaderFromByteCode(m_bytecode); + return true; } +void PixelShaderCache::UberPixelShaderCompilerWorkItem::Retrieve() +{ + if (InsertShader(m_uid, m_shader)) + g_uber_ps_disk_cache.Append(m_uid, m_bytecode->Data(), m_bytecode->Size()); + else + SAFE_RELEASE(m_shader); +} + } // DX11 -- cgit v1.2.3 From c8f31656cb1c5f5e3c83a5b977fd822e987cb596 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 27 Jul 2017 13:15:38 +1000 Subject: VideoBackends: Support a different number of threads for precompiling At runtime, we only really want a single shader compiler thread. However, for initial boots, we can use a higher number to speed things up. --- Source/Core/VideoBackends/D3D/PixelShaderCache.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Source/Core/VideoBackends/D3D/PixelShaderCache.cpp') diff --git a/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp b/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp index f173551da3..69dedb4d4b 100644 --- a/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp +++ b/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp @@ -513,7 +513,7 @@ void PixelShaderCache::Init() LoadShaderCache(); if (g_ActiveConfig.CanPrecompileUberShaders()) - PrecompileUberShaders(); + QueueUberShaderCompiles(); } void PixelShaderCache::LoadShaderCache() @@ -538,7 +538,7 @@ void PixelShaderCache::Reload() LoadShaderCache(); if (g_ActiveConfig.CanPrecompileUberShaders()) - PrecompileUberShaders(); + QueueUberShaderCompiles(); } // ONLY to be used during shutdown. @@ -757,7 +757,7 @@ bool PixelShaderCache::InsertShader(const UberShader::PixelShaderUid& uid, return (shader != nullptr); } -void PixelShaderCache::PrecompileUberShaders() +void PixelShaderCache::QueueUberShaderCompiles() { UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& uid) { if (UberPixelShaders.find(uid) != UberPixelShaders.end()) -- cgit v1.2.3 From e968c191ffc7bf2899e8f56f39f988c36b54034c Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 27 Jul 2017 20:52:20 +1000 Subject: Ubershaders: Support per-pixel lighting --- Source/Core/VideoBackends/D3D/PixelShaderCache.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'Source/Core/VideoBackends/D3D/PixelShaderCache.cpp') diff --git a/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp b/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp index 69dedb4d4b..bb1aa04911 100644 --- a/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp +++ b/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp @@ -593,11 +593,8 @@ void PixelShaderCache::Shutdown() bool PixelShaderCache::SetShader() { - if (g_ActiveConfig.CanUseUberShaders() && - (g_ActiveConfig.bDisableSpecializedShaders || g_ActiveConfig.bForcePixelUberShaders)) - { + if (g_ActiveConfig.bDisableSpecializedShaders || g_ActiveConfig.bForcePixelUberShaders) return SetUberShader(); - } PixelShaderUid uid = GetPixelShaderUid(); if (last_entry && uid == last_uid) -- cgit v1.2.3