Merge pull request #3592 from ReinUsesLisp/ipa

shader_decompiler: Remove FragCoord.w hack and change IPA implementation
This commit is contained in:
Fernando Sahmkow 2020-04-05 19:29:40 -04:00 committed by GitHub
commit 69277de29d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 74 additions and 68 deletions

View file

@ -4,6 +4,9 @@
#pragma once #pragma once
#include <array>
#include <optional>
#include "common/bit_field.h" #include "common/bit_field.h"
#include "common/common_funcs.h" #include "common/common_funcs.h"
#include "common/common_types.h" #include "common/common_types.h"
@ -16,7 +19,7 @@ enum class OutputTopology : u32 {
TriangleStrip = 7, TriangleStrip = 7,
}; };
enum class AttributeUse : u8 { enum class PixelImap : u8 {
Unused = 0, Unused = 0,
Constant = 1, Constant = 1,
Perspective = 2, Perspective = 2,
@ -24,7 +27,7 @@ enum class AttributeUse : u8 {
}; };
// 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
struct Header { struct Header {
union { union {
BitField<0, 5, u32> sph_type; BitField<0, 5, u32> sph_type;
@ -59,8 +62,8 @@ struct Header {
union { union {
BitField<0, 12, u32> max_output_vertices; BitField<0, 12, u32> max_output_vertices;
BitField<12, 8, u32> store_req_start; // NOTE: not used by geometry shaders. BitField<12, 8, u32> store_req_start; // NOTE: not used by geometry shaders.
BitField<24, 4, u32> reserved; BitField<20, 4, u32> reserved;
BitField<12, 8, u32> store_req_end; // NOTE: not used by geometry shaders. BitField<24, 8, u32> store_req_end; // NOTE: not used by geometry shaders.
} common4{}; } common4{};
union { union {
@ -93,17 +96,20 @@ struct Header {
struct { struct {
INSERT_UNION_PADDING_BYTES(3); // ImapSystemValuesA INSERT_UNION_PADDING_BYTES(3); // ImapSystemValuesA
INSERT_UNION_PADDING_BYTES(1); // ImapSystemValuesB INSERT_UNION_PADDING_BYTES(1); // ImapSystemValuesB
union { union {
BitField<0, 2, AttributeUse> x; BitField<0, 2, PixelImap> x;
BitField<2, 2, AttributeUse> y; BitField<2, 2, PixelImap> y;
BitField<4, 2, AttributeUse> w; BitField<4, 2, PixelImap> z;
BitField<6, 2, AttributeUse> z; BitField<6, 2, PixelImap> w;
u8 raw; u8 raw;
} imap_generic_vector[32]; } imap_generic_vector[32];
INSERT_UNION_PADDING_BYTES(2); // ImapColor INSERT_UNION_PADDING_BYTES(2); // ImapColor
INSERT_UNION_PADDING_BYTES(2); // ImapSystemValuesC INSERT_UNION_PADDING_BYTES(2); // ImapSystemValuesC
INSERT_UNION_PADDING_BYTES(10); // ImapFixedFncTexture[10] INSERT_UNION_PADDING_BYTES(10); // ImapFixedFncTexture[10]
INSERT_UNION_PADDING_BYTES(2); // ImapReserved INSERT_UNION_PADDING_BYTES(2); // ImapReserved
struct { struct {
u32 target; u32 target;
union { union {
@ -112,31 +118,30 @@ struct Header {
BitField<2, 30, u32> reserved; BitField<2, 30, u32> reserved;
}; };
} omap; } omap;
bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const { bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const {
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>( PixelImap GetPixelImap(u32 attribute) const {
(imap_generic_vector[attribute].raw >> (index * 2)) & 0x03); const auto get_index = [this, attribute](u32 index) {
} return static_cast<PixelImap>(
AttributeUse GetAttributeUse(u32 attribute) const { (imap_generic_vector[attribute].raw >> (index * 2)) & 3);
AttributeUse result = AttributeUse::Unused; };
for (u32 i = 0; i < 4; i++) {
const auto index = GetAttributeIndexUse(attribute, i); std::optional<PixelImap> result;
if (index == AttributeUse::Unused) { for (u32 component = 0; component < 4; ++component) {
const PixelImap index = get_index(component);
if (index == PixelImap::Unused) {
continue; continue;
} }
if (result == AttributeUse::Unused || result == index) { if (result && result != index) {
result = index; LOG_CRITICAL(HW_GPU, "Generic attribute conflict in interpolation mode");
continue;
} }
LOG_CRITICAL(HW_GPU, "Generic Attribute Conflict in Interpolation Mode");
if (index == AttributeUse::Perspective) {
result = index; result = index;
} }
} return result.value_or(PixelImap::Unused);
return result;
} }
} ps; } ps;

View file

@ -31,11 +31,11 @@ namespace {
using Tegra::Engines::ShaderType; using Tegra::Engines::ShaderType;
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;
using Tegra::Shader::IpaSampleMode; using Tegra::Shader::IpaSampleMode;
using Tegra::Shader::PixelImap;
using Tegra::Shader::Register; using Tegra::Shader::Register;
using VideoCommon::Shader::BuildTransformFeedback; using VideoCommon::Shader::BuildTransformFeedback;
using VideoCommon::Shader::Registry; using VideoCommon::Shader::Registry;
@ -702,20 +702,19 @@ private:
code.AddNewLine(); code.AddNewLine();
} }
std::string GetInputFlags(AttributeUse attribute) { const char* GetInputFlags(PixelImap attribute) {
switch (attribute) { switch (attribute) {
case AttributeUse::Perspective: case PixelImap::Perspective:
// Default, Smooth return "smooth";
return {}; case PixelImap::Constant:
case AttributeUse::Constant:
return "flat"; return "flat";
case AttributeUse::ScreenLinear: case PixelImap::ScreenLinear:
return "noperspective"; return "noperspective";
default: case PixelImap::Unused:
case AttributeUse::Unused: break;
UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<u32>(attribute));
return {};
} }
UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<int>(attribute));
return {};
} }
void DeclareInputAttributes() { void DeclareInputAttributes() {
@ -749,8 +748,8 @@ private:
std::string suffix; std::string suffix;
if (stage == ShaderType::Fragment) { if (stage == ShaderType::Fragment) {
const auto input_mode{header.ps.GetAttributeUse(location)}; const auto input_mode{header.ps.GetPixelImap(location)};
if (skip_unused && input_mode == AttributeUse::Unused) { if (input_mode == PixelImap::Unused) {
return; return;
} }
suffix = GetInputFlags(input_mode); suffix = GetInputFlags(input_mode);
@ -927,7 +926,7 @@ private:
const u32 address{generic_base + index * generic_stride + element * element_stride}; const u32 address{generic_base + index * generic_stride + element * element_stride};
const bool declared = stage != ShaderType::Fragment || const bool declared = stage != ShaderType::Fragment ||
header.ps.GetAttributeUse(index) != AttributeUse::Unused; header.ps.GetPixelImap(index) != PixelImap::Unused;
const std::string value = const std::string value =
declared ? ReadAttribute(attribute, element).AsFloat() : "0.0f"; declared ? ReadAttribute(attribute, element).AsFloat() : "0.0f";
code.AddLine("case 0x{:X}U: return {};", address, value); code.AddLine("case 0x{:X}U: return {};", address, value);
@ -1142,8 +1141,7 @@ private:
GetSwizzle(element)), GetSwizzle(element)),
Type::Float}; Type::Float};
case ShaderType::Fragment: case ShaderType::Fragment:
return {element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element)), return {"gl_FragCoord"s + GetSwizzle(element), Type::Float};
Type::Float};
default: default:
UNREACHABLE(); UNREACHABLE();
} }

View file

@ -35,7 +35,7 @@ namespace {
using Sirit::Id; using Sirit::Id;
using Tegra::Engines::ShaderType; using Tegra::Engines::ShaderType;
using Tegra::Shader::Attribute; using Tegra::Shader::Attribute;
using Tegra::Shader::AttributeUse; using Tegra::Shader::PixelImap;
using Tegra::Shader::Register; using Tegra::Shader::Register;
using namespace VideoCommon::Shader; using namespace VideoCommon::Shader;
@ -752,16 +752,16 @@ private:
if (stage != ShaderType::Fragment) { if (stage != ShaderType::Fragment) {
continue; continue;
} }
switch (header.ps.GetAttributeUse(location)) { switch (header.ps.GetPixelImap(location)) {
case AttributeUse::Constant: case PixelImap::Constant:
Decorate(id, spv::Decoration::Flat); Decorate(id, spv::Decoration::Flat);
break; break;
case AttributeUse::ScreenLinear: case PixelImap::Perspective:
Decorate(id, spv::Decoration::NoPerspective);
break;
case AttributeUse::Perspective:
// Default // Default
break; break;
case PixelImap::ScreenLinear:
Decorate(id, spv::Decoration::NoPerspective);
break;
default: default:
UNREACHABLE_MSG("Unused attribute being fetched"); UNREACHABLE_MSG("Unused attribute being fetched");
} }
@ -1145,9 +1145,6 @@ private:
switch (attribute) { switch (attribute) {
case Attribute::Index::Position: { case Attribute::Index::Position: {
if (stage == ShaderType::Fragment) { if (stage == ShaderType::Fragment) {
if (element == 3) {
return {Constant(t_float, 1.0f), Type::Float};
}
return {OpLoad(t_float, AccessElement(t_in_float, frag_coord, element)), return {OpLoad(t_float, AccessElement(t_in_float, frag_coord, element)),
Type::Float}; Type::Float};
} }

View file

@ -11,12 +11,17 @@
namespace VideoCommon::Shader { namespace VideoCommon::Shader {
using std::move;
using Tegra::Shader::ConditionCode; using Tegra::Shader::ConditionCode;
using Tegra::Shader::Instruction; using Tegra::Shader::Instruction;
using Tegra::Shader::IpaInterpMode;
using Tegra::Shader::OpCode; using Tegra::Shader::OpCode;
using Tegra::Shader::PixelImap;
using Tegra::Shader::Register; using Tegra::Shader::Register;
using Tegra::Shader::SystemVariable; using Tegra::Shader::SystemVariable;
using Index = Tegra::Shader::Attribute::Index;
u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) { u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
const Instruction instr = {program_code[pc]}; const Instruction instr = {program_code[pc]};
const auto opcode = OpCode::Decode(instr); const auto opcode = OpCode::Decode(instr);
@ -213,27 +218,28 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
} }
case OpCode::Id::IPA: { case OpCode::Id::IPA: {
const bool is_physical = instr.ipa.idx && instr.gpr8.Value() != 0xff; const bool is_physical = instr.ipa.idx && instr.gpr8.Value() != 0xff;
const auto attribute = instr.attribute.fmt28; const auto attribute = instr.attribute.fmt28;
const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(), const Index index = attribute.index;
instr.ipa.sample_mode.Value()};
Node value = is_physical ? GetPhysicalInputAttribute(instr.gpr8) Node value = is_physical ? GetPhysicalInputAttribute(instr.gpr8)
: GetInputAttribute(attribute.index, attribute.element); : GetInputAttribute(index, attribute.element);
const Tegra::Shader::Attribute::Index index = attribute.index.Value();
const bool is_generic = index >= Tegra::Shader::Attribute::Index::Attribute_0 &&
index <= Tegra::Shader::Attribute::Index::Attribute_31;
if (is_generic || is_physical) {
// 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); // Code taken from Ryujinx.
if (index >= Index::Attribute_0 && index <= Index::Attribute_31) {
const u32 location = static_cast<u32>(index) - static_cast<u32>(Index::Attribute_0);
if (header.ps.GetPixelImap(location) == PixelImap::Perspective) {
Node position_w = GetInputAttribute(Index::Position, 3);
value = Operation(OperationCode::FMul, move(value), move(position_w));
}
}
if (instr.ipa.interp_mode == IpaInterpMode::Multiply) {
value = Operation(OperationCode::FMul, move(value), GetRegister(instr.gpr20));
}
value = GetSaturatedFloat(move(value), instr.ipa.saturate);
SetRegister(bb, instr.gpr0, move(value));
break; break;
} }
case OpCode::Id::OUT_R: { case OpCode::Id::OUT_R: {