summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/video_core/engines/maxwell_3d.h1
-rw-r--r--src/video_core/engines/shader_bytecode.h22
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_device.h14
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp313
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp7
-rw-r--r--src/video_core/shader/decode/memory.cpp30
-rw-r--r--src/video_core/shader/decode/other.cpp13
-rw-r--r--src/video_core/shader/shader_ir.cpp13
-rw-r--r--src/video_core/shader/shader_ir.h45
10 files changed, 310 insertions, 157 deletions
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 48e4fec33..f342c78e6 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -59,6 +59,7 @@ public:
static constexpr std::size_t NumCBData = 16;
static constexpr std::size_t NumVertexArrays = 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 NumClipDistances = 8;
static constexpr std::size_t MaxShaderProgram = 6;
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index e5b4eadea..7bbc556da 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -98,6 +98,10 @@ union Attribute {
BitField<22, 2, u64> element;
BitField<24, 6, Index> index;
BitField<47, 3, AttributeSize> size;
+
+ bool IsPhysical() const {
+ return element == 0 && static_cast<u64>(index.Value()) == 0;
+ }
} fmt20;
union {
@@ -499,6 +503,11 @@ enum class SystemVariable : u64 {
CircularQueueEntryAddressHigh = 0x63,
};
+enum class PhysicalAttributeDirection : u64 {
+ Input = 0,
+ Output = 1,
+};
+
union Instruction {
Instruction& operator=(const Instruction& instr) {
value = instr.value;
@@ -587,6 +596,7 @@ union Instruction {
} alu;
union {
+ BitField<38, 1, u64> idx;
BitField<51, 1, u64> saturate;
BitField<52, 2, IpaSampleMode> sample_mode;
BitField<54, 2, IpaInterpMode> interp_mode;
@@ -812,6 +822,12 @@ union Instruction {
} stg;
union {
+ BitField<32, 1, PhysicalAttributeDirection> direction;
+ BitField<47, 3, AttributeSize> size;
+ BitField<20, 11, u64> address;
+ } al2p;
+
+ union {
BitField<0, 3, u64> pred0;
BitField<3, 3, u64> pred3;
BitField<7, 1, u64> abs_a;
@@ -1374,8 +1390,9 @@ public:
ST_A,
ST_L,
ST_S,
- LDG, // Load from global memory
- STG, // Store in global memory
+ LDG, // Load from global memory
+ STG, // Store in global memory
+ AL2P, // Transforms attribute memory into physical memory
TEX,
TEX_B, // Texture Load Bindless
TXQ, // Texture Query
@@ -1646,6 +1663,7 @@ private:
INST("1110111101010---", Id::ST_L, Type::Memory, "ST_L"),
INST("1110111011010---", Id::LDG, Type::Memory, "LDG"),
INST("1110111011011---", Id::STG, Type::Memory, "STG"),
+ INST("1110111110100---", Id::AL2P, Type::Memory, "AL2P"),
INST("110000----111---", Id::TEX, Type::Texture, "TEX"),
INST("1101111010111---", Id::TEX_B, Type::Texture, "TEX_B"),
INST("1101111101001---", Id::TXQ, Type::Texture, "TXQ"),
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index b6d9e0ddb..38497678a 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -21,9 +21,18 @@ T GetInteger(GLenum pname) {
Device::Device() {
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();
}
+Device::Device(std::nullptr_t) {
+ uniform_buffer_alignment = 0;
+ max_vertex_attributes = 16;
+ max_varyings = 15;
+ has_variable_aoffi = true;
+}
+
bool Device::TestVariableAoffi() {
const GLchar* AOFFI_TEST = R"(#version 430 core
uniform sampler2D tex;
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 78ff5ee58..de8490682 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -5,17 +5,27 @@
#pragma once
#include <cstddef>
+#include "common/common_types.h"
namespace OpenGL {
class Device {
public:
- Device();
+ explicit Device();
+ explicit Device(std::nullptr_t);
std::size_t GetUniformBufferAlignment() const {
return uniform_buffer_alignment;
}
+ u32 GetMaxVertexAttributes() const {
+ return max_vertex_attributes;
+ }
+
+ u32 GetMaxVaryings() const {
+ return max_varyings;
+ }
+
bool HasVariableAoffi() const {
return has_variable_aoffi;
}
@@ -24,6 +34,8 @@ private:
static bool TestVariableAoffi();
std::size_t uniform_buffer_alignment{};
+ u32 max_vertex_attributes{};
+ u32 max_varyings{};
bool has_variable_aoffi{};
};
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 1a62795e1..4bff54a59 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -134,6 +134,19 @@ bool IsPrecise(Node node) {
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 {
public:
explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage,
@@ -152,6 +165,7 @@ public:
DeclareConstantBuffers();
DeclareGlobalMemory();
DeclareSamplers();
+ DeclarePhysicalAttributeReader();
code.AddLine("void execute_" + suffix + "() {");
++code.scope;
@@ -296,76 +310,95 @@ private:
}
std::string GetInputFlags(AttributeUse attribute) {
- std::string out;
-
switch (attribute) {
- case AttributeUse::Constant:
- out += "flat ";
- break;
- case AttributeUse::ScreenLinear:
- out += "noperspective ";
- break;
case AttributeUse::Perspective:
// Default, Smooth
- break;
+ return {};
+ case AttributeUse::Constant:
+ return "flat ";
+ case AttributeUse::ScreenLinear:
+ return "noperspective ";
default:
- LOG_CRITICAL(HW_GPU, "Unused attribute being fetched");
- UNREACHABLE();
+ case AttributeUse::Unused:
+ UNREACHABLE_MSG("Unused attribute being fetched");
+ return {};
+ UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<u32>(attribute));
+ return {};
}
- return out;
}
void DeclareInputAttributes() {
- const auto& attributes = ir.GetInputAttributes();
- for (const auto element : attributes) {
- const Attribute::Index index = element.first;
- if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) {
- // 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;
+ if (ir.HasPhysicalAttributes()) {
+ const u32 num_inputs{GetNumPhysicalInputAttributes()};
+ for (u32 i = 0; i < num_inputs; ++i) {
+ DeclareInputAttribute(ToGenericAttribute(i), true);
}
+ code.AddNewLine();
+ return;
+ }
- 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);
+ const auto& attributes = ir.GetInputAttributes();
+ for (const auto index : attributes) {
+ if (IsGenericAttribute(index)) {
+ DeclareInputAttribute(index, false);
}
- code.AddLine("layout (location = " + std::to_string(idx) + ") " + suffix + "in vec4 " +
- attr + ';');
}
if (!attributes.empty())
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() {
+ if (ir.HasPhysicalAttributes() && stage != ShaderStage::Fragment) {
+ for (u32 i = 0; i < GetNumPhysicalVaryings(); ++i) {
+ DeclareOutputAttribute(ToGenericAttribute(i));
+ }
+ code.AddNewLine();
+ return;
+ }
+
const auto& attributes = ir.GetOutputAttributes();
for (const auto index : attributes) {
- if (index < Attribute::Index::Attribute_0 || index > Attribute::Index::Attribute_31) {
- // Skip when it's not a generic attribute
- continue;
+ if (IsGenericAttribute(index)) {
+ DeclareOutputAttribute(index);
}
- // 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())
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() {
for (const auto& entry : ir.GetConstantBuffers()) {
const auto [index, size] = entry;
@@ -429,6 +462,39 @@ private:
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) {
for (const Node node : bb) {
if (const std::string expr = Visit(node); !expr.empty()) {
@@ -483,70 +549,12 @@ private:
return value;
} else if (const auto abuf = std::get_if<AbufNode>(node)) {
- const auto attribute = abuf->GetIndex();
- const auto element = abuf->GetElement();
-
- const auto GeometryPass = [&](const std::string& name) {
- 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_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry,
+ "Physical attributes in geometry shaders are not implemented");
+ if (abuf->IsPhysicalBuffer()) {
+ return "readPhysicalAttribute(ftou(" + Visit(abuf->GetPhysicalAddress()) + "))";
}
- 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)) {
const Node offset = cbuf->GetOffset();
@@ -598,6 +606,69 @@ private:
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) {
if (!IsPrecise(operation)) {
return value;
@@ -833,6 +904,8 @@ private:
target = GetRegister(gpr->GetIndex());
} else if (const auto abuf = std::get_if<AbufNode>(dest)) {
+ UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
+
target = [&]() -> std::string {
switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) {
case Attribute::Index::Position:
@@ -844,8 +917,7 @@ private:
case Attribute::Index::ClipDistances4567:
return "gl_ClipDistance[" + std::to_string(abuf->GetElement() + 4) + ']';
default:
- if (attribute >= Attribute::Index::Attribute_0 &&
- attribute <= Attribute::Index::Attribute_31) {
+ if (IsGenericAttribute(attribute)) {
return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement());
}
UNIMPLEMENTED_MSG("Unhandled output attribute: {}",
@@ -1591,15 +1663,11 @@ private:
}
std::string GetInputAttribute(Attribute::Index attribute) const {
- const auto index{static_cast<u32>(attribute) -
- static_cast<u32>(Attribute::Index::Attribute_0)};
- return GetDeclarationWithSuffix(index, "input_attr");
+ return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "input_attr");
}
std::string GetOutputAttribute(Attribute::Index attribute) const {
- const auto index{static_cast<u32>(attribute) -
- static_cast<u32>(Attribute::Index::Attribute_0)};
- return GetDeclarationWithSuffix(index, "output_attr");
+ return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "output_attr");
}
std::string GetConstBuffer(u32 index) const {
@@ -1640,6 +1708,19 @@ private:
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 ShaderIR& ir;
const ShaderStage stage;
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index a11000f6b..b61a6d170 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -194,8 +194,8 @@ public:
for (const auto& sampler : ir.GetSamplers()) {
entries.samplers.emplace_back(sampler);
}
- for (const auto& attr : ir.GetInputAttributes()) {
- entries.attributes.insert(GetGenericAttributeLocation(attr.first));
+ for (const auto& attribute : ir.GetInputAttributes()) {
+ entries.attributes.insert(GetGenericAttributeLocation(attribute));
}
entries.clip_distances = ir.GetClipDistances();
entries.shader_length = ir.GetLength();
@@ -321,8 +321,7 @@ private:
}
void DeclareInputAttributes() {
- for (const auto element : ir.GetInputAttributes()) {
- const Attribute::Index index = element.first;
+ for (const auto index : ir.GetInputAttributes()) {
if (!IsGenericAttribute(index)) {
continue;
}
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index ea1092db1..6a992c543 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -12,6 +12,8 @@
#include "video_core/engines/shader_bytecode.h"
#include "video_core/shader/shader_ir.h"
+#pragma optimize("", off)
+
namespace VideoCommon::Shader {
using Tegra::Shader::Attribute;
@@ -47,17 +49,20 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
"Indirect attribute loads are not supported");
UNIMPLEMENTED_IF_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) != 0,
"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,
- Tegra::Shader::IpaSampleMode::Default};
+ const Node buffer{GetRegister(instr.gpr39)};
u64 next_element = instr.attribute.fmt20.element;
auto next_index = static_cast<u64>(instr.attribute.fmt20.index.Value());
const auto LoadNextElement = [&](u32 reg_offset) {
- const Node buffer = GetRegister(instr.gpr39);
- const Node attribute = GetInputAttribute(static_cast<Attribute::Index>(next_index),
- next_element, input_mode, buffer);
+ const Node attribute{instr.attribute.fmt20.IsPhysical()
+ ? GetPhysicalInputAttribute(instr.gpr8, buffer)
+ : GetInputAttribute(static_cast<Attribute::Index>(next_index),
+ next_element, buffer)};
SetRegister(bb, instr.gpr0.Value() + reg_offset, attribute);
@@ -239,6 +244,21 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
}
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:
UNIMPLEMENTED_MSG("Unhandled memory instruction: {}", opcode->get().GetName());
}
diff --git a/src/video_core/shader/decode/other.cpp b/src/video_core/shader/decode/other.cpp
index d750a2936..fa17c45b5 100644
--- a/src/video_core/shader/decode/other.cpp
+++ b/src/video_core/shader/decode/other.cpp
@@ -130,15 +130,18 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
break;
}
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(),
instr.ipa.sample_mode.Value()};
- const Node attr = GetInputAttribute(attribute.index, attribute.element, input_mode);
- Node value = attr;
+ Node value = is_physical ? GetPhysicalInputAttribute(instr.gpr8)
+ : GetInputAttribute(attribute.index, attribute.element);
const Tegra::Shader::Attribute::Index index = attribute.index.Value();
- if (index >= Tegra::Shader::Attribute::Index::Attribute_0 &&
- index <= Tegra::Shader::Attribute::Index::Attribute_31) {
+ 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.
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index 196235e5d..153ad1fd0 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -96,13 +96,14 @@ Node ShaderIR::GetPredicate(bool immediate) {
return GetPredicate(static_cast<u64>(immediate ? Pred::UnusedIndex : Pred::NeverExecute));
}
-Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element,
- const Tegra::Shader::IpaMode& input_mode, Node buffer) {
- const auto [entry, is_new] =
- used_input_attributes.emplace(std::make_pair(index, std::set<Tegra::Shader::IpaMode>{}));
- entry->second.insert(input_mode);
+Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, Node buffer) {
+ used_input_attributes.emplace(index);
+ return StoreNode(AbufNode(index, static_cast<u32>(element), buffer));
+}
- 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) {
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index e4253fdb3..0bf124252 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -456,17 +456,14 @@ private:
/// Attribute buffer memory (known as attributes or varyings in GLSL terms)
class AbufNode final {
public:
- explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
- const Tegra::Shader::IpaMode& input_mode, Node buffer = {})
- : input_mode{input_mode}, buffer{buffer}, index{index}, element{element} {}
-
+ // Initialize for standard attributes (index is explicit).
explicit constexpr AbufNode(Tegra::Shader::Attribute::Index index, u32 element,
Node buffer = {})
- : input_mode{}, buffer{buffer}, index{index}, element{element} {}
+ : buffer{buffer}, index{index}, element{element} {}
- Tegra::Shader::IpaMode GetInputMode() const {
- return input_mode;
- }
+ // Initialize for physical attributes (index is a variable value).
+ explicit constexpr AbufNode(Node physical_address, Node buffer = {})
+ : physical_address{physical_address}, buffer{buffer} {}
Tegra::Shader::Attribute::Index GetIndex() const {
return index;
@@ -480,11 +477,19 @@ public:
return buffer;
}
+ bool IsPhysicalBuffer() const {
+ return physical_address != nullptr;
+ }
+
+ Node GetPhysicalAddress() const {
+ return physical_address;
+ }
+
private:
- const Tegra::Shader::IpaMode input_mode;
- const Node buffer;
- const Tegra::Shader::Attribute::Index index;
- const u32 element;
+ Node physical_address{};
+ Node buffer{};
+ Tegra::Shader::Attribute::Index index{};
+ u32 element{};
};
/// Constant buffer node, usually mapped to uniform buffers in GLSL
@@ -573,8 +578,7 @@ public:
return used_predicates;
}
- const std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>>&
- GetInputAttributes() const {
+ const std::set<Tegra::Shader::Attribute::Index>& GetInputAttributes() const {
return used_input_attributes;
}
@@ -603,6 +607,10 @@ public:
return static_cast<std::size_t>(coverage_end * sizeof(u64));
}
+ bool HasPhysicalAttributes() const {
+ return uses_physical_attributes;
+ }
+
const Tegra::Shader::Header& GetHeader() const {
return header;
}
@@ -684,8 +692,9 @@ private:
/// Generates a predicate node for an immediate true or false value
Node GetPredicate(bool immediate);
/// Generates a node representing an input attribute. Keeps track of used attributes.
- Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element,
- const Tegra::Shader::IpaMode& input_mode, Node buffer = {});
+ Node GetInputAttribute(Tegra::Shader::Attribute::Index index, u64 element, 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.
Node GetOutputAttribute(Tegra::Shader::Attribute::Index index, u64 element, Node buffer);
/// Generates a node representing an internal flag
@@ -859,13 +868,13 @@ private:
std::set<u32> used_registers;
std::set<Tegra::Shader::Pred> used_predicates;
- std::map<Tegra::Shader::Attribute::Index, std::set<Tegra::Shader::IpaMode>>
- used_input_attributes;
+ std::set<Tegra::Shader::Attribute::Index> used_input_attributes;
std::set<Tegra::Shader::Attribute::Index> used_output_attributes;
std::map<u32, ConstBuffer> used_cbufs;
std::set<Sampler> used_samplers;
std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
+ bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
Tegra::Shader::Header header;
};