gl_shader_decompiler: Implement GLSL physical attributes

This commit is contained in:
ReinUsesLisp 2019-04-30 19:36:18 -03:00
parent 71aa9d0877
commit c6f9e651b2
2 changed files with 101 additions and 66 deletions

View File

@ -165,6 +165,7 @@ public:
DeclareConstantBuffers();
DeclareGlobalMemory();
DeclareSamplers();
DeclarePhysicalAttributeReader();
code.AddLine("void execute_" + suffix + "() {");
++code.scope;
@ -330,8 +331,7 @@ private:
void DeclareInputAttributes() {
if (ir.HasPhysicalAttributes()) {
const u32 num_inputs{stage == ShaderStage::Vertex ? GetNumPhysicalAttributes()
: GetNumPhysicalVaryings()};
const u32 num_inputs{GetNumPhysicalInputAttributes()};
for (u32 i = 0; i < num_inputs; ++i) {
DeclareInputAttribute(ToGenericAttribute(i));
}
@ -374,7 +374,7 @@ private:
}
void DeclareOutputAttributes() {
if (ir.HasPhysicalAttributes()) {
if (ir.HasPhysicalAttributes() && stage != ShaderStage::Fragment) {
for (u32 i = 0; i < GetNumPhysicalVaryings(); ++i) {
DeclareOutputAttribute(ToGenericAttribute(i));
}
@ -461,6 +461,31 @@ 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>(GetNumPhysicalAttributes())};
for (u32 index = 0; index < num_attributes; ++index) {
for (u32 element = 0; element < 4; ++element) {
code.AddLine(fmt::format("case 0x{:x}: return {};", 0x80 + index * 16 + element * 4,
ReadAttribute(ToGenericAttribute(index), element)));
}
}
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()) {
@ -515,16 +540,70 @@ private:
return value;
} else if (const auto abuf = std::get_if<AbufNode>(node)) {
const auto attribute = abuf->GetIndex();
const auto element = abuf->GetElement();
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()) + "))";
}
return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer());
const auto GeometryPass = [&](const std::string& name) {
if (stage == ShaderStage::Geometry && abuf->GetBuffer()) {
} else if (const auto cbuf = std::get_if<CbufNode>(node)) {
const Node offset = cbuf->GetOffset();
if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
// Direct access
const u32 offset_imm = immediate->GetValue();
ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access");
return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()),
offset_imm / (4 * 4), (offset_imm / 4) % 4);
} else if (std::holds_alternative<OperationNode>(*offset)) {
// Indirect access
const std::string final_offset = code.GenerateTemporary();
code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4);");
return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()),
final_offset, final_offset);
} else {
UNREACHABLE_MSG("Unmanaged offset node type");
}
} else if (const auto gmem = std::get_if<GmemNode>(node)) {
const std::string real = Visit(gmem->GetRealAddress());
const std::string base = Visit(gmem->GetBaseAddress());
const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4";
return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
} else if (const auto lmem = std::get_if<LmemNode>(node)) {
return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
} else if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) {
return GetInternalFlag(internal_flag->GetFlag());
} else if (const auto conditional = std::get_if<ConditionalNode>(node)) {
// It's invalid to call conditional on nested nodes, use an operation instead
code.AddLine("if (" + Visit(conditional->GetCondition()) + ") {");
++code.scope;
VisitBlock(conditional->GetCode());
--code.scope;
code.AddLine('}');
return {};
} else if (const auto comment = std::get_if<CommentNode>(node)) {
return "// " + comment->GetText();
}
UNREACHABLE();
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_" + name + "[ftou(" + Visit(abuf->GetBuffer()) +
") % MAX_VERTEX_INPUT]";
return "gs_" + std::move(name) + "[ftou(" + Visit(buffer) + ") % MAX_VERTEX_INPUT]";
}
return name;
};
@ -578,55 +657,7 @@ private:
break;
}
UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
} else if (const auto cbuf = std::get_if<CbufNode>(node)) {
const Node offset = cbuf->GetOffset();
if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
// Direct access
const u32 offset_imm = immediate->GetValue();
ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access");
return fmt::format("{}[{}][{}]", GetConstBuffer(cbuf->GetIndex()),
offset_imm / (4 * 4), (offset_imm / 4) % 4);
} else if (std::holds_alternative<OperationNode>(*offset)) {
// Indirect access
const std::string final_offset = code.GenerateTemporary();
code.AddLine("uint " + final_offset + " = (ftou(" + Visit(offset) + ") / 4);");
return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()),
final_offset, final_offset);
} else {
UNREACHABLE_MSG("Unmanaged offset node type");
}
} else if (const auto gmem = std::get_if<GmemNode>(node)) {
const std::string real = Visit(gmem->GetRealAddress());
const std::string base = Visit(gmem->GetBaseAddress());
const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4";
return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
} else if (const auto lmem = std::get_if<LmemNode>(node)) {
return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
} else if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) {
return GetInternalFlag(internal_flag->GetFlag());
} else if (const auto conditional = std::get_if<ConditionalNode>(node)) {
// It's invalid to call conditional on nested nodes, use an operation instead
code.AddLine("if (" + Visit(conditional->GetCondition()) + ") {");
++code.scope;
VisitBlock(conditional->GetCode());
--code.scope;
code.AddLine('}');
return {};
} else if (const auto comment = std::get_if<CommentNode>(node)) {
return "// " + comment->GetText();
}
UNREACHABLE();
return {};
return "0";
}
std::string ApplyPrecise(Operation operation, const std::string& value) {
@ -1677,6 +1708,10 @@ 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);
}

View File

@ -885,7 +885,7 @@ private:
std::set<Sampler> used_samplers;
std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
bool use_physical_attributes = true; // Shader uses AL2P
bool use_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
Tegra::Shader::Header header;
};