Просмотр исходного кода

Merge pull request #1256 from bunnei/tex-target-support

Initial support for non-2D textures
bunnei 7 лет назад
Родитель
Сommit
af074ee422

+ 0 - 4
src/video_core/engines/maxwell_3d.cpp

@@ -293,10 +293,6 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
                    tic_entry.header_version == Texture::TICHeaderVersion::Pitch,
                "TIC versions other than BlockLinear or Pitch are unimplemented");
 
-    ASSERT_MSG((tic_entry.texture_type == Texture::TextureType::Texture2D) ||
-                   (tic_entry.texture_type == Texture::TextureType::Texture2DNoMipmap),
-               "Texture types other than Texture2D are unimplemented");
-
     auto r_type = tic_entry.r_type.Value();
     auto g_type = tic_entry.g_type.Value();
     auto b_type = tic_entry.b_type.Value();

+ 11 - 5
src/video_core/renderer_opengl/gl_rasterizer.cpp

@@ -586,7 +586,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
 void RasterizerOpenGL::SamplerInfo::Create() {
     sampler.Create();
     mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear;
-    wrap_u = wrap_v = Tegra::Texture::WrapMode::Wrap;
+    wrap_u = wrap_v = wrap_p = Tegra::Texture::WrapMode::Wrap;
 
     // default is GL_LINEAR_MIPMAP_LINEAR
     glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -613,8 +613,13 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
         wrap_v = config.wrap_v;
         glSamplerParameteri(s, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v));
     }
+    if (wrap_p != config.wrap_p) {
+        wrap_p = config.wrap_p;
+        glSamplerParameteri(s, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(wrap_p));
+    }
 
-    if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border) {
+    if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border ||
+        wrap_p == Tegra::Texture::WrapMode::Border) {
         const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g,
                                           config.border_color_b, config.border_color_a}};
         if (border_color != new_border_color) {
@@ -698,14 +703,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
         const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
 
         if (!texture.enabled) {
-            state.texture_units[current_bindpoint].texture_2d = 0;
+            state.texture_units[current_bindpoint].texture = 0;
             continue;
         }
 
         texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
         Surface surface = res_cache.GetTextureSurface(texture);
         if (surface != nullptr) {
-            state.texture_units[current_bindpoint].texture_2d = surface->Texture().handle;
+            state.texture_units[current_bindpoint].texture = surface->Texture().handle;
+            state.texture_units[current_bindpoint].target = surface->Target();
             state.texture_units[current_bindpoint].swizzle.r =
                 MaxwellToGL::SwizzleSource(texture.tic.x_source);
             state.texture_units[current_bindpoint].swizzle.g =
@@ -716,7 +722,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
                 MaxwellToGL::SwizzleSource(texture.tic.w_source);
         } else {
             // Can occur when texture addr is null or its memory is unmapped/invalid
-            state.texture_units[current_bindpoint].texture_2d = 0;
+            state.texture_units[current_bindpoint].texture = 0;
         }
     }
 

+ 1 - 0
src/video_core/renderer_opengl/gl_rasterizer.h

@@ -93,6 +93,7 @@ private:
         Tegra::Texture::TextureFilter min_filter;
         Tegra::Texture::WrapMode wrap_u;
         Tegra::Texture::WrapMode wrap_v;
+        Tegra::Texture::WrapMode wrap_p;
         GLvec4 border_color;
     };
 

+ 198 - 166
src/video_core/renderer_opengl/gl_rasterizer_cache.cpp

@@ -7,6 +7,7 @@
 
 #include "common/alignment.h"
 #include "common/assert.h"
+#include "common/logging/log.h"
 #include "common/microprofile.h"
 #include "common/scope_exit.h"
 #include "core/core.h"
@@ -51,10 +52,12 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
     params.type = GetFormatType(params.pixel_format);
     params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
     params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
+    params.depth = config.tic.Depth();
     params.unaligned_height = config.tic.Height();
     params.size_in_bytes = params.SizeInBytes();
-    params.cache_width = Common::AlignUp(params.width, 16);
-    params.cache_height = Common::AlignUp(params.height, 16);
+    params.cache_width = Common::AlignUp(params.width, 8);
+    params.cache_height = Common::AlignUp(params.height, 8);
+    params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
     return params;
 }
 
@@ -69,10 +72,12 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
     params.type = GetFormatType(params.pixel_format);
     params.width = config.width;
     params.height = config.height;
+    params.depth = 1;
     params.unaligned_height = config.height;
     params.size_in_bytes = params.SizeInBytes();
-    params.cache_width = Common::AlignUp(params.width, 16);
-    params.cache_height = Common::AlignUp(params.height, 16);
+    params.cache_width = Common::AlignUp(params.width, 8);
+    params.cache_height = Common::AlignUp(params.height, 8);
+    params.target = SurfaceTarget::Texture2D;
     return params;
 }
 
@@ -86,13 +91,14 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
     params.pixel_format = PixelFormatFromDepthFormat(format);
     params.component_type = ComponentTypeFromDepthFormat(format);
     params.type = GetFormatType(params.pixel_format);
-    params.size_in_bytes = params.SizeInBytes();
     params.width = zeta_width;
     params.height = zeta_height;
+    params.depth = 1;
     params.unaligned_height = zeta_height;
     params.size_in_bytes = params.SizeInBytes();
