From 32c1bc6a67820f9df21c8f64f4df078b015aa7da Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 6 Nov 2019 04:32:43 -0300
Subject: shader/texture: Deduce texture buffers from locker

Instead of specializing shaders to separate texture buffers from 1D
textures, use the locker to deduce them while they are being decoded.
---
 src/video_core/renderer_opengl/gl_rasterizer.cpp   |  45 +++------
 src/video_core/renderer_opengl/gl_rasterizer.h     |  15 ++-
 src/video_core/renderer_opengl/gl_shader_cache.cpp |  12 ---
 .../renderer_opengl/gl_shader_decompiler.cpp       |  39 ++------
 .../renderer_opengl/gl_shader_disk_cache.cpp       |  33 +++----
 .../renderer_opengl/gl_shader_disk_cache.h         |   8 +-
 src/video_core/shader/decode/texture.cpp           | 102 +++++++++------------
 src/video_core/shader/node.h                       |  14 ++-
 src/video_core/shader/shader_ir.h                  |  13 ++-
 9 files changed, 107 insertions(+), 174 deletions(-)

(limited to 'src')

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 05f8e511b4..b76de71ec6 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -271,9 +271,9 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
         const auto stage = static_cast<Maxwell::ShaderStage>(index == 0 ? 0 : index - 1);
         SetupDrawConstBuffers(stage, shader);
         SetupDrawGlobalMemory(stage, shader);
-        const auto texture_buffer_usage{SetupDrawTextures(stage, shader, base_bindings)};
+        SetupDrawTextures(stage, shader, base_bindings);
 
-        const ProgramVariant variant{base_bindings, primitive_mode, texture_buffer_usage};
+        const ProgramVariant variant{base_bindings, primitive_mode};
         const auto [program_handle, next_bindings] = shader->GetProgramHandle(variant);
 
         switch (program) {
@@ -303,7 +303,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
         // When VertexA is enabled, we have dual vertex shaders
         if (program == Maxwell::ShaderProgram::VertexA) {
             // VertexB was combined with VertexA, so we skip the VertexB iteration
-            index++;
+            ++index;
         }
 
         base_bindings = next_bindings;
@@ -732,11 +732,10 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
     }
 
     auto kernel = shader_cache.GetComputeKernel(code_addr);
-    ProgramVariant variant;
-    variant.texture_buffer_usage = SetupComputeTextures(kernel);
+    SetupComputeTextures(kernel);
     SetupComputeImages(kernel);
 
-    const auto [program, next_bindings] = kernel->GetProgramHandle(variant);
+    const auto [program, next_bindings] = kernel->GetProgramHandle({});
     state.draw.shader_program = program;
     state.draw.program_pipeline = 0;
 
@@ -918,9 +917,8 @@ void RasterizerOpenGL::SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entr
     bind_ssbo_pushbuffer.Push(ssbo, buffer_offset, static_cast<GLsizeiptr>(size));
 }
 
-TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stage,
-                                                       const Shader& shader,
-                                                       BaseBindings base_bindings) {
+void RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stage, const Shader& shader,
+                                         BaseBindings base_bindings) {
     MICROPROFILE_SCOPE(OpenGL_Texture);
     const auto& gpu = system.GPU();
     const auto& maxwell3d = gpu.Maxwell3D();
@@ -929,8 +927,6 @@ TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stag
     ASSERT_MSG(base_bindings.sampler + entries.size() <= std::size(state.textures),
                "Exceeded the number of active textures.");
 
-    TextureBufferUsage texture_buffer_usage{0};
-
     for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
         const auto& entry = entries[bindpoint];
         const auto texture = [&] {
@@ -943,15 +939,11 @@ TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stag
             return maxwell3d.GetTextureInfo(tex_handle);
         }();
 
-        if (SetupTexture(base_bindings.sampler + bindpoint, texture, entry)) {
-            texture_buffer_usage.set(bindpoint);
-        }
+        SetupTexture(base_bindings.sampler + bindpoint, texture, entry);
     }
-
-    return texture_buffer_usage;
 }
 
-TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
+void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
     MICROPROFILE_SCOPE(OpenGL_Texture);
     const auto& compute = system.GPU().KeplerCompute();
     const auto& entries = kernel->GetShaderEntries().samplers;
@@ -959,8 +951,6 @@ TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel)
     ASSERT_MSG(entries.size() <= std::size(state.textures),
                "Exceeded the number of active textures.");
 
