From 8f68e096d4df2859e010a94d557383501e49fe62 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 22 Apr 2023 20:41:27 +1000 Subject: [PATCH] GS/HW: Avoid render pass restarts to turn off RT Significantly reduces render passes in Sly 2. --- pcsx2/GS/Renderers/DX12/GSDevice12.cpp | 16 +++++++++-- pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp | 11 ++++++-- pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp | 34 +++++++++++++++-------- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index 448113ce1d..ddbc99e2aa 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -3205,8 +3205,20 @@ void GSDevice12::RenderHW(GSHWDrawConfig& config) } // avoid restarting the render pass just to switch from rt+depth to rt and vice versa - if (m_in_render_pass && !hdr_rt && !draw_ds && m_current_depth_target && m_current_render_target == draw_rt && - config.tex != m_current_depth_target && m_current_depth_target->GetSize() == draw_rt->GetSize()) + if (m_in_render_pass && (m_current_render_target == draw_rt || m_current_depth_target == draw_ds)) + { + // avoid restarting the render pass just to switch from rt+depth to rt and vice versa + // keep the depth even if doing HDR draws, because the next draw will probably re-enable depth + if (!draw_rt && m_current_render_target && config.tex != m_current_render_target && + m_current_render_target->GetSize() == draw_ds->GetSize()) + { + draw_rt = m_current_render_target; + m_pipeline_selector.rt = true; + m_pipeline_selector.cms.wrgba = 0; + } + } + else if (!draw_ds && m_current_depth_target && config.tex != m_current_depth_target && + m_current_depth_target->GetSize() == draw_rt->GetSize()) { draw_ds = m_current_depth_target; m_pipeline_selector.ds = true; diff --git a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp index 3b9395fd20..8cde898022 100644 --- a/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp +++ b/pcsx2/GS/Renderers/OpenGL/GSDeviceOGL.cpp @@ -2424,13 +2424,17 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config) OMSetBlendState(config.blend.enable, s_gl_blend_factors[config.blend.src_factor], s_gl_blend_factors[config.blend.dst_factor], s_gl_blend_ops[config.blend.op], config.blend.constant_enable, config.blend.constant); - OMSetColorMaskState(config.colormask); // avoid changing framebuffer just to switch from rt+depth to rt and vice versa GSTexture* draw_rt = hdr_rt ? hdr_rt : config.rt; GSTexture* draw_ds = config.ds; - if (!draw_ds && GLState::ds && GLState::rt == draw_rt && config.tex != GLState::ds && - GLState::ds->GetSize() == draw_rt->GetSize()) + OMColorMaskSelector draw_colormask = config.colormask; + if (!draw_rt && GLState::rt && GLState::ds == draw_ds && GLState::rt->GetSize() == draw_ds->GetSize()) + { + draw_rt = GLState::rt; + draw_colormask.wrgba = 0; + } + else if (!draw_ds && GLState::ds && GLState::rt == draw_rt && GLState::ds->GetSize() == draw_rt->GetSize()) { // should already be always-pass. draw_ds = GLState::ds; @@ -2439,6 +2443,7 @@ void GSDeviceOGL::RenderHW(GSHWDrawConfig& config) } OMSetRenderTargets(draw_rt, draw_ds, &config.scissor); + OMSetColorMaskState(draw_colormask); SetupOM(config.depth); SendHWDraw(config, psel.ps.IsFeedbackLoop()); diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index dead6ad427..2ae953162e 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -2844,7 +2844,12 @@ void GSDeviceVK::ExecuteCommandBufferAndRestartRenderPass(bool wait_for_completi Console.Warning("Vulkan: Executing command buffer due to '%s'", reason); const VkRenderPass render_pass = m_current_render_pass; - const GSVector4i render_pass_area(m_current_render_pass_area); + const GSVector4i render_pass_area = m_current_render_pass_area; + const GSVector4i scissor = m_scissor; + GSTexture* const current_rt = m_current_render_target; + GSTexture* const current_ds = m_current_depth_target; + const FeedbackLoopFlag current_feedback_loop = m_current_framebuffer_feedback_loop; + EndRenderPass(); g_vulkan_context->ExecuteCommandBuffer(GetWaitType(wait_for_completion, GSConfig.HWSpinCPUForReadbacks)); InvalidateCachedState(); @@ -2852,8 +2857,7 @@ void GSDeviceVK::ExecuteCommandBufferAndRestartRenderPass(bool wait_for_completi if (render_pass != VK_NULL_HANDLE) { // rebind framebuffer - ApplyBaseState(m_dirty_flags, g_vulkan_context->GetCurrentCommandBuffer()); - m_dirty_flags &= ~DIRTY_BASE_STATE; + OMSetRenderTargets(current_rt, current_ds, scissor, current_feedback_loop); // restart render pass BeginRenderPass(render_pass, render_pass_area); @@ -2893,6 +2897,7 @@ void GSDeviceVK::InvalidateCachedState() m_current_framebuffer = VK_NULL_HANDLE; m_current_render_target = nullptr; m_current_depth_target = nullptr; + m_current_framebuffer_feedback_loop = FeedbackLoopFlag_None; m_current_pipeline_layout = PipelineLayout::Undefined; m_tfx_descriptor_sets[1] = VK_NULL_HANDLE; @@ -3637,13 +3642,19 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config) (!hdr_rt && DATE_rp != DATE_RENDER_PASS_STENCIL_ONE && CheckRenderPassArea(render_area)); // render pass restart optimizations - if (render_area_okay) + if (render_area_okay && (m_current_render_target == draw_rt || m_current_depth_target == draw_ds)) { // avoid restarting the render pass just to switch from rt+depth to rt and vice versa - if (!draw_ds && m_current_depth_target && m_current_render_target == draw_rt && - config.tex != m_current_depth_target && m_current_depth_target->GetSize() == draw_rt->GetSize() && - ((pipe.feedback_loop_flags & FeedbackLoopFlag_ReadAndWriteRT) == - (m_current_framebuffer_feedback_loop & FeedbackLoopFlag_ReadAndWriteRT))) + // keep the depth even if doing HDR draws, because the next draw will probably re-enable depth + if (!draw_rt && m_current_render_target && config.tex != m_current_render_target && + m_current_render_target->GetSize() == draw_ds->GetSize()) + { + draw_rt = m_current_render_target; + m_pipeline_selector.rt = true; + m_pipeline_selector.cms.wrgba = 0; + } + else if (!draw_ds && m_current_depth_target && config.tex != m_current_depth_target && + m_current_depth_target->GetSize() == draw_rt->GetSize()) { draw_ds = m_current_depth_target; m_pipeline_selector.ds = true; @@ -3652,9 +3663,7 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config) } // Prefer keeping feedback loop enabled, that way we're not constantly restarting render passes - pipe.feedback_loop_flags |= (m_current_render_target == draw_rt && m_current_depth_target == draw_ds) ? - m_current_framebuffer_feedback_loop : - 0; + pipe.feedback_loop_flags |= m_current_framebuffer_feedback_loop; } // We don't need the very first barrier if this is the first draw after switching to feedback loop, @@ -3822,7 +3831,8 @@ void GSDeviceVK::UpdateHWPipelineSelector(GSHWDrawConfig& config, PipelineSelect (config.ps.IsFeedbackLoop() || config.require_one_barrier || config.require_full_barrier)) ? FeedbackLoopFlag_ReadAndWriteRT : FeedbackLoopFlag_None; - pipe.feedback_loop_flags |= (config.tex == config.ds) ? FeedbackLoopFlag_ReadDS : FeedbackLoopFlag_None; + pipe.feedback_loop_flags |= + (config.tex && config.tex == config.ds) ? FeedbackLoopFlag_ReadDS : FeedbackLoopFlag_None; // enable point size in the vertex shader if we're rendering points regardless of upscaling. pipe.vs.point_size |= (config.topology == GSHWDrawConfig::Topology::Point);