Merge pull request #3592 from ReinUsesLisp/ipa
shader_decompiler: Remove FragCoord.w hack and change IPA implementation
This commit is contained in:
commit
69277de29d
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 &&
|
|
||||||
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: {
|
||||||
|
|
Reference in New Issue