diff --git a/rsx_decompiler/rsx_decompiler.cpp b/rsx_decompiler/rsx_decompiler.cpp index 1b5afdd..c37a7c2 100644 --- a/rsx_decompiler/rsx_decompiler.cpp +++ b/rsx_decompiler/rsx_decompiler.cpp @@ -133,7 +133,19 @@ namespace rsx return result; } - expression_from> src(context_t& context, int index) const + expression_from> swizzle_as_dst(expression_from> arg) const + { + std::string arg_mask; + + for (char channel : destination_swizzle()) + { + arg_mask += arg.mask[channel_to_index.at(channel)]; + } + + return expression_from>(arg.text, arg_mask, arg.is_single, arg.base_count); + } + + expression_from> src(context_t& context, int index, bool is_swizzle_as_dst = false) const { src_t src; @@ -160,6 +172,13 @@ namespace rsx expression_from> result = get_variable(src); + result.assign(result.swizzle(src.swizzle_x, src.swizzle_y, src.swizzle_z, src.swizzle_w)); + + if (is_swizzle_as_dst) + { + result.assign(swizzle_as_dst(result)); + } + if (src.abs) { result.assign(abs(result)); @@ -170,7 +189,7 @@ namespace rsx result.assign(-result); } - return result.swizzle(src.swizzle_x, src.swizzle_y, src.swizzle_z, src.swizzle_w); + return result; } @@ -209,31 +228,14 @@ namespace rsx context_t context; writer_t writer; - expression_from> src(int index) + expression_from> src(int index, bool is_swizzle_as_dst = false) { - return instruction.src(context, index); - } - - expression_from> swizzle_as_dst(expression_from> arg) const - { - std::string arg_mask; - - for (char channel : instruction.destination_swizzle()) - { - arg_mask += arg.mask[channel_to_index.at(channel)]; - } - - return expression_from>(arg.text, arg_mask, arg.is_single); + return instruction.src(context, index, is_swizzle_as_dst); } expression_from> src_swizzled_as_dst(int index) { - if (instruction.data.dst.set_cond || !instruction.data.dst.no_dest) - { - return swizzle_as_dst(src(index)); - } - - return src(index); + return src(index, instruction.data.dst.set_cond || !instruction.data.dst.no_dest); } expression_from> modify_condition() @@ -271,7 +273,7 @@ namespace rsx }; template - Type apply_dst_flags(Type arg) + Type apply_instruction_modifiers(Type arg) { using float_t = expression_from>; @@ -289,32 +291,98 @@ namespace rsx throw std::runtime_error("fragment program decompiler: unimplemented scale (" + std::to_string(instruction.data.src1.scale) + "). "); } - switch (instruction.data.dst.prec) - { - case 0: //fp32, do nothing - break; - - case 1: //fp16 - arg.assign(clamp(arg, -65536.0f, 65536.0f)); - break; - - case 2: //fixed point 12 - arg.assign(clamp(arg, -1.0f, 1.0f)); - break; - - default: - throw std::runtime_error("fragment program decompiler: unimplemented precision."); - } - if (instruction.data.dst.saturate) { arg.assign(clamp(arg, 0.0f, 1.0f)); } + else + { + switch (instruction.data.dst.prec) + { + case 0: //fp32 + if (!instruction.data.dst.fp16) + { + break; + } + //result is fp16, clamp to fp16 + + case 1: //fp16 + arg.assign(clamp(arg, -65536.0f, 65536.0f)); + break; + + case 2: //fixed point 12 + arg.assign(clamp(arg, -1.0f, 1.0f)); + break; + + default: + throw std::runtime_error("fragment program decompiler: unimplemented precision."); + } + } return arg; } - writer_t set_dst(const expression_from>& arg, set_dst_flags flags = none) + enum class compare_function + { + less, + greater, + equal, + less_equal, + greater_equal, + not_equal + }; + + static expression_from> single_compare_function(compare_function function, expression_from> a, expression_from> b) + { + std::string operator_string; + + switch (function) + { + case compare_function::less: operator_string = "<"; break; + case compare_function::greater: operator_string = ">"; break; + case compare_function::equal: operator_string = "=="; break; + case compare_function::less_equal: operator_string = "<="; break; + case compare_function::greater_equal: operator_string = ">="; break; + case compare_function::not_equal: operator_string = "!="; break; + + default: + throw; + } + + return a.to_string() + " " + operator_string + " " + b.to_string(); + } + + static expression_from> vector_compare_function(compare_function function, expression_from> a, expression_from> b) + { + switch (function) + { + case compare_function::less: return less(a, b); + case compare_function::greater: return greater(a, b); + case compare_function::equal: return equal(a, b); + case compare_function::less_equal: return less_equal(a, b); + case compare_function::greater_equal: return greater_equal(a, b); + case compare_function::not_equal: return not_equal(a, b); + } + + throw; + } + + static expression_from> compare(compare_function function, int channel_count, expression_from> a, expression_from> b) + { + if (channel_count == 1) + { + return single_compare_function(function, a, b); + } + + return vector_compare_function(function, a, b); + } + + expression_from> compare(compare_function function, expression_from> a, expression_from> b) + { + return compare(function, instruction.destination_swizzle().size(), a, b); + } + + writer_t set_dst(const expression_from>& arg, u32 flags = none) { writer_t result; @@ -354,41 +422,61 @@ namespace rsx static const expression_from> zero(0.0f); - auto set_channel = [&](int index) + auto set_channel = [&](int dest_swizzle, int src_swizzle) { - auto src = apply_dst_flags(arg.swizzle(index)); + auto src = expression_from>{ arg.text, arg.mask, arg.is_single, arg.base_count }; + + if ((flags & disable_swizzle_as_dst) != 0 && dest.mask.size() == 1) + { + src.assign(src.without_scope()); + } + else + { + src.assign(src.swizzle(src_swizzle)); + } + + src = apply_instruction_modifiers(src); if (instruction.data.dst.set_cond) { if (instruction.data.dst.no_dest) { - result += if_(cond.swizzle(index).call_operator>(operation, zero), - modify_cond.swizzle(index) = boolean_t<1>::ctor(src)); + result += if_(cond.swizzle(src_swizzle).call_operator>(operation, zero), + modify_cond.swizzle(src_swizzle) = boolean_t<1>::ctor(src)); } else { - result += if_(cond.swizzle(index).call_operator(operation, zero), - modify_cond.swizzle(index) = boolean_t<1>::ctor(dest.swizzle(index) = src)); + result += if_(cond.swizzle(src_swizzle).call_operator(operation, zero), + modify_cond.swizzle(src_swizzle) = boolean_t<1>::ctor(dest.swizzle(dest_swizzle) = src)); } } else { - result += if_(cond.swizzle(index).call_operator(operation, zero), dest.swizzle(index) = src); + result += if_(cond.swizzle(src_swizzle).call_operator(operation, zero), dest.swizzle(dest_swizzle) = src); } }; if (!instruction.data.dst.set_cond && instruction.data.dst.no_dest) { //condition must be already handled in instruction semantic (IFE, LOOP, etc) - result += comment("extra condition test skipped"); + result += comment("WARNING: extra condition test skipped"); result += arg; } else { - if (instruction.data.dst.mask_x) set_channel(0); - if (instruction.data.dst.mask_y) set_channel(1); - if (instruction.data.dst.mask_z) set_channel(2); - if (instruction.data.dst.mask_w) set_channel(3); + if (flags & disable_swizzle_as_dst) + { + for (int i = 0; i < dest.mask.size(); ++i) + set_channel(i, i); + } + else + { + int dest_swizzle = 0; + if (instruction.data.dst.mask_x) set_channel(dest_swizzle++, 0); + if (instruction.data.dst.mask_y) set_channel(dest_swizzle++, 1); + if (instruction.data.dst.mask_z) set_channel(dest_swizzle++, 2); + if (instruction.data.dst.mask_w) set_channel(dest_swizzle, 3); + } } } else @@ -399,10 +487,10 @@ namespace rsx { if ((flags & disable_swizzle_as_dst) == 0) { - src.assign(swizzle_as_dst(arg)); + src.assign(instruction.swizzle_as_dst(arg)); } - src.assign(apply_dst_flags(src).without_scope()); + src.assign(apply_instruction_modifiers(src).without_scope()); if (!instruction.data.dst.no_dest) { @@ -421,6 +509,34 @@ namespace rsx return result; } + writer_t set_dst(const expression_from>& arg, u32 flags = none) + { + if (instruction.destination_swizzle().size() != 1) + { + return set_dst(float_point_t<4>::ctor(arg), flags); + } + + return set_dst(expression_from>{ arg.to_string() }, flags | disable_swizzle_as_dst); + } + + writer_t set_dst(const expression_from>& arg, u32 flags = none) + { + std::string arg_string; + + switch (instruction.destination_swizzle().size()) + { + case 1: arg_string = float_point_t<1>::ctor(expression_from>{ arg.to_string() }).to_string(); break; + case 2: arg_string = float_point_t<2>::ctor(expression_from>{ arg.to_string() }).to_string(); break; + case 3: arg_string = float_point_t<3>::ctor(expression_from>{ arg.to_string() }).to_string(); break; + case 4: arg_string = float_point_t<4>::ctor(expression_from>{ arg.to_string() }).to_string(); break; + + default: + throw; + } + + return set_dst(expression_from>{ arg_string, std::string("xyzw"), true, 4 }, flags); + } + writer_t comment(const std::string& lines) { writer_t result; @@ -430,22 +546,27 @@ namespace rsx return result; } + writer_t warning(const std::string& lines) + { + return comment("WARNING: " + lines); + } + writer_t unimplemented(const std::string& lines) { - return comment(lines); + return comment("TODO: " + lines); } expression_base_t decode_instruction() { - switch (instruction.data.dst.opcode) + switch (instruction.data.dst.opcode | (instruction.data.src1.opcode_is_branch << 6)) { case opcode::NOP: return comment("nop"); case opcode::MOV: return set_dst(src(0)); case opcode::MUL: return set_dst(src_swizzled_as_dst(0) * src_swizzled_as_dst(1), disable_swizzle_as_dst); case opcode::ADD: return set_dst(src_swizzled_as_dst(0) + src_swizzled_as_dst(1), disable_swizzle_as_dst); case opcode::MAD: return set_dst((src_swizzled_as_dst(0) * src_swizzled_as_dst(1)).without_scope() + src_swizzled_as_dst(2), disable_swizzle_as_dst); - case opcode::DP3: return set_dst(float_point_t<4>::ctor(dot(src(0).xyz(), src(1).xyz()))); - case opcode::DP4: return set_dst(float_point_t<4>::ctor(dot(src(0), src(1)))); + case opcode::DP3: return set_dst(dot(src(0).xyz(), src(1).xyz())); + case opcode::DP4: return set_dst(dot(src(0), src(1))); case opcode::DST: { auto src_0 = src(0); @@ -455,12 +576,12 @@ namespace rsx } case opcode::MIN: return set_dst(min(src_swizzled_as_dst(0), src_swizzled_as_dst(1)), disable_swizzle_as_dst); case opcode::MAX: return set_dst(max(src_swizzled_as_dst(0), src_swizzled_as_dst(1)), disable_swizzle_as_dst); - case opcode::SLT: return set_dst(float_point_t<4>::ctor(less(src(0), src(1)))); - case opcode::SGE: return set_dst(float_point_t<4>::ctor(greater_equal(src(0), src(1)))); - case opcode::SLE: return set_dst(float_point_t<4>::ctor(less_equal(src(0), src(1)))); - case opcode::SGT: return set_dst(float_point_t<4>::ctor(greater(src(0), src(1)))); - case opcode::SNE: return set_dst(float_point_t<4>::ctor(not_equal(src(0), src(1)))); - case opcode::SEQ: return set_dst(float_point_t<4>::ctor(equal(src(0), src(1)))); + case opcode::SLT: return set_dst(compare(compare_function::less, src_swizzled_as_dst(0), src_swizzled_as_dst(1)), disable_swizzle_as_dst); + case opcode::SGE: return set_dst(compare(compare_function::greater_equal, src_swizzled_as_dst(0), src_swizzled_as_dst(1)), disable_swizzle_as_dst); + case opcode::SLE: return set_dst(compare(compare_function::less_equal, src_swizzled_as_dst(0), src_swizzled_as_dst(1)), disable_swizzle_as_dst); + case opcode::SGT: return set_dst(compare(compare_function::greater, src_swizzled_as_dst(0), src_swizzled_as_dst(1)), disable_swizzle_as_dst); + case opcode::SNE: return set_dst(compare(compare_function::not_equal, src_swizzled_as_dst(0), src_swizzled_as_dst(1)), disable_swizzle_as_dst); + case opcode::SEQ: return set_dst(compare(compare_function::equal, src_swizzled_as_dst(0), src_swizzled_as_dst(1)), disable_swizzle_as_dst); case opcode::FRC: return set_dst(fract(src_swizzled_as_dst(0)), disable_swizzle_as_dst); case opcode::FLR: return set_dst(floor(src_swizzled_as_dst(0)), disable_swizzle_as_dst); case opcode::KIL: return conditional(expression_from("discard")); @@ -477,8 +598,8 @@ namespace rsx case opcode::LG2: return set_dst(log2(src_swizzled_as_dst(0)), disable_swizzle_as_dst); case opcode::LIT: return unimplemented("LIT"); case opcode::LRP: return unimplemented("LRP"); - case opcode::STR: return set_dst(float_point_t<4>::ctor(1.0f)); - case opcode::SFL: return set_dst(float_point_t<4>::ctor(0.0f)); + case opcode::STR: return set_dst(1.0f); + case opcode::SFL: return set_dst(0.0f); case opcode::COS: return set_dst(cos(src_swizzled_as_dst(0)), disable_swizzle_as_dst); case opcode::SIN: return set_dst(sin(src_swizzled_as_dst(0)), disable_swizzle_as_dst); case opcode::PK2: return unimplemented("PK2"); @@ -505,7 +626,7 @@ namespace rsx case opcode::BEMLUM: return unimplemented("BEMLUM"); case opcode::REFL: return unimplemented("REFL"); case opcode::TIMESWTEX: return unimplemented("TIMESWTEX"); - case opcode::DP2: return set_dst(float_point_t<4>::ctor(dot(src(0).xy(), src(1).xy()))); + case opcode::DP2: return set_dst(dot(src(0).xy(), src(1).xy())); case opcode::NRM: return set_dst(normalize(src(0).xyz()).xyzx()); case opcode::DIV: return set_dst(src_swizzled_as_dst(0) / src_swizzled_as_dst(1), disable_swizzle_as_dst); case opcode::DIVSQ: return set_dst(src_swizzled_as_dst(0) / sqrt(src_swizzled_as_dst(1)), disable_swizzle_as_dst); @@ -514,7 +635,13 @@ namespace rsx case opcode::FENCB: return comment("fencb"); case opcode::BRK: return conditional(expression_from("break")); case opcode::CAL: return unimplemented("CAL"); - case opcode::IFE: return unimplemented("IFE"); + case opcode::IFE: + if (instruction.data.src2.end_offset != instruction.data.src1.else_offset) + writer.before(instruction.data.src1.else_offset >> 2, "}\nelse\n{\n"); + writer.after(instruction.data.src2.end_offset >> 2, "}\n"); + + return writer_t{ "if (" + all(execution_condition()).to_string() + ")\n{\n" }; + case opcode::LOOP: return unimplemented("LOOP"); case opcode::REP: return unimplemented("REP"); case opcode::RET: return conditional(expression_from("return")); @@ -528,7 +655,7 @@ namespace rsx context.offset = 0; context.is_next_is_constant = false; - for (std::size_t index = offset; index < 512; ++index, writer.next(), context.offset += sizeof(instruction_t)) + for (std::size_t index = offset; true; ++index, writer.next(), context.offset += sizeof(instruction_t)) { if (context.is_next_is_constant) { @@ -536,7 +663,7 @@ namespace rsx continue; } - instruction = (instructions + index)->unpack(); + instruction = instructions[index].unpack(); writer += decode_instruction(); diff --git a/rsx_decompiler/rsx_fp_ucode.h b/rsx_decompiler/rsx_fp_ucode.h index 578c754..dc1df00 100644 --- a/rsx_decompiler/rsx_fp_ucode.h +++ b/rsx_decompiler/rsx_fp_ucode.h @@ -70,6 +70,7 @@ namespace rsx LIF = 0x3C, // Final part of LIT FENCT = 0x3D, // Fence T? FENCB = 0x3E, // Fence B? + BRK = 0x40, // Break CAL = 0x41, // Subroutine call IFE = 0x42, // If @@ -78,6 +79,15 @@ namespace rsx RET = 0x45 // Return }; + inline opcode operator |(opcode lhs, u32 rhs) + { + return opcode(u32(lhs) | rhs); + } + inline opcode operator |(u32 lhs, opcode rhs) + { + return opcode(lhs | u32(rhs)); + } + enum class src_reg_type_t : u32 { temporary, diff --git a/rsx_program_decompiler/rsx_program_decompiler.vcxproj.user b/rsx_program_decompiler/rsx_program_decompiler.vcxproj.user index 065d719..9b5e257 100644 --- a/rsx_program_decompiler/rsx_program_decompiler.vcxproj.user +++ b/rsx_program_decompiler/rsx_program_decompiler.vcxproj.user @@ -1,22 +1,22 @@  - rsx_fp_static_test5.ppu.elf > fp_out.glsl + rsx_fp_dynamic_test3.ppu.elf > fp_out.glsl WindowsLocalDebugger $(SolutionDir)bin\ - rsx_fp_static_test5.ppu.elf > fp_out.glsl + rsx_fp_dynamic_test3.ppu.elf > fp_out.glsl WindowsLocalDebugger $(SolutionDir)bin\ - rsx_fp_static_test5.ppu.elf > fp_out.glsl + rsx_fp_dynamic_test3.ppu.elf > fp_out.glsl WindowsLocalDebugger $(SolutionDir)bin\ - rsx_fp_static_test5.ppu.elf > fp_out.glsl + rsx_fp_dynamic_test3.ppu.elf > fp_out.glsl WindowsLocalDebugger $(SolutionDir)bin\ diff --git a/shader_code/builder.cpp b/shader_code/builder.cpp index 3cb84a6..af59096 100644 --- a/shader_code/builder.cpp +++ b/shader_code/builder.cpp @@ -18,13 +18,8 @@ namespace shader_code return to_string(); } - builder::writer_t::writer_to builder::writer_t::operator()(std::size_t position) - { - return{ &code[position] }; - } - void builder::writer_t::next() { - ++position; + fill_to(++position); } } \ No newline at end of file diff --git a/shader_code/builder.h b/shader_code/builder.h index 15dbaa0..cd2f618 100644 --- a/shader_code/builder.h +++ b/shader_code/builder.h @@ -25,15 +25,26 @@ namespace shader_code struct writer_t { - std::unordered_map code; + std::vector code; std::size_t position = 0; - struct writer_to + writer_t() { - std::string *code; - }; + fill_to(position); + } - writer_to operator()(std::size_t position); + writer_t(const std::string &string) : writer_t() + { + lines(string); + } + + void fill_to(std::size_t position) + { + if (code.size() <= position) + { + code.resize(position + 1); + } + } template void lines(const T&... exprs) @@ -49,6 +60,18 @@ namespace shader_code code[position] += string; } + void before(std::size_t position, const std::string& string) + { + fill_to(position); + code[position] = string + code[position]; + } + + void after(std::size_t position, const std::string& string) + { + fill_to(position); + code[position] += string; + } + void lines(const writer_t& writer) { lines(writer.build()); @@ -72,9 +95,9 @@ namespace shader_code { std::string result; - for (auto entry : code) + for (const std::string &entry : code) { - result += entry.second; + result += entry; } return result; diff --git a/shader_code/clike_language.h b/shader_code/clike_language.h index 4ca99fc..ed5295b 100644 --- a/shader_code/clike_language.h +++ b/shader_code/clike_language.h @@ -131,10 +131,11 @@ namespace shader_code bool is_single; int base_count = Count; - expression_helper_t(const std::string& text, bool is_single = true) + expression_helper_t(const std::string& text, bool is_single = true, int base_count = Count) : expression_base_t{ text } , is_single(is_single) - , mask{ std::string("xyzw").substr(0, Count) } + , mask{ std::string("xyzw").substr(0, base_count) } + , base_count(base_count) { } @@ -158,6 +159,7 @@ namespace shader_code text = rhs.text; mask = rhs.mask; is_single = rhs.is_single; + base_count = rhs.base_count; } template