From de66a69ed4b556ad96f38570ea0a31cb2a1870f1 Mon Sep 17 00:00:00 2001
From: ameerj <52414509+ameerj@users.noreply.github.com>
Date: Wed, 21 Jul 2021 00:02:35 -0400
Subject: [PATCH] renderer_gl: Resolution scaling fixes

---
 .../renderer_opengl/gl_rasterizer.cpp         |  23 ++-
 .../renderer_opengl/gl_texture_cache.cpp      | 136 ++++++++++--------
 .../renderer_opengl/gl_texture_cache.h        |   9 +-
 3 files changed, 107 insertions(+), 61 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a6d9f7c43..b91e7edf8 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -577,6 +577,14 @@ void RasterizerOpenGL::SyncViewport() {
         const bool force = flags[Dirty::ViewportTransform];
         flags[Dirty::ViewportTransform] = false;
 
+        const auto& resolution = Settings::values.resolution_info;
+        const auto scale_up = [&](u32 value) -> u32 {
+            if (value == 0) {
+                return 0U;
+            }
+            const u32 converted_value = (value * resolution.up_scale) >> resolution.down_shift;
+            return std::max<u32>(converted_value, 1U);
+        };
         for (std::size_t i = 0; i < Maxwell::NumViewports; ++i) {
             if (!force && !flags[Dirty::Viewport0 + i]) {
                 continue;
@@ -585,8 +593,8 @@ void RasterizerOpenGL::SyncViewport() {
 
             const auto& src = regs.viewport_transform[i];
             const Common::Rectangle<f32> rect{src.GetRect()};
-            glViewportIndexedf(static_cast<GLuint>(i), rect.left, rect.bottom, rect.GetWidth(),
-                               rect.GetHeight());
+            glViewportIndexedf(static_cast<GLuint>(i), rect.left, rect.bottom,
+                               scale_up(rect.GetWidth()), scale_up(rect.GetHeight()));
 
             const GLdouble reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne;
             const GLdouble near_depth = src.translate_z - src.scale_z * reduce_z;
@@ -909,6 +917,15 @@ void RasterizerOpenGL::SyncScissorTest() {
     flags[Dirty::Scissors] = false;
 
     const auto& regs = maxwell3d.regs;
+
+    const auto& resolution = Settings::values.resolution_info;
+    const auto scale_up = [&](u32 value) -> u32 {
+        if (value == 0) {
+            return 0U;
+        }
+        const u32 converted_value = (value * resolution.up_scale) >> resolution.down_shift;
+        return std::max<u32>(converted_value, 1U);
+    };
     for (std::size_t index = 0; index < Maxwell::NumViewports; ++index) {
         if (!flags[Dirty::Scissor0 + index]) {
             continue;
@@ -919,7 +936,7 @@ void RasterizerOpenGL::SyncScissorTest() {
         if (src.enable) {
             glEnablei(GL_SCISSOR_TEST, static_cast<GLuint>(index));
             glScissorIndexed(static_cast<GLuint>(index), src.min_x, src.min_y,
-                             src.max_x - src.min_x, src.max_y - src.min_y);
+                             scale_up(src.max_x - src.min_x), scale_up(src.max_y - src.min_y));
         } else {
             glDisablei(GL_SCISSOR_TEST, static_cast<GLuint>(index));
         }
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 2d9f770cd..ecde5b600 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -316,6 +316,52 @@ void AttachTexture(GLuint fbo, GLenum attachment, const ImageView* image_view) {
     }
 }
 
+OGLTexture MakeImage(const VideoCommon::ImageInfo& info, GLenum gl_internal_format) {
+    const GLenum target = ImageTarget(info);
+    const GLsizei width = info.size.width;
+    const GLsizei height = info.size.height;
+    const GLsizei depth = info.size.depth;
+    const int max_host_mip_levels = std::bit_width(info.size.width);
+    const GLsizei num_levels = std::min(info.resources.levels, max_host_mip_levels);
+    const GLsizei num_layers = info.resources.layers;
+    const GLsizei num_samples = info.num_samples;
+
+    GLuint handle = 0;
+    OGLTexture texture;
+    if (target != GL_TEXTURE_BUFFER) {
+        texture.Create(target);
+        handle = texture.handle;
+    }
+    switch (target) {
+    case GL_TEXTURE_1D_ARRAY:
+        glTextureStorage2D(handle, num_levels, gl_internal_format, width, num_layers);
+        break;
+    case GL_TEXTURE_2D_ARRAY:
+        glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, num_layers);
+        break;
+    case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: {
+        // TODO: Where should 'fixedsamplelocations' come from?
+        const auto [samples_x, samples_y] = SamplesLog2(info.num_samples);
+        glTextureStorage3DMultisample(handle, num_samples, gl_internal_format, width >> samples_x,
+                                      height >> samples_y, num_layers, GL_FALSE);
+        break;
+    }
+    case GL_TEXTURE_RECTANGLE:
+        glTextureStorage2D(handle, num_levels, gl_internal_format, width, height);
+        break;
+    case GL_TEXTURE_3D:
+        glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, depth);
+        break;
+    case GL_TEXTURE_BUFFER:
+        UNREACHABLE();
+        break;
+    default:
+        UNREACHABLE_MSG("Invalid target=0x{:x}", target);
+        break;
+    }
+    return texture;
+}
+
 [[nodiscard]] bool IsPixelFormatBGR(PixelFormat format) {
     switch (format) {
     case PixelFormat::B5G6R5_UNORM:
@@ -430,6 +476,11 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager&
 
 TextureCacheRuntime::~TextureCacheRuntime() = default;
 
+void TextureCacheRuntime::Init() {
+    resolution = Settings::values.resolution_info;
+    is_rescaling_on = resolution.up_scale != 1 || resolution.down_shift != 0;
+}
+
 void TextureCacheRuntime::Finish() {
     glFinish();
 }
@@ -605,13 +656,13 @@ std::optional<size_t> TextureCacheRuntime::StagingBuffers::FindBuffer(size_t req
     return found;
 }
 
-Image::Image(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info_, GPUVAddr gpu_addr_,
+Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_, GPUVAddr gpu_addr_,
              VAddr cpu_addr_)
-    : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_) {
-    if (CanBeAccelerated(runtime, info)) {
+    : VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), runtime{&runtime_} {
+    if (CanBeAccelerated(*runtime, info)) {
         flags |= ImageFlagBits::AcceleratedUpload;
     }
-    if (IsConverted(runtime.device, info.format, info.type)) {
+    if (IsConverted(runtime->device, info.format, info.type)) {
         flags |= ImageFlagBits::Converted;
         gl_internal_format = IsPixelFormatSRGB(info.format) ? GL_SRGB8_ALPHA8 : GL_RGBA8;
         gl_format = GL_RGBA;
@@ -622,51 +673,11 @@ Image::Image(TextureCacheRuntime& runtime, const VideoCommon::ImageInfo& info_,
         gl_format = tuple.format;
         gl_type = tuple.type;
     }
-    const GLenum target = ImageTarget(info);
-    const GLsizei width = info.size.width;
-    const GLsizei height = info.size.height;
-    const GLsizei depth = info.size.depth;
-    const int max_host_mip_levels = std::bit_width(info.size.width);
-    const GLsizei num_levels = std::min(info.resources.levels, max_host_mip_levels);
-    const GLsizei num_layers = info.resources.layers;
-    const GLsizei num_samples = info.num_samples;
-
-    GLuint handle = 0;
-    if (target != GL_TEXTURE_BUFFER) {
-        texture.Create(target);
-        handle = texture.handle;
-    }
-    switch (target) {
-    case GL_TEXTURE_1D_ARRAY:
-        glTextureStorage2D(handle, num_levels, gl_internal_format, width, num_layers);
-        break;
-    case GL_TEXTURE_2D_ARRAY:
-        glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, num_layers);
-        break;
-    case GL_TEXTURE_2D_MULTISAMPLE_ARRAY: {
-        // TODO: Where should 'fixedsamplelocations' come from?
-        const auto [samples_x, samples_y] = SamplesLog2(info.num_samples);
-        glTextureStorage3DMultisample(handle, num_samples, gl_internal_format, width >> samples_x,
-                                      height >> samples_y, num_layers, GL_FALSE);
-        break;
-    }
-    case GL_TEXTURE_RECTANGLE:
-        glTextureStorage2D(handle, num_levels, gl_internal_format, width, height);
-        break;
-    case GL_TEXTURE_3D:
-        glTextureStorage3D(handle, num_levels, gl_internal_format, width, height, depth);
-        break;
-    case GL_TEXTURE_BUFFER:
-        UNREACHABLE();
-        break;
-    default:
-        UNREACHABLE_MSG("Invalid target=0x{:x}", target);
-        break;
-    }
-    if (runtime.device.HasDebuggingToolAttached()) {
+    texture = MakeImage(info, gl_internal_format);
+    if (runtime->device.HasDebuggingToolAttached()) {
         const std::string name = VideoCommon::Name(*this);
-        glObjectLabel(target == GL_TEXTURE_BUFFER ? GL_BUFFER : GL_TEXTURE, handle,
-                      static_cast<GLsizei>(name.size()), name.data());
+        glObjectLabel(ImageTarget(info) == GL_TEXTURE_BUFFER ? GL_BUFFER : GL_TEXTURE,
+                      texture.handle, static_cast<GLsizei>(name.size()), name.data());
     }
 }
 
@@ -855,7 +866,7 @@ void Image::CopyImageToBuffer(const VideoCommon::BufferImageCopy& copy, size_t b
     }
 }
 
-void Image::Scale() {
+void Image::Scale(u32 up, u32 down) {
     // TODO: Pass scaling factor?
     if (gl_format == 0 || gl_type == 0) {
         // compressed textures
@@ -902,12 +913,22 @@ void Image::Scale() {
     glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_handle);
     glNamedFramebufferTexture(fbo_handle, attachment, texture.handle, 0);
 
-    const size_t scaled_width = info.size.width;
-    const size_t scaled_height = info.size.height * 2;
-    glBlitNamedFramebuffer(fbo_handle, fbo_handle, 0, 0, info.size.width, info.size.height, 0, 0,
+    const auto scale_up = [&](u32 value) { return std::max<u32>((value * up) >> down, 1U); };
+    const u32 scaled_width = scale_up(info.size.width);
+    const u32 scaled_height = scale_up(info.size.height);
+    const u32 original_width = info.size.width;
+    const u32 original_height = info.size.height;
+
+    auto scaled_info = info;
+    scaled_info.size.width = scaled_width;
+    scaled_info.size.height = scaled_height;
+    auto scaled_texture = MakeImage(scaled_info, gl_internal_format);
+
+    glBlitNamedFramebuffer(fbo_handle, fbo_handle, 0, 0, original_width, original_height, 0, 0,
                            scaled_width, scaled_height, mask, filter);
-    // TODO: resize texture?
-    glCopyTextureSubImage3D(texture.handle, 0, 0, 0, 0, 0, 0, scaled_width, scaled_height / 2);
+    glCopyTextureSubImage3D(scaled_texture.handle, 0, 0, 0, 0, 0, 0, scaled_width, scaled_height);
+    texture = std::move(scaled_texture);
+
     // Restore previous framebuffers
     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, prev_draw_fbo);
     glBindFramebuffer(GL_READ_FRAMEBUFFER, prev_read_fbo);
@@ -918,7 +939,8 @@ bool Image::ScaleUp() {
         return false;
     }
     flags |= ImageFlagBits::Rescaled;
-    //Scale();
+    const auto& resolution = runtime->resolution;
+    Scale(resolution.up_scale, resolution.down_shift);
     return true;
 }
 
@@ -927,7 +949,9 @@ bool Image::ScaleDown() {
         return false;
     }
     flags &= ~ImageFlagBits::Rescaled;
-    //Scale();
+    UNIMPLEMENTED();
+    // const auto& resolution = runtime->resolution;
+    // Scale();
     return true;
 }
 
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 324a0f1cb..77ca14132 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -9,6 +9,7 @@
 
 #include <glad/glad.h>
 
+#include "common/settings.h"
 #include "shader_recompiler/shader_info.h"
 #include "video_core/renderer_opengl/gl_resource_manager.h"
 #include "video_core/renderer_opengl/util_shaders.h"
@@ -72,7 +73,7 @@ public:
                                  StateTracker& state_tracker);
     ~TextureCacheRuntime();
 
-    void Init() {}
+    void Init();
 
     void Finish();
 
@@ -153,6 +154,9 @@ private:
     OGLTextureView null_image_view_cube;
 
     std::array<GLuint, Shader::NUM_TEXTURE_TYPES> null_image_views{};
+
+    Settings::ResolutionScalingInfo resolution;
+    bool is_rescaling_on{};
 };
 
 class Image : public VideoCommon::ImageBase {
@@ -198,13 +202,14 @@ private:
 
     void CopyImageToBuffer(const VideoCommon::BufferImageCopy& copy, size_t buffer_offset);
 
-    void Scale();
+    void Scale(u32 up, u32 down);
 
     OGLTexture texture;
     OGLTextureView store_view;
     GLenum gl_internal_format = GL_NONE;
     GLenum gl_format = GL_NONE;
     GLenum gl_type = GL_NONE;
+    TextureCacheRuntime* runtime;
 };
 
 class ImageView : public VideoCommon::ImageViewBase {