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.
master
ReinUsesLisp 2019-11-06 04:32:43 +07:00
parent c52f37f259
commit 32c1bc6a67
No known key found for this signature in database
GPG Key ID: 2DFC508897B39CFE
9 changed files with 109 additions and 176 deletions

@ -271,9 +271,9 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
const auto stage = static_cast<Maxwell::ShaderStage>(index == 0 ? 0 : index - 1); const auto stage = static_cast<Maxwell::ShaderStage>(index == 0 ? 0 : index - 1);
SetupDrawConstBuffers(stage, shader); SetupDrawConstBuffers(stage, shader);
SetupDrawGlobalMemory(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); const auto [program_handle, next_bindings] = shader->GetProgramHandle(variant);
switch (program) { switch (program) {
@ -303,7 +303,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
// When VertexA is enabled, we have dual vertex shaders // When VertexA is enabled, we have dual vertex shaders
if (program == Maxwell::ShaderProgram::VertexA) { if (program == Maxwell::ShaderProgram::VertexA) {
// VertexB was combined with VertexA, so we skip the VertexB iteration // VertexB was combined with VertexA, so we skip the VertexB iteration
index++; ++index;
} }
base_bindings = next_bindings; base_bindings = next_bindings;
@ -732,11 +732,10 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
} }
auto kernel = shader_cache.GetComputeKernel(code_addr); auto kernel = shader_cache.GetComputeKernel(code_addr);
ProgramVariant variant; SetupComputeTextures(kernel);
variant.texture_buffer_usage = SetupComputeTextures(kernel);
SetupComputeImages(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.shader_program = program;
state.draw.program_pipeline = 0; state.draw.program_pipeline = 0;
@ -918,8 +917,7 @@ void RasterizerOpenGL::SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entr
bind_ssbo_pushbuffer.Push(ssbo, buffer_offset, static_cast<GLsizeiptr>(size)); bind_ssbo_pushbuffer.Push(ssbo, buffer_offset, static_cast<GLsizeiptr>(size));
} }
TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stage, void RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stage, const Shader& shader,
const Shader& shader,
BaseBindings base_bindings) { BaseBindings base_bindings) {
MICROPROFILE_SCOPE(OpenGL_Texture); MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& gpu = system.GPU(); const auto& gpu = system.GPU();
@ -929,8 +927,6 @@ TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stag
ASSERT_MSG(base_bindings.sampler + entries.size() <= std::size(state.textures), ASSERT_MSG(base_bindings.sampler + entries.size() <= std::size(state.textures),
"Exceeded the number of active textures."); "Exceeded the number of active textures.");
TextureBufferUsage texture_buffer_usage{0};
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& entry = entries[bindpoint]; const auto& entry = entries[bindpoint];
const auto texture = [&] { const auto texture = [&] {
@ -943,15 +939,11 @@ TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stag
return maxwell3d.GetTextureInfo(tex_handle); return maxwell3d.GetTextureInfo(tex_handle);
}(); }();
if (SetupTexture(base_bindings.sampler + bindpoint, texture, entry)) { SetupTexture(base_bindings.sampler + bindpoint, texture, entry);
texture_buffer_usage.set(bindpoint);
} }
} }
return texture_buffer_usage; void RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
}
TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
MICROPROFILE_SCOPE(OpenGL_Texture); MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& compute = system.GPU().KeplerCompute(); const auto& compute = system.GPU().KeplerCompute();
const auto& entries = kernel->GetShaderEntries().samplers; const auto& entries = kernel->GetShaderEntries().samplers;
@ -959,8 +951,6 @@ TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel)
ASSERT_MSG(entries.size() <= std::size(state.textures), ASSERT_MSG(entries.size() <= std::size(state.textures),
"Exceeded the number of active textures."); "Exceeded the number of active textures.");
TextureBufferUsage texture_buffer_usage{0};
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& entry = entries[bindpoint]; const auto& entry = entries[bindpoint];
const auto texture = [&] { const auto texture = [&] {
@ -972,34 +962,29 @@ TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel)
return compute.GetTextureInfo(tex_handle); return compute.GetTextureInfo(tex_handle);
}(); }();
if (SetupTexture(bindpoint, texture, entry)) { SetupTexture(bindpoint, texture, entry);
texture_buffer_usage.set(bindpoint);
} }
} }
return texture_buffer_usage; void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
}
bool RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
const GLShader::SamplerEntry& entry) { const GLShader::SamplerEntry& entry) {
state.samplers[binding] = sampler_cache.GetSampler(texture.tsc);
const auto view = texture_cache.GetTextureSurface(texture.tic, entry); const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
if (!view) { if (!view) {
// Can occur when texture addr is null or its memory is unmapped/invalid // Can occur when texture addr is null or its memory is unmapped/invalid
state.samplers[binding] = 0;
state.textures[binding] = 0; state.textures[binding] = 0;
return false; return;
} }
state.textures[binding] = view->GetTexture(); state.textures[binding] = view->GetTexture();
if (view->GetSurfaceParams().IsBuffer()) { if (view->GetSurfaceParams().IsBuffer()) {
return true; return;
} }
state.samplers[binding] = sampler_cache.GetSampler(texture.tsc);
// Apply swizzle to textures that are not buffers. // Apply swizzle to textures that are not buffers.
view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source, view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
texture.tic.w_source); texture.tic.w_source);
return false;
} }
void RasterizerOpenGL::SetupComputeImages(const Shader& shader) { void RasterizerOpenGL::SetupComputeImages(const Shader& shader) {

@ -107,16 +107,15 @@ private:
/// Syncs all the state, shaders, render targets and textures setting before a draw call. /// Syncs all the state, shaders, render targets and textures setting before a draw call.
void DrawPrelude(); void DrawPrelude();
/// Configures the current textures to use for the draw command. Returns shaders texture buffer /// Configures the current textures to use for the draw command.
/// usage. void SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, const Shader& shader,
TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, BaseBindings base_bindings);
const Shader& shader, BaseBindings base_bindings);
/// Configures the textures used in a compute shader. Returns texture buffer usage. /// Configures the textures used in a compute shader.
TextureBufferUsage SetupComputeTextures(const Shader& kernel); void SetupComputeTextures(const Shader& kernel);
/// Configures a texture. Returns true when the texture is a texture buffer. /// Configures a texture.
bool SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture, void SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
const GLShader::SamplerEntry& entry); const GLShader::SamplerEntry& entry);
/// Configures images in a compute shader. /// Configures images in a compute shader.

