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.
This commit is contained in:
ReinUsesLisp 2019-11-06 04:32:43 -03: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

View file

@ -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,9 +917,8 @@ 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();
const auto& maxwell3d = gpu.Maxwell3D(); 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), 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;
} }
TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) { void 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;
} }
bool RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture, void 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) {

View file

@ -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.

View file

@ -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);

View file

@ -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) { code.AddLine("{} {} {};", description, sampler_type, name);
// 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);
}
} }
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);
} }

View file

@ -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{};

View file

@ -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);
} }
}; };

View file

@ -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) {

View file

@ -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.
}; };

View file

@ -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);