shader_decompiler: Improve Accuracy of Attribute Interpolation.

This commit is contained in:
Fernando Sahmkow 2019-02-12 21:14:39 -04:00 committed by FernandoS27
parent 8490e7746a
commit 10682ad7e0
6 changed files with 74 additions and 38 deletions

View file

@ -376,9 +376,9 @@ enum class R2pMode : u64 {
}; };
enum class IpaInterpMode : u64 { enum class IpaInterpMode : u64 {
Linear = 0, Pass = 0,
Perspective = 1, Multiply = 1,
Flat = 2, Constant = 2,
Sc = 3, Sc = 3,
}; };

View file

@ -16,6 +16,13 @@ enum class OutputTopology : u32 {
TriangleStrip = 7, TriangleStrip = 7,
}; };
enum class AttributeUse : u8 {
Unused = 0,
Constant = 1,
Perspective = 2,
ScreenLinear = 3,
};
// Documentation in: // Documentation in:
// http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html#ImapTexture // http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html#ImapTexture
struct Header { struct Header {
@ -86,7 +93,13 @@ struct Header {
struct { struct {
INSERT_PADDING_BYTES(3); // ImapSystemValuesA INSERT_PADDING_BYTES(3); // ImapSystemValuesA
INSERT_PADDING_BYTES(1); // ImapSystemValuesB INSERT_PADDING_BYTES(1); // ImapSystemValuesB
INSERT_PADDING_BYTES(32); // ImapGenericVector[32] union {
BitField<0, 2, AttributeUse> x;
BitField<2, 2, AttributeUse> y;
BitField<4, 2, AttributeUse> w;
BitField<6, 2, AttributeUse> z;
u8 raw;
} imap_generic_vector[32];
INSERT_PADDING_BYTES(2); // ImapColor INSERT_PADDING_BYTES(2); // ImapColor
INSERT_PADDING_BYTES(2); // ImapSystemValuesC INSERT_PADDING_BYTES(2); // ImapSystemValuesC
INSERT_PADDING_BYTES(10); // ImapFixedFncTexture[10] INSERT_PADDING_BYTES(10); // ImapFixedFncTexture[10]
@ -103,6 +116,28 @@ struct Header {
const u32 bit = render_target * 4 + component; const u32 bit = render_target * 4 + component;
return omap.target & (1 << bit); return omap.target & (1 << bit);
} }
AttributeUse GetAttributeIndexUse(u32 attribute, u32 index) const {
return static_cast<AttributeUse>(
(imap_generic_vector[attribute].raw >> (index * 2)) & 0x03);
}
AttributeUse GetAttributeUse(u32 attribute) const {
AttributeUse result = AttributeUse::Unused;
for (u32 i = 0; i < 4; i++) {
const auto index = GetAttributeIndexUse(attribute, i);
if (index == AttributeUse::Unused) {
continue;
}
if (result == AttributeUse::Unused || result == index) {
result = index;
continue;
}
LOG_CRITICAL(HW_GPU, "Generic Attribute Conflict in Interpolation Mode");
if (index == AttributeUse::Perspective) {
result = index;
}
}
return result;
}
} ps; } ps;
}; };

View file

