From a32ab4cc978a1bbd543e76c72ef6e8b96b5bd9c8 Mon Sep 17 00:00:00 2001
From: Stenzek <stenzek@gmail.com>
Date: Sun, 23 Apr 2023 15:17:08 +1000
Subject: [PATCH] GS: Refactor ResizeTexture() to ResizeRenderTarget()

---
 pcsx2/GS/Renderers/Common/GSDevice.cpp   | 104 ++++++++++-------------
 pcsx2/GS/Renderers/Common/GSDevice.h     |   6 +-
 pcsx2/GS/Renderers/HW/GSTextureCache.cpp |   8 +-
 pcsx2/GS/Renderers/SW/GSRendererSW.cpp   |   2 +-
 4 files changed, 52 insertions(+), 68 deletions(-)

diff --git a/pcsx2/GS/Renderers/Common/GSDevice.cpp b/pcsx2/GS/Renderers/Common/GSDevice.cpp
index ec589655c8..20bf175fdb 100644
--- a/pcsx2/GS/Renderers/Common/GSDevice.cpp
+++ b/pcsx2/GS/Renderers/Common/GSDevice.cpp
@@ -380,14 +380,6 @@ GSTexture* GSDevice::CreateTexture(int w, int h, int mipmap_levels, GSTexture::F
 	return FetchSurface(GSTexture::Type::Texture, w, h, levels, format, false, prefer_reuse);
 }
 