-    params.cache_width = Common::AlignUp(params.width, 16);
-    params.cache_height = Common::AlignUp(params.height, 16);
+    params.cache_width = Common::AlignUp(params.width, 8);
+    params.cache_height = Common::AlignUp(params.height, 8);
+    params.target = SurfaceTarget::Texture2D;
     return params;
 }
 
@@ -166,6 +172,26 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
      ComponentType::Float, false}, // Z32FS8
 }};
 
+static GLenum SurfaceTargetToGL(SurfaceParams::SurfaceTarget target) {
+    switch (target) {
+    case SurfaceParams::SurfaceTarget::Texture1D:
+        return GL_TEXTURE_1D;
+    case SurfaceParams::SurfaceTarget::Texture2D:
+        return GL_TEXTURE_2D;
+    case SurfaceParams::SurfaceTarget::Texture3D:
+        return GL_TEXTURE_3D;
+    case SurfaceParams::SurfaceTarget::Texture1DArray:
+        return GL_TEXTURE_1D_ARRAY;
+    case SurfaceParams::SurfaceTarget::Texture2DArray:
+        return GL_TEXTURE_2D_ARRAY;
+    case SurfaceParams::SurfaceTarget::TextureCubemap:
+        return GL_TEXTURE_CUBE_MAP;
+    }
+    LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
+    UNREACHABLE();
+    return {};
+}
+
 static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
     ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size());
     auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)];
@@ -220,7 +246,8 @@ static bool IsFormatBCn(PixelFormat format) {
 }
 
 template <bool morton_to_gl, PixelFormat format>
-void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer, VAddr addr) {
+void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, size_t gl_buffer_size,
+                VAddr addr) {
     constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
     constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
 
@@ -230,18 +257,18 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_bu
         const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
         const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
             addr, tile_size, bytes_per_pixel, stride, height, block_height);
-        const size_t size_to_copy{std::min(gl_buffer.size(), data.size())};
-        gl_buffer.assign(data.begin(), data.begin() + size_to_copy);
+        const size_t size_to_copy{std::min(gl_buffer_size, data.size())};
+        memcpy(gl_buffer, data.data(), size_to_copy);
     } else {
         // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
         // check the configuration for this and perform more generic un/swizzle
         LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
         VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
-                                       Memory::GetPointer(addr), gl_buffer.data(), morton_to_gl);
+                                       Memory::GetPointer(addr), gl_buffer, morton_to_gl);
     }
 }
 
-static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
+static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr),
                             SurfaceParams::MaxPixelFormat>
     morton_to_gl_fns = {
         // clang-format off
@@ -298,7 +325,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
         // clang-format on
 };
 
-static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
+static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr),
                             SurfaceParams::MaxPixelFormat>
     gl_to_morton_fns = {
         // clang-format off
@@ -357,33 +384,6 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
         // clang-format on
 };
 
-// Allocate an uninitialized texture of appropriate size and format for the surface
-static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tuple, u32 width,
-                                   u32 height) {
-    OpenGLState cur_state = OpenGLState::GetCurState();
-
-    // Keep track of previous texture bindings
-    GLuint old_tex = cur_state.texture_units[0].texture_2d;
-    cur_state.texture_units[0].texture_2d = texture;
-    cur_state.Apply();
-    glActiveTexture(GL_TEXTURE0);
-
-    if (!format_tuple.compressed) {
-        // Only pre-create the texture for non-compressed textures.
-        glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, width, height, 0,
-                     format_tuple.format, format_tuple.type, nullptr);
-    }
-
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-
-    // Restore previous texture bindings
-    cur_state.texture_units[0].texture_2d = old_tex;
-    cur_state.Apply();
-}
-
 static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex,
                          const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type,
                          GLuint read_fb_handle, GLuint draw_fb_handle) {
@@ -438,12 +438,56 @@ static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rec
     return true;
 }
 
-CachedSurface::CachedSurface(const SurfaceParams& params) : params(params) {
+CachedSurface::CachedSurface(const SurfaceParams& params)
+    : params(params), gl_target(SurfaceTargetToGL(params.target)) {
     texture.Create();
     const auto& rect{params.GetRect()};
-    AllocateSurfaceTexture(texture.handle,
-                           GetFormatTuple(params.pixel_format, params.component_type),
-                           rect.GetWidth(), rect.GetHeight());
+
+    // Keep track of previous texture bindings
+    OpenGLState cur_state = OpenGLState::GetCurState();
+    const auto& old_tex = cur_state.texture_units[0];
+    SCOPE_EXIT({
+        cur_state.texture_units[0] = old_tex;
+        cur_state.Apply();
+    });
+
+    cur_state.texture_units[0].texture = texture.handle;
+    cur_state.texture_units[0].target = SurfaceTargetToGL(params.target);
+    cur_state.Apply();
+    glActiveTexture(GL_TEXTURE0);
+
+    const auto& format_tuple = GetFormatTuple(params.pixel_format, params.component_type);
+    if (!format_tuple.compressed) {
+        // Only pre-create the texture for non-compressed textures.
+        switch (params.target) {
+        case SurfaceParams::SurfaceTarget::Texture1D:
+            glTexImage1D(SurfaceTargetToGL(params.target), 0, format_tuple.internal_format,
+                         rect.GetWidth(), 0, format_tuple.format, format_tuple.type, nullptr);
+            break;
+        case SurfaceParams::SurfaceTarget::Texture2D:
+            glTexImage2D(SurfaceTargetToGL(params.target), 0, format_tuple.internal_format,
+                         rect.GetWidth(), rect.GetHeight(), 0, format_tuple.format,
+                         format_tuple.type, nullptr);
+            break;
+        case SurfaceParams::SurfaceTarget::Texture3D:
+        case SurfaceParams::SurfaceTarget::Texture2DArray:
+            glTexImage3D(SurfaceTargetToGL(params.target), 0, format_tuple.internal_format,
+                         rect.GetWidth(), rect.GetHeight(), params.depth, 0, format_tuple.format,
+                         format_tuple.type, nullptr);
+            break;
+        default:
+            LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
+                         static_cast<u32>(params.target));
+            UNREACHABLE();
+            glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, rect.GetWidth(),
+                         rect.GetHeight(), 0, format_tuple.format, format_tuple.type, nullptr);
+        }
+    }
+
+    glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAX_LEVEL, 0);
+    glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 }
 
 static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
