shader/texture: Support multiple unknown sampler properties

This allows deducing some properties from the texture instruction before
asking the runtime. By doing this we can handle type mismatches in some
instructions from the renderer instead of the shader decoder.

Fixes texelFetch issues with games using 2D texture instructions on a 1D
sampler.
This commit is contained in:
ReinUsesLisp 2020-04-16 02:07:42 -03:00
parent 72deb773fd
commit 4fb921ff6b
2 changed files with 86 additions and 61 deletions

View file

@ -139,7 +139,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 SamplerInfo info{TextureType::Texture2D, false, is_depth_compare, false}; SamplerInfo info;
info.is_shadow = is_depth_compare;
const std::optional<Sampler> sampler = GetSampler(instr.sampler, info); const std::optional<Sampler> sampler = GetSampler(instr.sampler, info);
Node4 values; Node4 values;
@ -164,20 +165,20 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
"AOFFI is not implemented"); "AOFFI is not implemented");
const bool is_array = instr.txd.is_array != 0; const bool is_array = instr.txd.is_array != 0;
u64 base_reg = instr.gpr8.Value();
const auto derivate_reg = instr.gpr20.Value(); const auto derivate_reg = instr.gpr20.Value();
const auto texture_type = instr.txd.texture_type.Value(); const auto texture_type = instr.txd.texture_type.Value();
const auto coord_count = GetCoordCount(texture_type); const auto coord_count = GetCoordCount(texture_type);
Node index_var{}; u64 base_reg = instr.gpr8.Value();
const std::optional<Sampler> sampler = Node index_var;
is_bindless SamplerInfo info;
? GetBindlessSampler(base_reg, index_var, {{texture_type, is_array, false, false}}) info.type = texture_type;
: GetSampler(instr.sampler, {{texture_type, is_array, false, false}}); info.is_array = is_array;
const std::optional<Sampler> sampler = is_bindless
? GetBindlessSampler(base_reg, info, index_var)
: GetSampler(instr.sampler, info);
Node4 values; Node4 values;
if (!sampler) { if (!sampler) {
for (u32 element = 0; element < values.size(); ++element) { std::generate(values.begin(), values.end(), [this] { return Immediate(0); });
values[element] = Immediate(0);
}
WriteTexInstructionFloat(bb, instr, values); WriteTexInstructionFloat(bb, instr, values);
break; break;
} }
@ -215,12 +216,10 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
is_bindless = true; is_bindless = true;
[[fallthrough]]; [[fallthrough]];
case OpCode::Id::TXQ: { case OpCode::Id::TXQ: {
// TODO: The new commits on the texture refactor, change the way samplers work. Node index_var;
// Sadly, not all texture instructions specify the type of texture their sampler const std::optional<Sampler> sampler = is_bindless
// uses. This must be fixed at a later instance. ? GetBindlessSampler(instr.gpr8, {}, index_var)
Node index_var{}; : GetSampler(instr.sampler, {});
const std::optional<Sampler> sampler =
is_bindless ? GetBindlessSampler(instr.gpr8, index_var) : GetSampler(instr.sampler);
if (!sampler) { if (!sampler) {
u32 indexer = 0; u32 indexer = 0;
@ -268,10 +267,15 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), UNIMPLEMENTED_IF_MSG(instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
"NDV is not implemented"); "NDV is not implemented");
auto texture_type = instr.tmml.texture_type.Value(); const auto texture_type = instr.tmml.texture_type.Value();
Node index_var{}; const bool is_array = instr.tmml.array != 0;
SamplerInfo info;
info.type = texture_type;
info.is_array = is_array;
Node index_var;
const std::optional<Sampler> sampler = const std::optional<Sampler> sampler =
is_bindless ? GetBindlessSampler(instr.gpr20, index_var) : GetSampler(instr.sampler); is_bindless ? GetBindlessSampler(instr.gpr20, info, index_var)
: GetSampler(instr.sampler, info);
if (!sampler) { if (!sampler) {
u32 indexer = 0; u32 indexer = 0;
@ -300,12 +304,11 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
coords.push_back(GetRegister(instr.gpr8.Value() + 1)); coords.push_back(GetRegister(instr.gpr8.Value() + 1));
break; break;
default: default:
UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type)); UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<int>(texture_type));
// Fallback to interpreting as a 2D texture for now // Fallback to interpreting as a 2D texture for now
coords.push_back(GetRegister(instr.gpr8.Value() + 0)); coords.push_back(GetRegister(instr.gpr8.Value() + 0));
coords.push_back(GetRegister(instr.gpr8.Value() + 1)); coords.push_back(GetRegister(instr.gpr8.Value() + 1));
texture_type = TextureType::Texture2D;
} }
u32 indexer = 0; u32 indexer = 0;
for (u32 element = 0; element < 2; ++element) { for (u32 element = 0; element < 2; ++element) {
@ -354,23 +357,30 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
return pc; return pc;
} }
ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(std::optional<SamplerInfo> sampler_info, u32 offset, ShaderIR::SamplerInfo ShaderIR::GetSamplerInfo(SamplerInfo info, u32 offset,
std::optional<u32> buffer) { std::optional<u32> buffer) {
if (sampler_info) { if (info.IsComplete()) {
return *sampler_info; return info;
} }
const auto sampler = buffer ? registry.ObtainBindlessSampler(*buffer, offset) const auto sampler = buffer ? registry.ObtainBindlessSampler(*buffer, offset)
: registry.ObtainBoundSampler(offset); : registry.ObtainBoundSampler(offset);
if (!sampler) { if (!sampler) {
LOG_WARNING(HW_GPU, "Unknown sampler info"); LOG_WARNING(HW_GPU, "Unknown sampler info");
return SamplerInfo{TextureType::Texture2D, false, false, false}; info.type = info.type.value_or(Tegra::Shader::TextureType::Texture2D);
info.is_array = info.is_array.value_or(false);
info.is_shadow = info.is_shadow.value_or(false);
info.is_buffer = info.is_buffer.value_or(false);
return info;
} }
return SamplerInfo{sampler->texture_type, sampler->is_array != 0, sampler->is_shadow != 0, info.type = info.type.value_or(sampler->texture_type);
sampler->is_buffer != 0}; info.is_array = info.is_array.value_or(sampler->is_array != 0);
info.is_shadow = info.is_shadow.value_or(sampler->is_shadow != 0);
info.is_buffer = info.is_buffer.value_or(sampler->is_buffer != 0);
return info;
} }
std::optional<Sampler> ShaderIR::GetSampler(const Tegra::Shader::Sampler& sampler, std::optional<Sampler> ShaderIR::GetSampler(Tegra::Shader::Sampler sampler,
std::optional<SamplerInfo> sampler_info) { 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); const auto info = GetSamplerInfo(sampler_info, offset);
@ -385,12 +395,12 @@ std::optional<Sampler> ShaderIR::GetSampler(const Tegra::Shader::Sampler& sample
// 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(next_index, offset, info.type, info.is_array, info.is_shadow, return used_samplers.emplace_back(next_index, offset, *info.type, *info.is_array,
info.is_buffer, false); *info.is_shadow, *info.is_buffer, false);
} }
std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, Node& index_var, std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg, SamplerInfo info,
std::optional<SamplerInfo> sampler_info) { Node& index_var) {
const Node sampler_register = GetRegister(reg); const Node sampler_register = GetRegister(reg);
const auto [base_node, tracked_sampler_info] = const auto [base_node, tracked_sampler_info] =
TrackBindlessSampler(sampler_register, global_code, static_cast<s64>(global_code.size())); TrackBindlessSampler(sampler_register, global_code, static_cast<s64>(global_code.size()));
@ -403,7 +413,7 @@ std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
std::get_if<BindlessSamplerNode>(&*tracked_sampler_info)) { std::get_if<BindlessSamplerNode>(&*tracked_sampler_info)) {
const u32 buffer = bindless_sampler_info->GetIndex(); const u32 buffer = bindless_sampler_info->GetIndex();
const u32 offset = bindless_sampler_info->GetOffset(); const u32 offset = bindless_sampler_info->GetOffset();
const auto info = GetSamplerInfo(sampler_info, offset, buffer); info = GetSamplerInfo(info, offset, buffer);
// 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 = std::find_if(used_samplers.begin(), used_samplers.end(), const auto it = std::find_if(used_samplers.begin(), used_samplers.end(),
@ -418,13 +428,13 @@ std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
// 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(next_index, offset, buffer, info.type, info.is_array, return used_samplers.emplace_back(next_index, offset, buffer, *info.type, *info.is_array,
info.is_shadow, info.is_buffer, false); *info.is_shadow, *info.is_buffer, false);
} }
if (const auto array_sampler_info = std::get_if<ArraySamplerNode>(&*tracked_sampler_info)) { if (const auto array_sampler_info = std::get_if<ArraySamplerNode>(&*tracked_sampler_info)) {
const u32 base_offset = array_sampler_info->GetBaseOffset() / 4; const u32 base_offset = array_sampler_info->GetBaseOffset() / 4;
index_var = GetCustomVariable(array_sampler_info->GetIndexVar()); index_var = GetCustomVariable(array_sampler_info->GetIndexVar());
const auto info = GetSamplerInfo(sampler_info, base_offset); info = GetSamplerInfo(info, base_offset);
// 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 = std::find_if( const auto it = std::find_if(
@ -440,8 +450,8 @@ std::optional<Sampler> ShaderIR::GetBindlessSampler(Tegra::Shader::Register reg,
uses_indexed_samplers = true; uses_indexed_samplers = true;
// 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(next_index, base_offset, info.type, info.is_array, return used_samplers.emplace_back(next_index, base_offset, *info.type, *info.is_array,
info.is_shadow, info.is_buffer, true); *info.is_shadow, *info.is_buffer, true);
} }
return std::nullopt; return std::nullopt;
} }
@ -528,10 +538,15 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
ASSERT_MSG(texture_type != TextureType::Texture3D || !is_array || !is_shadow, ASSERT_MSG(texture_type != TextureType::Texture3D || !is_array || !is_shadow,
"Illegal texture type"); "Illegal texture type");
const SamplerInfo info{texture_type, is_array, is_shadow, false}; SamplerInfo info;
info.type = texture_type;
info.is_array = is_array;
info.is_shadow = is_shadow;
info.is_buffer = false;
Node index_var; Node index_var;
std::optional<Sampler> sampler = is_bindless const std::optional<Sampler> sampler = is_bindless
? GetBindlessSampler(*bindless_reg, index_var, info) ? GetBindlessSampler(*bindless_reg, info, index_var)
: GetSampler(instr.sampler, info); : GetSampler(instr.sampler, info);
if (!sampler) { if (!sampler) {
return {Immediate(0), Immediate(0), Immediate(0), Immediate(0)}; return {Immediate(0), Immediate(0), Immediate(0), Immediate(0)};
@ -683,10 +698,14 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
u64 parameter_register = instr.gpr20.Value(); u64 parameter_register = instr.gpr20.Value();
const SamplerInfo info{texture_type, is_array, depth_compare, false}; SamplerInfo info;
Node index_var{}; info.type = texture_type;
info.is_array = is_array;
info.is_shadow = depth_compare;
Node index_var;
const std::optional<Sampler> sampler = const std::optional<Sampler> sampler =
is_bindless ? GetBindlessSampler(parameter_register++, index_var, info) is_bindless ? GetBindlessSampler(parameter_register++, info, index_var)
: GetSampler(instr.sampler, info); : GetSampler(instr.sampler, info);
Node4 values; Node4 values;
if (!sampler) { if (!sampler) {
@ -744,12 +763,12 @@ 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); const std::optional<Sampler> sampler = GetSampler(instr.sampler, {});
Node4 values; Node4 values;
for (u32 element = 0; element < values.size(); ++element) { for (u32 element = 0; element < values.size(); ++element) {
auto coords_copy = coords; auto coords_copy = coords;
MetaTexture meta{sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element, {}}; MetaTexture meta{*sampler, array_register, {}, {}, {}, {}, {}, lod, {}, element, {}};
values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
} }
@ -757,7 +776,11 @@ Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) {
} }
Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is_array) { Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is_array) {
const Sampler& sampler = *GetSampler(instr.sampler); SamplerInfo info;
info.type = texture_type;
info.is_array = is_array;
info.is_shadow = false;
const std::optional<Sampler> sampler = GetSampler(instr.sampler, info);
const std::size_t type_coord_count = GetCoordCount(texture_type); const std::size_t type_coord_count = GetCoordCount(texture_type);
const bool lod_enabled = instr.tlds.GetTextureProcessMode() == TextureProcessMode::LL; const bool lod_enabled = instr.tlds.GetTextureProcessMode() == TextureProcessMode::LL;
@ -785,7 +808,7 @@ Node4 ShaderIR::GetTldsCode(Instruction instr, TextureType texture_type, bool is
Node4 values; Node4 values;
for (u32 element = 0; element < values.size(); ++element) { for (u32 element = 0; element < values.size(); ++element) {
auto coords_copy = coords; auto coords_copy = coords;
MetaTexture meta{sampler, array, {}, {}, {}, {}, {}, lod, {}, element, {}}; MetaTexture meta{*sampler, array, {}, {}, {}, {}, {}, lod, {}, element, {}};
values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy)); values[element] = Operation(OperationCode::TexelFetch, meta, std::move(coords_copy));
} }
return values; return values;

