From bc0f1896fc1092bdc66fb66f977163de08672f01 Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Mon, 23 Apr 2018 23:45:47 -0400
Subject: [PATCH] gl_rasterizer_cache: Handle compressed texture sizes.

---
 .../renderer_opengl/gl_rasterizer_cache.cpp   | 37 ++++++-------
 .../renderer_opengl/gl_rasterizer_cache.h     | 52 +++++++++++++++++--
 2 files changed, 65 insertions(+), 24 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index d139d51e92..e1ad00feb8 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -41,18 +41,15 @@ struct FormatTuple {
     GLenum format;
     GLenum type;
     bool compressed;
-    // How many pixels in the original texture are equivalent to one pixel in the compressed
-    // texture.
-    u32 compression_factor;
 };
 
 static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{
-    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false, 1},                     // ABGR8
-    {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, false, 1},                        // B5G6R5
-    {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false, 1},               // A2B10G10R10
-    {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true, 16},   // DXT1
-    {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT23
-    {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT45
+    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false},                    // ABGR8
+    {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, false},                       // B5G6R5
+    {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false},              // A2B10G10R10
+    {GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true},   // DXT1
+    {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23
+    {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45
 }};
 
 static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
@@ -476,7 +473,7 @@ void CachedSurface::LoadGLBuffer(Tegra::GPUVAddr load_start, Tegra::GPUVAddr loa
         return;
 
     if (gl_buffer == nullptr) {
-        gl_buffer_size = width * height * GetGLBytesPerPixel(pixel_format);
+        gl_buffer_size = GetActualWidth() * GetActualHeight() * GetGLBytesPerPixel(pixel_format);
         gl_buffer.reset(new u8[gl_buffer_size]);
     }
 
@@ -491,8 +488,9 @@ void CachedSurface::LoadGLBuffer(Tegra::GPUVAddr load_start, Tegra::GPUVAddr loa
         std::memcpy(&gl_buffer[start_offset], texture_src_data + start_offset,
                     bytes_per_pixel * width * height);
     } else {
-        morton_to_gl_fns[static_cast<size_t>(pixel_format)](
-            stride, block_height, height, &gl_buffer[0], addr, load_start, load_end);
+        morton_to_gl_fns[static_cast<size_t>(pixel_format)](GetActualWidth(), block_height,
+                                                            GetActualHeight(), &gl_buffer[0], addr,
+                                                            load_start, load_end);
     }
 }
 
@@ -548,7 +546,8 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint
 
     MICROPROFILE_SCOPE(OpenGL_TextureUL);
 
-    ASSERT(gl_buffer_size == width * height * GetGLBytesPerPixel(pixel_format));
+    ASSERT(gl_buffer_size ==
+           GetActualWidth() * GetActualHeight() * GetGLBytesPerPixel(pixel_format));
 
     // Load data from memory to the surface
     GLint x0 = static_cast<GLint>(rect.left);
@@ -583,11 +582,9 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint
     glActiveTexture(GL_TEXTURE0);
     if (tuple.compressed) {
         glCompressedTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format,
-                               static_cast<GLsizei>(rect.GetWidth()),
-                               static_cast<GLsizei>(rect.GetHeight()), 0,
-                               rect.GetWidth() * rect.GetHeight() *
-                                   GetGLBytesPerPixel(pixel_format) / tuple.compression_factor,
-                               &gl_buffer[buffer_offset]);
+                               static_cast<GLsizei>(rect.GetWidth() * GetCompresssionFactor()),
+                               static_cast<GLsizei>(rect.GetHeight() * GetCompresssionFactor()), 0,
+                               size, &gl_buffer[buffer_offset]);
     } else {
         glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
                         static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
@@ -1041,10 +1038,10 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
 
     SurfaceParams params;
     params.addr = config.tic.Address();
-    params.width = config.tic.Width();
-    params.height = config.tic.Height();
     params.is_tiled = config.tic.IsTiled();
     params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(config.tic.format);
+    params.width = config.tic.Width() / params.GetCompresssionFactor();
+    params.height = config.tic.Height() / params.GetCompresssionFactor();
 
     // TODO(Subv): Different types per component are not supported.
     ASSERT(config.tic.r_type.Value() == config.tic.g_type.Value() &&
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 5f77f4e610..08858bab43 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -84,23 +84,49 @@ struct SurfaceParams {
         Invalid = 4,
     };
 
-    static constexpr unsigned int GetFormatBpp(PixelFormat format) {
+    /**
+     * Gets the compression factor for the specified PixelFormat. This applies to just the
+     * "compressed width" and "compressed height", not the overall compression factor of a
+     * compressed image. This is used for maintaining proper surface sizes for compressed texture
+     * formats.
+     */
+    static constexpr u32 GetCompresssionFactor(PixelFormat format) {
         if (format == PixelFormat::Invalid)
             return 0;
 
-        constexpr std::array<unsigned int, MaxPixelFormat> bpp_table = {
+        constexpr std::array<u32, MaxPixelFormat> compression_factor_table = {{
+            1, // ABGR8
+            1, // B5G6R5
+            1, // A2B10G10R10
+            4, // DXT1
+            4, // DXT23
+            4, // DXT45
+        }};
+
+        ASSERT(static_cast<size_t>(format) < compression_factor_table.size());
+        return compression_factor_table[static_cast<size_t>(format)];
+    }
+    u32 GetCompresssionFactor() const {
+        return GetCompresssionFactor(pixel_format);
+    }
+
+    static constexpr u32 GetFormatBpp(PixelFormat format) {
+        if (format == PixelFormat::Invalid)
+            return 0;
+
+        constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
             32,  // ABGR8
             16,  // B5G6R5
             32,  // A2B10G10R10
             64,  // DXT1
             128, // DXT23
             128, // DXT45
-        };
+        }};
 
         ASSERT(static_cast<size_t>(format) < bpp_table.size());
         return bpp_table[static_cast<size_t>(format)];
     }
-    unsigned int GetFormatBpp() const {
+    u32 GetFormatBpp() const {
         return GetFormatBpp(pixel_format);
     }
 
@@ -255,6 +281,24 @@ struct SurfaceParams {
     // Returns the region of the biggest valid rectange within interval
     SurfaceInterval GetCopyableInterval(const Surface& src_surface) const;
 
+    /**
+     * Gets the actual width (in pixels) of the surface. This is provided because `width` is used
+     * for tracking the surface region in memory, which may be compressed for certain formats. In
+     * this scenario, `width` is actually the compressed width.
+     */
+    u32 GetActualWidth() const {
+        return width * GetCompresssionFactor();
+    }
+
+    /**
+     * Gets the actual height (in pixels) of the surface. This is provided because `height` is used
+     * for tracking the surface region in memory, which may be compressed for certain formats. In
+     * this scenario, `height` is actually the compressed height.
+     */
+    u32 GetActualHeight() const {
+        return height * GetCompresssionFactor();
+    }
+
     u32 GetScaledWidth() const {
         return width * res_scale;
     }
-- 
GitLab