-    TextureBufferUsage texture_buffer_usage{0};
-
     for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
         const auto& entry = entries[bindpoint];
         const auto texture = [&] {
@@ -972,34 +962,29 @@ TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel)
             return compute.GetTextureInfo(tex_handle);
         }();
 
-        if (SetupTexture(bindpoint, texture, entry)) {
-            texture_buffer_usage.set(bindpoint);
-        }
+        SetupTexture(bindpoint, texture, entry);
     }
-
-    return texture_buffer_usage;
 }
 
-bool RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
+void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
                                     const GLShader::SamplerEntry& entry) {
-    state.samplers[binding] = sampler_cache.GetSampler(texture.tsc);
-
     const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
     if (!view) {
         // Can occur when texture addr is null or its memory is unmapped/invalid
+        state.samplers[binding] = 0;
         state.textures[binding] = 0;
-        return false;
+        return;
     }
     state.textures[binding] = view->GetTexture();
 
     if (view->GetSurfaceParams().IsBuffer()) {
-        return true;
+        return;
     }
+    state.samplers[binding] = sampler_cache.GetSampler(texture.tsc);
 
     // Apply swizzle to textures that are not buffers.
     view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
                        texture.tic.w_source);
-    return false;
 }
 
 void RasterizerOpenGL::SetupComputeImages(const Shader& shader) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index bd6fe5c3a7..0e0819d59f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -107,16 +107,15 @@ private:
     /// Syncs all the state, shaders, render targets and textures setting before a draw call.
     void DrawPrelude();
 
-    /// Configures the current textures to use for the draw command. Returns shaders texture buffer
-    /// usage.
-    TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
-                                         const Shader& shader, BaseBindings base_bindings);
+    /// Configures the current textures to use for the draw command.
+    void SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, const Shader& shader,
+                           BaseBindings base_bindings);
 
-    /// Configures the textures used in a compute shader. Returns texture buffer usage.
-    TextureBufferUsage SetupComputeTextures(const Shader& kernel);
+    /// Configures the textures used in a compute shader.
+    void SetupComputeTextures(const Shader& kernel);
 
