shader_decompiler: Remove FragCoord.w hack and change IPA implementation
Credits go to gdkchan and Ryujinx. The pull request used for this can be found here: https://github.com/Ryujinx/Ryujinx/pull/1082 yuzu was already using the header for interpolation, but it was missing the FragCoord.w multiplication described in the linked pull request. This commit finally removes the FragCoord.w == 1.0f hack from the shader decompiler. While we are at it, this commit renames some enumerations to match Nvidia's documentation (linked below) and fixes component declaration order in the shader program header (z and w were swapped). https://github.com/NVIDIA/open-gpu-doc/blob/master/Shader-Program-Header/Shader-Program-Header.html
This commit is contained in:
parent
baf91c920c
commit
2339fe199f
4 changed files with 74 additions and 68 deletions
|
@ -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;
|
return result.value_or(PixelImap::Unused);
|
||||||
}
|
}
|
||||||
} ps;
|
} ps;
|
||||||
|
|
||||||
|
|
|
@ -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 PixelImap::ScreenLinear:
|
||||||
case AttributeUse::ScreenLinear:
|
return "noperspective";
|
||||||
return "noperspective ";
|
case PixelImap::Unused:
|
||||||
default:
|
break;
|
||||||
case AttributeUse::Unused:
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 &&
|
// Code taken from Ryujinx.
|
||||||
index <= Tegra::Shader::Attribute::Index::Attribute_31;
|
if (index >= Index::Attribute_0 && index <= Index::Attribute_31) {
|
||||||
if (is_generic || is_physical) {
|
const u32 location = static_cast<u32>(index) - static_cast<u32>(Index::Attribute_0);
|
||||||
// TODO(Blinkhawk): There are cases where a perspective attribute use PASS.
|
if (header.ps.GetPixelImap(location) == PixelImap::Perspective) {
|
||||||
// In theory by setting them as perspective, OpenGL does the perspective correction.
|
Node position_w = GetInputAttribute(Index::Position, 3);
|
||||||
// A way must figured to reverse the last step of it.
|
value = Operation(OperationCode::FMul, move(value), move(position_w));
|
||||||
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);
|
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: {
|
||||||
|
|
Loading…
Reference in a new issue