From d45964d0c7906c4078424c845a2c4e2bb0208b13 Mon Sep 17 00:00:00 2001
From: refractionpcsx2 <refraction@gmail.com>
Date: Sat, 25 Mar 2023 00:57:45 +0000
Subject: [PATCH] GS-HW: improve dirty handling with expanded tex is rt's

---
 bin/resources/GameIndex.yaml             | 10 ++++++
 pcsx2/GS/Renderers/HW/GSTextureCache.cpp | 40 ++++++++++++++----------
 2 files changed, 33 insertions(+), 17 deletions(-)

diff --git a/bin/resources/GameIndex.yaml b/bin/resources/GameIndex.yaml
index 5bfd73b436..3aca5cfe0b 100644
--- a/bin/resources/GameIndex.yaml
+++ b/bin/resources/GameIndex.yaml
@@ -11114,6 +11114,8 @@ SLES-50677:
   compat: 5
   clampModes:
     eeClampMode: 3 # Fixes invisible characters in various scenes.
+  gsHWFixes:
+    partialTargetInvalidation: 1 # Fixes black screens on judgement wheel.
 SLES-50679:
   name: "Tenchu 3 - Wrath of Heaven"
   region: "PAL-E"
@@ -11485,6 +11487,8 @@ SLES-50822:
   region: "PAL-M3"
   clampModes:
     eeClampMode: 3 # Fixes invisible characters in various scenes.
+  gsHWFixes:
+    partialTargetInvalidation: 1 # Fixes black screens on judgement wheel.
 SLES-50826:
   name: "Star Wars - Clone Wars"
   region: "PAL-E"
@@ -37532,6 +37536,8 @@ SLPS-25041:
   region: "NTSC-J"
   clampModes:
     eeClampMode: 3 # Fixes invisible characters in various scenes.
+  gsHWFixes:
+    partialTargetInvalidation: 1 # Fixes black screens on judgement wheel.
 SLPS-25042:
   name: "Maken Shao [Limited Edition]"
   region: "NTSC-J"
@@ -41400,6 +41406,8 @@ SLPS-73418:
   region: "NTSC-J"
   clampModes:
     eeClampMode: 3 # Fixes invisible characters in various scenes.
+  gsHWFixes:
+    partialTargetInvalidation: 1 # Fixes black screens on judgement wheel.
 SLPS-73419:
   name: "Lupin III - Majutsu-Ou no Isan [PlayStation 2 The Best]"
   region: "NTSC-J"
@@ -42878,6 +42886,8 @@ SLUS-20347:
   compat: 5
   clampModes:
     eeClampMode: 3 # Fixes invisible characters in various scenes.
+  gsHWFixes:
+    partialTargetInvalidation: 1 # Fixes black screens on judgement wheel.
 SLUS-20348:
   name: "Monopoly Party"
   region: "NTSC-U"
diff --git a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp
index 98d0d5cf1c..7bed04a4d7 100644
--- a/pcsx2/GS/Renderers/HW/GSTextureCache.cpp
+++ b/pcsx2/GS/Renderers/HW/GSTextureCache.cpp
@@ -817,8 +817,30 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
 
 					const u32 channel_mask = GSUtil::GetChannelMask(psm);
 					const u32 channels = t->m_dirty.GetDirtyChannels() & channel_mask;
+
+					// If the source is reading the rt, make sure it's big enough.
+					if (t && GSUtil::HasCompatibleBits(psm, t->m_TEX0.PSM))
+					{
+						GSVector4i dirty_rect = t->m_dirty.GetTotalRect(t->m_TEX0, GSVector2i(new_rect.z, new_rect.w));
+						const GSVector2i size_delta = { (dirty_rect.z - t->m_valid.z), (dirty_rect.w - t->m_valid.w) };
+						if (size_delta.x > 0 || size_delta.y > 0)
+						{
+							RGBAMask rgba;
+							rgba._u32 = GSUtil::GetChannelMask(t->m_TEX0.PSM);
+							// Dirty the expanded areas.
+							AddDirtyRectTarget(t, GSVector4i(t->m_valid.x, t->m_valid.w, t->m_valid.z + std::max(0, size_delta.x), t->m_valid.w + std::max(0, size_delta.y)), t->m_TEX0.PSM, t->m_TEX0.TBW, rgba);
+							AddDirtyRectTarget(t, GSVector4i(t->m_valid.z, t->m_valid.y, t->m_valid.z + std::max(0, size_delta.x), t->m_valid.w + std::max(0, size_delta.y)), t->m_TEX0.PSM, t->m_TEX0.TBW, rgba);
+							const GSVector4i valid_rect = { t->m_valid.x, t->m_valid.y, t->m_valid.z + std::max(0, size_delta.x), t->m_valid.w + std::max(0, size_delta.y) };
+							t->UpdateValidity(valid_rect);
+							t->UpdateValidBits(GSLocalMemory::m_psm[t->m_TEX0.PSM].fmsk);
+							GetTargetHeight(TEX0.TBP0, TEX0.TBW, TEX0.PSM, t->m_valid.w);
+							const int new_w = std::max(t->m_unscaled_size.x, t->m_valid.z);
+							const int new_h = std::max(t->m_unscaled_size.y, t->m_valid.w);
+							t->ResizeTexture(new_w, new_h);
+						}
+					}
 					// If not all channels are clean/dirty or only part of the rect is dirty, we need to update the target.
-					if (((channels & channel_mask) != channel_mask || partial) && !rect_clean)
+					if (((channels & channel_mask) != channel_mask || partial))
 						t->Update(false);
 				}
 				else
@@ -992,22 +1014,6 @@ GSTextureCache::Source* GSTextureCache::LookupSource(const GIFRegTEX0& TEX0, con
 			}
 		}
 
-		// If the source is reading the rt, make sure it's big enough.
-		
-		if (dst && GSUtil::HasCompatibleBits(psm, dst->m_TEX0.PSM))
-		{
-			const GSVector2i size_delta = { (r.z - dst->m_valid.z), (r.w - dst->m_valid.w) };
-
-			if (size_delta.x > 0 || size_delta.y > 0)
-			{
-				const GSVector4i valid_rect = { dst->m_valid.x, dst->m_valid.y, dst->m_valid.z + std::max(0, size_delta.x), dst->m_valid.w + std::max(0, size_delta.y) };
-				dst->UpdateValidity(valid_rect);
-
-				const int new_w = std::max(dst->m_unscaled_size.x, dst->m_valid.z);
-				const int new_h = std::max(dst->m_unscaled_size.y, dst->m_valid.w);
-				dst->ResizeTexture(new_w, new_h);
-			}
-		}
 		// Pure depth texture format will be fetched by LookupDepthSource.
 		// However guess what, some games (GoW) read the depth as a standard
 		// color format (instead of a depth format). All pixels are scrambled