@ -20,6 +20,7 @@
namespace OpenGL::GLShader { namespace OpenGL::GLShader {
using Tegra::Shader::Attribute; using Tegra::Shader::Attribute;
using Tegra::Shader::AttributeUse;
using Tegra::Shader::Header; using Tegra::Shader::Header;
using Tegra::Shader::IpaInterpMode; using Tegra::Shader::IpaInterpMode;
using Tegra::Shader::IpaMode; using Tegra::Shader::IpaMode;
@ -288,34 +289,22 @@ private:
code.AddNewLine(); code.AddNewLine();
} }
std::string GetInputFlags(const IpaMode& input_mode) { std::string GetInputFlags(AttributeUse attribute) {
const IpaSampleMode sample_mode = input_mode.sampling_mode;
const IpaInterpMode interp_mode = input_mode.interpolation_mode;
std::string out; std::string out;
switch (interp_mode) { switch (attribute) {
case IpaInterpMode::Flat: case AttributeUse::Constant:
out += "flat "; out += "flat ";
break; break;
case IpaInterpMode::Linear: case AttributeUse::ScreenLinear:
out += "noperspective "; out += "noperspective ";
break; break;
case IpaInterpMode::Perspective: case AttributeUse::Perspective:
// Default, Smooth // Default, Smooth
break; break;
default: default:
UNIMPLEMENTED_MSG("Unhandled IPA interp mode: {}", static_cast<u32>(interp_mode)); LOG_CRITICAL(HW_GPU, "Unused attribute being fetched");
} UNREACHABLE();
switch (sample_mode) {
case IpaSampleMode::Centroid:
// It can be implemented with the "centroid " keyword in GLSL
UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode centroid");
break;
case IpaSampleMode::Default:
// Default, n/a
break;
default:
UNIMPLEMENTED_MSG("Unimplemented IPA sampler mode: {}", static_cast<u32>(sample_mode));
} }
return out; return out;
} }
@ -324,16 +313,11 @@ private:
const auto& attributes = ir.GetInputAttributes(); const auto& attributes = ir.GetInputAttributes();
for (const auto element : attributes) { for (const auto element : attributes) {
const Attribute::Index index = element.first; const Attribute::Index index = element.first;
const IpaMode& input_mode = *element.second.begin();
if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) { if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) {
// Skip when it's not a generic attribute // Skip when it's not a generic attribute
continue; continue;
} }
ASSERT(element.second.size() > 0);
UNIMPLEMENTED_IF_MSG(element.second.size() > 1,
"Multiple input flag modes are not supported in GLSL");
// TODO(bunnei): Use proper number of elements for these // TODO(bunnei): Use proper number of elements for these
u32 idx = static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0); u32 idx = static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
if (stage != ShaderStage::Vertex) { if (stage != ShaderStage::Vertex) {
@ -345,8 +329,14 @@ private:
if (stage == ShaderStage::Geometry) { if (stage == ShaderStage::Geometry) {
attr = "gs_" + attr + "[]"; attr = "gs_" + attr + "[]";
} }
code.AddLine("layout (location = " + std::to_string(idx) + ") " + std::string suffix;
GetInputFlags(input_mode) + "in vec4 " + attr + ';'); if (stage == ShaderStage::Fragment) {
const auto input_mode =
header.ps.GetAttributeUse(idx - GENERIC_VARYING_START_LOCATION);
suffix = GetInputFlags(input_mode);
}
code.AddLine("layout (location = " + std::to_string(idx) + ") " + suffix + "in vec4 " +
attr + ';');
} }
if (!attributes.empty()) if (!attributes.empty())
code.AddNewLine(); code.AddNewLine();

View file

@ -124,7 +124,7 @@ layout (location = 5) out vec4 FragColor5;
layout (location = 6) out vec4 FragColor6; layout (location = 6) out vec4 FragColor6;
layout (location = 7) out vec4 FragColor7; layout (location = 7) out vec4 FragColor7;
layout (location = 0) in vec4 position; layout (location = 0) in noperspective vec4 position;
layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config { layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config {
vec4 viewport_flip; vec4 viewport_flip;

View file

@ -48,7 +48,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0, UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
"Unaligned attribute loads are not supported"); "Unaligned attribute loads are not supported");
Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Pass,
Tegra::Shader::IpaSampleMode::Default}; Tegra::Shader::IpaSampleMode::Default};
u64 next_element = instr.attribute.fmt20.element; u64 next_element = instr.attribute.fmt20.element;

View file

@ -135,7 +135,18 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
instr.ipa.sample_mode.Value()}; instr.ipa.sample_mode.Value()};
const Node attr = GetInputAttribute(attribute.index, attribute.element, input_mode); const Node attr = GetInputAttribute(attribute.index, attribute.element, input_mode);
const Node value = GetSaturatedFloat(attr, instr.ipa.saturate); Node value = attr;
const Tegra::Shader::Attribute::Index index = attribute.index.Value();
if (index >= Tegra::Shader::Attribute::Index::Attribute_0 &&
index <= Tegra::Shader::Attribute::Index::Attribute_31) {
// TODO(Blinkhawk): There are cases where a perspective attribute use PASS.
// In theory by setting them as perspective, OpenGL does the perspective correction.
// A way must figured to reverse the last step of it.
if (input_mode.interpolation_mode == Tegra::Shader::IpaInterpMode::Multiply) {
value = Operation(OperationCode::FMul, PRECISE, value, GetRegister(instr.gpr20));
}
}
value = GetSaturatedFloat(value, instr.ipa.saturate);
SetRegister(bb, instr.gpr0, value); SetRegister(bb, instr.gpr0, value);
break; break;