diff options
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/StateTracker.cpp')
| -rw-r--r-- | Source/Core/VideoBackends/Vulkan/StateTracker.cpp | 145 |
1 files changed, 123 insertions, 22 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp index bfd198886b..c7c4c77c54 100644 --- a/Source/Core/VideoBackends/Vulkan/StateTracker.cpp +++ b/Source/Core/VideoBackends/Vulkan/StateTracker.cpp @@ -14,6 +14,7 @@ #include "VideoBackends/Vulkan/ObjectCache.h" #include "VideoBackends/Vulkan/StreamBuffer.h" #include "VideoBackends/Vulkan/Util.h" +#include "VideoBackends/Vulkan/VertexFormat.h" #include "VideoBackends/Vulkan/VulkanContext.h" #include "VideoCommon/GeometryShaderManager.h" @@ -116,6 +117,93 @@ bool StateTracker::Initialize() return true; } +void StateTracker::LoadPipelineUIDCache() +{ + class PipelineInserter final : public LinearDiskCacheReader<SerializedPipelineUID, u32> + { + 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; + }; + + std::string filename = g_object_cache->GetDiskCacheFileName("pipeline-uid"); + PipelineInserter inserter(this); + + // OpenAndRead calls Close() first, which will flush all data to disk when reloading. + // This assertion must hold true, otherwise data corruption will result. + m_uid_cache.OpenAndRead(filename, inserter); +} + +void StateTracker::AppendToPipelineUIDCache(const PipelineInfo& info) +{ + SerializedPipelineUID sinfo; + sinfo.blend_state_bits = info.blend_state.bits; + sinfo.rasterizer_state_bits = info.rasterization_state.bits; + sinfo.depth_stencil_state_bits = info.depth_stencil_state.bits; + 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; + sinfo.primitive_topology = info.primitive_topology; + + 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 = VertexFormat::GetOrCreateMatchingFormat(uid.vertex_decl); + pinfo.pipeline_layout = uid.ps_uid.GetUidData()->bounding_box ? + g_object_cache->GetBBoxPipelineLayout() : + g_object_cache->GetStandardPipelineLayout(); + pinfo.vs = g_object_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 (!uid.gs_uid.GetUidData()->IsPassthrough()) + { + pinfo.gs = g_object_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_object_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.blend_state.bits = uid.blend_state_bits; + pinfo.rasterization_state.bits = uid.rasterizer_state_bits; + pinfo.depth_stencil_state.bits = uid.depth_stencil_state_bits; + pinfo.primitive_topology = uid.primitive_topology; + + VkPipeline pipeline = g_object_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) @@ -793,41 +881,54 @@ void StateTracker::EndClearRenderPass() EndRenderPass(); } +PipelineInfo StateTracker::GetAlphaPassPipelineConfig(const PipelineInfo& info) const +{ + PipelineInfo temp_info = info; + + // Skip depth writes for this pass. The results will be the same, so no + // point in overwriting depth values with the same value. + temp_info.depth_stencil_state.write_enable = VK_FALSE; + + // Only allow alpha writes, and disable blending. + temp_info.blend_state.blend_enable = VK_FALSE; + temp_info.blend_state.logic_op_enable = VK_FALSE; + temp_info.blend_state.write_mask = VK_COLOR_COMPONENT_A_BIT; + + return temp_info; +} + +VkPipeline StateTracker::GetPipelineAndCacheUID(const PipelineInfo& info) +{ + auto result = g_object_cache->GetPipelineWithCacheResult(info); + + // Add to the UID cache if it is a new pipeline. + if (!result.second) + AppendToPipelineUIDCache(info); + + return result.first; +} + 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 - if (m_dstalpha_mode != DSTALPHA_ALPHA_PASS) + // Grab a new pipeline object, this can fail. + // We have to use a different blend state for the alpha pass of the dstalpha fallback. + if (m_dstalpha_mode == DSTALPHA_ALPHA_PASS) { - m_pipeline_object = g_object_cache->GetPipeline(m_pipeline_state); - if (m_pipeline_object == VK_NULL_HANDLE) - return false; + // We need to retain the existing state, since we don't want to break the next draw. + PipelineInfo temp_info = GetAlphaPassPipelineConfig(m_pipeline_state); + m_pipeline_object = GetPipelineAndCacheUID(temp_info); } else { - // We need to make a few modifications to the pipeline object, but retain - // the existing state, since we don't want to break the next draw. - PipelineInfo temp_info = m_pipeline_state; - - // Skip depth writes for this pass. The results will be the same, so no - // point in overwriting depth values with the same value. - temp_info.depth_stencil_state.write_enable = VK_FALSE; - - // Only allow alpha writes, and disable blending. - temp_info.blend_state.blend_enable = VK_FALSE; - temp_info.blend_state.logic_op_enable = VK_FALSE; - temp_info.blend_state.write_mask = VK_COLOR_COMPONENT_A_BIT; - - m_pipeline_object = g_object_cache->GetPipeline(temp_info); - if (m_pipeline_object == VK_NULL_HANDLE) - return false; + m_pipeline_object = GetPipelineAndCacheUID(m_pipeline_state); } m_dirty_flags |= DIRTY_FLAG_PIPELINE_BINDING; - return true; + return m_pipeline_object != VK_NULL_HANDLE; } bool StateTracker::UpdateDescriptorSet() |