@@ -514,23 +558,6 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
     }
 }
 
-/**
- * Helper function to perform software conversion (as needed) when flushing a buffer to Switch
- * memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
- * typical desktop GPUs.
- */
-static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& /*data*/, PixelFormat pixel_format,
-                                                u32 /*width*/, u32 /*height*/) {
-    switch (pixel_format) {
-    case PixelFormat::ASTC_2D_4X4:
-    case PixelFormat::S8Z24:
-        LOG_CRITICAL(Render_OpenGL, "Unimplemented pixel_format={}",
-                     static_cast<u32>(pixel_format));
-        UNREACHABLE();
-        break;
-    }
-}
-
 MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
 void CachedSurface::LoadGLBuffer() {
     ASSERT(params.type != SurfaceType::Fill);
@@ -545,13 +572,24 @@ void CachedSurface::LoadGLBuffer() {
     MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
 
     if (params.is_tiled) {
-        gl_buffer.resize(copy_size);
+        // TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do
+        // this for 3D textures, etc.
+        switch (params.target) {
+        case SurfaceParams::SurfaceTarget::Texture2D:
+            // Pass impl. to the fallback code below
+            break;
+        default:
+            LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}",
+                         static_cast<u32>(params.target));
+            UNREACHABLE();
+        }
 
+        gl_buffer.resize(params.depth * copy_size);
         morton_to_gl_fns[static_cast<size_t>(params.pixel_format)](
-            params.width, params.block_height, params.height, gl_buffer, params.addr);
+            params.width, params.block_height, params.height, gl_buffer.data(), copy_size,
+            params.addr);
     } else {
-        const u8* const texture_src_data_end = texture_src_data + copy_size;
-
+        const u8* const texture_src_data_end{texture_src_data + (params.depth * copy_size)};
         gl_buffer.assign(texture_src_data, texture_src_data_end);
     }
 
@@ -560,23 +598,7 @@ void CachedSurface::LoadGLBuffer() {
 
 MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
 void CachedSurface::FlushGLBuffer() {
-    u8* const dst_buffer = Memory::GetPointer(params.addr);
-
-    ASSERT(dst_buffer);
-    ASSERT(gl_buffer.size() ==
-           params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
-
-    MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
-
-    ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width,
-                                        params.height);
-
-    if (!params.is_tiled) {
-        std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes);
-    } else {
-        gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
-            params.width, params.block_height, params.height, gl_buffer, params.addr);
-    }
+    ASSERT_MSG(false, "Unimplemented");
 }
 
 MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
@@ -587,7 +609,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
     MICROPROFILE_SCOPE(OpenGL_TextureUL);
 
     ASSERT(gl_buffer.size() ==
-           params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
+           params.width * params.height * GetGLBytesPerPixel(params.pixel_format) * params.depth);
 
     const auto& rect{params.GetRect()};
 
@@ -600,8 +622,13 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
     GLuint target_tex = texture.handle;
     OpenGLState cur_state = OpenGLState::GetCurState();
 
-    GLuint old_tex = cur_state.texture_units[0].texture_2d;
-    cur_state.texture_units[0].texture_2d = target_tex;
+    const auto& old_tex = cur_state.texture_units[0];
+    SCOPE_EXIT({
+        cur_state.texture_units[0] = old_tex;
+        cur_state.Apply();
+    });
+    cur_state.texture_units[0].texture = target_tex;
+    cur_state.texture_units[0].target = SurfaceTargetToGL(params.target);
     cur_state.Apply();
 
     // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
