diff --git a/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp b/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp index cc8a9ba7bc..9941d3e234 100644 --- a/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp +++ b/Source/Core/VideoBackends/D3D/GeometryShaderCache.cpp @@ -6,7 +6,6 @@ #include "Common/Align.h" #include "Common/FileUtil.h" -#include "Common/LinearDiskCache.h" #include "Common/StringUtil.h" #include "Core/ConfigManager.h" @@ -25,16 +24,9 @@ namespace DX11 { -GeometryShaderCache::GSCache GeometryShaderCache::GeometryShaders; -const GeometryShaderCache::GSCacheEntry* GeometryShaderCache::last_entry; -GeometryShaderUid GeometryShaderCache::last_uid; -const GeometryShaderCache::GSCacheEntry GeometryShaderCache::pass_entry; - ID3D11GeometryShader* ClearGeometryShader = nullptr; ID3D11GeometryShader* CopyGeometryShader = nullptr; -LinearDiskCache g_gs_disk_cache; - ID3D11GeometryShader* GeometryShaderCache::GetClearGeometryShader() { return (g_ActiveConfig.stereo_mode != StereoMode::Off) ? ClearGeometryShader : nullptr; @@ -63,16 +55,6 @@ ID3D11Buffer*& GeometryShaderCache::GetConstantBuffer() return gscbuf; } -// this class will load the precompiled shaders into our cache -class GeometryShaderCacheInserter : public LinearDiskCacheReader -{ -public: - void Read(const GeometryShaderUid& key, const u8* value, u32 value_size) - { - GeometryShaderCache::InsertByteCode(key, value, value_size); - } -}; - const char clear_shader_code[] = { "struct VSOUTPUT\n" "{\n" @@ -155,44 +137,6 @@ void GeometryShaderCache::Init() CopyGeometryShader = D3D::CompileAndCreateGeometryShader(copy_shader_code); CHECK(CopyGeometryShader != nullptr, "Create copy geometry shader"); D3D::SetDebugObjectName(CopyGeometryShader, "copy geometry shader"); - - Clear(); - - if (g_ActiveConfig.bShaderCache) - LoadShaderCache(); - - if (g_ActiveConfig.CanPrecompileUberShaders()) - PrecompileShaders(); -} - -void GeometryShaderCache::LoadShaderCache() -{ - GeometryShaderCacheInserter inserter; - g_gs_disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::D3D, "GS", true, true), inserter); -} - -void GeometryShaderCache::Reload() -{ - g_gs_disk_cache.Sync(); - g_gs_disk_cache.Close(); - Clear(); - - if (g_ActiveConfig.bShaderCache) - LoadShaderCache(); - - if (g_ActiveConfig.CanPrecompileUberShaders()) - PrecompileShaders(); -} - -// ONLY to be used during shutdown. -void GeometryShaderCache::Clear() -{ - for (auto& iter : GeometryShaders) - iter.second.Destroy(); - GeometryShaders.clear(); - - last_entry = nullptr; - last_uid = {}; } void GeometryShaderCache::Shutdown() @@ -201,83 +145,5 @@ void GeometryShaderCache::Shutdown() SAFE_RELEASE(ClearGeometryShader); SAFE_RELEASE(CopyGeometryShader); - - Clear(); - g_gs_disk_cache.Sync(); - g_gs_disk_cache.Close(); } - -bool GeometryShaderCache::SetShader(PrimitiveType primitive_type) -{ - GeometryShaderUid uid = GetGeometryShaderUid(primitive_type); - if (last_entry && uid == last_uid) - { - GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - D3D::stateman->SetGeometryShader(last_entry->shader); - return true; - } - - // 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 - 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()); - if (!D3D::CompileGeometryShader(code.GetBuffer(), &bytecode) || - !InsertByteCode(uid, bytecode ? bytecode->Data() : nullptr, bytecode ? bytecode->Size() : 0)) - { - SAFE_RELEASE(bytecode); - return false; - } - - // Insert the bytecode into the caches - 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 fdd3d053f2..28c1ee3680 100644 --- a/Source/Core/VideoBackends/D3D/GeometryShaderCache.h +++ b/Source/Core/VideoBackends/D3D/GeometryShaderCache.h @@ -15,36 +15,12 @@ class GeometryShaderCache { public: static void Init(); - static void Reload(); - static void Clear(); static void Shutdown(); - static bool SetShader(PrimitiveType 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 ID3D11Buffer*& GetConstantBuffer(); - -private: - struct GSCacheEntry - { - ID3D11GeometryShader* shader; - - GSCacheEntry() : shader(nullptr) {} - void Destroy() { SAFE_RELEASE(shader); } - }; - - typedef std::map GSCache; - - static void LoadShaderCache(); - - static GSCache GeometryShaders; - static const GSCacheEntry* last_entry; - static GeometryShaderUid last_uid; - static const GSCacheEntry pass_entry; }; } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp b/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp index 664ecec3a2..e09e32d8a2 100644 --- a/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp +++ b/Source/Core/VideoBackends/D3D/PixelShaderCache.cpp @@ -7,7 +7,6 @@ #include "Common/Align.h" #include "Common/CommonTypes.h" #include "Common/FileUtil.h" -#include "Common/LinearDiskCache.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" @@ -27,17 +26,6 @@ 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_ColorCopyProgram[2] = {nullptr}; ID3D11PixelShader* s_ClearProgram = nullptr; ID3D11PixelShader* s_AnaglyphProgram = nullptr; @@ -309,17 +297,6 @@ ID3D11Buffer* PixelShaderCache::GetConstantBuffer() return pscbuf; } -// this class will load the precompiled shaders into our cache -template -class PixelShaderCacheInserter : public LinearDiskCacheReader -{ -public: - void Read(const UidType& key, const u8* value, u32 value_size) - { - PixelShaderCache::InsertByteCode(key, value, value_size); - } -}; - void PixelShaderCache::Init() { unsigned int cbsize = Common::AlignUp(static_cast(sizeof(PixelShaderConstants)), @@ -344,58 +321,6 @@ void PixelShaderCache::Init() s_ColorCopyProgram[0] = D3D::CompileAndCreatePixelShader(color_copy_program_code); CHECK(s_ColorCopyProgram[0] != nullptr, "Create color copy pixel shader"); D3D::SetDebugObjectName(s_ColorCopyProgram[0], "color copy pixel shader"); - - Clear(); - - SETSTAT(stats.numPixelShadersCreated, 0); - SETSTAT(stats.numPixelShadersAlive, 0); - - if (g_ActiveConfig.bShaderCache) - LoadShaderCache(); - - if (g_ActiveConfig.CanPrecompileUberShaders()) - QueueUberShaderCompiles(); -} - -void PixelShaderCache::LoadShaderCache() -{ - 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()) - QueueUberShaderCompiles(); -} - -// ONLY to be used during shutdown. -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 @@ -420,255 +345,5 @@ void PixelShaderCache::Shutdown() SAFE_RELEASE(s_rgba6_to_rgb8[i]); SAFE_RELEASE(s_rgb8_to_rgba6[i]); } - - 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() -{ - if (g_ActiveConfig.bDisableSpecializedShaders) - return SetUberShader(); - - PixelShaderUid uid = GetPixelShaderUid(); - ClearUnusedPixelShaderUidBits(APIType::D3D, &uid); - 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 - 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); - 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()); - D3D::CompilePixelShader(code.GetBuffer(), &bytecode); - if (!InsertByteCode(uid, bytecode ? bytecode->Data() : nullptr, bytecode ? bytecode->Size() : 0)) - { - SAFE_RELEASE(bytecode); - return false; - } - - g_ps_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); - return SetShader(); -} - -bool PixelShaderCache::SetUberShader() -{ - UberShader::PixelShaderUid uid = UberShader::GetPixelShaderUid(); - UberShader::ClearUnusedPixelShaderUidBits(APIType::D3D, &uid); - - 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 ? bytecode->Data() : nullptr, bytecode ? bytecode->Size() : 0)) - { - 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; - } - - 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::QueueUberShaderCompiles() -{ - 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 9a0faed9e5..22b6b0cc0c 100644 --- a/Source/Core/VideoBackends/D3D/PixelShaderCache.h +++ b/Source/Core/VideoBackends/D3D/PixelShaderCache.h @@ -19,16 +19,7 @@ class PixelShaderCache { public: static void Init(); - static void Reload(); - static void Clear(); static void Shutdown(); - 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 QueueUberShaderCompiles(); static ID3D11Buffer* GetConstantBuffer(); @@ -40,58 +31,6 @@ public: static ID3D11PixelShader* ReinterpRGB8ToRGBA6(bool multisampled); static void InvalidateMSAAShaders(); - -private: - struct PSCacheEntry - { - ID3D11PixelShader* shader; - bool pending; - - PSCacheEntry() : shader(nullptr), pending(false) {} - void Destroy() { SAFE_RELEASE(shader); } - }; - - class PixelShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit 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: - explicit 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 bce1ad674e..91475b89d9 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -71,15 +71,6 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height) g_framebuffer_manager = std::make_unique(m_target_width, m_target_height); SetupDeviceObjects(); - // Setup GX pipeline state - for (auto& sampler : m_gx_state.samplers) - sampler.hex = RenderState::GetPointSamplerState().hex; - - m_gx_state.zmode.testenable = false; - m_gx_state.zmode.updateenable = false; - m_gx_state.zmode.func = ZMode::NEVER; - m_gx_state.raster.cullmode = GenMode::CULL_NONE; - // Clear EFB textures constexpr std::array clear_color{{0.f, 0.f, 0.f, 1.f}}; D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(), @@ -315,11 +306,6 @@ void Renderer::SetPipeline(const AbstractPipeline* pipeline) void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices, u32 vertex_stride, u32 num_vertices) { - // Textures are fine, they're set directly via SetTexture. - // Since samplers are set via gx_state, we need to fix this up here. - for (size_t stage = 0; stage < m_gx_state.samplers.size(); stage++) - D3D::stateman->SetSampler(stage, m_state_cache.Get(m_gx_state.samplers[stage])); - // Copy in uniforms. if (uniforms_size > 0) { @@ -640,11 +626,6 @@ void Renderer::ReinterpretPixelData(unsigned int convtype) RestoreAPIState(); } -void Renderer::SetBlendingState(const BlendingState& state) -{ - m_gx_state.blend.hex = state.hex; -} - // This function has the final picture. We adjust the aspect ratio here. void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region, u64 ticks, float Gamma) @@ -685,7 +666,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region // Enable configuration changes UpdateActiveConfig(); g_texture_cache->OnConfigChanged(g_ActiveConfig); - VertexShaderCache::RetreiveAsyncShaders(); // Flip/present backbuffer to frontbuffer here if (D3D::swapchain) @@ -708,12 +688,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region D3D11_CLEAR_DEPTH, 0.f, 0); } - if (CheckForHostConfigChanges()) - { - VertexShaderCache::Reload(); - GeometryShaderCache::Reload(); - PixelShaderCache::Reload(); - } + CheckForHostConfigChanges(); // begin next frame RestoreAPIState(); @@ -786,30 +761,6 @@ void Renderer::RestoreAPIState() BPFunctions::SetScissor(); } -void Renderer::ApplyState() -{ - D3D::stateman->SetBlendState(m_state_cache.Get(m_gx_state.blend)); - D3D::stateman->SetDepthState(m_state_cache.Get(m_gx_state.zmode)); - D3D::stateman->SetRasterizerState(m_state_cache.Get(m_gx_state.raster)); - D3D::stateman->SetPrimitiveTopology( - StateCache::GetPrimitiveTopology(m_gx_state.raster.primitive)); - FramebufferManager::SetIntegerEFBRenderTarget(m_gx_state.blend.logicopenable); - - for (u32 stage = 0; stage < static_cast(m_gx_state.samplers.size()); stage++) - D3D::stateman->SetSampler(stage, m_state_cache.Get(m_gx_state.samplers[stage])); - - ID3D11Buffer* vertexConstants = VertexShaderCache::GetConstantBuffer(); - - D3D::stateman->SetPixelConstants(PixelShaderCache::GetConstantBuffer(), - g_ActiveConfig.bEnablePixelLighting ? vertexConstants : nullptr); - D3D::stateman->SetVertexConstants(vertexConstants); - D3D::stateman->SetGeometryConstants(GeometryShaderCache::GetConstantBuffer()); -} - -void Renderer::RestoreState() -{ -} - void Renderer::SetFramebuffer(const AbstractFramebuffer* framebuffer) { const DXFramebuffer* fb = static_cast(framebuffer); @@ -840,16 +791,6 @@ void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, } } -void Renderer::SetRasterizationState(const RasterizationState& state) -{ - m_gx_state.raster.hex = state.hex; -} - -void Renderer::SetDepthState(const DepthState& state) -{ - m_gx_state.zmode.hex = state.hex; -} - void Renderer::SetTexture(u32 index, const AbstractTexture* texture) { D3D::stateman->SetTexture( @@ -859,7 +800,7 @@ void Renderer::SetTexture(u32 index, const AbstractTexture* texture) void Renderer::SetSamplerState(u32 index, const SamplerState& state) { - m_gx_state.samplers[index].hex = state.hex; + D3D::stateman->SetSampler(index, m_state_cache.Get(state)); } void Renderer::UnbindTexture(const AbstractTexture* texture) diff --git a/Source/Core/VideoBackends/D3D/Render.h b/Source/Core/VideoBackends/D3D/Render.h index 7706cbe56d..a7ccb0b9ae 100644 --- a/Source/Core/VideoBackends/D3D/Render.h +++ b/Source/Core/VideoBackends/D3D/Render.h @@ -4,7 +4,6 @@ #pragma once -#include #include #include #include "VideoBackends/D3D/D3DState.h" @@ -41,10 +40,7 @@ public: void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, const ClearColor& color_value = {}, float depth_value = 0.0f) override; - void SetBlendingState(const BlendingState& state) override; void SetScissorRect(const MathUtil::Rectangle& rc) override; - void SetRasterizationState(const RasterizationState& state) override; - void SetDepthState(const DepthState& state) override; void SetTexture(u32 index, const AbstractTexture* texture) override; void SetSamplerState(u32 index, const SamplerState& state) override; void UnbindTexture(const AbstractTexture* texture) override; @@ -54,10 +50,6 @@ public: void SetFullscreen(bool enable_fullscreen) override; bool IsFullscreen() const override; - // TODO: Fix confusing names (see ResetAPIState and RestoreAPIState) - void ApplyState() override; - void RestoreState() override; - void RenderText(const std::string& text, int left, int top, u32 color) override; u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; @@ -84,14 +76,6 @@ public: u32 groups_x, u32 groups_y, u32 groups_z) override; private: - struct GXPipelineState - { - std::array samplers; - BlendingState blend; - DepthState zmode; - RasterizationState raster; - }; - void SetupDeviceObjects(); void TeardownDeviceObjects(); void Create3DVisionTexture(int width, int height); @@ -106,7 +90,6 @@ private: void UpdateUtilityVertexBuffer(const void* vertices, u32 vertex_stride, u32 num_vertices); StateCache m_state_cache; - GXPipelineState m_gx_state; std::array m_clear_blend_states{}; std::array m_clear_depth_states{}; diff --git a/Source/Core/VideoBackends/D3D/VertexManager.cpp b/Source/Core/VideoBackends/D3D/VertexManager.cpp index d4aec17bf4..f9861c08a8 100644 --- a/Source/Core/VideoBackends/D3D/VertexManager.cpp +++ b/Source/Core/VideoBackends/D3D/VertexManager.cpp @@ -11,6 +11,7 @@ #include "VideoBackends/D3D/BoundingBox.h" #include "VideoBackends/D3D/D3DBase.h" #include "VideoBackends/D3D/D3DState.h" +#include "VideoBackends/D3D/FramebufferManager.h" #include "VideoBackends/D3D/GeometryShaderCache.h" #include "VideoBackends/D3D/PixelShaderCache.h" #include "VideoBackends/D3D/Render.h" @@ -135,42 +136,23 @@ void VertexManager::Draw(u32 stride) void VertexManager::vFlush() { - if (!PixelShaderCache::SetShader()) - { - GFX_DEBUGGER_PAUSE_LOG_AT(NEXT_ERROR, true, { printf("Fail to set pixel shader\n"); }); - return; - } - - 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; - } - - if (!GeometryShaderCache::SetShader(m_current_primitive_type)) - { - GFX_DEBUGGER_PAUSE_LOG_AT(NEXT_ERROR, true, { printf("Fail to set pixel shader\n"); }); - return; - } - - if (g_ActiveConfig.backend_info.bSupportsBBox && BoundingBox::active) - { - D3D::context->OMSetRenderTargetsAndUnorderedAccessViews( - D3D11_KEEP_RENDER_TARGETS_AND_DEPTH_STENCIL, nullptr, nullptr, 2, 1, &BBox::GetUAV(), - nullptr); - } - u32 stride = VertexLoaderManager::GetCurrentVertexFormat()->GetVertexStride(); - PrepareDrawBuffers(stride); - g_renderer->ApplyState(); + if (!m_current_pipeline_object) + return; + + FramebufferManager::SetIntegerEFBRenderTarget( + m_current_pipeline_config.blending_state.logicopenable); + g_renderer->SetPipeline(m_current_pipeline_object); + + ID3D11Buffer* vertexConstants = VertexShaderCache::GetConstantBuffer(); + D3D::stateman->SetPixelConstants(PixelShaderCache::GetConstantBuffer(), + g_ActiveConfig.bEnablePixelLighting ? vertexConstants : nullptr); + D3D::stateman->SetVertexConstants(vertexConstants); + D3D::stateman->SetGeometryConstants(GeometryShaderCache::GetConstantBuffer()); Draw(stride); - - g_renderer->RestoreState(); } void VertexManager::ResetBuffer(u32 stride) diff --git a/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp b/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp index ef40f55a10..63d8b3b6ff 100644 --- a/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp +++ b/Source/Core/VideoBackends/D3D/VertexShaderCache.cpp @@ -7,7 +7,6 @@ #include "Common/Align.h" #include "Common/CommonTypes.h" #include "Common/FileUtil.h" -#include "Common/LinearDiskCache.h" #include "Common/MsgHandler.h" #include "Common/StringUtil.h" @@ -28,22 +27,11 @@ 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; 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() { return SimpleVertexShader; @@ -164,73 +152,12 @@ void VertexShaderCache::Init() D3D::SetDebugObjectName(ClearVertexShader, "clear vertex shader"); D3D::SetDebugObjectName(ClearLayout, "clear input layout"); - Clear(); - SETSTAT(stats.numVertexShadersCreated, 0); SETSTAT(stats.numVertexShadersAlive, 0); - - if (g_ActiveConfig.bShaderCache) - LoadShaderCache(); - - g_async_compiler = std::make_unique(); - g_async_compiler->ResizeWorkerThreads(g_ActiveConfig.CanPrecompileUberShaders() ? - g_ActiveConfig.GetShaderPrecompilerThreads() : - g_ActiveConfig.GetShaderCompilerThreads()); - - if (g_ActiveConfig.CanPrecompileUberShaders()) - QueueUberShaderCompiles(); -} - -void VertexShaderCache::LoadShaderCache() -{ - 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()) - QueueUberShaderCompiles(); -} - -void VertexShaderCache::Clear() -{ - for (auto& iter : vshaders) - iter.second.Destroy(); - for (auto& iter : ubervshaders) - iter.second.Destroy(); - vshaders.clear(); - ubervshaders.clear(); - - 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); @@ -238,271 +165,5 @@ void VertexShaderCache::Shutdown() SAFE_RELEASE(SimpleLayout); SAFE_RELEASE(ClearLayout); - - 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(D3DVertexFormat* vertex_format) -{ - if (g_ActiveConfig.bDisableSpecializedShaders) - return SetUberShader(vertex_format); - - VertexShaderUid uid = GetVertexShaderUid(); - if (last_entry && uid == last_uid) - { - if (last_entry->pending) - return SetUberShader(vertex_format); - - if (!last_entry->shader) - return false; - - D3D::stateman->SetInputLayout(vertex_format->GetInputLayout(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); - if (!last_entry->shader) - return false; - - D3D::stateman->SetInputLayout(vertex_format->GetInputLayout(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()); - D3D::CompileVertexShader(code.GetBuffer(), &bytecode); - if (!InsertByteCode(uid, bytecode)) - { - SAFE_RELEASE(bytecode); - return false; - } - - g_vs_disk_cache.Append(uid, bytecode->Data(), bytecode->Size()); - bytecode->Release(); - return SetShader(vertex_format); -} - -bool VertexShaderCache::SetUberShader(D3DVertexFormat* vertex_format) -{ - 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; - - D3D::stateman->SetInputLayout(uber_vertex_format->GetInputLayout(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; - - D3D::stateman->SetInputLayout(uber_vertex_format->GetInputLayout(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; - - VSCacheEntry& newentry = vshaders[uid]; - newentry.pending = false; - if (!shader || !blob) - return false; - - shader->AddRef(); - newentry.SetByteCode(blob); - newentry.shader = shader; - - INCSTAT(stats.numPixelShadersCreated); - SETSTAT(stats.numPixelShadersAlive, static_cast(vshaders.size())); - return true; -} - -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::QueueUberShaderCompiles() -{ - UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& uid) { - if (ubervshaders.find(uid) != ubervshaders.end()) - return; - - g_async_compiler->QueueWorkItem( - g_async_compiler->CreateWorkItem(uid)); - }); -} - -void VertexShaderCache::WaitForBackgroundCompilesToComplete() -{ - 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); - - // Switch from precompile -> runtime compiler threads. - g_async_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); -} - -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 aca223ceea..b59d75be47 100644 --- a/Source/Core/VideoBackends/D3D/VertexShaderCache.h +++ b/Source/Core/VideoBackends/D3D/VertexShaderCache.h @@ -21,14 +21,7 @@ class VertexShaderCache { public: static void Init(); - static void Reload(); - static void Clear(); static void Shutdown(); - static bool SetShader(D3DVertexFormat* vertex_format); - static bool SetUberShader(D3DVertexFormat* vertex_format); - static void RetreiveAsyncShaders(); - static void QueueUberShaderCompiles(); - static void WaitForBackgroundCompilesToComplete(); static ID3D11Buffer*& GetConstantBuffer(); @@ -36,76 +29,6 @@ public: static ID3D11VertexShader* GetClearVertexShader(); static ID3D11InputLayout* GetSimpleInputLayout(); static ID3D11InputLayout* GetClearInputLayout(); - - 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), pending(false) {} - void SetByteCode(D3DBlob* blob) - { - SAFE_RELEASE(bytecode); - bytecode = blob; - blob->AddRef(); - } - void Destroy() - { - SAFE_RELEASE(shader); - SAFE_RELEASE(bytecode); - } - }; - - class VertexShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit 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: - explicit 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 diff --git a/Source/Core/VideoBackends/D3D/main.cpp b/Source/Core/VideoBackends/D3D/main.cpp index 294cd0aebc..3c3237402c 100644 --- a/Source/Core/VideoBackends/D3D/main.cpp +++ b/Source/Core/VideoBackends/D3D/main.cpp @@ -21,6 +21,7 @@ #include "VideoBackends/D3D/VertexShaderCache.h" #include "VideoBackends/D3D/VideoBackend.h" +#include "VideoCommon/ShaderCache.h" #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" @@ -148,6 +149,7 @@ bool VideoBackend::Initialize(void* window_handle) // internal interfaces g_renderer = std::make_unique(backbuffer_width, backbuffer_height); + g_shader_cache = std::make_unique(); g_texture_cache = std::make_unique(); g_vertex_manager = std::make_unique(); g_perf_query = std::make_unique(); @@ -155,7 +157,9 @@ bool VideoBackend::Initialize(void* window_handle) VertexShaderCache::Init(); PixelShaderCache::Init(); GeometryShaderCache::Init(); - VertexShaderCache::WaitForBackgroundCompilesToComplete(); + if (!g_shader_cache->Initialize()) + return false; + D3D::InitUtils(); BBox::Init(); return true; @@ -163,6 +167,7 @@ bool VideoBackend::Initialize(void* window_handle) void VideoBackend::Shutdown() { + g_shader_cache->Shutdown(); g_renderer->Shutdown(); D3D::ShutdownUtils(); @@ -174,6 +179,7 @@ void VideoBackend::Shutdown() g_perf_query.reset(); g_vertex_manager.reset(); g_texture_cache.reset(); + g_shader_cache.reset(); g_renderer.reset(); ShutdownShared(); diff --git a/Source/Core/VideoBackends/Null/CMakeLists.txt b/Source/Core/VideoBackends/Null/CMakeLists.txt index 82efe38241..b89acd85e8 100644 --- a/Source/Core/VideoBackends/Null/CMakeLists.txt +++ b/Source/Core/VideoBackends/Null/CMakeLists.txt @@ -3,7 +3,6 @@ set(SRCS NullTexture.cpp Render.cpp VertexManager.cpp - ShaderCache.cpp ) set(LIBS diff --git a/Source/Core/VideoBackends/Null/Null.vcxproj b/Source/Core/VideoBackends/Null/Null.vcxproj index 664488035e..22bceda2c2 100644 --- a/Source/Core/VideoBackends/Null/Null.vcxproj +++ b/Source/Core/VideoBackends/Null/Null.vcxproj @@ -39,14 +39,12 @@ - - diff --git a/Source/Core/VideoBackends/Null/NullBackend.cpp b/Source/Core/VideoBackends/Null/NullBackend.cpp index c687c8f2df..6e260f08d5 100644 --- a/Source/Core/VideoBackends/Null/NullBackend.cpp +++ b/Source/Core/VideoBackends/Null/NullBackend.cpp @@ -9,7 +9,6 @@ #include "VideoBackends/Null/PerfQuery.h" #include "VideoBackends/Null/Render.h" -#include "VideoBackends/Null/ShaderCache.h" #include "VideoBackends/Null/TextureCache.h" #include "VideoBackends/Null/VertexManager.h" #include "VideoBackends/Null/VideoBackend.h" @@ -63,21 +62,15 @@ bool VideoBackend::Initialize(void* window_handle) g_perf_query = std::make_unique(); g_framebuffer_manager = std::make_unique(); g_texture_cache = std::make_unique(); - - VertexShaderCache::s_instance = std::make_unique(); - GeometryShaderCache::s_instance = std::make_unique(); - PixelShaderCache::s_instance = std::make_unique(); - return true; + g_shader_cache = std::make_unique(); + return g_shader_cache->Initialize(); } void VideoBackend::Shutdown() { + g_shader_cache->Shutdown(); g_renderer->Shutdown(); - PixelShaderCache::s_instance.reset(); - VertexShaderCache::s_instance.reset(); - GeometryShaderCache::s_instance.reset(); - g_texture_cache.reset(); g_perf_query.reset(); g_vertex_manager.reset(); diff --git a/Source/Core/VideoBackends/Null/ShaderCache.cpp b/Source/Core/VideoBackends/Null/ShaderCache.cpp deleted file mode 100644 index 0a5ef30595..0000000000 --- a/Source/Core/VideoBackends/Null/ShaderCache.cpp +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2015 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include "VideoBackends/Null/ShaderCache.h" - -#include "VideoCommon/Debugger.h" -#include "VideoCommon/Statistics.h" -#include "VideoCommon/VideoCommon.h" - -namespace Null -{ -template -ShaderCache::ShaderCache() -{ - Clear(); - - SETSTAT(stats.numPixelShadersCreated, 0); - SETSTAT(stats.numPixelShadersAlive, 0); -} - -template -ShaderCache::~ShaderCache() -{ - Clear(); -} - -template -void ShaderCache::Clear() -{ - m_shaders.clear(); - m_last_entry = nullptr; -} - -template -bool ShaderCache::SetShader(PrimitiveType primitive_type) -{ - Uid uid = GetUid(primitive_type, APIType::OpenGL); - - // Check if the shader is already set - if (m_last_entry) - { - if (uid == m_last_uid) - { - return true; - } - } - - m_last_uid = uid; - - // Check if the shader is already in the cache - auto iter = m_shaders.find(uid); - if (iter != m_shaders.end()) - { - const std::string& entry = iter->second; - m_last_entry = &entry; - - GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - return true; - } - - // Need to compile a new shader - ShaderCode code = GenerateCode(APIType::OpenGL, uid); - m_shaders.emplace(uid, code.GetBuffer()); - - GFX_DEBUGGER_PAUSE_AT(NEXT_PIXEL_SHADER_CHANGE, true); - return true; -} - -template class ShaderCache; -template class ShaderCache; -template class ShaderCache; - -std::unique_ptr VertexShaderCache::s_instance; -std::unique_ptr GeometryShaderCache::s_instance; -std::unique_ptr PixelShaderCache::s_instance; -} diff --git a/Source/Core/VideoBackends/Null/ShaderCache.h b/Source/Core/VideoBackends/Null/ShaderCache.h deleted file mode 100644 index b55185c952..0000000000 --- a/Source/Core/VideoBackends/Null/ShaderCache.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2015 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#pragma once - -#include -#include - -#include "VideoCommon/GeometryShaderGen.h" -#include "VideoCommon/PixelShaderGen.h" -#include "VideoCommon/VertexShaderGen.h" -#include "VideoCommon/VideoCommon.h" - -namespace Null -{ -template -class ShaderCache -{ -public: - ShaderCache(); - virtual ~ShaderCache(); - - void Clear(); - bool SetShader(PrimitiveType primitive_type); - -protected: - virtual Uid GetUid(PrimitiveType primitive_type, APIType api_type) = 0; - virtual ShaderCode GenerateCode(APIType api_type, Uid uid) = 0; - -private: - std::map m_shaders; - const std::string* m_last_entry = nullptr; - Uid m_last_uid; -}; - -class VertexShaderCache : public ShaderCache -{ -public: - static std::unique_ptr s_instance; - -protected: - VertexShaderUid GetUid(PrimitiveType primitive_type, APIType api_type) override - { - return GetVertexShaderUid(); - } - ShaderCode GenerateCode(APIType api_type, VertexShaderUid uid) override - { - return GenerateVertexShaderCode(api_type, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - } -}; - -class GeometryShaderCache : public ShaderCache -{ -public: - static std::unique_ptr s_instance; - -protected: - GeometryShaderUid GetUid(PrimitiveType primitive_type, APIType api_type) override - { - return GetGeometryShaderUid(primitive_type); - } - ShaderCode GenerateCode(APIType api_type, GeometryShaderUid uid) override - { - return GenerateGeometryShaderCode(api_type, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - } -}; - -class PixelShaderCache : public ShaderCache -{ -public: - static std::unique_ptr s_instance; - -protected: - PixelShaderUid GetUid(PrimitiveType primitive_type, APIType api_type) override - { - return GetPixelShaderUid(); - } - ShaderCode GenerateCode(APIType api_type, PixelShaderUid uid) override - { - return GeneratePixelShaderCode(api_type, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - } -}; - -} // namespace NULL diff --git a/Source/Core/VideoBackends/Null/VertexManager.cpp b/Source/Core/VideoBackends/Null/VertexManager.cpp index 60eaf83aa3..17cab39b05 100644 --- a/Source/Core/VideoBackends/Null/VertexManager.cpp +++ b/Source/Core/VideoBackends/Null/VertexManager.cpp @@ -4,8 +4,6 @@ #include "VideoBackends/Null/VertexManager.h" -#include "VideoBackends/Null/ShaderCache.h" - #include "VideoCommon/IndexGenerator.h" #include "VideoCommon/NativeVertexFormat.h" #include "VideoCommon/VertexLoaderManager.h" @@ -41,9 +39,6 @@ void VertexManager::ResetBuffer(u32 stride) void VertexManager::vFlush() { - VertexShaderCache::s_instance->SetShader(m_current_primitive_type); - GeometryShaderCache::s_instance->SetShader(m_current_primitive_type); - PixelShaderCache::s_instance->SetShader(m_current_primitive_type); } } // namespace diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp index a4cb6620dc..d953996396 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp @@ -33,8 +33,6 @@ #include "VideoCommon/ImageWrite.h" #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/Statistics.h" -#include "VideoCommon/UberShaderPixel.h" -#include "VideoCommon/UberShaderVertex.h" #include "VideoCommon/VertexLoaderManager.h" #include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VideoCommon.h" @@ -43,8 +41,6 @@ namespace OGL { static constexpr u32 UBO_LENGTH = 32 * 1024 * 1024; -std::unique_ptr - ProgramShaderCache::s_async_compiler; u32 ProgramShaderCache::s_ubo_buffer_size; s32 ProgramShaderCache::s_ubo_align; GLuint ProgramShaderCache::s_attributeless_VBO = 0; @@ -54,17 +50,9 @@ GLuint ProgramShaderCache::s_last_VAO = 0; static std::unique_ptr s_buffer; static int num_failures = 0; -static LinearDiskCache s_program_disk_cache; -static LinearDiskCache s_uber_program_disk_cache; static GLuint CurrentProgram = 0; -ProgramShaderCache::PCache ProgramShaderCache::pshaders; -ProgramShaderCache::UberPCache ProgramShaderCache::ubershaders; ProgramShaderCache::PipelineProgramMap ProgramShaderCache::pipelineprograms; std::mutex ProgramShaderCache::pipelineprogramlock; -ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_entry; -ProgramShaderCache::PCacheEntry* ProgramShaderCache::last_uber_entry; -SHADERUID ProgramShaderCache::last_uid; -UBERSHADERUID ProgramShaderCache::last_uber_uid; static std::string s_glsl_header = ""; static std::string GetGLSLVersionString() @@ -270,143 +258,6 @@ void ProgramShaderCache::UploadConstants() } } -SHADER* ProgramShaderCache::SetShader(PrimitiveType primitive_type, - const GLVertexFormat* vertex_format) -{ - if (g_ActiveConfig.bDisableSpecializedShaders) - return SetUberShader(primitive_type, vertex_format); - - SHADERUID uid; - std::memset(&uid, 0, sizeof(uid)); - uid.puid = GetPixelShaderUid(); - uid.vuid = GetVertexShaderUid(); - uid.guid = GetGeometryShaderUid(primitive_type); - ClearUnusedPixelShaderUidBits(APIType::OpenGL, &uid.puid); - - // Check if the shader is already set - if (last_entry && uid == last_uid) - { - last_entry->shader.Bind(); - BindVertexFormat(vertex_format); - return &last_entry->shader; - } - - // Check if shader is already in cache - auto iter = pshaders.find(uid); - if (iter != pshaders.end()) - { - PCacheEntry* entry = &iter->second; - if (entry->pending) - return SetUberShader(primitive_type, vertex_format); - - last_uid = uid; - last_entry = entry; - BindVertexFormat(vertex_format); - last_entry->shader.Bind(); - return &last_entry->shader; - } - - // Compile the new shader program. - PCacheEntry& newentry = pshaders[uid]; - newentry.in_cache = false; - newentry.pending = false; - - // Can we background compile this shader? Requires background shader compiling to be enabled, - // and all ubershaders to have been successfully compiled. - if (g_ActiveConfig.CanBackgroundCompileShaders() && !ubershaders.empty() && s_async_compiler) - { - newentry.pending = true; - s_async_compiler->QueueWorkItem(s_async_compiler->CreateWorkItem(uid)); - return SetUberShader(primitive_type, vertex_format); - } - - // Synchronous shader compiling. - ShaderHostConfig host_config = ShaderHostConfig::GetCurrent(); - ShaderCode vcode = GenerateVertexShaderCode(APIType::OpenGL, host_config, uid.vuid.GetUidData()); - ShaderCode pcode = GeneratePixelShaderCode(APIType::OpenGL, host_config, uid.puid.GetUidData()); - ShaderCode gcode; - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders && - !uid.guid.GetUidData()->IsPassthrough()) - gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, uid.guid.GetUidData()); - - if (!CompileShader(newentry.shader, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer())) - return nullptr; - - INCSTAT(stats.numPixelShadersCreated); - SETSTAT(stats.numPixelShadersAlive, pshaders.size()); - - last_uid = uid; - last_entry = &newentry; - BindVertexFormat(vertex_format); - last_entry->shader.Bind(); - return &last_entry->shader; -} - -SHADER* ProgramShaderCache::SetUberShader(PrimitiveType primitive_type, - const GLVertexFormat* vertex_format) -{ - UBERSHADERUID uid; - std::memset(&uid, 0, sizeof(uid)); - uid.puid = UberShader::GetPixelShaderUid(); - uid.vuid = UberShader::GetVertexShaderUid(); - uid.guid = GetGeometryShaderUid(primitive_type); - UberShader::ClearUnusedPixelShaderUidBits(APIType::OpenGL, &uid.puid); - - // We need to use the ubershader vertex format with all attributes enabled. - // Otherwise, the NV driver can generate variants for the vertex shaders. - const GLVertexFormat* uber_vertex_format = static_cast( - VertexLoaderManager::GetUberVertexFormat(vertex_format->GetVertexDeclaration())); - - // Check if the shader is already set - if (last_uber_entry && last_uber_uid == uid) - { - BindVertexFormat(uber_vertex_format); - last_uber_entry->shader.Bind(); - return &last_uber_entry->shader; - } - - // Check if shader is already in cache - auto iter = ubershaders.find(uid); - if (iter != ubershaders.end()) - { - PCacheEntry* entry = &iter->second; - last_uber_uid = uid; - last_uber_entry = entry; - BindVertexFormat(uber_vertex_format); - last_uber_entry->shader.Bind(); - return &last_uber_entry->shader; - } - - // Make an entry in the table - PCacheEntry& newentry = ubershaders[uid]; - newentry.in_cache = false; - newentry.pending = false; - - ShaderHostConfig host_config = ShaderHostConfig::GetCurrent(); - ShaderCode vcode = - UberShader::GenVertexShader(APIType::OpenGL, host_config, uid.vuid.GetUidData()); - ShaderCode pcode = - UberShader::GenPixelShader(APIType::OpenGL, host_config, uid.puid.GetUidData()); - ShaderCode gcode; - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders && - !uid.guid.GetUidData()->IsPassthrough()) - { - gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, uid.guid.GetUidData()); - } - - if (!CompileShader(newentry.shader, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer())) - { - GFX_DEBUGGER_PAUSE_AT(NEXT_ERROR, true); - return nullptr; - } - - last_uber_uid = uid; - last_uber_entry = &newentry; - BindVertexFormat(uber_vertex_format); - last_uber_entry->shader.Bind(); - return &last_uber_entry->shader; -} - bool ProgramShaderCache::CompileShader(SHADER& shader, const std::string& vcode, const std::string& pcode, const std::string& gcode) { @@ -620,11 +471,6 @@ bool ProgramShaderCache::CheckProgramLinkResult(GLuint id, const std::string& vc return true; } -ProgramShaderCache::PCacheEntry ProgramShaderCache::GetShaderProgram() -{ - return *last_entry; -} - void ProgramShaderCache::Init() { // We have to get the UBO alignment here because @@ -642,93 +488,14 @@ void ProgramShaderCache::Init() // Then once more to get bytes s_buffer = StreamBuffer::Create(GL_UNIFORM_BUFFER, UBO_LENGTH); - // The GPU shader code appears to be context-specific on Mesa/i965. - // This means that if we compiled the ubershaders asynchronously, they will be recompiled - // on the main thread the first time they are used, causing stutter. Nouveau has been - // reported to crash if draw calls are invoked on the shared context threads. For now, - // disable asynchronous compilation on Mesa. - if (!DriverDetails::HasBug(DriverDetails::BUG_SHARED_CONTEXT_SHADER_COMPILATION)) - s_async_compiler = std::make_unique(); - - // Read our shader cache, only if supported and enabled - if (g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache) - LoadProgramBinaries(); - CreateHeader(); CreateAttributelessVAO(); CurrentProgram = 0; - last_entry = nullptr; - last_uber_entry = nullptr; - - if (g_ActiveConfig.CanPrecompileUberShaders()) - { - if (s_async_compiler) - s_async_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderPrecompilerThreads()); - PrecompileUberShaders(); - } - - if (s_async_compiler) - { - // No point using the async compiler without workers. - s_async_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); - if (!s_async_compiler->HasWorkerThreads()) - s_async_compiler.reset(); - } -} - -void ProgramShaderCache::RetrieveAsyncShaders() -{ - if (s_async_compiler) - s_async_compiler->RetrieveWorkItems(); -} - -void ProgramShaderCache::Reload() -{ - if (s_async_compiler) - { - s_async_compiler->WaitUntilCompletion(); - s_async_compiler->RetrieveWorkItems(); - } - - const bool use_cache = g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache; - if (use_cache) - SaveProgramBinaries(); - - s_program_disk_cache.Close(); - s_uber_program_disk_cache.Close(); - DestroyShaders(); - - if (use_cache) - LoadProgramBinaries(); - - if (g_ActiveConfig.CanPrecompileUberShaders()) - PrecompileUberShaders(); - - CurrentProgram = 0; - last_entry = nullptr; - last_uber_entry = nullptr; - last_uid = {}; - last_uber_uid = {}; } void ProgramShaderCache::Shutdown() { - if (s_async_compiler) - { - s_async_compiler->WaitUntilCompletion(); - s_async_compiler->StopWorkerThreads(); - s_async_compiler->RetrieveWorkItems(); - s_async_compiler.reset(); - } - - // store all shaders in cache on disk - if (g_ogl_config.bSupportsGLSLCache && g_ActiveConfig.bShaderCache) - SaveProgramBinaries(); - s_program_disk_cache.Close(); - s_uber_program_disk_cache.Close(); - - DestroyShaders(); s_buffer.reset(); glBindVertexArray(0); @@ -781,134 +548,6 @@ void ProgramShaderCache::InvalidateLastProgram() CurrentProgram = 0; } -GLuint ProgramShaderCache::CreateProgramFromBinary(const u8* value, u32 value_size) -{ - const u8* binary = value + sizeof(GLenum); - GLint binary_size = value_size - sizeof(GLenum); - GLenum prog_format; - std::memcpy(&prog_format, value, sizeof(GLenum)); - - GLuint progid = glCreateProgram(); - glProgramBinary(progid, prog_format, binary, binary_size); - - GLint success; - glGetProgramiv(progid, GL_LINK_STATUS, &success); - if (!success) - { - glDeleteProgram(progid); - return 0; - } - - return progid; -} - -bool ProgramShaderCache::CreateCacheEntryFromBinary(PCacheEntry* entry, const u8* value, - u32 value_size) -{ - entry->in_cache = true; - entry->pending = false; - entry->shader.glprogid = CreateProgramFromBinary(value, value_size); - if (entry->shader.glprogid == 0) - return false; - - entry->shader.SetProgramVariables(); - return true; -} - -void ProgramShaderCache::LoadProgramBinaries() -{ - GLint Supported; - glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &Supported); - if (!Supported) - { - ERROR_LOG(VIDEO, "GL_ARB_get_program_binary is supported, but no binary format is known. So " - "disable shader cache."); - g_ogl_config.bSupportsGLSLCache = false; - } - else - { - // Load game-specific shaders. - std::string cache_filename = - GetDiskShaderCacheFileName(APIType::OpenGL, "ProgramBinaries", true, true); - ProgramShaderCacheInserter inserter(pshaders); - s_program_disk_cache.OpenAndRead(cache_filename, inserter); - - // Load global ubershaders. - cache_filename = - GetDiskShaderCacheFileName(APIType::OpenGL, "UberProgramBinaries", false, true); - ProgramShaderCacheInserter uber_inserter(ubershaders); - s_uber_program_disk_cache.OpenAndRead(cache_filename, uber_inserter); - } - SETSTAT(stats.numPixelShadersAlive, pshaders.size()); -} - -static bool GetProgramBinary(const ProgramShaderCache::PCacheEntry& entry, std::vector& data) -{ - // Clear any prior error code - glGetError(); - - GLint link_status = GL_FALSE, delete_status = GL_TRUE, binary_size = 0; - glGetProgramiv(entry.shader.glprogid, GL_LINK_STATUS, &link_status); - glGetProgramiv(entry.shader.glprogid, GL_DELETE_STATUS, &delete_status); - glGetProgramiv(entry.shader.glprogid, GL_PROGRAM_BINARY_LENGTH, &binary_size); - if (glGetError() != GL_NO_ERROR || link_status == GL_FALSE || delete_status == GL_TRUE || - binary_size == 0) - { - return false; - } - - data.resize(binary_size + sizeof(GLenum)); - - GLsizei length = binary_size; - GLenum prog_format; - glGetProgramBinary(entry.shader.glprogid, binary_size, &length, &prog_format, - &data[sizeof(GLenum)]); - if (glGetError() != GL_NO_ERROR) - return false; - - std::memcpy(&data[0], &prog_format, sizeof(prog_format)); - return true; -} - -template -static void SaveProgramBinaryMap(CacheMapType& program_map, DiskCacheType& disk_cache) -{ - std::vector binary_data; - for (auto& entry : program_map) - { - if (entry.second.in_cache || entry.second.pending) - continue; - - // Entry is now in cache (even if it fails, we don't want to try to save it again). - entry.second.in_cache = true; - if (!GetProgramBinary(entry.second, binary_data)) - continue; - - disk_cache.Append(entry.first, &binary_data[0], static_cast(binary_data.size())); - } - - disk_cache.Sync(); -} - -void ProgramShaderCache::SaveProgramBinaries() -{ - SaveProgramBinaryMap(pshaders, s_program_disk_cache); - SaveProgramBinaryMap(ubershaders, s_uber_program_disk_cache); -} - -void ProgramShaderCache::DestroyShaders() -{ - glUseProgram(0); - - for (auto& entry : pshaders) - entry.second.Destroy(); - pshaders.clear(); - - for (auto& entry : ubershaders) - entry.second.Destroy(); - ubershaders.clear(); -} - const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* vertex_shader, const OGLShader* geometry_shader, const OGLShader* pixel_shader) @@ -1144,346 +783,4 @@ void ProgramShaderCache::CreateHeader() v > GlslEs300 ? "precision highp sampler2DMS;" : "", v >= GlslEs310 ? "precision highp image2DArray;" : ""); } - -void ProgramShaderCache::PrecompileUberShaders() -{ - bool success = true; - - UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& vuid) { - UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& puid) { - // UIDs must have compatible texgens, a mismatching combination will never be queried. - if (vuid.GetUidData()->num_texgens != puid.GetUidData()->num_texgens) - return; - - EnumerateGeometryShaderUids([&](const GeometryShaderUid& guid) { - if (guid.GetUidData()->numTexGens != vuid.GetUidData()->num_texgens) - return; - - UBERSHADERUID uid; - std::memcpy(&uid.vuid, &vuid, sizeof(uid.vuid)); - std::memcpy(&uid.puid, &puid, sizeof(uid.puid)); - std::memcpy(&uid.guid, &guid, sizeof(uid.guid)); - - // The ubershader may already exist if shader caching is enabled. - if (!success || ubershaders.find(uid) != ubershaders.end()) - return; - - PCacheEntry& entry = ubershaders[uid]; - entry.in_cache = false; - entry.pending = false; - - // Multi-context path? - if (s_async_compiler) - { - entry.pending = true; - s_async_compiler->QueueWorkItem( - s_async_compiler->CreateWorkItem(uid)); - return; - } - - ShaderHostConfig host_config = ShaderHostConfig::GetCurrent(); - ShaderCode vcode = - UberShader::GenVertexShader(APIType::OpenGL, host_config, uid.vuid.GetUidData()); - ShaderCode pcode = - UberShader::GenPixelShader(APIType::OpenGL, host_config, uid.puid.GetUidData()); - ShaderCode gcode; - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders && - !uid.guid.GetUidData()->IsPassthrough()) - { - GenerateGeometryShaderCode(APIType::OpenGL, host_config, uid.guid.GetUidData()); - } - - // Always background compile, even when it's not supported. - // This way hopefully the driver can still compile the shaders in parallel. - if (!CompileShader(entry.shader, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer())) - { - // Stop compiling shaders if any of them fail, no point continuing. - success = false; - return; - } - }); - }); - }); - - if (s_async_compiler) - { - s_async_compiler->WaitUntilCompletion([](size_t completed, size_t total) { - Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(), - static_cast(completed), static_cast(total)); - }); - s_async_compiler->RetrieveWorkItems(); - Host_UpdateProgressDialog("", -1, -1); - } - - if (!success) - { - PanicAlert("One or more ubershaders failed to compile. Disabling ubershaders."); - for (auto& it : ubershaders) - it.second.Destroy(); - ubershaders.clear(); - } -} - -bool ProgramShaderCache::SharedContextAsyncShaderCompiler::WorkerThreadInitMainThread(void** param) -{ - SharedContextData* ctx_data = new SharedContextData(); - ctx_data->context = GLInterface->CreateSharedContext(); - if (!ctx_data->context) - { - PanicAlert("Failed to create shared context for shader compiling."); - delete ctx_data; - return false; - } - - *param = ctx_data; - return true; -} - -bool ProgramShaderCache::SharedContextAsyncShaderCompiler::WorkerThreadInitWorkerThread(void* param) -{ - SharedContextData* ctx_data = reinterpret_cast(param); - if (!ctx_data->context->MakeCurrent()) - { - PanicAlert("Failed to make shared context current."); - ctx_data->context->Shutdown(); - delete ctx_data; - return false; - } - - CreatePrerenderArrays(ctx_data); - return true; -} - -void ProgramShaderCache::SharedContextAsyncShaderCompiler::WorkerThreadExit(void* param) -{ - SharedContextData* ctx_data = reinterpret_cast(param); - DestroyPrerenderArrays(ctx_data); - ctx_data->context->Shutdown(); - delete ctx_data; -} - -ProgramShaderCache::ShaderCompileWorkItem::ShaderCompileWorkItem(const SHADERUID& uid) -{ - std::memcpy(&m_uid, &uid, sizeof(m_uid)); -} - -bool ProgramShaderCache::ShaderCompileWorkItem::Compile() -{ - ShaderHostConfig host_config = ShaderHostConfig::GetCurrent(); - ShaderCode vcode = - GenerateVertexShaderCode(APIType::OpenGL, host_config, m_uid.vuid.GetUidData()); - ShaderCode pcode = GeneratePixelShaderCode(APIType::OpenGL, host_config, m_uid.puid.GetUidData()); - ShaderCode gcode; - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders && - !m_uid.guid.GetUidData()->IsPassthrough()) - gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, m_uid.guid.GetUidData()); - - CompileShader(m_program, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer()); - DrawPrerenderArray(m_program, - static_cast(m_uid.guid.GetUidData()->primitive_type)); - return true; -} - -void ProgramShaderCache::ShaderCompileWorkItem::Retrieve() -{ - auto iter = pshaders.find(m_uid); - if (iter != pshaders.end() && !iter->second.pending) - { - // Main thread already compiled this shader. - m_program.Destroy(); - return; - } - - PCacheEntry& entry = pshaders[m_uid]; - entry.shader = m_program; - entry.in_cache = false; - entry.pending = false; -} - -ProgramShaderCache::UberShaderCompileWorkItem::UberShaderCompileWorkItem(const UBERSHADERUID& uid) -{ - std::memcpy(&m_uid, &uid, sizeof(m_uid)); -} - -bool ProgramShaderCache::UberShaderCompileWorkItem::Compile() -{ - ShaderHostConfig host_config = ShaderHostConfig::GetCurrent(); - ShaderCode vcode = - UberShader::GenVertexShader(APIType::OpenGL, host_config, m_uid.vuid.GetUidData()); - ShaderCode pcode = - UberShader::GenPixelShader(APIType::OpenGL, host_config, m_uid.puid.GetUidData()); - ShaderCode gcode; - if (g_ActiveConfig.backend_info.bSupportsGeometryShaders && - !m_uid.guid.GetUidData()->IsPassthrough()) - gcode = GenerateGeometryShaderCode(APIType::OpenGL, host_config, m_uid.guid.GetUidData()); - - CompileShader(m_program, vcode.GetBuffer(), pcode.GetBuffer(), gcode.GetBuffer()); - DrawPrerenderArray(m_program, - static_cast(m_uid.guid.GetUidData()->primitive_type)); - return true; -} - -void ProgramShaderCache::UberShaderCompileWorkItem::Retrieve() -{ - auto iter = ubershaders.find(m_uid); - if (iter != ubershaders.end() && !iter->second.pending) - { - // Main thread already compiled this shader. - m_program.Destroy(); - return; - } - - PCacheEntry& entry = ubershaders[m_uid]; - entry.shader = m_program; - entry.in_cache = false; - entry.pending = false; -} - -void ProgramShaderCache::CreatePrerenderArrays(SharedContextData* data) -{ - // Create a framebuffer object to render into. - // This is because in EGL, and potentially GLX, we have a surfaceless context. - glGenTextures(1, &data->prerender_FBO_tex); - glBindTexture(GL_TEXTURE_2D_ARRAY, data->prerender_FBO_tex); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 1); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - glGenTextures(1, &data->prerender_FBO_depth); - glBindTexture(GL_TEXTURE_2D_ARRAY, data->prerender_FBO_depth); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 1); - glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT32F, 1, 1, 1, 0, GL_DEPTH_COMPONENT, - GL_FLOAT, nullptr); - glGenFramebuffers(1, &data->prerender_FBO); - glBindFramebuffer(GL_FRAMEBUFFER, data->prerender_FBO); - glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, data->prerender_FBO_tex, 0, 0); - glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, data->prerender_FBO_depth, 0, 0); - - // Create VAO for the prerender vertices. - // We don't use the normal VAO map, since we need to change the VBO pointer. - glGenVertexArrays(1, &data->prerender_VAO); - glBindVertexArray(data->prerender_VAO); - - // Create and populate the prerender VBO. We need enough space to draw 3 triangles. - static constexpr float vbo_data[] = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; - constexpr u32 vbo_stride = sizeof(float) * 3; - glGenBuffers(1, &data->prerender_VBO); - glBindBuffer(GL_ARRAY_BUFFER, data->prerender_VBO); - glBufferData(GL_ARRAY_BUFFER, sizeof(vbo_data), vbo_data, GL_STATIC_DRAW); - - // We only need a position in our prerender vertex. - glEnableVertexAttribArray(SHADER_POSITION_ATTRIB); - glVertexAttribPointer(SHADER_POSITION_ATTRIB, 3, GL_FLOAT, GL_FALSE, vbo_stride, nullptr); - - // The other attributes have to be active to avoid variant generation. - glEnableVertexAttribArray(SHADER_POSMTX_ATTRIB); - glVertexAttribIPointer(SHADER_POSMTX_ATTRIB, 1, GL_UNSIGNED_BYTE, vbo_stride, nullptr); - for (u32 i = 0; i < 3; i++) - { - glEnableVertexAttribArray(SHADER_NORM0_ATTRIB + i); - glVertexAttribPointer(SHADER_NORM0_ATTRIB + i, 3, GL_FLOAT, GL_FALSE, vbo_stride, nullptr); - } - for (u32 i = 0; i < 2; i++) - { - glEnableVertexAttribArray(SHADER_COLOR0_ATTRIB + i); - glVertexAttribPointer(SHADER_COLOR0_ATTRIB + i, 4, GL_UNSIGNED_BYTE, GL_TRUE, vbo_stride, - nullptr); - } - for (u32 i = 0; i < 8; i++) - { - glEnableVertexAttribArray(SHADER_TEXTURE0_ATTRIB + i); - glVertexAttribPointer(SHADER_TEXTURE0_ATTRIB + i, 3, GL_FLOAT, GL_FALSE, vbo_stride, nullptr); - } - - // We need an index buffer to set up the same drawing state on Mesa. - static constexpr u16 ibo_data[] = {0, 1, 2}; - glGenBuffers(1, &data->prerender_IBO); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data->prerender_IBO); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ibo_data), ibo_data, GL_STATIC_DRAW); - - // Mesa also requires the primitive restart state matches? - if (g_ActiveConfig.backend_info.bSupportsPrimitiveRestart) - { - if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGLES3) - { - glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); - } - else - { - if (GLExtensions::Version() >= 310) - { - glEnable(GL_PRIMITIVE_RESTART); - glPrimitiveRestartIndex(65535); - } - else - { - glEnableClientState(GL_PRIMITIVE_RESTART_NV); - glPrimitiveRestartIndexNV(65535); - } - } - } -} - -void ProgramShaderCache::DestroyPrerenderArrays(SharedContextData* data) -{ - if (data->prerender_VAO) - { - glDeleteVertexArrays(1, &data->prerender_VAO); - data->prerender_VAO = 0; - } - if (data->prerender_VBO) - { - glDeleteBuffers(1, &data->prerender_VBO); - data->prerender_VBO = 0; - } - if (data->prerender_IBO) - { - glDeleteBuffers(1, &data->prerender_IBO); - data->prerender_IBO = 0; - } - if (data->prerender_FBO) - { - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDeleteFramebuffers(1, &data->prerender_FBO); - data->prerender_FBO = 0; - } - if (data->prerender_FBO_tex) - { - glDeleteTextures(1, &data->prerender_FBO_tex); - data->prerender_FBO_tex = 0; - } - if (data->prerender_FBO_depth) - { - glDeleteTextures(1, &data->prerender_FBO_depth); - data->prerender_FBO_depth = 0; - } -} - -void ProgramShaderCache::DrawPrerenderArray(const SHADER& shader, PrimitiveType primitive_type) -{ - // This is called on a worker thread, so we don't want to use the normal binding process. - glUseProgram(shader.glprogid); - - // The number of primitives drawn depends on the type. - switch (primitive_type) - { - case PrimitiveType::Points: - glDrawElements(GL_POINTS, 1, GL_UNSIGNED_SHORT, nullptr); - break; - case PrimitiveType::Lines: - glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, nullptr); - break; - case PrimitiveType::Triangles: - glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, nullptr); - break; - case PrimitiveType::TriangleStrip: - glDrawElements(GL_TRIANGLE_STRIP, 3, GL_UNSIGNED_SHORT, nullptr); - break; - } - - // Has to be finished by the time the main thread picks it up. - GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); - glDeleteSync(sync); -} } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h index 6db44460a1..af2aa10383 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h @@ -11,16 +11,6 @@ #include #include "Common/GL/GLUtil.h" -#include "Common/LinearDiskCache.h" - -#include "VideoCommon/AsyncShaderCompiler.h" -#include "VideoCommon/GeometryShaderGen.h" -#include "VideoCommon/PixelShaderGen.h" -#include "VideoCommon/UberShaderPixel.h" -#include "VideoCommon/UberShaderVertex.h" -#include "VideoCommon/VertexShaderGen.h" - -class cInterfaceBase; namespace OGL { @@ -28,41 +18,6 @@ class OGLShader; class GLVertexFormat; class StreamBuffer; -class SHADERUID -{ -public: - VertexShaderUid vuid; - PixelShaderUid puid; - GeometryShaderUid guid; - - bool operator<(const SHADERUID& r) const - { - return std::tie(vuid, puid, guid) < std::tie(r.vuid, r.puid, r.guid); - } - - bool operator==(const SHADERUID& r) const - { - return std::tie(vuid, puid, guid) == std::tie(r.vuid, r.puid, r.guid); - } -}; -class UBERSHADERUID -{ -public: - UberShader::VertexShaderUid vuid; - UberShader::PixelShaderUid puid; - GeometryShaderUid guid; - - bool operator<(const UBERSHADERUID& r) const - { - return std::tie(vuid, puid, guid) < std::tie(r.vuid, r.puid, r.guid); - } - - bool operator==(const UBERSHADERUID& r) const - { - return std::tie(vuid, puid, guid) == std::tie(r.vuid, r.puid, r.guid); - } -}; - struct SHADER { void Destroy() @@ -112,18 +67,6 @@ struct PipelineProgram class ProgramShaderCache { public: - struct PCacheEntry - { - SHADER shader; - bool in_cache; - bool pending; - - void Destroy() { shader.Destroy(); } - }; - - static PCacheEntry GetShaderProgram(); - static SHADER* SetShader(PrimitiveType primitive_type, const GLVertexFormat* vertex_format); - static SHADER* SetUberShader(PrimitiveType primitive_type, const GLVertexFormat* vertex_format); static void BindVertexFormat(const GLVertexFormat* vertex_format); static void InvalidateVertexFormat(); static void InvalidateLastProgram(); @@ -141,11 +84,8 @@ public: static void UploadConstants(); static void Init(); - static void Reload(); static void Shutdown(); static void CreateHeader(); - static void RetrieveAsyncShaders(); - static void PrecompileUberShaders(); static const PipelineProgram* GetPipelineProgram(const OGLShader* vertex_shader, const OGLShader* geometry_shader, @@ -153,103 +93,15 @@ public: static void ReleasePipelineProgram(const PipelineProgram* prog); private: - template - class ProgramShaderCacheInserter : public LinearDiskCacheReader - { - public: - ProgramShaderCacheInserter(std::map& shader_map) - : m_shader_map(shader_map) - { - } - - void Read(const UIDType& key, const u8* value, u32 value_size) override - { - if (m_shader_map.find(key) != m_shader_map.end()) - return; - - PCacheEntry& entry = m_shader_map[key]; - if (!CreateCacheEntryFromBinary(&entry, value, value_size)) - { - m_shader_map.erase(key); - return; - } - } - - private: - std::map& m_shader_map; - }; - - class SharedContextAsyncShaderCompiler : public VideoCommon::AsyncShaderCompiler - { - protected: - bool WorkerThreadInitMainThread(void** param) override; - bool WorkerThreadInitWorkerThread(void* param) override; - void WorkerThreadExit(void* param) override; - }; - - struct SharedContextData - { - std::unique_ptr context; - GLuint prerender_FBO; - GLuint prerender_FBO_tex; - GLuint prerender_FBO_depth; - GLuint prerender_VBO; - GLuint prerender_VAO; - GLuint prerender_IBO; - }; - - class ShaderCompileWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit ShaderCompileWorkItem(const SHADERUID& uid); - - bool Compile() override; - void Retrieve() override; - - private: - SHADERUID m_uid; - SHADER m_program; - }; - - class UberShaderCompileWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit UberShaderCompileWorkItem(const UBERSHADERUID& uid); - - bool Compile() override; - void Retrieve() override; - - private: - UBERSHADERUID m_uid; - SHADER m_program; - }; - - typedef std::map PCache; - typedef std::map UberPCache; typedef std::unordered_map, PipelineProgramKeyHash> PipelineProgramMap; static void CreateAttributelessVAO(); - static GLuint CreateProgramFromBinary(const u8* value, u32 value_size); - static bool CreateCacheEntryFromBinary(PCacheEntry* entry, const u8* value, u32 value_size); - static void LoadProgramBinaries(); - static void SaveProgramBinaries(); - static void DestroyShaders(); - static void CreatePrerenderArrays(SharedContextData* data); - static void DestroyPrerenderArrays(SharedContextData* data); - static void DrawPrerenderArray(const SHADER& shader, PrimitiveType primitive_type); - static PCache pshaders; - static UberPCache ubershaders; static PipelineProgramMap pipelineprograms; static std::mutex pipelineprogramlock; - static PCacheEntry* last_entry; - static PCacheEntry* last_uber_entry; - static SHADERUID last_uid; - static UBERSHADERUID last_uber_uid; - static std::unique_ptr s_async_compiler; static u32 s_ubo_buffer_size; static s32 s_ubo_align; diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index ae9e45db99..ac08ae57cd 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -1465,7 +1465,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region // Clean out old stuff from caches. It's not worth it to clean out the shader caches. g_texture_cache->Cleanup(frameCount); - ProgramShaderCache::RetrieveAsyncShaders(); RestoreAPIState(); @@ -1479,8 +1478,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region g_sampler_cache->Clear(); // Invalidate shader cache when the host config changes. - if (CheckForHostConfigChanges()) - ProgramShaderCache::Reload(); + CheckForHostConfigChanges(); // For testing zbuffer targets. // Renderer::SetZBufferRender(); @@ -1602,21 +1600,6 @@ void Renderer::ApplyDepthState(const DepthState& state) } } -void Renderer::SetRasterizationState(const RasterizationState& state) -{ - ApplyRasterizationState(state); -} - -void Renderer::SetDepthState(const DepthState& state) -{ - ApplyDepthState(state); -} - -void Renderer::SetBlendingState(const BlendingState& state) -{ - ApplyBlendingState(state); -} - void Renderer::SetPipeline(const AbstractPipeline* pipeline) { // Not all shader changes currently go through SetPipeline, so we can't diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index e28f2b52d1..a9124ad4fb 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -105,10 +105,7 @@ public: void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, const ClearColor& color_value = {}, float depth_value = 0.0f) override; - void SetBlendingState(const BlendingState& state) override; void SetScissorRect(const MathUtil::Rectangle& rc) override; - void SetRasterizationState(const RasterizationState& state) override; - void SetDepthState(const DepthState& state) override; void SetTexture(u32 index, const AbstractTexture* texture) override; void SetSamplerState(u32 index, const SamplerState& state) override; void UnbindTexture(const AbstractTexture* texture) override; diff --git a/Source/Core/VideoBackends/OGL/VertexManager.cpp b/Source/Core/VideoBackends/OGL/VertexManager.cpp index f98c72b1ad..00b20e3c76 100644 --- a/Source/Core/VideoBackends/OGL/VertexManager.cpp +++ b/Source/Core/VideoBackends/OGL/VertexManager.cpp @@ -162,8 +162,6 @@ void VertexManager::vFlush() GLVertexFormat* nativeVertexFmt = (GLVertexFormat*)VertexLoaderManager::GetCurrentVertexFormat(); u32 stride = nativeVertexFmt->GetVertexStride(); - ProgramShaderCache::SetShader(m_current_primitive_type, nativeVertexFmt); - PrepareDrawBuffers(stride); // upload global constants @@ -174,7 +172,11 @@ void VertexManager::vFlush() glEnable(GL_STENCIL_TEST); } - Draw(stride); + if (m_current_pipeline_object) + { + g_renderer->SetPipeline(m_current_pipeline_object); + Draw(stride); + } if (::BoundingBox::active && !g_Config.BBoxUseFragmentShaderImplementation()) { diff --git a/Source/Core/VideoBackends/OGL/main.cpp b/Source/Core/VideoBackends/OGL/main.cpp index 1e88680606..99952d1d9f 100644 --- a/Source/Core/VideoBackends/OGL/main.cpp +++ b/Source/Core/VideoBackends/OGL/main.cpp @@ -175,17 +175,20 @@ bool VideoBackend::Initialize(void* window_handle) ProgramShaderCache::Init(); g_texture_cache = std::make_unique(); g_sampler_cache = std::make_unique(); + g_shader_cache = std::make_unique(); static_cast(g_renderer.get())->Init(); TextureConverter::Init(); BoundingBox::Init(g_renderer->GetTargetWidth(), g_renderer->GetTargetHeight()); - return true; + return g_shader_cache->Initialize(); } void VideoBackend::Shutdown() { + g_shader_cache->Shutdown(); g_renderer->Shutdown(); BoundingBox::Shutdown(); TextureConverter::Shutdown(); + g_shader_cache.reset(); g_sampler_cache.reset(); g_texture_cache.reset(); ProgramShaderCache::Shutdown(); diff --git a/Source/Core/VideoBackends/Software/SWmain.cpp b/Source/Core/VideoBackends/Software/SWmain.cpp index 20a8958d18..504c008511 100644 --- a/Source/Core/VideoBackends/Software/SWmain.cpp +++ b/Source/Core/VideoBackends/Software/SWmain.cpp @@ -96,11 +96,15 @@ bool VideoSoftware::Initialize(void* window_handle) g_vertex_manager = std::make_unique(); g_perf_query = std::make_unique(); g_texture_cache = std::make_unique(); - return true; + g_shader_cache = std::make_unique(); + return g_shader_cache->Initialize(); } void VideoSoftware::Shutdown() { + if (g_shader_cache) + g_shader_cache->Shutdown(); + if (g_renderer) g_renderer->Shutdown(); diff --git a/Source/Core/VideoBackends/Vulkan/Constants.h b/Source/Core/VideoBackends/Vulkan/Constants.h index c6758c6fdc..f2af115b2a 100644 --- a/Source/Core/VideoBackends/Vulkan/Constants.h +++ b/Source/Core/VideoBackends/Vulkan/Constants.h @@ -48,8 +48,7 @@ enum DESCRIPTOR_SET_BIND_POINT // - Standard // - Per-stage UBO (VS/GS/PS, VS constants accessible from PS) // - 8 combined image samplers (accessible from PS) -// - BBox Enabled -// - Same as standard, plus a single SSBO accessible from PS +// - 1 SSBO accessible from PS if supported // - Push Constant // - Same as standard, plus 128 bytes of push constants, accessible from all stages. // - Texture Decoding @@ -67,7 +66,6 @@ enum DESCRIPTOR_SET_BIND_POINT enum PIPELINE_LAYOUT { PIPELINE_LAYOUT_STANDARD, - PIPELINE_LAYOUT_BBOX, PIPELINE_LAYOUT_PUSH_CONSTANT, PIPELINE_LAYOUT_TEXTURE_CONVERSION, PIPELINE_LAYOUT_UTILITY, diff --git a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp index cbd28b3630..8f5721b4d3 100644 --- a/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/ObjectCache.cpp @@ -109,6 +109,9 @@ bool ObjectCache::CreateDescriptorSetLayouts() static const VkDescriptorSetLayoutBinding single_ubo_set_bindings[] = { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_FRAGMENT_BIT}; + + // The geometry shader buffer must be last in this binding set, as we don't include it + // if geometry shaders are not supported by the device. See the decrement below. static const VkDescriptorSetLayoutBinding per_stage_ubo_set_bindings[] = { {UBO_DESCRIPTOR_SET_BINDING_PS, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_FRAGMENT_BIT}, @@ -139,7 +142,7 @@ bool ObjectCache::CreateDescriptorSetLayouts() {7, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT}, }; - static const VkDescriptorSetLayoutCreateInfo create_infos[NUM_DESCRIPTOR_SET_LAYOUTS] = { + VkDescriptorSetLayoutCreateInfo create_infos[NUM_DESCRIPTOR_SET_LAYOUTS] = { {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0, static_cast(ArraySize(single_ubo_set_bindings)), single_ubo_set_bindings}, {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0, @@ -153,6 +156,10 @@ bool ObjectCache::CreateDescriptorSetLayouts() {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0, static_cast(ArraySize(compute_set_bindings)), compute_set_bindings}}; + // Don't set the GS bit if geometry shaders aren't available. + if (!g_vulkan_context->SupportsGeometryShaders()) + create_infos[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS].bindingCount--; + for (size_t i = 0; i < NUM_DESCRIPTOR_SET_LAYOUTS; i++) { VkResult res = vkCreateDescriptorSetLayout(g_vulkan_context->GetDevice(), &create_infos[i], @@ -180,11 +187,10 @@ bool ObjectCache::CreatePipelineLayouts() { VkResult res; - // Descriptor sets for each pipeline layout + // Descriptor sets for each pipeline layout. + // In the standard set, the SSBO must be the last descriptor, as we do not include it + // when fragment stores and atomics are not supported by the device. VkDescriptorSetLayout standard_sets[] = { - m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS], - m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS]}; - VkDescriptorSetLayout bbox_sets[] = { m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PER_STAGE_UNIFORM_BUFFERS], m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS], m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS]}; @@ -207,10 +213,6 @@ bool ObjectCache::CreatePipelineLayouts() {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, static_cast(ArraySize(standard_sets)), standard_sets, 0, nullptr}, - // BBox - {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, - static_cast(ArraySize(bbox_sets)), bbox_sets, 0, nullptr}, - // Push Constant {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, static_cast(ArraySize(standard_sets)), standard_sets, 1, &push_constant_range}, @@ -228,6 +230,10 @@ bool ObjectCache::CreatePipelineLayouts() {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0, static_cast(ArraySize(compute_sets)), compute_sets, 1, &compute_push_constant_range}}; + // If bounding box is unsupported, don't bother with the SSBO descriptor set. + if (!g_vulkan_context->SupportsBoundingBox()) + pipeline_layout_info[PIPELINE_LAYOUT_STANDARD].setLayoutCount--; + for (size_t i = 0; i < NUM_PIPELINE_LAYOUTS; i++) { if ((res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &pipeline_layout_info[i], diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.cpp b/Source/Core/VideoBackends/Vulkan/Renderer.cpp index 588e4b0919..1beb4f3461 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.cpp +++ b/Source/Core/VideoBackends/Vulkan/Renderer.cpp @@ -224,7 +224,7 @@ std::tuple Renderer::UpdateUtilityUniformBuffer(const void* unifo void Renderer::SetPipeline(const AbstractPipeline* pipeline) { - m_graphics_pipeline = static_cast(pipeline); + StateTracker::GetInstance()->SetPipeline(static_cast(pipeline)); } void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, const void* vertices, @@ -305,7 +305,7 @@ void Renderer::DrawUtilityPipeline(const void* uniforms, u32 uniforms_size, cons // Build commands. VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer(); vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - m_graphics_pipeline->GetPipeline()); + StateTracker::GetInstance()->GetPipeline()->GetVkPipeline()); if (vertex_buffer != VK_NULL_HANDLE) vkCmdBindVertexBuffers(command_buffer, 0, 1, &vertex_buffer, &vertex_buffer_offset); @@ -759,9 +759,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region // Clean up stale textures. TextureCache::GetInstance()->Cleanup(frameCount); - - // Pull in now-ready async shaders. - g_shader_cache->RetrieveAsyncShaders(); } void Renderer::DrawScreen(VKTexture* xfb_texture, const EFBRectangle& xfb_region) @@ -975,10 +972,8 @@ void Renderer::CheckForConfigChanges() RecreateEFBFramebuffer(); RecompileShaders(); FramebufferManager::GetInstance()->RecompileShaders(); - g_shader_cache->ReloadShaderAndPipelineCaches(); + g_shader_cache->ReloadPipelineCache(); g_shader_cache->RecompileSharedShaders(); - StateTracker::GetInstance()->InvalidateShaderPointers(); - StateTracker::GetInstance()->ReloadPipelineUIDCache(); } // For vsync, we need to change the present mode, which means recreating the swap chain. @@ -1021,8 +1016,6 @@ void Renderer::BindEFBToStateTracker() FramebufferManager::GetInstance()->GetEFBClearRenderPass()); StateTracker::GetInstance()->SetFramebuffer( FramebufferManager::GetInstance()->GetEFBFramebuffer(), framebuffer_size); - StateTracker::GetInstance()->SetMultisamplingstate( - FramebufferManager::GetInstance()->GetEFBMultisamplingState()); m_current_framebuffer = nullptr; m_current_framebuffer_width = FramebufferManager::GetInstance()->GetEFBWidth(); m_current_framebuffer_height = FramebufferManager::GetInstance()->GetEFBHeight(); @@ -1125,21 +1118,6 @@ void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, num_clear_values); } -void Renderer::SetRasterizationState(const RasterizationState& state) -{ - StateTracker::GetInstance()->SetRasterizationState(state); -} - -void Renderer::SetDepthState(const DepthState& state) -{ - StateTracker::GetInstance()->SetDepthState(state); -} - -void Renderer::SetBlendingState(const BlendingState& state) -{ - StateTracker::GetInstance()->SetBlendState(state); -} - void Renderer::SetTexture(u32 index, const AbstractTexture* texture) { // Texture should always be in SHADER_READ_ONLY layout prior to use. diff --git a/Source/Core/VideoBackends/Vulkan/Renderer.h b/Source/Core/VideoBackends/Vulkan/Renderer.h index e62849ac19..b2662a3d4b 100644 --- a/Source/Core/VideoBackends/Vulkan/Renderer.h +++ b/Source/Core/VideoBackends/Vulkan/Renderer.h @@ -77,10 +77,7 @@ public: void SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, const ClearColor& color_value = {}, float depth_value = 0.0f) override; - void SetBlendingState(const BlendingState& state) override; void SetScissorRect(const MathUtil::Rectangle& rc) override; - void SetRasterizationState(const RasterizationState& state) override; - void SetDepthState(const DepthState& state) override; void SetTexture(u32 index, const AbstractTexture* texture) override; void SetSamplerState(u32 index, const SamplerState& state) override; void UnbindTexture(const AbstractTexture* texture) override; @@ -135,6 +132,5 @@ private: // Shaders used for clear/blit. VkShaderModule m_clear_fragment_shader = VK_NULL_HANDLE; - const VKPipeline* m_graphics_pipeline = nullptr; }; } diff --git a/Source/Core/VideoBackends/Vulkan/ShaderCache.cpp b/Source/Core/VideoBackends/Vulkan/ShaderCache.cpp index 9f77958b0f..2fa75f4913 100644 --- a/Source/Core/VideoBackends/Vulkan/ShaderCache.cpp +++ b/Source/Core/VideoBackends/Vulkan/ShaderCache.cpp @@ -23,12 +23,7 @@ #include "VideoBackends/Vulkan/Util.h" #include "VideoBackends/Vulkan/VertexFormat.h" #include "VideoBackends/Vulkan/VulkanContext.h" -#include "VideoCommon/AsyncShaderCompiler.h" -#include "VideoCommon/GeometryShaderGen.h" #include "VideoCommon/Statistics.h" -#include "VideoCommon/UberShaderPixel.h" -#include "VideoCommon/UberShaderVertex.h" -#include "VideoCommon/VertexLoaderManager.h" namespace Vulkan { @@ -41,7 +36,6 @@ ShaderCache::ShaderCache() ShaderCache::~ShaderCache() { DestroyPipelineCache(); - DestroyShaderCaches(); DestroySharedShaders(); } @@ -49,7 +43,6 @@ bool ShaderCache::Initialize() { if (g_ActiveConfig.bShaderCache) { - LoadShaderCaches(); if (!LoadPipelineCache()) return false; } @@ -62,21 +55,11 @@ bool ShaderCache::Initialize() if (!CompileSharedShaders()) return false; - m_async_shader_compiler = std::make_unique(); - m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.CanPrecompileUberShaders() ? - g_ActiveConfig.GetShaderPrecompilerThreads() : - g_ActiveConfig.GetShaderCompilerThreads()); return true; } void ShaderCache::Shutdown() { - if (m_async_shader_compiler) - { - m_async_shader_compiler->StopWorkerThreads(); - m_async_shader_compiler->RetrieveWorkItems(); - } - if (g_ActiveConfig.bShaderCache && m_pipeline_cache != VK_NULL_HANDLE) SavePipelineCache(); } @@ -392,40 +375,14 @@ VkPipeline ShaderCache::CreatePipeline(const PipelineInfo& info) } VkPipeline ShaderCache::GetPipeline(const PipelineInfo& info) -{ - return GetPipelineWithCacheResult(info).first; -} - -std::pair ShaderCache::GetPipelineWithCacheResult(const PipelineInfo& info) { auto iter = m_pipeline_objects.find(info); if (iter != m_pipeline_objects.end()) - { - // If it's background compiling, ignore it, and recompile it synchronously. - if (!iter->second.second) - return std::make_pair(iter->second.first, true); - else - m_pipeline_objects.erase(iter); - } + return iter->second; VkPipeline pipeline = CreatePipeline(info); - m_pipeline_objects.emplace(info, std::make_pair(pipeline, false)); - _assert_(pipeline != VK_NULL_HANDLE); - return {pipeline, false}; -} - -std::pair, bool> -ShaderCache::GetPipelineWithCacheResultAsync(const PipelineInfo& info) -{ - auto iter = m_pipeline_objects.find(info); - if (iter != m_pipeline_objects.end()) - return std::make_pair(iter->second, true); - - // Kick a job off. - m_async_shader_compiler->QueueWorkItem( - m_async_shader_compiler->CreateWorkItem(info)); - m_pipeline_objects.emplace(info, std::make_pair(static_cast(VK_NULL_HANDLE), true)); - return std::make_pair(std::make_pair(static_cast(VK_NULL_HANDLE), true), false); + m_pipeline_objects.emplace(info, pipeline); + return pipeline; } VkPipeline ShaderCache::CreateComputePipeline(const ComputePipelineInfo& info) @@ -465,11 +422,10 @@ VkPipeline ShaderCache::GetComputePipeline(const ComputePipelineInfo& info) void ShaderCache::ClearPipelineCache() { - // TODO: Stop any async compiling happening. for (const auto& it : m_pipeline_objects) { - if (it.second.first != VK_NULL_HANDLE) - vkDestroyPipeline(g_vulkan_context->GetDevice(), it.second.first, nullptr); + if (it.second != VK_NULL_HANDLE) + vkDestroyPipeline(g_vulkan_context->GetDevice(), it.second, nullptr); } m_pipeline_objects.clear(); @@ -673,266 +629,6 @@ void ShaderCache::SavePipelineCache() disk_cache.Close(); } -// Cache inserter that is called back when reading from the file -template -struct ShaderCacheReader : public LinearDiskCacheReader -{ - ShaderCacheReader(std::map>& shader_map) - : m_shader_map(shader_map) - { - } - void Read(const Uid& key, const u32* value, u32 value_size) override - { - // We don't insert null modules into the shader map since creation could succeed later on. - // e.g. we're generating bad code, but fix this in a later version, and for some reason - // the cache is not invalidated. - VkShaderModule module = Util::CreateShaderModule(value, value_size); - if (module == VK_NULL_HANDLE) - return; - - m_shader_map.emplace(key, std::make_pair(module, false)); - } - - std::map>& m_shader_map; -}; - -void ShaderCache::LoadShaderCaches() -{ - ShaderCacheReader vs_reader(m_vs_cache.shader_map); - m_vs_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "VS", true, true), - vs_reader); - - ShaderCacheReader ps_reader(m_ps_cache.shader_map); - m_ps_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "PS", true, true), - ps_reader); - - if (g_vulkan_context->SupportsGeometryShaders()) - { - ShaderCacheReader gs_reader(m_gs_cache.shader_map); - m_gs_cache.disk_cache.OpenAndRead(GetDiskShaderCacheFileName(APIType::Vulkan, "GS", true, true), - gs_reader); - } - - ShaderCacheReader uber_vs_reader(m_uber_vs_cache.shader_map); - m_uber_vs_cache.disk_cache.OpenAndRead( - GetDiskShaderCacheFileName(APIType::Vulkan, "UberVS", false, true), uber_vs_reader); - ShaderCacheReader uber_ps_reader(m_uber_ps_cache.shader_map); - m_uber_ps_cache.disk_cache.OpenAndRead( - GetDiskShaderCacheFileName(APIType::Vulkan, "UberPS", false, true), uber_ps_reader); - - SETSTAT(stats.numPixelShadersCreated, static_cast(m_ps_cache.shader_map.size())); - SETSTAT(stats.numPixelShadersAlive, static_cast(m_ps_cache.shader_map.size())); - SETSTAT(stats.numVertexShadersCreated, static_cast(m_vs_cache.shader_map.size())); - SETSTAT(stats.numVertexShadersAlive, static_cast(m_vs_cache.shader_map.size())); -} - -template -static void DestroyShaderCache(T& cache) -{ - cache.disk_cache.Sync(); - cache.disk_cache.Close(); - for (const auto& it : cache.shader_map) - { - if (it.second.first != VK_NULL_HANDLE) - vkDestroyShaderModule(g_vulkan_context->GetDevice(), it.second.first, nullptr); - } - cache.shader_map.clear(); -} - -void ShaderCache::DestroyShaderCaches() -{ - DestroyShaderCache(m_vs_cache); - DestroyShaderCache(m_ps_cache); - - if (g_vulkan_context->SupportsGeometryShaders()) - DestroyShaderCache(m_gs_cache); - - DestroyShaderCache(m_uber_vs_cache); - DestroyShaderCache(m_uber_ps_cache); - - SETSTAT(stats.numPixelShadersCreated, 0); - SETSTAT(stats.numPixelShadersAlive, 0); - SETSTAT(stats.numVertexShadersCreated, 0); - SETSTAT(stats.numVertexShadersAlive, 0); -} - -VkShaderModule ShaderCache::GetVertexShaderForUid(const VertexShaderUid& uid) -{ - auto it = m_vs_cache.shader_map.find(uid); - if (it != m_vs_cache.shader_map.end()) - { - // If it's pending, compile it synchronously. - if (!it->second.second) - return it->second.first; - else - m_vs_cache.shader_map.erase(it); - } - - // Not in the cache, so compile the shader. - ShaderCompiler::SPIRVCodeVector spv; - VkShaderModule module = VK_NULL_HANDLE; - ShaderCode source_code = - GenerateVertexShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - if (ShaderCompiler::CompileVertexShader(&spv, source_code.GetBuffer().c_str(), - source_code.GetBuffer().length())) - { - module = Util::CreateShaderModule(spv.data(), spv.size()); - - // Append to shader cache if it created successfully. - if (module != VK_NULL_HANDLE) - { - m_vs_cache.disk_cache.Append(uid, spv.data(), static_cast(spv.size())); - INCSTAT(stats.numVertexShadersCreated); - INCSTAT(stats.numVertexShadersAlive); - } - } - - // We still insert null entries to prevent further compilation attempts. - m_vs_cache.shader_map.emplace(uid, std::make_pair(module, false)); - return module; -} - -VkShaderModule ShaderCache::GetGeometryShaderForUid(const GeometryShaderUid& uid) -{ - _assert_(g_vulkan_context->SupportsGeometryShaders()); - auto it = m_gs_cache.shader_map.find(uid); - if (it != m_gs_cache.shader_map.end()) - { - // If it's pending, compile it synchronously. - if (!it->second.second) - return it->second.first; - else - m_gs_cache.shader_map.erase(it); - } - - // Not in the cache, so compile the shader. - ShaderCompiler::SPIRVCodeVector spv; - VkShaderModule module = VK_NULL_HANDLE; - ShaderCode source_code = - GenerateGeometryShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - if (ShaderCompiler::CompileGeometryShader(&spv, source_code.GetBuffer().c_str(), - source_code.GetBuffer().length())) - { - module = Util::CreateShaderModule(spv.data(), spv.size()); - - // Append to shader cache if it created successfully. - if (module != VK_NULL_HANDLE) - m_gs_cache.disk_cache.Append(uid, spv.data(), static_cast(spv.size())); - } - - // We still insert null entries to prevent further compilation attempts. - m_gs_cache.shader_map.emplace(uid, std::make_pair(module, false)); - return module; -} - -VkShaderModule ShaderCache::GetPixelShaderForUid(const PixelShaderUid& uid) -{ - auto it = m_ps_cache.shader_map.find(uid); - if (it != m_ps_cache.shader_map.end()) - { - // If it's pending, compile it synchronously. - if (!it->second.second) - return it->second.first; - else - m_ps_cache.shader_map.erase(it); - } - - // Not in the cache, so compile the shader. - ShaderCompiler::SPIRVCodeVector spv; - VkShaderModule module = VK_NULL_HANDLE; - ShaderCode source_code = - GeneratePixelShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - if (ShaderCompiler::CompileFragmentShader(&spv, source_code.GetBuffer().c_str(), - source_code.GetBuffer().length())) - { - module = Util::CreateShaderModule(spv.data(), spv.size()); - - // Append to shader cache if it created successfully. - if (module != VK_NULL_HANDLE) - { - m_ps_cache.disk_cache.Append(uid, spv.data(), static_cast(spv.size())); - INCSTAT(stats.numPixelShadersCreated); - INCSTAT(stats.numPixelShadersAlive); - } - } - - // We still insert null entries to prevent further compilation attempts. - m_ps_cache.shader_map.emplace(uid, std::make_pair(module, false)); - return module; -} - -VkShaderModule ShaderCache::GetVertexUberShaderForUid(const UberShader::VertexShaderUid& uid) -{ - auto it = m_uber_vs_cache.shader_map.find(uid); - if (it != m_uber_vs_cache.shader_map.end()) - { - // If it's pending, compile it synchronously. - if (!it->second.second) - return it->second.first; - else - m_uber_vs_cache.shader_map.erase(it); - } - - // Not in the cache, so compile the shader. - ShaderCompiler::SPIRVCodeVector spv; - VkShaderModule module = VK_NULL_HANDLE; - ShaderCode source_code = UberShader::GenVertexShader( - APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - if (ShaderCompiler::CompileVertexShader(&spv, source_code.GetBuffer().c_str(), - source_code.GetBuffer().length())) - { - module = Util::CreateShaderModule(spv.data(), spv.size()); - - // Append to shader cache if it created successfully. - if (module != VK_NULL_HANDLE) - { - m_uber_vs_cache.disk_cache.Append(uid, spv.data(), static_cast(spv.size())); - INCSTAT(stats.numVertexShadersCreated); - INCSTAT(stats.numVertexShadersAlive); - } - } - - // We still insert null entries to prevent further compilation attempts. - m_uber_vs_cache.shader_map.emplace(uid, std::make_pair(module, false)); - return module; -} - -VkShaderModule ShaderCache::GetPixelUberShaderForUid(const UberShader::PixelShaderUid& uid) -{ - auto it = m_uber_ps_cache.shader_map.find(uid); - if (it != m_uber_ps_cache.shader_map.end()) - { - // If it's pending, compile it synchronously. - if (!it->second.second) - return it->second.first; - else - m_uber_ps_cache.shader_map.erase(it); - } - - // Not in the cache, so compile the shader. - ShaderCompiler::SPIRVCodeVector spv; - VkShaderModule module = VK_NULL_HANDLE; - ShaderCode source_code = - UberShader::GenPixelShader(APIType::Vulkan, ShaderHostConfig::GetCurrent(), uid.GetUidData()); - if (ShaderCompiler::CompileFragmentShader(&spv, source_code.GetBuffer().c_str(), - source_code.GetBuffer().length())) - { - module = Util::CreateShaderModule(spv.data(), spv.size()); - - // Append to shader cache if it created successfully. - if (module != VK_NULL_HANDLE) - { - m_uber_ps_cache.disk_cache.Append(uid, spv.data(), static_cast(spv.size())); - INCSTAT(stats.numPixelShadersCreated); - INCSTAT(stats.numPixelShadersAlive); - } - } - - // We still insert null entries to prevent further compilation attempts. - m_uber_ps_cache.shader_map.emplace(uid, std::make_pair(module, false)); - return module; -} - void ShaderCache::RecompileSharedShaders() { DestroySharedShaders(); @@ -940,27 +636,15 @@ void ShaderCache::RecompileSharedShaders() PanicAlert("Failed to recompile shared shaders."); } -void ShaderCache::ReloadShaderAndPipelineCaches() +void ShaderCache::ReloadPipelineCache() { - m_async_shader_compiler->WaitUntilCompletion(); - m_async_shader_compiler->RetrieveWorkItems(); - SavePipelineCache(); - DestroyShaderCaches(); DestroyPipelineCache(); if (g_ActiveConfig.bShaderCache) - { - LoadShaderCaches(); LoadPipelineCache(); - } else - { CreatePipelineCache(); - } - - if (g_ActiveConfig.CanPrecompileUberShaders()) - PrecompileUberShaders(); } std::string ShaderCache::GetUtilityShaderHeader() const @@ -1160,203 +844,4 @@ void ShaderCache::DestroySharedShaders() DestroyShader(m_screen_quad_geometry_shader); DestroyShader(m_passthrough_geometry_shader); } - -void ShaderCache::CreateDummyPipeline(const UberShader::VertexShaderUid& vuid, - const GeometryShaderUid& guid, - const UberShader::PixelShaderUid& puid) -{ - PortableVertexDeclaration vertex_decl; - std::memset(&vertex_decl, 0, sizeof(vertex_decl)); - - PipelineInfo pinfo; - pinfo.vertex_format = - static_cast(VertexLoaderManager::GetUberVertexFormat(vertex_decl)); - pinfo.pipeline_layout = g_object_cache->GetPipelineLayout( - g_ActiveConfig.bBBoxEnable && g_ActiveConfig.BBoxUseFragmentShaderImplementation() ? - PIPELINE_LAYOUT_BBOX : - PIPELINE_LAYOUT_STANDARD); - pinfo.vs = GetVertexUberShaderForUid(vuid); - pinfo.gs = (!guid.GetUidData()->IsPassthrough() && g_vulkan_context->SupportsGeometryShaders()) ? - GetGeometryShaderForUid(guid) : - VK_NULL_HANDLE; - pinfo.ps = GetPixelUberShaderForUid(puid); - pinfo.render_pass = FramebufferManager::GetInstance()->GetEFBLoadRenderPass(); - pinfo.rasterization_state.hex = RenderState::GetNoCullRasterizationState().hex; - pinfo.depth_state.hex = RenderState::GetNoDepthTestingDepthStencilState().hex; - pinfo.blend_state.hex = RenderState::GetNoBlendingBlendState().hex; - pinfo.multisampling_state.hex = FramebufferManager::GetInstance()->GetEFBMultisamplingState().hex; - pinfo.rasterization_state.primitive = - static_cast(guid.GetUidData()->primitive_type); - GetPipelineWithCacheResultAsync(pinfo); -} - -void ShaderCache::PrecompileUberShaders() -{ - UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& vuid) { - UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& puid) { - // UIDs must have compatible texgens, a mismatching combination will never be queried. - if (vuid.GetUidData()->num_texgens != puid.GetUidData()->num_texgens) - return; - - EnumerateGeometryShaderUids([&](const GeometryShaderUid& guid) { - if (guid.GetUidData()->numTexGens != vuid.GetUidData()->num_texgens) - return; - - CreateDummyPipeline(vuid, guid, puid); - }); - }); - }); - - WaitForBackgroundCompilesToComplete(); - - // Switch to the runtime/background thread config. - m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); -} - -void ShaderCache::WaitForBackgroundCompilesToComplete() -{ - m_async_shader_compiler->WaitUntilCompletion([](size_t completed, size_t total) { - Host_UpdateProgressDialog(GetStringT("Compiling shaders...").c_str(), - static_cast(completed), static_cast(total)); - }); - m_async_shader_compiler->RetrieveWorkItems(); - Host_UpdateProgressDialog("", -1, -1); -} - -void ShaderCache::RetrieveAsyncShaders() -{ - m_async_shader_compiler->RetrieveWorkItems(); -} - -std::pair ShaderCache::GetVertexShaderForUidAsync(const VertexShaderUid& uid) -{ - auto it = m_vs_cache.shader_map.find(uid); - if (it != m_vs_cache.shader_map.end()) - return it->second; - - // Kick a compile job off. - m_async_shader_compiler->QueueWorkItem( - m_async_shader_compiler->CreateWorkItem(uid)); - m_vs_cache.shader_map.emplace(uid, - std::make_pair(static_cast(VK_NULL_HANDLE), true)); - return std::make_pair(VK_NULL_HANDLE, true); -} - -std::pair ShaderCache::GetPixelShaderForUidAsync(const PixelShaderUid& uid) -{ - auto it = m_ps_cache.shader_map.find(uid); - if (it != m_ps_cache.shader_map.end()) - return it->second; - - // Kick a compile job off. - m_async_shader_compiler->QueueWorkItem( - m_async_shader_compiler->CreateWorkItem(uid)); - m_ps_cache.shader_map.emplace(uid, - std::make_pair(static_cast(VK_NULL_HANDLE), true)); - return std::make_pair(VK_NULL_HANDLE, true); -} - -bool ShaderCache::VertexShaderCompilerWorkItem::Compile() -{ - ShaderCode code = - GenerateVertexShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), m_uid.GetUidData()); - if (!ShaderCompiler::CompileVertexShader(&m_spirv, code.GetBuffer().c_str(), - code.GetBuffer().length())) - return true; - - m_module = Util::CreateShaderModule(m_spirv.data(), m_spirv.size()); - return true; -} - -void ShaderCache::VertexShaderCompilerWorkItem::Retrieve() -{ - auto it = g_shader_cache->m_vs_cache.shader_map.find(m_uid); - if (it == g_shader_cache->m_vs_cache.shader_map.end()) - { - g_shader_cache->m_vs_cache.shader_map.emplace(m_uid, std::make_pair(m_module, false)); - g_shader_cache->m_vs_cache.disk_cache.Append(m_uid, m_spirv.data(), - static_cast(m_spirv.size())); - return; - } - - // The main thread may have also compiled this shader. - if (!it->second.second) - { - if (m_module != VK_NULL_HANDLE) - vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_module, nullptr); - return; - } - - // No longer pending. - it->second.first = m_module; - it->second.second = false; - g_shader_cache->m_vs_cache.disk_cache.Append(m_uid, m_spirv.data(), - static_cast(m_spirv.size())); -} - -bool ShaderCache::PixelShaderCompilerWorkItem::Compile() -{ - ShaderCode code = - GeneratePixelShaderCode(APIType::Vulkan, ShaderHostConfig::GetCurrent(), m_uid.GetUidData()); - if (!ShaderCompiler::CompileFragmentShader(&m_spirv, code.GetBuffer().c_str(), - code.GetBuffer().length())) - return true; - - m_module = Util::CreateShaderModule(m_spirv.data(), m_spirv.size()); - return true; -} - -void ShaderCache::PixelShaderCompilerWorkItem::Retrieve() -{ - auto it = g_shader_cache->m_ps_cache.shader_map.find(m_uid); - if (it == g_shader_cache->m_ps_cache.shader_map.end()) - { - g_shader_cache->m_ps_cache.shader_map.emplace(m_uid, std::make_pair(m_module, false)); - g_shader_cache->m_ps_cache.disk_cache.Append(m_uid, m_spirv.data(), - static_cast(m_spirv.size())); - return; - } - - // The main thread may have also compiled this shader. - if (!it->second.second) - { - if (m_module != VK_NULL_HANDLE) - vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_module, nullptr); - return; - } - - // No longer pending. - it->second.first = m_module; - it->second.second = false; - g_shader_cache->m_ps_cache.disk_cache.Append(m_uid, m_spirv.data(), - static_cast(m_spirv.size())); -} - -bool ShaderCache::PipelineCompilerWorkItem::Compile() -{ - m_pipeline = g_shader_cache->CreatePipeline(m_info); - return true; -} - -void ShaderCache::PipelineCompilerWorkItem::Retrieve() -{ - auto it = g_shader_cache->m_pipeline_objects.find(m_info); - if (it == g_shader_cache->m_pipeline_objects.end()) - { - g_shader_cache->m_pipeline_objects.emplace(m_info, std::make_pair(m_pipeline, false)); - return; - } - - // The main thread may have also compiled this shader. - if (!it->second.second) - { - if (m_pipeline != VK_NULL_HANDLE) - vkDestroyPipeline(g_vulkan_context->GetDevice(), m_pipeline, nullptr); - return; - } - - // No longer pending. - it->second.first = m_pipeline; - it->second.second = false; -} } diff --git a/Source/Core/VideoBackends/Vulkan/ShaderCache.h b/Source/Core/VideoBackends/Vulkan/ShaderCache.h index 0ad080a3fc..1082d0039e 100644 --- a/Source/Core/VideoBackends/Vulkan/ShaderCache.h +++ b/Source/Core/VideoBackends/Vulkan/ShaderCache.h @@ -19,13 +19,7 @@ #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/ShaderCompiler.h" -#include "VideoCommon/AsyncShaderCompiler.h" -#include "VideoCommon/GeometryShaderGen.h" -#include "VideoCommon/PixelShaderGen.h" #include "VideoCommon/RenderState.h" -#include "VideoCommon/UberShaderPixel.h" -#include "VideoCommon/UberShaderVertex.h" -#include "VideoCommon/VertexShaderGen.h" namespace Vulkan { @@ -33,10 +27,6 @@ class CommandBufferManager; class VertexFormat; class StreamBuffer; -class CommandBufferManager; -class VertexFormat; -class StreamBuffer; - struct PipelineInfo { // These are packed in descending order of size, to avoid any padding so that the structure @@ -88,19 +78,6 @@ public: // Get utility shader header based on current config. std::string GetUtilityShaderHeader() const; - // Accesses ShaderGen shader caches - VkShaderModule GetVertexShaderForUid(const VertexShaderUid& uid); - VkShaderModule GetGeometryShaderForUid(const GeometryShaderUid& uid); - VkShaderModule GetPixelShaderForUid(const PixelShaderUid& uid); - - // Ubershader caches - VkShaderModule GetVertexUberShaderForUid(const UberShader::VertexShaderUid& uid); - VkShaderModule GetPixelUberShaderForUid(const UberShader::PixelShaderUid& uid); - - // Accesses ShaderGen shader caches asynchronously - std::pair GetVertexShaderForUidAsync(const VertexShaderUid& uid); - std::pair GetPixelShaderForUidAsync(const PixelShaderUid& uid); - // Perform at startup, create descriptor layouts, compiles all static shaders. bool Initialize(); void Shutdown(); @@ -112,13 +89,6 @@ public: // Find a pipeline by the specified description, if not found, attempts to create it. VkPipeline GetPipeline(const PipelineInfo& info); - // Find a pipeline by the specified description, if not found, attempts to create it. If this - // resulted in a pipeline being created, the second field of the return value will be false, - // otherwise for a cache hit it will be true. - std::pair GetPipelineWithCacheResult(const PipelineInfo& info); - std::pair, bool> - GetPipelineWithCacheResultAsync(const PipelineInfo& info); - // Creates a compute pipeline, and does not track the handle. VkPipeline CreateComputePipeline(const ComputePipelineInfo& info); @@ -139,47 +109,22 @@ public: void RecompileSharedShaders(); // Reload pipeline cache. This will destroy all pipelines. - void ReloadShaderAndPipelineCaches(); + void ReloadPipelineCache(); // Shared shader accessors VkShaderModule GetScreenQuadVertexShader() const { return m_screen_quad_vertex_shader; } VkShaderModule GetPassthroughVertexShader() const { return m_passthrough_vertex_shader; } VkShaderModule GetScreenQuadGeometryShader() const { return m_screen_quad_geometry_shader; } VkShaderModule GetPassthroughGeometryShader() const { return m_passthrough_geometry_shader; } - void PrecompileUberShaders(); - void WaitForBackgroundCompilesToComplete(); - void RetrieveAsyncShaders(); - private: bool CreatePipelineCache(); bool LoadPipelineCache(); bool ValidatePipelineCache(const u8* data, size_t data_length); void DestroyPipelineCache(); - void LoadShaderCaches(); - void DestroyShaderCaches(); bool CompileSharedShaders(); void DestroySharedShaders(); - // We generate a dummy pipeline with some defaults in the blend/depth states, - // that way the driver is forced to compile something (looking at you, NVIDIA). - // It can then hopefully re-use part of this pipeline for others in the future. - void CreateDummyPipeline(const UberShader::VertexShaderUid& vuid, const GeometryShaderUid& guid, - const UberShader::PixelShaderUid& puid); - - template - struct ShaderModuleCache - { - std::map> shader_map; - LinearDiskCache disk_cache; - }; - ShaderModuleCache m_vs_cache; - ShaderModuleCache m_gs_cache; - ShaderModuleCache m_ps_cache; - ShaderModuleCache m_uber_vs_cache; - ShaderModuleCache m_uber_ps_cache; - - std::unordered_map, PipelineInfoHash> - m_pipeline_objects; + std::unordered_map m_pipeline_objects; std::unordered_map m_compute_pipeline_objects; VkPipelineCache m_pipeline_cache = VK_NULL_HANDLE; @@ -190,45 +135,6 @@ private: VkShaderModule m_passthrough_vertex_shader = VK_NULL_HANDLE; VkShaderModule m_screen_quad_geometry_shader = VK_NULL_HANDLE; VkShaderModule m_passthrough_geometry_shader = VK_NULL_HANDLE; - - std::unique_ptr m_async_shader_compiler; - - // TODO: Use templates to reduce the number of these classes. - class VertexShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit VertexShaderCompilerWorkItem(const VertexShaderUid& uid) : m_uid(uid) {} - bool Compile() override; - void Retrieve() override; - - private: - VertexShaderUid m_uid; - ShaderCompiler::SPIRVCodeVector m_spirv; - VkShaderModule m_module = VK_NULL_HANDLE; - }; - class PixelShaderCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit PixelShaderCompilerWorkItem(const PixelShaderUid& uid) : m_uid(uid) {} - bool Compile() override; - void Retrieve() override; - - private: - PixelShaderUid m_uid; - ShaderCompiler::SPIRVCodeVector m_spirv; - VkShaderModule m_module = VK_NULL_HANDLE; - }; - class PipelineCompilerWorkItem : public VideoCommon::AsyncShaderCompiler::WorkItem - { - public: - explicit PipelineCompilerWorkItem(const PipelineInfo& info) : m_info(info) {} - bool Compile() override; - void Retrieve() override; - - private: - PipelineInfo m_info; - VkPipeline m_pipeline; - }; }; extern std::unique_ptr g_shader_cache; diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp index e5be3e2c1e..a3961daf48 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp @@ -15,6 +15,7 @@ #include "VideoBackends/Vulkan/ShaderCache.h" #include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/Util.h" +#include "VideoBackends/Vulkan/VKPipeline.h" #include "VideoBackends/Vulkan/VertexFormat.h" #include "VideoBackends/Vulkan/VulkanContext.h" @@ -53,12 +54,6 @@ void StateTracker::DestroyInstance() bool StateTracker::Initialize() { - // BBox is disabled by default. - m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD); - m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS; - m_bbox_enabled = false; - ClearShaders(); - // Initialize all samplers to point by default for (size_t i = 0; i < NUM_PIXEL_SHADER_SAMPLERS; i++) { @@ -97,122 +92,6 @@ bool StateTracker::Initialize() return true; } -void StateTracker::InvalidateShaderPointers() -{ - // Clear UIDs, forcing a false match next time. - m_vs_uid = {}; - m_gs_uid = {}; - m_ps_uid = {}; - - // Invalidate shader pointers. - m_pipeline_state.vs = VK_NULL_HANDLE; - m_pipeline_state.gs = VK_NULL_HANDLE; - m_pipeline_state.ps = VK_NULL_HANDLE; -} - -void StateTracker::ReloadPipelineUIDCache() -{ - class PipelineInserter final : public LinearDiskCacheReader - { - public: - explicit PipelineInserter(StateTracker* this_ptr_) : this_ptr(this_ptr_) {} - void Read(const SerializedPipelineUID& key, const u32* value, u32 value_size) - { - this_ptr->PrecachePipelineUID(key); - } - - private: - StateTracker* this_ptr; - }; - - m_uid_cache.Sync(); - m_uid_cache.Close(); - - // UID caches don't contain any host state, so use a single uid cache per gameid. - std::string filename = GetDiskShaderCacheFileName(APIType::Vulkan, "PipelineUID", true, false); - if (g_ActiveConfig.bShaderCache) - { - PipelineInserter inserter(this); - m_uid_cache.OpenAndRead(filename, inserter); - } - - // If we were using background compilation, ensure everything is ready before continuing. - if (g_ActiveConfig.bBackgroundShaderCompiling) - g_shader_cache->WaitForBackgroundCompilesToComplete(); -} - -void StateTracker::AppendToPipelineUIDCache(const PipelineInfo& info) -{ - SerializedPipelineUID sinfo; - sinfo.blend_state_bits = info.blend_state.hex; - sinfo.rasterizer_state_bits = info.rasterization_state.hex; - sinfo.depth_state_bits = info.depth_state.hex; - sinfo.vertex_decl = m_pipeline_state.vertex_format->GetVertexDeclaration(); - sinfo.vs_uid = m_vs_uid; - sinfo.gs_uid = m_gs_uid; - sinfo.ps_uid = m_ps_uid; - - u32 dummy_value = 0; - m_uid_cache.Append(sinfo, &dummy_value, 1); -} - -bool StateTracker::PrecachePipelineUID(const SerializedPipelineUID& uid) -{ - PipelineInfo pinfo = {}; - - // Need to create the vertex declaration first, rather than deferring to when a game creates a - // vertex loader that uses this format, since we need it to create a pipeline. - pinfo.vertex_format = - static_cast(VertexLoaderManager::GetOrCreateMatchingFormat(uid.vertex_decl)); - pinfo.pipeline_layout = uid.ps_uid.GetUidData()->bounding_box ? - g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX) : - g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD); - pinfo.vs = g_shader_cache->GetVertexShaderForUid(uid.vs_uid); - if (pinfo.vs == VK_NULL_HANDLE) - { - WARN_LOG(VIDEO, "Failed to get vertex shader from cached UID."); - return false; - } - if (g_vulkan_context->SupportsGeometryShaders() && !uid.gs_uid.GetUidData()->IsPassthrough()) - { - pinfo.gs = g_shader_cache->GetGeometryShaderForUid(uid.gs_uid); - if (pinfo.gs == VK_NULL_HANDLE) - { - WARN_LOG(VIDEO, "Failed to get geometry shader from cached UID."); - return false; - } - } - pinfo.ps = g_shader_cache->GetPixelShaderForUid(uid.ps_uid); - if (pinfo.ps == VK_NULL_HANDLE) - { - WARN_LOG(VIDEO, "Failed to get pixel shader from cached UID."); - return false; - } - pinfo.render_pass = m_load_render_pass; - pinfo.rasterization_state.hex = uid.rasterizer_state_bits; - pinfo.depth_state.hex = uid.depth_state_bits; - pinfo.blend_state.hex = uid.blend_state_bits; - pinfo.multisampling_state.hex = m_pipeline_state.multisampling_state.hex; - - if (g_ActiveConfig.bBackgroundShaderCompiling) - { - // Use async for multithreaded compilation. - g_shader_cache->GetPipelineWithCacheResultAsync(pinfo); - } - else - { - VkPipeline pipeline = g_shader_cache->GetPipeline(pinfo); - if (pipeline == VK_NULL_HANDLE) - { - WARN_LOG(VIDEO, "Failed to get pipeline from cached UID."); - return false; - } - } - - // We don't need to do anything with this pipeline, just make sure it exists. - return true; -} - void StateTracker::SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset) { if (m_vertex_buffer == buffer && m_vertex_buffer_offset == offset) @@ -238,14 +117,6 @@ void StateTracker::SetRenderPass(VkRenderPass load_render_pass, VkRenderPass cle { // Should not be changed within a render pass. _assert_(!InRenderPass()); - - // The clear and load render passes are compatible, so we don't need to change our pipeline. - if (m_pipeline_state.render_pass != load_render_pass) - { - m_pipeline_state.render_pass = load_render_pass; - m_dirty_flags |= DIRTY_FLAG_PIPELINE; - } - m_load_render_pass = load_render_pass; m_clear_render_pass = clear_render_pass; } @@ -258,169 +129,18 @@ void StateTracker::SetFramebuffer(VkFramebuffer framebuffer, const VkRect2D& ren m_framebuffer_size = render_area; } -void StateTracker::SetVertexFormat(const VertexFormat* vertex_format) +void StateTracker::SetPipeline(const VKPipeline* pipeline) { - if (m_vertex_format == vertex_format) + if (m_pipeline == pipeline) return; - m_vertex_format = vertex_format; - UpdatePipelineVertexFormat(); -} - -void StateTracker::SetRasterizationState(const RasterizationState& state) -{ - if (m_pipeline_state.rasterization_state.hex == state.hex) - return; - - m_pipeline_state.rasterization_state.hex = state.hex; - m_dirty_flags |= DIRTY_FLAG_PIPELINE; -} - -void StateTracker::SetMultisamplingstate(const MultisamplingState& state) -{ - if (m_pipeline_state.multisampling_state.hex == state.hex) - return; - - m_pipeline_state.multisampling_state.hex = state.hex; - m_dirty_flags |= DIRTY_FLAG_PIPELINE; -} - -void StateTracker::SetDepthState(const DepthState& state) -{ - if (m_pipeline_state.depth_state.hex == state.hex) - return; - - m_pipeline_state.depth_state.hex = state.hex; - m_dirty_flags |= DIRTY_FLAG_PIPELINE; -} - -void StateTracker::SetBlendState(const BlendingState& state) -{ - if (m_pipeline_state.blend_state.hex == state.hex) - return; - - m_pipeline_state.blend_state.hex = state.hex; - m_dirty_flags |= DIRTY_FLAG_PIPELINE; -} - -bool StateTracker::CheckForShaderChanges() -{ - VertexShaderUid vs_uid = GetVertexShaderUid(); - PixelShaderUid ps_uid = GetPixelShaderUid(); - ClearUnusedPixelShaderUidBits(APIType::Vulkan, &ps_uid); - - bool changed = false; - bool use_ubershaders = g_ActiveConfig.bDisableSpecializedShaders; - if (g_ActiveConfig.CanBackgroundCompileShaders() && !g_ActiveConfig.bDisableSpecializedShaders) - { - // Look up both VS and PS, and check if we can compile it asynchronously. - auto vs = g_shader_cache->GetVertexShaderForUidAsync(vs_uid); - auto ps = g_shader_cache->GetPixelShaderForUidAsync(ps_uid); - if (vs.second || ps.second) - { - // One of the shaders is still pending. Use the ubershader for both. - use_ubershaders = true; - } - else - { - // Use the standard shaders for both. - if (m_pipeline_state.vs != vs.first) - { - m_pipeline_state.vs = vs.first; - m_vs_uid = vs_uid; - changed = true; - } - if (m_pipeline_state.ps != ps.first) - { - m_pipeline_state.ps = ps.first; - m_ps_uid = ps_uid; - changed = true; - } - } - } - else - { - // Normal shader path. No ubershaders. - if (vs_uid != m_vs_uid) - { - m_vs_uid = vs_uid; - m_pipeline_state.vs = g_shader_cache->GetVertexShaderForUid(vs_uid); - changed = true; - } - if (ps_uid != m_ps_uid) - { - m_ps_uid = ps_uid; - m_pipeline_state.ps = g_shader_cache->GetPixelShaderForUid(ps_uid); - changed = true; - } - } - - // Switching to/from ubershaders? Have to adjust the vertex format and pipeline layout. - if (use_ubershaders != m_using_ubershaders) - { - m_using_ubershaders = use_ubershaders; - UpdatePipelineLayout(); - UpdatePipelineVertexFormat(); - } - - if (use_ubershaders) - { - UberShader::VertexShaderUid uber_vs_uid = UberShader::GetVertexShaderUid(); - VkShaderModule vs = g_shader_cache->GetVertexUberShaderForUid(uber_vs_uid); - if (vs != m_pipeline_state.vs) - { - m_uber_vs_uid = uber_vs_uid; - m_pipeline_state.vs = vs; - changed = true; - } - - UberShader::PixelShaderUid uber_ps_uid = UberShader::GetPixelShaderUid(); - UberShader::ClearUnusedPixelShaderUidBits(APIType::Vulkan, &uber_ps_uid); - VkShaderModule ps = g_shader_cache->GetPixelUberShaderForUid(uber_ps_uid); - if (ps != m_pipeline_state.ps) - { - m_uber_ps_uid = uber_ps_uid; - m_pipeline_state.ps = ps; - changed = true; - } - } - - if (g_vulkan_context->SupportsGeometryShaders()) - { - GeometryShaderUid gs_uid = GetGeometryShaderUid(m_pipeline_state.rasterization_state.primitive); - if (gs_uid != m_gs_uid) - { - m_gs_uid = gs_uid; - if (gs_uid.GetUidData()->IsPassthrough()) - m_pipeline_state.gs = VK_NULL_HANDLE; - else - m_pipeline_state.gs = g_shader_cache->GetGeometryShaderForUid(gs_uid); - - changed = true; - } - } - - if (changed) - m_dirty_flags |= DIRTY_FLAG_PIPELINE; - - return changed; -} - -void StateTracker::ClearShaders() -{ - // Set the UIDs to something that will never match, so on the first access they are checked. - std::memset(&m_vs_uid, 0xFF, sizeof(m_vs_uid)); - std::memset(&m_gs_uid, 0xFF, sizeof(m_gs_uid)); - std::memset(&m_ps_uid, 0xFF, sizeof(m_ps_uid)); - std::memset(&m_uber_vs_uid, 0xFF, sizeof(m_uber_vs_uid)); - std::memset(&m_uber_ps_uid, 0xFF, sizeof(m_uber_ps_uid)); - - m_pipeline_state.vs = VK_NULL_HANDLE; - m_pipeline_state.gs = VK_NULL_HANDLE; - m_pipeline_state.ps = VK_NULL_HANDLE; - m_pipeline_state.vertex_format = nullptr; + const bool new_usage = + pipeline && (!m_pipeline || m_pipeline->GetUsage() != pipeline->GetUsage()); + m_pipeline = pipeline; m_dirty_flags |= DIRTY_FLAG_PIPELINE; + if (new_usage) + m_dirty_flags |= DIRTY_FLAG_ALL_DESCRIPTOR_SETS; } void StateTracker::UpdateVertexShaderConstants() @@ -450,20 +170,6 @@ void StateTracker::UpdateVertexShaderConstants() void StateTracker::UpdateGeometryShaderConstants() { - // Skip updating geometry shader constants if it's not in use. - if (m_pipeline_state.gs == VK_NULL_HANDLE) - { - // However, if the buffer has changed, we can't skip the update, because then we'll - // try to include the now non-existant buffer in the descriptor set. - if (m_uniform_stream_buffer->GetBuffer() == - m_bindings.uniform_buffer_bindings[UBO_DESCRIPTOR_SET_BINDING_GS].buffer) - { - return; - } - - GeometryShaderManager::dirty = true; - } - if (!GeometryShaderManager::dirty || !ReserveConstantStorage()) return; @@ -614,15 +320,6 @@ void StateTracker::SetSampler(size_t index, VkSampler sampler) m_dirty_flags |= DIRTY_FLAG_PS_SAMPLERS; } -void StateTracker::SetBBoxEnable(bool enable) -{ - if (m_bbox_enabled == enable) - return; - - m_bbox_enabled = enable; - UpdatePipelineLayout(); -} - void StateTracker::SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range) { if (m_bindings.ps_ssbo.buffer == buffer && m_bindings.ps_ssbo.offset == offset && @@ -634,10 +331,7 @@ void StateTracker::SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceS m_bindings.ps_ssbo.buffer = buffer; m_bindings.ps_ssbo.offset = offset; m_bindings.ps_ssbo.range = range; - - // Defer descriptor update until bbox is actually enabled. - if (IsSSBODescriptorRequired()) - m_dirty_flags |= DIRTY_FLAG_PS_SSBO; + m_dirty_flags |= DIRTY_FLAG_PS_SSBO; } void StateTracker::UnbindTexture(VkImageView view) @@ -653,10 +347,6 @@ void StateTracker::InvalidateDescriptorSets() { m_descriptor_sets.fill(VK_NULL_HANDLE); m_dirty_flags |= DIRTY_FLAG_ALL_DESCRIPTOR_SETS; - - // Defer SSBO descriptor update until bbox is actually enabled. - if (!IsSSBODescriptorRequired()) - m_dirty_flags &= ~DIRTY_FLAG_PS_SSBO; } void StateTracker::InvalidateConstants() @@ -669,9 +359,8 @@ void StateTracker::InvalidateConstants() void StateTracker::SetPendingRebind() { m_dirty_flags |= DIRTY_FLAG_DYNAMIC_OFFSETS | DIRTY_FLAG_DESCRIPTOR_SET_BINDING | - DIRTY_FLAG_PIPELINE_BINDING | DIRTY_FLAG_VERTEX_BUFFER | - DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_VIEWPORT | DIRTY_FLAG_SCISSOR | - DIRTY_FLAG_PIPELINE; + DIRTY_FLAG_VERTEX_BUFFER | DIRTY_FLAG_INDEX_BUFFER | DIRTY_FLAG_VIEWPORT | + DIRTY_FLAG_SCISSOR | DIRTY_FLAG_PIPELINE; } void StateTracker::BeginRenderPass() @@ -743,17 +432,14 @@ void StateTracker::SetScissor(const VkRect2D& scissor) bool StateTracker::Bind(bool rebind_all /*= false*/) { + // Must have a pipeline. + if (!m_pipeline) + return false; + // Check the render area if we were in a clear pass. if (m_current_render_pass == m_clear_render_pass && !IsViewportWithinRenderArea()) EndRenderPass(); - // Get new pipeline object if any parts have changed - if (m_dirty_flags & DIRTY_FLAG_PIPELINE && !UpdatePipeline()) - { - ERROR_LOG(VIDEO, "Failed to get pipeline object, skipping draw"); - return false; - } - // Get a new descriptor set if any parts have changed if (m_dirty_flags & DIRTY_FLAG_ALL_DESCRIPTOR_SETS && !UpdateDescriptorSet()) { @@ -780,20 +466,20 @@ bool StateTracker::Bind(bool rebind_all /*= false*/) if (m_dirty_flags & DIRTY_FLAG_INDEX_BUFFER || rebind_all) vkCmdBindIndexBuffer(command_buffer, m_index_buffer, m_index_buffer_offset, m_index_type); - if (m_dirty_flags & DIRTY_FLAG_PIPELINE_BINDING || rebind_all) - vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_object); + if (m_dirty_flags & DIRTY_FLAG_PIPELINE || rebind_all) + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipeline()); if (m_dirty_flags & DIRTY_FLAG_DESCRIPTOR_SET_BINDING || rebind_all) { vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, - m_pipeline_state.pipeline_layout, 0, m_num_active_descriptor_sets, + m_pipeline->GetVkPipelineLayout(), 0, m_num_active_descriptor_sets, m_descriptor_sets.data(), NUM_UBO_DESCRIPTOR_SET_BINDINGS, m_bindings.uniform_buffer_offsets.data()); } else if (m_dirty_flags & DIRTY_FLAG_DYNAMIC_OFFSETS) { vkCmdBindDescriptorSets( - command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_state.pipeline_layout, + command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline->GetVkPipelineLayout(), DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS, 1, &m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS], NUM_UBO_DESCRIPTOR_SET_BINDINGS, m_bindings.uniform_buffer_offsets.data()); @@ -933,105 +619,6 @@ void StateTracker::EndClearRenderPass() EndRenderPass(); } -VkPipeline StateTracker::GetPipelineAndCacheUID() -{ - // We can't cache ubershader uids, only normal shader uids. - if (g_ActiveConfig.CanBackgroundCompileShaders() && !m_using_ubershaders) - { - // Append to UID cache if it is a new pipeline. - auto result = g_shader_cache->GetPipelineWithCacheResultAsync(m_pipeline_state); - if (!result.second && g_ActiveConfig.bShaderCache) - AppendToPipelineUIDCache(m_pipeline_state); - - // Still waiting for the pipeline to compile? - if (!result.first.second) - return result.first.first; - - // Use ubershader instead. - m_using_ubershaders = true; - UpdatePipelineLayout(); - UpdatePipelineVertexFormat(); - - PipelineInfo uber_info = m_pipeline_state; - UberShader::VertexShaderUid uber_vuid = UberShader::GetVertexShaderUid(); - UberShader::PixelShaderUid uber_puid = UberShader::GetPixelShaderUid(); - UberShader::ClearUnusedPixelShaderUidBits(APIType::Vulkan, &uber_puid); - uber_info.vs = g_shader_cache->GetVertexUberShaderForUid(uber_vuid); - uber_info.ps = g_shader_cache->GetPixelUberShaderForUid(uber_puid); - - auto uber_result = g_shader_cache->GetPipelineWithCacheResult(uber_info); - return uber_result.first; - } - else - { - // Add to the UID cache if it is a new pipeline. - auto result = g_shader_cache->GetPipelineWithCacheResult(m_pipeline_state); - if (!result.second && !m_using_ubershaders && g_ActiveConfig.bShaderCache) - AppendToPipelineUIDCache(m_pipeline_state); - - return result.first; - } -} - -bool StateTracker::IsSSBODescriptorRequired() const -{ - return m_bbox_enabled || (m_using_ubershaders && g_ActiveConfig.bBBoxEnable && - g_ActiveConfig.BBoxUseFragmentShaderImplementation()); -} - -bool StateTracker::UpdatePipeline() -{ - // We need at least a vertex and fragment shader - if (m_pipeline_state.vs == VK_NULL_HANDLE || m_pipeline_state.ps == VK_NULL_HANDLE) - return false; - - // Grab a new pipeline object, this can fail. - m_pipeline_object = GetPipelineAndCacheUID(); - - m_dirty_flags |= DIRTY_FLAG_PIPELINE_BINDING; - return m_pipeline_object != VK_NULL_HANDLE; -} - -void StateTracker::UpdatePipelineLayout() -{ - const bool use_bbox_pipeline_layout = IsSSBODescriptorRequired(); - VkPipelineLayout pipeline_layout = - use_bbox_pipeline_layout ? g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX) : - g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD); - if (m_pipeline_state.pipeline_layout == pipeline_layout) - return; - - // Change the number of active descriptor sets, as well as the pipeline layout - m_pipeline_state.pipeline_layout = pipeline_layout; - if (use_bbox_pipeline_layout) - { - m_num_active_descriptor_sets = NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS; - - // The bbox buffer never changes, so we defer descriptor updates until it is enabled. - if (m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE) - m_dirty_flags |= DIRTY_FLAG_PS_SSBO; - } - else - { - m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS; - } - - m_dirty_flags |= DIRTY_FLAG_PIPELINE | DIRTY_FLAG_DESCRIPTOR_SET_BINDING; -} - -void StateTracker::UpdatePipelineVertexFormat() -{ - const NativeVertexFormat* vertex_format = - m_using_ubershaders ? - VertexLoaderManager::GetUberVertexFormat(m_vertex_format->GetVertexDeclaration()) : - m_vertex_format; - if (m_pipeline_state.vertex_format == vertex_format) - return; - - m_pipeline_state.vertex_format = static_cast(vertex_format); - m_dirty_flags |= DIRTY_FLAG_PIPELINE; -} - bool StateTracker::UpdateDescriptorSet() { const size_t MAX_DESCRIPTOR_WRITES = NUM_UBO_DESCRIPTOR_SET_BINDINGS + // UBO @@ -1051,6 +638,9 @@ bool StateTracker::UpdateDescriptorSet() for (size_t i = 0; i < NUM_UBO_DESCRIPTOR_SET_BINDINGS; i++) { + if (i == UBO_DESCRIPTOR_SET_BINDING_GS && !g_vulkan_context->SupportsGeometryShaders()) + continue; + writes[num_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, @@ -1091,7 +681,7 @@ bool StateTracker::UpdateDescriptorSet() m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING; } - if (IsSSBODescriptorRequired() && + if (g_vulkan_context->SupportsBoundingBox() && (m_dirty_flags & DIRTY_FLAG_PS_SSBO || m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE)) { @@ -1119,6 +709,7 @@ bool StateTracker::UpdateDescriptorSet() if (num_writes > 0) vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), num_writes, writes.data(), 0, nullptr); + m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS; return true; } diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.h b/Source/Core/VideoBackends/Vulkan/StateTracker.h index 92771504a9..547c62f655 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.h +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.h @@ -9,19 +9,14 @@ #include #include "Common/CommonTypes.h" -#include "Common/LinearDiskCache.h" #include "VideoBackends/Vulkan/Constants.h" #include "VideoBackends/Vulkan/ShaderCache.h" -#include "VideoCommon/GeometryShaderGen.h" #include "VideoCommon/NativeVertexFormat.h" -#include "VideoCommon/PixelShaderGen.h" #include "VideoCommon/RenderBase.h" -#include "VideoCommon/UberShaderPixel.h" -#include "VideoCommon/UberShaderVertex.h" -#include "VideoCommon/VertexShaderGen.h" namespace Vulkan { +class VKPipeline; class StreamBuffer; class VertexFormat; @@ -35,31 +30,18 @@ public: static bool CreateInstance(); static void DestroyInstance(); - const RasterizationState& GetRasterizationState() const - { - return m_pipeline_state.rasterization_state; - } - const DepthState& GetDepthStencilState() const { return m_pipeline_state.depth_state; } - const BlendingState& GetBlendState() const { return m_pipeline_state.blend_state; } const std::array& GetPSSamplerBindings() const { return m_bindings.ps_samplers; } VkFramebuffer GetFramebuffer() const { return m_framebuffer; } + const VKPipeline* GetPipeline() const { return m_pipeline; } void SetVertexBuffer(VkBuffer buffer, VkDeviceSize offset); void SetIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType type); void SetRenderPass(VkRenderPass load_render_pass, VkRenderPass clear_render_pass); void SetFramebuffer(VkFramebuffer framebuffer, const VkRect2D& render_area); - void SetVertexFormat(const VertexFormat* vertex_format); - - void SetRasterizationState(const RasterizationState& state); - void SetMultisamplingstate(const MultisamplingState& state); - void SetDepthState(const DepthState& state); - void SetBlendState(const BlendingState& state); - - bool CheckForShaderChanges(); - void ClearShaders(); + void SetPipeline(const VKPipeline* pipeline); void UpdateVertexShaderConstants(); void UpdateGeometryShaderConstants(); @@ -68,7 +50,6 @@ public: void SetTexture(size_t index, VkImageView view); void SetSampler(size_t index, VkSampler sampler); - void SetBBoxEnable(bool enable); void SetBBoxBuffer(VkBuffer buffer, VkDeviceSize offset, VkDeviceSize range); void UnbindTexture(VkImageView view); @@ -117,30 +98,11 @@ public: bool IsWithinRenderArea(s32 x, s32 y, u32 width, u32 height) const; - // Reloads the UID cache, ensuring all pipelines used by the game so far have been created. - void ReloadPipelineUIDCache(); - - // Clears shader pointers, ensuring that now-deleted modules are not used. - void InvalidateShaderPointers(); - private: - // Serialized version of PipelineInfo, used when loading/saving the pipeline UID cache. - struct SerializedPipelineUID - { - u32 rasterizer_state_bits; - u32 depth_state_bits; - u32 blend_state_bits; - PortableVertexDeclaration vertex_decl; - VertexShaderUid vs_uid; - GeometryShaderUid gs_uid; - PixelShaderUid ps_uid; - }; - // Number of descriptor sets for game draws. enum { - NUM_GX_DRAW_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS + 1, - NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER + 1 + NUM_GX_DRAW_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER + 1 }; enum DITRY_FLAG : u32 @@ -157,7 +119,6 @@ private: DIRTY_FLAG_SCISSOR = (1 << 9), DIRTY_FLAG_PIPELINE = (1 << 10), DIRTY_FLAG_DESCRIPTOR_SET_BINDING = (1 << 11), - DIRTY_FLAG_PIPELINE_BINDING = (1 << 12), DIRTY_FLAG_ALL_DESCRIPTOR_SETS = DIRTY_FLAG_VS_UBO | DIRTY_FLAG_GS_UBO | DIRTY_FLAG_PS_UBO | DIRTY_FLAG_PS_SAMPLERS | DIRTY_FLAG_PS_SSBO @@ -165,28 +126,10 @@ private: bool Initialize(); - // Appends the specified pipeline info, combined with the UIDs stored in the class. - // The info is here so that we can store variations of a UID, e.g. blend state. - void AppendToPipelineUIDCache(const PipelineInfo& info); - - // Precaches a pipeline based on the UID information. - bool PrecachePipelineUID(const SerializedPipelineUID& uid); - // Check that the specified viewport is within the render area. // If not, ends the render pass if it is a clear render pass. bool IsViewportWithinRenderArea() const; - // Obtains a Vulkan pipeline object for the specified pipeline configuration. - // Also adds this pipeline configuration to the UID cache if it is not present already. - VkPipeline GetPipelineAndCacheUID(); - - // Are bounding box ubershaders enabled? If so, we need to ensure the SSBO is set up, - // since the bbox writes are determined by a uniform. - bool IsSSBODescriptorRequired() const; - - bool UpdatePipeline(); - void UpdatePipelineLayout(); - void UpdatePipelineVertexFormat(); bool UpdateDescriptorSet(); // Allocates storage in the uniform buffer of the specified size. If this storage cannot be @@ -205,18 +148,8 @@ private: VkDeviceSize m_index_buffer_offset = 0; VkIndexType m_index_type = VK_INDEX_TYPE_UINT16; - // shader state - VertexShaderUid m_vs_uid = {}; - GeometryShaderUid m_gs_uid = {}; - PixelShaderUid m_ps_uid = {}; - UberShader::VertexShaderUid m_uber_vs_uid = {}; - UberShader::PixelShaderUid m_uber_ps_uid = {}; - bool m_using_ubershaders = false; - // pipeline state - PipelineInfo m_pipeline_state = {}; - VkPipeline m_pipeline_object = VK_NULL_HANDLE; - const VertexFormat* m_vertex_format = nullptr; + const VKPipeline* m_pipeline = nullptr; // shader bindings std::array m_descriptor_sets = {}; @@ -230,8 +163,8 @@ private: VkDescriptorBufferInfo ps_ssbo = {}; } m_bindings; - u32 m_num_active_descriptor_sets = 0; size_t m_uniform_buffer_reserve_size = 0; + u32 m_num_active_descriptor_sets = 0; // rasterization VkViewport m_viewport = {0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; @@ -246,18 +179,11 @@ private: VkRenderPass m_current_render_pass = VK_NULL_HANDLE; VkRect2D m_framebuffer_size = {}; VkRect2D m_framebuffer_render_area = {}; - bool m_bbox_enabled = false; // CPU access tracking u32 m_draw_counter = 0; std::vector m_cpu_accesses_this_frame; std::vector m_scheduled_command_buffer_kicks; bool m_allow_background_execution = true; - - // Draw state cache on disk - // We don't actually use the value field here, instead we generate the shaders from the uid - // on-demand. If all goes well, it should hit the shader and Vulkan pipeline cache, therefore - // loading should be reasonably efficient. - LinearDiskCache m_uid_cache; }; } diff --git a/Source/Core/VideoBackends/Vulkan/Util.cpp b/Source/Core/VideoBackends/Vulkan/Util.cpp index 4977a270a3..facae2ac14 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.cpp +++ b/Source/Core/VideoBackends/Vulkan/Util.cpp @@ -592,16 +592,19 @@ void UtilityShaderDraw::BindDescriptors() &dummy_uniform_buffer, nullptr}; - set_writes[num_set_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - nullptr, - set, - UBO_DESCRIPTOR_SET_BINDING_GS, - 0, - 1, - VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, - nullptr, - &dummy_uniform_buffer, - nullptr}; + if (g_vulkan_context->SupportsGeometryShaders()) + { + set_writes[num_set_writes++] = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + nullptr, + set, + UBO_DESCRIPTOR_SET_BINDING_GS, + 0, + 1, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, + nullptr, + &dummy_uniform_buffer, + nullptr}; + } set_writes[num_set_writes++] = { VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, nullptr, set, UBO_DESCRIPTOR_SET_BINDING_PS, 0, 1, diff --git a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp index c9aad0254c..1e97cf6308 100644 --- a/Source/Core/VideoBackends/Vulkan/VertexManager.cpp +++ b/Source/Core/VideoBackends/Vulkan/VertexManager.cpp @@ -139,8 +139,6 @@ void VertexManager::vFlush() u32 index_count = IndexGenerator::GetIndexLen(); // Update tracked state - StateTracker::GetInstance()->SetVertexFormat(vertex_format); - StateTracker::GetInstance()->CheckForShaderChanges(); StateTracker::GetInstance()->UpdateVertexShaderConstants(); StateTracker::GetInstance()->UpdateGeometryShaderConstants(); StateTracker::GetInstance()->UpdatePixelShaderConstants(); @@ -165,12 +163,10 @@ void VertexManager::vFlush() bounding_box->Flush(); bounding_box->Invalidate(); } - - // Update which descriptor set/pipeline layout to use. - StateTracker::GetInstance()->SetBBoxEnable(bounding_box_enabled); } // Bind all pending state to the command buffer + g_renderer->SetPipeline(m_current_pipeline_object); if (!StateTracker::GetInstance()->Bind()) { WARN_LOG(VIDEO, "Skipped draw of %u indices", index_count); diff --git a/Source/Core/VideoBackends/Vulkan/main.cpp b/Source/Core/VideoBackends/Vulkan/main.cpp index d8a725845a..9bd4039138 100644 --- a/Source/Core/VideoBackends/Vulkan/main.cpp +++ b/Source/Core/VideoBackends/Vulkan/main.cpp @@ -223,6 +223,7 @@ bool VideoBackend::Initialize(void* window_handle) g_renderer = std::make_unique(std::move(swap_chain)); g_vertex_manager = std::make_unique(); g_texture_cache = std::make_unique(); + ::g_shader_cache = std::make_unique(); g_perf_query = std::make_unique(); // Invoke init methods on main wrapper classes. @@ -230,21 +231,14 @@ bool VideoBackend::Initialize(void* window_handle) // for the remaining classes may call methods on these. if (!StateTracker::CreateInstance() || !FramebufferManager::GetInstance()->Initialize() || !Renderer::GetInstance()->Initialize() || !VertexManager::GetInstance()->Initialize() || - !TextureCache::GetInstance()->Initialize() || !PerfQuery::GetInstance()->Initialize()) + !TextureCache::GetInstance()->Initialize() || !PerfQuery::GetInstance()->Initialize() || + !::g_shader_cache->Initialize()) { PanicAlert("Failed to initialize Vulkan classes."); Shutdown(); return false; } - // Ensure all pipelines previously used by the game have been created. - StateTracker::GetInstance()->ReloadPipelineUIDCache(); - - // Lastly, precompile ubershaders, if requested. - // This has to be done after the texture cache and shader cache are initialized. - if (g_ActiveConfig.CanPrecompileUberShaders()) - g_shader_cache->PrecompileUberShaders(); - // Display the name so the user knows which device was actually created. INFO_LOG(VIDEO, "Vulkan Device: %s", g_vulkan_context->GetDeviceProperties().deviceName); return true; @@ -252,13 +246,17 @@ bool VideoBackend::Initialize(void* window_handle) void VideoBackend::Shutdown() { - if (g_renderer) - g_renderer->Shutdown(); - if (g_command_buffer_mgr) g_command_buffer_mgr->WaitForGPUIdle(); + if (::g_shader_cache) + ::g_shader_cache->Shutdown(); + + if (g_renderer) + g_renderer->Shutdown(); + g_perf_query.reset(); + ::g_shader_cache.reset(); g_texture_cache.reset(); g_vertex_manager.reset(); g_renderer.reset(); diff --git a/Source/Core/VideoCommon/BPFunctions.cpp b/Source/Core/VideoCommon/BPFunctions.cpp index 0e4551da09..1afcb35040 100644 --- a/Source/Core/VideoCommon/BPFunctions.cpp +++ b/Source/Core/VideoCommon/BPFunctions.cpp @@ -31,6 +31,7 @@ void SetGenerationMode() RasterizationState state = {}; state.Generate(bpmem, g_vertex_manager->GetCurrentPrimitiveType()); g_renderer->SetRasterizationState(state); + g_vertex_manager->SetRasterizationStateChanged(); } void SetScissor() @@ -132,6 +133,7 @@ void SetDepthMode() DepthState state = {}; state.Generate(bpmem); g_renderer->SetDepthState(state); + g_vertex_manager->SetDepthStateChanged(); } void SetBlendMode() @@ -139,6 +141,7 @@ void SetBlendMode() BlendingState state = {}; state.Generate(bpmem); g_renderer->SetBlendingState(state); + g_vertex_manager->SetBlendingStateChanged(); } /* Explanation of the magic behind ClearScreen: diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 42de08e9f1..22ed8519d5 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -32,6 +32,7 @@ set(SRCS PostProcessing.cpp RenderBase.cpp RenderState.cpp + ShaderCache.cpp ShaderGenCommon.cpp Statistics.cpp UberShaderCommon.cpp diff --git a/Source/Core/VideoCommon/GeometryShaderGen.h b/Source/Core/VideoCommon/GeometryShaderGen.h index ace05baca7..0c688d30a7 100644 --- a/Source/Core/VideoCommon/GeometryShaderGen.h +++ b/Source/Core/VideoCommon/GeometryShaderGen.h @@ -8,7 +8,6 @@ #include "Common/CommonTypes.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/ShaderGenCommon.h" -#include "VideoCommon/VertexManagerBase.h" enum class APIType; diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 8aea2f098d..4c03168d20 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -56,6 +56,7 @@ #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/PixelShaderManager.h" #include "VideoCommon/PostProcessing.h" +#include "VideoCommon/ShaderCache.h" #include "VideoCommon/ShaderGenCommon.h" #include "VideoCommon/Statistics.h" #include "VideoCommon/TextureCacheBase.h" @@ -92,6 +93,7 @@ Renderer::Renderer(int backbuffer_width, int backbuffer_height) m_surface_handle = Host_GetRenderHandle(); m_last_host_config_bits = ShaderHostConfig::GetCurrent().bits; + m_last_efb_multisamples = g_ActiveConfig.iMultisamples; } Renderer::~Renderer() = default; @@ -234,11 +236,20 @@ void Renderer::SaveScreenshot(const std::string& filename, bool wait_for_complet bool Renderer::CheckForHostConfigChanges() { ShaderHostConfig new_host_config = ShaderHostConfig::GetCurrent(); - if (new_host_config.bits == m_last_host_config_bits) + if (new_host_config.bits == m_last_host_config_bits && + m_last_efb_multisamples == g_ActiveConfig.iMultisamples) + { return false; + } - OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL); m_last_host_config_bits = new_host_config.bits; + m_last_efb_multisamples = g_ActiveConfig.iMultisamples; + + // Reload shaders. + OSD::AddMessage("Video config changed, reloading shaders.", OSD::Duration::NORMAL); + SetPipeline(nullptr); + g_vertex_manager->InvalidatePipelineObject(); + g_shader_cache->SetHostConfig(new_host_config, g_ActiveConfig.iMultisamples); return true; } @@ -688,6 +699,13 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const // Set default viewport and scissor, for the clear to work correctly // New frame stats.ResetFrame(); + g_shader_cache->RetrieveAsyncShaders(); + + // We invalidate the pipeline object at the start of the frame. + // This is for the rare case where only a single pipeline configuration is used, + // and hybrid ubershaders have compiled the specialized shader, but without any + // state changes the specialized shader will not take over. + g_vertex_manager->InvalidatePipelineObject(); Core::Callback_VideoCopiedToXFB(true); } diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index a51f1b2813..da8e0b819f 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -243,6 +243,7 @@ protected: std::mutex m_swap_mutex; u32 m_last_host_config_bits = 0; + u32 m_last_efb_multisamples = 1; private: void RunFrameDumps(); diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp new file mode 100644 index 0000000000..05624e9fe4 --- /dev/null +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -0,0 +1,913 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoCommon/ShaderCache.h" + +#include "Common/Assert.h" +#include "Common/MsgHandler.h" +#include "Core/Host.h" + +#include "VideoCommon/RenderBase.h" +#include "VideoCommon/Statistics.h" +#include "VideoCommon/VertexLoaderManager.h" +#include "VideoCommon/VertexManagerBase.h" + +std::unique_ptr g_shader_cache; + +namespace VideoCommon +{ +ShaderCache::ShaderCache() = default; +ShaderCache::~ShaderCache() = default; + +bool ShaderCache::Initialize() +{ + m_api_type = g_ActiveConfig.backend_info.api_type; + m_host_config = ShaderHostConfig::GetCurrent(); + m_efb_multisamples = g_ActiveConfig.iMultisamples; + + // Create the async compiler, and start the worker threads. + m_async_shader_compiler = std::make_unique(); + m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderPrecompilerThreads()); + + // Load shader and UID caches. + if (g_ActiveConfig.bShaderCache) + { + LoadShaderCaches(); + LoadPipelineUIDCache(); + } + + // Queue ubershader precompiling if required. + if (g_ActiveConfig.CanPrecompileUberShaders()) + PrecompileUberShaders(); + + // Compile all known UIDs. + CompileMissingPipelines(); + + // Switch to the runtime shader compiler thread configuration. + m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); + return true; +} + +void ShaderCache::SetHostConfig(const ShaderHostConfig& host_config, u32 efb_multisamples) +{ + if (m_host_config.bits == host_config.bits && m_efb_multisamples == efb_multisamples) + return; + + m_host_config = host_config; + m_efb_multisamples = efb_multisamples; + Reload(); +} + +void ShaderCache::Reload() +{ + m_async_shader_compiler->WaitUntilCompletion(); + m_async_shader_compiler->RetrieveWorkItems(); + + InvalidateCachedPipelines(); + ClearShaderCaches(); + + if (g_ActiveConfig.bShaderCache) + LoadShaderCaches(); + + // Switch to the precompiling shader configuration while we rebuild. + m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderPrecompilerThreads()); + + // We don't need to explicitly recompile the individual ubershaders here, as the pipelines + // UIDs are still be in the map. Therefore, when these are rebuilt, the shaders will also + // be recompiled. + CompileMissingPipelines(); + m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); +} + +void ShaderCache::RetrieveAsyncShaders() +{ + m_async_shader_compiler->RetrieveWorkItems(); +} + +void ShaderCache::Shutdown() +{ + m_async_shader_compiler->StopWorkerThreads(); + m_async_shader_compiler->RetrieveWorkItems(); + + ClearShaderCaches(); + ClearPipelineCaches(); +} + +const AbstractPipeline* ShaderCache::GetPipelineForUid(const GXPipelineConfig& uid) +{ + auto it = m_gx_pipeline_cache.find(uid); + if (it != m_gx_pipeline_cache.end() && !it->second.second) + return it->second.first.get(); + + std::unique_ptr pipeline; + std::optional pipeline_config = GetGXPipelineConfig(uid); + if (pipeline_config) + pipeline = g_renderer->CreatePipeline(*pipeline_config); + if (g_ActiveConfig.bShaderCache) + AppendGXPipelineUID(uid); + return InsertGXPipeline(uid, std::move(pipeline)); +} + +std::optional +ShaderCache::GetPipelineForUidAsync(const GXPipelineConfig& uid) +{ + auto it = m_gx_pipeline_cache.find(uid); + if (it != m_gx_pipeline_cache.end()) + { + if (!it->second.second) + return it->second.first.get(); + else + return {}; + } + + auto vs_iter = m_vs_cache.shader_map.find(uid.vs_uid); + if (vs_iter == m_vs_cache.shader_map.end()) + { + QueueVertexShaderCompile(uid.vs_uid); + return {}; + } + else if (vs_iter->second.pending) + { + // VS is still compiling. + return {}; + } + + auto ps_iter = m_ps_cache.shader_map.find(uid.ps_uid); + if (ps_iter == m_ps_cache.shader_map.end()) + { + QueuePixelShaderCompile(uid.ps_uid); + return {}; + } + else if (ps_iter->second.pending) + { + // PS is still compiling. + return {}; + } + + if (NeedsGeometryShader(uid.gs_uid)) + { + auto gs_iter = m_gs_cache.shader_map.find(uid.gs_uid); + if (gs_iter == m_gs_cache.shader_map.end()) + CreateGeometryShader(uid.gs_uid); + } + + // All shader stages are present, queue the pipeline compile. + if (g_ActiveConfig.bShaderCache) + AppendGXPipelineUID(uid); + QueuePipelineCompile(uid); + return {}; +} + +const AbstractPipeline* ShaderCache::GetUberPipelineForUid(const GXUberPipelineConfig& uid) +{ + auto it = m_gx_uber_pipeline_cache.find(uid); + if (it != m_gx_uber_pipeline_cache.end() && !it->second.second) + return it->second.first.get(); + + std::unique_ptr pipeline; + std::optional pipeline_config = GetGXUberPipelineConfig(uid); + if (pipeline_config) + pipeline = g_renderer->CreatePipeline(*pipeline_config); + return InsertGXUberPipeline(uid, std::move(pipeline)); +} + +void ShaderCache::WaitForAsyncCompiler(const std::string& msg) +{ + m_async_shader_compiler->WaitUntilCompletion([&msg](size_t completed, size_t total) { + Host_UpdateProgressDialog(msg.c_str(), static_cast(completed), static_cast(total)); + }); + m_async_shader_compiler->RetrieveWorkItems(); + Host_UpdateProgressDialog("", -1, -1); +} + +template +static void LoadShaderCache(T& cache, APIType api_type, const char* type, bool include_gameid) +{ + class CacheReader : public LinearDiskCacheReader + { + public: + CacheReader(T& cache_) : cache(cache_) {} + void Read(const K& key, const u8* value, u32 value_size) + { + auto shader = g_renderer->CreateShaderFromBinary(stage, value, value_size); + if (shader) + { + auto& entry = cache.shader_map[key]; + entry.shader = std::move(shader); + entry.pending = false; + + switch (stage) + { + case ShaderStage::Vertex: + INCSTAT(stats.numVertexShadersCreated); + INCSTAT(stats.numVertexShadersAlive); + break; + case ShaderStage::Pixel: + INCSTAT(stats.numPixelShadersCreated); + INCSTAT(stats.numPixelShadersAlive); + break; + default: + break; + } + } + } + + private: + T& cache; + }; + + std::string filename = GetDiskShaderCacheFileName(api_type, type, include_gameid, true); + CacheReader reader(cache); + u32 count = cache.disk_cache.OpenAndRead(filename, reader); + INFO_LOG(VIDEO, "Loaded %u cached shaders from %s", count, filename.c_str()); +} + +template +static void ClearShaderCache(T& cache) +{ + cache.disk_cache.Sync(); + cache.disk_cache.Close(); + cache.shader_map.clear(); +} + +void ShaderCache::LoadShaderCaches() +{ + // Ubershader caches, if present. + LoadShaderCache(m_uber_vs_cache, m_api_type, + "uber-vs", false); + LoadShaderCache(m_uber_ps_cache, m_api_type, + "uber-ps", false); + + // We also share geometry shaders, as there aren't many variants. + if (m_host_config.backend_geometry_shaders) + LoadShaderCache(m_gs_cache, m_api_type, "gs", false); + + // Specialized shaders, gameid-specific. + LoadShaderCache(m_vs_cache, m_api_type, "specialized-vs", + true); + LoadShaderCache(m_ps_cache, m_api_type, "specialized-ps", + true); +} + +void ShaderCache::ClearShaderCaches() +{ + ClearShaderCache(m_vs_cache); + ClearShaderCache(m_gs_cache); + ClearShaderCache(m_ps_cache); + + ClearShaderCache(m_uber_vs_cache); + ClearShaderCache(m_uber_ps_cache); + + SETSTAT(stats.numPixelShadersCreated, 0); + SETSTAT(stats.numPixelShadersAlive, 0); + SETSTAT(stats.numVertexShadersCreated, 0); + SETSTAT(stats.numVertexShadersAlive, 0); +} + +void ShaderCache::LoadPipelineUIDCache() +{ + // We use the async compiler here to speed up startup time. + class CacheReader : public LinearDiskCacheReader + { + public: + CacheReader(ShaderCache* shader_cache_) : shader_cache(shader_cache_) {} + void Read(const GXPipelineDiskCacheUid& key, const u8* data, u32 data_size) + { + GXPipelineConfig config = {}; + config.vertex_format = VertexLoaderManager::GetOrCreateMatchingFormat(key.vertex_decl); + config.vs_uid = key.vs_uid; + config.gs_uid = key.gs_uid; + config.ps_uid = key.ps_uid; + config.rasterization_state.hex = key.rasterization_state_bits; + config.depth_state.hex = key.depth_state_bits; + config.blending_state.hex = key.blending_state_bits; + + auto iter = shader_cache->m_gx_pipeline_cache.find(config); + if (iter != shader_cache->m_gx_pipeline_cache.end()) + return; + + auto& entry = shader_cache->m_gx_pipeline_cache[config]; + entry.second = false; + } + + private: + ShaderCache* shader_cache; + }; + + std::string filename = GetDiskShaderCacheFileName(m_api_type, "pipeline-uid", true, false, false); + CacheReader reader(this); + u32 count = m_gx_pipeline_uid_disk_cache.OpenAndRead(filename, reader); + INFO_LOG(VIDEO, "Read %u pipeline UIDs from %s", count, filename.c_str()); + CompileMissingPipelines(); +} + +void ShaderCache::CompileMissingPipelines() +{ + // Queue all uids with a null pipeline for compilation. + for (auto& it : m_gx_pipeline_cache) + { + if (!it.second.second) + QueuePipelineCompile(it.first); + } + for (auto& it : m_gx_uber_pipeline_cache) + { + if (!it.second.second) + QueueUberPipelineCompile(it.first); + } + + WaitForAsyncCompiler(GetStringT("Compiling shaders...")); +} + +void ShaderCache::InvalidateCachedPipelines() +{ + // Set the pending flag to false, and destroy the pipeline. + for (auto& it : m_gx_pipeline_cache) + { + it.second.first.reset(); + it.second.second = false; + } + for (auto& it : m_gx_uber_pipeline_cache) + { + it.second.first.reset(); + it.second.second = false; + } +} + +void ShaderCache::ClearPipelineCaches() +{ + m_gx_pipeline_cache.clear(); + m_gx_uber_pipeline_cache.clear(); +} + +std::unique_ptr ShaderCache::CompileVertexShader(const VertexShaderUid& uid) const +{ + ShaderCode source_code = GenerateVertexShaderCode(m_api_type, m_host_config, uid.GetUidData()); + return g_renderer->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer().c_str(), + source_code.GetBuffer().size()); +} + +std::unique_ptr +ShaderCache::CompileVertexUberShader(const UberShader::VertexShaderUid& uid) const +{ + ShaderCode source_code = UberShader::GenVertexShader(m_api_type, m_host_config, uid.GetUidData()); + return g_renderer->CreateShaderFromSource(ShaderStage::Vertex, source_code.GetBuffer().c_str(), + source_code.GetBuffer().size()); +} + +std::unique_ptr ShaderCache::CompilePixelShader(const PixelShaderUid& uid) const +{ + ShaderCode source_code = GeneratePixelShaderCode(m_api_type, m_host_config, uid.GetUidData()); + return g_renderer->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer().c_str(), + source_code.GetBuffer().size()); +} + +std::unique_ptr +ShaderCache::CompilePixelUberShader(const UberShader::PixelShaderUid& uid) const +{ + ShaderCode source_code = UberShader::GenPixelShader(m_api_type, m_host_config, uid.GetUidData()); + return g_renderer->CreateShaderFromSource(ShaderStage::Pixel, source_code.GetBuffer().c_str(), + source_code.GetBuffer().size()); +} + +const AbstractShader* ShaderCache::InsertVertexShader(const VertexShaderUid& uid, + std::unique_ptr shader) +{ + auto& entry = m_vs_cache.shader_map[uid]; + entry.pending = false; + + if (shader && !entry.shader) + { + if (g_ActiveConfig.bShaderCache && shader->HasBinary()) + { + auto binary = shader->GetBinary(); + if (!binary.empty()) + m_vs_cache.disk_cache.Append(uid, binary.data(), static_cast(binary.size())); + } + INCSTAT(stats.numVertexShadersCreated); + INCSTAT(stats.numVertexShadersAlive); + entry.shader = std::move(shader); + } + + return entry.shader.get(); +} + +const AbstractShader* ShaderCache::InsertVertexUberShader(const UberShader::VertexShaderUid& uid, + std::unique_ptr shader) +{ + auto& entry = m_uber_vs_cache.shader_map[uid]; + entry.pending = false; + + if (shader && !entry.shader) + { + if (g_ActiveConfig.bShaderCache && shader->HasBinary()) + { + auto binary = shader->GetBinary(); + if (!binary.empty()) + m_uber_vs_cache.disk_cache.Append(uid, binary.data(), static_cast(binary.size())); + } + INCSTAT(stats.numVertexShadersCreated); + INCSTAT(stats.numVertexShadersAlive); + entry.shader = std::move(shader); + } + + return entry.shader.get(); +} + +const AbstractShader* ShaderCache::InsertPixelShader(const PixelShaderUid& uid, + std::unique_ptr shader) +{ + auto& entry = m_ps_cache.shader_map[uid]; + entry.pending = false; + + if (shader && !entry.shader) + { + if (g_ActiveConfig.bShaderCache && shader->HasBinary()) + { + auto binary = shader->GetBinary(); + if (!binary.empty()) + m_ps_cache.disk_cache.Append(uid, binary.data(), static_cast(binary.size())); + } + INCSTAT(stats.numPixelShadersCreated); + INCSTAT(stats.numPixelShadersAlive); + entry.shader = std::move(shader); + } + + return entry.shader.get(); +} + +const AbstractShader* ShaderCache::InsertPixelUberShader(const UberShader::PixelShaderUid& uid, + std::unique_ptr shader) +{ + auto& entry = m_uber_ps_cache.shader_map[uid]; + entry.pending = false; + + if (shader && !entry.shader) + { + if (g_ActiveConfig.bShaderCache && shader->HasBinary()) + { + auto binary = shader->GetBinary(); + if (!binary.empty()) + m_uber_ps_cache.disk_cache.Append(uid, binary.data(), static_cast(binary.size())); + } + INCSTAT(stats.numPixelShadersCreated); + INCSTAT(stats.numPixelShadersAlive); + entry.shader = std::move(shader); + } + + return entry.shader.get(); +} + +const AbstractShader* ShaderCache::CreateGeometryShader(const GeometryShaderUid& uid) +{ + ShaderCode source_code = GenerateGeometryShaderCode(m_api_type, m_host_config, uid.GetUidData()); + std::unique_ptr shader = g_renderer->CreateShaderFromSource( + ShaderStage::Geometry, source_code.GetBuffer().c_str(), source_code.GetBuffer().size()); + + auto& entry = m_gs_cache.shader_map[uid]; + entry.pending = false; + + if (shader && !entry.shader) + { + if (g_ActiveConfig.bShaderCache && shader->HasBinary()) + { + auto binary = shader->GetBinary(); + if (!binary.empty()) + m_gs_cache.disk_cache.Append(uid, binary.data(), static_cast(binary.size())); + } + entry.shader = std::move(shader); + } + + return entry.shader.get(); +} + +bool ShaderCache::NeedsGeometryShader(const GeometryShaderUid& uid) const +{ + return m_host_config.backend_geometry_shaders && !uid.GetUidData()->IsPassthrough(); +} + +AbstractPipelineConfig ShaderCache::GetGXPipelineConfig( + const NativeVertexFormat* vertex_format, const AbstractShader* vertex_shader, + const AbstractShader* geometry_shader, const AbstractShader* pixel_shader, + const RasterizationState& rasterization_state, const DepthState& depth_state, + const BlendingState& blending_state) +{ + AbstractPipelineConfig config = {}; + config.usage = AbstractPipelineUsage::GX; + config.vertex_format = vertex_format; + config.vertex_shader = vertex_shader; + config.geometry_shader = geometry_shader; + config.pixel_shader = pixel_shader; + config.rasterization_state = rasterization_state; + config.depth_state = depth_state; + config.blending_state = blending_state; + config.framebuffer_state.color_texture_format = AbstractTextureFormat::RGBA8; + config.framebuffer_state.depth_texture_format = AbstractTextureFormat::D32F; + config.framebuffer_state.per_sample_shading = m_host_config.ssaa; + config.framebuffer_state.samples = m_efb_multisamples; + return config; +} + +std::optional +ShaderCache::GetGXPipelineConfig(const GXPipelineConfig& config) +{ + const AbstractShader* vs; + auto vs_iter = m_vs_cache.shader_map.find(config.vs_uid); + if (vs_iter != m_vs_cache.shader_map.end() && !vs_iter->second.pending) + vs = vs_iter->second.shader.get(); + else + vs = InsertVertexShader(config.vs_uid, CompileVertexShader(config.vs_uid)); + + const AbstractShader* ps; + auto ps_iter = m_ps_cache.shader_map.find(config.ps_uid); + if (ps_iter != m_ps_cache.shader_map.end() && !ps_iter->second.pending) + ps = ps_iter->second.shader.get(); + else + ps = InsertPixelShader(config.ps_uid, CompilePixelShader(config.ps_uid)); + + if (!vs || !ps) + return {}; + + const AbstractShader* gs = nullptr; + if (NeedsGeometryShader(config.gs_uid)) + { + auto gs_iter = m_gs_cache.shader_map.find(config.gs_uid); + if (gs_iter != m_gs_cache.shader_map.end() && !gs_iter->second.pending) + gs = gs_iter->second.shader.get(); + else + gs = CreateGeometryShader(config.gs_uid); + if (!gs) + return {}; + } + + return GetGXPipelineConfig(config.vertex_format, vs, gs, ps, config.rasterization_state, + config.depth_state, config.blending_state); +} + +std::optional +ShaderCache::GetGXUberPipelineConfig(const GXUberPipelineConfig& config) +{ + const AbstractShader* vs; + auto vs_iter = m_uber_vs_cache.shader_map.find(config.vs_uid); + if (vs_iter != m_uber_vs_cache.shader_map.end() && !vs_iter->second.pending) + vs = vs_iter->second.shader.get(); + else + vs = InsertVertexUberShader(config.vs_uid, CompileVertexUberShader(config.vs_uid)); + + const AbstractShader* ps; + auto ps_iter = m_uber_ps_cache.shader_map.find(config.ps_uid); + if (ps_iter != m_uber_ps_cache.shader_map.end() && !ps_iter->second.pending) + ps = ps_iter->second.shader.get(); + else + ps = InsertPixelUberShader(config.ps_uid, CompilePixelUberShader(config.ps_uid)); + + if (!vs || !ps) + return {}; + + const AbstractShader* gs = nullptr; + if (NeedsGeometryShader(config.gs_uid)) + { + auto gs_iter = m_gs_cache.shader_map.find(config.gs_uid); + if (gs_iter != m_gs_cache.shader_map.end() && !gs_iter->second.pending) + gs = gs_iter->second.shader.get(); + else + gs = CreateGeometryShader(config.gs_uid); + if (!gs) + return {}; + } + + return GetGXPipelineConfig(config.vertex_format, vs, gs, ps, config.rasterization_state, + config.depth_state, config.blending_state); +} + +const AbstractPipeline* ShaderCache::InsertGXPipeline(const GXPipelineConfig& config, + std::unique_ptr pipeline) +{ + auto& entry = m_gx_pipeline_cache[config]; + entry.second = false; + if (!entry.first && pipeline) + entry.first = std::move(pipeline); + + return entry.first.get(); +} + +const AbstractPipeline* +ShaderCache::InsertGXUberPipeline(const GXUberPipelineConfig& config, + std::unique_ptr pipeline) +{ + auto& entry = m_gx_uber_pipeline_cache[config]; + entry.second = false; + if (!entry.first && pipeline) + entry.first = std::move(pipeline); + + return entry.first.get(); +} + +void ShaderCache::AppendGXPipelineUID(const GXPipelineConfig& config) +{ + // Convert to disk format. + GXPipelineDiskCacheUid disk_uid = {}; + disk_uid.vertex_decl = config.vertex_format->GetVertexDeclaration(); + disk_uid.vs_uid = config.vs_uid; + disk_uid.gs_uid = config.gs_uid; + disk_uid.ps_uid = config.ps_uid; + disk_uid.rasterization_state_bits = config.rasterization_state.hex; + disk_uid.depth_state_bits = config.depth_state.hex; + disk_uid.blending_state_bits = config.blending_state.hex; + m_gx_pipeline_uid_disk_cache.Append(disk_uid, nullptr, 0); +} + +void ShaderCache::QueueVertexShaderCompile(const VertexShaderUid& uid) +{ + class VertexShaderWorkItem final : public AsyncShaderCompiler::WorkItem + { + public: + VertexShaderWorkItem(ShaderCache* shader_cache_, const VertexShaderUid& uid_) + : shader_cache(shader_cache_), uid(uid_) + { + } + + bool Compile() override + { + shader = shader_cache->CompileVertexShader(uid); + return true; + } + + virtual void Retrieve() override { shader_cache->InsertVertexShader(uid, std::move(shader)); } + private: + ShaderCache* shader_cache; + std::unique_ptr shader; + VertexShaderUid uid; + }; + + m_vs_cache.shader_map[uid].pending = true; + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid); + m_async_shader_compiler->QueueWorkItem(std::move(wi)); +} + +void ShaderCache::QueueVertexUberShaderCompile(const UberShader::VertexShaderUid& uid) +{ + class VertexUberShaderWorkItem final : public AsyncShaderCompiler::WorkItem + { + public: + VertexUberShaderWorkItem(ShaderCache* shader_cache_, const UberShader::VertexShaderUid& uid_) + : shader_cache(shader_cache_), uid(uid_) + { + } + + bool Compile() override + { + shader = shader_cache->CompileVertexUberShader(uid); + return true; + } + + virtual void Retrieve() override + { + shader_cache->InsertVertexUberShader(uid, std::move(shader)); + } + + private: + ShaderCache* shader_cache; + std::unique_ptr shader; + UberShader::VertexShaderUid uid; + }; + + m_uber_vs_cache.shader_map[uid].pending = true; + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid); + m_async_shader_compiler->QueueWorkItem(std::move(wi)); +} + +void ShaderCache::QueuePixelShaderCompile(const PixelShaderUid& uid) +{ + class PixelShaderWorkItem final : public AsyncShaderCompiler::WorkItem + { + public: + PixelShaderWorkItem(ShaderCache* shader_cache_, const PixelShaderUid& uid_) + : shader_cache(shader_cache_), uid(uid_) + { + } + + bool Compile() override + { + shader = shader_cache->CompilePixelShader(uid); + return true; + } + + virtual void Retrieve() override { shader_cache->InsertPixelShader(uid, std::move(shader)); } + private: + ShaderCache* shader_cache; + std::unique_ptr shader; + PixelShaderUid uid; + }; + + m_ps_cache.shader_map[uid].pending = true; + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid); + m_async_shader_compiler->QueueWorkItem(std::move(wi)); +} + +void ShaderCache::QueuePixelUberShaderCompile(const UberShader::PixelShaderUid& uid) +{ + class PixelUberShaderWorkItem final : public AsyncShaderCompiler::WorkItem + { + public: + PixelUberShaderWorkItem(ShaderCache* shader_cache_, const UberShader::PixelShaderUid& uid_) + : shader_cache(shader_cache_), uid(uid_) + { + } + + bool Compile() override + { + shader = shader_cache->CompilePixelUberShader(uid); + return true; + } + + virtual void Retrieve() override + { + shader_cache->InsertPixelUberShader(uid, std::move(shader)); + } + + private: + ShaderCache* shader_cache; + std::unique_ptr shader; + UberShader::PixelShaderUid uid; + }; + + m_uber_ps_cache.shader_map[uid].pending = true; + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid); + m_async_shader_compiler->QueueWorkItem(std::move(wi)); +} + +void ShaderCache::QueuePipelineCompile(const GXPipelineConfig& uid) +{ + class PipelineWorkItem final : public AsyncShaderCompiler::WorkItem + { + public: + PipelineWorkItem(ShaderCache* shader_cache_, const GXPipelineConfig& uid_, + const AbstractPipelineConfig& config_) + : shader_cache(shader_cache_), uid(uid_), config(config_) + { + } + + bool Compile() override + { + pipeline = g_renderer->CreatePipeline(config); + return true; + } + + virtual void Retrieve() override { shader_cache->InsertGXPipeline(uid, std::move(pipeline)); } + private: + ShaderCache* shader_cache; + std::unique_ptr pipeline; + GXPipelineConfig uid; + AbstractPipelineConfig config; + }; + + auto config = GetGXPipelineConfig(uid); + if (!config) + { + // One or more stages failed to compile. + InsertGXPipeline(uid, nullptr); + return; + } + + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid, *config); + m_async_shader_compiler->QueueWorkItem(std::move(wi)); + m_gx_pipeline_cache[uid].second = true; +} + +void ShaderCache::QueueUberPipelineCompile(const GXUberPipelineConfig& uid) +{ + class UberPipelineWorkItem final : public AsyncShaderCompiler::WorkItem + { + public: + UberPipelineWorkItem(ShaderCache* shader_cache_, const GXUberPipelineConfig& uid_, + const AbstractPipelineConfig& config_) + : shader_cache(shader_cache_), uid(uid_), config(config_) + { + } + + bool Compile() override + { + pipeline = g_renderer->CreatePipeline(config); + return true; + } + + virtual void Retrieve() override + { + shader_cache->InsertGXUberPipeline(uid, std::move(pipeline)); + } + + private: + ShaderCache* shader_cache; + std::unique_ptr pipeline; + GXUberPipelineConfig uid; + AbstractPipelineConfig config; + }; + + auto config = GetGXUberPipelineConfig(uid); + if (!config) + { + // One or more stages failed to compile. + InsertGXUberPipeline(uid, nullptr); + return; + } + + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid, *config); + m_async_shader_compiler->QueueWorkItem(std::move(wi)); + m_gx_uber_pipeline_cache[uid].second = true; +} + +void ShaderCache::PrecompileUberShaders() +{ + // Geometry shaders are required for the pipelines. + if (m_host_config.backend_geometry_shaders) + { + EnumerateGeometryShaderUids([&](const GeometryShaderUid& guid) { + auto iter = m_gs_cache.shader_map.find(guid); + if (iter == m_gs_cache.shader_map.end()) + CreateGeometryShader(guid); + }); + } + + // Queue shader compiling. + UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& vuid) { + auto iter = m_uber_vs_cache.shader_map.find(vuid); + if (iter == m_uber_vs_cache.shader_map.end()) + QueueVertexUberShaderCompile(vuid); + }); + UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& puid) { + auto iter = m_uber_ps_cache.shader_map.find(puid); + if (iter == m_uber_ps_cache.shader_map.end()) + QueuePixelUberShaderCompile(puid); + }); + + // Wait for shaders to finish compiling. + WaitForAsyncCompiler(GetStringT("Compiling uber shaders...")); + + // Create a dummy vertex format with no attributes. + // All attributes will be enabled in GetUberVertexFormat. + PortableVertexDeclaration dummy_vertex_decl = {}; + dummy_vertex_decl.position.components = 4; + dummy_vertex_decl.position.type = VAR_FLOAT; + dummy_vertex_decl.position.enable = true; + dummy_vertex_decl.stride = sizeof(float) * 4; + NativeVertexFormat* dummy_vertex_format = + VertexLoaderManager::GetUberVertexFormat(dummy_vertex_decl); + auto QueueDummyPipeline = [&](const UberShader::VertexShaderUid& vs_uid, + const GeometryShaderUid& gs_uid, + const UberShader::PixelShaderUid& ps_uid) { + GXUberPipelineConfig config; + config.vertex_format = dummy_vertex_format; + config.vs_uid = vs_uid; + config.gs_uid = gs_uid; + config.ps_uid = ps_uid; + config.rasterization_state = RenderState::GetNoCullRasterizationState(); + config.depth_state = RenderState::GetNoDepthTestingDepthStencilState(); + config.blending_state = RenderState::GetNoBlendingBlendState(); + + auto iter = m_gx_uber_pipeline_cache.find(config); + if (iter != m_gx_uber_pipeline_cache.end()) + return; + + auto& entry = m_gx_uber_pipeline_cache[config]; + entry.second = false; + }; + + // Populate the pipeline configs with empty entries, these will be compiled afterwards. + UberShader::EnumerateVertexShaderUids([&](const UberShader::VertexShaderUid& vuid) { + UberShader::EnumeratePixelShaderUids([&](const UberShader::PixelShaderUid& puid) { + // UIDs must have compatible texgens, a mismatching combination will never be queried. + if (vuid.GetUidData()->num_texgens != puid.GetUidData()->num_texgens) + return; + + EnumerateGeometryShaderUids([&](const GeometryShaderUid& guid) { + if (guid.GetUidData()->numTexGens != vuid.GetUidData()->num_texgens) + return; + + QueueDummyPipeline(vuid, guid, puid); + }); + }); + }); +} + +std::string ShaderCache::GetUtilityShaderHeader() const +{ + std::stringstream ss; + + ss << "#define API_D3D " << (m_api_type == APIType::D3D ? 1 : 0) << "\n"; + ss << "#define API_OPENGL " << (m_api_type == APIType::OpenGL ? 1 : 0) << "\n"; + ss << "#define API_VULKAN " << (m_api_type == APIType::Vulkan ? 1 : 0) << "\n"; + + if (m_efb_multisamples > 1) + { + ss << "#define MSAA_ENABLED 1" << std::endl; + ss << "#define MSAA_SAMPLES " << m_efb_multisamples << std::endl; + if (m_host_config.ssaa) + ss << "#define SSAA_ENABLED 1" << std::endl; + } + + ss << "#define EFB_LAYERS " << (m_host_config.stereo ? 2 : 1) << std::endl; + + return ss.str(); +} +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/ShaderCache.h b/Source/Core/VideoCommon/ShaderCache.h new file mode 100644 index 0000000000..922cd4d5fa --- /dev/null +++ b/Source/Core/VideoCommon/ShaderCache.h @@ -0,0 +1,216 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/LinearDiskCache.h" + +#include "VideoCommon/AbstractPipeline.h" +#include "VideoCommon/AbstractShader.h" +#include "VideoCommon/NativeVertexFormat.h" + +#include "VideoCommon/AsyncShaderCompiler.h" +#include "VideoCommon/GeometryShaderGen.h" +#include "VideoCommon/PixelShaderGen.h" +#include "VideoCommon/RenderState.h" +#include "VideoCommon/UberShaderPixel.h" +#include "VideoCommon/UberShaderVertex.h" +#include "VideoCommon/VertexShaderGen.h" + +class NativeVertexFormat; + +namespace VideoCommon +{ +struct GXPipelineConfig +{ + const NativeVertexFormat* vertex_format; + VertexShaderUid vs_uid; + GeometryShaderUid gs_uid; + PixelShaderUid ps_uid; + RasterizationState rasterization_state; + DepthState depth_state; + BlendingState blending_state; + + bool operator<(const GXPipelineConfig& rhs) const + { + return std::tie(vertex_format, vs_uid, gs_uid, ps_uid, rasterization_state.hex, depth_state.hex, + blending_state.hex) < std::tie(rhs.vertex_format, rhs.vs_uid, rhs.gs_uid, + rhs.ps_uid, rhs.rasterization_state.hex, + rhs.depth_state.hex, rhs.blending_state.hex); + } + bool operator==(const GXPipelineConfig& rhs) const + { + return std::tie(vertex_format, vs_uid, gs_uid, ps_uid, rasterization_state.hex, depth_state.hex, + blending_state.hex) == std::tie(rhs.vertex_format, rhs.vs_uid, rhs.gs_uid, + rhs.ps_uid, rhs.rasterization_state.hex, + rhs.depth_state.hex, rhs.blending_state.hex); + } + bool operator!=(const GXPipelineConfig& rhs) const { return !operator==(rhs); } +}; +struct GXUberPipelineConfig +{ + const NativeVertexFormat* vertex_format; + UberShader::VertexShaderUid vs_uid; + GeometryShaderUid gs_uid; + UberShader::PixelShaderUid ps_uid; + RasterizationState rasterization_state; + DepthState depth_state; + BlendingState blending_state; + + bool operator<(const GXUberPipelineConfig& rhs) const + { + return std::tie(vertex_format, vs_uid, gs_uid, ps_uid, rasterization_state.hex, depth_state.hex, + blending_state.hex) < std::tie(rhs.vertex_format, rhs.vs_uid, rhs.gs_uid, + rhs.ps_uid, rhs.rasterization_state.hex, + rhs.depth_state.hex, rhs.blending_state.hex); + } + bool operator==(const GXUberPipelineConfig& rhs) const + { + return std::tie(vertex_format, vs_uid, gs_uid, ps_uid, rasterization_state.hex, depth_state.hex, + blending_state.hex) == std::tie(rhs.vertex_format, rhs.vs_uid, rhs.gs_uid, + rhs.ps_uid, rhs.rasterization_state.hex, + rhs.depth_state.hex, rhs.blending_state.hex); + } + bool operator!=(const GXUberPipelineConfig& rhs) const { return !operator==(rhs); } +}; + +class ShaderCache final +{ +public: + ShaderCache(); + ~ShaderCache(); + + // Perform at startup, create descriptor layouts, compiles all static shaders. + bool Initialize(); + void Shutdown(); + + // Changes the shader host config. Shaders will be reloaded if there are changes. + void SetHostConfig(const ShaderHostConfig& host_config, u32 efb_multisamples); + + // Reloads/recreates all shaders and pipelines. + void Reload(); + + // Retrieves all pending shaders/pipelines from the async compiler. + void RetrieveAsyncShaders(); + + // Get utility shader header based on current config. + std::string GetUtilityShaderHeader() const; + + // Accesses ShaderGen shader caches + const AbstractPipeline* GetPipelineForUid(const GXPipelineConfig& uid); + const AbstractPipeline* GetUberPipelineForUid(const GXUberPipelineConfig& uid); + + // Accesses ShaderGen shader caches asynchronously. + // The optional will be empty if this pipeline is now background compiling. + std::optional GetPipelineForUidAsync(const GXPipelineConfig& uid); + +private: + void WaitForAsyncCompiler(const std::string& msg); + void LoadShaderCaches(); + void ClearShaderCaches(); + void LoadPipelineUIDCache(); + void CompileMissingPipelines(); + void InvalidateCachedPipelines(); + void ClearPipelineCaches(); + void PrecompileUberShaders(); + + // GX shader compiler methods + std::unique_ptr CompileVertexShader(const VertexShaderUid& uid) const; + std::unique_ptr + CompileVertexUberShader(const UberShader::VertexShaderUid& uid) const; + std::unique_ptr CompilePixelShader(const PixelShaderUid& uid) const; + std::unique_ptr + CompilePixelUberShader(const UberShader::PixelShaderUid& uid) const; + const AbstractShader* InsertVertexShader(const VertexShaderUid& uid, + std::unique_ptr shader); + const AbstractShader* InsertVertexUberShader(const UberShader::VertexShaderUid& uid, + std::unique_ptr shader); + const AbstractShader* InsertPixelShader(const PixelShaderUid& uid, + std::unique_ptr shader); + const AbstractShader* InsertPixelUberShader(const UberShader::PixelShaderUid& uid, + std::unique_ptr shader); + const AbstractShader* CreateGeometryShader(const GeometryShaderUid& uid); + bool NeedsGeometryShader(const GeometryShaderUid& uid) const; + + // GX pipeline compiler methods + AbstractPipelineConfig + GetGXPipelineConfig(const NativeVertexFormat* vertex_format, const AbstractShader* vertex_shader, + const AbstractShader* geometry_shader, const AbstractShader* pixel_shader, + const RasterizationState& rasterization_state, const DepthState& depth_state, + const BlendingState& blending_state); + std::optional GetGXPipelineConfig(const GXPipelineConfig& uid); + std::optional GetGXUberPipelineConfig(const GXUberPipelineConfig& uid); + const AbstractPipeline* InsertGXPipeline(const GXPipelineConfig& config, + std::unique_ptr pipeline); + const AbstractPipeline* InsertGXUberPipeline(const GXUberPipelineConfig& config, + std::unique_ptr pipeline); + void AppendGXPipelineUID(const GXPipelineConfig& config); + + // ASync Compiler Methods + void QueueVertexShaderCompile(const VertexShaderUid& uid); + void QueueVertexUberShaderCompile(const UberShader::VertexShaderUid& uid); + void QueuePixelShaderCompile(const PixelShaderUid& uid); + void QueuePixelUberShaderCompile(const UberShader::PixelShaderUid& uid); + void QueuePipelineCompile(const GXPipelineConfig& uid); + void QueueUberPipelineCompile(const GXUberPipelineConfig& uid); + + // Configuration bits. + APIType m_api_type = APIType::Nothing; + ShaderHostConfig m_host_config = {}; + u32 m_efb_multisamples = 1; + std::unique_ptr m_async_shader_compiler; + + // GX Shader Caches + template + struct ShaderModuleCache + { + struct Shader + { + std::unique_ptr shader; + bool pending; + }; + std::map shader_map; + LinearDiskCache disk_cache; + }; + ShaderModuleCache m_vs_cache; + ShaderModuleCache m_gs_cache; + ShaderModuleCache m_ps_cache; + ShaderModuleCache m_uber_vs_cache; + ShaderModuleCache m_uber_ps_cache; + + // GX Pipeline Caches - .first - pipeline, .second - pending + // TODO: Use unordered_map for speed. + std::map, bool>> + m_gx_pipeline_cache; + std::map, bool>> + m_gx_uber_pipeline_cache; + + // Disk cache of pipeline UIDs + // We can't use the whole UID as a type + struct GXPipelineDiskCacheUid + { + PortableVertexDeclaration vertex_decl; + VertexShaderUid vs_uid; + GeometryShaderUid gs_uid; + PixelShaderUid ps_uid; + u32 rasterization_state_bits; + u32 depth_state_bits; + u32 blending_state_bits; + }; + LinearDiskCache m_gx_pipeline_uid_disk_cache; +}; + +} // namespace VideoCommon + +extern std::unique_ptr g_shader_cache; diff --git a/Source/Core/VideoCommon/ShaderGenCommon.cpp b/Source/Core/VideoCommon/ShaderGenCommon.cpp index 8cc7e54f5c..b4cf9e9a48 100644 --- a/Source/Core/VideoCommon/ShaderGenCommon.cpp +++ b/Source/Core/VideoCommon/ShaderGenCommon.cpp @@ -37,28 +37,31 @@ ShaderHostConfig ShaderHostConfig::GetCurrent() } std::string GetDiskShaderCacheFileName(APIType api_type, const char* type, bool include_gameid, - bool include_host_config) + bool include_host_config, bool include_api) { if (!File::Exists(File::GetUserPath(D_SHADERCACHE_IDX))) File::CreateDir(File::GetUserPath(D_SHADERCACHE_IDX)); std::string filename = File::GetUserPath(D_SHADERCACHE_IDX); - switch (api_type) + if (include_api) { - case APIType::D3D: - filename += "D3D"; - break; - case APIType::OpenGL: - filename += "OpenGL"; - break; - case APIType::Vulkan: - filename += "Vulkan"; - break; - default: - break; + switch (api_type) + { + case APIType::D3D: + filename += "D3D"; + break; + case APIType::OpenGL: + filename += "OpenGL"; + break; + case APIType::Vulkan: + filename += "Vulkan"; + break; + default: + break; + } + filename += '-'; } - filename += '-'; filename += type; if (include_gameid) diff --git a/Source/Core/VideoCommon/ShaderGenCommon.h b/Source/Core/VideoCommon/ShaderGenCommon.h index 5aa391c412..af9b97cf2f 100644 --- a/Source/Core/VideoCommon/ShaderGenCommon.h +++ b/Source/Core/VideoCommon/ShaderGenCommon.h @@ -187,7 +187,7 @@ union ShaderHostConfig // Gets the filename of the specified type of cache object (e.g. vertex shader, pipeline). std::string GetDiskShaderCacheFileName(APIType api_type, const char* type, bool include_gameid, - bool include_host_config); + bool include_host_config, bool include_api = true); template inline void DefineOutputMember(T& object, APIType api_type, const char* qualifier, const char* type, diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 44ff88b633..8ccf9865e5 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -105,10 +105,11 @@ DataReader VertexManagerBase::PrepareForAdditionalData(int primitive, u32 count, Flush(); // Have to update the rasterization state for point/line cull modes. + m_current_primitive_type = new_primitive_type; RasterizationState raster_state = {}; raster_state.Generate(bpmem, new_primitive_type); g_renderer->SetRasterizationState(raster_state); - m_current_primitive_type = new_primitive_type; + SetRasterizationStateChanged(); } // Check for size in buffer, if the buffer gets full, call Flush() @@ -386,6 +387,10 @@ void VertexManagerBase::Flush() if (!m_cull_all) { + // Update the pipeline, or compile one if needed. + UpdatePipelineConfig(); + UpdatePipelineObject(); + // set the rest of the global constants GeometryShaderManager::SetConstants(); PixelShaderManager::SetConstants(); @@ -477,3 +482,114 @@ void VertexManagerBase::CalculateZSlope(NativeVertexFormat* format) m_zslope.f0 = out[2] - (out[0] * m_zslope.dfdx + out[1] * m_zslope.dfdy); m_zslope.dirty = true; } + +void VertexManagerBase::UpdatePipelineConfig() +{ + NativeVertexFormat* vertex_format = VertexLoaderManager::GetCurrentVertexFormat(); + if (vertex_format != m_current_pipeline_config.vertex_format) + { + m_current_pipeline_config.vertex_format = vertex_format; + m_current_uber_pipeline_config.vertex_format = + VertexLoaderManager::GetUberVertexFormat(vertex_format->GetVertexDeclaration()); + m_pipeline_config_changed = true; + } + + VertexShaderUid vs_uid = GetVertexShaderUid(); + if (vs_uid != m_current_pipeline_config.vs_uid) + { + m_current_pipeline_config.vs_uid = vs_uid; + m_current_uber_pipeline_config.vs_uid = UberShader::GetVertexShaderUid(); + m_pipeline_config_changed = true; + } + + PixelShaderUid ps_uid = GetPixelShaderUid(); + if (ps_uid != m_current_pipeline_config.ps_uid) + { + m_current_pipeline_config.ps_uid = ps_uid; + m_current_uber_pipeline_config.ps_uid = UberShader::GetPixelShaderUid(); + m_pipeline_config_changed = true; + } + + GeometryShaderUid gs_uid = GetGeometryShaderUid(GetCurrentPrimitiveType()); + if (gs_uid != m_current_pipeline_config.gs_uid) + { + m_current_pipeline_config.gs_uid = gs_uid; + m_current_uber_pipeline_config.gs_uid = gs_uid; + m_pipeline_config_changed = true; + } + + if (m_rasterization_state_changed) + { + m_rasterization_state_changed = false; + + RasterizationState new_rs = {}; + new_rs.Generate(bpmem, m_current_primitive_type); + if (new_rs != m_current_pipeline_config.rasterization_state) + { + m_current_pipeline_config.rasterization_state = new_rs; + m_current_uber_pipeline_config.rasterization_state = new_rs; + m_pipeline_config_changed = true; + } + } + + if (m_depth_state_changed) + { + m_depth_state_changed = false; + + DepthState new_ds = {}; + new_ds.Generate(bpmem); + if (new_ds != m_current_pipeline_config.depth_state) + { + m_current_pipeline_config.depth_state = new_ds; + m_current_uber_pipeline_config.depth_state = new_ds; + m_pipeline_config_changed = true; + } + } + + if (m_blending_state_changed) + { + m_blending_state_changed = false; + + BlendingState new_bs = {}; + new_bs.Generate(bpmem); + if (new_bs != m_current_pipeline_config.blending_state) + { + m_current_pipeline_config.blending_state = new_bs; + m_current_uber_pipeline_config.blending_state = new_bs; + m_pipeline_config_changed = true; + } + } +} + +void VertexManagerBase::UpdatePipelineObject() +{ + if (!m_pipeline_config_changed) + return; + + m_current_pipeline_object = nullptr; + m_pipeline_config_changed = false; + + // Try for specialized shaders. + if (!g_ActiveConfig.bDisableSpecializedShaders) + { + // Can we background compile shaders? If so, get the pipeline asynchronously. + if (g_ActiveConfig.bBackgroundShaderCompiling) + { + auto res = g_shader_cache->GetPipelineForUidAsync(m_current_pipeline_config); + if (res) + { + // Specialized shaders are ready. + m_current_pipeline_object = *res; + return; + } + } + else + { + m_current_pipeline_object = g_shader_cache->GetPipelineForUid(m_current_pipeline_config); + return; + } + } + + // Fallback to ubershaders. + m_current_pipeline_object = g_shader_cache->GetUberPipelineForUid(m_current_uber_pipeline_config); +} diff --git a/Source/Core/VideoCommon/VertexManagerBase.h b/Source/Core/VideoCommon/VertexManagerBase.h index 22c9b0d2a6..20a63a3e2a 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.h +++ b/Source/Core/VideoCommon/VertexManagerBase.h @@ -10,6 +10,7 @@ #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" #include "VideoCommon/RenderState.h" +#include "VideoCommon/ShaderCache.h" class DataReader; class NativeVertexFormat; @@ -58,6 +59,16 @@ public: std::pair ResetFlushAspectRatioCount(); + // State setters, called from register update functions. + void SetRasterizationStateChanged() { m_rasterization_state_changed = true; } + void SetDepthStateChanged() { m_depth_state_changed = true; } + void SetBlendingStateChanged() { m_blending_state_changed = true; } + void InvalidatePipelineObject() + { + m_current_pipeline_object = nullptr; + m_pipeline_config_changed = true; + } + protected: virtual void vDoState(PointerWrap& p) {} virtual void ResetBuffer(u32 stride) = 0; @@ -72,8 +83,15 @@ protected: Slope m_zslope = {}; void CalculateZSlope(NativeVertexFormat* format); - bool m_cull_all = false; + VideoCommon::GXPipelineConfig m_current_pipeline_config = {}; + VideoCommon::GXUberPipelineConfig m_current_uber_pipeline_config = {}; + const AbstractPipeline* m_current_pipeline_object = nullptr; PrimitiveType m_current_primitive_type = PrimitiveType::Points; + bool m_pipeline_config_changed = true; + bool m_rasterization_state_changed = true; + bool m_depth_state_changed = true; + bool m_blending_state_changed = true; + bool m_cull_all = false; private: bool m_is_flushed = true; @@ -84,6 +102,8 @@ private: virtual void CreateDeviceObjects() {} virtual void DestroyDeviceObjects() {} + void UpdatePipelineConfig(); + void UpdatePipelineObject(); }; extern std::unique_ptr g_vertex_manager; diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index 33931bdb17..4cc6611ee8 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -68,6 +68,7 @@ + @@ -119,6 +120,7 @@ + @@ -188,4 +190,4 @@ - \ No newline at end of file + diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index cc7d80508c..ca0fe0029b 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -197,6 +197,9 @@ Base + + Shader Managers + @@ -378,6 +381,9 @@ Base + + Shader Managers + diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 2a4811eece..771adf60bf 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -187,6 +187,10 @@ static u32 GetNumAutoShaderCompilerThreads() u32 VideoConfig::GetShaderCompilerThreads() const { + // videocommon shader cache is currently broken on OGL, needs multiple contexts. + if (backend_info.api_type == APIType::OpenGL) + return 0; + if (iShaderCompilerThreads >= 0) return static_cast(iShaderCompilerThreads); else @@ -195,6 +199,10 @@ u32 VideoConfig::GetShaderCompilerThreads() const u32 VideoConfig::GetShaderPrecompilerThreads() const { + // videocommon shader cache is currently broken on OGL, needs multiple contexts. + if (backend_info.api_type == APIType::OpenGL) + return 0; + if (iShaderPrecompilerThreads >= 0) return static_cast(iShaderPrecompilerThreads); else