From 00204dc98866c180aecc8bbfac583af63fbe9676 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 24 Feb 2018 22:14:31 +1000 Subject: [PATCH 01/16] OGL: Make ProgramShaderCache thread safe --- .../VideoBackends/OGL/ProgramShaderCache.cpp | 23 +++++++++++++++---- .../VideoBackends/OGL/ProgramShaderCache.h | 2 ++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp index c2f9ddf468..5a3a09281f 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp @@ -60,6 +60,7 @@ 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; @@ -910,11 +911,14 @@ const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* v const OGLShader* pixel_shader) { PipelineProgramKey key = {vertex_shader, geometry_shader, pixel_shader}; - auto iter = pipelineprograms.find(key); - if (iter != pipelineprograms.end()) { - iter->second->reference_count++; - return iter->second.get(); + std::lock_guard guard(pipelineprogramlock); + auto iter = pipelineprograms.find(key); + if (iter != pipelineprograms.end()) + { + iter->second->reference_count++; + return iter->second.get(); + } } std::unique_ptr prog = std::make_unique(); @@ -941,6 +945,17 @@ const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* v return nullptr; } + // Lock to insert. A duplicate program may have been created in the meantime. + std::lock_guard guard(pipelineprogramlock); + auto iter = pipelineprograms.find(key); + if (iter != pipelineprograms.end()) + { + // Destroy this program, and use the one which was created first. + prog->shader.Destroy(); + iter->second->reference_count++; + return iter->second.get(); + } + auto ip = pipelineprograms.emplace(key, std::move(prog)); return ip.first->second.get(); } diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h index 635c1d3097..6db44460a1 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -242,6 +243,7 @@ private: 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; From a61fcb0088a0aef0b2aa79a84e5d7dc1252bc317 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 3 Mar 2018 19:10:45 +1000 Subject: [PATCH 02/16] OGL: Fix abstract pipelines on drivers without binding layout support --- .../VideoBackends/OGL/ProgramShaderCache.cpp | 60 ++++++++++--------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp index 5a3a09281f..a4cb6620dc 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp @@ -98,37 +98,40 @@ static std::string GetGLSLVersionString() void SHADER::SetProgramVariables() { + if (g_ActiveConfig.backend_info.bSupportsBindingLayout) + return; + + // To set uniform blocks/uniforms, the program must be active. We restore the + // current binding at the end of this method to maintain the invariant. + glUseProgram(glprogid); + // Bind UBO and texture samplers - if (!g_ActiveConfig.backend_info.bSupportsBindingLayout) + GLint PSBlock_id = glGetUniformBlockIndex(glprogid, "PSBlock"); + GLint VSBlock_id = glGetUniformBlockIndex(glprogid, "VSBlock"); + GLint GSBlock_id = glGetUniformBlockIndex(glprogid, "GSBlock"); + GLint UBERBlock_id = glGetUniformBlockIndex(glprogid, "UBERBlock"); + if (PSBlock_id != -1) + glUniformBlockBinding(glprogid, PSBlock_id, 1); + if (VSBlock_id != -1) + glUniformBlockBinding(glprogid, VSBlock_id, 2); + if (GSBlock_id != -1) + glUniformBlockBinding(glprogid, GSBlock_id, 3); + if (UBERBlock_id != -1) + glUniformBlockBinding(glprogid, UBERBlock_id, 4); + + // Bind Texture Samplers + for (int a = 0; a < 10; ++a) { - // glsl shader must be bind to set samplers if we don't support binding layout - Bind(); + std::string name = StringFromFormat(a < 8 ? "samp[%d]" : "samp%d", a); - GLint PSBlock_id = glGetUniformBlockIndex(glprogid, "PSBlock"); - GLint VSBlock_id = glGetUniformBlockIndex(glprogid, "VSBlock"); - GLint GSBlock_id = glGetUniformBlockIndex(glprogid, "GSBlock"); - GLint UBERBlock_id = glGetUniformBlockIndex(glprogid, "UBERBlock"); - - if (PSBlock_id != -1) - glUniformBlockBinding(glprogid, PSBlock_id, 1); - if (VSBlock_id != -1) - glUniformBlockBinding(glprogid, VSBlock_id, 2); - if (GSBlock_id != -1) - glUniformBlockBinding(glprogid, GSBlock_id, 3); - if (UBERBlock_id != -1) - glUniformBlockBinding(glprogid, UBERBlock_id, 4); - - // Bind Texture Samplers - for (int a = 0; a <= 9; ++a) - { - std::string name = StringFromFormat(a < 8 ? "samp[%d]" : "samp%d", a); - - // Still need to get sampler locations since we aren't binding them statically in the shaders - int loc = glGetUniformLocation(glprogid, name.c_str()); - if (loc != -1) - glUniform1i(loc, a); - } + // Still need to get sampler locations since we aren't binding them statically in the shaders + int loc = glGetUniformLocation(glprogid, name.c_str()); + if (loc != -1) + glUniform1i(loc, a); } + + // Restore previous program binding. + glUseProgram(CurrentProgram); } void SHADER::SetProgramBindings(bool is_compute) @@ -956,6 +959,9 @@ const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* v return iter->second.get(); } + // Set program variables on the shader which will be returned. + // This is only needed for drivers which don't support binding layout. + prog->shader.SetProgramVariables(); auto ip = pipelineprograms.emplace(key, std::move(prog)); return ip.first->second.get(); } From 5e5dfe686ab04edcaf18a2dee0f18407645384e4 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 24 Feb 2018 23:10:22 +1000 Subject: [PATCH 03/16] VKPipeline: Fix render pass and add pipeline layout fields --- Source/Core/VideoBackends/Vulkan/VKPipeline.cpp | 9 ++++++--- Source/Core/VideoBackends/Vulkan/VKPipeline.h | 9 +++++++-- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp b/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp index df245644c4..c6c0aa0c85 100644 --- a/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKPipeline.cpp @@ -14,7 +14,9 @@ namespace Vulkan { -VKPipeline::VKPipeline(VkPipeline pipeline) : m_pipeline(pipeline) +VKPipeline::VKPipeline(VkPipeline pipeline, VkPipelineLayout pipeline_layout, + AbstractPipelineUsage usage) + : m_pipeline(pipeline), m_pipeline_layout(pipeline_layout), m_usage(usage) { } @@ -30,7 +32,8 @@ std::unique_ptr VKPipeline::Create(const AbstractPipelineConfig& con // Get render pass for config. VkRenderPass render_pass = g_object_cache->GetRenderPass( Util::GetVkFormatForHostTextureFormat(config.framebuffer_state.color_texture_format), - VK_FORMAT_UNDEFINED, config.framebuffer_state.samples, VK_ATTACHMENT_LOAD_OP_LOAD); + Util::GetVkFormatForHostTextureFormat(config.framebuffer_state.depth_texture_format), + config.framebuffer_state.samples, VK_ATTACHMENT_LOAD_OP_LOAD); // Get pipeline layout. VkPipelineLayout pipeline_layout; @@ -68,6 +71,6 @@ std::unique_ptr VKPipeline::Create(const AbstractPipelineConfig& con if (pipeline == VK_NULL_HANDLE) return nullptr; - return std::make_unique(pipeline); + return std::make_unique(pipeline, pipeline_layout, config.usage); } } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/VKPipeline.h b/Source/Core/VideoBackends/Vulkan/VKPipeline.h index ad66313e4c..767215062a 100644 --- a/Source/Core/VideoBackends/Vulkan/VKPipeline.h +++ b/Source/Core/VideoBackends/Vulkan/VKPipeline.h @@ -14,14 +14,19 @@ namespace Vulkan class VKPipeline final : public AbstractPipeline { public: - explicit VKPipeline(VkPipeline pipeline); + explicit VKPipeline(VkPipeline pipeline, VkPipelineLayout pipeline_layout, + AbstractPipelineUsage usage); ~VKPipeline() override; - VkPipeline GetPipeline() const { return m_pipeline; } + VkPipeline GetVkPipeline() const { return m_pipeline; } + VkPipelineLayout GetVkPipelineLayout() const { return m_pipeline_layout; } + AbstractPipelineUsage GetUsage() const { return m_usage; } static std::unique_ptr Create(const AbstractPipelineConfig& config); private: VkPipeline m_pipeline; + VkPipelineLayout m_pipeline_layout; + AbstractPipelineUsage m_usage; }; } // namespace Vulkan From bfb4709c80fe07c1a072eb514f560a81a1a4b5f3 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 24 Feb 2018 23:13:00 +1000 Subject: [PATCH 04/16] AbstractPipeline: Allow setting pipeline to null --- Source/Core/VideoBackends/D3D/Render.cpp | 2 ++ Source/Core/VideoBackends/OGL/Render.cpp | 3 +++ 2 files changed, 5 insertions(+) diff --git a/Source/Core/VideoBackends/D3D/Render.cpp b/Source/Core/VideoBackends/D3D/Render.cpp index 79fa51a504..bce1ad674e 100644 --- a/Source/Core/VideoBackends/D3D/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Render.cpp @@ -299,6 +299,8 @@ void Renderer::UpdateUtilityVertexBuffer(const void* vertices, u32 vertex_stride void Renderer::SetPipeline(const AbstractPipeline* pipeline) { const DXPipeline* dx_pipeline = static_cast(pipeline); + if (!dx_pipeline) + return; D3D::stateman->SetRasterizerState(dx_pipeline->GetRasterizerState()); D3D::stateman->SetDepthState(dx_pipeline->GetDepthState()); diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index e7c15782ec..ae9e45db99 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -1622,6 +1622,9 @@ void Renderer::SetPipeline(const AbstractPipeline* pipeline) // Not all shader changes currently go through SetPipeline, so we can't // test if the pipeline hasn't changed and skip these applications. Yet. m_graphics_pipeline = static_cast(pipeline); + if (!m_graphics_pipeline) + return; + ApplyRasterizationState(m_graphics_pipeline->GetRasterizationState()); ApplyDepthState(m_graphics_pipeline->GetDepthState()); ApplyBlendingState(m_graphics_pipeline->GetBlendingState()); From 40845e6b8f10d1fb0d4b6d02b1994b1ebe7f8552 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 25 Feb 2018 00:21:04 +1000 Subject: [PATCH 05/16] D3D: Make StateCache thread safe --- Source/Core/VideoBackends/D3D/D3DState.cpp | 4 ++++ Source/Core/VideoBackends/D3D/D3DState.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/Source/Core/VideoBackends/D3D/D3DState.cpp b/Source/Core/VideoBackends/D3D/D3DState.cpp index b779683fb8..8a2862479b 100644 --- a/Source/Core/VideoBackends/D3D/D3DState.cpp +++ b/Source/Core/VideoBackends/D3D/D3DState.cpp @@ -212,6 +212,7 @@ StateCache::~StateCache() ID3D11SamplerState* StateCache::Get(SamplerState state) { + std::lock_guard guard(m_lock); auto it = m_sampler.find(state.hex); if (it != m_sampler.end()) return it->second; @@ -266,6 +267,7 @@ ID3D11SamplerState* StateCache::Get(SamplerState state) ID3D11BlendState* StateCache::Get(BlendingState state) { + std::lock_guard guard(m_lock); auto it = m_blend.find(state.hex); if (it != m_blend.end()) return it->second; @@ -348,6 +350,7 @@ ID3D11BlendState* StateCache::Get(BlendingState state) ID3D11RasterizerState* StateCache::Get(RasterizationState state) { + std::lock_guard guard(m_lock); auto it = m_raster.find(state.hex); if (it != m_raster.end()) return it->second; @@ -372,6 +375,7 @@ ID3D11RasterizerState* StateCache::Get(RasterizationState state) ID3D11DepthStencilState* StateCache::Get(DepthState state) { + std::lock_guard guard(m_lock); auto it = m_depth.find(state.hex); if (it != m_depth.end()) return it->second; diff --git a/Source/Core/VideoBackends/D3D/D3DState.h b/Source/Core/VideoBackends/D3D/D3DState.h index 805ed8996b..db48e7f18b 100644 --- a/Source/Core/VideoBackends/D3D/D3DState.h +++ b/Source/Core/VideoBackends/D3D/D3DState.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "Common/BitField.h" @@ -35,6 +36,7 @@ private: std::unordered_map m_raster; std::unordered_map m_blend; std::unordered_map m_sampler; + std::mutex m_lock; }; namespace D3D From 1ddc4c55685d38741f95e286fad5b49537ef5399 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 25 Feb 2018 00:23:24 +1000 Subject: [PATCH 06/16] D3D: Make NativeVertexFormat thread safe --- .../VideoBackends/D3D/NativeVertexFormat.cpp | 29 ++++++++++++++----- Source/Core/VideoBackends/D3D/VertexManager.h | 3 +- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp b/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp index 525e2b33cb..4ba64eec21 100644 --- a/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp +++ b/Source/Core/VideoBackends/D3D/NativeVertexFormat.cpp @@ -13,6 +13,8 @@ namespace DX11 { +std::mutex s_input_layout_lock; + std::unique_ptr VertexManager::CreateNativeVertexFormat(const PortableVertexDeclaration& vtx_decl) { @@ -116,23 +118,34 @@ D3DVertexFormat::D3DVertexFormat(const PortableVertexDeclaration& _vtx_decl) D3DVertexFormat::~D3DVertexFormat() { - SAFE_RELEASE(m_layout); + ID3D11InputLayout* layout = m_layout.load(); + SAFE_RELEASE(layout); } ID3D11InputLayout* D3DVertexFormat::GetInputLayout(D3DBlob* vs_bytecode) { - if (m_layout) - return m_layout; + // CreateInputLayout requires a shader input, but it only looks at the signature of the shader, + // so we don't need to recompute it if the shader changes. + ID3D11InputLayout* layout = m_layout.load(); + if (layout) + return layout; - // CreateInputLayout requires a shader input, but it only looks at the - // signature of the shader, so we don't need to recompute it if the shader - // changes. HRESULT hr = DX11::D3D::device->CreateInputLayout( - m_elems.data(), m_num_elems, vs_bytecode->Data(), vs_bytecode->Size(), &m_layout); + m_elems.data(), m_num_elems, vs_bytecode->Data(), vs_bytecode->Size(), &layout); if (FAILED(hr)) PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__); DX11::D3D::SetDebugObjectName(m_layout, "input layout used to emulate the GX pipeline"); - return m_layout; + + // This method can be called from multiple threads, so ensure that only one thread sets the + // cached input layout pointer. If another thread beats this thread, use the existing layout. + ID3D11InputLayout* expected = nullptr; + if (!m_layout.compare_exchange_strong(expected, layout)) + { + SAFE_RELEASE(layout); + layout = expected; + } + + return layout; } } // namespace DX11 diff --git a/Source/Core/VideoBackends/D3D/VertexManager.h b/Source/Core/VideoBackends/D3D/VertexManager.h index 5b20cbabaa..c4b45931d2 100644 --- a/Source/Core/VideoBackends/D3D/VertexManager.h +++ b/Source/Core/VideoBackends/D3D/VertexManager.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -29,7 +30,7 @@ private: std::array m_elems{}; UINT m_num_elems = 0; - ID3D11InputLayout* m_layout = nullptr; + std::atomic m_layout{nullptr}; }; class VertexManager : public VertexManagerBase From 24df896eb8d1520706b086039743ef2dca7ce6df Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 25 Feb 2018 01:09:32 +1000 Subject: [PATCH 07/16] VKShader: Fix incorrect loading of binary shaders --- Source/Core/VideoBackends/Vulkan/VKShader.cpp | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/Source/Core/VideoBackends/Vulkan/VKShader.cpp b/Source/Core/VideoBackends/Vulkan/VKShader.cpp index 59128de6b2..ed723235db 100644 --- a/Source/Core/VideoBackends/Vulkan/VKShader.cpp +++ b/Source/Core/VideoBackends/Vulkan/VKShader.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include "Common/Align.h" #include "Common/Assert.h" #include "VideoBackends/Vulkan/ShaderCompiler.h" @@ -103,20 +104,11 @@ std::unique_ptr VKShader::CreateFromSource(ShaderStage stage, const ch std::unique_ptr VKShader::CreateFromBinary(ShaderStage stage, const void* data, size_t length) { - ShaderCompiler::SPIRVCodeVector spv; - const size_t size_in_words = sizeof(length) / sizeof(ShaderCompiler::SPIRVCodeType); - if (size_in_words > 0) - { - spv.resize(length / size_in_words); - std::memcpy(spv.data(), data, size_in_words); - } - - // Non-aligned code sizes, unlikely (unless using VK_NV_glsl). - if ((length % sizeof(ShaderCompiler::SPIRVCodeType)) != 0) - { - spv.resize(size_in_words + 1); - std::memcpy(&spv[size_in_words], data, (length % sizeof(ShaderCompiler::SPIRVCodeType))); - } + const size_t size_in_words = Common::AlignUp(length, sizeof(ShaderCompiler::SPIRVCodeType)) / + sizeof(ShaderCompiler::SPIRVCodeType); + ShaderCompiler::SPIRVCodeVector spv(size_in_words); + if (length > 0) + std::memcpy(spv.data(), data, length); return CreateShaderObject(stage, std::move(spv)); } From dec0c3bce85ceda89ea05f00b94aad00e675598d Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 25 Feb 2018 01:15:35 +1000 Subject: [PATCH 08/16] Move shader caches to VideoCommon --- .../VideoBackends/D3D/GeometryShaderCache.cpp | 134 --- .../VideoBackends/D3D/GeometryShaderCache.h | 24 - .../VideoBackends/D3D/PixelShaderCache.cpp | 325 ------- .../Core/VideoBackends/D3D/PixelShaderCache.h | 61 -- Source/Core/VideoBackends/D3D/Render.cpp | 63 +- Source/Core/VideoBackends/D3D/Render.h | 17 - .../Core/VideoBackends/D3D/VertexManager.cpp | 44 +- .../VideoBackends/D3D/VertexShaderCache.cpp | 339 ------- .../VideoBackends/D3D/VertexShaderCache.h | 77 -- Source/Core/VideoBackends/D3D/main.cpp | 8 +- Source/Core/VideoBackends/Null/CMakeLists.txt | 1 - Source/Core/VideoBackends/Null/Null.vcxproj | 2 - .../Core/VideoBackends/Null/NullBackend.cpp | 13 +- .../Core/VideoBackends/Null/ShaderCache.cpp | 77 -- Source/Core/VideoBackends/Null/ShaderCache.h | 85 -- .../Core/VideoBackends/Null/VertexManager.cpp | 5 - .../VideoBackends/OGL/ProgramShaderCache.cpp | 703 -------------- .../VideoBackends/OGL/ProgramShaderCache.h | 148 --- Source/Core/VideoBackends/OGL/Render.cpp | 19 +- Source/Core/VideoBackends/OGL/Render.h | 3 - .../Core/VideoBackends/OGL/VertexManager.cpp | 8 +- Source/Core/VideoBackends/OGL/main.cpp | 5 +- Source/Core/VideoBackends/Software/SWmain.cpp | 6 +- Source/Core/VideoBackends/Vulkan/Constants.h | 4 +- .../Core/VideoBackends/Vulkan/ObjectCache.cpp | 24 +- Source/Core/VideoBackends/Vulkan/Renderer.cpp | 28 +- Source/Core/VideoBackends/Vulkan/Renderer.h | 4 - .../Core/VideoBackends/Vulkan/ShaderCache.cpp | 527 +--------- .../Core/VideoBackends/Vulkan/ShaderCache.h | 98 +- .../VideoBackends/Vulkan/StateTracker.cpp | 457 +-------- .../Core/VideoBackends/Vulkan/StateTracker.h | 86 +- Source/Core/VideoBackends/Vulkan/Util.cpp | 23 +- .../VideoBackends/Vulkan/VertexManager.cpp | 6 +- Source/Core/VideoBackends/Vulkan/main.cpp | 22 +- Source/Core/VideoCommon/BPFunctions.cpp | 3 + Source/Core/VideoCommon/CMakeLists.txt | 1 + Source/Core/VideoCommon/GeometryShaderGen.h | 1 - Source/Core/VideoCommon/RenderBase.cpp | 22 +- Source/Core/VideoCommon/RenderBase.h | 1 + Source/Core/VideoCommon/ShaderCache.cpp | 913 ++++++++++++++++++ Source/Core/VideoCommon/ShaderCache.h | 216 +++++ Source/Core/VideoCommon/ShaderGenCommon.cpp | 31 +- Source/Core/VideoCommon/ShaderGenCommon.h | 2 +- Source/Core/VideoCommon/VertexManagerBase.cpp | 118 ++- Source/Core/VideoCommon/VertexManagerBase.h | 22 +- Source/Core/VideoCommon/VideoCommon.vcxproj | 4 +- .../VideoCommon/VideoCommon.vcxproj.filters | 6 + Source/Core/VideoCommon/VideoConfig.cpp | 8 + 48 files changed, 1448 insertions(+), 3346 deletions(-) delete mode 100644 Source/Core/VideoBackends/Null/ShaderCache.cpp delete mode 100644 Source/Core/VideoBackends/Null/ShaderCache.h create mode 100644 Source/Core/VideoCommon/ShaderCache.cpp create mode 100644 Source/Core/VideoCommon/ShaderCache.h 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 From f9c829c7f711efadea1db7aabb51c703380fa03a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 25 Feb 2018 17:56:09 +1000 Subject: [PATCH 09/16] OGL: Re-implement async shader compiling --- Source/Core/VideoBackends/D3D/main.cpp | 1 + .../Core/VideoBackends/Null/NullBackend.cpp | 1 + Source/Core/VideoBackends/OGL/OGLPipeline.cpp | 9 +- .../VideoBackends/OGL/ProgramShaderCache.cpp | 100 +++++++++++++++--- .../VideoBackends/OGL/ProgramShaderCache.h | 16 ++- Source/Core/VideoBackends/OGL/Render.cpp | 13 ++- Source/Core/VideoBackends/OGL/Render.h | 2 + Source/Core/VideoBackends/Software/SWmain.cpp | 1 + .../VideoBackends/Vulkan/VulkanContext.cpp | 1 + Source/Core/VideoCommon/DriverDetails.cpp | 2 - Source/Core/VideoCommon/DriverDetails.h | 6 +- Source/Core/VideoCommon/RenderBase.cpp | 5 + Source/Core/VideoCommon/RenderBase.h | 3 + Source/Core/VideoCommon/ShaderCache.cpp | 2 +- Source/Core/VideoCommon/VideoConfig.cpp | 6 +- Source/Core/VideoCommon/VideoConfig.h | 1 + 16 files changed, 136 insertions(+), 33 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/main.cpp b/Source/Core/VideoBackends/D3D/main.cpp index 3c3237402c..fb8c18ae5e 100644 --- a/Source/Core/VideoBackends/D3D/main.cpp +++ b/Source/Core/VideoBackends/D3D/main.cpp @@ -70,6 +70,7 @@ void VideoBackend::InitBackendInfo() g_Config.backend_info.bSupportsDynamicSamplerIndexing = false; g_Config.backend_info.bSupportsBPTCTextures = false; g_Config.backend_info.bSupportsFramebufferFetch = false; + g_Config.backend_info.bSupportsBackgroundCompiling = true; IDXGIFactory2* factory; IDXGIAdapter* ad; diff --git a/Source/Core/VideoBackends/Null/NullBackend.cpp b/Source/Core/VideoBackends/Null/NullBackend.cpp index 6e260f08d5..430da35f84 100644 --- a/Source/Core/VideoBackends/Null/NullBackend.cpp +++ b/Source/Core/VideoBackends/Null/NullBackend.cpp @@ -46,6 +46,7 @@ void VideoBackend::InitBackendInfo() g_Config.backend_info.bSupportsST3CTextures = false; g_Config.backend_info.bSupportsBPTCTextures = false; g_Config.backend_info.bSupportsFramebufferFetch = false; + g_Config.backend_info.bSupportsBackgroundCompiling = false; // aamodes: We only support 1 sample, so no MSAA g_Config.backend_info.Adapters.clear(); diff --git a/Source/Core/VideoBackends/OGL/OGLPipeline.cpp b/Source/Core/VideoBackends/OGL/OGLPipeline.cpp index 9d93381d46..bd439898db 100644 --- a/Source/Core/VideoBackends/OGL/OGLPipeline.cpp +++ b/Source/Core/VideoBackends/OGL/OGLPipeline.cpp @@ -46,10 +46,11 @@ OGLPipeline::~OGLPipeline() std::unique_ptr OGLPipeline::Create(const AbstractPipelineConfig& config) { - const PipelineProgram* program = - ProgramShaderCache::GetPipelineProgram(static_cast(config.vertex_shader), - static_cast(config.geometry_shader), - static_cast(config.pixel_shader)); + const PipelineProgram* program = ProgramShaderCache::GetPipelineProgram( + static_cast(config.vertex_format), + static_cast(config.vertex_shader), + static_cast(config.geometry_shader), + static_cast(config.pixel_shader)); if (!program) return nullptr; diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp index d953996396..60b7da82b9 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp @@ -51,9 +51,10 @@ static std::unique_ptr s_buffer; static int num_failures = 0; static GLuint CurrentProgram = 0; -ProgramShaderCache::PipelineProgramMap ProgramShaderCache::pipelineprograms; -std::mutex ProgramShaderCache::pipelineprogramlock; +ProgramShaderCache::PipelineProgramMap ProgramShaderCache::s_pipeline_programs; +std::mutex ProgramShaderCache::s_pipeline_program_lock; static std::string s_glsl_header = ""; +static thread_local bool s_is_shared_context = false; static std::string GetGLSLVersionString() { @@ -506,8 +507,8 @@ void ProgramShaderCache::Shutdown() s_last_VAO = 0; // All pipeline programs should have been released. - _dbg_assert_(VIDEO, pipelineprograms.empty()); - pipelineprograms.clear(); + _dbg_assert_(VIDEO, s_pipeline_programs.empty()); + s_pipeline_programs.clear(); } void ProgramShaderCache::CreateAttributelessVAO() @@ -548,21 +549,28 @@ void ProgramShaderCache::InvalidateLastProgram() CurrentProgram = 0; } -const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* vertex_shader, +const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const GLVertexFormat* vertex_format, + const OGLShader* vertex_shader, const OGLShader* geometry_shader, const OGLShader* pixel_shader) { PipelineProgramKey key = {vertex_shader, geometry_shader, pixel_shader}; { - std::lock_guard guard(pipelineprogramlock); - auto iter = pipelineprograms.find(key); - if (iter != pipelineprograms.end()) + std::lock_guard guard(s_pipeline_program_lock); + auto iter = s_pipeline_programs.find(key); + if (iter != s_pipeline_programs.end()) { iter->second->reference_count++; return iter->second.get(); } } + // We temporarily change the vertex array to the pipeline's vertex format. + // This can prevent the NVIDIA OpenGL driver from recompiling on first use. + GLuint vao = vertex_format ? vertex_format->VAO : s_attributeless_VAO; + if (s_is_shared_context || vao != s_last_VAO) + glBindVertexArray(vao); + std::unique_ptr prog = std::make_unique(); prog->key = key; @@ -581,6 +589,11 @@ const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* v // Link program. prog->shader.SetProgramBindings(false); glLinkProgram(prog->shader.glprogid); + + // Restore VAO binding after linking. + if (!s_is_shared_context && vao != s_last_VAO) + glBindVertexArray(s_last_VAO); + if (!ProgramShaderCache::CheckProgramLinkResult(prog->shader.glprogid, {}, {}, {})) { prog->shader.Destroy(); @@ -588,9 +601,9 @@ const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* v } // Lock to insert. A duplicate program may have been created in the meantime. - std::lock_guard guard(pipelineprogramlock); - auto iter = pipelineprograms.find(key); - if (iter != pipelineprograms.end()) + std::lock_guard guard(s_pipeline_program_lock); + auto iter = s_pipeline_programs.find(key); + if (iter != s_pipeline_programs.end()) { // Destroy this program, and use the one which was created first. prog->shader.Destroy(); @@ -601,19 +614,25 @@ const PipelineProgram* ProgramShaderCache::GetPipelineProgram(const OGLShader* v // Set program variables on the shader which will be returned. // This is only needed for drivers which don't support binding layout. prog->shader.SetProgramVariables(); - auto ip = pipelineprograms.emplace(key, std::move(prog)); + + // If this is a shared context, ensure we sync before we return the program to + // the main thread. If we don't do this, some driver can lock up (e.g. AMD). + if (s_is_shared_context) + glFinish(); + + auto ip = s_pipeline_programs.emplace(key, std::move(prog)); return ip.first->second.get(); } void ProgramShaderCache::ReleasePipelineProgram(const PipelineProgram* prog) { - auto iter = pipelineprograms.find(prog->key); - _assert_(iter != pipelineprograms.end() && prog == iter->second.get()); + auto iter = s_pipeline_programs.find(prog->key); + _assert_(iter != s_pipeline_programs.end() && prog == iter->second.get()); if (--iter->second->reference_count == 0) { iter->second->shader.Destroy(); - pipelineprograms.erase(iter); + s_pipeline_programs.erase(iter); } } @@ -783,4 +802,55 @@ void ProgramShaderCache::CreateHeader() v > GlslEs300 ? "precision highp sampler2DMS;" : "", v >= GlslEs310 ? "precision highp image2DArray;" : ""); } + +bool SharedContextAsyncShaderCompiler::WorkerThreadInitMainThread(void** param) +{ + std::unique_ptr context = GLInterface->CreateSharedContext(); + if (!context) + { + PanicAlert("Failed to create shared context for shader compiling."); + return false; + } + + *param = context.release(); + return true; +} + +bool SharedContextAsyncShaderCompiler::WorkerThreadInitWorkerThread(void* param) +{ + cInterfaceBase* context = static_cast(param); + if (!context->MakeCurrent()) + return false; + + s_is_shared_context = true; + 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); + } + } + } + + return true; +} + +void SharedContextAsyncShaderCompiler::WorkerThreadExit(void* param) +{ + cInterfaceBase* context = static_cast(param); + context->ClearCurrent(); + delete context; +} } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h index af2aa10383..0096c6e5b9 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.h +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.h @@ -11,6 +11,7 @@ #include #include "Common/GL/GLUtil.h" +#include "VideoCommon/AsyncShaderCompiler.h" namespace OGL { @@ -87,7 +88,8 @@ public: static void Shutdown(); static void CreateHeader(); - static const PipelineProgram* GetPipelineProgram(const OGLShader* vertex_shader, + static const PipelineProgram* GetPipelineProgram(const GLVertexFormat* vertex_format, + const OGLShader* vertex_shader, const OGLShader* geometry_shader, const OGLShader* pixel_shader); static void ReleasePipelineProgram(const PipelineProgram* prog); @@ -99,8 +101,8 @@ private: static void CreateAttributelessVAO(); - static PipelineProgramMap pipelineprograms; - static std::mutex pipelineprogramlock; + static PipelineProgramMap s_pipeline_programs; + static std::mutex s_pipeline_program_lock; static u32 s_ubo_buffer_size; static s32 s_ubo_align; @@ -110,4 +112,12 @@ private: static GLuint s_last_VAO; }; +class SharedContextAsyncShaderCompiler : public VideoCommon::AsyncShaderCompiler +{ +protected: + bool WorkerThreadInitMainThread(void** param) override; + bool WorkerThreadInitWorkerThread(void* param) override; + void WorkerThreadExit(void* param) override; +}; + } // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index ac08ae57cd..5319e41cbd 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -81,8 +81,8 @@ static bool s_efbCacheIsCleared = false; static std::vector s_efbCache[2][EFB_CACHE_WIDTH * EFB_CACHE_HEIGHT]; // 2 for PeekZ and PeekColor -static void APIENTRY ErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, - GLsizei length, const char* message, const void* userParam) +void APIENTRY ErrorCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, + const char* message, const void* userParam) { const char* s_source; const char* s_type; @@ -677,6 +677,10 @@ Renderer::Renderer() g_Config.backend_info.bSupportsPaletteConversion && g_Config.backend_info.bSupportsComputeShaders && g_ogl_config.bSupportsImageLoadStore; + // Background compiling is supported only when shared contexts aren't broken. + g_Config.backend_info.bSupportsBackgroundCompiling = + !DriverDetails::HasBug(DriverDetails::BUG_SHARED_CONTEXT_SHADER_COMPILATION); + if (g_ogl_config.bSupportsDebug) { if (GLExtensions::Supports("GL_KHR_debug")) @@ -1695,4 +1699,9 @@ void Renderer::DispatchComputeShader(const AbstractShader* shader, const void* u glDispatchCompute(groups_x, groups_y, groups_z); ProgramShaderCache::InvalidateLastProgram(); } + +std::unique_ptr Renderer::CreateAsyncShaderCompiler() +{ + return std::make_unique(); +} } diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index a9124ad4fb..3f1277a0c2 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -139,6 +139,8 @@ public: void DispatchComputeShader(const AbstractShader* shader, const void* uniforms, u32 uniforms_size, u32 groups_x, u32 groups_y, u32 groups_z) override; + std::unique_ptr CreateAsyncShaderCompiler() override; + private: void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc, const TargetRectangle& targetPixelRc, const void* data); diff --git a/Source/Core/VideoBackends/Software/SWmain.cpp b/Source/Core/VideoBackends/Software/SWmain.cpp index 504c008511..8765a28e4c 100644 --- a/Source/Core/VideoBackends/Software/SWmain.cpp +++ b/Source/Core/VideoBackends/Software/SWmain.cpp @@ -73,6 +73,7 @@ void VideoSoftware::InitBackendInfo() g_Config.backend_info.bSupportsBPTCTextures = false; g_Config.backend_info.bSupportsCopyToVram = false; g_Config.backend_info.bSupportsFramebufferFetch = false; + g_Config.backend_info.bSupportsBackgroundCompiling = false; // aamodes g_Config.backend_info.AAModes = {1}; diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index 77a7a8ffe1..f9c254a04d 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -235,6 +235,7 @@ void VulkanContext::PopulateBackendInfo(VideoConfig* config) config->backend_info.bSupportsBitfield = true; // Assumed support. config->backend_info.bSupportsDynamicSamplerIndexing = true; // Assumed support. config->backend_info.bSupportsPostProcessing = true; // Assumed support. + config->backend_info.bSupportsBackgroundCompiling = true; // Assumed support. config->backend_info.bSupportsDualSourceBlend = false; // Dependent on features. config->backend_info.bSupportsGeometryShaders = false; // Dependent on features. config->backend_info.bSupportsGSInstancing = false; // Dependent on features. diff --git a/Source/Core/VideoCommon/DriverDetails.cpp b/Source/Core/VideoCommon/DriverDetails.cpp index d280a6a9b4..8fd990395d 100644 --- a/Source/Core/VideoCommon/DriverDetails.cpp +++ b/Source/Core/VideoCommon/DriverDetails.cpp @@ -102,8 +102,6 @@ static BugInfo m_known_bugs[] = { true}, {API_OPENGL, OS_LINUX, VENDOR_MESA, DRIVER_I965, Family::UNKNOWN, BUG_SHARED_CONTEXT_SHADER_COMPILATION, -1.0, -1.0, true}, - {API_OPENGL, OS_LINUX, VENDOR_MESA, DRIVER_NOUVEAU, Family::UNKNOWN, - BUG_SHARED_CONTEXT_SHADER_COMPILATION, -1.0, -1.0, true}, {API_VULKAN, OS_ALL, VENDOR_NVIDIA, DRIVER_NVIDIA, Family::UNKNOWN, BUG_BROKEN_MSAA_CLEAR, -1.0, -1.0, true}, {API_VULKAN, OS_ALL, VENDOR_IMGTEC, DRIVER_IMGTEC, Family::UNKNOWN, diff --git a/Source/Core/VideoCommon/DriverDetails.h b/Source/Core/VideoCommon/DriverDetails.h index df06a77d92..bc96ee2aab 100644 --- a/Source/Core/VideoCommon/DriverDetails.h +++ b/Source/Core/VideoCommon/DriverDetails.h @@ -252,8 +252,10 @@ enum Bug // the negated value to a temporary variable then using that in the bitwise op. BUG_BROKEN_BITWISE_OP_NEGATION, - // Bug: Shaders are recompiled on the main thread after being previously compiled on - // a worker thread on Mesa i965. + // BUG: 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. For now, disable + // asynchronous compilation on Mesa i965. // Started version: -1 // Ended Version: -1 BUG_SHARED_CONTEXT_SHADER_COMPILATION, diff --git a/Source/Core/VideoCommon/RenderBase.cpp b/Source/Core/VideoCommon/RenderBase.cpp index 4c03168d20..a5e5272651 100644 --- a/Source/Core/VideoCommon/RenderBase.cpp +++ b/Source/Core/VideoCommon/RenderBase.cpp @@ -1027,3 +1027,8 @@ bool Renderer::UseVertexDepthRange() const // in the vertex shader. return fabs(xfmem.viewport.zRange) > 16777215.0f || fabs(xfmem.viewport.farZ) > 16777215.0f; } + +std::unique_ptr Renderer::CreateAsyncShaderCompiler() +{ + return std::make_unique(); +} diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index da8e0b819f..b906cd2558 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -28,6 +28,7 @@ #include "Common/Flag.h" #include "Common/MathUtil.h" #include "VideoCommon/AVIDump.h" +#include "VideoCommon/AsyncShaderCompiler.h" #include "VideoCommon/BPMemory.h" #include "VideoCommon/FPSCounter.h" #include "VideoCommon/RenderState.h" @@ -189,6 +190,8 @@ public: void ResizeSurface(int new_width, int new_height); bool UseVertexDepthRange() const; + virtual std::unique_ptr CreateAsyncShaderCompiler(); + virtual void Shutdown(); // Drawing utility shaders. diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index 05624e9fe4..1e4a0ef741 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -27,7 +27,7 @@ bool ShaderCache::Initialize() 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 = g_renderer->CreateAsyncShaderCompiler(); m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderPrecompilerThreads()); // Load shader and UID caches. diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index 771adf60bf..d8c0dfaa6c 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -187,8 +187,7 @@ 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) + if (!backend_info.bSupportsBackgroundCompiling) return 0; if (iShaderCompilerThreads >= 0) @@ -199,8 +198,7 @@ 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) + if (!backend_info.bSupportsBackgroundCompiling) return 0; if (iShaderPrecompilerThreads >= 0) diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index fb05408303..7839cb0e8f 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -227,6 +227,7 @@ struct VideoConfig final bool bSupportsDynamicSamplerIndexing; // Needed by UberShaders, so must stay in VideoCommon bool bSupportsBPTCTextures; bool bSupportsFramebufferFetch; // Used as an alternative to dual-source blend on GLES + bool bSupportsBackgroundCompiling; } backend_info; // Utility From fc1fe0672bd4179a9af3ebc55b180e19cefcd65a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 26 Feb 2018 21:01:21 +1000 Subject: [PATCH 10/16] OGL: Add some basic state tracking We would want to improve the granularity here in the future, but for now, this should avoid any performance loss from switching to the VideoCommon shader cache. --- Source/Core/VideoBackends/OGL/Render.cpp | 27 ++++++++++++++++++------ Source/Core/VideoBackends/OGL/Render.h | 9 +++++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index 5319e41cbd..945dbadcf1 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -1290,8 +1290,11 @@ void Renderer::SetAndClearFramebuffer(const AbstractFramebuffer* framebuffer, glClear(clear_mask); } -void Renderer::ApplyBlendingState(const BlendingState& state) +void Renderer::ApplyBlendingState(const BlendingState state, bool force) { + if (!force && m_current_blend_state == state) + return; + bool useDualSource = state.usedualsrc && g_ActiveConfig.backend_info.bSupportsDualSourceBlend && (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DUAL_SOURCE_BLENDING) || state.dstalpha); @@ -1364,6 +1367,7 @@ void Renderer::ApplyBlendingState(const BlendingState& state) } glColorMask(state.colorupdate, state.colorupdate, state.colorupdate, state.alphaupdate); + m_current_blend_state = state; } // This function has the final picture. We adjust the aspect ratio here. @@ -1561,15 +1565,19 @@ void Renderer::RestoreAPIState() glEnable(GL_CLIP_DISTANCE0); glEnable(GL_CLIP_DISTANCE1); } - BPFunctions::SetGenerationMode(); BPFunctions::SetScissor(); BPFunctions::SetViewport(); - BPFunctions::SetDepthMode(); - BPFunctions::SetBlendMode(); + + ApplyRasterizationState(m_current_rasterization_state, true); + ApplyDepthState(m_current_depth_state, true); + ApplyBlendingState(m_current_blend_state, true); } -void Renderer::ApplyRasterizationState(const RasterizationState& state) +void Renderer::ApplyRasterizationState(const RasterizationState state, bool force) { + if (!force && m_current_rasterization_state == state) + return; + // none, ccw, cw, ccw if (state.cullmode != GenMode::CULL_NONE) { @@ -1581,10 +1589,15 @@ void Renderer::ApplyRasterizationState(const RasterizationState& state) { glDisable(GL_CULL_FACE); } + + m_current_rasterization_state = state; } -void Renderer::ApplyDepthState(const DepthState& state) +void Renderer::ApplyDepthState(const DepthState state, bool force) { + if (!force && m_current_depth_state == state) + return; + const GLenum glCmpFuncs[8] = {GL_NEVER, GL_LESS, GL_EQUAL, GL_LEQUAL, GL_GREATER, GL_NOTEQUAL, GL_GEQUAL, GL_ALWAYS}; @@ -1602,6 +1615,8 @@ void Renderer::ApplyDepthState(const DepthState& state) glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); } + + m_current_depth_state = state; } void Renderer::SetPipeline(const AbstractPipeline* pipeline) diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index 3f1277a0c2..d8882e2111 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -154,12 +154,15 @@ private: void CheckForSurfaceChange(); void CheckForSurfaceResize(); - void ApplyBlendingState(const BlendingState& state); - void ApplyRasterizationState(const RasterizationState& state); - void ApplyDepthState(const DepthState& state); + void ApplyBlendingState(const BlendingState state, bool force = false); + void ApplyRasterizationState(const RasterizationState state, bool force = false); + void ApplyDepthState(const DepthState state, bool force = false); void UploadUtilityUniforms(const void* uniforms, u32 uniforms_size); std::array m_bound_textures{}; const OGLPipeline* m_graphics_pipeline = nullptr; + RasterizationState m_current_rasterization_state = {}; + DepthState m_current_depth_state = {}; + BlendingState m_current_blend_state = {}; }; } From 41296db0835b210c988d540677dbdfa0acc553c9 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Mon, 26 Feb 2018 21:09:33 +1000 Subject: [PATCH 11/16] Renderer: Remove now-redundant Set{Rasterization,Depth,Blending}State --- Source/Core/VideoCommon/BPFunctions.cpp | 9 --------- Source/Core/VideoCommon/RenderBase.h | 3 --- Source/Core/VideoCommon/VertexManagerBase.cpp | 3 --- 3 files changed, 15 deletions(-) diff --git a/Source/Core/VideoCommon/BPFunctions.cpp b/Source/Core/VideoCommon/BPFunctions.cpp index 1afcb35040..61e4c54b27 100644 --- a/Source/Core/VideoCommon/BPFunctions.cpp +++ b/Source/Core/VideoCommon/BPFunctions.cpp @@ -28,9 +28,6 @@ void FlushPipeline() void SetGenerationMode() { - RasterizationState state = {}; - state.Generate(bpmem, g_vertex_manager->GetCurrentPrimitiveType()); - g_renderer->SetRasterizationState(state); g_vertex_manager->SetRasterizationStateChanged(); } @@ -130,17 +127,11 @@ void SetViewport() void SetDepthMode() { - DepthState state = {}; - state.Generate(bpmem); - g_renderer->SetDepthState(state); g_vertex_manager->SetDepthStateChanged(); } void SetBlendMode() { - BlendingState state = {}; - state.Generate(bpmem); - g_renderer->SetBlendingState(state); g_vertex_manager->SetBlendingStateChanged(); } diff --git a/Source/Core/VideoCommon/RenderBase.h b/Source/Core/VideoCommon/RenderBase.h index b906cd2558..e7f7668f22 100644 --- a/Source/Core/VideoCommon/RenderBase.h +++ b/Source/Core/VideoCommon/RenderBase.h @@ -79,10 +79,7 @@ public: }; virtual void SetPipeline(const AbstractPipeline* pipeline) {} - virtual void SetBlendingState(const BlendingState& state) {} virtual void SetScissorRect(const MathUtil::Rectangle& rc) {} - virtual void SetRasterizationState(const RasterizationState& state) {} - virtual void SetDepthState(const DepthState& state) {} virtual void SetTexture(u32 index, const AbstractTexture* texture) {} virtual void SetSamplerState(u32 index, const SamplerState& state) {} virtual void UnbindTexture(const AbstractTexture* texture) {} diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index 8ccf9865e5..f32ac72b0a 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -106,9 +106,6 @@ DataReader VertexManagerBase::PrepareForAdditionalData(int primitive, u32 count, // 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); SetRasterizationStateChanged(); } From 590307b94c5865c0a75a18e9a593d251ce38407a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 27 Feb 2018 19:38:00 +1000 Subject: [PATCH 12/16] ShaderCache: Use memcmp for comparing pipeline UIDs As these are stored in a map, operator< will become a hot function when doing lookups, which happen every frame. std::tie generated a rather large function here with quite a few branches. --- Source/Core/VideoCommon/ShaderCache.h | 38 ++++++++++++--------- Source/Core/VideoCommon/VertexManagerBase.h | 4 +-- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/Source/Core/VideoCommon/ShaderCache.h b/Source/Core/VideoCommon/ShaderCache.h index 922cd4d5fa..6516edd8d8 100644 --- a/Source/Core/VideoCommon/ShaderCache.h +++ b/Source/Core/VideoCommon/ShaderCache.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -42,19 +43,23 @@ struct GXPipelineConfig DepthState depth_state; BlendingState blending_state; + // We use memcmp() for comparing pipelines as std::tie generates a large number of instructions, + // and this map lookup can happen every draw call. However, as using memcmp() will also compare + // any padding bytes, we have to ensure these are zeroed out. + GXPipelineConfig() { std::memset(this, 0, sizeof(*this)); } + GXPipelineConfig(const GXPipelineConfig& rhs) { std::memcpy(this, &rhs, sizeof(*this)); } + GXPipelineConfig& operator=(const GXPipelineConfig& rhs) + { + std::memcpy(this, &rhs, sizeof(*this)); + return *this; + } 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); + return std::memcmp(this, &rhs, sizeof(*this)) < 0; } 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); + return std::memcmp(this, &rhs, sizeof(*this)) == 0; } bool operator!=(const GXPipelineConfig& rhs) const { return !operator==(rhs); } }; @@ -68,19 +73,20 @@ struct GXUberPipelineConfig DepthState depth_state; BlendingState blending_state; + GXUberPipelineConfig() { std::memset(this, 0, sizeof(*this)); } + GXUberPipelineConfig(const GXUberPipelineConfig& rhs) { std::memcpy(this, &rhs, sizeof(*this)); } + GXUberPipelineConfig& operator=(const GXUberPipelineConfig& rhs) + { + std::memcpy(this, &rhs, sizeof(*this)); + return *this; + } 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); + return std::memcmp(this, &rhs, sizeof(*this)) < 0; } 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); + return std::memcmp(this, &rhs, sizeof(*this)) == 0; } bool operator!=(const GXUberPipelineConfig& rhs) const { return !operator==(rhs); } }; diff --git a/Source/Core/VideoCommon/VertexManagerBase.h b/Source/Core/VideoCommon/VertexManagerBase.h index 20a63a3e2a..eac95f614f 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.h +++ b/Source/Core/VideoCommon/VertexManagerBase.h @@ -83,8 +83,8 @@ protected: Slope m_zslope = {}; void CalculateZSlope(NativeVertexFormat* format); - VideoCommon::GXPipelineConfig m_current_pipeline_config = {}; - VideoCommon::GXUberPipelineConfig m_current_uber_pipeline_config = {}; + 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; From 9fa24700b69b98b2569f4bd1353482cbfb1ec2d7 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 1 Mar 2018 18:03:24 +1000 Subject: [PATCH 13/16] VideoConfig: Collapse ubershader configuration fields to a single value --- Source/Core/Core/Analytics.cpp | 13 +++++--- Source/Core/Core/Config/GraphicsSettings.cpp | 8 ++--- Source/Core/Core/Config/GraphicsSettings.h | 4 +-- .../Core/ConfigLoaders/IsSettingSaveable.cpp | 4 +-- .../Config/Graphics/EnhancementsWidget.cpp | 19 ++---------- Source/Core/DolphinWX/VideoConfigDiag.cpp | 29 +++-------------- Source/Core/DolphinWX/VideoConfigDiag.h | 1 - Source/Core/VideoCommon/ShaderCache.cpp | 2 +- Source/Core/VideoCommon/VertexManagerBase.cpp | 27 +++++++--------- Source/Core/VideoCommon/VideoConfig.cpp | 16 +--------- Source/Core/VideoCommon/VideoConfig.h | 31 ++++++------------- 11 files changed, 42 insertions(+), 112 deletions(-) diff --git a/Source/Core/Core/Analytics.cpp b/Source/Core/Core/Analytics.cpp index 39f62b3f13..db881d61de 100644 --- a/Source/Core/Core/Analytics.cpp +++ b/Source/Core/Core/Analytics.cpp @@ -180,13 +180,16 @@ void DolphinAnalytics::MakeBaseBuilder() static const char* GetUbershaderMode(const VideoConfig& video_config) { - if (video_config.bDisableSpecializedShaders) + switch (video_config.iUberShaderMode) + { + case UberShaderMode::Exclusive: return "exclusive"; - - if (video_config.bBackgroundShaderCompiling) + case UberShaderMode::Hybrid: return "hybrid"; - - return "disabled"; + case UberShaderMode::Disabled: + default: + return "disabled"; + } } void DolphinAnalytics::MakePerGameBuilder() diff --git a/Source/Core/Core/Config/GraphicsSettings.cpp b/Source/Core/Core/Config/GraphicsSettings.cpp index 5f8d4c102a..b06ccdaf41 100644 --- a/Source/Core/Core/Config/GraphicsSettings.cpp +++ b/Source/Core/Core/Config/GraphicsSettings.cpp @@ -76,12 +76,8 @@ const ConfigInfo GFX_BACKEND_MULTITHREADING{ const ConfigInfo GFX_COMMAND_BUFFER_EXECUTE_INTERVAL{ {System::GFX, "Settings", "CommandBufferExecuteInterval"}, 100}; const ConfigInfo GFX_SHADER_CACHE{{System::GFX, "Settings", "ShaderCache"}, true}; -const ConfigInfo GFX_BACKGROUND_SHADER_COMPILING{ - {System::GFX, "Settings", "BackgroundShaderCompiling"}, false}; -const ConfigInfo GFX_DISABLE_SPECIALIZED_SHADERS{ - {System::GFX, "Settings", "DisableSpecializedShaders"}, false}; -const ConfigInfo GFX_PRECOMPILE_UBER_SHADERS{ - {System::GFX, "Settings", "PrecompileUberShaders"}, true}; +const ConfigInfo GFX_UBERSHADER_MODE{{System::GFX, "Settings", "UberShaderMode"}, + static_cast(UberShaderMode::Disabled)}; const ConfigInfo GFX_SHADER_COMPILER_THREADS{ {System::GFX, "Settings", "ShaderCompilerThreads"}, 1}; const ConfigInfo GFX_SHADER_PRECOMPILER_THREADS{ diff --git a/Source/Core/Core/Config/GraphicsSettings.h b/Source/Core/Core/Config/GraphicsSettings.h index 70613bee00..5f07d1906a 100644 --- a/Source/Core/Core/Config/GraphicsSettings.h +++ b/Source/Core/Core/Config/GraphicsSettings.h @@ -59,9 +59,7 @@ extern const ConfigInfo GFX_ENABLE_VALIDATION_LAYER; extern const ConfigInfo GFX_BACKEND_MULTITHREADING; extern const ConfigInfo GFX_COMMAND_BUFFER_EXECUTE_INTERVAL; extern const ConfigInfo GFX_SHADER_CACHE; -extern const ConfigInfo GFX_BACKGROUND_SHADER_COMPILING; -extern const ConfigInfo GFX_DISABLE_SPECIALIZED_SHADERS; -extern const ConfigInfo GFX_PRECOMPILE_UBER_SHADERS; +extern const ConfigInfo GFX_UBERSHADER_MODE; extern const ConfigInfo GFX_SHADER_COMPILER_THREADS; extern const ConfigInfo GFX_SHADER_PRECOMPILER_THREADS; diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp index d625ba3dce..abc30b24d3 100644 --- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp +++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp @@ -46,9 +46,7 @@ bool IsSettingSaveable(const Config::ConfigLocation& config_location) Config::GFX_DISABLE_FOG.location, Config::GFX_BORDERLESS_FULLSCREEN.location, Config::GFX_ENABLE_VALIDATION_LAYER.location, Config::GFX_BACKEND_MULTITHREADING.location, Config::GFX_COMMAND_BUFFER_EXECUTE_INTERVAL.location, Config::GFX_SHADER_CACHE.location, - Config::GFX_BACKGROUND_SHADER_COMPILING.location, - Config::GFX_DISABLE_SPECIALIZED_SHADERS.location, - Config::GFX_PRECOMPILE_UBER_SHADERS.location, Config::GFX_SHADER_COMPILER_THREADS.location, + Config::GFX_UBERSHADER_MODE.location, Config::GFX_SHADER_COMPILER_THREADS.location, Config::GFX_SHADER_PRECOMPILER_THREADS.location, Config::GFX_SW_ZCOMPLOC.location, Config::GFX_SW_ZFREEZE.location, diff --git a/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp b/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp index 8182e65d85..b6baa9819f 100644 --- a/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp +++ b/Source/Core/DolphinQt2/Config/Graphics/EnhancementsWidget.cpp @@ -63,9 +63,8 @@ void EnhancementsWidget::CreateWidgets() m_af_combo = new GraphicsChoice({tr("1x"), tr("2x"), tr("4x"), tr("8x"), tr("16x")}, Config::GFX_ENHANCE_MAX_ANISOTROPY); - m_ubershader_combo = new QComboBox; - for (const auto& option : {tr("Disabled"), tr("Hybrid"), tr("Exclusive")}) - m_ubershader_combo->addItem(option); + m_ubershader_combo = new GraphicsChoice({tr("Disabled"), tr("Hybrid"), tr("Exclusive")}, + Config::GFX_UBERSHADER_MODE); m_pp_effect = new QComboBox(); m_configure_pp_effect = new QPushButton(tr("Configure")); @@ -131,9 +130,6 @@ void EnhancementsWidget::ConnectWidgets() { connect(m_aa_combo, static_cast(&QComboBox::currentIndexChanged), [this](int) { SaveSettings(); }); - connect(m_ubershader_combo, - static_cast(&QComboBox::currentIndexChanged), - [this](int) { SaveSettings(); }); connect(m_pp_effect, static_cast(&QComboBox::currentIndexChanged), [this](int) { SaveSettings(); }); connect(m_3d_mode, static_cast(&QComboBox::currentIndexChanged), @@ -156,13 +152,6 @@ void EnhancementsWidget::LoadSettings() QString::fromStdString(std::to_string(aa_selection) + "x " + (ssaa ? "SSAA" : "MSAA"))); m_aa_combo->setEnabled(m_aa_combo->count() > 1); - if (Config::GetBase(Config::GFX_DISABLE_SPECIALIZED_SHADERS)) - m_ubershader_combo->setCurrentIndex(2); - else if (Config::GetBase(Config::GFX_BACKGROUND_SHADER_COMPILING)) - m_ubershader_combo->setCurrentIndex(1); - else - m_ubershader_combo->setCurrentIndex(0); - // Post Processing Shader std::vector shaders = g_Config.stereo_mode == StereoMode::Anaglyph ? @@ -220,10 +209,6 @@ void EnhancementsWidget::SaveSettings() Config::SetBaseOrCurrent(Config::GFX_SSAA, is_ssaa); - int us_value = m_ubershader_combo->currentIndex(); - Config::SetBaseOrCurrent(Config::GFX_BACKGROUND_SHADER_COMPILING, us_value == 1); - Config::SetBaseOrCurrent(Config::GFX_DISABLE_SPECIALIZED_SHADERS, us_value == 2); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER, m_pp_effect->currentText().toStdString()); diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp index a4dfebfbed..874508459e 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.cpp +++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp @@ -534,24 +534,13 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) // ubershaders { const std::array mode_choices = {{_("Disabled"), _("Hybrid"), _("Exclusive")}}; - - wxChoice* const choice_mode = - new wxChoice(page_enh, wxID_ANY, wxDefaultPosition, wxDefaultSize, - static_cast(mode_choices.size()), mode_choices.data()); - RegisterControl(choice_mode, wxGetTranslation(ubershader_desc)); szr_enh->Add(new wxStaticText(page_enh, wxID_ANY, _("Ubershaders:")), wxGBPosition(row, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL); - szr_enh->Add(choice_mode, wxGBPosition(row, 1), span2, wxALIGN_CENTER_VERTICAL); + szr_enh->Add(CreateChoice(page_enh, Config::GFX_UBERSHADER_MODE, + wxGetTranslation(ubershader_desc), mode_choices.size(), + mode_choices.data()), + wxGBPosition(row, 1), span2, wxALIGN_CENTER_VERTICAL); row += 1; - - // Determine ubershader mode - choice_mode->Bind(wxEVT_CHOICE, &VideoConfigDiag::OnUberShaderModeChanged, this); - if (Config::GetBase(Config::GFX_DISABLE_SPECIALIZED_SHADERS)) - choice_mode->SetSelection(2); - else if (Config::GetBase(Config::GFX_BACKGROUND_SHADER_COMPILING)) - choice_mode->SetSelection(1); - else - choice_mode->SetSelection(0); } // postproc shader @@ -1290,13 +1279,3 @@ void VideoConfigDiag::OnAAChanged(wxCommandEvent& ev) Config::SetBaseOrCurrent(Config::GFX_MSAA, vconfig.backend_info.AAModes[mode]); } - -void VideoConfigDiag::OnUberShaderModeChanged(wxCommandEvent& ev) -{ - // 0: No ubershaders - // 1: Hybrid ubershaders - // 2: Only ubershaders - int mode = ev.GetInt(); - Config::SetBaseOrCurrent(Config::GFX_BACKGROUND_SHADER_COMPILING, mode == 1); - Config::SetBaseOrCurrent(Config::GFX_DISABLE_SPECIALIZED_SHADERS, mode == 2); -} diff --git a/Source/Core/DolphinWX/VideoConfigDiag.h b/Source/Core/DolphinWX/VideoConfigDiag.h index d02d344325..a15472885a 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.h +++ b/Source/Core/DolphinWX/VideoConfigDiag.h @@ -139,7 +139,6 @@ protected: void PopulatePostProcessingShaders(); void PopulateAAList(); void OnAAChanged(wxCommandEvent& ev); - void OnUberShaderModeChanged(wxCommandEvent& ev); wxChoice* choice_backend; wxChoice* choice_adapter; diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index 1e4a0ef741..43a31dfd6b 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -38,7 +38,7 @@ bool ShaderCache::Initialize() } // Queue ubershader precompiling if required. - if (g_ActiveConfig.CanPrecompileUberShaders()) + if (g_ActiveConfig.UsingUberShaders()) PrecompileUberShaders(); // Compile all known UIDs. diff --git a/Source/Core/VideoCommon/VertexManagerBase.cpp b/Source/Core/VideoCommon/VertexManagerBase.cpp index f32ac72b0a..b54b8060b9 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.cpp +++ b/Source/Core/VideoCommon/VertexManagerBase.cpp @@ -566,27 +566,24 @@ void VertexManagerBase::UpdatePipelineObject() m_current_pipeline_object = nullptr; m_pipeline_config_changed = false; - // Try for specialized shaders. - if (!g_ActiveConfig.bDisableSpecializedShaders) + if (g_ActiveConfig.iUberShaderMode == UberShaderMode::Disabled) + { + // Ubershaders disabled? Block and compile the specialized shader. + m_current_pipeline_object = g_shader_cache->GetPipelineForUid(m_current_pipeline_config); + return; + } + else if (g_ActiveConfig.iUberShaderMode == UberShaderMode::Hybrid) { // 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) { - 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); + // Specialized shaders are ready, prefer these. + m_current_pipeline_object = *res; return; } } - // Fallback to ubershaders. + // Exclusive ubershader mode, or hybrid and shaders are still compiling. m_current_pipeline_object = g_shader_cache->GetUberPipelineForUid(m_current_uber_pipeline_config); } diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index d8c0dfaa6c..c14a00757a 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -102,9 +102,7 @@ void VideoConfig::Refresh() bBackendMultithreading = Config::Get(Config::GFX_BACKEND_MULTITHREADING); iCommandBufferExecuteInterval = Config::Get(Config::GFX_COMMAND_BUFFER_EXECUTE_INTERVAL); bShaderCache = Config::Get(Config::GFX_SHADER_CACHE); - bBackgroundShaderCompiling = Config::Get(Config::GFX_BACKGROUND_SHADER_COMPILING); - bDisableSpecializedShaders = Config::Get(Config::GFX_DISABLE_SPECIALIZED_SHADERS); - bPrecompileUberShaders = Config::Get(Config::GFX_PRECOMPILE_UBER_SHADERS); + iUberShaderMode = static_cast(Config::Get(Config::GFX_UBERSHADER_MODE)); iShaderCompilerThreads = Config::Get(Config::GFX_SHADER_COMPILER_THREADS); iShaderPrecompilerThreads = Config::Get(Config::GFX_SHADER_PRECOMPILER_THREADS); @@ -206,15 +204,3 @@ u32 VideoConfig::GetShaderPrecompilerThreads() const else return GetNumAutoShaderCompilerThreads(); } - -bool VideoConfig::CanPrecompileUberShaders() const -{ - // We don't want to precompile ubershaders if they're never going to be used. - return bPrecompileUberShaders && (bBackgroundShaderCompiling || bDisableSpecializedShaders); -} - -bool VideoConfig::CanBackgroundCompileShaders() const -{ - // We require precompiled ubershaders to background compile shaders. - return bBackgroundShaderCompiling && bPrecompileUberShaders; -} diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index 7839cb0e8f..2b69919528 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -42,6 +42,13 @@ enum class StereoMode : int Nvidia3DVision }; +enum class UberShaderMode : int +{ + Disabled, + Hybrid, + Exclusive +}; + struct ProjectionHackConfig final { bool m_enable; @@ -161,25 +168,8 @@ struct VideoConfig final // Currently only supported with Vulkan. int iCommandBufferExecuteInterval; - // The following options determine the ubershader mode: - // No ubershaders: - // - bBackgroundShaderCompiling = false - // - bDisableSpecializedShaders = false - // Hybrid/background compiling: - // - bBackgroundShaderCompiling = true - // - bDisableSpecializedShaders = false - // Ubershaders only: - // - bBackgroundShaderCompiling = false - // - bDisableSpecializedShaders = true - - // Enable background shader compiling, use ubershaders while waiting. - bool bBackgroundShaderCompiling; - - // Use ubershaders only, don't compile specialized shaders. - bool bDisableSpecializedShaders; - - // Precompile ubershader variants at boot/config reload time. - bool bPrecompileUberShaders; + // Shader compilation settings. + UberShaderMode iUberShaderMode; // Number of shader compiler threads. // 0 disables background compilation. @@ -247,10 +237,9 @@ struct VideoConfig final return backend_info.bSupportsGPUTextureDecoding && bEnableGPUTextureDecoding; } bool UseVertexRounding() const { return bVertexRounding && iEFBScale != 1; } + bool UsingUberShaders() const { return iUberShaderMode != UberShaderMode::Disabled; } u32 GetShaderCompilerThreads() const; u32 GetShaderPrecompilerThreads() const; - bool CanPrecompileUberShaders() const; - bool CanBackgroundCompileShaders() const; }; extern VideoConfig g_Config; From e31cc1f679db82f6baca1bb3d934e2dd17542d55 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 1 Mar 2018 19:21:06 +1000 Subject: [PATCH 14/16] ShaderCache: Implement background shader compilation This enables shaders to be compiled while the game is starting, instead of blocking startup. If a shader is needed before it is compiled, emulation will block. --- Source/Core/Core/Config/GraphicsSettings.cpp | 2 + Source/Core/Core/Config/GraphicsSettings.h | 1 + .../Core/ConfigLoaders/IsSettingSaveable.cpp | 4 +- .../Config/Graphics/GeneralWidget.cpp | 10 ++ .../Config/Graphics/GeneralWidget.h | 1 + Source/Core/DolphinWX/VideoConfigDiag.cpp | 10 ++ .../Core/VideoCommon/AsyncShaderCompiler.cpp | 1 - Source/Core/VideoCommon/GXPipelineTypes.h | 75 ++++++++++ Source/Core/VideoCommon/ShaderCache.cpp | 131 ++++++++++-------- Source/Core/VideoCommon/ShaderCache.h | 90 ++---------- Source/Core/VideoCommon/VertexManagerBase.h | 4 +- Source/Core/VideoCommon/VideoCommon.vcxproj | 3 +- .../VideoCommon/VideoCommon.vcxproj.filters | 9 +- Source/Core/VideoCommon/VideoConfig.cpp | 5 + Source/Core/VideoCommon/VideoConfig.h | 1 + 15 files changed, 202 insertions(+), 145 deletions(-) create mode 100644 Source/Core/VideoCommon/GXPipelineTypes.h diff --git a/Source/Core/Core/Config/GraphicsSettings.cpp b/Source/Core/Core/Config/GraphicsSettings.cpp index b06ccdaf41..10a70c6020 100644 --- a/Source/Core/Core/Config/GraphicsSettings.cpp +++ b/Source/Core/Core/Config/GraphicsSettings.cpp @@ -76,6 +76,8 @@ const ConfigInfo GFX_BACKEND_MULTITHREADING{ const ConfigInfo GFX_COMMAND_BUFFER_EXECUTE_INTERVAL{ {System::GFX, "Settings", "CommandBufferExecuteInterval"}, 100}; const ConfigInfo GFX_SHADER_CACHE{{System::GFX, "Settings", "ShaderCache"}, true}; +const ConfigInfo GFX_WAIT_FOR_SHADERS_BEFORE_STARTING{ + {System::GFX, "Settings", "WaitForShadersBeforeStarting"}, false}; const ConfigInfo GFX_UBERSHADER_MODE{{System::GFX, "Settings", "UberShaderMode"}, static_cast(UberShaderMode::Disabled)}; const ConfigInfo GFX_SHADER_COMPILER_THREADS{ diff --git a/Source/Core/Core/Config/GraphicsSettings.h b/Source/Core/Core/Config/GraphicsSettings.h index 5f07d1906a..5c1684777b 100644 --- a/Source/Core/Core/Config/GraphicsSettings.h +++ b/Source/Core/Core/Config/GraphicsSettings.h @@ -59,6 +59,7 @@ extern const ConfigInfo GFX_ENABLE_VALIDATION_LAYER; extern const ConfigInfo GFX_BACKEND_MULTITHREADING; extern const ConfigInfo GFX_COMMAND_BUFFER_EXECUTE_INTERVAL; extern const ConfigInfo GFX_SHADER_CACHE; +extern const ConfigInfo GFX_WAIT_FOR_SHADERS_BEFORE_STARTING; extern const ConfigInfo GFX_UBERSHADER_MODE; extern const ConfigInfo GFX_SHADER_COMPILER_THREADS; extern const ConfigInfo GFX_SHADER_PRECOMPILER_THREADS; diff --git a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp index abc30b24d3..ef71c085ab 100644 --- a/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp +++ b/Source/Core/Core/ConfigLoaders/IsSettingSaveable.cpp @@ -46,8 +46,8 @@ bool IsSettingSaveable(const Config::ConfigLocation& config_location) Config::GFX_DISABLE_FOG.location, Config::GFX_BORDERLESS_FULLSCREEN.location, Config::GFX_ENABLE_VALIDATION_LAYER.location, Config::GFX_BACKEND_MULTITHREADING.location, Config::GFX_COMMAND_BUFFER_EXECUTE_INTERVAL.location, Config::GFX_SHADER_CACHE.location, - Config::GFX_UBERSHADER_MODE.location, Config::GFX_SHADER_COMPILER_THREADS.location, - Config::GFX_SHADER_PRECOMPILER_THREADS.location, + Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING.location, Config::GFX_UBERSHADER_MODE.location, + Config::GFX_SHADER_COMPILER_THREADS.location, Config::GFX_SHADER_PRECOMPILER_THREADS.location, Config::GFX_SW_ZCOMPLOC.location, Config::GFX_SW_ZFREEZE.location, Config::GFX_SW_DUMP_OBJECTS.location, Config::GFX_SW_DUMP_TEV_STAGES.location, diff --git a/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.cpp b/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.cpp index e76fc6a35c..21281488b8 100644 --- a/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.cpp +++ b/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.cpp @@ -87,6 +87,8 @@ void GeneralWidget::CreateWidgets() m_keep_window_top = new QCheckBox(tr("Keep Window on Top")); m_hide_cursor = new QCheckBox(tr("Hide Mouse Cursor")); m_render_main_window = new QCheckBox(tr("Render to Main Window")); + m_wait_for_shaders = new GraphicsBool(tr("Immediately Compile Shaders"), + Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING); m_options_box->setLayout(m_options_layout); @@ -101,6 +103,7 @@ void GeneralWidget::CreateWidgets() m_options_layout->addWidget(m_hide_cursor, 3, 0); m_options_layout->addWidget(m_render_main_window, 3, 1); + m_options_layout->addWidget(m_wait_for_shaders, 4, 0); main_layout->addWidget(m_video_box); main_layout->addWidget(m_options_box); @@ -265,6 +268,12 @@ void GeneralWidget::AddDescriptions() static const char* TR_SHOW_NETPLAY_MESSAGES_DESCRIPTION = QT_TR_NOOP("When playing on NetPlay, show chat messages, buffer changes and " "desync alerts.\n\nIf unsure, leave this unchecked."); + static const char* TR_WAIT_FOR_SHADERS_DESCRIPTION = QT_TR_NOOP( + "Waits for all shaders to finish compiling before starting a game. Enabling this " + "option may reduce stuttering or hitching for a short time after the game is " + "started, at the cost of a longer delay before the game starts.\n\nFor systems " + "with two or fewer cores, it is recommended to enable this option, as a large " + "shader queue may reduce frame rates. Otherwise, if unsure, leave this unchecked."); AddDescription(m_backend_combo, TR_BACKEND_DESCRIPTION); #ifdef _WIN32 @@ -282,6 +291,7 @@ void GeneralWidget::AddDescriptions() AddDescription(m_show_messages, TR_SHOW_FPS_DESCRIPTION); AddDescription(m_keep_window_top, TR_KEEP_WINDOW_ON_TOP_DESCRIPTION); AddDescription(m_show_messages, TR_SHOW_NETPLAY_MESSAGES_DESCRIPTION); + AddDescription(m_wait_for_shaders, TR_WAIT_FOR_SHADERS_DESCRIPTION); } void GeneralWidget::OnBackendChanged(const QString& backend_name) { diff --git a/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.h b/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.h index e611950dec..8b2d22f331 100644 --- a/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.h +++ b/Source/Core/DolphinQt2/Config/Graphics/GeneralWidget.h @@ -52,6 +52,7 @@ private: QCheckBox* m_keep_window_top; QCheckBox* m_hide_cursor; QCheckBox* m_render_main_window; + QCheckBox* m_wait_for_shaders; X11Utils::XRRConfiguration* m_xrr_config; }; diff --git a/Source/Core/DolphinWX/VideoConfigDiag.cpp b/Source/Core/DolphinWX/VideoConfigDiag.cpp index 874508459e..f1a57f8b8c 100644 --- a/Source/Core/DolphinWX/VideoConfigDiag.cpp +++ b/Source/Core/DolphinWX/VideoConfigDiag.cpp @@ -317,6 +317,12 @@ static wxString ubershader_desc = "stuttering. Balances performance and smoothness.\n\n" "Exclusive: Ubershaders will always be used. Only recommended for high-end " "systems."); +static wxString wait_for_shaders_desc = + wxTRANSLATE("Waits for all shaders to finish compiling before starting a game. Enabling this " + "option may reduce stuttering or hitching for a short time after the game is " + "started, at the cost of a longer delay before the game starts.\n\nFor systems " + "with two or fewer cores, it is recommended to enable this option, as a large " + "shader queue may reduce frame rates. Otherwise, if unsure, leave this unchecked."); VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) : wxDialog(parent, wxID_ANY, wxString::Format(_("Dolphin %s Graphics Configuration"), @@ -442,6 +448,10 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string& title) wxGetTranslation(backend_multithreading_desc), Config::GFX_BACKEND_MULTITHREADING)); } + + szr_other->Add(CreateCheckBox(page_general, _("Immediately Compile Shaders"), + wxGetTranslation(wait_for_shaders_desc), + Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING)); } wxStaticBoxSizer* const group_basic = diff --git a/Source/Core/VideoCommon/AsyncShaderCompiler.cpp b/Source/Core/VideoCommon/AsyncShaderCompiler.cpp index 59ef9762f1..e605828ed5 100644 --- a/Source/Core/VideoCommon/AsyncShaderCompiler.cpp +++ b/Source/Core/VideoCommon/AsyncShaderCompiler.cpp @@ -18,7 +18,6 @@ AsyncShaderCompiler::~AsyncShaderCompiler() // Pending work can be left at shutdown. // The work item classes are expected to clean up after themselves. _assert_(!HasWorkerThreads()); - _assert_(m_completed_work.empty()); } void AsyncShaderCompiler::QueueWorkItem(WorkItemPtr item) diff --git a/Source/Core/VideoCommon/GXPipelineTypes.h b/Source/Core/VideoCommon/GXPipelineTypes.h new file mode 100644 index 0000000000..165e9c67f8 --- /dev/null +++ b/Source/Core/VideoCommon/GXPipelineTypes.h @@ -0,0 +1,75 @@ +// Copyright 2018 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#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 GXPipelineUid +{ + const NativeVertexFormat* vertex_format; + VertexShaderUid vs_uid; + GeometryShaderUid gs_uid; + PixelShaderUid ps_uid; + RasterizationState rasterization_state; + DepthState depth_state; + BlendingState blending_state; + + // We use memcmp() for comparing pipelines as std::tie generates a large number of instructions, + // and this map lookup can happen every draw call. However, as using memcmp() will also compare + // any padding bytes, we have to ensure these are zeroed out. + GXPipelineUid() { std::memset(this, 0, sizeof(*this)); } + GXPipelineUid(const GXPipelineUid& rhs) { std::memcpy(this, &rhs, sizeof(*this)); } + GXPipelineUid& operator=(const GXPipelineUid& rhs) + { + std::memcpy(this, &rhs, sizeof(*this)); + return *this; + } + bool operator<(const GXPipelineUid& rhs) const + { + return std::memcmp(this, &rhs, sizeof(*this)) < 0; + } + bool operator==(const GXPipelineUid& rhs) const + { + return std::memcmp(this, &rhs, sizeof(*this)) == 0; + } + bool operator!=(const GXPipelineUid& rhs) const { return !operator==(rhs); } +}; +struct GXUberPipelineUid +{ + const NativeVertexFormat* vertex_format; + UberShader::VertexShaderUid vs_uid; + GeometryShaderUid gs_uid; + UberShader::PixelShaderUid ps_uid; + RasterizationState rasterization_state; + DepthState depth_state; + BlendingState blending_state; + + GXUberPipelineUid() { std::memset(this, 0, sizeof(*this)); } + GXUberPipelineUid(const GXUberPipelineUid& rhs) { std::memcpy(this, &rhs, sizeof(*this)); } + GXUberPipelineUid& operator=(const GXUberPipelineUid& rhs) + { + std::memcpy(this, &rhs, sizeof(*this)); + return *this; + } + bool operator<(const GXUberPipelineUid& rhs) const + { + return std::memcmp(this, &rhs, sizeof(*this)) < 0; + } + bool operator==(const GXUberPipelineUid& rhs) const + { + return std::memcmp(this, &rhs, sizeof(*this)) == 0; + } + bool operator!=(const GXUberPipelineUid& rhs) const { return !operator==(rhs); } +}; +} // namespace VideoCommon diff --git a/Source/Core/VideoCommon/ShaderCache.cpp b/Source/Core/VideoCommon/ShaderCache.cpp index 43a31dfd6b..512adbd12a 100644 --- a/Source/Core/VideoCommon/ShaderCache.cpp +++ b/Source/Core/VideoCommon/ShaderCache.cpp @@ -43,6 +43,8 @@ bool ShaderCache::Initialize() // Compile all known UIDs. CompileMissingPipelines(); + if (g_ActiveConfig.bWaitForShadersBeforeStarting) + WaitForAsyncCompiler(); // Switch to the runtime shader compiler thread configuration. m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); @@ -61,9 +63,7 @@ void ShaderCache::SetHostConfig(const ShaderHostConfig& host_config, u32 efb_mul void ShaderCache::Reload() { - m_async_shader_compiler->WaitUntilCompletion(); - m_async_shader_compiler->RetrieveWorkItems(); - + WaitForAsyncCompiler(); InvalidateCachedPipelines(); ClearShaderCaches(); @@ -77,6 +77,8 @@ void ShaderCache::Reload() // UIDs are still be in the map. Therefore, when these are rebuilt, the shaders will also // be recompiled. CompileMissingPipelines(); + if (g_ActiveConfig.bWaitForShadersBeforeStarting) + WaitForAsyncCompiler(); m_async_shader_compiler->ResizeWorkerThreads(g_ActiveConfig.GetShaderCompilerThreads()); } @@ -87,14 +89,14 @@ void ShaderCache::RetrieveAsyncShaders() void ShaderCache::Shutdown() { + // This may leave shaders uncommitted to the cache, but it's better than blocking shutdown + // until everything has finished compiling. m_async_shader_compiler->StopWorkerThreads(); - m_async_shader_compiler->RetrieveWorkItems(); - ClearShaderCaches(); ClearPipelineCaches(); } -const AbstractPipeline* ShaderCache::GetPipelineForUid(const GXPipelineConfig& uid) +const AbstractPipeline* ShaderCache::GetPipelineForUid(const GXPipelineUid& uid) { auto it = m_gx_pipeline_cache.find(uid); if (it != m_gx_pipeline_cache.end() && !it->second.second) @@ -109,8 +111,7 @@ const AbstractPipeline* ShaderCache::GetPipelineForUid(const GXPipelineConfig& u return InsertGXPipeline(uid, std::move(pipeline)); } -std::optional -ShaderCache::GetPipelineForUidAsync(const GXPipelineConfig& uid) +std::optional ShaderCache::GetPipelineForUidAsync(const GXPipelineUid& uid) { auto it = m_gx_pipeline_cache.find(uid); if (it != m_gx_pipeline_cache.end()) @@ -159,7 +160,7 @@ ShaderCache::GetPipelineForUidAsync(const GXPipelineConfig& uid) return {}; } -const AbstractPipeline* ShaderCache::GetUberPipelineForUid(const GXUberPipelineConfig& uid) +const AbstractPipeline* ShaderCache::GetUberPipelineForUid(const GXUberPipelineUid& uid) { auto it = m_gx_uber_pipeline_cache.find(uid); if (it != m_gx_uber_pipeline_cache.end() && !it->second.second) @@ -172,12 +173,16 @@ const AbstractPipeline* ShaderCache::GetUberPipelineForUid(const GXUberPipelineC return InsertGXUberPipeline(uid, std::move(pipeline)); } -void ShaderCache::WaitForAsyncCompiler(const std::string& msg) +void ShaderCache::WaitForAsyncCompiler() { - 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(); + while (m_async_shader_compiler->HasPendingWork()) + { + 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); } @@ -274,7 +279,7 @@ void ShaderCache::LoadPipelineUIDCache() CacheReader(ShaderCache* shader_cache_) : shader_cache(shader_cache_) {} void Read(const GXPipelineDiskCacheUid& key, const u8* data, u32 data_size) { - GXPipelineConfig config = {}; + GXPipelineUid config = {}; config.vertex_format = VertexLoaderManager::GetOrCreateMatchingFormat(key.vertex_decl); config.vs_uid = key.vs_uid; config.gs_uid = key.gs_uid; @@ -315,8 +320,6 @@ void ShaderCache::CompileMissingPipelines() if (!it.second.second) QueueUberPipelineCompile(it.first); } - - WaitForAsyncCompiler(GetStringT("Compiling shaders...")); } void ShaderCache::InvalidateCachedPipelines() @@ -508,8 +511,7 @@ AbstractPipelineConfig ShaderCache::GetGXPipelineConfig( return config; } -std::optional -ShaderCache::GetGXPipelineConfig(const GXPipelineConfig& config) +std::optional ShaderCache::GetGXPipelineConfig(const GXPipelineUid& config) { const AbstractShader* vs; auto vs_iter = m_vs_cache.shader_map.find(config.vs_uid); @@ -545,7 +547,7 @@ ShaderCache::GetGXPipelineConfig(const GXPipelineConfig& config) } std::optional -ShaderCache::GetGXUberPipelineConfig(const GXUberPipelineConfig& config) +ShaderCache::GetGXUberPipelineConfig(const GXUberPipelineUid& config) { const AbstractShader* vs; auto vs_iter = m_uber_vs_cache.shader_map.find(config.vs_uid); @@ -580,7 +582,7 @@ ShaderCache::GetGXUberPipelineConfig(const GXUberPipelineConfig& config) config.depth_state, config.blending_state); } -const AbstractPipeline* ShaderCache::InsertGXPipeline(const GXPipelineConfig& config, +const AbstractPipeline* ShaderCache::InsertGXPipeline(const GXPipelineUid& config, std::unique_ptr pipeline) { auto& entry = m_gx_pipeline_cache[config]; @@ -592,7 +594,7 @@ const AbstractPipeline* ShaderCache::InsertGXPipeline(const GXPipelineConfig& co } const AbstractPipeline* -ShaderCache::InsertGXUberPipeline(const GXUberPipelineConfig& config, +ShaderCache::InsertGXUberPipeline(const GXUberPipelineUid& config, std::unique_ptr pipeline) { auto& entry = m_gx_uber_pipeline_cache[config]; @@ -603,7 +605,7 @@ ShaderCache::InsertGXUberPipeline(const GXUberPipelineConfig& config, return entry.first.get(); } -void ShaderCache::AppendGXPipelineUID(const GXPipelineConfig& config) +void ShaderCache::AppendGXPipelineUID(const GXPipelineUid& config) { // Convert to disk format. GXPipelineDiskCacheUid disk_uid = {}; @@ -633,7 +635,7 @@ void ShaderCache::QueueVertexShaderCompile(const VertexShaderUid& uid) return true; } - virtual void Retrieve() override { shader_cache->InsertVertexShader(uid, std::move(shader)); } + void Retrieve() override { shader_cache->InsertVertexShader(uid, std::move(shader)); } private: ShaderCache* shader_cache; std::unique_ptr shader; @@ -661,11 +663,7 @@ void ShaderCache::QueueVertexUberShaderCompile(const UberShader::VertexShaderUid return true; } - virtual void Retrieve() override - { - shader_cache->InsertVertexUberShader(uid, std::move(shader)); - } - + void Retrieve() override { shader_cache->InsertVertexUberShader(uid, std::move(shader)); } private: ShaderCache* shader_cache; std::unique_ptr shader; @@ -693,7 +691,7 @@ void ShaderCache::QueuePixelShaderCompile(const PixelShaderUid& uid) return true; } - virtual void Retrieve() override { shader_cache->InsertPixelShader(uid, std::move(shader)); } + void Retrieve() override { shader_cache->InsertPixelShader(uid, std::move(shader)); } private: ShaderCache* shader_cache; std::unique_ptr shader; @@ -721,11 +719,7 @@ void ShaderCache::QueuePixelUberShaderCompile(const UberShader::PixelShaderUid& return true; } - virtual void Retrieve() override - { - shader_cache->InsertPixelUberShader(uid, std::move(shader)); - } - + void Retrieve() override { shader_cache->InsertPixelUberShader(uid, std::move(shader)); } private: ShaderCache* shader_cache; std::unique_ptr shader; @@ -737,12 +731,12 @@ void ShaderCache::QueuePixelUberShaderCompile(const UberShader::PixelShaderUid& m_async_shader_compiler->QueueWorkItem(std::move(wi)); } -void ShaderCache::QueuePipelineCompile(const GXPipelineConfig& uid) +void ShaderCache::QueuePipelineCompile(const GXPipelineUid& uid) { class PipelineWorkItem final : public AsyncShaderCompiler::WorkItem { public: - PipelineWorkItem(ShaderCache* shader_cache_, const GXPipelineConfig& uid_, + PipelineWorkItem(ShaderCache* shader_cache_, const GXPipelineUid& uid_, const AbstractPipelineConfig& config_) : shader_cache(shader_cache_), uid(uid_), config(config_) { @@ -754,11 +748,11 @@ void ShaderCache::QueuePipelineCompile(const GXPipelineConfig& uid) return true; } - virtual void Retrieve() override { shader_cache->InsertGXPipeline(uid, std::move(pipeline)); } + void Retrieve() override { shader_cache->InsertGXPipeline(uid, std::move(pipeline)); } private: ShaderCache* shader_cache; std::unique_ptr pipeline; - GXPipelineConfig uid; + GXPipelineUid uid; AbstractPipelineConfig config; }; @@ -775,13 +769,15 @@ void ShaderCache::QueuePipelineCompile(const GXPipelineConfig& uid) m_gx_pipeline_cache[uid].second = true; } -void ShaderCache::QueueUberPipelineCompile(const GXUberPipelineConfig& uid) +void ShaderCache::QueueUberPipelineCompile(const GXUberPipelineUid& uid) { - class UberPipelineWorkItem final : public AsyncShaderCompiler::WorkItem + // Since the shaders may not be compiled at pipelines request time, we do this in two passes. + // This is necessary because we can't access the caches in the worker thread. + class UberPipelineCompilePass final : public AsyncShaderCompiler::WorkItem { public: - UberPipelineWorkItem(ShaderCache* shader_cache_, const GXUberPipelineConfig& uid_, - const AbstractPipelineConfig& config_) + UberPipelineCompilePass(ShaderCache* shader_cache_, const GXUberPipelineUid& uid_, + const AbstractPipelineConfig& config_) : shader_cache(shader_cache_), uid(uid_), config(config_) { } @@ -792,27 +788,43 @@ void ShaderCache::QueueUberPipelineCompile(const GXUberPipelineConfig& uid) return true; } - virtual void Retrieve() override + void Retrieve() override { shader_cache->InsertGXUberPipeline(uid, std::move(pipeline)); } + private: + ShaderCache* shader_cache; + std::unique_ptr pipeline; + GXUberPipelineUid uid; + AbstractPipelineConfig config; + }; + class UberPipelinePreparePass final : public AsyncShaderCompiler::WorkItem + { + public: + UberPipelinePreparePass(ShaderCache* shader_cache_, const GXUberPipelineUid& uid_) + : shader_cache(shader_cache_), uid(uid_) { - shader_cache->InsertGXUberPipeline(uid, std::move(pipeline)); + } + + bool Compile() override { return true; } + void Retrieve() override + { + auto config = shader_cache->GetGXUberPipelineConfig(uid); + if (!config) + { + // One or more stages failed to compile. + shader_cache->InsertGXUberPipeline(uid, nullptr); + return; + } + + auto wi = shader_cache->m_async_shader_compiler->CreateWorkItem( + shader_cache, uid, *config); + shader_cache->m_async_shader_compiler->QueueWorkItem(std::move(wi)); } private: ShaderCache* shader_cache; - std::unique_ptr pipeline; - GXUberPipelineConfig uid; - AbstractPipelineConfig config; + GXUberPipelineUid uid; }; - 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); + auto wi = m_async_shader_compiler->CreateWorkItem(this, uid); m_async_shader_compiler->QueueWorkItem(std::move(wi)); m_gx_uber_pipeline_cache[uid].second = true; } @@ -841,9 +853,6 @@ void ShaderCache::PrecompileUberShaders() 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 = {}; @@ -856,7 +865,7 @@ void ShaderCache::PrecompileUberShaders() auto QueueDummyPipeline = [&](const UberShader::VertexShaderUid& vs_uid, const GeometryShaderUid& gs_uid, const UberShader::PixelShaderUid& ps_uid) { - GXUberPipelineConfig config; + GXUberPipelineUid config; config.vertex_format = dummy_vertex_format; config.vs_uid = vs_uid; config.gs_uid = gs_uid; diff --git a/Source/Core/VideoCommon/ShaderCache.h b/Source/Core/VideoCommon/ShaderCache.h index 6516edd8d8..8681df19be 100644 --- a/Source/Core/VideoCommon/ShaderCache.h +++ b/Source/Core/VideoCommon/ShaderCache.h @@ -19,10 +19,10 @@ #include "VideoCommon/AbstractPipeline.h" #include "VideoCommon/AbstractShader.h" -#include "VideoCommon/NativeVertexFormat.h" - #include "VideoCommon/AsyncShaderCompiler.h" +#include "VideoCommon/GXPipelineTypes.h" #include "VideoCommon/GeometryShaderGen.h" +#include "VideoCommon/NativeVertexFormat.h" #include "VideoCommon/PixelShaderGen.h" #include "VideoCommon/RenderState.h" #include "VideoCommon/UberShaderPixel.h" @@ -33,64 +33,6 @@ 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; - - // We use memcmp() for comparing pipelines as std::tie generates a large number of instructions, - // and this map lookup can happen every draw call. However, as using memcmp() will also compare - // any padding bytes, we have to ensure these are zeroed out. - GXPipelineConfig() { std::memset(this, 0, sizeof(*this)); } - GXPipelineConfig(const GXPipelineConfig& rhs) { std::memcpy(this, &rhs, sizeof(*this)); } - GXPipelineConfig& operator=(const GXPipelineConfig& rhs) - { - std::memcpy(this, &rhs, sizeof(*this)); - return *this; - } - bool operator<(const GXPipelineConfig& rhs) const - { - return std::memcmp(this, &rhs, sizeof(*this)) < 0; - } - bool operator==(const GXPipelineConfig& rhs) const - { - return std::memcmp(this, &rhs, sizeof(*this)) == 0; - } - 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; - - GXUberPipelineConfig() { std::memset(this, 0, sizeof(*this)); } - GXUberPipelineConfig(const GXUberPipelineConfig& rhs) { std::memcpy(this, &rhs, sizeof(*this)); } - GXUberPipelineConfig& operator=(const GXUberPipelineConfig& rhs) - { - std::memcpy(this, &rhs, sizeof(*this)); - return *this; - } - bool operator<(const GXUberPipelineConfig& rhs) const - { - return std::memcmp(this, &rhs, sizeof(*this)) < 0; - } - bool operator==(const GXUberPipelineConfig& rhs) const - { - return std::memcmp(this, &rhs, sizeof(*this)) == 0; - } - bool operator!=(const GXUberPipelineConfig& rhs) const { return !operator==(rhs); } -}; - class ShaderCache final { public: @@ -114,15 +56,15 @@ public: std::string GetUtilityShaderHeader() const; // Accesses ShaderGen shader caches - const AbstractPipeline* GetPipelineForUid(const GXPipelineConfig& uid); - const AbstractPipeline* GetUberPipelineForUid(const GXUberPipelineConfig& uid); + const AbstractPipeline* GetPipelineForUid(const GXPipelineUid& uid); + const AbstractPipeline* GetUberPipelineForUid(const GXUberPipelineUid& uid); // Accesses ShaderGen shader caches asynchronously. // The optional will be empty if this pipeline is now background compiling. - std::optional GetPipelineForUidAsync(const GXPipelineConfig& uid); + std::optional GetPipelineForUidAsync(const GXPipelineUid& uid); private: - void WaitForAsyncCompiler(const std::string& msg); + void WaitForAsyncCompiler(); void LoadShaderCaches(); void ClearShaderCaches(); void LoadPipelineUIDCache(); @@ -155,21 +97,21 @@ private: 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::optional GetGXPipelineConfig(const GXPipelineUid& uid); + std::optional GetGXUberPipelineConfig(const GXUberPipelineUid& uid); + const AbstractPipeline* InsertGXPipeline(const GXPipelineUid& config, std::unique_ptr pipeline); - const AbstractPipeline* InsertGXUberPipeline(const GXUberPipelineConfig& config, + const AbstractPipeline* InsertGXUberPipeline(const GXUberPipelineUid& config, std::unique_ptr pipeline); - void AppendGXPipelineUID(const GXPipelineConfig& config); + void AppendGXPipelineUID(const GXPipelineUid& 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); + void QueuePipelineCompile(const GXPipelineUid& uid); + void QueueUberPipelineCompile(const GXUberPipelineUid& uid); // Configuration bits. APIType m_api_type = APIType::Nothing; @@ -196,10 +138,8 @@ private: 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>> + std::map, bool>> m_gx_pipeline_cache; + std::map, bool>> m_gx_uber_pipeline_cache; // Disk cache of pipeline UIDs diff --git a/Source/Core/VideoCommon/VertexManagerBase.h b/Source/Core/VideoCommon/VertexManagerBase.h index eac95f614f..4d07b08411 100644 --- a/Source/Core/VideoCommon/VertexManagerBase.h +++ b/Source/Core/VideoCommon/VertexManagerBase.h @@ -83,8 +83,8 @@ protected: Slope m_zslope = {}; void CalculateZSlope(NativeVertexFormat* format); - VideoCommon::GXPipelineConfig m_current_pipeline_config; - VideoCommon::GXUberPipelineConfig m_current_uber_pipeline_config; + VideoCommon::GXPipelineUid m_current_pipeline_config; + VideoCommon::GXUberPipelineUid 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; diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index 4cc6611ee8..b84bfb6d66 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -120,6 +120,7 @@ + @@ -190,4 +191,4 @@ - + \ No newline at end of file diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index ca0fe0029b..f356cc9ef4 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -198,7 +198,7 @@ Base - Shader Managers + Shader Generators @@ -381,11 +381,14 @@ Base + + Shader Generators + - Shader Managers + Shader Generators - + \ No newline at end of file diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp index c14a00757a..271223ba3d 100644 --- a/Source/Core/VideoCommon/VideoConfig.cpp +++ b/Source/Core/VideoCommon/VideoConfig.cpp @@ -102,6 +102,7 @@ void VideoConfig::Refresh() bBackendMultithreading = Config::Get(Config::GFX_BACKEND_MULTITHREADING); iCommandBufferExecuteInterval = Config::Get(Config::GFX_COMMAND_BUFFER_EXECUTE_INTERVAL); bShaderCache = Config::Get(Config::GFX_SHADER_CACHE); + bWaitForShadersBeforeStarting = Config::Get(Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING); iUberShaderMode = static_cast(Config::Get(Config::GFX_UBERSHADER_MODE)); iShaderCompilerThreads = Config::Get(Config::GFX_SHADER_COMPILER_THREADS); iShaderPrecompilerThreads = Config::Get(Config::GFX_SHADER_PRECOMPILER_THREADS); @@ -196,6 +197,10 @@ u32 VideoConfig::GetShaderCompilerThreads() const u32 VideoConfig::GetShaderPrecompilerThreads() const { + // When using background compilation, always keep the same thread count. + if (bWaitForShadersBeforeStarting) + return GetShaderCompilerThreads(); + if (!backend_info.bSupportsBackgroundCompiling) return 0; diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h index 2b69919528..c8bded371b 100644 --- a/Source/Core/VideoCommon/VideoConfig.h +++ b/Source/Core/VideoCommon/VideoConfig.h @@ -169,6 +169,7 @@ struct VideoConfig final int iCommandBufferExecuteInterval; // Shader compilation settings. + bool bWaitForShadersBeforeStarting; UberShaderMode iUberShaderMode; // Number of shader compiler threads. From 51a586d11abba26cba611b57d5261b6111062cd4 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 10 Mar 2018 14:52:48 +1000 Subject: [PATCH 15/16] GLUtil: Encapsulate functions in a namespace --- Source/Core/Common/GL/GLUtil.cpp | 5 ++++- Source/Core/Common/GL/GLUtil.h | 12 ++++-------- Source/Core/VideoBackends/OGL/main.cpp | 2 +- Source/Core/VideoBackends/Software/SWOGLWindow.cpp | 4 ++-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/Source/Core/Common/GL/GLUtil.cpp b/Source/Core/Common/GL/GLUtil.cpp index b9f35110ee..0cbe77a1ba 100644 --- a/Source/Core/Common/GL/GLUtil.cpp +++ b/Source/Core/Common/GL/GLUtil.cpp @@ -11,12 +11,14 @@ std::unique_ptr GLInterface; +namespace GLUtil +{ void InitInterface() { GLInterface = HostGL_CreateGLInterface(); } -GLuint OpenGL_CompileProgram(const std::string& vertexShader, const std::string& fragmentShader) +GLuint CompileProgram(const std::string& vertexShader, const std::string& fragmentShader) { // generate objects GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER); @@ -100,3 +102,4 @@ GLuint OpenGL_CompileProgram(const std::string& vertexShader, const std::string& return programID; } +} diff --git a/Source/Core/Common/GL/GLUtil.h b/Source/Core/Common/GL/GLUtil.h index 02f4f18d8a..f7030f8a7a 100644 --- a/Source/Core/Common/GL/GLUtil.h +++ b/Source/Core/Common/GL/GLUtil.h @@ -8,12 +8,8 @@ #include "Common/GL/GLExtensions/GLExtensions.h" -#ifndef _WIN32 - -#include - -#endif +namespace GLUtil +{ void InitInterface(); - -// Helpers -GLuint OpenGL_CompileProgram(const std::string& vertexShader, const std::string& fragmentShader); +GLuint CompileProgram(const std::string& vertexShader, const std::string& fragmentShader); +} diff --git a/Source/Core/VideoBackends/OGL/main.cpp b/Source/Core/VideoBackends/OGL/main.cpp index 99952d1d9f..95dfeb23e7 100644 --- a/Source/Core/VideoBackends/OGL/main.cpp +++ b/Source/Core/VideoBackends/OGL/main.cpp @@ -160,7 +160,7 @@ bool VideoBackend::Initialize(void* window_handle) InitBackendInfo(); InitializeShared(); - InitInterface(); + GLUtil::InitInterface(); GLInterface->SetMode(GLInterfaceMode::MODE_DETECT); if (!GLInterface->Create(window_handle, g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer)) return false; diff --git a/Source/Core/VideoBackends/Software/SWOGLWindow.cpp b/Source/Core/VideoBackends/Software/SWOGLWindow.cpp index 8027d3fb68..695f58cc1d 100644 --- a/Source/Core/VideoBackends/Software/SWOGLWindow.cpp +++ b/Source/Core/VideoBackends/Software/SWOGLWindow.cpp @@ -15,7 +15,7 @@ std::unique_ptr SWOGLWindow::s_instance; void SWOGLWindow::Init(void* window_handle) { - InitInterface(); + GLUtil::InitInterface(); GLInterface->SetMode(GLInterfaceMode::MODE_DETECT); if (!GLInterface->Create(window_handle)) { @@ -71,7 +71,7 @@ void SWOGLWindow::Prepare() "#version 300 es\n" "precision highp float;\n"; - m_image_program = OpenGL_CompileProgram(header + vertex_shader, header + frag_shader); + m_image_program = GLUtil::CompileProgram(header + vertex_shader, header + frag_shader); glUseProgram(m_image_program); From 93ab50c55584c416d32b5bbdf39f37e9c67f4105 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 10 Mar 2018 14:54:44 +1000 Subject: [PATCH 16/16] OGL: Move primitive restart enable logic to GLUtil --- Source/Core/Common/GL/GLUtil.cpp | 23 +++++++++++++++++++ Source/Core/Common/GL/GLUtil.h | 1 + .../VideoBackends/OGL/ProgramShaderCache.cpp | 20 +--------------- Source/Core/VideoBackends/OGL/Render.cpp | 20 +--------------- 4 files changed, 26 insertions(+), 38 deletions(-) diff --git a/Source/Core/Common/GL/GLUtil.cpp b/Source/Core/Common/GL/GLUtil.cpp index 0cbe77a1ba..02a38d8654 100644 --- a/Source/Core/Common/GL/GLUtil.cpp +++ b/Source/Core/Common/GL/GLUtil.cpp @@ -102,4 +102,27 @@ GLuint CompileProgram(const std::string& vertexShader, const std::string& fragme return programID; } + +void EnablePrimitiveRestart() +{ + constexpr GLuint PRIMITIVE_RESTART_INDEX = 65535; + + if (GLInterface->GetMode() == GLInterfaceMode::MODE_OPENGLES3) + { + glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); + } + else + { + if (GLExtensions::Version() >= 310) + { + glEnable(GL_PRIMITIVE_RESTART); + glPrimitiveRestartIndex(PRIMITIVE_RESTART_INDEX); + } + else + { + glEnableClientState(GL_PRIMITIVE_RESTART_NV); + glPrimitiveRestartIndexNV(PRIMITIVE_RESTART_INDEX); + } + } +} } diff --git a/Source/Core/Common/GL/GLUtil.h b/Source/Core/Common/GL/GLUtil.h index f7030f8a7a..d4714026f0 100644 --- a/Source/Core/Common/GL/GLUtil.h +++ b/Source/Core/Common/GL/GLUtil.h @@ -12,4 +12,5 @@ namespace GLUtil { void InitInterface(); GLuint CompileProgram(const std::string& vertexShader, const std::string& fragmentShader); +void EnablePrimitiveRestart(); } diff --git a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp index 60b7da82b9..95d5b0a9cd 100644 --- a/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp +++ b/Source/Core/VideoBackends/OGL/ProgramShaderCache.cpp @@ -824,25 +824,7 @@ bool SharedContextAsyncShaderCompiler::WorkerThreadInitWorkerThread(void* param) s_is_shared_context = true; 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); - } - } - } + GLUtil::EnablePrimitiveRestart(); return true; } diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index 945dbadcf1..8f2ca8a05c 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -788,25 +788,7 @@ Renderer::Renderer() glClearDepthf(1.0f); 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); - } - } - } + GLUtil::EnablePrimitiveRestart(); IndexGenerator::Init(); UpdateActiveConfig();