@@ -610,74 +637,68 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
 
     glActiveTexture(GL_TEXTURE0);
     if (tuple.compressed) {
-        glCompressedTexImage2D(
-            GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),
-            static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes),
-            &gl_buffer[buffer_offset]);
+        switch (params.target) {
+        case SurfaceParams::SurfaceTarget::Texture2D:
+            glCompressedTexImage2D(
+                SurfaceTargetToGL(params.target), 0, tuple.internal_format,
+                static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0,
+                static_cast<GLsizei>(params.size_in_bytes), &gl_buffer[buffer_offset]);
+            break;
+        case SurfaceParams::SurfaceTarget::Texture3D:
+        case SurfaceParams::SurfaceTarget::Texture2DArray:
+            glCompressedTexImage3D(
+                SurfaceTargetToGL(params.target), 0, tuple.internal_format,
+                static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height),
+                static_cast<GLsizei>(params.depth), 0, static_cast<GLsizei>(params.size_in_bytes),
+                &gl_buffer[buffer_offset]);
+            break;
+        default:
+            LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
+                         static_cast<u32>(params.target));
+            UNREACHABLE();
+            glCompressedTexImage2D(
+                GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),
+                static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes),
+                &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,
-                        &gl_buffer[buffer_offset]);
-    }
-
-    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
-
-    cur_state.texture_units[0].texture_2d = old_tex;
-    cur_state.Apply();
-}
-
-MICROPROFILE_DEFINE(OpenGL_TextureDL, "OpenGL", "Texture Download", MP_RGB(128, 192, 64));
-void CachedSurface::DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
-    if (params.type == SurfaceType::Fill)
-        return;
-
-    MICROPROFILE_SCOPE(OpenGL_TextureDL);
-
-    gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
-
-    OpenGLState state = OpenGLState::GetCurState();
-    OpenGLState prev_state = state;
-    SCOPE_EXIT({ prev_state.Apply(); });
 
-    const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
-
-    // Ensure no bad interactions with GL_PACK_ALIGNMENT
-    ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0);
-    glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
-
-    const auto& rect{params.GetRect()};
-    size_t buffer_offset =
-        (rect.bottom * params.width + rect.left) * GetGLBytesPerPixel(params.pixel_format);
-
-    state.UnbindTexture(texture.handle);
-    state.draw.read_framebuffer = read_fb_handle;
-    state.Apply();
-
-    if (params.type == SurfaceType::ColorTexture) {
-        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                               texture.handle, 0);
-        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
-                               0);
-    } else if (params.type == SurfaceType::Depth) {
-        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
-        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
-                               texture.handle, 0);
-        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
-    } else {
-        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
-        glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
-                               texture.handle, 0);
+        switch (params.target) {
+        case SurfaceParams::SurfaceTarget::Texture1D:
+            glTexSubImage1D(SurfaceTargetToGL(params.target), 0, x0,
+                            static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type,
+                            &gl_buffer[buffer_offset]);
+            break;
+        case SurfaceParams::SurfaceTarget::Texture2D:
+            glTexSubImage2D(SurfaceTargetToGL(params.target), 0, x0, y0,
+                            static_cast<GLsizei>(rect.GetWidth()),
+                            static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
+                            &gl_buffer[buffer_offset]);
+            break;
+        case SurfaceParams::SurfaceTarget::Texture3D:
+        case SurfaceParams::SurfaceTarget::Texture2DArray:
+            glTexSubImage3D(SurfaceTargetToGL(params.target), 0, x0, y0, 0,
+                            static_cast<GLsizei>(rect.GetWidth()),
+                            static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
+                            tuple.type, &gl_buffer[buffer_offset]);
+            break;
+        default:
+            LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
+                         static_cast<u32>(params.target));
+            UNREACHABLE();
+            glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
+                            static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
+                            &gl_buffer[buffer_offset]);
+        }
     }
-    glReadPixels(static_cast<GLint>(rect.left), static_cast<GLint>(rect.bottom),
-                 static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()),
-                 tuple.format, tuple.type, &gl_buffer[buffer_offset]);
 
-    glPixelStorei(GL_PACK_ROW_LENGTH, 0);
+    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
 }
 
 RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
     read_framebuffer.Create();
     draw_framebuffer.Create();
+    copy_pbo.Create();
 }
 
 Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) {
@@ -748,7 +769,6 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
 }
 
 void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) {
-    surface->DownloadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
     surface->FlushGLBuffer();
 }
 
@@ -809,8 +829,8 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
     // If format is unchanged, we can do a faster blit without reinterpreting pixel data
     if (params.pixel_format == new_params.pixel_format) {
         BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle,
-                     new_surface->GetSurfaceParams().GetRect(), params.type,
-                     read_framebuffer.handle, draw_framebuffer.handle);
+                     params.GetRect(), params.type, read_framebuffer.handle,
+                     draw_framebuffer.handle);
         return new_surface;
     }
 
@@ -821,12 +841,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
 
         size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes());
 
-        // Use a Pixel Buffer Object to download the previous texture and then upload it to the new
-        // one using the new format.
-        OGLBuffer pbo;
-        pbo.Create();
-
-        glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle);
+        glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo.handle);
         glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
         if (source_format.compressed) {
             glGetCompressedTextureImage(surface->Texture().handle, 0,
@@ -845,8 +860,8 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
                 // of the data in this case. Games like Super Mario Odyssey seem to hit this case
                 // when drawing, it re-uses the memory of a previous texture as a bigger framebuffer
                 // but it doesn't clear it beforehand, the texture is already full of zeros.
-                LOG_CRITICAL(HW_GPU, "Trying to upload extra texture data from the CPU during "
-                                     "reinterpretation but the texture is tiled.");
+                LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
+                                  "reinterpretation but the texture is tiled.");
             }
             size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes();
             std::vector<u8> data(remaining_size);
@@ -859,21 +874,38 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
 
         const auto& dest_rect{new_params.GetRect()};
 
