From ebd23026a03e22e09295fe2aaba580bc088fed7a Mon Sep 17 00:00:00 2001 From: GPUCode Date: Sun, 25 Sep 2022 21:25:30 +0300 Subject: [PATCH] renderer_vulkan: Minimize state changes * Store current renderpass/pipelines and only rebind when they change * Enable extended dynamic state support and only apply them when they change --- .../rasterizer_cache/rasterizer_cache.h | 4 +- .../renderer_vulkan/renderer_vulkan.cpp | 7 ++ src/video_core/renderer_vulkan/vk_instance.h | 3 +- .../renderer_vulkan/vk_pipeline_cache.cpp | 26 ++--- .../renderer_vulkan/vk_rasterizer.cpp | 100 +++++++++++++----- .../renderer_vulkan/vk_rasterizer.h | 3 + .../renderer_vulkan/vk_renderpass_cache.cpp | 14 +-- .../renderer_vulkan/vk_renderpass_cache.h | 2 +- 8 files changed, 102 insertions(+), 57 deletions(-) diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.h b/src/video_core/rasterizer_cache/rasterizer_cache.h index 1d2843443..7509a0f6e 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache.h @@ -953,9 +953,9 @@ void RasterizerCache::DownloadSurface(const Surface& surface, SurfaceInterval if (surface->is_tiled) { std::vector swizzled_data(staging.size); + runtime.FormatConvert(surface->pixel_format, false, swizzled_data, swizzled_data); SwizzleTexture(*surface, flush_start - surface->addr, flush_end - surface->addr, - staging.mapped, swizzled_data); - runtime.FormatConvert(surface->pixel_format, false, swizzled_data, download_dest); + staging.mapped, download_dest); } else { runtime.FormatConvert(surface->pixel_format, false, staging.mapped, download_dest); } diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 8f80a3cff..69b3f9ad9 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -1044,6 +1044,13 @@ void RendererVulkan::SwapBuffers() { // Inform texture runtime about the switch runtime.OnSlotSwitch(scheduler.GetCurrentSlotIndex()); + + // When the command buffer switches, all state becomes undefined. + // This is problematic when using dynamic states, so set all + // states here + if (instance.IsExtendedDynamicStateSupported()) { + rasterizer->SyncFixedState(); + } } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 9d9b84ecf..3297e3a31 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -74,8 +74,7 @@ public: /// Returns true when VK_EXT_extended_dynamic_state is supported bool IsExtendedDynamicStateSupported() const { - // TODO: Enable this when the pipeline builder is confirmed functional - return false; + return extended_dynamic_state; } /// Returns true when VK_KHR_push_descriptors is supported diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index f6a72086c..449faea0d 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -197,8 +197,11 @@ void PipelineCache::BindPipeline(const PipelineInfo& info) { it->second = BuildPipeline(info); } - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, it->second); + if (it->second != current_pipeline) { + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + command_buffer.bindPipeline(vk::PipelineBindPoint::eGraphics, it->second); + current_pipeline = it->second; + } BindDescriptorSets(); } @@ -302,27 +305,13 @@ void PipelineCache::SetScissor(s32 x, s32 y, u32 width, u32 height) { void PipelineCache::MarkDescriptorSetsDirty() { descriptor_dirty.fill(true); + current_pipeline = VK_NULL_HANDLE; } void PipelineCache::ApplyDynamic(const PipelineInfo& info) { - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); - command_buffer.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, info.depth_stencil.stencil_compare_mask); - command_buffer.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, info.depth_stencil.stencil_write_mask); - command_buffer.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, info.depth_stencil.stencil_reference); - if (instance.IsExtendedDynamicStateSupported()) { - command_buffer.setCullModeEXT(PicaToVK::CullMode(info.rasterization.cull_mode)); - command_buffer.setDepthCompareOpEXT(PicaToVK::CompareFunc(info.depth_stencil.depth_compare_op)); - command_buffer.setDepthTestEnableEXT(info.depth_stencil.depth_test_enable); - command_buffer.setDepthWriteEnableEXT(info.depth_stencil.depth_write_enable); - command_buffer.setFrontFaceEXT(PicaToVK::FrontFace(info.rasterization.cull_mode)); + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); command_buffer.setPrimitiveTopologyEXT(PicaToVK::PrimitiveTopology(info.rasterization.topology)); - command_buffer.setStencilTestEnableEXT(info.depth_stencil.stencil_test_enable); - command_buffer.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack, - PicaToVK::StencilOp(info.depth_stencil.stencil_fail_op), - PicaToVK::StencilOp(info.depth_stencil.stencil_pass_op), - PicaToVK::StencilOp(info.depth_stencil.stencil_depth_fail_op), - PicaToVK::CompareFunc(info.depth_stencil.stencil_compare_op)); } } @@ -505,7 +494,6 @@ vk::Pipeline PipelineCache::BuildPipeline(const PipelineInfo& info) { const std::array dynamic_states = { vk::DynamicState::eViewport, vk::DynamicState::eScissor, - vk::DynamicState::eLineWidth, vk::DynamicState::eStencilCompareMask, vk::DynamicState::eStencilWriteMask, vk::DynamicState::eStencilReference, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 9b0aebe64..dbbc64300 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -172,6 +172,7 @@ RasterizerVulkan::RasterizerVulkan(Frontend::EmuWindow& emu_window, const Instan } RasterizerVulkan::~RasterizerVulkan() { + renderpass_cache.ExitRenderpass(); scheduler.Submit(SubmitMode::Flush | SubmitMode::Shutdown); VmaAllocator allocator = instance.GetAllocator(); @@ -197,17 +198,7 @@ void RasterizerVulkan::LoadDiskResources(const std::atomic_bool& stop_loading, void RasterizerVulkan::SyncEntireState() { // Sync fixed function Vulkan state - SyncClipEnabled(); - SyncCullMode(); - SyncBlendEnabled(); - SyncBlendFuncs(); - SyncBlendColor(); - SyncLogicOp(); - SyncStencilTest(); - SyncDepthTest(); - SyncColorWriteMask(); - SyncStencilWriteMask(); - SyncDepthWriteMask(); + SyncFixedState(); // Sync uniforms SyncClipCoef(); @@ -237,6 +228,20 @@ void RasterizerVulkan::SyncEntireState() { SyncShadowTextureBias(); } +void RasterizerVulkan::SyncFixedState() { + SyncClipEnabled(); + SyncCullMode(); + SyncBlendEnabled(); + SyncBlendFuncs(); + SyncBlendColor(); + SyncLogicOp(); + SyncStencilTest(); + SyncDepthTest(); + SyncColorWriteMask(); + SyncStencilWriteMask(); + SyncDepthWriteMask(); +} + /** * This is a helper function to resolve an issue when interpolating opposite quaternions. See below * for a detailed description of this issue (yuriks): @@ -1641,6 +1646,12 @@ void RasterizerVulkan::SyncClipCoef() { void RasterizerVulkan::SyncCullMode() { const auto& regs = Pica::g_state.regs; + if (instance.IsExtendedDynamicStateSupported()) { + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + command_buffer.setCullModeEXT(PicaToVK::CullMode(regs.rasterizer.cull_mode)); + command_buffer.setFrontFaceEXT(PicaToVK::FrontFace(regs.rasterizer.cull_mode)); + } + pipeline_info.rasterization.cull_mode.Assign(regs.rasterizer.cull_mode); } @@ -1748,36 +1759,71 @@ void RasterizerVulkan::SyncStencilWriteMask() { (regs.framebuffer.framebuffer.allow_depth_stencil_write != 0) ? static_cast(regs.framebuffer.output_merger.stencil_test.write_mask) : 0; + + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + command_buffer.setStencilWriteMask(vk::StencilFaceFlagBits::eFrontAndBack, pipeline_info.depth_stencil.stencil_write_mask); } void RasterizerVulkan::SyncDepthWriteMask() { const auto& regs = Pica::g_state.regs; - pipeline_info.depth_stencil.depth_write_enable.Assign( - (regs.framebuffer.framebuffer.allow_depth_stencil_write != 0 && - regs.framebuffer.output_merger.depth_write_enable)); + + const bool write_enable = (regs.framebuffer.framebuffer.allow_depth_stencil_write != 0 && + regs.framebuffer.output_merger.depth_write_enable); + + if (instance.IsExtendedDynamicStateSupported()) { + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + command_buffer.setDepthWriteEnableEXT(write_enable); + } + + pipeline_info.depth_stencil.depth_write_enable.Assign(write_enable); } void RasterizerVulkan::SyncStencilTest() { const auto& regs = Pica::g_state.regs; - pipeline_info.depth_stencil.stencil_test_enable.Assign(regs.framebuffer.output_merger.stencil_test.enable && - regs.framebuffer.framebuffer.depth_format == Pica::FramebufferRegs::DepthFormat::D24S8); - pipeline_info.depth_stencil.stencil_fail_op.Assign(regs.framebuffer.output_merger.stencil_test.action_stencil_fail); - pipeline_info.depth_stencil.stencil_pass_op.Assign(regs.framebuffer.output_merger.stencil_test.action_depth_pass); - pipeline_info.depth_stencil.stencil_depth_fail_op.Assign(regs.framebuffer.output_merger.stencil_test.action_depth_fail); - pipeline_info.depth_stencil.stencil_compare_op.Assign(regs.framebuffer.output_merger.stencil_test.func); - pipeline_info.depth_stencil.stencil_reference = regs.framebuffer.output_merger.stencil_test.reference_value; - pipeline_info.depth_stencil.stencil_write_mask = regs.framebuffer.output_merger.stencil_test.input_mask; + const bool test_enable = regs.framebuffer.output_merger.stencil_test.enable && + regs.framebuffer.framebuffer.depth_format == Pica::FramebufferRegs::DepthFormat::D24S8; + const auto& stencil_test = regs.framebuffer.output_merger.stencil_test; + + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + command_buffer.setStencilCompareMask(vk::StencilFaceFlagBits::eFrontAndBack, stencil_test.input_mask); + command_buffer.setStencilReference(vk::StencilFaceFlagBits::eFrontAndBack, stencil_test.reference_value); + + if (instance.IsExtendedDynamicStateSupported()) { + command_buffer.setStencilTestEnableEXT(test_enable); + command_buffer.setStencilOpEXT(vk::StencilFaceFlagBits::eFrontAndBack, + PicaToVK::StencilOp(stencil_test.action_stencil_fail), + PicaToVK::StencilOp(stencil_test.action_depth_pass), + PicaToVK::StencilOp(stencil_test.action_depth_fail), + PicaToVK::CompareFunc(stencil_test.func)); + } + + pipeline_info.depth_stencil.stencil_test_enable.Assign(test_enable); + pipeline_info.depth_stencil.stencil_fail_op.Assign(stencil_test.action_stencil_fail); + pipeline_info.depth_stencil.stencil_pass_op.Assign(stencil_test.action_depth_pass); + pipeline_info.depth_stencil.stencil_depth_fail_op.Assign(stencil_test.action_depth_fail); + pipeline_info.depth_stencil.stencil_compare_op.Assign(stencil_test.func); + pipeline_info.depth_stencil.stencil_reference = stencil_test.reference_value; + pipeline_info.depth_stencil.stencil_write_mask = stencil_test.input_mask; } void RasterizerVulkan::SyncDepthTest() { const auto& regs = Pica::g_state.regs; - pipeline_info.depth_stencil.depth_test_enable.Assign(regs.framebuffer.output_merger.depth_test_enable == 1 || - regs.framebuffer.output_merger.depth_write_enable == 1); - pipeline_info.depth_stencil.depth_compare_op.Assign( - regs.framebuffer.output_merger.depth_test_enable == 1 + + const bool test_enabled = regs.framebuffer.output_merger.depth_test_enable == 1 || + regs.framebuffer.output_merger.depth_write_enable == 1; + const auto compare_op = regs.framebuffer.output_merger.depth_test_enable == 1 ? regs.framebuffer.output_merger.depth_test_func.Value() - : Pica::FramebufferRegs::CompareFunc::Always); + : Pica::FramebufferRegs::CompareFunc::Always; + + if (instance.IsExtendedDynamicStateSupported()) { + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + command_buffer.setDepthCompareOpEXT(PicaToVK::CompareFunc(compare_op)); + command_buffer.setDepthTestEnableEXT(test_enabled); + } + + pipeline_info.depth_stencil.depth_test_enable.Assign(test_enabled); + pipeline_info.depth_stencil.depth_compare_op.Assign(compare_op); } void RasterizerVulkan::SyncCombinerColor() { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index d8b4a4be9..1a58b8f51 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -103,6 +103,9 @@ public: /// Syncs entire status to match PICA registers void SyncEntireState() override; + /// Sync fixed function pipeline state + void SyncFixedState(); + /// Flushes all rasterizer owned buffers void FlushBuffers(); diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp index fdd38bcdf..5f7a3a7b0 100644 --- a/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.cpp @@ -7,7 +7,6 @@ #include "video_core/renderer_vulkan/vk_renderpass_cache.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_task_scheduler.h" -#include "video_core/renderer_vulkan/vk_swapchain.h" namespace Vulkan { @@ -77,24 +76,27 @@ RenderpassCache::~RenderpassCache() { } void RenderpassCache::EnterRenderpass(const vk::RenderPassBeginInfo begin_info) { - vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + if (active_renderpass == begin_info.renderPass) { + return; + } - if (renderpass_active) { + vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); + if (active_renderpass) { command_buffer.endRenderPass(); } command_buffer.beginRenderPass(begin_info, vk::SubpassContents::eInline); - renderpass_active = true; + active_renderpass = begin_info.renderPass; } void RenderpassCache::ExitRenderpass() { - if (!renderpass_active) { + if (!active_renderpass) { return; } vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); command_buffer.endRenderPass(); - renderpass_active = false; + active_renderpass = VK_NULL_HANDLE; } void RenderpassCache::CreatePresentRenderpass(vk::Format format) { diff --git a/src/video_core/renderer_vulkan/vk_renderpass_cache.h b/src/video_core/renderer_vulkan/vk_renderpass_cache.h index a73c8e892..1db3aa94a 100644 --- a/src/video_core/renderer_vulkan/vk_renderpass_cache.h +++ b/src/video_core/renderer_vulkan/vk_renderpass_cache.h @@ -47,7 +47,7 @@ private: const Instance& instance; TaskScheduler& scheduler; - bool renderpass_active = false; + vk::RenderPass active_renderpass = VK_NULL_HANDLE; vk::RenderPass present_renderpass{}; vk::RenderPass cached_renderpasses[MAX_COLOR_FORMATS+1][MAX_DEPTH_FORMATS+1][2]; };