View file

@ -191,10 +191,14 @@ private:
friend class ASTDecoder; friend class ASTDecoder;
struct SamplerInfo { struct SamplerInfo {
Tegra::Shader::TextureType type; std::optional<Tegra::Shader::TextureType> type;
bool is_array; std::optional<bool> is_array;
bool is_shadow; std::optional<bool> is_shadow;
bool is_buffer; std::optional<bool> is_buffer;
constexpr bool IsComplete() const noexcept {
return type && is_array && is_shadow && is_buffer;
}
}; };
void Decode(); void Decode();
@ -327,17 +331,15 @@ private:
OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation); OperationCode GetPredicateCombiner(Tegra::Shader::PredOperation operation);
/// Queries the missing sampler info from the execution context. /// Queries the missing sampler info from the execution context.
SamplerInfo GetSamplerInfo(std::optional<SamplerInfo> sampler_info, u32 offset, SamplerInfo GetSamplerInfo(SamplerInfo info, u32 offset,
std::optional<u32> buffer = std::nullopt); std::optional<u32> buffer = std::nullopt);
/// Accesses a texture sampler /// Accesses a texture sampler.
std::optional<Sampler> GetSampler(const Tegra::Shader::Sampler& sampler, std::optional<Sampler> GetSampler(Tegra::Shader::Sampler sampler, SamplerInfo info);
std::optional<SamplerInfo> sampler_info = std::nullopt);
/// Accesses a texture sampler for a bindless texture. /// Accesses a texture sampler for a bindless texture.
std::optional<Sampler> GetBindlessSampler( std::optional<Sampler> GetBindlessSampler(Tegra::Shader::Register reg, SamplerInfo info,
Tegra::Shader::Register reg, Node& index_var, Node& index_var);
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);