-        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.handle);
+        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, copy_pbo.handle);
         if (dest_format.compressed) {
-            glCompressedTexSubImage2D(
-                GL_TEXTURE_2D, 0, 0, 0, static_cast<GLsizei>(dest_rect.GetWidth()),
-                static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
-                static_cast<GLsizei>(new_params.SizeInBytes()), nullptr);
+            LOG_CRITICAL(HW_GPU, "Compressed copy is unimplemented!");
+            UNREACHABLE();
         } else {
-            glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0,
-                                static_cast<GLsizei>(dest_rect.GetWidth()),
-                                static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
-                                dest_format.type, nullptr);
+            switch (new_params.target) {
+            case SurfaceParams::SurfaceTarget::Texture1D:
+                glTextureSubImage1D(new_surface->Texture().handle, 0, 0,
+                                    static_cast<GLsizei>(dest_rect.GetWidth()), dest_format.format,
+                                    dest_format.type, nullptr);
+                break;
+            case SurfaceParams::SurfaceTarget::Texture2D:
+                glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0,
+                                    static_cast<GLsizei>(dest_rect.GetWidth()),
+                                    static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
+                                    dest_format.type, nullptr);
+                break;
+            case SurfaceParams::SurfaceTarget::Texture3D:
+            case SurfaceParams::SurfaceTarget::Texture2DArray:
+                glTextureSubImage3D(new_surface->Texture().handle, 0, 0, 0, 0,
+                                    static_cast<GLsizei>(dest_rect.GetWidth()),
+                                    static_cast<GLsizei>(dest_rect.GetHeight()),
+                                    static_cast<GLsizei>(new_params.depth), dest_format.format,
+                                    dest_format.type, nullptr);
+                break;
+            default:
+                LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
+                             static_cast<u32>(params.target));
+                UNREACHABLE();
+            }
         }
         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
-
-        pbo.Release();
     }
 
     return new_surface;

+ 40 - 3
src/video_core/renderer_opengl/gl_rasterizer_cache.h

@@ -109,6 +109,33 @@ struct SurfaceParams {
         Invalid = 4,
     };
 
