diff --git a/Source/Core/VideoBackends/D3D/D3DBase.cpp b/Source/Core/VideoBackends/D3D/D3DBase.cpp index 60475988ce..cab8c628e6 100644 --- a/Source/Core/VideoBackends/D3D/D3DBase.cpp +++ b/Source/Core/VideoBackends/D3D/D3DBase.cpp @@ -185,10 +185,9 @@ std::vector EnumAAModes(IDXGIAdapter* adapter) ID3D11Device* _device; ID3D11DeviceContext* _context; D3D_FEATURE_LEVEL feat_level; - HRESULT hr = PD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, - D3D11_CREATE_DEVICE_SINGLETHREADED, supported_feature_levels, - NUM_SUPPORTED_FEATURE_LEVELS, D3D11_SDK_VERSION, &_device, - &feat_level, &_context); + HRESULT hr = PD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, + supported_feature_levels, NUM_SUPPORTED_FEATURE_LEVELS, + D3D11_SDK_VERSION, &_device, &feat_level, &_context); if (FAILED(hr) || feat_level == D3D_FEATURE_LEVEL_10_0) { DXGI_SAMPLE_DESC desc; @@ -221,9 +220,9 @@ std::vector EnumAAModes(IDXGIAdapter* adapter) D3D_FEATURE_LEVEL GetFeatureLevel(IDXGIAdapter* adapter) { D3D_FEATURE_LEVEL feat_level = D3D_FEATURE_LEVEL_9_1; - PD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_SINGLETHREADED, - supported_feature_levels, NUM_SUPPORTED_FEATURE_LEVELS, D3D11_SDK_VERSION, - nullptr, &feat_level, nullptr); + PD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, supported_feature_levels, + NUM_SUPPORTED_FEATURE_LEVELS, D3D11_SDK_VERSION, nullptr, &feat_level, + nullptr); return feat_level; } @@ -311,8 +310,7 @@ HRESULT Create(HWND wnd) // Creating debug devices can sometimes fail if the user doesn't have the correct // version of the DirectX SDK. If it does, simply fallback to a non-debug device. { - hr = PD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, - D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_DEBUG, + hr = PD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, D3D11_CREATE_DEVICE_DEBUG, supported_feature_levels, NUM_SUPPORTED_FEATURE_LEVELS, D3D11_SDK_VERSION, &device, &featlevel, &context); @@ -339,8 +337,7 @@ HRESULT Create(HWND wnd) if (FAILED(hr)) #endif { - hr = PD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, - D3D11_CREATE_DEVICE_SINGLETHREADED, supported_feature_levels, + hr = PD3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, supported_feature_levels, NUM_SUPPORTED_FEATURE_LEVELS, D3D11_SDK_VERSION, &device, &featlevel, &context); } diff --git a/Source/Core/VideoBackends/D3D/D3DShader.cpp b/Source/Core/VideoBackends/D3D/D3DShader.cpp index cd9622b4a3..6afb1eb1cc 100644 --- a/Source/Core/VideoBackends/D3D/D3DShader.cpp +++ b/Source/Core/VideoBackends/D3D/D3DShader.cpp @@ -18,7 +18,7 @@ namespace DX11 namespace D3D { // bytecode->shader -ID3D11VertexShader* CreateVertexShaderFromByteCode(const void* bytecode, unsigned int len) +ID3D11VertexShader* CreateVertexShaderFromByteCode(const void* bytecode, size_t len) { ID3D11VertexShader* v_shader; HRESULT hr = D3D::device->CreateVertexShader(bytecode, len, nullptr, &v_shader); @@ -73,7 +73,7 @@ bool CompileVertexShader(const std::string& code, D3DBlob** blob) } // bytecode->shader -ID3D11GeometryShader* CreateGeometryShaderFromByteCode(const void* bytecode, unsigned int len) +ID3D11GeometryShader* CreateGeometryShaderFromByteCode(const void* bytecode, size_t len) { ID3D11GeometryShader* g_shader; HRESULT hr = D3D::device->CreateGeometryShader(bytecode, len, nullptr, &g_shader); @@ -131,7 +131,7 @@ bool CompileGeometryShader(const std::string& code, D3DBlob** blob, } // bytecode->shader -ID3D11PixelShader* CreatePixelShaderFromByteCode(const void* bytecode, unsigned int len) +ID3D11PixelShader* CreatePixelShaderFromByteCode(const void* bytecode, size_t len) { ID3D11PixelShader* p_shader; HRESULT hr = D3D::device->CreatePixelShader(bytecode, len, nullptr, &p_shader); diff --git a/Source/Core/VideoBackends/D3D/D3DShader.h b/Source/Core/VideoBackends/D3D/D3DShader.h index 0b417d9885..751c854015 100644 --- a/Source/Core/VideoBackends/D3D/D3DShader.h +++ b/Source/Core/VideoBackends/D3D/D3DShader.h @@ -16,9 +16,9 @@ namespace DX11 { namespace D3D { -ID3D11VertexShader* CreateVertexShaderFromByteCode(const void* bytecode, unsigned int len); -ID3D11GeometryShader* CreateGeometryShaderFromByteCode(const void* bytecode, unsigned int len); -ID3D11PixelShader* CreatePixelShaderFromByteCode(const void* bytecode, unsigned int len); +ID3D11VertexShader* CreateVertexShaderFromByteCode(const void* bytecode, size_t len); +ID3D11GeometryShader* CreateGeometryShaderFromByteCode(const void* bytecode, size_t len); +ID3D11PixelShader* CreatePixelShaderFromByteCode(const void* bytecode, size_t len); // The returned bytecode buffers should be Release()d. bool CompileVertexShader(const std::string& code, D3DBlob** blob); diff --git a/Source/Core/VideoBackends/D3D/D3DState.cpp b/Source/Core/VideoBackends/D3D/D3DState.cpp index 09d8dec3e2..f12359e978 100644 --- a/Source/Core/VideoBackends/D3D/D3DState.cpp +++ b/Source/Core/VideoBackends/D3D/D3DState.cpp @@ -136,7 +136,7 @@ void StateManager::Apply() m_current.pixelConstants[1] != m_pending.pixelConstants[1]) { D3D::context->PSSetConstantBuffers(0, m_pending.pixelConstants[1] ? 2 : 1, - m_pending.pixelConstants); + m_pending.pixelConstants.data()); m_current.pixelConstants[0] = m_pending.pixelConstants[0]; m_current.pixelConstants[1] = m_pending.pixelConstants[1]; } diff --git a/Source/Core/VideoBackends/D3D/D3DState.h b/Source/Core/VideoBackends/D3D/D3DState.h index bf86cf025a..f09bb22bb2 100644 --- a/Source/Core/VideoBackends/D3D/D3DState.h +++ b/Source/Core/VideoBackends/D3D/D3DState.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -269,9 +270,9 @@ private: struct Resources { - ID3D11ShaderResourceView* textures[8]; - ID3D11SamplerState* samplers[8]; - ID3D11Buffer* pixelConstants[2]; + std::array textures; + std::array samplers; + std::array pixelConstants; ID3D11Buffer* vertexConstants; ID3D11Buffer* geometryConstants; ID3D11Buffer* vertexBuffer; diff --git a/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp b/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp index f6fd869b8c..f6d0599ac3 100644 --- a/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp +++ b/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp @@ -13,6 +13,7 @@ #include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DShader.h" +#include "VideoBackends/D3D/D3DState.h" #include "VideoBackends/D3D/FramebufferManager.h" #include "VideoBackends/D3D/GeometryShaderCache.h" @@ -159,6 +160,9 @@ void GeometryShaderCache::Init() if (g_ActiveConfig.bShaderCache) LoadShaderCache(); + + if (g_ActiveConfig.CanPrecompileUberShaders()) + PrecompileShaders(); } void GeometryShaderCache::LoadShaderCache() @@ -175,6 +179,9 @@ void GeometryShaderCache::Reload() if (g_ActiveConfig.bShaderCache) LoadShaderCache(); + + if (g_ActiveConfig.CanPrecompileUberShaders()) + PrecompileShaders(); } // ONLY to be used during shutdown. @@ -203,78 +210,74 @@ void GeometryShaderCache::Shutdown() bool GeometryShaderCache::SetShader(u32 primitive_type) { GeometryShaderUid uid = GetGeometryShaderUid(primitive_type); - - // Check if the shader is already set - if (last_entry) + if (last_entry && uid == last_uid) { - if (uid == last_uid) - { - GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - return true; - } + GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); + D3D::stateman->SetGeometryShader(last_entry->shader); + return true; } - last_uid = uid; - // Check if the shader is a pass-through shader if (uid.GetUidData()->IsPassthrough()) { // Return the default pass-through shader + last_uid = uid; last_entry = &pass_entry; + D3D::stateman->SetGeometryShader(last_entry->shader); return true; } // Check if the shader is already in the cache - GSCache::iterator iter; - iter = GeometryShaders.find(uid); + auto iter = GeometryShaders.find(uid); if (iter != GeometryShaders.end()) { const GSCacheEntry& entry = iter->second; + last_uid = uid; last_entry = &entry; - + D3D::stateman->SetGeometryShader(last_entry->shader); return (entry.shader != nullptr); } // Need to compile a new shader + if (CompileShader(uid)) + return SetShader(primitive_type); + else + return false; +} + +bool GeometryShaderCache::CompileShader(const GeometryShaderUid& uid) +{ + D3DBlob* bytecode; ShaderCode code = GenerateGeometryShaderCode(APIType::D3D, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - - D3DBlob* pbytecode; - if (!D3D::CompileGeometryShader(code.GetBuffer(), &pbytecode)) + if (!D3D::CompileGeometryShader(code.GetBuffer(), &bytecode) || + !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_gs_disk_cache.Append(uid, pbytecode->Data(), pbytecode->Size()); - - bool success = InsertByteCode(uid, pbytecode->Data(), pbytecode->Size()); - pbytecode->Release(); - - return success; -} - -bool GeometryShaderCache::InsertByteCode(const GeometryShaderUid& uid, const void* bytecode, - unsigned int bytecodelen) -{ - ID3D11GeometryShader* shader = D3D::CreateGeometryShaderFromByteCode(bytecode, bytecodelen); - if (shader == nullptr) - return false; - - // TODO: Somehow make the debug name a bit more specific - D3D::SetDebugObjectName((ID3D11DeviceChild*)shader, "a pixel shader of GeometryShaderCache"); - - // Make an entry in the table - GSCacheEntry newentry; - newentry.shader = shader; - GeometryShaders[uid] = newentry; - last_entry = &GeometryShaders[uid]; - - if (!shader) - return false; - + g_gs_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); return true; } +bool GeometryShaderCache::InsertByteCode(const GeometryShaderUid& uid, const u8* bytecode, + size_t len) +{ + GSCacheEntry& newentry = GeometryShaders[uid]; + newentry.shader = bytecode ? D3D::CreateGeometryShaderFromByteCode(bytecode, len) : nullptr; + return newentry.shader != nullptr; +} + +void GeometryShaderCache::PrecompileShaders() +{ + EnumerateGeometryShaderUids([](const GeometryShaderUid& uid) { + if (GeometryShaders.find(uid) != GeometryShaders.end()) + return; + + CompileShader(uid); + }); +} + } // DX11 diff --git a/Source/Core/VideoBackends/D3D/GeometryShaderCache.h b/Source/Core/VideoBackends/D3D/GeometryShaderCache.h index e6eca3b2e0..6bebf0a2a4 100644 --- a/Source/Core/VideoBackends/D3D/GeometryShaderCache.h +++ b/Source/Core/VideoBackends/D3D/GeometryShaderCache.h @@ -18,14 +18,14 @@ public: static void Reload(); static void Clear(); static void Shutdown(); - static bool SetShader(u32 primitive_type); // TODO: Should be renamed to LoadShader - static bool InsertByteCode(const GeometryShaderUid& uid, const void* bytecode, - unsigned int bytecodelen); + static bool SetShader(u32 primitive_type); + static bool CompileShader(const GeometryShaderUid& uid); + static bool InsertByteCode(const GeometryShaderUid& uid, const u8* bytecode, size_t len); + static void PrecompileShaders(); static ID3D11GeometryShader* GetClearGeometryShader(); static ID3D11GeometryShader* GetCopyGeometryShader(); - static ID3D11GeometryShader* GetActiveShader() { return last_entry->shader; } static ID3D11Buffer*& GetConstantBuffer(); private: diff --git a/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp b/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp index 01658b66e6..2e0df19808 100644 --- a/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp +++ b/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp @@ -13,20 +13,6 @@ namespace DX11 { -class D3DVertexFormat : public NativeVertexFormat -{ -public: - D3DVertexFormat(const PortableVertexDeclaration& vtx_decl); - ~D3DVertexFormat() { SAFE_RELEASE(m_layout); } - void SetupVertexPointers() override; - -private: - std::array m_elems{}; - UINT m_num_elems = 0; - - ID3D11InputLayout* m_layout = nullptr; -}; - std::unique_ptr VertexManager::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) { @@ -66,7 +52,6 @@ D3DVertexFormat::D3DVertexFormat(const PortableVertexDeclaration& _vtx_decl) this->vtx_decl = _vtx_decl; const AttributeFormat* format = &_vtx_decl.position; - if (format->enable) { m_elems[m_num_elems].SemanticName = "POSITION"; @@ -129,15 +114,22 @@ D3DVertexFormat::D3DVertexFormat(const PortableVertexDeclaration& _vtx_decl) } } +D3DVertexFormat::~D3DVertexFormat() +{ + SAFE_RELEASE(m_layout); +} + void D3DVertexFormat::SetupVertexPointers() +{ +} + +void D3DVertexFormat::SetInputLayout(D3DBlob* vs_bytecode) { if (!m_layout) { // CreateInputLayout requires a shader input, but it only looks at the // signature of the shader, so we don't need to recompute it if the shader // changes. - D3DBlob* vs_bytecode = DX11::VertexShaderCache::GetActiveShaderBytecode(); - HRESULT hr = DX11::D3D::device->CreateInputLayout( m_elems.data(), m_num_elems, vs_bytecode->Data(), vs_bytecode->Size(), &m_layout); if (FAILED(hr)) 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()); - - bool success = InsertByteCode(uid, pbytecode->Data(), pbytecode->Size()); - pbytecode->Release(); - - GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - return success; + g_ps_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); + return SetShader(); } -bool PixelShaderCache::InsertByteCode(const PixelShaderUid& uid, const void* bytecode, - unsigned int bytecodelen) +bool PixelShaderCache::SetUberShader() { - ID3D11PixelShader* shader = D3D::CreatePixelShaderFromByteCode(bytecode, bytecodelen); - if (shader == nullptr) - return false; + UberShader::PixelShaderUid uid = UberShader::GetPixelShaderUid(); - // 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]; - - if (!shader) + if (last_uber_entry && last_uber_uid == uid) { - // INCSTAT(stats.numPixelShadersFailed); + 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 u8* data, size_t len) +{ + ID3D11PixelShader* shader = data ? D3D::CreatePixelShaderFromByteCode(data, len) : nullptr; + if (!InsertShader(uid, shader)) + { + SAFE_RELEASE(shader); return false; } - INCSTAT(stats.numPixelShadersCreated); - SETSTAT(stats.numPixelShadersAlive, PixelShaders.size()); return true; } +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)) + { + 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 diff --git a/Source/Core/VideoBackends/D3D/PixelShaderCache.h b/Source/Core/VideoBackends/D3D/PixelShaderCache.h index fb1b79ad6c..7f1bd56b33 100644 --- a/Source/Core/VideoBackends/D3D/PixelShaderCache.h +++ b/Source/Core/VideoBackends/D3D/PixelShaderCache.h @@ -7,10 +7,14 @@ #include #include +#include "VideoCommon/AsyncShaderCompiler.h" #include "VideoCommon/PixelShaderGen.h" +#include "VideoCommon/UberShaderPixel.h" namespace DX11 { +class D3DBlob; + class PixelShaderCache { public: @@ -18,12 +22,15 @@ public: static void Reload(); static void Clear(); static void Shutdown(); - static bool SetShader(); // TODO: Should be renamed to LoadShader - static bool InsertByteCode(const PixelShaderUid& uid, const void* bytecode, - unsigned int bytecodelen); + static bool SetShader(); + static bool SetUberShader(); + static bool InsertByteCode(const PixelShaderUid& uid, const u8* data, size_t len); + static bool InsertByteCode(const UberShader::PixelShaderUid& uid, const u8* data, size_t len); + static bool InsertShader(const PixelShaderUid& uid, ID3D11PixelShader* shader); + static bool InsertShader(const UberShader::PixelShaderUid& uid, ID3D11PixelShader* shader); + static void PrecompileUberShaders(); - static ID3D11PixelShader* GetActiveShader() { return last_entry->shader; } - static ID3D11Buffer*& GetConstantBuffer(); + static ID3D11Buffer* GetConstantBuffer(); static ID3D11PixelShader* GetColorMatrixProgram(bool multisampled); static ID3D11PixelShader* GetColorCopyProgram(bool multisampled); @@ -40,18 +47,53 @@ private: struct PSCacheEntry { ID3D11PixelShader* shader; + bool pending; - PSCacheEntry() : shader(nullptr) {} + PSCacheEntry() : shader(nullptr), pending(false) {} void Destroy() { SAFE_RELEASE(shader); } }; + class PixelShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem + { + public: + PixelShaderCompilerWorkItem(const PixelShaderUid& uid); + ~PixelShaderCompilerWorkItem() override; + + bool Compile() override; + void Retrieve() override; + + private: + PixelShaderUid m_uid; + ID3D11PixelShader* m_shader = nullptr; + D3DBlob* m_bytecode = nullptr; + }; + + class UberPixelShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem + { + public: + UberPixelShaderCompilerWorkItem(const UberShader::PixelShaderUid& uid); + ~UberPixelShaderCompilerWorkItem() override; + + bool Compile() override; + void Retrieve() override; + + private: + UberShader::PixelShaderUid m_uid; + ID3D11PixelShader* m_shader = nullptr; + D3DBlob* m_bytecode = nullptr; + }; + typedef std::map PSCache; + typedef std::map UberPSCache; static void LoadShaderCache(); static PSCache PixelShaders; + static UberPSCache UberPixelShaders; static const PSCacheEntry* last_entry; + static const PSCacheEntry* last_uber_entry; static PixelShaderUid last_uid; + static UberShader::PixelShaderUid last_uber_uid; }; } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index d3a5b78cc3..157c2218d7 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -837,6 +837,7 @@ void Renderer::SwapImpl(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, // Enable configuration changes UpdateActiveConfig(); g_texture_cache->OnConfigChanged(g_ActiveConfig); + VertexShaderCache::RetreiveAsyncShaders(); SetWindowSize(fbStride, fbHeight); @@ -958,10 +959,6 @@ void Renderer::ApplyState() g_ActiveConfig.bEnablePixelLighting ? vertexConstants : nullptr); D3D::stateman->SetVertexConstants(vertexConstants); D3D::stateman->SetGeometryConstants(GeometryShaderCache::GetConstantBuffer()); - - D3D::stateman->SetPixelShader(PixelShaderCache::GetActiveShader()); - D3D::stateman->SetVertexShader(VertexShaderCache::GetActiveShader()); - D3D::stateman->SetGeometryShader(GeometryShaderCache::GetActiveShader()); } void Renderer::RestoreState() diff --git a/Source/Core/VideoBackends/D3D/VertexManager.cpp b/Source/Core/VideoBackends/D3D/VertexManager.cpp index 7bb41a0f42..db27b937df 100644 --- a/Source/Core/VideoBackends/D3D/VertexManager.cpp +++ b/Source/Core/VideoBackends/D3D/VertexManager.cpp @@ -159,7 +159,9 @@ void VertexManager::vFlush() return; } - if (!VertexShaderCache::SetShader()) + D3DVertexFormat* vertex_format = + static_cast(VertexLoaderManager::GetCurrentVertexFormat()); + if (!VertexShaderCache::SetShader(vertex_format)) { GFX_DEBUGGER_PAUSE_LOG_AT(NEXT_ERROR, true, { printf("Fail to set pixel shader\n"); }); return; @@ -182,7 +184,6 @@ void VertexManager::vFlush() PrepareDrawBuffers(stride); - VertexLoaderManager::GetCurrentVertexFormat()->SetupVertexPointers(); g_renderer->ApplyState(); Draw(stride); diff --git a/Source/Core/VideoBackends/D3D/VertexManager.h b/Source/Core/VideoBackends/D3D/VertexManager.h index 3bb444a3b4..99d45957ed 100644 --- a/Source/Core/VideoBackends/D3D/VertexManager.h +++ b/Source/Core/VideoBackends/D3D/VertexManager.h @@ -4,13 +4,31 @@ #pragma once +#include #include +#include "VideoCommon/NativeVertexFormat.h" #include "VideoCommon/VertexManagerBase.h" struct ID3D11Buffer; namespace DX11 { +class D3DBlob; +class D3DVertexFormat : public NativeVertexFormat +{ +public: + D3DVertexFormat(const PortableVertexDeclaration& vtx_decl); + ~D3DVertexFormat(); + void SetupVertexPointers() override; + void SetInputLayout(D3DBlob* vs_bytecode); + +private: + std::array m_elems{}; + UINT m_num_elems = 0; + + ID3D11InputLayout* m_layout = nullptr; +}; + class VertexManager : public VertexManagerBase { public: diff --git a/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp b/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp index 023caedba0..8df1ec718f 100644 --- a/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp +++ b/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp @@ -8,23 +8,32 @@ #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/D3DShader.h" +#include "VideoBackends/D3D/D3DState.h" +#include "VideoBackends/D3D/VertexManager.h" #include "VideoBackends/D3D/VertexShaderCache.h" #include "VideoCommon/Debugger.h" #include "VideoCommon/Statistics.h" +#include "VideoCommon/UberShaderVertex.h" +#include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexShaderGen.h" #include "VideoCommon/VertexShaderManager.h" namespace DX11 { VertexShaderCache::VSCache VertexShaderCache::vshaders; +VertexShaderCache::UberVSCache VertexShaderCache::ubervshaders; const VertexShaderCache::VSCacheEntry* VertexShaderCache::last_entry; +const VertexShaderCache::VSCacheEntry* VertexShaderCache::last_uber_entry; VertexShaderUid VertexShaderCache::last_uid; +UberShader::VertexShaderUid VertexShaderCache::last_uber_uid; static ID3D11VertexShader* SimpleVertexShader = nullptr; static ID3D11VertexShader* ClearVertexShader = nullptr; @@ -32,6 +41,8 @@ static ID3D11InputLayout* SimpleLayout = nullptr; static ID3D11InputLayout* ClearLayout = nullptr; LinearDiskCache g_vs_disk_cache; +LinearDiskCache g_uber_vs_disk_cache; +std::unique_ptr g_async_compiler; ID3D11VertexShader* VertexShaderCache::GetSimpleVertexShader() { @@ -70,10 +81,11 @@ ID3D11Buffer*& VertexShaderCache::GetConstantBuffer() } // this class will load the precompiled shaders into our cache -class VertexShaderCacheInserter : public LinearDiskCacheReader +template +class VertexShaderCacheInserter : public LinearDiskCacheReader { public: - void Read(const VertexShaderUid& key, const u8* value, u32 value_size) + void Read(const UidType& key, const u8* value, u32 value_size) { D3DBlob* blob = new D3DBlob(value_size, value); VertexShaderCache::InsertByteCode(key, blob); @@ -160,36 +172,65 @@ void VertexShaderCache::Init() if (g_ActiveConfig.bShaderCache) LoadShaderCache(); + + g_async_compiler = std::make_unique(); + if (g_ActiveConfig.GetShaderCompilerThreads() > 0) + g_async_compiler->StartWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); + + if (g_ActiveConfig.CanPrecompileUberShaders()) + PrecompileUberShaders(); } void VertexShaderCache::LoadShaderCache() { - VertexShaderCacheInserter inserter; + VertexShaderCacheInserter inserter; g_vs_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "VS", true, true), inserter); + + VertexShaderCacheInserter uber_inserter; + g_uber_vs_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "UberVS", false, true), + uber_inserter); } void VertexShaderCache::Reload() { + g_async_compiler->WaitUntilCompletion(); + g_async_compiler->RetrieveWorkItems(); + g_vs_disk_cache.Sync(); g_vs_disk_cache.Close(); + g_uber_vs_disk_cache.Sync(); + g_uber_vs_disk_cache.Close(); Clear(); if (g_ActiveConfig.bShaderCache) LoadShaderCache(); + + if (g_ActiveConfig.CanPrecompileUberShaders()) + PrecompileUberShaders(); } void VertexShaderCache::Clear() { for (auto& iter : vshaders) iter.second.Destroy(); + for (auto& iter : ubervshaders) + iter.second.Destroy(); vshaders.clear(); + ubervshaders.clear(); - last_entry = nullptr; last_uid = {}; + last_uber_uid = {}; + last_entry = nullptr; + last_uber_entry = nullptr; + last_uid = {}; + last_uber_uid = {}; } void VertexShaderCache::Shutdown() { + g_async_compiler->StopWorkerThreads(); + g_async_compiler->RetrieveWorkItems(); + SAFE_RELEASE(vscbuf); SAFE_RELEASE(SimpleVertexShader); @@ -201,74 +242,264 @@ void VertexShaderCache::Shutdown() Clear(); g_vs_disk_cache.Sync(); g_vs_disk_cache.Close(); + g_uber_vs_disk_cache.Sync(); + g_uber_vs_disk_cache.Close(); } -bool VertexShaderCache::SetShader() +bool VertexShaderCache::SetShader(D3DVertexFormat* vertex_format) { - VertexShaderUid uid = GetVertexShaderUid(); - - if (last_entry) + if (g_ActiveConfig.CanUseUberShaders() && + (g_ActiveConfig.bDisableSpecializedShaders || g_ActiveConfig.bForceVertexUberShaders)) { - if (uid == last_uid) - { - GFX_DEBUGGER_PAUSE_AT(NEXT_VERTEX_SHADER_CHANGE, true); - return (last_entry->shader != nullptr); - } + return SetUberShader(vertex_format); } - last_uid = uid; + VertexShaderUid uid = GetVertexShaderUid(); + if (last_entry && uid == last_uid) + { + if (last_entry->pending) + return SetUberShader(vertex_format); - VSCache::iterator iter = vshaders.find(uid); + if (!last_entry->shader) + return false; + + vertex_format->SetInputLayout(last_entry->bytecode); + D3D::stateman->SetVertexShader(last_entry->shader); + return true; + } + + auto iter = vshaders.find(uid); if (iter != vshaders.end()) { const VSCacheEntry& entry = iter->second; + if (entry.pending) + return SetUberShader(vertex_format); + + last_uid = uid; last_entry = &entry; GFX_DEBUGGER_PAUSE_AT(NEXT_VERTEX_SHADER_CHANGE, true); - return (entry.shader != nullptr); + if (!last_entry->shader) + return false; + + vertex_format->SetInputLayout(last_entry->bytecode); + D3D::stateman->SetVertexShader(last_entry->shader); + return true; } + // Background compiling? + if (g_ActiveConfig.CanBackgroundCompileShaders()) + { + // Create a pending entry + VSCacheEntry entry; + entry.pending = true; + vshaders[uid] = entry; + + // Queue normal shader compiling and use ubershader + g_async_compiler->QueueWorkItem( + g_async_compiler->CreateWorkItem(uid)); + return SetUberShader(vertex_format); + } + + // Need to compile a new shader + D3DBlob* bytecode = nullptr; ShaderCode code = GenerateVertexShaderCode(APIType::D3D, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - - D3DBlob* pbytecode = nullptr; - D3D::CompileVertexShader(code.GetBuffer(), &pbytecode); - - if (pbytecode == nullptr) + D3D::CompileVertexShader(code.GetBuffer(), &bytecode); + if (!InsertByteCode(uid, bytecode)) { - GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true); + SAFE_RELEASE(bytecode); return false; } - g_vs_disk_cache.Append(uid, pbytecode->Data(), pbytecode->Size()); - bool success = InsertByteCode(uid, pbytecode); - pbytecode->Release(); - - GFX_DEBUGGER_PAUSE_AT(NEXT_VERTEX_SHADER_CHANGE, true); - return success; + g_vs_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); + bytecode->Release(); + return SetShader(vertex_format); } -bool VertexShaderCache::InsertByteCode(const VertexShaderUid& uid, D3DBlob* bcodeblob) +bool VertexShaderCache::SetUberShader(D3DVertexFormat* vertex_format) { - ID3D11VertexShader* shader = D3D::CreateVertexShaderFromByteCode(bcodeblob); - if (shader == nullptr) + D3DVertexFormat* uber_vertex_format = static_cast( + VertexLoaderManager::GetUberVertexFormat(vertex_format->GetVertexDeclaration())); + UberShader::VertexShaderUid uid = UberShader::GetVertexShaderUid(); + if (last_uber_entry && last_uber_uid == uid) + { + if (!last_uber_entry->shader) + return false; + + uber_vertex_format->SetInputLayout(last_uber_entry->bytecode); + D3D::stateman->SetVertexShader(last_uber_entry->shader); + return true; + } + + auto iter = ubervshaders.find(uid); + if (iter != ubervshaders.end()) + { + const VSCacheEntry& entry = iter->second; + last_uber_uid = uid; + last_uber_entry = &entry; + + GFX_DEBUGGER_PAUSE_AT(NEXT_VERTEX_SHADER_CHANGE, true); + if (!last_uber_entry->shader) + return false; + + uber_vertex_format->SetInputLayout(last_uber_entry->bytecode); + D3D::stateman->SetVertexShader(last_uber_entry->shader); + return true; + } + + // Need to compile a new shader + D3DBlob* bytecode = nullptr; + ShaderCode code = + UberShader::GenVertexShader(APIType::D3D, ShaderHostConfig::GetCurrent(), uid.GetUidData()); + D3D::CompileVertexShader(code.GetBuffer(), &bytecode); + if (!InsertByteCode(uid, bytecode)) + { + SAFE_RELEASE(bytecode); + return false; + } + + g_uber_vs_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); + bytecode->Release(); + return SetUberShader(vertex_format); +} + +bool VertexShaderCache::InsertByteCode(const VertexShaderUid& uid, D3DBlob* blob) +{ + ID3D11VertexShader* shader = + blob ? D3D::CreateVertexShaderFromByteCode(blob->Data(), blob->Size()) : nullptr; + bool result = InsertShader(uid, shader, blob); + SAFE_RELEASE(shader); + return result; +} + +bool VertexShaderCache::InsertByteCode(const UberShader::VertexShaderUid& uid, D3DBlob* blob) +{ + ID3D11VertexShader* shader = + blob ? D3D::CreateVertexShaderFromByteCode(blob->Data(), blob->Size()) : nullptr; + bool result = InsertShader(uid, shader, blob); + SAFE_RELEASE(shader); + return result; +} + +bool VertexShaderCache::InsertShader(const VertexShaderUid& uid, ID3D11VertexShader* shader, + D3DBlob* blob) +{ + auto iter = vshaders.find(uid); + if (iter != vshaders.end() && !iter->second.pending) return false; - // TODO: Somehow make the debug name a bit more specific - D3D::SetDebugObjectName((ID3D11DeviceChild*)shader, "a vertex shader of VertexShaderCache"); + VSCacheEntry& newentry = vshaders[uid]; + newentry.pending = false; + if (!shader || !blob) + return false; - // Make an entry in the table - VSCacheEntry entry; - entry.shader = shader; - entry.SetByteCode(bcodeblob); + shader->AddRef(); + newentry.SetByteCode(blob); + newentry.shader = shader; - vshaders[uid] = entry; - last_entry = &vshaders[uid]; + INCSTAT(stats.numPixelShadersCreated); + SETSTAT(stats.numPixelShadersAlive, static_cast(vshaders.size())); + return true; +} - INCSTAT(stats.numVertexShadersCreated); - SETSTAT(stats.numVertexShadersAlive, (int)vshaders.size()); +bool VertexShaderCache::InsertShader(const UberShader::VertexShaderUid& uid, + ID3D11VertexShader* shader, D3DBlob* blob) +{ + auto iter = ubervshaders.find(uid); + if (iter != ubervshaders.end() && !iter->second.pending) + return false; + + VSCacheEntry& newentry = ubervshaders[uid]; + newentry.pending = false; + if (!shader || !blob) + return false; + + shader->AddRef(); + newentry.SetByteCode(blob); + newentry.shader = shader; + return true; +} + +void VertexShaderCache::RetreiveAsyncShaders() +{ + g_async_compiler->RetrieveWorkItems(); +} + +void VertexShaderCache::PrecompileUberShaders() +{ + UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& uid) { + if (ubervshaders.find(uid) != ubervshaders.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); +} + +VertexShaderCache::VertexShaderCompilerWorkItem::VertexShaderCompilerWorkItem( + const VertexShaderUid& uid) +{ + std::memcpy(&m_uid, &uid, sizeof(uid)); +} + +VertexShaderCache::VertexShaderCompilerWorkItem::~VertexShaderCompilerWorkItem() +{ + SAFE_RELEASE(m_bytecode); + SAFE_RELEASE(m_vs); +} + +bool VertexShaderCache::VertexShaderCompilerWorkItem::Compile() +{ + ShaderCode code = + GenerateVertexShaderCode(APIType::D3D, ShaderHostConfig::GetCurrent(), m_uid.GetUidData()); + + if (D3D::CompileVertexShader(code.GetBuffer(), &m_bytecode)) + m_vs = D3D::CreateVertexShaderFromByteCode(m_bytecode); return true; } +void VertexShaderCache::VertexShaderCompilerWorkItem::Retrieve() +{ + if (InsertShader(m_uid, m_vs, m_bytecode)) + g_vs_disk_cache.Append(m_uid, m_bytecode->Data(), m_bytecode->Size()); +} + +VertexShaderCache::UberVertexShaderCompilerWorkItem::UberVertexShaderCompilerWorkItem( + const UberShader::VertexShaderUid& uid) +{ + std::memcpy(&m_uid, &uid, sizeof(uid)); +} + +VertexShaderCache::UberVertexShaderCompilerWorkItem::~UberVertexShaderCompilerWorkItem() +{ + SAFE_RELEASE(m_bytecode); + SAFE_RELEASE(m_vs); +} + +bool VertexShaderCache::UberVertexShaderCompilerWorkItem::Compile() +{ + ShaderCode code = + UberShader::GenVertexShader(APIType::D3D, ShaderHostConfig::GetCurrent(), m_uid.GetUidData()); + + if (D3D::CompileVertexShader(code.GetBuffer(), &m_bytecode)) + m_vs = D3D::CreateVertexShaderFromByteCode(m_bytecode); + + return true; +} + +void VertexShaderCache::UberVertexShaderCompilerWorkItem::Retrieve() +{ + if (InsertShader(m_uid, m_vs, m_bytecode)) + g_uber_vs_disk_cache.Append(m_uid, m_bytecode->Data(), m_bytecode->Size()); +} + } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/VertexShaderCache.h b/Source/Core/VideoBackends/D3D/VertexShaderCache.h index 514e807192..04c1a05821 100644 --- a/Source/Core/VideoBackends/D3D/VertexShaderCache.h +++ b/Source/Core/VideoBackends/D3D/VertexShaderCache.h @@ -9,10 +9,14 @@ #include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DBlob.h" +#include "VideoCommon/AsyncShaderCompiler.h" +#include "VideoCommon/UberShaderVertex.h" #include "VideoCommon/VertexShaderGen.h" namespace DX11 { +class D3DVertexFormat; + class VertexShaderCache { public: @@ -20,10 +24,11 @@ public: static void Reload(); static void Clear(); static void Shutdown(); - static bool SetShader(); // TODO: Should be renamed to LoadShader + static bool SetShader(D3DVertexFormat* vertex_format); + static bool SetUberShader(D3DVertexFormat* vertex_format); + static void RetreiveAsyncShaders(); + static void PrecompileUberShaders(); - static ID3D11VertexShader* GetActiveShader() { return last_entry->shader; } - static D3DBlob* GetActiveShaderBytecode() { return last_entry->bytecode; } static ID3D11Buffer*& GetConstantBuffer(); static ID3D11VertexShader* GetSimpleVertexShader(); @@ -31,15 +36,20 @@ public: static ID3D11InputLayout* GetSimpleInputLayout(); static ID3D11InputLayout* GetClearInputLayout(); - static bool InsertByteCode(const VertexShaderUid& uid, D3DBlob* bcodeblob); + static bool InsertByteCode(const VertexShaderUid& uid, D3DBlob* blob); + static bool InsertByteCode(const UberShader::VertexShaderUid& uid, D3DBlob* blob); + static bool InsertShader(const VertexShaderUid& uid, ID3D11VertexShader* shader, D3DBlob* blob); + static bool InsertShader(const UberShader::VertexShaderUid& uid, ID3D11VertexShader* shader, + D3DBlob* blob); private: struct VSCacheEntry { ID3D11VertexShader* shader; D3DBlob* bytecode; // needed to initialize the input layout + bool pending; - VSCacheEntry() : shader(nullptr), bytecode(nullptr) {} + VSCacheEntry() : shader(nullptr), bytecode(nullptr), pending(false) {} void SetByteCode(D3DBlob* blob) { SAFE_RELEASE(bytecode); @@ -52,13 +62,49 @@ private: SAFE_RELEASE(bytecode); } }; + + class VertexShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem + { + public: + VertexShaderCompilerWorkItem(const VertexShaderUid& uid); + ~VertexShaderCompilerWorkItem() override; + + bool Compile() override; + void Retrieve() override; + + private: + VertexShaderUid m_uid; + D3DBlob* m_bytecode = nullptr; + ID3D11VertexShader* m_vs = nullptr; + }; + + class UberVertexShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem + { + public: + UberVertexShaderCompilerWorkItem(const UberShader::VertexShaderUid& uid); + ~UberVertexShaderCompilerWorkItem() override; + + bool Compile() override; + void Retrieve() override; + + private: + UberShader::VertexShaderUid m_uid; + D3DBlob* m_bytecode = nullptr; + ID3D11VertexShader* m_vs = nullptr; + }; + typedef std::map VSCache; + typedef std::map UberVSCache; static void LoadShaderCache(); + static void SetInputLayout(); static VSCache vshaders; + static UberVSCache ubervshaders; static const VSCacheEntry* last_entry; + static const VSCacheEntry* last_uber_entry; static VertexShaderUid last_uid; + static UberShader::VertexShaderUid last_uber_uid; }; } // namespace DX11