From 27ddbeb01cba488e2b59409a0375f6c98b431102 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 21 Feb 2019 01:11:49 -0300
Subject: [PATCH] gl_rasterizer_cache: Create texture views for array
 discrepancies

When a texture is sampled in a shader with a different array mode than
the cached state, create a texture view and bind that to the shader
instead.
---
 .../renderer_opengl/gl_rasterizer.cpp         |  5 +--
 .../renderer_opengl/gl_rasterizer_cache.cpp   | 41 +++++++++++++++----
 .../renderer_opengl/gl_rasterizer_cache.h     | 28 ++++---------
 3 files changed, 42 insertions(+), 32 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 09fa01d25..406d720a0 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1012,10 +1012,9 @@ void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& s
 
         texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
 
-        Surface surface = res_cache.GetTextureSurface(texture, entry);
-        if (surface != nullptr) {
+        if (Surface surface = res_cache.GetTextureSurface(texture, entry); surface) {
             state.texture_units[current_bindpoint].texture =
-                entry.IsArray() ? surface->TextureLayer().handle : surface->Texture().handle;
+                surface->Texture(entry.IsArray()).handle;
             surface->UpdateSwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
                                    texture.tic.w_source);
         } else {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 49c79811d..837523b82 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -399,6 +399,27 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
     return format;
 }
 
+/// Returns the discrepant array target
+constexpr GLenum GetArrayDiscrepantTarget(SurfaceTarget target) {
+    switch (target) {
+    case SurfaceTarget::Texture1D:
+        return GL_TEXTURE_1D_ARRAY;
+    case SurfaceTarget::Texture2D:
+        return GL_TEXTURE_2D_ARRAY;
+    case SurfaceTarget::Texture3D:
+        return GL_NONE;
+    case SurfaceTarget::Texture1DArray:
+        return GL_TEXTURE_1D;
+    case SurfaceTarget::Texture2DArray:
+        return GL_TEXTURE_2D;
+    case SurfaceTarget::TextureCubemap:
+        return GL_TEXTURE_CUBE_MAP_ARRAY;
+    case SurfaceTarget::TextureCubeArray:
+        return GL_TEXTURE_CUBE_MAP;
+    }
+    return GL_NONE;
+}
+
 Common::Rectangle<u32> SurfaceParams::GetRect(u32 mip_level) const {
     u32 actual_height{std::max(1U, unaligned_height >> mip_level)};
     if (IsPixelFormatASTC(pixel_format)) {
@@ -881,20 +902,22 @@ void CachedSurface::UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle,
     glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
 }
 
-void CachedSurface::EnsureTextureView() {
-    if (texture_view.handle != 0)
+void CachedSurface::EnsureTextureDiscrepantView() {
+    if (discrepant_view.handle != 0)
         return;
 
-    const GLenum target{TargetLayer()};
+    const GLenum target{GetArrayDiscrepantTarget(params.target)};
+    ASSERT(target != GL_NONE);
+
     const GLuint num_layers{target == GL_TEXTURE_CUBE_MAP_ARRAY ? 6u : 1u};
     constexpr GLuint min_layer = 0;
     constexpr GLuint min_level = 0;
 
-    glGenTextures(1, &texture_view.handle);
-    glTextureView(texture_view.handle, target, texture.handle, gl_internal_format, min_level,
+    glGenTextures(1, &discrepant_view.handle);
+    glTextureView(discrepant_view.handle, target, texture.handle, gl_internal_format, min_level,
                   params.max_mip_level, min_layer, num_layers);
-    ApplyTextureDefaults(texture_view.handle, params.max_mip_level);
-    glTextureParameteriv(texture_view.handle, GL_TEXTURE_SWIZZLE_RGBA,
+    ApplyTextureDefaults(discrepant_view.handle, params.max_mip_level);
+    glTextureParameteriv(discrepant_view.handle, GL_TEXTURE_SWIZZLE_RGBA,
                          reinterpret_cast<const GLint*>(swizzle.data()));
 }
 
@@ -920,8 +943,8 @@ void CachedSurface::UpdateSwizzle(Tegra::Texture::SwizzleSource swizzle_x,
     swizzle = {new_x, new_y, new_z, new_w};
     const auto swizzle_data = reinterpret_cast<const GLint*>(swizzle.data());
     glTextureParameteriv(texture.handle, GL_TEXTURE_SWIZZLE_RGBA, swizzle_data);
-    if (texture_view.handle != 0) {
-        glTextureParameteriv(texture_view.handle, GL_TEXTURE_SWIZZLE_RGBA, swizzle_data);
+    if (discrepant_view.handle != 0) {
+        glTextureParameteriv(discrepant_view.handle, GL_TEXTURE_SWIZZLE_RGBA, swizzle_data);
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 838554c35..32fc6538a 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -354,31 +354,19 @@ public:
         return texture;
     }
 
-    const OGLTexture& TextureLayer() {
-        if (params.is_array) {
-            return Texture();
+    const OGLTexture& Texture(bool as_array) {
+        if (params.is_array == as_array) {
+            return texture;
+        } else {
+            EnsureTextureDiscrepantView();
+            return discrepant_view;
         }
-        EnsureTextureView();
-        return texture_view;
     }
 
     GLenum Target() const {
         return gl_target;
     }
 
-    GLenum TargetLayer() const {
-        using VideoCore::Surface::SurfaceTarget;
-        switch (params.target) {
-        case SurfaceTarget::Texture1D:
-            return GL_TEXTURE_1D_ARRAY;
-        case SurfaceTarget::Texture2D:
-            return GL_TEXTURE_2D_ARRAY;
-        case SurfaceTarget::TextureCubemap:
-            return GL_TEXTURE_CUBE_MAP_ARRAY;
-        }
-        return Target();
-    }
-
     const SurfaceParams& GetSurfaceParams() const {
         return params;
     }
@@ -398,10 +386,10 @@ public:
 private:
     void UploadGLMipmapTexture(u32 mip_map, GLuint read_fb_handle, GLuint draw_fb_handle);
 
-    void EnsureTextureView();
+    void EnsureTextureDiscrepantView();
 
     OGLTexture texture;
-    OGLTexture texture_view;
+    OGLTexture discrepant_view;
     std::vector<std::vector<u8>> gl_buffer;
     SurfaceParams params{};
     GLenum gl_target{};