+    enum class SurfaceTarget {
+        Texture1D,
+        Texture2D,
+        Texture3D,
+        Texture1DArray,
+        Texture2DArray,
+        TextureCubemap,
+    };
+
+    static SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) {
+        switch (texture_type) {
+        case Tegra::Texture::TextureType::Texture1D:
+            return SurfaceTarget::Texture1D;
+        case Tegra::Texture::TextureType::Texture2D:
+        case Tegra::Texture::TextureType::Texture2DNoMipmap:
+            return SurfaceTarget::Texture2D;
+        case Tegra::Texture::TextureType::Texture1DArray:
+            return SurfaceTarget::Texture1DArray;
+        case Tegra::Texture::TextureType::Texture2DArray:
+            return SurfaceTarget::Texture2DArray;
+        default:
+            LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type));
+            UNREACHABLE();
+            return SurfaceTarget::Texture2D;
+        }
+    }
+
     /**
      * 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
@@ -635,7 +662,7 @@ struct SurfaceParams {
         ASSERT(width % compression_factor == 0);
         ASSERT(height % compression_factor == 0);
         return (width / compression_factor) * (height / compression_factor) *
-               GetFormatBpp(pixel_format) / CHAR_BIT;
+               GetFormatBpp(pixel_format) * depth / CHAR_BIT;
     }
 
     /// Creates SurfaceParams from a texture configuration
@@ -664,8 +691,10 @@ struct SurfaceParams {
     SurfaceType type;
     u32 width;
     u32 height;
+    u32 depth;
     u32 unaligned_height;
     size_t size_in_bytes;
+    SurfaceTarget target;
 
     // Parameters used for caching only
     u32 cache_width;
@@ -709,6 +738,10 @@ public:
         return texture;
     }
 
+    GLenum Target() const {
+        return gl_target;
+    }
+
     static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) {
         if (format == SurfaceParams::PixelFormat::Invalid)
             return 0;
@@ -724,14 +757,14 @@ public:
     void LoadGLBuffer();
     void FlushGLBuffer();
 
-    // Upload/Download data in gl_buffer in/to this surface's texture
+    // Upload data in gl_buffer to this surface's texture
     void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
-    void DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
 
 private:
     OGLTexture texture;
     std::vector<u8> gl_buffer;
     SurfaceParams params;
+    GLenum gl_target;
 };
 
 class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@@ -774,6 +807,10 @@ private:
 
     OGLFramebuffer read_framebuffer;
     OGLFramebuffer draw_framebuffer;
+
+    /// Use a Pixel Buffer Object to download the previous texture and then upload it to the new one
+    /// using the new format.
+    OGLBuffer copy_pbo;
 };
 
 } // namespace OpenGL

+ 99 - 26
src/video_core/renderer_opengl/gl_shader_decompiler.cpp

@@ -443,13 +443,12 @@ public:
         }
         declarations.AddNewLine();
 
-        // Append the sampler2D array for the used textures.
-        const size_t num_samplers = used_samplers.size();
-        if (num_samplers > 0) {
-            declarations.AddLine("uniform sampler2D " + SamplerEntry::GetArrayName(stage) + '[' +
-                                 std::to_string(num_samplers) + "];");
-            declarations.AddNewLine();
+        const auto& samplers = GetSamplers();
+        for (const auto& sampler : samplers) {
+            declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
+                                 ';');
         }
+        declarations.AddNewLine();
     }
 
     /// Returns a list of constant buffer declarations
@@ -461,13 +460,14 @@ public:
     }
 
     /// Returns a list of samplers used in the shader
-    std::vector<SamplerEntry> GetSamplers() const {
+    const std::vector<SamplerEntry>& GetSamplers() const {
         return used_samplers;
     }
 
     /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
     /// necessary.
-    std::string AccessSampler(const Sampler& sampler) {
+    std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
+                              bool is_array) {
         size_t offset = static_cast<size_t>(sampler.index.Value());
 
         // If this sampler has already been used, return the existing mapping.
@@ -476,12 +476,13 @@ public:
                          [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
 
         if (itr != used_samplers.end()) {
+            ASSERT(itr->GetType() == type && itr->IsArray() == is_array);
             return itr->GetName();
         }
 
         // Otherwise create a new mapping for this sampler
         size_t next_index = used_samplers.size();
-        SamplerEntry entry{stage, offset, next_index};
+        SamplerEntry entry{stage, offset, next_index, type, is_array};
         used_samplers.emplace_back(entry);
         return entry.GetName();
     }
@@ -722,8 +723,8 @@ private:
     }
 
     /// Generates code representing a texture sampler.
-    std::string GetSampler(const Sampler& sampler) {
-        return regs.AccessSampler(sampler);
+    std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array) {
+        return regs.AccessSampler(sampler, type, is_array);
     }
 
     /**
@@ -1753,10 +1754,35 @@ private:
                 break;
             }
             case OpCode::Id::TEX: {
-                const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
-                const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
-                const std::string sampler = GetSampler(instr.sampler);
-                const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
+                ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");
+                Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
+                std::string coord;
+
+                switch (texture_type) {
+                case Tegra::Shader::TextureType::Texture1D: {
+                    std::string x = regs.GetRegisterAsFloat(instr.gpr8);
+                    coord = "float coords = " + x + ';';
+                    break;
+                }
+                case Tegra::Shader::TextureType::Texture2D: {
+                    std::string x = regs.GetRegisterAsFloat(instr.gpr8);
+                    std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
+                    coord = "vec2 coords = vec2(" + x + ", " + y + ");";
+                    break;
+                }
+                default:
+                    LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
+                                 static_cast<u32>(texture_type));
+                    UNREACHABLE();
+
+                    // Fallback to interpreting as a 2D texture for now
+                    std::string x = regs.GetRegisterAsFloat(instr.gpr8);
+                    std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
+                    coord = "vec2 coords = vec2(" + x + ", " + y + ");";
+                    texture_type = Tegra::Shader::TextureType::Texture2D;
+                }
+
+                const std::string sampler = GetSampler(instr.sampler, texture_type, false);
                 // Add an extra scope and declare the texture coords inside to prevent
                 // overwriting them in case they are used as outputs of the texs instruction.
                 shader.AddLine("{");
@@ -1778,20 +1804,65 @@ private:
                 break;
             }
             case OpCode::Id::TEXS: {
-                const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
-                const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
-                const std::string sampler = GetSampler(instr.sampler);
-                const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
+                std::string coord;
+                Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()};
+                bool is_array{instr.texs.IsArrayTexture()};
+
+                switch (texture_type) {
+                case Tegra::Shader::TextureType::Texture2D: {
+                    if (is_array) {
+                        std::string index = regs.GetRegisterAsInteger(instr.gpr8);
+                        std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
+                        std::string y = regs.GetRegisterAsFloat(instr.gpr20);
+                        coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
+                    } else {
+                        std::string x = regs.GetRegisterAsFloat(instr.gpr8);
+                        std::string y = regs.GetRegisterAsFloat(instr.gpr20);
+                        coord = "vec2 coords = vec2(" + x + ", " + y + ");";
+                    }
+                    break;
+                }
+                default:
+                    LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
+                                 static_cast<u32>(texture_type));
+                    UNREACHABLE();
 
+                    // Fallback to interpreting as a 2D texture for now
+                    std::string x = regs.GetRegisterAsFloat(instr.gpr8);
+                    std::string y = regs.GetRegisterAsFloat(instr.gpr20);
+                    coord = "vec2 coords = vec2(" + x + ", " + y + ");";
+                    texture_type = Tegra::Shader::TextureType::Texture2D;
+                    is_array = false;
+                }
+                const std::string sampler = GetSampler(instr.sampler, texture_type, is_array);
                 const std::string texture = "texture(" + sampler + ", coords)";
                 WriteTexsInstruction(instr, coord, texture);
                 break;
             }
             case OpCode::Id::TLDS: {
-                const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
-                const std::string op_b = regs.GetRegisterAsInteger(instr.gpr20);
-                const std::string sampler = GetSampler(instr.sampler);
-                const std::string coord = "ivec2 coords = ivec2(" + op_a + ", " + op_b + ");";
+                ASSERT(instr.tlds.GetTextureType() == Tegra::Shader::TextureType::Texture2D);
+                ASSERT(instr.tlds.IsArrayTexture() == false);
+                std::string coord;
+
+                switch (instr.tlds.GetTextureType()) {
+                case Tegra::Shader::TextureType::Texture2D: {
+                    if (instr.tlds.IsArrayTexture()) {
+                        LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture");
+                        UNREACHABLE();
+                    } else {
+                        std::string x = regs.GetRegisterAsInteger(instr.gpr8);
+                        std::string y = regs.GetRegisterAsInteger(instr.gpr20);
+                        coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
+                    }
+                    break;
+                }
+                default:
+                    LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
+                                 static_cast<u32>(instr.tlds.GetTextureType()));
+                    UNREACHABLE();
+                }
+                const std::string sampler = GetSampler(instr.sampler, instr.tlds.GetTextureType(),
+                                                       instr.tlds.IsArrayTexture());
                 const std::string texture = "texelFetch(" + sampler + ", coords, 0)";
                 WriteTexsInstruction(instr, coord, texture);
                 break;
@@ -1799,7 +1870,7 @@ private:
             case OpCode::Id::TLD4: {
                 ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D);
                 ASSERT(instr.tld4.array == 0);
-                std::string coord{};
+                std::string coord;
 
                 switch (instr.tld4.texture_type) {
                 case Tegra::Shader::TextureType::Texture2D: {
@@ -1814,7 +1885,8 @@ private:
                     UNREACHABLE();
                 }
 
-                const std::string sampler = GetSampler(instr.sampler);
+                const std::string sampler =
+                    GetSampler(instr.sampler, instr.tld4.texture_type, false);
                 // Add an extra scope and declare the texture coords inside to prevent
                 // overwriting them in case they are used as outputs of the texs instruction.
                 shader.AddLine("{");
@@ -1840,7 +1912,8 @@ private:
                 const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
                 const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
                 // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
-                const std::string sampler = GetSampler(instr.sampler);
+                const std::string sampler =
+                    GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false);
                 const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
                 const std::string texture = "textureGather(" + sampler + ", coords, " +
                                             std::to_string(instr.tld4s.component) + ')';

+ 44 - 6
src/video_core/renderer_opengl/gl_shader_gen.h

@@ -9,6 +9,7 @@
 #include <vector>
 
 #include "common/common_types.h"
+#include "video_core/engines/shader_bytecode.h"
 
 namespace OpenGL::GLShader {
 
@@ -73,8 +74,9 @@ class SamplerEntry {
     using Maxwell = Tegra::Engines::Maxwell3D::Regs;
 
 public:
-    SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index)
-        : offset(offset), stage(stage), sampler_index(index) {}
+    SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index,
+                 Tegra::Shader::TextureType type, bool is_array)
+        : offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {}
 
     size_t GetOffset() const {
         return offset;
@@ -89,8 +91,41 @@ public:
     }
 
     std::string GetName() const {
-        return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '[' +
-               std::to_string(sampler_index) + ']';
+        return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '_' +
+               std::to_string(sampler_index);
+    }
+
+    std::string GetTypeString() const {
+        using Tegra::Shader::TextureType;
+        std::string glsl_type;
+
+        switch (type) {
+        case TextureType::Texture1D:
+            glsl_type = "sampler1D";
+            break;
+        case TextureType::Texture2D:
+            glsl_type = "sampler2D";
+            break;
+        case TextureType::Texture3D:
+            glsl_type = "sampler3D";
+            break;
+        case TextureType::TextureCube:
+            glsl_type = "samplerCube";
+            break;
+        default:
+            UNIMPLEMENTED();
+        }
+        if (is_array)
+            glsl_type += "Array";
+        return glsl_type;
+    }
+
+    Tegra::Shader::TextureType GetType() const {
+        return type;
+    }
+
+    bool IsArray() const {
+        return is_array;
     }
 
     u32 GetHash() const {
@@ -105,11 +140,14 @@ private:
     static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = {
         "tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs",
     };
+
     /// Offset in TSC memory from which to read the sampler object, as specified by the sampling
     /// instruction.
     size_t offset;
-    Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
-    size_t sampler_index;       ///< Value used to index into the generated GLSL sampler array.
+    Maxwell::ShaderStage stage;      ///< Shader stage where this sampler was used.
+    size_t sampler_index;            ///< Value used to index into the generated GLSL sampler array.
+    Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc)
+    bool is_array; ///< Whether the texture is being sampled as an array texture or not.
 };
 
 struct ShaderEntries {

+ 4 - 4
src/video_core/renderer_opengl/gl_state.cpp

@@ -200,9 +200,9 @@ void OpenGLState::Apply() const {
         const auto& texture_unit = texture_units[i];
         const auto& cur_state_texture_unit = cur_state.texture_units[i];
 
-        if (texture_unit.texture_2d != cur_state_texture_unit.texture_2d) {
+        if (texture_unit.texture != cur_state_texture_unit.texture) {
             glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum());
-            glBindTexture(GL_TEXTURE_2D, texture_unit.texture_2d);
+            glBindTexture(texture_unit.target, texture_unit.texture);
         }
         if (texture_unit.sampler != cur_state_texture_unit.sampler) {
             glBindSampler(static_cast<GLuint>(i), texture_unit.sampler);
@@ -214,7 +214,7 @@ void OpenGLState::Apply() const {
             texture_unit.swizzle.a != cur_state_texture_unit.swizzle.a) {
             std::array<GLint, 4> mask = {texture_unit.swizzle.r, texture_unit.swizzle.g,
                                          texture_unit.swizzle.b, texture_unit.swizzle.a};
-            glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask.data());
+            glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data());
         }
     }
 
@@ -287,7 +287,7 @@ void OpenGLState::Apply() const {
 
 OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
     for (auto& unit : texture_units) {
-        if (unit.texture_2d == handle) {
+        if (unit.texture == handle) {
             unit.Unbind();
         }
     }

+ 5 - 3
src/video_core/renderer_opengl/gl_state.h

@@ -94,8 +94,9 @@ public:
 
     // 3 texture units - one for each that is used in PICA fragment shader emulation
     struct TextureUnit {
-        GLuint texture_2d; // GL_TEXTURE_BINDING_2D
-        GLuint sampler;    // GL_SAMPLER_BINDING
+        GLuint texture; // GL_TEXTURE_BINDING_2D
+        GLuint sampler; // GL_SAMPLER_BINDING
+        GLenum target;
         struct {
             GLint r; // GL_TEXTURE_SWIZZLE_R
             GLint g; // GL_TEXTURE_SWIZZLE_G
@@ -104,7 +105,7 @@ public:
         } swizzle;
 
         void Unbind() {
-            texture_2d = 0;
+            texture = 0;
             swizzle.r = GL_RED;
             swizzle.g = GL_GREEN;
             swizzle.b = GL_BLUE;
@@ -114,6 +115,7 @@ public:
         void Reset() {
             Unbind();
             sampler = 0;
+            target = GL_TEXTURE_2D;
         }
     };
     std::array<TextureUnit, 32> texture_units;

+ 10 - 10
src/video_core/renderer_opengl/renderer_opengl.cpp

@@ -177,7 +177,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
                                        Memory::GetPointer(framebuffer_addr),
                                        gl_framebuffer_data.data(), true);
 
-        state.texture_units[0].texture_2d = screen_info.texture.resource.handle;
+        state.texture_units[0].texture = screen_info.texture.resource.handle;
         state.Apply();
 
         glActiveTexture(GL_TEXTURE0);
@@ -194,7 +194,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
 
         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
 
-        state.texture_units[0].texture_2d = 0;
+        state.texture_units[0].texture = 0;
         state.Apply();
     }
 }
@@ -205,7 +205,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
  */
 void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
                                                 const TextureInfo& texture) {
-    state.texture_units[0].texture_2d = texture.resource.handle;
+    state.texture_units[0].texture = texture.resource.handle;
     state.Apply();
 
     glActiveTexture(GL_TEXTURE0);
@@ -214,7 +214,7 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
     // Update existing texture
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data);
 
