From 778700ff9d6eca96945deebcd4415e70d58330d9 Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Tue, 20 Jul 2021 19:36:38 +0200
Subject: [PATCH] TextureCache: Modify Viewports/Scissors according to Rescale.

---
 src/video_core/dirty_flags.h                  |  3 +-
 .../renderer_vulkan/vk_rasterizer.cpp         | 87 ++++++++++++-------
 .../renderer_vulkan/vk_state_tracker.h        |  6 +-
 src/video_core/texture_cache/texture_cache.h  | 25 +++++-
 .../texture_cache/texture_cache_base.h        |  3 +
 src/yuzu/configuration/config.cpp             |  4 +
 6 files changed, 93 insertions(+), 35 deletions(-)

diff --git a/src/video_core/dirty_flags.h b/src/video_core/dirty_flags.h
index f11ff5d94..d63ad5a35 100644
--- a/src/video_core/dirty_flags.h
+++ b/src/video_core/dirty_flags.h
@@ -29,7 +29,8 @@ enum : u8 {
     ColorBuffer6,
     ColorBuffer7,
     ZetaBuffer,
-    Rescale,
+    RescaleViewports,
+    RescaleScissors,
 
     VertexBuffers,
     VertexBuffer0,
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 30b47a7a0..7a7374b78 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -58,19 +58,14 @@ struct DrawParams {
     bool is_indexed;
 };
 
-VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index) {
+VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t index, float scale) {
     const auto& src = regs.viewport_transform[index];
-    const float width = src.scale_x * 2.0f;
-    float y = src.translate_y - src.scale_y;
-    float height = src.scale_y * 2.0f;
-    if (regs.screen_y_control.y_negate) {
-        y += height;
-        height = -height;
-    }
+    const float width = src.scale_x * 2.0f * scale;
+    const float height = src.scale_y * 2.0f * scale;
     const float reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1.0f : 0.0f;
     VkViewport viewport{
-        .x = src.translate_x - src.scale_x,
-        .y = y,
+        .x = (src.translate_x - src.scale_x) * scale,
+        .y = (src.translate_y - src.scale_y) * scale,
         .width = width != 0.0f ? width : 1.0f,
         .height = height != 0.0f ? height : 1.0f,
         .minDepth = src.translate_z - src.scale_z * reduce_z,
@@ -83,14 +78,21 @@ VkViewport GetViewportState(const Device& device, const Maxwell& regs, size_t in
     return viewport;
 }
 
-VkRect2D GetScissorState(const Maxwell& regs, size_t index) {
+VkRect2D GetScissorState(const Maxwell& regs, size_t index, u32 up_scale = 1, u32 down_shift = 0) {
     const auto& src = regs.scissor_test[index];
     VkRect2D scissor;
+    const auto scale_up = [&](u32 value) -> u32 {
+        if (value == 0) {
+            return 0U;
+        }
+        const u32 converted_value = (value * up_scale) >> down_shift;
+        return std::max<u32>(converted_value, 1U);
+    };
     if (src.enable) {
-        scissor.offset.x = static_cast<s32>(src.min_x);
-        scissor.offset.y = static_cast<s32>(src.min_y);
-        scissor.extent.width = src.max_x - src.min_x;
-        scissor.extent.height = src.max_y - src.min_y;
+        scissor.offset.x = static_cast<s32>(scale_up(src.min_x));
+        scissor.offset.y = static_cast<s32>(scale_up(src.min_y));
+        scissor.extent.width = scale_up(src.max_x - src.min_x);
+        scissor.extent.height = scale_up(src.max_y - src.min_y);
     } else {
         scissor.offset.x = 0;
         scissor.offset.y = 0;
@@ -214,8 +216,15 @@ void RasterizerVulkan::Clear() {
     const VkExtent2D render_area = framebuffer->RenderArea();
     scheduler.RequestRenderpass(framebuffer);
 
+    u32 up_scale = 1;
+    u32 down_shift = 0;
+    if (texture_cache.IsRescaling()) {
+        up_scale = Settings::values.resolution_info.up_scale;
+        down_shift = Settings::values.resolution_info.down_shift;
+    }
+
     VkClearRect clear_rect{
-        .rect = GetScissorState(regs, 0),
+        .rect = GetScissorState(regs, 0, up_scale, down_shift),
         .baseArrayLayer = regs.clear_buffers.layer,
         .layerCount = 1,
     };
@@ -595,15 +604,17 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
     if (!state_tracker.TouchViewports()) {
         return;
     }
+    const float scale =
+        texture_cache.IsRescaling() ? Settings::values.resolution_info.up_factor : 1.0f;
     const std::array viewports{
-        GetViewportState(device, regs, 0),  GetViewportState(device, regs, 1),
-        GetViewportState(device, regs, 2),  GetViewportState(device, regs, 3),
-        GetViewportState(device, regs, 4),  GetViewportState(device, regs, 5),
-        GetViewportState(device, regs, 6),  GetViewportState(device, regs, 7),
-        GetViewportState(device, regs, 8),  GetViewportState(device, regs, 9),
-        GetViewportState(device, regs, 10), GetViewportState(device, regs, 11),
-        GetViewportState(device, regs, 12), GetViewportState(device, regs, 13),
-        GetViewportState(device, regs, 14), GetViewportState(device, regs, 15),
+        GetViewportState(device, regs, 0, scale),  GetViewportState(device, regs, 1, scale),
+        GetViewportState(device, regs, 2, scale),  GetViewportState(device, regs, 3, scale),
+        GetViewportState(device, regs, 4, scale),  GetViewportState(device, regs, 5, scale),
+        GetViewportState(device, regs, 6, scale),  GetViewportState(device, regs, 7, scale),
+        GetViewportState(device, regs, 8, scale),  GetViewportState(device, regs, 9, scale),
+        GetViewportState(device, regs, 10, scale), GetViewportState(device, regs, 11, scale),
+        GetViewportState(device, regs, 12, scale), GetViewportState(device, regs, 13, scale),
+        GetViewportState(device, regs, 14, scale), GetViewportState(device, regs, 15, scale),
     };
     scheduler.Record([viewports](vk::CommandBuffer cmdbuf) { cmdbuf.SetViewport(0, viewports); });
 }
@@ -612,13 +623,29 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
     if (!state_tracker.TouchScissors()) {
         return;
     }
+    u32 up_scale = 1;
+    u32 down_shift = 0;
+    if (texture_cache.IsRescaling()) {
+        up_scale = Settings::values.resolution_info.up_scale;
+        down_shift = Settings::values.resolution_info.down_shift;
+    }
     const std::array scissors{
-        GetScissorState(regs, 0),  GetScissorState(regs, 1),  GetScissorState(regs, 2),
-        GetScissorState(regs, 3),  GetScissorState(regs, 4),  GetScissorState(regs, 5),
-        GetScissorState(regs, 6),  GetScissorState(regs, 7),  GetScissorState(regs, 8),
-        GetScissorState(regs, 9),  GetScissorState(regs, 10), GetScissorState(regs, 11),
-        GetScissorState(regs, 12), GetScissorState(regs, 13), GetScissorState(regs, 14),
-        GetScissorState(regs, 15),
+        GetScissorState(regs, 0, up_scale, down_shift),
+        GetScissorState(regs, 1, up_scale, down_shift),
+        GetScissorState(regs, 2, up_scale, down_shift),
+        GetScissorState(regs, 3, up_scale, down_shift),
+        GetScissorState(regs, 4, up_scale, down_shift),
+        GetScissorState(regs, 5, up_scale, down_shift),
+        GetScissorState(regs, 6, up_scale, down_shift),
+        GetScissorState(regs, 7, up_scale, down_shift),
+        GetScissorState(regs, 8, up_scale, down_shift),
+        GetScissorState(regs, 9, up_scale, down_shift),
+        GetScissorState(regs, 10, up_scale, down_shift),
+        GetScissorState(regs, 11, up_scale, down_shift),
+        GetScissorState(regs, 12, up_scale, down_shift),
+        GetScissorState(regs, 13, up_scale, down_shift),
+        GetScissorState(regs, 14, up_scale, down_shift),
+        GetScissorState(regs, 15, up_scale, down_shift),
     };
     scheduler.Record([scissors](vk::CommandBuffer cmdbuf) { cmdbuf.SetScissor(0, scissors); });
 }
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 2f2d6b31f..ac2bbebe0 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -71,11 +71,13 @@ public:
     }
 
     bool TouchViewports() {
-        return Exchange(Dirty::Viewports, false);
+        return Exchange(Dirty::Viewports, false) ||
+               Exchange(VideoCommon::Dirty::RescaleViewports, false);
     }
 
     bool TouchScissors() {
-        return Exchange(Dirty::Scissors, false);
+        return Exchange(Dirty::Scissors, false) ||
+               Exchange(VideoCommon::Dirty::RescaleScissors, false);
     }
 
     bool TouchDepthBias() {
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index b7d1ae92d..4e5031acc 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -7,6 +7,7 @@
 #include <unordered_set>
 
 #include "common/alignment.h"
+#include "common/settings.h"
 #include "video_core/dirty_flags.h"
 #include "video_core/engines/kepler_compute.h"
 #include "video_core/texture_cache/image_view_base.h"
@@ -205,6 +206,7 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
         return;
     }
 
+    bool rescaled;
     do {
         flags[Dirty::RenderTargets] = false;
 
@@ -243,6 +245,7 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
         check_rescale(render_targets.depth_buffer_id, tmp_depth_image);
 
         if (can_rescale) {
+            rescaled = true;
             const auto scale_up = [this](ImageId image_id) {
                 if (image_id != CORRUPT_ID) {
                     Image& image = slot_images[image_id];
@@ -254,6 +257,7 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
             }
             scale_up(tmp_depth_image);
         } else {
+            rescaled = false;
             const auto scale_down = [this](ImageId image_id) {
                 if (image_id != CORRUPT_ID) {
                     Image& image = slot_images[image_id];
@@ -268,6 +272,12 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
     } while (has_deleted_images);
     // Rescale End
 
+    if (is_rescaling != rescaled) {
+        flags[Dirty::RescaleViewports] = true;
+        flags[Dirty::RescaleScissors] = true;
+        is_rescaling = rescaled;
+    }
+
     for (size_t index = 0; index < NUM_RT; ++index) {
         ImageViewId& color_buffer_id = render_targets.color_buffer_ids[index];
         PrepareImageView(color_buffer_id, true, is_clear && IsFullClear(color_buffer_id));
@@ -279,9 +289,15 @@ void TextureCache<P>::UpdateRenderTargets(bool is_clear) {
     for (size_t index = 0; index < NUM_RT; ++index) {
         render_targets.draw_buffers[index] = static_cast<u8>(maxwell3d.regs.rt_control.Map(index));
     }
+    u32 up_scale = 1;
+    u32 down_shift = 0;
+    if (is_rescaling) {
+        up_scale = Settings::values.resolution_info.up_scale;
+        down_shift = Settings::values.resolution_info.down_shift;
+    }
     render_targets.size = Extent2D{
-        maxwell3d.regs.render_area.width,
-        maxwell3d.regs.render_area.height,
+        (maxwell3d.regs.render_area.width * up_scale) >> down_shift,
+        (maxwell3d.regs.render_area.height * up_scale) >> down_shift,
     };
 
     flags[Dirty::DepthBiasGlobal] = true;
@@ -538,6 +554,11 @@ void TextureCache<P>::PopAsyncFlushes() {
     committed_downloads.pop();
 }
 
+template <class P>
+bool TextureCache<P>::IsRescaling() {
+    return is_rescaling;
+}
+
 template <class P>
 bool TextureCache<P>::IsRegionGpuModified(VAddr addr, size_t size) {
     bool is_modified = false;
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index cdd99242b..1f51fcee8 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -168,6 +168,8 @@ public:
     /// Return true when a CPU region is modified from the GPU
     [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
 
+    [[nodiscard]] bool IsRescaling();
+
     std::mutex mutex;
 
 private:
@@ -362,6 +364,7 @@ private:
     VAddr virtual_invalid_space{};
 
     bool has_deleted_images = false;
+    bool is_rescaling = false;
     u64 total_used_memory = 0;
     u64 minimum_memory;
     u64 expected_memory;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 7ddc40b00..7ed833203 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -849,6 +849,8 @@ void Config::ReadRendererValues() {
         ReadBasicSetting(Settings::values.disable_shader_loop_safety_checks);
     }
 
+    Settings::UpdateRescalingInfo();
+
     qt_config->endGroup();
 }
 
@@ -1402,6 +1404,8 @@ void Config::SaveRendererValues() {
         WriteBasicSetting(Settings::values.disable_shader_loop_safety_checks);
     }
 
+    Settings::UpdateRescalingInfo();
+
     qt_config->endGroup();
 }