-    /// Configures a texture. Returns true when the texture is a texture buffer.
-    bool SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
+    /// Configures a texture.
+    void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
                       const GLShader::SamplerEntry& entry);
 
     /// Configures images in a compute shader.
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 04a239a398..7ce06a978a 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -270,7 +270,6 @@ CachedProgram BuildShader(const Device& device, u64 unique_identifier, ProgramTy
 
     auto base_bindings{variant.base_bindings};
     const auto primitive_mode{variant.primitive_mode};
-    const auto texture_buffer_usage{variant.texture_buffer_usage};
 
     std::string source = fmt::format(R"(// {}
 #version 430 core
@@ -317,17 +316,6 @@ CachedProgram BuildShader(const Device& device, u64 unique_identifier, ProgramTy
             fmt::format("#define IMAGE_BINDING_{} {}\n", image.GetIndex(), base_bindings.image++);
     }
 
-    // Transform 1D textures to texture samplers by declaring its preprocessor macros.
-    for (std::size_t i = 0; i < texture_buffer_usage.size(); ++i) {
-        if (!texture_buffer_usage.test(i)) {
-            continue;
-        }
-        source += fmt::format("#define SAMPLER_{}_IS_BUFFER\n", i);
-    }
-    if (texture_buffer_usage.any()) {
-        source += '\n';
-    }
-
     if (program_type == ProgramType::Geometry) {
         const auto [glsl_topology, debug_name, max_vertices] =
             GetPrimitiveDescription(primitive_mode);
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 4f2b491707..51c80bf32c 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -658,9 +658,11 @@ private:
             const std::string description{"layout (binding = SAMPLER_BINDING_" +
                                           std::to_string(sampler.GetIndex()) + ") uniform"};
             std::string sampler_type = [&]() {
+                if (sampler.IsBuffer()) {
+                    return "samplerBuffer";
+                }
                 switch (sampler.GetType()) {
                 case Tegra::Shader::TextureType::Texture1D:
-                    // Special cased, read below.
                     return "sampler1D";
                 case Tegra::Shader::TextureType::Texture2D:
                     return "sampler2D";
@@ -680,19 +682,7 @@ private:
                 sampler_type += "Shadow";
             }
 
-            if (sampler.GetType() == Tegra::Shader::TextureType::Texture1D) {
-                // 1D textures can be aliased to texture buffers, hide the declarations behind a
-                // preprocessor flag and use one or the other from the GPU state. This has to be
-                // done because shaders don't have enough information to determine the texture type.
-                EmitIfdefIsBuffer(sampler);
-                code.AddLine("{} samplerBuffer {};", description, name);
-                code.AddLine("#else");
-                code.AddLine("{} {} {};", description, sampler_type, name);
-                code.AddLine("#endif");
-            } else {
-                // The other texture types (2D, 3D and cubes) don't have this issue.
-                code.AddLine("{} {} {};", description, sampler_type, name);
-            }
+            code.AddLine("{} {} {};", description, sampler_type, name);
         }
         if (!samplers.empty()) {
             code.AddNewLine();
@@ -1749,27 +1739,14 @@ private:
                 expr += ", ";
         }
 
-        // Store a copy of the expression without the lod to be used with texture buffers
-        std::string expr_buffer = expr;
-
-        if (meta->lod) {
+        if (meta->lod && !meta->sampler.IsBuffer()) {
             expr += ", ";
             expr += Visit(meta->lod).AsInt();
         }
         expr += ')';
         expr += GetSwizzle(meta->element);
 
-        expr_buffer += ')';
-        expr_buffer += GetSwizzle(meta->element);
-
-        const std::string tmp{code.GenerateTemporary()};
-        EmitIfdefIsBuffer(meta->sampler);
-        code.AddLine("float {} = {};", tmp, expr_buffer);
-        code.AddLine("#else");
-        code.AddLine("float {} = {};", tmp, expr);
-        code.AddLine("#endif");
-
-        return {tmp, Type::Float};
+        return {std::move(expr), Type::Float};
     }
 
     Expression ImageLoad(Operation operation) {
@@ -2214,10 +2191,6 @@ private:
         return GetDeclarationWithSuffix(static_cast<u32>(image.GetIndex()), "image");
     }
 
-    void EmitIfdefIsBuffer(const Sampler& sampler) {
-        code.AddLine("#ifdef SAMPLER_{}_IS_BUFFER", sampler.GetIndex());
-    }
-
     std::string GetDeclarationWithSuffix(u32 index, std::string_view name) const {
         return fmt::format("{}_{}_{}", name, index, suffix);
     }
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 184a565e60..3f4daf28d7 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -28,34 +28,35 @@ using VideoCommon::Shader::KeyMap;
 
 namespace {
 
+using ShaderCacheVersionHash = std::array<u8, 64>;
+
+enum class TransferableEntryKind : u32 {
+    Raw,
+    Usage,
+};
+
 struct ConstBufferKey {
-    u32 cbuf;
-    u32 offset;
-    u32 value;
+    u32 cbuf{};
+    u32 offset{};
+    u32 value{};
 };
 
 struct BoundSamplerKey {
-    u32 offset;
-    Tegra::Engines::SamplerDescriptor sampler;
+    u32 offset{};
+    Tegra::Engines::SamplerDescriptor sampler{};
 };
 
 struct BindlessSamplerKey {
-    u32 cbuf;
-    u32 offset;
-    Tegra::Engines::SamplerDescriptor sampler;
-};
-
-using ShaderCacheVersionHash = std::array<u8, 64>;
-
-enum class TransferableEntryKind : u32 {
-    Raw,
-    Usage,
+    u32 cbuf{};
+    u32 offset{};
+    Tegra::Engines::SamplerDescriptor sampler{};
 };
 
-constexpr u32 NativeVersion = 5;
+constexpr u32 NativeVersion = 6;
 
 // Making sure sizes doesn't change by accident
 static_assert(sizeof(BaseBindings) == 16);
+static_assert(sizeof(ProgramVariant) == 20);
 
 ShaderCacheVersionHash GetShaderCacheVersionHash() {
     ShaderCacheVersionHash hash{};
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
index db23ada93a..55311dc6df 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
@@ -4,7 +4,6 @@
 
 #pragma once
 
-#include <bitset>
 #include <optional>
 #include <string>
 #include <tuple>
@@ -37,7 +36,6 @@ struct ShaderDiskCacheDump;
 
 using ProgramCode = std::vector<u64>;
 using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>;
-using TextureBufferUsage = std::bitset<64>;
 
 /// Allocated bindings used by an OpenGL shader program
 struct BaseBindings {
@@ -61,11 +59,10 @@ static_assert(std::is_trivially_copyable_v<BaseBindings>);
 struct ProgramVariant {
     BaseBindings base_bindings;
     GLenum primitive_mode{};
-    TextureBufferUsage texture_buffer_usage{};
 
     bool operator==(const ProgramVariant& rhs) const {
-        return std::tie(base_bindings, primitive_mode, texture_buffer_usage) ==
-               std::tie(rhs.base_bindings, rhs.primitive_mode, rhs.texture_buffer_usage);
+        return std::tie(base_bindings, primitive_mode) ==
+               std::tie(rhs.base_bindings, rhs.primitive_mode);
     }
 
     bool operator!=(const ProgramVariant& rhs) const {
@@ -112,7 +109,6 @@ template <>
 struct hash<OpenGL::ProgramVariant> {
     std::size_t operator()(const OpenGL::ProgramVariant& variant) const noexcept {
         return std::hash<OpenGL::BaseBindings>()(variant.base_bindings) ^
-               std::hash<OpenGL::TextureBufferUsage>()(variant.texture_buffer_usage) ^
                (static_cast<std::size_t>(variant.primitive_mode) << 6);
     }
 };
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index bb926a1329..695fdbd243 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -128,8 +128,8 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
         }
         const Node component = Immediate(static_cast<u32>(instr.tld4s.component));
 
-        const auto& sampler =
-            GetSampler(instr.sampler, {{TextureType::Texture2D, false, depth_compare}});
+        const SamplerInfo info{TextureType::Texture2D, false, depth_compare};
+        const auto& sampler = GetSampler(instr.sampler, info);
 
         Node4 values;
         for (u32 element = 0; element < values.size(); ++element) {
@@ -149,7 +149,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
         // Sadly, not all texture instructions specify the type of texture their sampler
         // uses. This must be fixed at a later instance.
         const auto& sampler =
-            is_bindless ? GetBindlessSampler(instr.gpr8, {}) : GetSampler(instr.sampler, {});
+            is_bindless ? GetBindlessSampler(instr.gpr8) : GetSampler(instr.sampler);
 
         u32 indexer = 0;
         switch (instr.txq.query_type) {
@@ -185,8 +185,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
         auto texture_type = instr.tmml.texture_type.Value();
         const bool is_array = instr.tmml.array != 0;
         const auto& sampler =
-            is_bindless ? GetBindlessSampler(instr.gpr20, {{texture_type, is_array, false}})
-                        : GetSampler(instr.sampler, {{texture_type, is_array, false}});
+            is_bindless ? GetBindlessSampler(instr.gpr20) : GetSampler(instr.sampler);
 
         std::vector<Node> coords;
 
@@ -254,67 +253,50 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
     return pc;
 }
 
-const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler,
-                                    std::optional<SamplerInfo> sampler_info) {
-    const auto offset = static_cast<u32>(sampler.index.Value());
-
-    TextureType type;
-    bool is_array;
-    bool is_shadow;
+ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(std::optional<SamplerInfo> sampler_info, u32 offset,
+                                               std::optional<u32> buffer) {
     if (sampler_info) {
-        type = sampler_info->type;
-        is_array = sampler_info->is_array;
-        is_shadow = sampler_info->is_shadow;
-    } else if (const auto sampler = locker.ObtainBoundSampler(offset)) {
-        type = sampler->texture_type.Value();
-        is_array = sampler->is_array.Value() != 0;
-        is_shadow = sampler->is_shadow.Value() != 0;
-    } else {
+        return *sampler_info;
+    }
+    const auto sampler =
+        buffer ? locker.ObtainBindlessSampler(*buffer, offset) : locker.ObtainBoundSampler(offset);
+    if (!sampler) {
         LOG_WARNING(HW_GPU, "Unknown sampler info");
-        type = TextureType::Texture2D;
-        is_array = false;
-        is_shadow = false;
+        return SamplerInfo{TextureType::Texture2D, false, false, false};
     }
+    return SamplerInfo{sampler->texture_type, sampler->is_array != 0, sampler->is_shadow != 0,
+                       sampler->is_buffer != 0};
+}
+
+const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler,
+                                    std::optional<SamplerInfo> sampler_info) {
+    const auto offset = static_cast<u32>(sampler.index.Value());
+    const auto info = GetSamplerInfo(sampler_info, offset);
 
     // If this sampler has already been used, return the existing mapping.
     const auto it =
         std::find_if(used_samplers.begin(), used_samplers.end(),
                      [offset](const Sampler& entry) { return entry.GetOffset() == offset; });
     if (it != used_samplers.end()) {
-        ASSERT(!it->IsBindless() && it->GetType() == type && it->IsArray() == is_array &&
-               it->IsShadow() == is_shadow);
+        ASSERT(!it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array &&
+               it->IsShadow() == info.is_shadow && it->IsBuffer() == info.is_buffer);
         return *it;
     }
 
     // Otherwise create a new mapping for this sampler
     const auto next_index = static_cast<u32>(used_samplers.size());
-    return used_samplers.emplace_back(Sampler(next_index, offset, type, is_array, is_shadow));
+    return used_samplers.emplace_back(next_index, offset, info.type, info.is_array, info.is_shadow,
+                                      info.is_buffer);
 }
 
-const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg,
+const Sampler& ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
                                             std::optional<SamplerInfo> sampler_info) {
     const Node sampler_register = GetRegister(reg);
     const auto [base_sampler, buffer, offset] =
         TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size()));
     ASSERT(base_sampler != nullptr);
 
-    TextureType type;
-    bool is_array;
-    bool is_shadow;
-    if (sampler_info) {
-        type = sampler_info->type;
-        is_array = sampler_info->is_array;
-        is_shadow = sampler_info->is_shadow;
-    } else if (const auto sampler = locker.ObtainBindlessSampler(buffer, offset)) {
-        type = sampler->texture_type.Value();
-        is_array = sampler->is_array.Value() != 0;
-        is_shadow = sampler->is_shadow.Value() != 0;
-    } else {
-        LOG_WARNING(HW_GPU, "Unknown sampler info");
-        type = TextureType::Texture2D;
-        is_array = false;
-        is_shadow = false;
-    }
+    const auto info = GetSamplerInfo(sampler_info, offset, buffer);
 
     // If this sampler has already been used, return the existing mapping.
     const auto it =
@@ -323,15 +305,15 @@ const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg,
                          return entry.GetBuffer() == buffer && entry.GetOffset() == offset;
                      });
     if (it != used_samplers.end()) {
-        ASSERT(it->IsBindless() && it->GetType() == type && it->IsArray() == is_array &&
-               it->IsShadow() == is_shadow);
+        ASSERT(it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array &&
+               it->IsShadow() == info.is_shadow);
         return *it;
     }
 
     // Otherwise create a new mapping for this sampler
     const auto next_index = static_cast<u32>(used_samplers.size());
-    return used_samplers.emplace_back(
-        Sampler(next_index, offset, buffer, type, is_array, is_shadow));
+    return used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array,
+                                      info.is_shadow, info.is_buffer);
 }
 
 void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) {
@@ -416,17 +398,16 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
                              (texture_type == TextureType::TextureCube && is_array && is_shadow),
                          "This method is not supported.");
 