-    state.texture_units[0].texture_2d = 0;
+    state.texture_units[0].texture = 0;
     state.Apply();
 }
 
@@ -260,7 +260,7 @@ void RendererOpenGL::InitOpenGLObjects() {
     // Allocation of storage is deferred until the first frame, when we
     // know the framebuffer size.
 
-    state.texture_units[0].texture_2d = screen_info.texture.resource.handle;
+    state.texture_units[0].texture = screen_info.texture.resource.handle;
     state.Apply();
 
     glActiveTexture(GL_TEXTURE0);
@@ -272,7 +272,7 @@ void RendererOpenGL::InitOpenGLObjects() {
 
     screen_info.display_texture = screen_info.texture.resource.handle;
 
-    state.texture_units[0].texture_2d = 0;
+    state.texture_units[0].texture = 0;
     state.Apply();
 
     // Clear screen to black
@@ -305,14 +305,14 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
         UNREACHABLE();
     }
 
-    state.texture_units[0].texture_2d = texture.resource.handle;
+    state.texture_units[0].texture = texture.resource.handle;
     state.Apply();
 
     glActiveTexture(GL_TEXTURE0);
     glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0,
                  texture.gl_format, texture.gl_type, nullptr);
 
-    state.texture_units[0].texture_2d = 0;
+    state.texture_units[0].texture = 0;
     state.Apply();
 }
 
