From 7f1aec8fbb96b5c29c91332250eef60dff4c8f12 Mon Sep 17 00:00:00 2001
From: Phantom <phanto.m@free.fr>
Date: Sat, 30 Dec 2017 07:42:32 +0100
Subject: [PATCH] Support for textures smaller than 8*8

---
 .../renderer_opengl/gl_rasterizer_cache.cpp   | 89 +++++++++++--------
 .../renderer_opengl/gl_rasterizer_cache.h     |  1 +
 2 files changed, 55 insertions(+), 35 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index aa3009b5b..066a58085 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -449,27 +449,18 @@ MathUtil::Rectangle<u32> SurfaceParams::GetScaledSubRect(const SurfaceParams& su
 bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const {
     return (other_surface.addr == addr && other_surface.width == width &&
             other_surface.height == height && other_surface.stride == stride &&
-            other_surface.pixel_format == pixel_format && other_surface.is_tiled == is_tiled);
+            other_surface.pixel_format == pixel_format && pixel_format != PixelFormat::Invalid &&
+            other_surface.is_tiled == is_tiled);
 }
 
 bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const {
-    if (sub_surface.addr < addr || sub_surface.end > end || sub_surface.stride != stride ||
-        sub_surface.pixel_format != pixel_format || sub_surface.is_tiled != is_tiled ||
-        (sub_surface.addr - addr) * 8 % GetFormatBpp() != 0)
-        return false;
-
-    auto rect = GetSubRect(sub_surface);
-
-    if (rect.left + sub_surface.width > stride) {
-        return false;
-    }
-
-    if (is_tiled) {
-        return PixelsInBytes(sub_surface.addr - addr) % 64 == 0 && sub_surface.height % 8 == 0 &&
-               sub_surface.width % 8 == 0;
-    }
-
-    return true;
+    return (sub_surface.addr >= addr && sub_surface.end <= end &&
+            sub_surface.pixel_format == pixel_format && pixel_format != PixelFormat::Invalid &&
+            sub_surface.is_tiled == is_tiled &&
+            (sub_surface.addr - addr) * 8 % GetFormatBpp() == 0 &&
+            (!is_tiled || PixelsInBytes(sub_surface.addr - addr) % 64 == 0) &&
+            (sub_surface.stride == stride || sub_surface.height <= (is_tiled ? 8u : 1u)) &&
+            GetSubRect(sub_surface).left + sub_surface.width <= stride);
 }
 
 bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const {
@@ -1006,15 +997,6 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatc
             if (expandable != nullptr && expandable->res_scale > target_res_scale) {
                 target_res_scale = expandable->res_scale;
             }
-            // Keep res_scale when reinterpreting d24s8 -> rgba8
-            if (params.pixel_format == PixelFormat::RGBA8) {
-                find_params.pixel_format = PixelFormat::D24S8;
-                expandable = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(
-                    surface_cache, find_params, match_res_scale);
-                if (expandable != nullptr && expandable->res_scale > target_res_scale) {
-                    target_res_scale = expandable->res_scale;
-                }
-            }
         }
         SurfaceParams new_params = params;
         new_params.res_scale = target_res_scale;
@@ -1057,17 +1039,29 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams&
         }
     }
 
+    SurfaceParams aligned_params = params;
+    if (params.is_tiled) {
+        aligned_params.height = Common::AlignUp(params.height, 8);
+        aligned_params.width = Common::AlignUp(params.width, 8);
+        aligned_params.stride = Common::AlignUp(params.stride, 8);
+        aligned_params.UpdateParams();
+    }
+
     // Check for a surface we can expand before creating a new one
     if (surface == nullptr) {
-        surface = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(surface_cache, params,
+        surface = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(surface_cache, aligned_params,
                                                                       match_res_scale);
         if (surface != nullptr) {
+            aligned_params.width = aligned_params.stride;
+            aligned_params.UpdateParams();
+
             SurfaceParams new_params = *surface;
-            new_params.addr = std::min(params.addr, surface->addr);
-            new_params.end = std::max(params.end, surface->end);
+            new_params.addr = std::min(aligned_params.addr, surface->addr);
+            new_params.end = std::max(aligned_params.end, surface->end);
             new_params.size = new_params.end - new_params.addr;
-            new_params.height = new_params.size / params.BytesInPixels(params.stride);
-            ASSERT(new_params.size % params.BytesInPixels(params.stride) == 0);
+            new_params.height =
+                new_params.size / aligned_params.BytesInPixels(aligned_params.stride);
+            ASSERT(new_params.size % aligned_params.BytesInPixels(aligned_params.stride) == 0);
 
             Surface new_surface = CreateSurface(new_params);
             DuplicateSurface(surface, new_surface);
@@ -1083,14 +1077,14 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams&
 
     // No subrect found - create and return a new surface
     if (surface == nullptr) {
-        SurfaceParams new_params = params;
+        SurfaceParams new_params = aligned_params;
         // Can't have gaps in a surface
-        new_params.width = params.stride;
+        new_params.width = aligned_params.stride;
         new_params.UpdateParams();
         // GetSurface will create the new surface and possibly adjust res_scale if necessary
         surface = GetSurface(new_params, match_res_scale, load_if_create);
     } else if (load_if_create) {
-        ValidateSurface(surface, params.addr, params.size);
+        ValidateSurface(surface, aligned_params.addr, aligned_params.size);
     }
 
     return std::make_tuple(surface, surface->GetScaledSubRect(params));
@@ -1108,6 +1102,23 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(
     params.is_tiled = true;
     params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(info.format);
     params.UpdateParams();
+
+    if (info.width % 8 != 0 || info.height % 8 != 0) {
+        Surface src_surface;
+        MathUtil::Rectangle<u32> rect;
+        std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true);
+
+        params.res_scale = src_surface->res_scale;
+        Surface tmp_surface = CreateSurface(params);
+        BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle,
+                     tmp_surface->GetScaledRect(),
+                     SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle,
+                     draw_framebuffer.handle);
+
+        remove_surfaces.emplace(tmp_surface);
+        return tmp_surface;
+    }
+
     return GetSurface(params, ScaleMatch::Ignore, true);
 }
 
@@ -1422,11 +1433,19 @@ Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) {
 }
 
 void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) {
+    if (surface->registered) {
+        return;
+    }
+    surface->registered = true;
     surface_cache.add({surface->GetInterval(), SurfaceSet{surface}});
     UpdatePagesCachedCount(surface->addr, surface->size, 1);
 }
 
 void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) {
+    if (!surface->registered) {
+        return;
+    }
+    surface->registered = false;
     UpdatePagesCachedCount(surface->addr, surface->size, -1);
     surface_cache.subtract({surface->GetInterval(), SurfaceSet{surface}});
 }
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index deb091317..d1f739a78 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -265,6 +265,7 @@ struct CachedSurface : SurfaceParams {
         return (invalid_regions & GetInterval()) == SurfaceRegions(GetInterval());
     }
 
+    bool registered = false;
     SurfaceRegions invalid_regions;
 
     u32 fill_size = 0; /// Number of bytes to read from fill_data