+    const SamplerInfo info{texture_type, is_array, is_shadow, false};
     const auto& sampler =
-        is_bindless ? GetBindlessSampler(*bindless_reg, {{texture_type, is_array, is_shadow}})
-                    : GetSampler(instr.sampler, {{texture_type, is_array, is_shadow}});
+        is_bindless ? GetBindlessSampler(*bindless_reg, info) : GetSampler(instr.sampler, info);
 
     const bool lod_needed = process_mode == TextureProcessMode::LZ ||
                             process_mode == TextureProcessMode::LL ||
                             process_mode == TextureProcessMode::LLA;
 
-    // LOD selection (either via bias or explicit textureLod) not
-    // supported in GL for sampler2DArrayShadow and
-    // samplerCubeArrayShadow.
+    // LOD selection (either via bias or explicit textureLod) not supported in GL for
+    // sampler2DArrayShadow and samplerCubeArrayShadow.
     const bool gl_lod_supported =
         !((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && is_shadow) ||
           (texture_type == Tegra::Shader::TextureType::TextureCube && is_array && is_shadow));
@@ -436,8 +417,8 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
 
     UNIMPLEMENTED_IF(process_mode != TextureProcessMode::None && !gl_lod_supported);
 
-    Node bias = {};
-    Node lod = {};
+    Node bias;
+    Node lod;
     if (process_mode != TextureProcessMode::None && gl_lod_supported) {
         switch (process_mode) {
         case TextureProcessMode::LZ:
@@ -573,10 +554,9 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
 
     u64 parameter_register = instr.gpr20.Value();
 
-    const auto& sampler =
-        is_bindless
-            ? GetBindlessSampler(parameter_register++, {{texture_type, is_array, depth_compare}})
-            : GetSampler(instr.sampler, {{texture_type, is_array, depth_compare}});
+    const SamplerInfo info{texture_type, is_array, depth_compare, false};
+    const auto& sampler = is_bindless ? GetBindlessSampler(parameter_register++, info)
+                                      : GetSampler(instr.sampler, info);
 
     std::vector<Node> aoffi;
     if (is_aoffi) {
@@ -623,7 +603,7 @@ Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) {
     // const Node aoffi_register{is_aoffi ? GetRegister(gpr20_cursor++) : nullptr};
     // const Node multisample{is_multisample ? GetRegister(gpr20_cursor++) : nullptr};
 
-    const auto& sampler = GetSampler(instr.sampler, {{texture_type, is_array, false}});
+    const auto& sampler = GetSampler(instr.sampler);
 
     Node4 values;
     for (u32 element = 0; element < values.size(); ++element) {
@@ -659,7 +639,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is
     // When lod is used always is in gpr20
     const Node lod = lod_enabled ? GetRegister(instr.gpr20) : Immediate(0);
 
-    const auto& sampler = GetSampler(instr.sampler, {{texture_type, is_array, false}});
+    const auto& sampler = GetSampler(instr.sampler);
 
     Node4 values;
     for (u32 element = 0; element < values.size(); ++element) {
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h
index 54217e6a40..44d85d4344 100644
--- a/src/video_core/shader/node.h
+++ b/src/video_core/shader/node.h
@@ -225,14 +225,15 @@ class Sampler {
 public:
     /// This constructor is for bound samplers
     constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type,
-                               bool is_array, bool is_shadow)
-        : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow} {}
+                               bool is_array, bool is_shadow, bool is_buffer)
+        : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow},
+          is_buffer{is_buffer} {}
 
     /// This constructor is for bindless samplers
     constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type,
-                               bool is_array, bool is_shadow)
+                               bool is_array, bool is_shadow, bool is_buffer)
         : index{index}, offset{offset}, buffer{buffer}, type{type}, is_array{is_array},
