From fdb751ed1a29fd36a74acccce4a34123229d2df6 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sat, 11 May 2024 21:04:21 +1000 Subject: [PATCH] GS/HW: Fix possible texture leak on skipped draw --- pcsx2/GS/Renderers/Common/GSDevice.cpp | 5 +++++ pcsx2/GS/Renderers/Common/GSDevice.h | 6 ++++++ pcsx2/GS/Renderers/HW/GSRendererHW.cpp | 29 ++++++++++---------------- pcsx2/GS/Renderers/HW/GSRendererHW.h | 4 ++-- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/pcsx2/GS/Renderers/Common/GSDevice.cpp b/pcsx2/GS/Renderers/Common/GSDevice.cpp index 36a402400c..d0b40b11d2 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.cpp +++ b/pcsx2/GS/Renderers/Common/GSDevice.cpp @@ -396,6 +396,11 @@ bool GSDevice::UpdateImGuiFontTexture() return true; } +void GSDevice::TextureRecycleDeleter::operator()(GSTexture* const tex) +{ + g_gs_device->Recycle(tex); +} + GSTexture* GSDevice::FetchSurface(GSTexture::Type type, int width, int height, int levels, GSTexture::Format format, bool clear, bool prefer_unused_texture) { const GSVector2i size(width, height); diff --git a/pcsx2/GS/Renderers/Common/GSDevice.h b/pcsx2/GS/Renderers/Common/GSDevice.h index ba18334f68..38878156ee 100644 --- a/pcsx2/GS/Renderers/Common/GSDevice.h +++ b/pcsx2/GS/Renderers/Common/GSDevice.h @@ -763,6 +763,12 @@ public: GSHWDrawConfig::ColorMaskSelector wmask; // 0xf for all channels by default }; + struct TextureRecycleDeleter + { + void operator()(GSTexture* const tex); + }; + using RecycledTexture = std::unique_ptr; + enum BlendFactor : u8 { // HW blend factors diff --git a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp index 63d5435e79..83bcc02226 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.cpp +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.cpp @@ -4586,7 +4586,8 @@ __ri static constexpr u8 EffectiveClamp(u8 clamp, bool has_region) return (clamp >= CLAMP_REGION_CLAMP && has_region) ? (clamp ^ 3) : clamp; } -__ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt, const GSTextureCache::Target* ds, GSTextureCache::Source* tex, const TextureMinMaxResult& tmm, GSTexture*& src_copy) +__ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt, const GSTextureCache::Target* ds, GSTextureCache::Source* tex, + const TextureMinMaxResult& tmm, GSDevice::RecycledTexture& src_copy) { // don't overwrite the texture when using channel shuffle, but keep the palette if (!m_channel_shuffle) @@ -4908,7 +4909,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt, __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, const GSTextureCache::Target* ds, const GSTextureCache::Source* tex, const TextureMinMaxResult& tmm, GSTextureCache::SourceRegion& source_region, - bool& target_region, GSVector2i& unscaled_size, float& scale, GSTexture*& src_copy) + bool& target_region, GSVector2i& unscaled_size, float& scale, GSDevice::RecycledTexture& src_copy) { // Detect framebuffer read that will need special handling const GSTextureCache::Target* src_target = nullptr; @@ -5051,11 +5052,11 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c GSVector2i(static_cast(std::ceil(static_cast(copy_dst_offset.x) * scale)), static_cast(std::ceil(static_cast(copy_dst_offset.y) * scale))); - src_copy = src_target->m_texture->IsDepthStencil() ? + src_copy.reset(src_target->m_texture->IsDepthStencil() ? g_gs_device->CreateDepthStencil( scaled_copy_size.x, scaled_copy_size.y, src_target->m_texture->GetFormat(), false) : g_gs_device->CreateTexture( - scaled_copy_size.x, scaled_copy_size.y, 1, src_target->m_texture->GetFormat(), true); + scaled_copy_size.x, scaled_copy_size.y, 1, src_target->m_texture->GetFormat(), true)); if (!src_copy) [[unlikely]] { Console.Error("Failed to allocate %dx%d texture for hazard copy", scaled_copy_size.x, scaled_copy_size.y); @@ -5065,8 +5066,8 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c } g_gs_device->CopyRect( - src_target->m_texture, src_copy, scaled_copy_range, scaled_copy_dst_offset.x, scaled_copy_dst_offset.y); - m_conf.tex = src_copy; + src_target->m_texture, src_copy.get(), scaled_copy_range, scaled_copy_dst_offset.x, scaled_copy_dst_offset.y); + m_conf.tex = src_copy.get(); } bool GSRendererHW::CanUseTexIsFB(const GSTextureCache::Target* rt, const GSTextureCache::Source* tex, @@ -5761,12 +5762,12 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta m_conf.datm = static_cast(m_cached_ctx.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; + GSDevice::RecycledTexture temp_ds; if (m_conf.destination_alpha >= GSHWDrawConfig::DestinationAlphaMode::Stencil && m_conf.destination_alpha <= GSHWDrawConfig::DestinationAlphaMode::StencilOne && !m_conf.ds) { - temp_ds = g_gs_device->CreateDepthStencil(m_conf.rt->GetWidth(), m_conf.rt->GetHeight(), GSTexture::Format::DepthStencil, false); - m_conf.ds = temp_ds; + temp_ds.reset(g_gs_device->CreateDepthStencil(m_conf.rt->GetWidth(), m_conf.rt->GetHeight(), GSTexture::Format::DepthStencil, false)); + m_conf.ds = temp_ds.get(); } // vs @@ -5868,7 +5869,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta m_conf.cb_ps.FogColor_AREF = fc.blend32<8>(m_conf.cb_ps.FogColor_AREF); } - GSTexture* tex_copy = nullptr; + GSDevice::RecycledTexture tex_copy; if (tex) { EmulateTextureSampler(rt, ds, tex, tmm, tex_copy); @@ -5983,10 +5984,7 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta if (!ate_first_pass) { if (!m_conf.alpha_second_pass.enable) - { - CleanupDraw(true); return; - } // RenderHW always renders first pass, replace first pass with second std::memcpy(&m_conf.ps, &m_conf.alpha_second_pass.ps, sizeof(m_conf.ps)); @@ -5999,11 +5997,6 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta 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 (tex_copy) - g_gs_device->Recycle(tex_copy); - 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/HW/GSRendererHW.h b/pcsx2/GS/Renderers/HW/GSRendererHW.h index 77b2f163c1..78a123a944 100644 --- a/pcsx2/GS/Renderers/HW/GSRendererHW.h +++ b/pcsx2/GS/Renderers/HW/GSRendererHW.h @@ -92,10 +92,10 @@ private: void CleanupDraw(bool invalidate_temp_src); void EmulateTextureSampler(const GSTextureCache::Target* rt, const GSTextureCache::Target* ds, - GSTextureCache::Source* tex, const TextureMinMaxResult& tmm, GSTexture*& src_copy); + GSTextureCache::Source* tex, const TextureMinMaxResult& tmm, GSDevice::RecycledTexture& src_copy); void HandleTextureHazards(const GSTextureCache::Target* rt, const GSTextureCache::Target* ds, const GSTextureCache::Source* tex, const TextureMinMaxResult& tmm, GSTextureCache::SourceRegion& source_region, - bool& target_region, GSVector2i& unscaled_size, float& scale, GSTexture*& src_copy); + bool& target_region, GSVector2i& unscaled_size, float& scale, GSDevice::RecycledTexture& src_copy); bool CanUseTexIsFB(const GSTextureCache::Target* rt, const GSTextureCache::Source* tex, const TextureMinMaxResult& tmm);