From 1eaec773e2d39b31173f8d9b0c5587371b116756 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 24 Feb 2023 22:07:00 +1000 Subject: [PATCH] GS/HW: Don't allocate Z buffer if tests always pass .. and Z is masked. Stops some offscreen targets from expanding in size. Partial fix for regressions in True Crime: New York City. --- pcsx2/GS/Renderers/DX12/GSDevice12.cpp | 3 +- pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 48 +++++++++++++----------- pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp | 3 +- 3 files changed, 31 insertions(+), 23 deletions(-) diff --git a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp index 3a87ea946d..e2857ae970 100644 --- a/pcsx2/GS/Renderers/DX12/GSDevice12.cpp +++ b/pcsx2/GS/Renderers/DX12/GSDevice12.cpp @@ -2540,7 +2540,8 @@ 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) + 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()) { draw_ds = m_current_depth_target; m_pipeline_selector.ds = true; diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index f624cdd97d..3c4e970b94 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -1296,10 +1296,6 @@ void GSRendererHW::Draw() // skip alpha test if possible // Note: do it first so we know if frame/depth writes are masked - const GIFRegTEST TEST = context->TEST; - const GIFRegFRAME FRAME = context->FRAME; - const GIFRegZBUF ZBUF = context->ZBUF; - u32 fm = context->FRAME.FBMSK; u32 zm = context->ZBUF.ZMSK || context->TEST.ZTE == 0 ? 0xffffffff : 0; const u32 fm_mask = GSLocalMemory::m_psm[m_context->FRAME.PSM].fmsk; @@ -1359,14 +1355,26 @@ void GSRendererHW::Draw() // 2/ SuperMan really draws (0,0,0,0) color and a (0) 32-bits depth // 3/ 50cents really draws (0,0,0,128) color and a (0) 24 bits depth // Note: FF DoC has both buffer at same location but disable the depth test (write?) with ZTE = 0 + const u32 max_z = (0xFFFFFFFF >> (GSLocalMemory::m_psm[context->ZBUF.PSM].fmt * 8)); const bool no_rt = (context->ALPHA.IsCd() && PRIM->ABE && (context->FRAME.PSM == 1)) || (!context->TEST.DATE && (context->FRAME.FBMSK & GSLocalMemory::m_psm[context->FRAME.PSM].fmsk) == GSLocalMemory::m_psm[context->FRAME.PSM].fmsk); const bool no_ds = ( - // Depth is always pass/fail (no read) and write are discarded (tekken 5). (Note: DATE is currently implemented with a stencil buffer => a depth/stencil buffer) - (zm != 0 && m_context->TEST.ZTST <= ZTST_ALWAYS && !m_context->TEST.DATE) || + // Depth is always pass/fail (no read) and write are discarded. + (zm != 0 && context->TEST.ZTST <= ZTST_ALWAYS) || + // Depth test will always pass + (zm != 0 && context->TEST.ZTST == ZTST_GEQUAL && m_vt.m_eq.z && std::min(m_vertex.buff[0].XYZ.Z, max_z) == max_z) || // Depth will be written through the RT (!no_rt && context->FRAME.FBP == context->ZBUF.ZBP && !PRIM->TME && zm == 0 && (fm & fm_mask) == 0 && context->TEST.ZTE)); + // No Z test if no z buffer. + if (no_ds) + { + if (context->TEST.ZTST != ZTST_ALWAYS) + GL_CACHE("Disabling Z buffer because all tests will pass."); + + context->TEST.ZTST = ZTST_ALWAYS; + } + if (no_rt && no_ds) { GL_CACHE("Skipping draw with no color nor depth output."); @@ -1381,7 +1389,7 @@ void GSRendererHW::Draw() if (CanUseSwPrimRender(no_rt, no_ds, draw_sprite_tex) && SwPrimRender(*this, true)) { GL_CACHE("Possible texture decompression, drawn with SwPrimRender() (BP %x BW %u TBP0 %x TBW %u)", - m_context->FRAME.Block(), m_context->FRAME.FBMSK, m_context->TEX0.TBP0, m_context->TEX0.TBW); + context->FRAME.Block(), context->FRAME.FBMSK, context->TEX0.TBP0, context->TEX0.TBW); return; } @@ -1999,12 +2007,6 @@ void GSRendererHW::Draw() // - context->TEST = TEST; - context->FRAME = FRAME; - context->ZBUF = ZBUF; - - // - // Temporary source *must* be invalidated before normal, because otherwise it'll be double freed. m_tc->InvalidateTemporarySource(); @@ -2282,14 +2284,6 @@ void GSRendererHW::EmulateZbuffer() m_conf.ps.zclamp = 1; } } - - const GSVertex* v = &m_vertex.buff[0]; - // Minor optimization of a corner case (it allow to better emulate some alpha test effects) - if (m_conf.depth.ztst == ZTST_GEQUAL && m_vt.m_eq.z && v[0].XYZ.Z == max_z) - { - GL_DBG("Optimize Z test GEQUAL to ALWAYS (%s)", psm_str(m_context->ZBUF.PSM)); - m_conf.depth.ztst = ZTST_ALWAYS; - } } void GSRendererHW::EmulateTextureShuffleAndFbmask() @@ -3790,6 +3784,15 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc m_conf.datm = m_context->TEST.DATM; + // If we're doing stencil DATE and we don't have a depth buffer, we need to allocate a temporary one. + GSTexture* temp_ds = nullptr; + if (m_conf.destination_alpha >= GSHWDrawConfig::DestinationAlphaMode::Stencil && + m_conf.destination_alpha <= GSHWDrawConfig::DestinationAlphaMode::StencilOne && !m_conf.ds) + { + temp_ds = g_gs_device->CreateDepthStencil(rt->GetWidth(), rt->GetHeight(), GSTexture::Format::DepthStencil, false); + m_conf.ds = temp_ds; + } + // vs m_conf.vs.tme = PRIM->TME; @@ -4098,6 +4101,9 @@ void GSRendererHW::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc m_conf.drawlist = (m_conf.require_full_barrier && m_vt.m_primclass == GS_SPRITE_CLASS) ? &m_drawlist : nullptr; g_gs_device->RenderHW(m_conf); + + if (temp_ds) + g_gs_device->Recycle(temp_ds); } // If the EE uploaded a new CLUT since the last draw, use that. diff --git a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp index c210f49ed6..b3540272c2 100644 --- a/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp +++ b/pcsx2/GS/Renderers/Vulkan/GSDeviceVK.cpp @@ -3034,7 +3034,8 @@ void GSDeviceVK::RenderHW(GSHWDrawConfig& config) { // 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 && !(pipe.feedback_loop && !CurrentFramebufferHasFeedbackLoop())) + config.tex != m_current_depth_target && m_current_depth_target->GetSize() == draw_rt->GetSize() && + !(pipe.feedback_loop && !CurrentFramebufferHasFeedbackLoop())) { draw_ds = m_current_depth_target; m_pipeline_selector.ds = true;