From 0b0d3a4ac34c40010db0b8b4ba6e8be53c099d3f Mon Sep 17 00:00:00 2001
From: GPUCode <47210458+GPUCode@users.noreply.github.com>
Date: Mon, 11 Sep 2023 00:30:06 +0300
Subject: [PATCH] gpu: Correct display transfer output with vertical flip+crop
 lines (#6952)

---
 src/core/hw/gpu.cpp                                | 10 +++++++++-
 src/video_core/rasterizer_cache/rasterizer_cache.h |  8 ++++++++
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp
index 6525605da..156fb4b14 100644
--- a/src/core/hw/gpu.cpp
+++ b/src/core/hw/gpu.cpp
@@ -128,7 +128,7 @@ static void MemoryFill(const Regs::MemoryFillConfig& config) {
 
 static void DisplayTransfer(const Regs::DisplayTransferConfig& config) {
     const PAddr src_addr = config.GetPhysicalInputAddress();
-    const PAddr dst_addr = config.GetPhysicalOutputAddress();
+    PAddr dst_addr = config.GetPhysicalOutputAddress();
 
     // TODO: do hwtest with these cases
     if (!g_memory->IsValidPhysicalAddress(src_addr)) {
@@ -164,6 +164,14 @@ static void DisplayTransfer(const Regs::DisplayTransferConfig& config) {
     if (VideoCore::g_renderer->Rasterizer()->AccelerateDisplayTransfer(config))
         return;
 
+    // Using flip_vertically alongside crop_input_lines produces skewed output on hardware.
+    // We have to emulate this because some games rely on this behaviour to render correctly.
+    if (config.flip_vertically && config.crop_input_lines &&
+        config.input_width > config.output_width) {
+        dst_addr += (config.input_width - config.output_width) * (config.output_height - 1) *
+                    GPU::Regs::BytesPerPixel(config.output_format);
+    }
+
     u8* src_pointer = g_memory->GetPhysicalPointer(src_addr);
     u8* dst_pointer = g_memory->GetPhysicalPointer(dst_addr);
 
diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.h b/src/video_core/rasterizer_cache/rasterizer_cache.h
index 37abcae14..cebf54377 100644
--- a/src/video_core/rasterizer_cache/rasterizer_cache.h
+++ b/src/video_core/rasterizer_cache/rasterizer_cache.h
@@ -272,6 +272,14 @@ bool RasterizerCache<T>::AccelerateDisplayTransfer(const GPU::Regs::DisplayTrans
     dst_params.pixel_format = PixelFormatFromGPUPixelFormat(config.output_format);
     dst_params.UpdateParams();
 
+    // Using flip_vertically alongside crop_input_lines produces skewed output on hardware.
+    // We have to emulate this because some games rely on this behaviour to render correctly.
+    if (config.flip_vertically && config.crop_input_lines &&
+        config.input_width > config.output_width) {
+        dst_params.addr += (config.input_width - config.output_width) * (config.output_height - 1) *
+                           GPU::Regs::BytesPerPixel(config.output_format);
+    }
+
     auto [src_surface_id, src_rect] = GetSurfaceSubRect(src_params, ScaleMatch::Ignore, true);
     if (!src_surface_id) {
         return false;