-GSTexture::Format GSDevice::GetDefaultTextureFormat(GSTexture::Type type)
-{
-	if (type == GSTexture::Type::DepthStencil)
-		return GSTexture::Format::DepthStencil;
-	else
-		return GSTexture::Format::Color;
-}
-
 void GSDevice::StretchRect(GSTexture* sTex, GSTexture* dTex, const GSVector4& dRect, ShaderConvert shader, bool linear)
 {
 	StretchRect(sTex, GSVector4(0, 0, 1, 1), dTex, dRect, shader, linear);
@@ -441,7 +433,7 @@ void GSDevice::ClearCurrent()
 
 void GSDevice::Merge(GSTexture* sTex[3], GSVector4* sRect, GSVector4* dRect, const GSVector2i& fs, const GSRegPMODE& PMODE, const GSRegEXTBUF& EXTBUF, const GSVector4& c)
 {
-	if (ResizeTarget(&m_merge, fs.x, fs.y))
+	if (ResizeRenderTarget(&m_merge, fs.x, fs.y, false, false))
 		DoMerge(sTex, sRect, m_merge, dRect, PMODE, EXTBUF, c, GSConfig.PCRTCOffsets);
 
 	m_current = m_merge;
@@ -481,20 +473,20 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
 	switch (mode)
 	{
 		case 0: // Weave
-			ResizeTarget(&m_weavebob, ds.x, ds.y);
+			ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
 			do_interlace(m_merge, m_weavebob, ShaderInterlace::WEAVE, false, offset, field);
 			m_current = m_weavebob;
 			break;
 		case 1: // Bob
 			// Field is reversed here as we are countering the bounce.
-			ResizeTarget(&m_weavebob, ds.x, ds.y);
+			ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
 			do_interlace(m_merge, m_weavebob, ShaderInterlace::BOB, true, yoffset * (1 - field), 0);
 			m_current = m_weavebob;
 			break;
 		case 2: // Blend
-			ResizeTarget(&m_weavebob, ds.x, ds.y);
+			ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
 			do_interlace(m_merge, m_weavebob, ShaderInterlace::WEAVE, false, offset, field);
-			ResizeTarget(&m_blend, ds.x, ds.y);
+			ResizeRenderTarget(&m_blend, ds.x, ds.y, true, false);
 			do_interlace(m_weavebob, m_blend, ShaderInterlace::BLEND, false, 0, 0);
 			m_current = m_blend;
 			break;
@@ -503,9 +495,9 @@ void GSDevice::Interlace(const GSVector2i& ds, int field, int mode, float yoffse
 			bufIdx &= ~1;
 			bufIdx |= field;
 			bufIdx &= 3;
-			ResizeTarget(&m_mad, ds.x, ds.y * 2.0f);
+			ResizeRenderTarget(&m_mad, ds.x, ds.y * 2.0f, true, false);
 			do_interlace(m_merge, m_mad, ShaderInterlace::MAD_BUFFER, false, offset, bufIdx);
-			ResizeTarget(&m_weavebob, ds.x, ds.y);
+			ResizeRenderTarget(&m_weavebob, ds.x, ds.y, true, false);
 			do_interlace(m_mad, m_weavebob, ShaderInterlace::MAD_RECONSTRUCT, false, 0, bufIdx);
 			m_current = m_weavebob;
 			break;
@@ -519,9 +511,8 @@ void GSDevice::FXAA()
 {
 	// Combining FXAA+ShadeBoost can't share the same target.
 	GSTexture*& dTex = (m_current == m_target_tmp) ? m_merge : m_target_tmp;
-	if (ResizeTexture(&dTex, GSTexture::Type::RenderTarget, m_current->GetWidth(), m_current->GetHeight(), false, true))
+	if (ResizeRenderTarget(&dTex, m_current->GetWidth(), m_current->GetHeight(), false, false))
 	{
-		InvalidateRenderTarget(dTex);
 		DoFXAA(m_current, dTex);
 		m_current = dTex;
 	}
@@ -529,10 +520,8 @@ void GSDevice::FXAA()
 
 void GSDevice::ShadeBoost()
 {
-	if (ResizeTexture(&m_target_tmp, GSTexture::Type::RenderTarget, m_current->GetWidth(), m_current->GetHeight(), false, true))
+	if (ResizeRenderTarget(&m_target_tmp, m_current->GetWidth(), m_current->GetHeight(), false, false))
 	{
-		InvalidateRenderTarget(m_target_tmp);
-
 		// predivide to avoid the divide (multiply) in the shader
 		const float params[4] = {
 			static_cast<float>(GSConfig.ShadeBoost_Brightness) * (1.0f / 50.0f),
@@ -557,7 +546,7 @@ void GSDevice::Resize(int width, int height)
 		s = m_current->GetSize() * GSVector2i(++multiplier);
 	}
 
-	if (ResizeTexture(&dTex, GSTexture::Type::RenderTarget, s.x, s.y, false))
+	if (ResizeRenderTarget(&dTex, s.x, s.y, false, false))
 	{
 		const GSVector4 sRect(0, 0, 1, 1);
 		const GSVector4 dRect(0, 0, s.x, s.y);
@@ -566,55 +555,48 @@ void GSDevice::Resize(int width, int height)
 	}
 }
 
-bool GSDevice::ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h, bool clear, bool prefer_reuse)
+bool GSDevice::ResizeRenderTarget(GSTexture** t, int w, int h, bool preserve_contents, bool recycle)
 {
-	if (t == NULL)
+	pxAssert(t);
+
+	GSTexture* orig_tex = *t;
+	if (orig_tex && orig_tex->GetWidth() == w && orig_tex->GetHeight() == h)
 	{
-		ASSERT(0);
+		if (!preserve_contents)
+			InvalidateRenderTarget(orig_tex);
+
+		return true;
+	}
+
+	GSTexture* new_tex;
+	try
+	{
+		const GSTexture::Format fmt = orig_tex ? orig_tex->GetFormat() : GSTexture::Format::Color;
+		new_tex = FetchSurface(GSTexture::Type::RenderTarget, w, h, 1, fmt, !preserve_contents, true);
+	}
+	catch (std::bad_alloc&)
+	{
+		Console.WriteLn("%dx%d texture allocation failed in ResizeTexture()", w, h);
 		return false;
 	}
 
-	GSTexture* t2 = *t;
-
-	if (t2 == NULL || t2->GetWidth() != w || t2->GetHeight() != h)
+	if (preserve_contents && orig_tex)
 	{
-		const GSTexture::Format fmt = t2 ? t2->GetFormat() : GetDefaultTextureFormat(type);
-		const int levels = t2 ? (t2->IsMipmap() ? MipmapLevelsForSize(w, h) : 1) : 1;
-
-		GSTexture* new_t = FetchSurface(type, w, h, levels, fmt, clear, prefer_reuse);
-		if (new_t)
-		{
-			if (t2)
-			{
-				// TODO: We probably want to make this optional if we're overwriting it...
-				const GSVector4 sRect(0, 0, 1, 1);
-				const GSVector4 dRect(0, 0, t2->GetWidth(), t2->GetHeight());
-				StretchRect(t2, sRect, new_t, dRect, ShaderConvert::COPY, true);
-				Recycle(t2);
-			}
-
-			t2 = new_t;
-			*t = t2;
-		}
+		constexpr GSVector4 sRect = GSVector4::cxpr(0, 0, 1, 1);
+		const GSVector4 dRect = GSVector4(orig_tex->GetRect());
+		StretchRect(orig_tex, sRect, new_tex, dRect, ShaderConvert::COPY, true);
 	}
 
-	return t2 != NULL;
-}
+	if (orig_tex)
+	{
+		if (recycle)
+			Recycle(orig_tex);
+		else
+			delete orig_tex;
+	}
 
-bool GSDevice::ResizeTexture(GSTexture** t, int w, int h, bool prefer_reuse)
-{
-	return ResizeTexture(t, GSTexture::Type::Texture, w, h, false, prefer_reuse);
-}
-
-bool GSDevice::ResizeTarget(GSTexture** t, int w, int h)
-{
-	return ResizeTexture(t, GSTexture::Type::RenderTarget, w, h);
-}
-
-bool GSDevice::ResizeTarget(GSTexture** t)
-{
-	GSVector2i s = m_current->GetSize();
-	return ResizeTexture(t, GSTexture::Type::RenderTarget, s.x, s.y);
+	*t = new_tex;
+	return true;
 }
 
 void GSDevice::SetHWDrawConfigForAlphaPass(GSHWDrawConfig::PSSelector* ps,
diff --git a/pcsx2/GS/Renderers/Common/GSDevice.h b/pcsx2/GS/Renderers/Common/GSDevice.h
index 569551d697..c365fe3ccd 100644
--- a/pcsx2/GS/Renderers/Common/GSDevice.h
+++ b/pcsx2/GS/Renderers/Common/GSDevice.h
@@ -890,7 +890,6 @@ public:
 	GSTexture* CreateRenderTarget(int w, int h, GSTexture::Format format, bool clear = true);
 	GSTexture* CreateDepthStencil(int w, int h, GSTexture::Format format, bool clear = true);
 	GSTexture* CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse = false);
-	GSTexture::Format GetDefaultTextureFormat(GSTexture::Type type);
 
 	virtual std::unique_ptr<GSDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GSTexture::Format format) = 0;
 
@@ -929,10 +928,7 @@ public:
 
 	void CAS(GSTexture*& tex, GSVector4i& src_rect, GSVector4& src_uv, const GSVector4& draw_rect, bool sharpen_only);
 
-	bool ResizeTexture(GSTexture** t, GSTexture::Type type, int w, int h, bool clear = true, bool prefer_reuse = false);
-	bool ResizeTexture(GSTexture** t, int w, int h, bool prefer_reuse = false);
-	bool ResizeTarget(GSTexture** t, int w, int h);
-	bool ResizeTarget(GSTexture** t);
+	bool ResizeRenderTarget(GSTexture** t, int w, int h, bool preserve_contents, bool recycle);
 
 	bool IsRBSwapped() { return m_rbswapped; }
 
diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp
index 651d7d88c0..e9050e076b 100644
--- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp
+++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp
@@ -1268,7 +1268,13 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
 			g_gs_device->StretchRect(dst->m_texture, sRect, tex, dRect, (type == RenderTarget) ? ShaderConvert::COPY : ShaderConvert::DEPTH_COPY, false);
 			g_perfmon.Put(GSPerfMon::TextureCopies, 1);
 			m_target_memory_usage = (m_target_memory_usage - dst->m_texture->GetMemUsage()) + tex->GetMemUsage();
-			g_gs_device->Recycle(dst->m_texture);
+			
+			// If we're changing resolution scale, just toss the texture, it's not going to get reused.
+			if (!GSConfig.UserHacks_NativePaletteDraw || (dst->m_scale != 1.0f && scale != 1.0f))
+				delete dst->m_texture;
+			else
+				g_gs_device->Recycle(dst->m_texture);
+
 			dst->m_texture = tex;
 			dst->m_scale = scale;
 			dst->m_unscaled_size = new_size;
diff --git a/pcsx2/GS/Renderers/SW/GSRendererSW.cpp b/pcsx2/GS/Renderers/SW/GSRendererSW.cpp
index ffebf84e9b..2de43a19b0 100644
--- a/pcsx2/GS/Renderers/SW/GSRendererSW.cpp
+++ b/pcsx2/GS/Renderers/SW/GSRendererSW.cpp
@@ -118,7 +118,7 @@ GSTexture* GSRendererSW::GetOutput(int i, float& scale, int& y_offset)
 	int w = curFramebuffer.FBW * 64;
 	int h = framebufferSize.y;
 
-	if (g_gs_device->ResizeTarget(&m_texture[index], w, h))
+	if (g_gs_device->ResizeRenderTarget(&m_texture[index], w, h, false, false))
 	{
 		const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[curFramebuffer.PSM];
 		constexpr int pitch = 1024 * 4;