@ -270,7 +270,6 @@ CachedProgram BuildShader(const Device& device, u64 unique_identifier, ProgramTy
auto base_bindings{variant.base_bindings}; auto base_bindings{variant.base_bindings};
const auto primitive_mode{variant.primitive_mode}; const auto primitive_mode{variant.primitive_mode};
const auto texture_buffer_usage{variant.texture_buffer_usage};
std::string source = fmt::format(R"(// {} std::string source = fmt::format(R"(// {}
#version 430 core #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++); 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) { if (program_type == ProgramType::Geometry) {
const auto [glsl_topology, debug_name, max_vertices] = const auto [glsl_topology, debug_name, max_vertices] =
GetPrimitiveDescription(primitive_mode); GetPrimitiveDescription(primitive_mode);

@ -658,9 +658,11 @@ private:
const std::string description{"layout (binding = SAMPLER_BINDING_" + const std::string description{"layout (binding = SAMPLER_BINDING_" +
std::to_string(sampler.GetIndex()) + ") uniform"}; std::to_string(sampler.GetIndex()) + ") uniform"};
std::string sampler_type = [&]() { std::string sampler_type = [&]() {
if (sampler.IsBuffer()) {
return "samplerBuffer";
}
switch (sampler.GetType()) { switch (sampler.GetType()) {
case Tegra::Shader::TextureType::Texture1D: case Tegra::Shader::TextureType::Texture1D:
// Special cased, read below.
return "sampler1D"; return "sampler1D";
case Tegra::Shader::TextureType::Texture2D: case Tegra::Shader::TextureType::Texture2D:
return "sampler2D"; return "sampler2D";
@ -680,19 +682,7 @@ private:
sampler_type += "Shadow"; 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("{} {} {};", 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);
}
} }
if (!samplers.empty()) { if (!samplers.empty()) {
code.AddNewLine(); code.AddNewLine();
@ -1749,27 +1739,14 @@ private:
expr += ", "; expr += ", ";
} }
// Store a copy of the expression without the lod to be used with texture buffers if (meta->lod && !meta->sampler.IsBuffer()) {
std::string expr_buffer = expr;
if (meta->lod) {
expr += ", "; expr += ", ";
expr += Visit(meta->lod).AsInt(); expr += Visit(meta->lod).AsInt();
} }
expr += ')'; expr += ')';
expr += GetSwizzle(meta->element); expr += GetSwizzle(meta->element);
expr_buffer += ')'; return {std::move(expr), Type::Float};
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};
} }
Expression ImageLoad(Operation operation) { Expression ImageLoad(Operation operation) {
@ -2214,10 +2191,6 @@ private:
return GetDeclarationWithSuffix(static_cast<u32>(image.GetIndex()), "image"); 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 { std::string GetDeclarationWithSuffix(u32 index, std::string_view name) const {
return fmt::format("{}_{}_{}", name, index, suffix); return fmt::format("{}_{}_{}", name, index, suffix);
} }

@ -28,23 +28,6 @@ using VideoCommon::Shader::KeyMap;
namespace { namespace {
struct ConstBufferKey {
u32 cbuf;
u32 offset;
u32 value;
};
struct BoundSamplerKey {
u32 offset;
Tegra::Engines::SamplerDescriptor sampler;
};
struct BindlessSamplerKey {
u32 cbuf;
u32 offset;
Tegra::Engines::SamplerDescriptor sampler;
};
using ShaderCacheVersionHash = std::array<u8, 64>; using ShaderCacheVersionHash = std::array<u8, 64>;
enum class TransferableEntryKind : u32 { enum class TransferableEntryKind : u32 {
@ -52,10 +35,28 @@ enum class TransferableEntryKind : u32 {
Usage, Usage,
}; };
constexpr u32 NativeVersion = 5; struct ConstBufferKey {
u32 cbuf{};
u32 offset{};
u32 value{};
};
struct BoundSamplerKey {
u32 offset{};
Tegra::Engines::SamplerDescriptor sampler{};
};
struct BindlessSamplerKey {
u32 cbuf{};
u32 offset{};
Tegra::Engines::SamplerDescriptor sampler{};
};
constexpr u32 NativeVersion = 6;
// Making sure sizes doesn't change by accident // Making sure sizes doesn't change by accident
static_assert(sizeof(BaseBindings) == 16); static_assert(sizeof(BaseBindings) == 16);
static_assert(sizeof(ProgramVariant) == 20);
ShaderCacheVersionHash GetShaderCacheVersionHash() { ShaderCacheVersionHash GetShaderCacheVersionHash() {
ShaderCacheVersionHash hash{}; ShaderCacheVersionHash hash{};

@ -4,7 +4,6 @@
#pragma once #pragma once
#include <bitset>
#include <optional> #include <optional>
#include <string> #include <string>
#include <tuple> #include <tuple>
@ -37,7 +36,6 @@ struct ShaderDiskCacheDump;
using ProgramCode = std::vector<u64>; using ProgramCode = std::vector<u64>;
using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>; using ShaderDumpsMap = std::unordered_map<ShaderDiskCacheUsage, ShaderDiskCacheDump>;
using TextureBufferUsage = std::bitset<64>;
/// Allocated bindings used by an OpenGL shader program /// Allocated bindings used by an OpenGL shader program
struct BaseBindings { struct BaseBindings {
@ -61,11 +59,10 @@ static_assert(std::is_trivially_copyable_v<BaseBindings>);
struct ProgramVariant { struct ProgramVariant {
BaseBindings base_bindings; BaseBindings base_bindings;
GLenum primitive_mode{}; GLenum primitive_mode{};
TextureBufferUsage texture_buffer_usage{};
bool operator==(const ProgramVariant& rhs) const { bool operator==(const ProgramVariant& rhs) const {
return std::tie(base_bindings, primitive_mode, texture_buffer_usage) == return std::tie(base_bindings, primitive_mode) ==
std::tie(rhs.base_bindings, rhs.primitive_mode, rhs.texture_buffer_usage); std::tie(rhs.base_bindings, rhs.primitive_mode);
} }
bool operator!=(const ProgramVariant& rhs) const { bool operator!=(const ProgramVariant& rhs) const {
@ -112,7 +109,6 @@ template <>
struct hash<OpenGL::ProgramVariant> { struct hash<OpenGL::ProgramVariant> {
std::size_t operator()(const OpenGL::ProgramVariant& variant) const noexcept { std::size_t operator()(const OpenGL::ProgramVariant& variant) const noexcept {
return std::hash<OpenGL::BaseBindings>()(variant.base_bindings) ^ 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); (static_cast<std::size_t>(variant.primitive_mode) << 6);
} }
}; };

@ -128,8 +128,8 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
} }
const Node component = Immediate(static_cast<u32>(instr.tld4s.component)); const Node component = Immediate(static_cast<u32>(instr.tld4s.component));
const auto& sampler = const SamplerInfo info{TextureType::Texture2D, false, depth_compare};
GetSampler(instr.sampler, {{TextureType::Texture2D, false, depth_compare}}); const auto& sampler = GetSampler(instr.sampler, info);
Node4 values; Node4 values;
for (u32 element = 0; element < values.size(); ++element) { 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 // Sadly, not all texture instructions specify the type of texture their sampler
// uses. This must be fixed at a later instance. // uses. This must be fixed at a later instance.
const auto& sampler = const auto& sampler =
is_bindless ? GetBindlessSampler(instr.gpr8, {}) : GetSampler(instr.sampler, {}); is_bindless ? GetBindlessSampler(instr.gpr8) : GetSampler(instr.sampler);
u32 indexer = 0; u32 indexer = 0;
switch (instr.txq.query_type) { switch (instr.txq.query_type) {
@ -185,8 +185,7 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
auto texture_type = instr.tmml.texture_type.Value(); auto texture_type = instr.tmml.texture_type.Value();
const bool is_array = instr.tmml.array != 0; const bool is_array = instr.tmml.array != 0;
const auto& sampler = const auto& sampler =
is_bindless ? GetBindlessSampler(instr.gpr20, {{texture_type, is_array, false}}) is_bindless ? GetBindlessSampler(instr.gpr20) : GetSampler(instr.sampler);
: GetSampler(instr.sampler, {{texture_type, is_array, false}});
std::vector<Node> coords; std::vector<Node> coords;
@ -254,67 +253,50 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
return pc; return pc;
} }
ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(std::optional<SamplerInfo> sampler_info, u32 offset,
std::optional<u32> buffer) {
if (sampler_info) {
return *sampler_info;
}
const auto sampler =
buffer ? locker.ObtainBindlessSampler(*buffer, offset) : locker.ObtainBoundSampler(offset);
if (!sampler) {
LOG_WARNING(HW_GPU, "Unknown sampler info");
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, const Sampler& ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler,
std::optional<SamplerInfo> sampler_info) { std::optional<SamplerInfo> sampler_info) {
const auto offset = static_cast<u32>(sampler.index.Value()); const auto offset = static_cast<u32>(sampler.index.Value());
const auto info = GetSamplerInfo(sampler_info, offset);
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.ObtainBoundSampler(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;
}
// If this sampler has already been used, return the existing mapping. // If this sampler has already been used, return the existing mapping.
const auto it = const auto it =
std::find_if(used_samplers.begin(), used_samplers.end(), std::find_if(used_samplers.begin(), used_samplers.end(),
[offset](const Sampler& entry) { return entry.GetOffset() == offset; }); [offset](const Sampler& entry) { return entry.GetOffset() == offset; });
if (it != used_samplers.end()) { if (it != used_samplers.end()) {
ASSERT(!it->IsBindless() && it->GetType() == type && it->IsArray() == is_array && ASSERT(!it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array &&
it->IsShadow() == is_shadow); it->IsShadow() == info.is_shadow && it->IsBuffer() == info.is_buffer);
return *it; return *it;
} }
// Otherwise create a new mapping for this sampler // Otherwise create a new mapping for this sampler
const auto next_index = static_cast<u32>(used_samplers.size()); 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) { std::optional<SamplerInfo> sampler_info) {
const Node sampler_register = GetRegister(reg); const Node sampler_register = GetRegister(reg);
const auto [base_sampler, buffer, offset] = const auto [base_sampler, buffer, offset] =
TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size())); TrackCbuf(sampler_register, global_code, static_cast<s64>(global_code.size()));
ASSERT(base_sampler != nullptr); ASSERT(base_sampler != nullptr);
TextureType type; const auto info = GetSamplerInfo(sampler_info, offset, buffer);
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;
}
// If this sampler has already been used, return the existing mapping. // If this sampler has already been used, return the existing mapping.
const auto it = const auto it =
@ -323,15 +305,15 @@ const Sampler& ShaderIR::GetBindlessSampler(const Tegra::Shader::Register& reg,
return entry.GetBuffer() == buffer && entry.GetOffset() == offset; return entry.GetBuffer() == buffer && entry.GetOffset() == offset;
}); });
if (it != used_samplers.end()) { if (it != used_samplers.end()) {
ASSERT(it->IsBindless() && it->GetType() == type && it->IsArray() == is_array && ASSERT(it->IsBindless() && it->GetType() == info.type && it->IsArray() == info.is_array &&
it->IsShadow() == is_shadow); it->IsShadow() == info.is_shadow);
return *it; return *it;
} }
// Otherwise create a new mapping for this sampler // Otherwise create a new mapping for this sampler
const auto next_index = static_cast<u32>(used_samplers.size()); const auto next_index = static_cast<u32>(used_samplers.size());
return used_samplers.emplace_back( return used_samplers.emplace_back(next_index, offset, buffer, info.type, info.is_array,
Sampler(next_index, offset, buffer, type, is_array, is_shadow)); info.is_shadow, info.is_buffer);
} }
void ShaderIR::WriteTexInstructionFloat(NodeBlock& bb, Instruction instr, const Node4& components) { 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), (texture_type == TextureType::TextureCube && is_array && is_shadow),
"This method is not supported."); "This method is not supported.");
const SamplerInfo info{texture_type, is_array, is_shadow, false};
const auto& sampler = const auto& sampler =
is_bindless ? GetBindlessSampler(*bindless_reg, {{texture_type, is_array, is_shadow}}) is_bindless ? GetBindlessSampler(*bindless_reg, info) : GetSampler(instr.sampler, info);
: GetSampler(instr.sampler, {{texture_type, is_array, is_shadow}});
const bool lod_needed = process_mode == TextureProcessMode::LZ || const bool lod_needed = process_mode == TextureProcessMode::LZ ||
process_mode == TextureProcessMode::LL || process_mode == TextureProcessMode::LL ||
process_mode == TextureProcessMode::LLA; process_mode == TextureProcessMode::LLA;
// LOD selection (either via bias or explicit textureLod) not // LOD selection (either via bias or explicit textureLod) not supported in GL for
// supported in GL for sampler2DArrayShadow and // sampler2DArrayShadow and samplerCubeArrayShadow.
// samplerCubeArrayShadow.
const bool gl_lod_supported = const bool gl_lod_supported =
!((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && is_shadow) || !((texture_type == Tegra::Shader::TextureType::Texture2D && is_array && is_shadow) ||
(texture_type == Tegra::Shader::TextureType::TextureCube && 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); UNIMPLEMENTED_IF(process_mode != TextureProcessMode::None && !gl_lod_supported);
Node bias = {}; Node bias;
Node lod = {}; Node lod;
if (process_mode != TextureProcessMode::None && gl_lod_supported) { if (process_mode != TextureProcessMode::None && gl_lod_supported) {
switch (process_mode) { switch (process_mode) {
case TextureProcessMode::LZ: case TextureProcessMode::LZ:
@ -573,10 +554,9 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
u64 parameter_register = instr.gpr20.Value(); u64 parameter_register = instr.gpr20.Value();
const auto& sampler = const SamplerInfo info{texture_type, is_array, depth_compare, false};
is_bindless const auto& sampler = is_bindless ? GetBindlessSampler(parameter_register++, info)
? GetBindlessSampler(parameter_register++, {{texture_type, is_array, depth_compare}}) : GetSampler(instr.sampler, info);
: GetSampler(instr.sampler, {{texture_type, is_array, depth_compare}});
std::vector<Node> aoffi; std::vector<Node> aoffi;
if (is_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 aoffi_register{is_aoffi ? GetRegister(gpr20_cursor++) : nullptr};
// const Node multisample{is_multisample ? 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; Node4 values;
for (u32 element = 0; element < values.size(); ++element) { 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 // When lod is used always is in gpr20
const Node lod = lod_enabled ? GetRegister(instr.gpr20) : Immediate(0); 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; Node4 values;
for (u32 element = 0; element < values.size(); ++element) { for (u32 element = 0; element < values.size(); ++element) {

@ -225,14 +225,15 @@ class Sampler {
public: public:
/// This constructor is for bound samplers /// This constructor is for bound samplers
constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type, constexpr explicit Sampler(u32 index, u32 offset, Tegra::Shader::TextureType type,
bool is_array, bool 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} {} : index{index}, offset{offset}, type{type}, is_array{is_array}, is_shadow{is_shadow},
is_buffer{is_buffer} {}
/// This constructor is for bindless samplers /// This constructor is for bindless samplers
constexpr explicit Sampler(u32 index, u32 offset, u32 buffer, Tegra::Shader::TextureType type, 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}, : 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 { constexpr u32 GetIndex() const {
return index; return index;
@ -258,6 +259,10 @@ public:
return is_shadow; return is_shadow;
} }
constexpr bool IsBuffer() const {
return is_buffer;
}
constexpr bool IsBindless() const { constexpr bool IsBindless() const {
return is_bindless; return is_bindless;
} }
@ -270,6 +275,7 @@ private:
Tegra::Shader::TextureType type{}; ///< The type used to sample this texture (Texture2D, etc) 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_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_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. bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not.
}; };

@ -179,6 +179,7 @@ private:
Tegra::Shader::TextureType type; Tegra::Shader::TextureType type;
bool is_array; bool is_array;
bool is_shadow; bool is_shadow;
bool is_buffer;
}; };
void Decode(); void Decode();
@ -303,13 +304,17 @@ private:
/// Returns a predicate combiner operation /// Returns a predicate combiner operation
OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation 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 /// Accesses a texture sampler
const Sampler& GetSampler(const Tegra::Shader::Sampler& 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. /// Accesses a texture sampler for a bindless texture.
const Sampler& GetBindlessSampler(const Tegra::Shader::Register& reg, const Sampler& GetBindlessSampler(Tegra::Shader::Register reg,
std::optional<SamplerInfo> sampler_info); std::optional<SamplerInfo> sampler_info = std::nullopt);
/// Accesses an image. /// Accesses an image.
Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type); Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);