@@ -354,14 +354,14 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
         ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v),
     }};
 
-    state.texture_units[0].texture_2d = screen_info.display_texture;
+    state.texture_units[0].texture = screen_info.display_texture;
     state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
     state.Apply();
 
     glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
-    state.texture_units[0].texture_2d = 0;
+    state.texture_units[0].texture = 0;
     state.Apply();
 }
 

+ 10 - 2
src/video_core/textures/texture.h

@@ -170,8 +170,12 @@ struct TICEntry {
         BitField<0, 16, u32> width_minus_1;
         BitField<23, 4, TextureType> texture_type;
     };
-    u16 height_minus_1;
-    INSERT_PADDING_BYTES(10);
+    union {
+        BitField<0, 16, u32> height_minus_1;
+        BitField<16, 15, u32> depth_minus_1;
+    };
+
+    INSERT_PADDING_BYTES(8);
 
     GPUVAddr Address() const {
         return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low);
@@ -192,6 +196,10 @@ struct TICEntry {
         return height_minus_1 + 1;
     }
 
+    u32 Depth() const {
+        return depth_minus_1 + 1;
+    }
+
     u32 BlockHeight() const {
         ASSERT(header_version == TICHeaderVersion::BlockLinear ||
                header_version == TICHeaderVersion::BlockLinearColorKey);