Merge pull request #2494 from lioncash/shader-text
gl_shader_decompiler: Add AddLine() overloads with single function that forwards to libfmt
This commit is contained in:
commit
9a17b20896
1 changed files with 200 additions and 186 deletions
|
@ -57,15 +57,14 @@ public:
|
|||
shader_source += text;
|
||||
}
|
||||
|
||||
void AddLine(std::string_view text) {
|
||||
AddExpression(text);
|
||||
AddNewLine();
|
||||
}
|
||||
|
||||
void AddLine(char character) {
|
||||
DEBUG_ASSERT(scope >= 0);
|
||||
AppendIndentation();
|
||||
shader_source += character;
|
||||
// Forwards all arguments directly to libfmt.
|
||||
// Note that all formatting requirements for fmt must be
|
||||
// obeyed when using this function. (e.g. {{ must be used
|
||||
// printing the character '{' is desirable. Ditto for }} and '}',
|
||||
// etc).
|
||||
template <typename... Args>
|
||||
void AddLine(std::string_view text, Args&&... args) {
|
||||
AddExpression(fmt::format(text, std::forward<Args>(args)...));
|
||||
AddNewLine();
|
||||
}
|
||||
|
||||
|
@ -75,9 +74,7 @@ public:
|
|||
}
|
||||
|
||||
std::string GenerateTemporary() {
|
||||
std::string temporary = "tmp";
|
||||
temporary += std::to_string(temporary_index++);
|
||||
return temporary;
|
||||
return fmt::format("tmp{}", temporary_index++);
|
||||
}
|
||||
|
||||
std::string GetResult() {
|
||||
|
@ -167,41 +164,41 @@ public:
|
|||
DeclareSamplers();
|
||||
DeclarePhysicalAttributeReader();
|
||||
|
||||
code.AddLine("void execute_" + suffix + "() {");
|
||||
code.AddLine("void execute_{}() {{", suffix);
|
||||
++code.scope;
|
||||
|
||||
// VM's program counter
|
||||
const auto first_address = ir.GetBasicBlocks().begin()->first;
|
||||
code.AddLine("uint jmp_to = " + std::to_string(first_address) + "u;");
|
||||
code.AddLine("uint jmp_to = {}u;", first_address);
|
||||
|
||||
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
|
||||
// unlikely that shaders will use 20 nested SSYs and PBKs.
|
||||
constexpr u32 FLOW_STACK_SIZE = 20;
|
||||
code.AddLine(fmt::format("uint flow_stack[{}];", FLOW_STACK_SIZE));
|
||||
code.AddLine("uint flow_stack[{}];", FLOW_STACK_SIZE);
|
||||
code.AddLine("uint flow_stack_top = 0u;");
|
||||
|
||||
code.AddLine("while (true) {");
|
||||
code.AddLine("while (true) {{");
|
||||
++code.scope;
|
||||
|
||||
code.AddLine("switch (jmp_to) {");
|
||||
code.AddLine("switch (jmp_to) {{");
|
||||
|
||||
for (const auto& pair : ir.GetBasicBlocks()) {
|
||||
const auto [address, bb] = pair;
|
||||
code.AddLine(fmt::format("case 0x{:x}u: {{", address));
|
||||
code.AddLine("case 0x{:x}u: {{", address);
|
||||
++code.scope;
|
||||
|
||||
VisitBlock(bb);
|
||||
|
||||
--code.scope;
|
||||
code.AddLine('}');
|
||||
code.AddLine("}}");
|
||||
}
|
||||
|
||||
code.AddLine("default: return;");
|
||||
code.AddLine('}');
|
||||
code.AddLine("}}");
|
||||
|
||||
for (std::size_t i = 0; i < 2; ++i) {
|
||||
--code.scope;
|
||||
code.AddLine('}');
|
||||
code.AddLine("}}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,12 +238,13 @@ private:
|
|||
}
|
||||
|
||||
void DeclareGeometry() {
|
||||
if (stage != ShaderStage::Geometry)
|
||||
if (stage != ShaderStage::Geometry) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto topology = GetTopologyName(header.common3.output_topology);
|
||||
const auto max_vertices = std::to_string(header.common4.max_output_vertices);
|
||||
code.AddLine("layout (" + topology + ", max_vertices = " + max_vertices + ") out;");
|
||||
const auto max_vertices = header.common4.max_output_vertices.Value();
|
||||
code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices);
|
||||
code.AddNewLine();
|
||||
|
||||
DeclareVertexRedeclarations();
|
||||
|
@ -255,7 +253,7 @@ private:
|
|||
void DeclareVertexRedeclarations() {
|
||||
bool clip_distances_declared = false;
|
||||
|
||||
code.AddLine("out gl_PerVertex {");
|
||||
code.AddLine("out gl_PerVertex {{");
|
||||
++code.scope;
|
||||
|
||||
code.AddLine("vec4 gl_Position;");
|
||||
|
@ -271,40 +269,42 @@ private:
|
|||
}
|
||||
|
||||
--code.scope;
|
||||
code.AddLine("};");
|
||||
code.AddLine("}};");
|
||||
code.AddNewLine();
|
||||
}
|
||||
|
||||
void DeclareRegisters() {
|
||||
const auto& registers = ir.GetRegisters();
|
||||
for (const u32 gpr : registers) {
|
||||
code.AddLine("float " + GetRegister(gpr) + " = 0;");
|
||||
code.AddLine("float {} = 0;", GetRegister(gpr));
|
||||
}
|
||||
if (!registers.empty())
|
||||
if (!registers.empty()) {
|
||||
code.AddNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
void DeclarePredicates() {
|
||||
const auto& predicates = ir.GetPredicates();
|
||||
for (const auto pred : predicates) {
|
||||
code.AddLine("bool " + GetPredicate(pred) + " = false;");
|
||||
code.AddLine("bool {} = false;", GetPredicate(pred));
|
||||
}
|
||||
if (!predicates.empty())
|
||||
if (!predicates.empty()) {
|
||||
code.AddNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
void DeclareLocalMemory() {
|
||||
if (const u64 local_memory_size = header.GetLocalMemorySize(); local_memory_size > 0) {
|
||||
const auto element_count = Common::AlignUp(local_memory_size, 4) / 4;
|
||||
code.AddLine("float " + GetLocalMemory() + '[' + std::to_string(element_count) + "];");
|
||||
code.AddLine("float {}[{}];", GetLocalMemory(), element_count);
|
||||
code.AddNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
void DeclareInternalFlags() {
|
||||
for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) {
|
||||
const InternalFlag flag_code = static_cast<InternalFlag>(flag);
|
||||
code.AddLine("bool " + GetInternalFlag(flag_code) + " = false;");
|
||||
const auto flag_code = static_cast<InternalFlag>(flag);
|
||||
code.AddLine("bool {} = false;", GetInternalFlag(flag_code));
|
||||
}
|
||||
code.AddNewLine();
|
||||
}
|
||||
|
@ -343,8 +343,9 @@ private:
|
|||
DeclareInputAttribute(index, false);
|
||||
}
|
||||
}
|
||||
if (!attributes.empty())
|
||||
if (!attributes.empty()) {
|
||||
code.AddNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
void DeclareInputAttribute(Attribute::Index index, bool skip_unused) {
|
||||
|
@ -370,8 +371,7 @@ private:
|
|||
location += GENERIC_VARYING_START_LOCATION;
|
||||
}
|
||||
|
||||
code.AddLine("layout (location = " + std::to_string(location) + ") " + suffix + "in vec4 " +
|
||||
name + ';');
|
||||
code.AddLine("layout (location = {}) {} in vec4 {};", name, location, suffix, name);
|
||||
}
|
||||
|
||||
void DeclareOutputAttributes() {
|
||||
|
@ -389,23 +389,23 @@ private:
|
|||
DeclareOutputAttribute(index);
|
||||
}
|
||||
}
|
||||
if (!attributes.empty())
|
||||
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) + ';');
|
||||
code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index));
|
||||
}
|
||||
|
||||
void DeclareConstantBuffers() {
|
||||
for (const auto& entry : ir.GetConstantBuffers()) {
|
||||
const auto [index, size] = entry;
|
||||
code.AddLine("layout (std140, binding = CBUF_BINDING_" + std::to_string(index) +
|
||||
") uniform " + GetConstBufferBlock(index) + " {");
|
||||
code.AddLine(" vec4 " + GetConstBuffer(index) + "[MAX_CONSTBUFFER_ELEMENTS];");
|
||||
code.AddLine("};");
|
||||
code.AddLine("layout (std140, binding = CBUF_BINDING_{}) uniform {} {{", index,
|
||||
GetConstBufferBlock(index));
|
||||
code.AddLine(" vec4 {}[MAX_CONSTBUFFER_ELEMENTS];", GetConstBuffer(index));
|
||||
code.AddLine("}};");
|
||||
code.AddNewLine();
|
||||
}
|
||||
}
|
||||
|
@ -417,17 +417,16 @@ private:
|
|||
// Since we don't know how the shader will use the shader, hint the driver to disable as
|
||||
// much optimizations as possible
|
||||
std::string qualifier = "coherent volatile";
|
||||
if (usage.is_read && !usage.is_written)
|
||||
if (usage.is_read && !usage.is_written) {
|
||||
qualifier += " readonly";
|
||||
else if (usage.is_written && !usage.is_read)
|
||||
} else if (usage.is_written && !usage.is_read) {
|
||||
qualifier += " writeonly";
|
||||
}
|
||||
|
||||
const std::string binding =
|
||||
fmt::format("GMEM_BINDING_{}_{}", base.cbuf_index, base.cbuf_offset);
|
||||
code.AddLine("layout (std430, binding = " + binding + ") " + qualifier + " buffer " +
|
||||
GetGlobalMemoryBlock(base) + " {");
|
||||
code.AddLine(" float " + GetGlobalMemory(base) + "[];");
|
||||
code.AddLine("};");
|
||||
code.AddLine("layout (std430, binding = GMEM_BINDING_{}_{}) {} buffer {} {{",
|
||||
base.cbuf_index, base.cbuf_offset, qualifier, GetGlobalMemoryBlock(base));
|
||||
code.AddLine(" float {}[];", GetGlobalMemory(base));
|
||||
code.AddLine("}};");
|
||||
code.AddNewLine();
|
||||
}
|
||||
}
|
||||
|
@ -435,7 +434,7 @@ private:
|
|||
void DeclareSamplers() {
|
||||
const auto& samplers = ir.GetSamplers();
|
||||
for (const auto& sampler : samplers) {
|
||||
std::string sampler_type = [&]() {
|
||||
std::string sampler_type = [&sampler] {
|
||||
switch (sampler.GetType()) {
|
||||
case Tegra::Shader::TextureType::Texture1D:
|
||||
return "sampler1D";
|
||||
|
@ -450,25 +449,28 @@ private:
|
|||
return "sampler2D";
|
||||
}
|
||||
}();
|
||||
if (sampler.IsArray())
|
||||
if (sampler.IsArray()) {
|
||||
sampler_type += "Array";
|
||||
if (sampler.IsShadow())
|
||||
}
|
||||
if (sampler.IsShadow()) {
|
||||
sampler_type += "Shadow";
|
||||
}
|
||||
|
||||
code.AddLine("layout (binding = SAMPLER_BINDING_" + std::to_string(sampler.GetIndex()) +
|
||||
") uniform " + sampler_type + ' ' + GetSampler(sampler) + ';');
|
||||
code.AddLine("layout (binding = SAMPLER_BINDING_{}) uniform {} {};", sampler.GetIndex(),
|
||||
sampler_type, GetSampler(sampler));
|
||||
}
|
||||
if (!samplers.empty())
|
||||
if (!samplers.empty()) {
|
||||
code.AddNewLine();
|
||||
}
|
||||
}
|
||||
|
||||
void DeclarePhysicalAttributeReader() {
|
||||
if (!ir.HasPhysicalAttributes()) {
|
||||
return;
|
||||
}
|
||||
code.AddLine("float readPhysicalAttribute(uint physical_address) {");
|
||||
code.AddLine("float readPhysicalAttribute(uint physical_address) {{");
|
||||
++code.scope;
|
||||
code.AddLine("switch (physical_address) {");
|
||||
code.AddLine("switch (physical_address) {{");
|
||||
|
||||
// Just declare generic attributes for now.
|
||||
const auto num_attributes{static_cast<u32>(GetNumPhysicalInputAttributes())};
|
||||
|
@ -483,15 +485,15 @@ private:
|
|||
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("case 0x{:x}: return {};", address, value);
|
||||
}
|
||||
}
|
||||
|
||||
code.AddLine("default: return 0;");
|
||||
|
||||
code.AddLine('}');
|
||||
code.AddLine("}}");
|
||||
--code.scope;
|
||||
code.AddLine('}');
|
||||
code.AddLine("}}");
|
||||
code.AddNewLine();
|
||||
}
|
||||
|
||||
|
@ -516,23 +518,26 @@ private:
|
|||
return {};
|
||||
}
|
||||
return (this->*decompiler)(*operation);
|
||||
}
|
||||
|
||||
} else if (const auto gpr = std::get_if<GprNode>(node)) {
|
||||
if (const auto gpr = std::get_if<GprNode>(node)) {
|
||||
const u32 index = gpr->GetIndex();
|
||||
if (index == Register::ZeroIndex) {
|
||||
return "0";
|
||||
}
|
||||
return GetRegister(index);
|
||||
}
|
||||
|
||||
} else if (const auto immediate = std::get_if<ImmediateNode>(node)) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(node)) {
|
||||
const u32 value = immediate->GetValue();
|
||||
if (value < 10) {
|
||||
// For eyecandy avoid using hex numbers on single digits
|
||||
return fmt::format("utof({}u)", immediate->GetValue());
|
||||
}
|
||||
return fmt::format("utof(0x{:x}u)", immediate->GetValue());
|
||||
}
|
||||
|
||||
} else if (const auto predicate = std::get_if<PredicateNode>(node)) {
|
||||
if (const auto predicate = std::get_if<PredicateNode>(node)) {
|
||||
const auto value = [&]() -> std::string {
|
||||
switch (const auto index = predicate->GetIndex(); index) {
|
||||
case Tegra::Shader::Pred::UnusedIndex:
|
||||
|
@ -544,19 +549,22 @@ private:
|
|||
}
|
||||
}();
|
||||
if (predicate->IsNegated()) {
|
||||
return "!(" + value + ')';
|
||||
return fmt::format("!({})", value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(node)) {
|
||||
if (const auto abuf = std::get_if<AbufNode>(node)) {
|
||||
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 fmt::format("readPhysicalAttribute(ftou({}))",
|
||||
Visit(abuf->GetPhysicalAddress()));
|
||||
}
|
||||
return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer());
|
||||
}
|
||||
|
||||
} else if (const auto cbuf = std::get_if<CbufNode>(node)) {
|
||||
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
|
||||
|
@ -564,57 +572,63 @@ private:
|
|||
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)) {
|
||||
if (std::holds_alternative<OperationNode>(*offset)) {
|
||||
// Indirect access
|
||||
const std::string final_offset = code.GenerateTemporary();
|
||||
code.AddLine("uint {} = (ftou({}) / 4);", final_offset, Visit(offset));
|
||||
return fmt::format("{}[{} / 4][{} % 4]", GetConstBuffer(cbuf->GetIndex()),
|
||||
final_offset, final_offset);
|
||||
}
|
||||
|
||||
UNREACHABLE_MSG("Unmanaged offset node type");
|
||||
}
|
||||
|
||||
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";
|
||||
const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
|
||||
return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
|
||||
}
|
||||
|
||||
} else if (const auto lmem = std::get_if<LmemNode>(node)) {
|
||||
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)) {
|
||||
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)) {
|
||||
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.AddLine("if ({}) {{", Visit(conditional->GetCondition()));
|
||||
++code.scope;
|
||||
|
||||
VisitBlock(conditional->GetCode());
|
||||
|
||||
--code.scope;
|
||||
code.AddLine('}');
|
||||
code.AddLine("}}");
|
||||
return {};
|
||||
}
|
||||
|
||||
} else if (const auto comment = std::get_if<CommentNode>(node)) {
|
||||
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) {
|
||||
const auto GeometryPass = [&](std::string_view 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 fmt::format("gs_{}[ftou({}) % MAX_VERTEX_INPUT]", name, Visit(buffer));
|
||||
}
|
||||
return name;
|
||||
return std::string(name);
|
||||
};
|
||||
|
||||
switch (attribute) {
|
||||
|
@ -677,7 +691,7 @@ private:
|
|||
const std::string precise = stage != ShaderStage::Fragment ? "precise " : "";
|
||||
|
||||
const std::string temporary = code.GenerateTemporary();
|
||||
code.AddLine(precise + "float " + temporary + " = " + value + ';');
|
||||
code.AddLine("{}float {} = {};", precise, temporary, value);
|
||||
return temporary;
|
||||
}
|
||||
|
||||
|
@ -691,7 +705,7 @@ private:
|
|||
}
|
||||
|
||||
const std::string temporary = code.GenerateTemporary();
|
||||
code.AddLine("float " + temporary + " = " + Visit(operand) + ';');
|
||||
code.AddLine("float {} = {};", temporary, Visit(operand));
|
||||
return temporary;
|
||||
}
|
||||
|
||||
|
@ -706,31 +720,32 @@ private:
|
|||
case Type::Float:
|
||||
return value;
|
||||
case Type::Int:
|
||||
return "ftoi(" + value + ')';
|
||||
return fmt::format("ftoi({})", value);
|
||||
case Type::Uint:
|
||||
return "ftou(" + value + ')';
|
||||
return fmt::format("ftou({})", value);
|
||||
case Type::HalfFloat:
|
||||
return "toHalf2(" + value + ')';
|
||||
return fmt::format("toHalf2({})", value);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return value;
|
||||
}
|
||||
|
||||
std::string BitwiseCastResult(std::string value, Type type, bool needs_parenthesis = false) {
|
||||
std::string BitwiseCastResult(const std::string& value, Type type,
|
||||
bool needs_parenthesis = false) {
|
||||
switch (type) {
|
||||
case Type::Bool:
|
||||
case Type::Bool2:
|
||||
case Type::Float:
|
||||
if (needs_parenthesis) {
|
||||
return '(' + value + ')';
|
||||
return fmt::format("({})", value);
|
||||
}
|
||||
return value;
|
||||
case Type::Int:
|
||||
return "itof(" + value + ')';
|
||||
return fmt::format("itof({})", value);
|
||||
case Type::Uint:
|
||||
return "utof(" + value + ')';
|
||||
return fmt::format("utof({})", value);
|
||||
case Type::HalfFloat:
|
||||
return "fromHalf2(" + value + ')';
|
||||
return fmt::format("fromHalf2({})", value);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return value;
|
||||
|
@ -738,27 +753,27 @@ private:
|
|||
|
||||
std::string GenerateUnary(Operation operation, const std::string& func, Type result_type,
|
||||
Type type_a, bool needs_parenthesis = true) {
|
||||
return ApplyPrecise(operation,
|
||||
BitwiseCastResult(func + '(' + VisitOperand(operation, 0, type_a) + ')',
|
||||
result_type, needs_parenthesis));
|
||||
const std::string op_str = fmt::format("{}({})", func, VisitOperand(operation, 0, type_a));
|
||||
|
||||
return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type, needs_parenthesis));
|
||||
}
|
||||
|
||||
std::string GenerateBinaryInfix(Operation operation, const std::string& func, Type result_type,
|
||||
Type type_a, Type type_b) {
|
||||
const std::string op_a = VisitOperand(operation, 0, type_a);
|
||||
const std::string op_b = VisitOperand(operation, 1, type_b);
|
||||
const std::string op_str = fmt::format("({} {} {})", op_a, func, op_b);
|
||||
|
||||
return ApplyPrecise(
|
||||
operation, BitwiseCastResult('(' + op_a + ' ' + func + ' ' + op_b + ')', result_type));
|
||||
return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
|
||||
}
|
||||
|
||||
std::string GenerateBinaryCall(Operation operation, const std::string& func, Type result_type,
|
||||
Type type_a, Type type_b) {
|
||||
const std::string op_a = VisitOperand(operation, 0, type_a);
|
||||
const std::string op_b = VisitOperand(operation, 1, type_b);
|
||||
const std::string op_str = fmt::format("{}({}, {})", func, op_a, op_b);
|
||||
|
||||
return ApplyPrecise(operation,
|
||||
BitwiseCastResult(func + '(' + op_a + ", " + op_b + ')', result_type));
|
||||
return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
|
||||
}
|
||||
|
||||
std::string GenerateTernary(Operation operation, const std::string& func, Type result_type,
|
||||
|
@ -766,10 +781,9 @@ private:
|
|||
const std::string op_a = VisitOperand(operation, 0, type_a);
|
||||
const std::string op_b = VisitOperand(operation, 1, type_b);
|
||||
const std::string op_c = VisitOperand(operation, 2, type_c);
|
||||
const std::string op_str = fmt::format("{}({}, {}, {})", func, op_a, op_b, op_c);
|
||||
|
||||
return ApplyPrecise(
|
||||
operation,
|
||||
BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " + op_c + ')', result_type));
|
||||
return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
|
||||
}
|
||||
|
||||
std::string GenerateQuaternary(Operation operation, const std::string& func, Type result_type,
|
||||
|
@ -778,10 +792,9 @@ private:
|
|||
const std::string op_b = VisitOperand(operation, 1, type_b);
|
||||
const std::string op_c = VisitOperand(operation, 2, type_c);
|
||||
const std::string op_d = VisitOperand(operation, 3, type_d);
|
||||
const std::string op_str = fmt::format("{}({}, {}, {}, {})", func, op_a, op_b, op_c, op_d);
|
||||
|
||||
return ApplyPrecise(operation, BitwiseCastResult(func + '(' + op_a + ", " + op_b + ", " +
|
||||
op_c + ", " + op_d + ')',
|
||||
result_type));
|
||||
return ApplyPrecise(operation, BitwiseCastResult(op_str, result_type));
|
||||
}
|
||||
|
||||
std::string GenerateTexture(Operation operation, const std::string& function_suffix,
|
||||
|
@ -844,7 +857,7 @@ private:
|
|||
// required to be constant)
|
||||
expr += std::to_string(static_cast<s32>(immediate->GetValue()));
|
||||
} else {
|
||||
expr += "ftoi(" + Visit(operand) + ')';
|
||||
expr += fmt::format("ftoi({})", Visit(operand));
|
||||
}
|
||||
break;
|
||||
case Type::Float:
|
||||
|
@ -877,7 +890,7 @@ private:
|
|||
expr += std::to_string(static_cast<s32>(immediate->GetValue()));
|
||||
} else if (device.HasVariableAoffi()) {
|
||||
// Avoid using variable AOFFI on unsupported devices.
|
||||
expr += "ftoi(" + Visit(operand) + ')';
|
||||
expr += fmt::format("ftoi({})", Visit(operand));
|
||||
} else {
|
||||
// Insert 0 on devices not supporting variable AOFFI.
|
||||
expr += '0';
|
||||
|
@ -902,7 +915,6 @@ private:
|
|||
return {};
|
||||
}
|
||||
target = GetRegister(gpr->GetIndex());
|
||||
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(dest)) {
|
||||
UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
|
||||
|
||||
|
@ -913,9 +925,9 @@ private:
|
|||
case Attribute::Index::PointSize:
|
||||
return "gl_PointSize";
|
||||
case Attribute::Index::ClipDistances0123:
|
||||
return "gl_ClipDistance[" + std::to_string(abuf->GetElement()) + ']';
|
||||
return fmt::format("gl_ClipDistance[{}]", abuf->GetElement());
|
||||
case Attribute::Index::ClipDistances4567:
|
||||
return "gl_ClipDistance[" + std::to_string(abuf->GetElement() + 4) + ']';
|
||||
return fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4);
|
||||
default:
|
||||
if (IsGenericAttribute(attribute)) {
|
||||
return GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement());
|
||||
|
@ -925,21 +937,18 @@ private:
|
|||
return "0";
|
||||
}
|
||||
}();
|
||||
|
||||
} else if (const auto lmem = std::get_if<LmemNode>(dest)) {
|
||||
target = GetLocalMemory() + "[ftou(" + Visit(lmem->GetAddress()) + ") / 4]";
|
||||
|
||||
target = fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(dest)) {
|
||||
const std::string real = Visit(gmem->GetRealAddress());
|
||||
const std::string base = Visit(gmem->GetBaseAddress());
|
||||
const std::string final_offset = "(ftou(" + real + ") - ftou(" + base + ")) / 4";
|
||||
const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
|
||||
target = fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
|
||||
|
||||
} else {
|
||||
UNREACHABLE_MSG("Assign called without a proper target");
|
||||
}
|
||||
|
||||
code.AddLine(target + " = " + Visit(src) + ';');
|
||||
code.AddLine("{} = {};", target, Visit(src));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -992,8 +1001,9 @@ private:
|
|||
const std::string condition = Visit(operation[0]);
|
||||
const std::string true_case = Visit(operation[1]);
|
||||
const std::string false_case = Visit(operation[2]);
|
||||
return ApplyPrecise(operation,
|
||||
'(' + condition + " ? " + true_case + " : " + false_case + ')');
|
||||
const std::string op_str = fmt::format("({} ? {} : {})", condition, true_case, false_case);
|
||||
|
||||
return ApplyPrecise(operation, op_str);
|
||||
}
|
||||
|
||||
std::string FCos(Operation operation) {
|
||||
|
@ -1057,9 +1067,9 @@ private:
|
|||
std::string ILogicalShiftRight(Operation operation) {
|
||||
const std::string op_a = VisitOperand(operation, 0, Type::Uint);
|
||||
const std::string op_b = VisitOperand(operation, 1, Type::Uint);
|
||||
const std::string op_str = fmt::format("int({} >> {})", op_a, op_b);
|
||||
|
||||
return ApplyPrecise(operation,
|
||||
BitwiseCastResult("int(" + op_a + " >> " + op_b + ')', Type::Int));
|
||||
return ApplyPrecise(operation, BitwiseCastResult(op_str, Type::Int));
|
||||
}
|
||||
|
||||
std::string IArithmeticShiftRight(Operation operation) {
|
||||
|
@ -1115,11 +1125,12 @@ private:
|
|||
}
|
||||
|
||||
std::string HNegate(Operation operation) {
|
||||
const auto GetNegate = [&](std::size_t index) -> std::string {
|
||||
const auto GetNegate = [&](std::size_t index) {
|
||||
return VisitOperand(operation, index, Type::Bool) + " ? -1 : 1";
|
||||
};
|
||||
const std::string value = '(' + VisitOperand(operation, 0, Type::HalfFloat) + " * vec2(" +
|
||||
GetNegate(1) + ", " + GetNegate(2) + "))";
|
||||
const std::string value =
|
||||
fmt::format("({} * vec2({}, {}))", VisitOperand(operation, 0, Type::HalfFloat),
|
||||
GetNegate(1), GetNegate(2));
|
||||
return BitwiseCastResult(value, Type::HalfFloat);
|
||||
}
|
||||
|
||||
|
@ -1127,7 +1138,8 @@ private:
|
|||
const std::string value = VisitOperand(operation, 0, Type::HalfFloat);
|
||||
const std::string min = VisitOperand(operation, 1, Type::Float);
|
||||
const std::string max = VisitOperand(operation, 2, Type::Float);
|
||||
const std::string clamped = "clamp(" + value + ", vec2(" + min + "), vec2(" + max + "))";
|
||||
const std::string clamped = fmt::format("clamp({}, vec2({}), vec2({}))", value, min, max);
|
||||
|
||||
return ApplyPrecise(operation, BitwiseCastResult(clamped, Type::HalfFloat));
|
||||
}
|
||||
|
||||
|
@ -1138,34 +1150,35 @@ private:
|
|||
case Tegra::Shader::HalfType::H0_H1:
|
||||
return operand;
|
||||
case Tegra::Shader::HalfType::F32:
|
||||
return "vec2(fromHalf2(" + operand + "))";
|
||||
return fmt::format("vec2(fromHalf2({}))", operand);
|
||||
case Tegra::Shader::HalfType::H0_H0:
|
||||
return "vec2(" + operand + "[0])";
|
||||
return fmt::format("vec2({}[0])", operand);
|
||||
case Tegra::Shader::HalfType::H1_H1:
|
||||
return "vec2(" + operand + "[1])";
|
||||
return fmt::format("vec2({}[1])", operand);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return "0";
|
||||
}();
|
||||
return "fromHalf2(" + value + ')';
|
||||
return fmt::format("fromHalf2({})", value);
|
||||
}
|
||||
|
||||
std::string HMergeF32(Operation operation) {
|
||||
return "float(toHalf2(" + Visit(operation[0]) + ")[0])";
|
||||
return fmt::format("float(toHalf2({})[0])", Visit(operation[0]));
|
||||
}
|
||||
|
||||
std::string HMergeH0(Operation operation) {
|
||||
return "fromHalf2(vec2(toHalf2(" + Visit(operation[1]) + ")[0], toHalf2(" +
|
||||
Visit(operation[0]) + ")[1]))";
|
||||
return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[1]),
|
||||
Visit(operation[0]));
|
||||
}
|
||||
|
||||
std::string HMergeH1(Operation operation) {
|
||||
return "fromHalf2(vec2(toHalf2(" + Visit(operation[0]) + ")[0], toHalf2(" +
|
||||
Visit(operation[1]) + ")[1]))";
|
||||
return fmt::format("fromHalf2(vec2(toHalf2({})[0], toHalf2({})[1]))", Visit(operation[0]),
|
||||
Visit(operation[1]));
|
||||
}
|
||||
|
||||
std::string HPack2(Operation operation) {
|
||||
return "utof(packHalf2x16(vec2(" + Visit(operation[0]) + ", " + Visit(operation[1]) + ")))";
|
||||
return fmt::format("utof(packHalf2x16(vec2({}, {})))", Visit(operation[0]),
|
||||
Visit(operation[1]));
|
||||
}
|
||||
|
||||
template <Type type>
|
||||
|
@ -1223,7 +1236,7 @@ private:
|
|||
target = GetInternalFlag(flag->GetFlag());
|
||||
}
|
||||
|
||||
code.AddLine(target + " = " + Visit(src) + ';');
|
||||
code.AddLine("{} = {};", target, Visit(src));
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -1245,7 +1258,7 @@ private:
|
|||
|
||||
std::string LogicalPick2(Operation operation) {
|
||||
const std::string pair = VisitOperand(operation, 0, Type::Bool2);
|
||||
return pair + '[' + VisitOperand(operation, 1, Type::Uint) + ']';
|
||||
return fmt::format("{}[{}]", pair, VisitOperand(operation, 1, Type::Uint));
|
||||
}
|
||||
|
||||
std::string LogicalAll2(Operation operation) {
|
||||
|
@ -1257,15 +1270,15 @@ private:
|
|||
}
|
||||
|
||||
template <bool with_nan>
|
||||
std::string GenerateHalfComparison(Operation operation, std::string compare_op) {
|
||||
std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2,
|
||||
Type::HalfFloat, Type::HalfFloat)};
|
||||
std::string GenerateHalfComparison(Operation operation, const std::string& compare_op) {
|
||||
const std::string comparison{GenerateBinaryCall(operation, compare_op, Type::Bool2,
|
||||
Type::HalfFloat, Type::HalfFloat)};
|
||||
if constexpr (!with_nan) {
|
||||
return comparison;
|
||||
}
|
||||
return "halfFloatNanComparison(" + comparison + ", " +
|
||||
VisitOperand(operation, 0, Type::HalfFloat) + ", " +
|
||||
VisitOperand(operation, 1, Type::HalfFloat) + ')';
|
||||
return fmt::format("halfFloatNanComparison({}, {}, {})", comparison,
|
||||
VisitOperand(operation, 0, Type::HalfFloat),
|
||||
VisitOperand(operation, 1, Type::HalfFloat));
|
||||
}
|
||||
|
||||
template <bool with_nan>
|
||||
|
@ -1342,12 +1355,12 @@ private:
|
|||
switch (meta->element) {
|
||||
case 0:
|
||||
case 1:
|
||||
return "itof(int(textureSize(" + sampler + ", " + lod + ')' +
|
||||
GetSwizzle(meta->element) + "))";
|
||||
return fmt::format("itof(int(textureSize({}, {}){}))", sampler, lod,
|
||||
GetSwizzle(meta->element));
|
||||
case 2:
|
||||
return "0";
|
||||
case 3:
|
||||
return "itof(textureQueryLevels(" + sampler + "))";
|
||||
return fmt::format("itof(textureQueryLevels({}))", sampler);
|
||||
}
|
||||
UNREACHABLE();
|
||||
return "0";
|
||||
|
@ -1358,8 +1371,9 @@ private:
|
|||
ASSERT(meta);
|
||||
|
||||
if (meta->element < 2) {
|
||||
return "itof(int((" + GenerateTexture(operation, "QueryLod", {}) + " * vec2(256))" +
|
||||
GetSwizzle(meta->element) + "))";
|
||||
return fmt::format("itof(int(({} * vec2(256)){}))",
|
||||
GenerateTexture(operation, "QueryLod", {}),
|
||||
GetSwizzle(meta->element));
|
||||
}
|
||||
return "0";
|
||||
}
|
||||
|
@ -1398,7 +1412,7 @@ private:
|
|||
const auto target = std::get_if<ImmediateNode>(operation[0]);
|
||||
UNIMPLEMENTED_IF(!target);
|
||||
|
||||
code.AddLine(fmt::format("jmp_to = 0x{:x}u;", target->GetValue()));
|
||||
code.AddLine("jmp_to = 0x{:x}u;", target->GetValue());
|
||||
code.AddLine("break;");
|
||||
return {};
|
||||
}
|
||||
|
@ -1407,7 +1421,7 @@ private:
|
|||
const auto target = std::get_if<ImmediateNode>(operation[0]);
|
||||
UNIMPLEMENTED_IF(!target);
|
||||
|
||||
code.AddLine(fmt::format("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue()));
|
||||
code.AddLine("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue());
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -1433,7 +1447,7 @@ private:
|
|||
|
||||
UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented");
|
||||
|
||||
code.AddLine("if (alpha_test[0] != 0) {");
|
||||
code.AddLine("if (alpha_test[0] != 0) {{");
|
||||
++code.scope;
|
||||
// We start on the register containing the alpha value in the first RT.
|
||||
u32 current_reg = 3;
|
||||
|
@ -1444,13 +1458,12 @@ private:
|
|||
header.ps.IsColorComponentOutputEnabled(render_target, 1) ||
|
||||
header.ps.IsColorComponentOutputEnabled(render_target, 2) ||
|
||||
header.ps.IsColorComponentOutputEnabled(render_target, 3)) {
|
||||
code.AddLine(
|
||||
fmt::format("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg)));
|
||||
code.AddLine("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg));
|
||||
current_reg += 4;
|
||||
}
|
||||
}
|
||||
--code.scope;
|
||||
code.AddLine('}');
|
||||
code.AddLine("}}");
|
||||
|
||||
// Write the color outputs using the data in the shader registers, disabled
|
||||
// rendertargets/components are skipped in the register assignment.
|
||||
|
@ -1459,8 +1472,8 @@ private:
|
|||
// TODO(Subv): Figure out how dual-source blending is configured in the Switch.
|
||||
for (u32 component = 0; component < 4; ++component) {
|
||||
if (header.ps.IsColorComponentOutputEnabled(render_target, component)) {
|
||||
code.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component,
|
||||
SafeGetRegister(current_reg)));
|
||||
code.AddLine("FragColor{}[{}] = {};", render_target, component,
|
||||
SafeGetRegister(current_reg));
|
||||
++current_reg;
|
||||
}
|
||||
}
|
||||
|
@ -1469,7 +1482,7 @@ private:
|
|||
if (header.ps.omap.depth) {
|
||||
// The depth output is always 2 registers after the last color output, and current_reg
|
||||
// already contains one past the last color register.
|
||||
code.AddLine("gl_FragDepth = " + SafeGetRegister(current_reg + 1) + ';');
|
||||
code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1));
|
||||
}
|
||||
|
||||
code.AddLine("return;");
|
||||
|
@ -1479,11 +1492,11 @@ private:
|
|||
std::string Discard(Operation operation) {
|
||||
// Enclose "discard" in a conditional, so that GLSL compilation does not complain
|
||||
// about unexecuted instructions that may follow this.
|
||||
code.AddLine("if (true) {");
|
||||
code.AddLine("if (true) {{");
|
||||
++code.scope;
|
||||
code.AddLine("discard;");
|
||||
--code.scope;
|
||||
code.AddLine("}");
|
||||
code.AddLine("}}");
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -1697,7 +1710,7 @@ private:
|
|||
const auto index = static_cast<u32>(flag);
|
||||
ASSERT(index < static_cast<u32>(InternalFlag::Amount));
|
||||
|
||||
return std::string(InternalFlagNames[index]) + '_' + suffix;
|
||||
return fmt::format("{}_{}", InternalFlagNames[index], suffix);
|
||||
}
|
||||
|
||||
std::string GetSampler(const Sampler& sampler) const {
|
||||
|
@ -1705,7 +1718,7 @@ private:
|
|||
}
|
||||
|
||||
std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const {
|
||||
return name + '_' + std::to_string(index) + '_' + suffix;
|
||||
return fmt::format("{}_{}_{}", name, index, suffix);
|
||||
}
|
||||
|
||||
u32 GetNumPhysicalInputAttributes() const {
|
||||
|
@ -1733,24 +1746,25 @@ private:
|
|||
} // Anonymous namespace
|
||||
|
||||
std::string GetCommonDeclarations() {
|
||||
const auto cbuf = std::to_string(MAX_CONSTBUFFER_ELEMENTS);
|
||||
return "#define MAX_CONSTBUFFER_ELEMENTS " + cbuf + "\n" +
|
||||
"#define ftoi floatBitsToInt\n"
|
||||
"#define ftou floatBitsToUint\n"
|
||||
"#define itof intBitsToFloat\n"
|
||||
"#define utof uintBitsToFloat\n\n"
|
||||
"float fromHalf2(vec2 pair) {\n"
|
||||
" return utof(packHalf2x16(pair));\n"
|
||||
"}\n\n"
|
||||
"vec2 toHalf2(float value) {\n"
|
||||
" return unpackHalf2x16(ftou(value));\n"
|
||||
"}\n\n"
|
||||
"bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {\n"
|
||||
" bvec2 is_nan1 = isnan(pair1);\n"
|
||||
" bvec2 is_nan2 = isnan(pair2);\n"
|
||||
" return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || "
|
||||
"is_nan2.y);\n"
|
||||
"}\n";
|
||||
return fmt::format(
|
||||
"#define MAX_CONSTBUFFER_ELEMENTS {}\n"
|
||||
"#define ftoi floatBitsToInt\n"
|
||||
"#define ftou floatBitsToUint\n"
|
||||
"#define itof intBitsToFloat\n"
|
||||
"#define utof uintBitsToFloat\n\n"
|
||||
"float fromHalf2(vec2 pair) {{\n"
|
||||
" return utof(packHalf2x16(pair));\n"
|
||||
"}}\n\n"
|
||||
"vec2 toHalf2(float value) {{\n"
|
||||
" return unpackHalf2x16(ftou(value));\n"
|
||||
"}}\n\n"
|
||||
"bvec2 halfFloatNanComparison(bvec2 comparison, vec2 pair1, vec2 pair2) {{\n"
|
||||
" bvec2 is_nan1 = isnan(pair1);\n"
|
||||
" bvec2 is_nan2 = isnan(pair2);\n"
|
||||
" return bvec2(comparison.x || is_nan1.x || is_nan2.x, comparison.y || is_nan1.y || "
|
||||
"is_nan2.y);\n"
|
||||
"}}\n",
|
||||
MAX_CONSTBUFFER_ELEMENTS);
|
||||
}
|
||||
|
||||
ProgramResult Decompile(const Device& device, const ShaderIR& ir, Maxwell::ShaderStage stage,
|
||||
|
|
Loading…
Reference in a new issue