yuzu-emu
/
yuzu-android
Archived
1
0
Fork 0

Merge pull request #2441 from ReinUsesLisp/al2p

shader: Implement AL2P and ALD.PHYS
This commit is contained in:
bunnei 2019-05-19 14:02:58 -04:00 committed by GitHub
commit d49efbfb4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 311 additions and 158 deletions

View File

@ -59,6 +59,7 @@ public:
static constexpr std::size_t NumCBData = 16; static constexpr std::size_t NumCBData = 16;
static constexpr std::size_t NumVertexArrays = 32; static constexpr std::size_t NumVertexArrays = 32;
static constexpr std::size_t NumVertexAttributes = 32; static constexpr std::size_t NumVertexAttributes = 32;
static constexpr std::size_t NumVaryings = 31;
static constexpr std::size_t NumTextureSamplers = 32; static constexpr std::size_t NumTextureSamplers = 32;
static constexpr std::size_t NumClipDistances = 8; static constexpr std::size_t NumClipDistances = 8;
static constexpr std::size_t MaxShaderProgram = 6; static constexpr std::size_t MaxShaderProgram = 6;

View File

@ -98,6 +98,10 @@ union Attribute {
BitField<22, 2, u64> element; BitField<22, 2, u64> element;
BitField<24, 6, Index> index; BitField<24, 6, Index> index;
BitField<47, 3, AttributeSize> size; BitField<47, 3, AttributeSize> size;
bool IsPhysical() const {
return element == 0 && static_cast<u64>(index.Value()) == 0;
}
} fmt20; } fmt20;
union { union {
@ -499,6 +503,11 @@ enum class SystemVariable : u64 {
CircularQueueEntryAddressHigh = 0x63, CircularQueueEntryAddressHigh = 0x63,
}; };
enum class PhysicalAttributeDirection : u64 {
Input = 0,
Output = 1,
};
union Instruction { union Instruction {
Instruction& operator=(const Instruction& instr) { Instruction& operator=(const Instruction& instr) {
value = instr.value; value = instr.value;
@ -587,6 +596,7 @@ union Instruction {
} alu; } alu;
union { union {
BitField<38, 1, u64> idx;
BitField<51, 1, u64> saturate; BitField<51, 1, u64> saturate;
BitField<52, 2, IpaSampleMode> sample_mode; BitField<52, 2, IpaSampleMode> sample_mode;
BitField<54, 2, IpaInterpMode> interp_mode; BitField<54, 2, IpaInterpMode> interp_mode;
@ -811,6 +821,12 @@ union Instruction {
BitField<20, 24, s64> immediate_offset; BitField<20, 24, s64> immediate_offset;
} stg; } stg;
union {
BitField<32, 1, PhysicalAttributeDirection> direction;
BitField<47, 3, AttributeSize> size;
BitField<20, 11, u64> address;
} al2p;
union { union {
BitField<0, 3, u64> pred0; BitField<0, 3, u64> pred0;
BitField<3, 3, u64> pred3; BitField<3, 3, u64> pred3;
@ -1374,8 +1390,9 @@ public:
ST_A, ST_A,
ST_L, ST_L,
ST_S, ST_S,
LDG, // Load from global memory LDG, // Load from global memory
STG, // Store in global memory STG, // Store in global memory
AL2P, // Transforms attribute memory into physical memory
TEX, TEX,
TEX_B, // Texture Load Bindless TEX_B, // Texture Load Bindless
TXQ, // Texture Query TXQ, // Texture Query
@ -1646,6 +1663,7 @@ private:
INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"), INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"),
INST("1110111011010---", Id::LDG, Type::Memory, "LDG"), INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
INST("1110111011011---", Id::STG, Type::Memory, "STG"), INST("1110111011011---", Id::STG, Type::Memory, "STG"),
INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"),
INST("110000----111---", Id::TEX, Type::Texture, "TEX"), INST("110000----111---", Id::TEX, Type::Texture, "TEX"),
INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"), INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"),
INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"), INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"),

View File

@ -21,9 +21,18 @@ T GetInteger(GLenum pname) {
Device::Device() { Device::Device() {
uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
max_varyings = GetInteger<u32>(GL_MAX_VARYING_VECTORS);
has_variable_aoffi = TestVariableAoffi(); has_variable_aoffi = TestVariableAoffi();
} }
Device::Device(std::nullptr_t) {
uniform_buffer_alignment = 0;
max_vertex_attributes = 16;
max_varyings = 15;
has_variable_aoffi = true;
}
bool Device::TestVariableAoffi() { bool Device::TestVariableAoffi() {
const GLchar* AOFFI_TEST = R"(#version 430 core const GLchar* AOFFI_TEST = R"(#version 430 core
uniform sampler2D tex; uniform sampler2D tex;

View File

@ -5,17 +5,27 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include "common/common_types.h"
namespace OpenGL { namespace OpenGL {
class Device { class Device {
public: public:
Device(); explicit Device();
explicit Device(std::nullptr_t);
std::size_t GetUniformBufferAlignment() const { std::size_t GetUniformBufferAlignment() const {
return uniform_buffer_alignment; return uniform_buffer_alignment;
} }
u32 GetMaxVertexAttributes() const {
return max_vertex_attributes;
}
u32 GetMaxVaryings() const {
return max_varyings;
}
bool HasVariableAoffi() const { bool HasVariableAoffi() const {
return has_variable_aoffi; return has_variable_aoffi;
} }
@ -24,6 +34,8 @@ private:
static bool TestVariableAoffi(); static bool TestVariableAoffi();
std::size_t uniform_buffer_alignment{}; std::size_t uniform_buffer_alignment{};
u32 max_vertex_attributes{};
u32 max_varyings{};
bool has_variable_aoffi{}; bool has_variable_aoffi{};
}; };

View File

@ -134,6 +134,19 @@ bool IsPrecise(Node node) {
return false; return false;
} }
constexpr bool IsGenericAttribute(Attribute::Index index) {
return index >= Attribute::Index::Attribute_0 && index <= Attribute::Index::Attribute_31;
}
constexpr Attribute::Index ToGenericAttribute(u32 value) {
return static_cast<Attribute::Index>(value + static_cast<u32>(Attribute::Index::Attribute_0));
}
u32 GetGenericAttributeIndex(Attribute::Index index) {
ASSERT(IsGenericAttribute(index));
return static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
}
class GLSLDecompiler final { class GLSLDecompiler final {
public: public:
explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage, explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage,
@ -152,6 +165,7 @@ public:
DeclareConstantBuffers(); DeclareConstantBuffers();
DeclareGlobalMemory(); DeclareGlobalMemory();
DeclareSamplers(); DeclareSamplers();
DeclarePhysicalAttributeReader();
code.AddLine("void execute_" + suffix + "() {"); code.AddLine("void execute_" + suffix + "() {");
++code.scope; ++code.scope;
@ -296,76 +310,95 @@ private:
} }
std::string GetInputFlags(AttributeUse attribute) { std::string GetInputFlags(AttributeUse attribute) {
std::string out;
switch (attribute) { switch (attribute) {
case AttributeUse::Constant:
out += "flat ";
break;
case AttributeUse::ScreenLinear:
out += "noperspective ";
break;
case AttributeUse::Perspective: case AttributeUse::Perspective:
// Default, Smooth // Default, Smooth
break; return {};
case AttributeUse::Constant:
return "flat ";
case AttributeUse::ScreenLinear:
return "noperspective ";
default: default:
LOG_CRITICAL(HW_GPU, "Unused attribute being fetched"); case AttributeUse::Unused:
UNREACHABLE(); UNREACHABLE_MSG("Unused attribute being fetched");
return {};
UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<u32>(attribute));
return {};
} }
return out;
} }
void DeclareInputAttributes() { void DeclareInputAttributes() {
if (ir.HasPhysicalAttributes()) {
const u32 num_inputs{GetNumPhysicalInputAttributes()};
for (u32 i = 0; i < num_inputs; ++i) {
DeclareInputAttribute(ToGenericAttribute(i), true);
}
code.AddNewLine();
return;
}
const auto& attributes = ir.GetInputAttributes(); const auto& attributes = ir.GetInputAttributes();
for (const auto element : attributes) { for (const auto index : attributes) {
const Attribute::Index index = element.first; if (IsGenericAttribute(index)) {
if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) { DeclareInputAttribute(index, false);
// Skip when it's not a generic attribute
continue;
} }
// TODO(bunnei): Use proper number of elements for these
u32 idx = static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
if (stage != ShaderStage::Vertex) {
// If inputs are varyings, add an offset
idx += GENERIC_VARYING_START_LOCATION;
}
std::string attr = GetInputAttribute(index);
if (stage == ShaderStage::Geometry) {
attr = "gs_" + attr + "[]";
}
std::string suffix;
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();
} }
void DeclareInputAttribute(Attribute::Index index, bool skip_unused) {
const u32 generic_index{GetGenericAttributeIndex(index)};
std::string name{GetInputAttribute(index)};
if (stage == ShaderStage::Geometry) {
name = "gs_" + name + "[]";
}
std::string suffix;
if (stage == ShaderStage::Fragment) {
const auto input_mode{header.ps.GetAttributeUse(generic_index)};
if (skip_unused && input_mode == AttributeUse::Unused) {
return;
}
suffix = GetInputFlags(input_mode);
}
u32 location = generic_index;
if (stage != ShaderStage::Vertex) {
// If inputs are varyings, add an offset
location += GENERIC_VARYING_START_LOCATION;
}
code.AddLine("layout (location = " + std::to_string(location) + ") " + suffix + "in vec4 " +
name + ';');
}
void DeclareOutputAttributes() { void DeclareOutputAttributes() {
if (ir.HasPhysicalAttributes() && stage != ShaderStage::Fragment) {
for (u32 i = 0; i < GetNumPhysicalVaryings(); ++i) {
DeclareOutputAttribute(ToGenericAttribute(i));
}
code.AddNewLine();
return;
}
const auto& attributes = ir.GetOutputAttributes(); const auto& attributes = ir.GetOutputAttributes();
for (const auto index : attributes) { for (const auto index : attributes) {
if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) { if (IsGenericAttribute(index)) {
// Skip when it's not a generic attribute DeclareOutputAttribute(index);
continue;
} }
// TODO(bunnei): Use proper number of elements for these
const auto idx = static_cast<u32>(index) -
static_cast<u32>(Attribute::Index::Attribute_0) +
GENERIC_VARYING_START_LOCATION;
code.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " +
GetOutputAttribute(index) + ';');
} }
if (!attributes.empty()) if (!attributes.empty())
code.AddNewLine(); code.AddNewLine();
} }
void DeclareOutputAttribute(Attribute::Index index) {
const u32 location{GetGenericAttributeIndex(index) + GENERIC_VARYING_START_LOCATION};
code.AddLine("layout (location = " + std::to_string(location) + ") out vec4 " +
GetOutputAttribute(index) + ';');
}
void DeclareConstantBuffers() { void DeclareConstantBuffers() {
for (const auto& entry : ir.GetConstantBuffers()) { for (const auto& entry : ir.GetConstantBuffers()) {
const auto [index, size] = entry; const auto [index, size] = entry;
@ -429,6 +462,39 @@ private:
code.AddNewLine(); code.AddNewLine();
} }
void DeclarePhysicalAttributeReader() {
if (!ir.HasPhysicalAttributes()) {
return;
}
code.AddLine("float readPhysicalAttribute(uint physical_address) {");
++code.scope;
code.AddLine("switch (physical_address) {");
// Just declare generic attributes for now.
const auto num_attributes{static_cast<u32>(GetNumPhysicalInputAttributes())};
for (u32 index = 0; index < num_attributes; ++index) {
const auto attribute{ToGenericAttribute(index)};
for (u32 element = 0; element < 4; ++element) {
constexpr u32 generic_base{0x80};
constexpr u32 generic_stride{16};
constexpr u32 element_stride{4};
const u32 address{generic_base + index * generic_stride + element * element_stride};
const bool declared{stage != ShaderStage::Fragment ||
header.ps.GetAttributeUse(index) != AttributeUse::Unused};
const std::string value{declared ? ReadAttribute(attribute, element) : "0"};
code.AddLine(fmt::format("case 0x{:x}: return {};", address, value));
}
}
code.AddLine("default: return 0;");
code.AddLine('}');
--code.scope;
code.AddLine('}');
code.AddNewLine();
}
void VisitBlock(const NodeBlock& bb) { void VisitBlock(const NodeBlock& bb) {
for (const Node node : bb) { for (const Node node : bb) {
if (const std::string expr = Visit(node); !expr.empty()) { if (const std::string expr = Visit(node); !expr.empty()) {
@ -483,70 +549,12 @@ private:
return value; return value;
} else if (const auto abuf = std::get_if<AbufNode>(node)) { } else if (const auto abuf = std::get_if<AbufNode>(node)) {
const auto attribute = abuf->GetIndex(); UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry,
const auto element = abuf->GetElement(); "Physical attributes in geometry shaders are not implemented");
if (abuf->IsPhysicalBuffer()) {
const auto GeometryPass = [&](const std::string& name) { return "readPhysicalAttribute(ftou(" + Visit(abuf->GetPhysicalAddress()) + "))";
if (stage == ShaderStage::Geometry && abuf->GetBuffer()) {
// TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
// set an 0x80000000 index for those and the shader fails to build. Find out why
// this happens and what's its intent.
return "gs_" + name + "[ftou(" + Visit(abuf->GetBuffer()) +
") % MAX_VERTEX_INPUT]";
}
return name;
};
switch (attribute) {
case Attribute::Index::Position:
if (stage != ShaderStage::Fragment) {
return GeometryPass("position") + GetSwizzle(element);
} else {
return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element);
}
case Attribute::Index::PointCoord:
switch (element) {
case 0:
return "gl_PointCoord.x";
case 1:
return "gl_PointCoord.y";
case 2:
case 3:
return "0";
}
UNREACHABLE();
return "0";
case Attribute::Index::TessCoordInstanceIDVertexID:
// TODO(Subv): Find out what the values are for the first two elements when inside a
// vertex shader, and what's the value of the fourth element when inside a Tess Eval
// shader.
ASSERT(stage == ShaderStage::Vertex);
switch (element) {
case 2:
// Config pack's first value is instance_id.
return "uintBitsToFloat(config_pack[0])";
case 3:
return "uintBitsToFloat(gl_VertexID)";
}
UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
return "0";
case Attribute::Index::FrontFacing:
// TODO(Subv): Find out what the values are for the other elements.
ASSERT(stage == ShaderStage::Fragment);
switch (element) {
case 3:
return "itof(gl_FrontFacing ? -1 : 0)";
}
UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
return "0";
default:
if (attribute >= Attribute::Index::Attribute_0 &&
attribute <= Attribute::Index::Attribute_31) {
return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element);
}
break;
} }
UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute)); return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer());
} else if (const auto cbuf = std::get_if<CbufNode>(node)) { } else if (const auto cbuf = std::get_if<CbufNode>(node)) {
const Node offset = cbuf->GetOffset(); const Node offset = cbuf->GetOffset();
@ -598,6 +606,69 @@ private:
return {}; return {};
} }
std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) {
const auto GeometryPass = [&](std::string name) {
if (stage == ShaderStage::Geometry && buffer) {
// TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
// set an 0x80000000 index for those and the shader fails to build. Find out why
// this happens and what's its intent.
return "gs_" + std::move(name) + "[ftou(" + Visit(buffer) + ") % MAX_VERTEX_INPUT]";
}
return name;
};
switch (attribute) {
case Attribute::Index::Position:
if (stage != ShaderStage::Fragment) {
return GeometryPass("position") + GetSwizzle(element);
} else {
return element == 3 ? "1.0f" : "gl_FragCoord" + GetSwizzle(element);
}
case Attribute::Index::PointCoord:
switch (element) {
case 0:
return "gl_PointCoord.x";
case 1:
return "gl_PointCoord.y";
case 2:
case 3:
return "0";
}
UNREACHABLE();
return "0";
case Attribute::Index::TessCoordInstanceIDVertexID:
// TODO(Subv): Find out what the values are for the first two elements when inside a
// vertex shader, and what's the value of the fourth element when inside a Tess Eval
// shader.
ASSERT(stage == ShaderStage::Vertex);
switch (element) {
case 2:
// Config pack's first value is instance_id.
return "uintBitsToFloat(config_pack[0])";
case 3:
return "uintBitsToFloat(gl_VertexID)";
}
UNIMPLEMENTED_MSG("Unmanaged TessCoordInstanceIDVertexID element={}", element);
return "0";
case Attribute::Index::FrontFacing:
// TODO(Subv): Find out what the values are for the other elements.
ASSERT(stage == ShaderStage::Fragment);
switch (element) {
case 3:
return "itof(gl_FrontFacing ? -1 : 0)";
}
UNIMPLEMENTED_MSG("Unmanaged FrontFacing element={}", element);
return "0";
default:
if (IsGenericAttribute(attribute)) {
return GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element);
}
break;
}
UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
return "0";
}
std::string ApplyPrecise(Operation operation, const std::string& value) { std::string ApplyPrecise(Operation operation, const std::string& value) {
if (!IsPrecise(operation)) { if (!IsPrecise(operation)) {
return value; return value;
@ -833,6 +904,8 @@ private:
target = GetRegister(gpr->GetIndex()); target = GetRegister(gpr->GetIndex());
} else if (const auto abuf = std::get_if<AbufNode>(dest)) { } else if (const auto abuf = std::get_if<AbufNode>(dest)) {
UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
target = [&]() -> std::string { target = [&]() -> std::string {
switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) { switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) {
case Attribute::Index::Position: case Attribute::Index::Position:
@ -844,8 +917,7 @@ private:
case Attribute::Index::ClipDistances4567: case Attribute::Index::ClipDistances4567:
return "gl_ClipDistance[" + std::to_string(abuf->GetElement() + 4) + ']'; return "gl_ClipDistance[" + std::to_string(abuf->GetElement() + 4) + ']';
default: default:
if (attribute >= Attribute::Index::Attribute_0 && if (IsGenericAttribute(attribute)) {
attribute <= Attribute::Index::Attribute_31) {
return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()); return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement());
} }
UNIMPLEMENTED_MSG("Unhandled output attribute: {}", UNIMPLEMENTED_MSG("Unhandled output attribute: {}",
@ -1591,15 +1663,11 @@ private:
} }
std::string GetInputAttribute(Attribute::Index attribute) const { std::string GetInputAttribute(Attribute::Index attribute) const {
const auto index{static_cast<u32>(attribute) - return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "input_attr");
static_cast<u32>(Attribute::Index::Attribute_0)};
return GetDeclarationWithSuffix(index, "input_attr");
} }
std::string GetOutputAttribute(Attribute::Index attribute) const { std::string GetOutputAttribute(Attribute::Index attribute) const {
const auto index{static_cast<u32>(attribute) - return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "output_attr");
static_cast<u32>(Attribute::Index::Attribute_0)};
return GetDeclarationWithSuffix(index, "output_attr");
} }
std::string GetConstBuffer(u32 index) const { std::string GetConstBuffer(u32 index) const {
@ -1640,6 +1708,19 @@ private:
return name + '_' + std::to_string(index) + '_' + suffix; return name + '_' + std::to_string(index) + '_' + suffix;
} }
u32 GetNumPhysicalInputAttributes() const {
return stage == ShaderStage::Vertex ? GetNumPhysicalAttributes() : GetNumPhysicalVaryings();
}
u32 GetNumPhysicalAttributes() const {
return std::min<u32>(device.GetMaxVertexAttributes(), Maxwell::NumVertexAttributes);
}
u32 GetNumPhysicalVaryings() const {
return std::min<u32>(device.GetMaxVaryings() - GENERIC_VARYING_START_LOCATION,
Maxwell::NumVaryings);
}
const Device& device; const Device& device;
const ShaderIR& ir; const ShaderIR& ir;
const ShaderStage stage; const ShaderStage stage;

View File

@ -194,8 +194,8 @@ public:
for (const auto& sampler : ir.GetSamplers()) { for (const auto& sampler : ir.GetSamplers()) {
entries.samplers.emplace_back(sampler); entries.samplers.emplace_back(sampler);
} }
for (const auto& attr : ir.GetInputAttributes()) { for (const auto& attribute : ir.GetInputAttributes()) {
entries.attributes.insert(GetGenericAttributeLocation(attr.first)); entries.attributes.insert(GetGenericAttributeLocation(attribute));
} }
entries.clip_distances = ir.GetClipDistances(); entries.clip_distances = ir.GetClipDistances();
entries.shader_length = ir.GetLength(); entries.shader_length = ir.GetLength();
@ -321,8 +321,7 @@ private:
} }
void DeclareInputAttributes() { void DeclareInputAttributes() {
for (const auto element : ir.GetInputAttributes()) { for (const auto index : ir.GetInputAttributes()) {
const Attribute::Index index = element.first;
if (!IsGenericAttribute(index)) { if (!IsGenericAttribute(index)) {
continue; continue;
} }

View File

@ -12,6 +12,8 @@
#include "video_core/engines/shader_bytecode.h" #include "video_core/engines/shader_bytecode.h"
#include "video_core/shader/shader_ir.h" #include "video_core/shader/shader_ir.h"
#pragma optimize("", off)
namespace VideoCommon::Shader { namespace VideoCommon::Shader {
using Tegra::Shader::Attribute; using Tegra::Shader::Attribute;
@ -47,17 +49,20 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
"Indirect attribute loads are not supported"); "Indirect attribute loads are not supported");
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");
UNIMPLEMENTED_IF_MSG(instr.attribute.fmt20.IsPhysical() &&
instr.attribute.fmt20.size != Tegra::Shader::AttributeSize::Word,
"Non-32 bits PHYS reads are not implemented");
Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Pass, const Node buffer{GetRegister(instr.gpr39)};
Tegra::Shader::IpaSampleMode::Default};
u64 next_element = instr.attribute.fmt20.element; u64 next_element = instr.attribute.fmt20.element;
auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
const auto LoadNextElement = [&](u32 reg_offset) { const auto LoadNextElement = [&](u32 reg_offset) {
const Node buffer = GetRegister(instr.gpr39); const Node attribute{instr.attribute.fmt20.IsPhysical()
const Node attribute = GetInputAttribute(static_cast<Attribute::Index>(next_index), ? GetPhysicalInputAttribute(instr.gpr8, buffer)
next_element, input_mode, buffer); : GetInputAttribute(static_cast<Attribute::Index>(next_index),
next_element, buffer)};
SetRegister(bb, instr.gpr0.Value() + reg_offset, attribute); SetRegister(bb, instr.gpr0.Value() + reg_offset, attribute);
@ -239,6 +244,21 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
} }
break; break;
} }
case OpCode::Id::AL2P: {
// Ignore al2p.direction since we don't care about it.
// Calculate emulation fake physical address.
const Node fixed_address{Immediate(static_cast<u32>(instr.al2p.address))};
const Node reg{GetRegister(instr.gpr8)};
const Node fake_address{Operation(OperationCode::IAdd, NO_PRECISE, reg, fixed_address)};
// Set the fake address to target register.
SetRegister(bb, instr.gpr0, fake_address);
// Signal the shader IR to declare all possible attributes and varyings
uses_physical_attributes = true;
break;
}
default: default:
UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName()); UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName());
} }

View File

@ -130,15 +130,18 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
break; break;
} }
case OpCode::Id::IPA: { case OpCode::Id::IPA: {
const auto& attribute = instr.attribute.fmt28; const bool is_physical = instr.ipa.idx && instr.gpr8.Value() != 0xff;
const auto attribute = instr.attribute.fmt28;
const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(), const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(),
instr.ipa.sample_mode.Value()}; instr.ipa.sample_mode.Value()};
const Node attr = GetInputAttribute(attribute.index, attribute.element, input_mode); Node value = is_physical ? GetPhysicalInputAttribute(instr.gpr8)
Node value = attr; : GetInputAttribute(attribute.index, attribute.element);
const Tegra::Shader::Attribute::Index index = attribute.index.Value(); const Tegra::Shader::Attribute::Index index = attribute.index.Value();
if (index >= Tegra::Shader::Attribute::Index::Attribute_0 && const bool is_generic = index >= Tegra::Shader::Attribute::Index::Attribute_0 &&
index <= Tegra::Shader::Attribute::Index::Attribute_31) { index <= Tegra::Shader::Attribute::Index::Attribute_31;
if (is_generic || is_physical) {
// TODO(Blinkhawk): There are cases where a perspective attribute use PASS. // TODO(Blinkhawk): There are cases where a perspective attribute use PASS.
// In theory by setting them as perspective, OpenGL does the perspective correction. // In theory by setting them as perspective, OpenGL does the perspective correction.
// A way must figured to reverse the last step of it. // A way must figured to reverse the last step of it.

View File

@ -96,13 +96,14 @@ Node ShaderIR::GetPredicate(bool immediate) {
return GetPredicate(static_cast<u64>(immediate ? Pred::UnusedIndex : Pred::NeverExecute)); return GetPredicate(static_cast<u64>(immediate ? Pred::UnusedIndex : Pred::NeverExecute));
} }
Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, Node buffer) {
const Tegra::Shader::IpaMode& input_mode, Node buffer) { used_input_attributes.emplace(index);
const auto [entry, is_new] = return StoreNode(AbufNode(index, static_cast<u32>(element), buffer));
used_input_attributes.emplace(std::make_pair(index, std::set<Tegra::Shader::IpaMode>{})); }
entry->second.insert(input_mode);
return StoreNode(AbufNode(index, static_cast<u32>(element), input_mode, buffer)); Node ShaderIR::GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer) {
uses_physical_attributes = true;
return StoreNode(AbufNode(GetRegister(physical_address), buffer));
} }
Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) { Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) {

View File

@ -456,17 +456,14 @@ private:
/// Attribute buffer memory (known as attributes or varyings in GLSL terms) /// Attribute buffer memory (known as attributes or varyings in GLSL terms)
class AbufNode final { class AbufNode final {
public: public:
explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element, // Initialize for standard attributes (index is explicit).
const Tegra::Shader::IpaMode& input_mode, Node buffer = {})
: input_mode{input_mode}, buffer{buffer}, index{index}, element{element} {}
explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element, explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
Node buffer = {}) Node buffer = {})
: input_mode{}, buffer{buffer}, index{index}, element{element} {} : buffer{buffer}, index{index}, element{element} {}
Tegra::Shader::IpaMode GetInputMode() const { // Initialize for physical attributes (index is a variable value).
return input_mode; explicit constexpr AbufNode(Node physical_address, Node buffer = {})
} : physical_address{physical_address}, buffer{buffer} {}
Tegra::Shader::Attribute::Index GetIndex() const { Tegra::Shader::Attribute::Index GetIndex() const {
return index; return index;
@ -480,11 +477,19 @@ public:
return buffer; return buffer;
} }
bool IsPhysicalBuffer() const {
return physical_address != nullptr;
}
Node GetPhysicalAddress() const {
return physical_address;
}
private: private:
const Tegra::Shader::IpaMode input_mode; Node physical_address{};
const Node buffer; Node buffer{};
const Tegra::Shader::Attribute::Index index; Tegra::Shader::Attribute::Index index{};
const u32 element; u32 element{};
}; };
/// Constant buffer node, usually mapped to uniform buffers in GLSL /// Constant buffer node, usually mapped to uniform buffers in GLSL
@ -573,8 +578,7 @@ public:
return used_predicates; return used_predicates;
} }
const std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>>& const std::set<Tegra::Shader::Attribute::Index>& GetInputAttributes() const {
GetInputAttributes() const {
return used_input_attributes; return used_input_attributes;
} }
@ -603,6 +607,10 @@ public:
return static_cast<std::size_t>(coverage_end * sizeof(u64)); return static_cast<std::size_t>(coverage_end * sizeof(u64));
} }
bool HasPhysicalAttributes() const {
return uses_physical_attributes;
}
const Tegra::Shader::Header& GetHeader() const { const Tegra::Shader::Header& GetHeader() const {
return header; return header;
} }
@ -684,8 +692,9 @@ private:
/// Generates a predicate node for an immediate true or false value /// Generates a predicate node for an immediate true or false value
Node GetPredicate(bool immediate); Node GetPredicate(bool immediate);
/// Generates a node representing an input attribute. Keeps track of used attributes. /// Generates a node representing an input attribute. Keeps track of used attributes.
Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer = {});
const Tegra::Shader::IpaMode& input_mode, Node buffer = {}); /// Generates a node representing a physical input attribute.
Node GetPhysicalInputAttribute(Tegra::Shader::Register physical_address, Node buffer = {});
/// Generates a node representing an output attribute. Keeps track of used attributes. /// Generates a node representing an output attribute. Keeps track of used attributes.
Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer); Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer);
/// Generates a node representing an internal flag /// Generates a node representing an internal flag
@ -859,13 +868,13 @@ private:
std::set<u32> used_registers; std::set<u32> used_registers;
std::set<Tegra::Shader::Pred> used_predicates; std::set<Tegra::Shader::Pred> used_predicates;
std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>> std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
used_input_attributes;
std::set<Tegra::Shader::Attribute::Index> used_output_attributes; std::set<Tegra::Shader::Attribute::Index> used_output_attributes;
std::map<u32, ConstBuffer> used_cbufs; std::map<u32, ConstBuffer> used_cbufs;
std::set<Sampler> used_samplers; std::set<Sampler> used_samplers;
std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory; std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
Tegra::Shader::Header header; Tegra::Shader::Header header;
}; };