-          is_shadow{is_shadow}, is_bindless{true} {}
+          is_shadow{is_shadow}, is_buffer{is_buffer}, is_bindless{true} {}
 
     constexpr u32 GetIndex() const {
         return index;
@@ -258,6 +259,10 @@ public:
         return is_shadow;
     }
 
+    constexpr bool IsBuffer() const {
+        return is_buffer;
+    }
+
     constexpr bool IsBindless() const {
         return is_bindless;
     }
@@ -270,6 +275,7 @@ private:
     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.
     bool is_shadow{};   ///< Whether the texture is being sampled as a depth texture or not.
+    bool is_buffer{};   ///< Whether the texture is a texture buffer without sampler.
     bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not.
 };
 
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 76a8498182..2f71a50d24 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -179,6 +179,7 @@ private:
         Tegra::Shader::TextureType type;
         bool is_array;
         bool is_shadow;
+        bool is_buffer;
     };
 
     void Decode();
@@ -303,13 +304,17 @@ private:
     /// Returns a predicate combiner operation
     OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation);
 
+    /// Queries the missing sampler info from the execution context.
+    SamplerInfo GetSamplerInfo(std::optional<SamplerInfo> sampler_info, u32 offset,
+                               std::optional<u32> buffer = std::nullopt);
+
     /// Accesses a texture sampler
     const Sampler& GetSampler(const Tegra::Shader::Sampler& sampler,
-                              std::optional<SamplerInfo> sampler_info);
+                              std::optional<SamplerInfo> sampler_info = std::nullopt);
 
-    // Accesses a texture sampler for a bindless texture.
-    const Sampler& GetBindlessSampler(const Tegra::Shader::Register& reg,
-                                      std::optional<SamplerInfo> sampler_info);
+    /// Accesses a texture sampler for a bindless texture.
+    const Sampler& GetBindlessSampler(Tegra::Shader::Register reg,
+                                      std::optional<SamplerInfo> sampler_info = std::nullopt);
 
     /// Accesses an image.
     Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
-- 
cgit v1.2.3-70-g09d2