From 7a98f70db400c5bf6cdac30066da6144179e6adc Mon Sep 17 00:00:00 2001 From: DHrpcs3 Date: Sun, 20 Dec 2015 00:17:58 +0200 Subject: [PATCH] Separate fp::decompiler and decompiler_base Added vp_ucode --- rsx_decompiler/pch.cpp | 1 - rsx_decompiler/pch.h | 2 - rsx_decompiler/rsx_decompiler.cpp | 788 +----------------- rsx_decompiler/rsx_decompiler.h | 69 +- rsx_decompiler/rsx_decompiler.vcxproj | 26 +- rsx_decompiler/rsx_decompiler.vcxproj.filters | 7 +- rsx_decompiler/rsx_decompiler_base.h | 69 ++ rsx_decompiler/rsx_fp_decompiler.cpp | 709 ++++++++++++++++ rsx_decompiler/rsx_fp_ucode.h | 138 +-- rsx_decompiler/rsx_vp_decompiler.cpp | 27 + rsx_decompiler/rsx_vp_ucode.cpp | 22 + rsx_decompiler/rsx_vp_ucode.h | 197 +++++ .../rsx_program_decompiler.cpp | 4 +- 13 files changed, 1176 insertions(+), 883 deletions(-) delete mode 100644 rsx_decompiler/pch.cpp delete mode 100644 rsx_decompiler/pch.h create mode 100644 rsx_decompiler/rsx_decompiler_base.h create mode 100644 rsx_decompiler/rsx_fp_decompiler.cpp create mode 100644 rsx_decompiler/rsx_vp_decompiler.cpp create mode 100644 rsx_decompiler/rsx_vp_ucode.cpp create mode 100644 rsx_decompiler/rsx_vp_ucode.h diff --git a/rsx_decompiler/pch.cpp b/rsx_decompiler/pch.cpp deleted file mode 100644 index bcb5590..0000000 --- a/rsx_decompiler/pch.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "pch.h" diff --git a/rsx_decompiler/pch.h b/rsx_decompiler/pch.h deleted file mode 100644 index 079a383..0000000 --- a/rsx_decompiler/pch.h +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once - diff --git a/rsx_decompiler/rsx_decompiler.cpp b/rsx_decompiler/rsx_decompiler.cpp index 7d3d776..b74d5ca 100644 --- a/rsx_decompiler/rsx_decompiler.cpp +++ b/rsx_decompiler/rsx_decompiler.cpp @@ -1,766 +1,54 @@ -#include "pch.h" -#include "clike_builder.h" -#include -#include -#include -#include -#include +#include "rsx_decompiler.h" +#include +#include namespace rsx { - static const std::string index_to_channel[4] = { "x", "y", "z", "w" }; - static const std::unordered_map channel_to_index = { { 'x', 0 },{ 'y', 1 },{ 'z', 2 },{ 'w', 3 } }; - static const std::string mask = "xyzw"; + const std::string index_to_channel[4] = { "x", "y", "z", "w" }; + const std::unordered_map channel_to_index = { { 'x', 0 },{ 'y', 1 },{ 'z', 2 },{ 'w', 3 } }; + const std::string mask = "xyzw"; - template - struct decompiler_base : shader_code::clike_builder + complete_program finalize_program(const decompiled_program& program) { - enum class compare_function + complete_program result{ "#version 420\n\n" }; + + for (const constant_info& constant : program.constants) { - less, - greater, - equal, - less_equal, - greater_equal, - not_equal - }; - - template - static expression_from> single_compare_function(compare_function function, expression_t a, expression_t 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(); + result.code += "uniform vec4 " + constant.name + ";\n"; } - template - static expression_from> vector_compare_function(compare_function function, expression_t a, expression_t 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); - } + result.code += "\n"; - throw; + for (const register_info& temporary : program.temporary_registers) + { + result.code += "vec4 " + temporary.name + " = vec4(0.0);\n"; } - template - static expression_from> custom_compare(compare_function function, int channel_count, expression_t a, expression_t b) - { - if (channel_count == 1) - { - return single_compare_function(function, a, b); - } + result.code += "\n"; - return vector_compare_function(function, a, b); + for (const texture_info& texture : program.textures) + { + result.code += "uniform sampler2D " + texture.name + ";\n"; } - }; + + for (std::size_t index = 0; index < std::size(rsx::fragment_program::input_attrib_map); ++index) + { + if (program.input_attributes & (1 << index)) + { + result.code += "in vec4 " + rsx::fragment_program::input_attrib_map[index] + ";\n"; + } + } + + result.code += "\n"; + result.code += program.code; + + result.code += + R"( +void main() +{ + )" + program.entry_function + R"((); } - - -#include "glsl_language.h" -#include "rsx_fp_ucode.h" -#include "rsx_decompiler.h" - -namespace rsx -{ - namespace fragment_program - { - class decompiler : public decompiler_base - { - struct context_t - { - decompiled_program program; - bool is_next_is_constant; - u32 offset; - - expression_from> constant() - { - constant_info info; - info.id = offset + sizeof(instruction_t); - info.name = "fc" + std::to_string(info.id); - program.constants.insert(info); - is_next_is_constant = true; - - return info.name; - } - - expression_from> temporary(bool is_fp16, int index) - { - register_info info; - info.id = index; - info.type = is_fp16 ? register_type::half_float_point : register_type::single_float_point; - info.name = (is_fp16 ? "h" : "r") + std::to_string(index); - program.temporary_registers.insert(info); - return info.name; - } - - expression_from> condition(int index) - { - register_info info; - info.id = index; - info.type = register_type::single_float_point; - info.name = "cc" + std::to_string(index); - program.temporary_registers.insert(info); - - return info.name; - } - - expression_from> input(int index) - { - program.input_attributes |= (1 << index); - return input_attrib_map[index]; - } - }; - - public: - struct instruction_t - { - ucode_instr data; - - struct src_t - { - src_reg_type_t reg_type; - u32 tmp_index; - - u32 swizzle_x; - u32 swizzle_y; - u32 swizzle_z; - u32 swizzle_w; - - bool fp16; - bool neg; - bool abs; - }; - - static u32 swap_endianess(u32 data) - { - return - ((data >> 24) & 0x000000ff) | - ((data >> 8) & 0x0000ff00) | - ((data << 8) & 0x00ff0000) | - ((data << 24) & 0xff000000); - } - - instruction_t unpack() - { - instruction_t result; - - result.data.dst._u32 = swap_endianess((data.dst._u32 << 16) | (data.dst._u32 >> 16)); - result.data.src0._u32 = swap_endianess((data.src0._u32 << 16) | (data.src0._u32 >> 16)); - result.data.src1._u32 = swap_endianess((data.src1._u32 << 16) | (data.src1._u32 >> 16)); - result.data.src2._u32 = swap_endianess((data.src2._u32 << 16) | (data.src2._u32 >> 16)); - - return result; - } - - template - static src_t unpack_src(const SrcType& src) - { - src_t result; - - result.reg_type = src.reg_type; - result.tmp_index = src.tmp_reg_index; - - result.swizzle_x = src.swizzle_x; - result.swizzle_y = src.swizzle_y; - result.swizzle_z = src.swizzle_z; - result.swizzle_w = src.swizzle_w; - - result.fp16 = src.fp16; - result.abs = src.abs; - result.neg = src.neg; - - return result; - } - - 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; - - switch (index) - { - case 0: src = unpack_src(data.src0); break; - case 1: src = unpack_src(data.src1); break; - case 2: src = unpack_src(data.src2); break; - } - - auto get_variable = [&](const src_t& src) - { - switch (src.reg_type) - { - case src_reg_type_t::temporary: return context.temporary(src.fp16, src.tmp_index); - case src_reg_type_t::input: return context.input(data.dst.src_attr_reg_num); - case src_reg_type_t::constant: return context.constant(); - } - - throw; - }; - - 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)); - } - - if (src.neg) - { - result.assign(-result); - } - - return result; - } - - std::string destination_swizzle() const - { - std::string swizzle; - - if (data.dst.mask_x) swizzle += mask[0]; - if (data.dst.mask_y) swizzle += mask[1]; - if (data.dst.mask_z) swizzle += mask[2]; - if (data.dst.mask_w) swizzle += mask[3]; - - return swizzle; - } - - expression_from> destination(context_t& context) const - { - if (data.dst.no_dest) - { - return{ "", destination_swizzle() }; - } - - return{ context.temporary(data.dst.fp16, data.dst.dest_reg).to_string(), destination_swizzle() }; - } - }; - - static_assert(sizeof(instruction_t) == 16, "Bad instruction_t implementation"); - - instruction_t instruction; - context_t context; - writer_t writer; - - expression_from> src(int index, bool is_swizzle_as_dst = false) - { - return instruction.src(context, index, is_swizzle_as_dst); - } - - expression_from> src_swizzled_as_dst(int index) - { - return src(index, instruction.data.dst.set_cond || !instruction.data.dst.no_dest); - } - - expression_from> modify_condition_register() - { - return context.condition(instruction.data.src0.cond_mod_reg_index); - } - - expression_from> execution_condition_register() - { - std::string swizzle; - - swizzle += mask[instruction.data.src0.cond_swizzle_x]; - swizzle += mask[instruction.data.src0.cond_swizzle_y]; - swizzle += mask[instruction.data.src0.cond_swizzle_z]; - swizzle += mask[instruction.data.src0.cond_swizzle_w]; - - return{ context.condition(instruction.data.src0.cond_reg_index).text, swizzle }; - } - - enum class condition_operation - { - all, - any - }; - - compare_function execution_condition_function() const - { - if (instruction.data.src0.exec_if_gr && instruction.data.src0.exec_if_eq) - { - return compare_function::greater_equal; - } - if (instruction.data.src0.exec_if_lt && instruction.data.src0.exec_if_eq) - { - return compare_function::less_equal; - } - if (instruction.data.src0.exec_if_gr && instruction.data.src0.exec_if_lt) - { - return compare_function::not_equal; - } - if (instruction.data.src0.exec_if_gr) - { - return compare_function::greater; - } - if (instruction.data.src0.exec_if_lt) - { - return compare_function::less; - } - - if (instruction.data.src0.exec_if_eq) - { - return compare_function::equal; - } - - throw; - } - - expression_from> execution_condition(condition_operation operation) - { - auto cond = execution_condition_register(); - - if (instruction.data.src0.exec_if_gr && instruction.data.src0.exec_if_eq && instruction.data.src0.exec_if_gr) - { - return true; - } - - if (!instruction.data.src0.exec_if_gr && !instruction.data.src0.exec_if_eq && !instruction.data.src0.exec_if_gr) - { - return false; - } - - if (instruction.data.src0.cond_swizzle_x == instruction.data.src0.cond_swizzle_y && - instruction.data.src0.cond_swizzle_y == instruction.data.src0.cond_swizzle_z && - instruction.data.src0.cond_swizzle_z == instruction.data.src0.cond_swizzle_w) - { - return custom_compare(execution_condition_function(), 1, cond.x(), expression_from>(0.0f)); - } - - auto result = custom_compare(execution_condition_function(), 4, cond, float_point_t<4>::ctor(0.0f)); - - switch (operation) - { - case condition_operation::all: return all(result); - case condition_operation::any: return any(result); - } - - throw; - } - - expression_from> compare(compare_function function, expression_from> a, expression_from> b) - { - return custom_compare(function, (int)instruction.destination_swizzle().size(), a, b); - } - - expression_from tex() - { - return{ "unk_tex" }; - } - - template - expression_from conditional(const ExprType& expr) - { - bool need_condition = false; - - if (need_condition) - { - return if_(any(execution_condition(condition_operation::any)), expr); - } - - return expr; - } - - enum set_dst_flags - { - none, - disable_swizzle_as_dst = 1, - }; - - template - Type apply_instruction_modifiers(Type arg) - { - using float_t = expression_from>; - - switch (instruction.data.src1.scale) - { - case 0: break; - case 1: arg.assign(arg * (float_t)2.0f); break; - case 2: arg.assign(arg * (float_t)4.0f); break; - case 3: arg.assign(arg * (float_t)8.0f); break; - case 5: arg.assign(arg / (float_t)2.0f); break; - case 6: arg.assign(arg / (float_t)4.0f); break; - case 7: arg.assign(arg / (float_t)8.0f); break; - - default: - throw std::runtime_error("fragment program decompiler: unimplemented scale (" + std::to_string(instruction.data.src1.scale) + "). "); - } - - 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, u32 flags = none) - { - writer_t result; - - auto modify_cond = modify_condition_register(); - auto dest = instruction.destination(context); - - if (!instruction.data.src0.exec_if_eq || !instruction.data.src0.exec_if_gr || !instruction.data.src0.exec_if_lt) - { - std::string cond_mask; - cond_mask += index_to_channel[instruction.data.src0.cond_swizzle_x]; - cond_mask += index_to_channel[instruction.data.src0.cond_swizzle_y]; - cond_mask += index_to_channel[instruction.data.src0.cond_swizzle_z]; - cond_mask += index_to_channel[instruction.data.src0.cond_swizzle_w]; - - auto cond = execution_condition_register(); - - std::string operation; - - if (instruction.data.src0.exec_if_gr && instruction.data.src0.exec_if_lt) - { - operation = "!="; - } - else if (!instruction.data.src0.exec_if_gr && !instruction.data.src0.exec_if_lt) - { - operation = "=="; - } - else - { - if (instruction.data.src0.exec_if_gr) - operation += ">"; - else if (instruction.data.src0.exec_if_lt) - operation += "<"; - - if (instruction.data.src0.exec_if_eq) - operation += "="; - } - - if (!instruction.data.dst.set_cond && instruction.data.dst.no_dest) - { - //condition must be already handled in instruction semantic (IFE, LOOP, etc) - result += comment("WARNING: extra condition test skipped"); - result += arg; - } - else - { - static const expression_from> zero(0.0f); - - std::map>> condition_map; - - int channel_index = 0; - if (instruction.data.dst.mask_x) condition_map[cond.mask[0]].push_back({ 0, channel_index++ }); - if (instruction.data.dst.mask_y) condition_map[cond.mask[1]].push_back({ 1, channel_index++ }); - if (instruction.data.dst.mask_z) condition_map[cond.mask[2]].push_back({ 2, channel_index++ }); - if (instruction.data.dst.mask_w) condition_map[cond.mask[3]].push_back({ 3, channel_index }); - - auto src = arg; - - if (flags & disable_swizzle_as_dst) - { - src.assign(expression_from>(arg.text, arg.mask, true, dest.mask.size())); - } - - for (auto &entry : condition_map) - { - std::string src_swizzle; - std::string dst_swizzle; - - for (std::pair channels : entry.second) - { - src_swizzle += src.swizzle(flags & disable_swizzle_as_dst ? channels.second : channels.first).mask[0]; - dst_swizzle += dest.swizzle(channels.second).mask[0]; - } - - expression_from> expression{ src.with_mask(src_swizzle) }; - - if (!instruction.data.dst.no_dest) - { - expression.assign(dest.with_mask(dst_swizzle) = expression); - } - - if (instruction.data.dst.set_cond) - { - expression.assign(cond.with_mask(dst_swizzle) = expression); - } - - result += if_(cond.swizzle(channel_to_index.at(entry.first)).call_operator>(operation, zero), expression); - } - } - } - else - { - expression_from> src = arg; - - if (instruction.data.dst.set_cond || !instruction.data.dst.no_dest) - { - if ((flags & disable_swizzle_as_dst) == 0) - { - src.assign(instruction.swizzle_as_dst(arg)); - } - - src.assign(apply_instruction_modifiers(src).without_scope()); - - if (!instruction.data.dst.no_dest) - { - src.assign(dest = src); - } - - if (instruction.data.dst.set_cond) - { - src.assign(expression_from>(modify_cond.text, dest.mask) = src); - } - } - - result += src; - } - - 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; - - bool is_single = true; - - switch (instruction.destination_swizzle().size()) - { - case 1: arg_string = arg.to_string() + " ? 1.0 : 0.0"; is_single = false; 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"), is_single, 4 }, flags); - } - - writer_t comment(const std::string& lines) - { - writer_t result; - - result += "//" + lines + "\n"; - - return result; - } - - writer_t warning(const std::string& lines) - { - return comment("WARNING: " + lines); - } - - writer_t unimplemented(const std::string& lines) - { - return comment("TODO: " + lines); - } - - expression_base_t decode_instruction() - { - 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_swizzled_as_dst(0), disable_swizzle_as_dst); - 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(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); - auto src_1 = src(1); - - return set_dst(float_point_t<4>::ctor(1.0f, src_0.y() * src_1.y(), src_0.z(), src_1.w())); - } - 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(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;")); - case opcode::PK4: return unimplemented("PK4"); - case opcode::UP4: return unimplemented("UP4"); - case opcode::DDX: return set_dst(ddx(src_swizzled_as_dst(0)), disable_swizzle_as_dst); - case opcode::DDY: return set_dst(ddy(src_swizzled_as_dst(0)), disable_swizzle_as_dst); - case opcode::TEX: return set_dst(texture(tex(), src(0).xy())); - case opcode::TXP: return set_dst(texture(tex(), src(0).xy() / src(0).w())); - case opcode::TXD: return set_dst(texture_grad(tex(), src(0).xy(), src(1).xy(), src(2).xy())); - case opcode::RCP: return set_dst(float_point_t<1>::ctor(1.0f) / src_swizzled_as_dst(0), disable_swizzle_as_dst); - case opcode::RSQ: return set_dst(rsqrt(src_swizzled_as_dst(0)), disable_swizzle_as_dst); - case opcode::EX2: return set_dst(exp2(src_swizzled_as_dst(0)), disable_swizzle_as_dst); - 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(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"); - case opcode::UP2: return unimplemented("UP2"); - case opcode::POW: return set_dst(pow(src_swizzled_as_dst(0), src_swizzled_as_dst(1)), disable_swizzle_as_dst); - case opcode::PKB: return unimplemented("PKB"); - case opcode::UPB: return unimplemented("UPB"); - case opcode::PK16: return unimplemented("PK16"); - case opcode::UP16: return unimplemented("UP16"); - case opcode::BEM: return unimplemented("BEM"); - case opcode::PKG: return unimplemented("PKG"); - case opcode::UPG: return unimplemented("UPG"); - case opcode::DP2A: - { - auto src_0 = src(0); - auto src_1 = src(1); - - return set_dst(float_point_t<4>::ctor(src_0.x() * src_1.x() + src_0.y() * src_1.y() + src(2).z())); - } - case opcode::TXL: return set_dst(texture_lod(tex(), src(0).xy(), src(1).x())); - case opcode::TXB: return set_dst(texture_bias(tex(), src(0).xy(), src(1).x())); - case opcode::TEXBEM: return unimplemented("TEXBEM"); - case opcode::TXPBEM: return unimplemented("TXPBEM"); - case opcode::BEMLUM: return unimplemented("BEMLUM"); - case opcode::REFL: return unimplemented("REFL"); - case opcode::TIMESWTEX: return unimplemented("TIMESWTEX"); - 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); - case opcode::LIF: return unimplemented("LIF"); - case opcode::FENCT: return comment("fenct"); - case opcode::FENCB: return comment("fencb"); - case opcode::BRK: return conditional(expression_from("break;")); - case opcode::CAL: return unimplemented("CAL"); - case opcode::IFE: - writer += writer_t{ "if (" + execution_condition(condition_operation::all).to_string() + ")\n{\n" }; - - 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 ""; - - case opcode::LOOP: - writer += writer_t - { - "for (" - "int i = " + std::to_string(instruction.data.src1.init_counter) + "; " + - "i < " + std::to_string(instruction.data.src1.end_counter) + "; " + - (instruction.data.src1.increment == 1 ? "i++" : "i += " + std::to_string(instruction.data.src1.increment)) + - ")\n{\n" - }; - - writer.after(instruction.data.src2.end_offset >> 2, "}\n"); - return ""; - case opcode::REP: return unimplemented("REP"); - case opcode::RET: return conditional(expression_from("return;")); - } - - throw; - } - - decompiled_program decompile(std::size_t offset, instruction_t* instructions) - { - context.offset = 0; - context.is_next_is_constant = false; - - for (std::size_t index = offset; true; ++index, writer.next(), context.offset += sizeof(instruction_t)) - { - if (context.is_next_is_constant) - { - context.is_next_is_constant = false; - continue; - } - - instruction = instructions[index].unpack(); - - writer += decode_instruction(); - - if (instruction.data.dst.end) - break; - } - - context.program.entry_function = "func0"; - context.program.code = "void func0()\n{\n" + writer.build() + "}\n"; - - return context.program; - } - }; - - decompiled_program decompile(std::size_t offset, ucode_instr *instructions) - { - return decompiler{}.decompile(offset, (decompiler::instruction_t*)instructions); - } +)"; + return result; } } diff --git a/rsx_decompiler/rsx_decompiler.h b/rsx_decompiler/rsx_decompiler.h index 8f03c91..c289a02 100644 --- a/rsx_decompiler/rsx_decompiler.h +++ b/rsx_decompiler/rsx_decompiler.h @@ -1,7 +1,9 @@ #pragma once #include "rsx_fp_ucode.h" +#include "rsx_vp_ucode.h" #include #include +#include namespace rsx { @@ -79,8 +81,22 @@ namespace rsx } }; + enum class decompile_language + { + glsl + }; + + enum class program_type + { + vertex, + fragment + }; + struct decompiled_program { + program_type type; + decompile_language code_language; + std::unordered_set constants; std::unordered_set textures; std::unordered_set temporary_registers; @@ -96,52 +112,21 @@ namespace rsx std::string code; }; + extern const std::string index_to_channel[4]; + extern const std::unordered_map channel_to_index; + extern const std::string mask; + + complete_program finalize_program(const decompiled_program& program); + + namespace fragment_program { - decompiled_program decompile(std::size_t offset, ucode_instr* instructions); + decompiled_program decompile(std::size_t offset, ucode_instr* instructions, decompile_language lang); } - inline complete_program finalize_program(const decompiled_program& program) + namespace vertex_program { - complete_program result{ "#version 420\n\n" }; - - for (const constant_info& constant : program.constants) - { - result.code += "uniform vec4 " + constant.name + ";\n"; - } - - result.code += "\n"; - - for (const register_info& temporary : program.temporary_registers) - { - result.code += "vec4 " + temporary.name + " = vec4(0.0);\n"; - } - - result.code += "\n"; - - for (const texture_info& texture : program.textures) - { - result.code += "uniform sampler2D " + texture.name + ";\n"; - } - - for (std::size_t index = 0; index < std::size(rsx::fragment_program::input_attrib_map); ++index) - { - if (program.input_attributes & (1 << index)) - { - result.code += "in vec4 " + rsx::fragment_program::input_attrib_map[index] + ";\n"; - } - } - - result.code += "\n"; - result.code += program.code; - - result.code += - R"( -void main() -{ - )" + program.entry_function + R"((); -} -)"; - return result; + decompiled_program decompile(std::size_t offset, ucode_instr* instructions, decompile_language lang); } } + diff --git a/rsx_decompiler/rsx_decompiler.vcxproj b/rsx_decompiler/rsx_decompiler.vcxproj index 84f5e1a..97a897a 100644 --- a/rsx_decompiler/rsx_decompiler.vcxproj +++ b/rsx_decompiler/rsx_decompiler.vcxproj @@ -142,7 +142,7 @@ - Use + NotUsing false true @@ -157,7 +157,7 @@ - Use + NotUsing false true @@ -172,7 +172,7 @@ - Use + NotUsing false true @@ -187,7 +187,7 @@ - Use + NotUsing false true @@ -202,7 +202,7 @@ - Use + NotUsing false true @@ -217,7 +217,7 @@ - Use + NotUsing false true @@ -233,19 +233,15 @@ - + + - - Create - Create - Create - Create - Create - Create - + + + CppCode diff --git a/rsx_decompiler/rsx_decompiler.vcxproj.filters b/rsx_decompiler/rsx_decompiler.vcxproj.filters index ca35f3d..b320774 100644 --- a/rsx_decompiler/rsx_decompiler.vcxproj.filters +++ b/rsx_decompiler/rsx_decompiler.vcxproj.filters @@ -8,13 +8,16 @@ - + + + - + + \ No newline at end of file diff --git a/rsx_decompiler/rsx_decompiler_base.h b/rsx_decompiler/rsx_decompiler_base.h new file mode 100644 index 0000000..5b89d04 --- /dev/null +++ b/rsx_decompiler/rsx_decompiler_base.h @@ -0,0 +1,69 @@ +#pragma once + +#include "rsx_decompiler.h" +#include + +namespace rsx +{ + template + struct decompiler_base : shader_code::clike_builder + { + enum class compare_function + { + less, + greater, + equal, + less_equal, + greater_equal, + not_equal + }; + + template + static expression_from> single_compare_function(compare_function function, expression_t a, expression_t 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(); + } + + template + static expression_from> vector_compare_function(compare_function function, expression_t a, expression_t 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; + } + + template + static expression_from> custom_compare(compare_function function, int channel_count, expression_t a, expression_t b) + { + if (channel_count == 1) + { + return single_compare_function(function, a, b); + } + + return vector_compare_function(function, a, b); + } + }; +} \ No newline at end of file diff --git a/rsx_decompiler/rsx_fp_decompiler.cpp b/rsx_decompiler/rsx_fp_decompiler.cpp new file mode 100644 index 0000000..3318651 --- /dev/null +++ b/rsx_decompiler/rsx_fp_decompiler.cpp @@ -0,0 +1,709 @@ +#include "rsx_decompiler_base.h" +#include "rsx_fp_ucode.h" +#include +#include + +namespace rsx +{ + namespace fragment_program + { + template + class decompiler : public decompiler_base + { + struct context_t + { + decompiled_program program; + bool is_next_is_constant; + u32 offset; + + expression_from> constant() + { + constant_info info; + info.id = offset + sizeof(instruction_t); + info.name = "fc" + std::to_string(info.id); + program.constants.insert(info); + is_next_is_constant = true; + + return info.name; + } + + expression_from> temporary(bool is_fp16, int index) + { + register_info info; + info.id = index; + info.type = is_fp16 ? register_type::half_float_point : register_type::single_float_point; + info.name = (is_fp16 ? "h" : "r") + std::to_string(index); + program.temporary_registers.insert(info); + return info.name; + } + + expression_from> condition(int index) + { + register_info info; + info.id = index; + info.type = register_type::single_float_point; + info.name = "cc" + std::to_string(index); + program.temporary_registers.insert(info); + + return info.name; + } + + expression_from> input(int index) + { + program.input_attributes |= (1 << index); + return input_attrib_map[index]; + } + }; + + public: + struct instruction_t + { + ucode_instr data; + + struct src_t + { + src_reg_type_t reg_type; + u32 tmp_index; + + u32 swizzle_x; + u32 swizzle_y; + u32 swizzle_z; + u32 swizzle_w; + + bool fp16; + bool neg; + bool abs; + }; + + static u32 swap_endianess(u32 data) + { + return + ((data >> 24) & 0x000000ff) | + ((data >> 8) & 0x0000ff00) | + ((data << 8) & 0x00ff0000) | + ((data << 24) & 0xff000000); + } + + instruction_t unpack() + { + instruction_t result; + + result.data.dst._u32 = swap_endianess((data.dst._u32 << 16) | (data.dst._u32 >> 16)); + result.data.src0._u32 = swap_endianess((data.src0._u32 << 16) | (data.src0._u32 >> 16)); + result.data.src1._u32 = swap_endianess((data.src1._u32 << 16) | (data.src1._u32 >> 16)); + result.data.src2._u32 = swap_endianess((data.src2._u32 << 16) | (data.src2._u32 >> 16)); + + return result; + } + + template + static src_t unpack_src(const SrcType& src) + { + src_t result; + + result.reg_type = src.reg_type; + result.tmp_index = src.tmp_reg_index; + + result.swizzle_x = src.swizzle_x; + result.swizzle_y = src.swizzle_y; + result.swizzle_z = src.swizzle_z; + result.swizzle_w = src.swizzle_w; + + result.fp16 = src.fp16; + result.abs = src.abs; + result.neg = src.neg; + + return result; + } + + 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; + + switch (index) + { + case 0: src = unpack_src(data.src0); break; + case 1: src = unpack_src(data.src1); break; + case 2: src = unpack_src(data.src2); break; + } + + auto get_variable = [&](const src_t& src) + { + switch (src.reg_type) + { + case src_reg_type_t::temporary: return context.temporary(src.fp16, src.tmp_index); + case src_reg_type_t::input: return context.input(data.dst.src_attr_reg_num); + case src_reg_type_t::constant: return context.constant(); + } + + throw; + }; + + 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)); + } + + if (src.neg) + { + result.assign(-result); + } + + return result; + } + + std::string destination_swizzle() const + { + std::string swizzle; + + if (data.dst.mask_x) swizzle += mask[0]; + if (data.dst.mask_y) swizzle += mask[1]; + if (data.dst.mask_z) swizzle += mask[2]; + if (data.dst.mask_w) swizzle += mask[3]; + + return swizzle; + } + + expression_from> destination(context_t& context) const + { + if (data.dst.no_dest) + { + return{ "", destination_swizzle() }; + } + + return{ context.temporary(data.dst.fp16, data.dst.dest_reg).to_string(), destination_swizzle() }; + } + }; + + static_assert(sizeof(instruction_t) == 16, "Bad instruction_t implementation"); + + instruction_t instruction; + context_t context; + writer_t writer; + + expression_from> src(int index, bool is_swizzle_as_dst = false) + { + return instruction.src(context, index, is_swizzle_as_dst); + } + + expression_from> src_swizzled_as_dst(int index) + { + return src(index, instruction.data.dst.set_cond || !instruction.data.dst.no_dest); + } + + expression_from> modify_condition_register() + { + return context.condition(instruction.data.src0.cond_mod_reg_index); + } + + expression_from> execution_condition_register() + { + std::string swizzle; + + swizzle += mask[instruction.data.src0.cond_swizzle_x]; + swizzle += mask[instruction.data.src0.cond_swizzle_y]; + swizzle += mask[instruction.data.src0.cond_swizzle_z]; + swizzle += mask[instruction.data.src0.cond_swizzle_w]; + + return{ context.condition(instruction.data.src0.cond_reg_index).text, swizzle }; + } + + enum class condition_operation + { + all, + any + }; + + compare_function execution_condition_function() const + { + if (instruction.data.src0.exec_if_gr && instruction.data.src0.exec_if_eq) + { + return compare_function::greater_equal; + } + if (instruction.data.src0.exec_if_lt && instruction.data.src0.exec_if_eq) + { + return compare_function::less_equal; + } + if (instruction.data.src0.exec_if_gr && instruction.data.src0.exec_if_lt) + { + return compare_function::not_equal; + } + if (instruction.data.src0.exec_if_gr) + { + return compare_function::greater; + } + if (instruction.data.src0.exec_if_lt) + { + return compare_function::less; + } + + if (instruction.data.src0.exec_if_eq) + { + return compare_function::equal; + } + + throw; + } + + expression_from> execution_condition(condition_operation operation) + { + auto cond = execution_condition_register(); + + if (instruction.data.src0.exec_if_gr && instruction.data.src0.exec_if_eq && instruction.data.src0.exec_if_gr) + { + return true; + } + + if (!instruction.data.src0.exec_if_gr && !instruction.data.src0.exec_if_eq && !instruction.data.src0.exec_if_gr) + { + return false; + } + + if (instruction.data.src0.cond_swizzle_x == instruction.data.src0.cond_swizzle_y && + instruction.data.src0.cond_swizzle_y == instruction.data.src0.cond_swizzle_z && + instruction.data.src0.cond_swizzle_z == instruction.data.src0.cond_swizzle_w) + { + return custom_compare(execution_condition_function(), 1, cond.x(), expression_from>(0.0f)); + } + + auto result = custom_compare(execution_condition_function(), 4, cond, float_point_t<4>::ctor(0.0f)); + + switch (operation) + { + case condition_operation::all: return all(result); + case condition_operation::any: return any(result); + } + + throw; + } + + expression_from> compare(compare_function function, expression_from> a, expression_from> b) + { + return custom_compare(function, (int)instruction.destination_swizzle().size(), a, b); + } + + expression_from tex() + { + return{ "unk_tex" }; + } + + template + expression_from conditional(const ExprType& expr) + { + bool need_condition = false; + + if (need_condition) + { + return if_(any(execution_condition(condition_operation::any)), expr); + } + + return expr; + } + + enum set_dst_flags + { + none, + disable_swizzle_as_dst = 1, + }; + + template + Type apply_instruction_modifiers(Type arg) + { + using float_t = expression_from>; + + switch (instruction.data.src1.scale) + { + case 0: break; + case 1: arg.assign(arg * (float_t)2.0f); break; + case 2: arg.assign(arg * (float_t)4.0f); break; + case 3: arg.assign(arg * (float_t)8.0f); break; + case 5: arg.assign(arg / (float_t)2.0f); break; + case 6: arg.assign(arg / (float_t)4.0f); break; + case 7: arg.assign(arg / (float_t)8.0f); break; + + default: + throw std::runtime_error("fragment program decompiler: unimplemented scale (" + std::to_string(instruction.data.src1.scale) + "). "); + } + + 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, u32 flags = none) + { + writer_t result; + + auto modify_cond = modify_condition_register(); + auto dest = instruction.destination(context); + + if (!instruction.data.src0.exec_if_eq || !instruction.data.src0.exec_if_gr || !instruction.data.src0.exec_if_lt) + { + std::string cond_mask; + cond_mask += index_to_channel[instruction.data.src0.cond_swizzle_x]; + cond_mask += index_to_channel[instruction.data.src0.cond_swizzle_y]; + cond_mask += index_to_channel[instruction.data.src0.cond_swizzle_z]; + cond_mask += index_to_channel[instruction.data.src0.cond_swizzle_w]; + + auto cond = execution_condition_register(); + + std::string operation; + + if (instruction.data.src0.exec_if_gr && instruction.data.src0.exec_if_lt) + { + operation = "!="; + } + else if (!instruction.data.src0.exec_if_gr && !instruction.data.src0.exec_if_lt) + { + operation = "=="; + } + else + { + if (instruction.data.src0.exec_if_gr) + operation += ">"; + else if (instruction.data.src0.exec_if_lt) + operation += "<"; + + if (instruction.data.src0.exec_if_eq) + operation += "="; + } + + if (!instruction.data.dst.set_cond && instruction.data.dst.no_dest) + { + //condition must be already handled in instruction semantic (IFE, LOOP, etc) + result += comment("WARNING: extra condition test skipped"); + result += arg; + } + else + { + static const expression_from> zero(0.0f); + + std::map>> condition_map; + + int channel_index = 0; + if (instruction.data.dst.mask_x) condition_map[cond.mask[0]].push_back({ 0, channel_index++ }); + if (instruction.data.dst.mask_y) condition_map[cond.mask[1]].push_back({ 1, channel_index++ }); + if (instruction.data.dst.mask_z) condition_map[cond.mask[2]].push_back({ 2, channel_index++ }); + if (instruction.data.dst.mask_w) condition_map[cond.mask[3]].push_back({ 3, channel_index }); + + auto src = arg; + + if (flags & disable_swizzle_as_dst) + { + src.assign(expression_from>(arg.text, arg.mask, true, (int)dest.mask.size())); + } + + for (auto &entry : condition_map) + { + std::string src_swizzle; + std::string dst_swizzle; + + for (std::pair channels : entry.second) + { + src_swizzle += src.swizzle(flags & disable_swizzle_as_dst ? channels.second : channels.first).mask[0]; + dst_swizzle += dest.swizzle(channels.second).mask[0]; + } + + expression_from> expression{ src.with_mask(src_swizzle) }; + + if (!instruction.data.dst.no_dest) + { + expression.assign(dest.with_mask(dst_swizzle) = expression); + } + + if (instruction.data.dst.set_cond) + { + expression.assign(cond.with_mask(dst_swizzle) = expression); + } + + result += if_(cond.swizzle(channel_to_index.at(entry.first)).call_operator>(operation, zero), expression); + } + } + } + else + { + expression_from> src = arg; + + if (instruction.data.dst.set_cond || !instruction.data.dst.no_dest) + { + if ((flags & disable_swizzle_as_dst) == 0) + { + src.assign(instruction.swizzle_as_dst(arg)); + } + + src.assign(apply_instruction_modifiers(src).without_scope()); + + if (!instruction.data.dst.no_dest) + { + src.assign(dest = src); + } + + if (instruction.data.dst.set_cond) + { + src.assign(expression_from>(modify_cond.text, dest.mask) = src); + } + } + + result += src; + } + + 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; + + bool is_single = true; + + switch (instruction.destination_swizzle().size()) + { + case 1: arg_string = arg.to_string() + " ? 1.0 : 0.0"; is_single = false; 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"), is_single, 4 }, flags); + } + + writer_t comment(const std::string& lines) + { + writer_t result; + + result += "//" + lines + "\n"; + + return result; + } + + writer_t warning(const std::string& lines) + { + return comment("WARNING: " + lines); + } + + writer_t unimplemented(const std::string& lines) + { + return comment("TODO: " + lines); + } + + expression_base_t decode_instruction() + { + 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_swizzled_as_dst(0), disable_swizzle_as_dst); + 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(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); + auto src_1 = src(1); + + return set_dst(float_point_t<4>::ctor(1.0f, src_0.y() * src_1.y(), src_0.z(), src_1.w())); + } + 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(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;")); + case opcode::pk4: return unimplemented("PK4"); + case opcode::up4: return unimplemented("UP4"); + case opcode::ddx: return set_dst(ddx(src_swizzled_as_dst(0)), disable_swizzle_as_dst); + case opcode::ddy: return set_dst(ddy(src_swizzled_as_dst(0)), disable_swizzle_as_dst); + case opcode::tex: return set_dst(texture(tex(), src(0).xy())); + case opcode::txp: return set_dst(texture(tex(), src(0).xy() / src(0).w())); + case opcode::txd: return set_dst(texture_grad(tex(), src(0).xy(), src(1).xy(), src(2).xy())); + case opcode::rcp: return set_dst(float_point_t<1>::ctor(1.0f) / src_swizzled_as_dst(0), disable_swizzle_as_dst); + case opcode::rsq: return set_dst(rsqrt(src_swizzled_as_dst(0)), disable_swizzle_as_dst); + case opcode::ex2: return set_dst(exp2(src_swizzled_as_dst(0)), disable_swizzle_as_dst); + 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(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"); + case opcode::up2: return unimplemented("UP2"); + case opcode::pow: return set_dst(pow(src_swizzled_as_dst(0), src_swizzled_as_dst(1)), disable_swizzle_as_dst); + case opcode::pkb: return unimplemented("PKB"); + case opcode::upb: return unimplemented("UPB"); + case opcode::pk16: return unimplemented("PK16"); + case opcode::up16: return unimplemented("UP16"); + case opcode::bem: return unimplemented("BEM"); + case opcode::pkg: return unimplemented("PKG"); + case opcode::upg: return unimplemented("UPG"); + case opcode::dp2a: + { + auto src_0 = src(0); + auto src_1 = src(1); + + return set_dst(float_point_t<4>::ctor(src_0.x() * src_1.x() + src_0.y() * src_1.y() + src(2).z())); + } + case opcode::txl: return set_dst(texture_lod(tex(), src(0).xy(), src(1).x())); + case opcode::txb: return set_dst(texture_bias(tex(), src(0).xy(), src(1).x())); + case opcode::texbem: return unimplemented("TEXBEM"); + case opcode::txpbem: return unimplemented("TXPBEM"); + case opcode::bemlum: return unimplemented("BEMLUM"); + case opcode::refl: return unimplemented("REFL"); + case opcode::timeswtex: return unimplemented("TIMESWTEX"); + 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); + case opcode::lif: return unimplemented("LIF"); + case opcode::fenct: return comment("fenct"); + case opcode::fencb: return comment("fencb"); + case opcode::brk: return conditional(expression_from("break;")); + case opcode::cal: return unimplemented("CAL"); + case opcode::ife: + writer += writer_t{ "if (" + execution_condition(condition_operation::all).to_string() + ")\n{\n" }; + + 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 ""; + + case opcode::loop: + writer += writer_t + { + "for (" + "int i = " + std::to_string(instruction.data.src1.init_counter) + "; " + + "i < " + std::to_string(instruction.data.src1.end_counter) + "; " + + (instruction.data.src1.increment == 1 ? "i++" : "i += " + std::to_string(instruction.data.src1.increment)) + + ")\n{\n" + }; + + writer.after(instruction.data.src2.end_offset >> 2, "}\n"); + return ""; + case opcode::rep: return unimplemented("REP"); + case opcode::ret: return conditional(expression_from("return;")); + } + + throw; + } + + decompiled_program decompile(std::size_t offset, instruction_t* instructions) + { + context.offset = 0; + context.is_next_is_constant = false; + + for (std::size_t index = offset; true; ++index, writer.next(), context.offset += sizeof(instruction_t)) + { + if (context.is_next_is_constant) + { + context.is_next_is_constant = false; + continue; + } + + instruction = instructions[index].unpack(); + + writer += decode_instruction(); + + if (instruction.data.dst.end) + break; + } + + context.program.entry_function = "func0"; + context.program.code = "void func0()\n{\n" + writer.build() + "}\n"; + + return context.program; + } + }; + + template + decompiled_program decompile(std::size_t offset, ucode_instr *instructions) + { + return decompiler{}.decompile(offset, (decompiler::instruction_t*)instructions); + } + + decompiled_program decompile(std::size_t offset, ucode_instr *instructions, decompile_language lang) + { + decompiled_program result; + switch (lang) + { + case decompile_language::glsl: + result = decompile(offset, instructions); + result.code_language = decompile_language::glsl; + break; + + default: + throw; + } + + result.type = program_type::fragment; + return result; + } + } +} diff --git a/rsx_decompiler/rsx_fp_ucode.h b/rsx_decompiler/rsx_fp_ucode.h index dc1df00..a0dfc8e 100644 --- a/rsx_decompiler/rsx_fp_ucode.h +++ b/rsx_decompiler/rsx_fp_ucode.h @@ -3,80 +3,80 @@ namespace rsx { - using u32 = unsigned int; - namespace fragment_program { + using u32 = std::uint32_t; + enum class opcode : u32 { - NOP = 0x00, // No-Operation - MOV = 0x01, // Move - MUL = 0x02, // Multiply - ADD = 0x03, // Add - MAD = 0x04, // Multiply-Add - DP3 = 0x05, // 3-component Dot Product - DP4 = 0x06, // 4-component Dot Product - DST = 0x07, // Distance - MIN = 0x08, // Minimum - MAX = 0x09, // Maximum - SLT = 0x0A, // Set-If-LessThan - SGE = 0x0B, // Set-If-GreaterEqual - SLE = 0x0C, // Set-If-LessEqual - SGT = 0x0D, // Set-If-GreaterThan - SNE = 0x0E, // Set-If-NotEqual - SEQ = 0x0F, // Set-If-Equal - FRC = 0x10, // Fraction (fract) - FLR = 0x11, // Floor - KIL = 0x12, // Kill fragment - PK4 = 0x13, // Pack four signed 8-bit values - UP4 = 0x14, // Unpack four signed 8-bit values - DDX = 0x15, // Partial-derivative in x (Screen space derivative w.r.t. x) - DDY = 0x16, // Partial-derivative in y (Screen space derivative w.r.t. y) - TEX = 0x17, // Texture lookup - TXP = 0x18, // Texture sample with projection (Projective texture lookup) - TXD = 0x19, // Texture sample with partial differentiation (Texture lookup with derivatives) - RCP = 0x1A, // Reciprocal - RSQ = 0x1B, // Reciprocal Square Root - EX2 = 0x1C, // Exponentiation base 2 - LG2 = 0x1D, // Log base 2 - LIT = 0x1E, // Lighting coefficients - LRP = 0x1F, // Linear Interpolation - STR = 0x20, // Set-If-True - SFL = 0x21, // Set-If-False - COS = 0x22, // Cosine - SIN = 0x23, // Sine - PK2 = 0x24, // Pack two 16-bit floats - UP2 = 0x25, // Unpack two 16-bit floats - POW = 0x26, // Power - PKB = 0x27, // Pack bytes - UPB = 0x28, // Unpack bytes - PK16 = 0x29, // Pack 16 bits - UP16 = 0x2A, // Unpack 16 - BEM = 0x2B, // Bump-environment map (a.k.a. 2D coordinate transform) - PKG = 0x2C, // Pack with sRGB transformation - UPG = 0x2D, // Unpack gamma - DP2A = 0x2E, // 2-component dot product with scalar addition - TXL = 0x2F, // Texture sample with explicit LOD - TXB = 0x31, // Texture sample with bias - TEXBEM = 0x33, - TXPBEM = 0x34, - BEMLUM = 0x35, - REFL = 0x36, // Reflection vector - TIMESWTEX = 0x37, - DP2 = 0x38, // 2-component dot product - NRM = 0x39, // Normalize - DIV = 0x3A, // Division - DIVSQ = 0x3B, // Divide by Square Root - LIF = 0x3C, // Final part of LIT - FENCT = 0x3D, // Fence T? - FENCB = 0x3E, // Fence B? + nop = 0x00, // No-Operation + mov = 0x01, // Move + mul = 0x02, // Multiply + add = 0x03, // Add + mad = 0x04, // Multiply-Add + dp3 = 0x05, // 3-component Dot Product + dp4 = 0x06, // 4-component Dot Product + dst = 0x07, // Distance + min = 0x08, // Minimum + max = 0x09, // Maximum + slt = 0x0A, // Set-If-LessThan + sge = 0x0B, // Set-If-GreaterEqual + sle = 0x0C, // Set-If-LessEqual + sgt = 0x0D, // Set-If-GreaterThan + sne = 0x0E, // Set-If-NotEqual + seq = 0x0F, // Set-If-Equal + frc = 0x10, // Fraction (fract) + flr = 0x11, // Floor + kil = 0x12, // Kill fragment + pk4 = 0x13, // Pack four signed 8-bit values + up4 = 0x14, // Unpack four signed 8-bit values + ddx = 0x15, // Partial-derivative in x (Screen space derivative w.r.t. x) + ddy = 0x16, // Partial-derivative in y (Screen space derivative w.r.t. y) + tex = 0x17, // Texture lookup + txp = 0x18, // Texture sample with projection (Projective texture lookup) + txd = 0x19, // Texture sample with partial differentiation (Texture lookup with derivatives) + rcp = 0x1A, // Reciprocal + rsq = 0x1B, // Reciprocal Square Root + ex2 = 0x1C, // Exponentiation base 2 + lg2 = 0x1D, // Log base 2 + lit = 0x1E, // Lighting coefficients + lrp = 0x1F, // Linear Interpolation + str = 0x20, // Set-If-True + sfl = 0x21, // Set-If-False + cos = 0x22, // Cosine + sin = 0x23, // Sine + pk2 = 0x24, // Pack two 16-bit floats + up2 = 0x25, // Unpack two 16-bit floats + pow = 0x26, // Power + pkb = 0x27, // Pack bytes + upb = 0x28, // Unpack bytes + pk16 = 0x29, // Pack 16 bits + up16 = 0x2A, // Unpack 16 + bem = 0x2B, // Bump-environment map (a.k.a. 2D coordinate transform) + pkg = 0x2C, // Pack with sRGB transformation + upg = 0x2D, // Unpack gamma + dp2a = 0x2E, // 2-component dot product with scalar addition + txl= 0x2F, // Texture sample with explicit LOD + txb = 0x31, // Texture sample with bias + texbem = 0x33, + txpbem = 0x34, + bemlum = 0x35, + refl = 0x36, // Reflection vector + timeswtex = 0x37, + dp2 = 0x38, // 2-component dot product + nrm = 0x39, // Normalize + div = 0x3A, // Division + divsq = 0x3B, // Divide by Square Root + lif = 0x3C, // Final part of LIT + fenct = 0x3D, // Fence T? + fencb = 0x3E, // Fence B? - BRK = 0x40, // Break - CAL = 0x41, // Subroutine call - IFE = 0x42, // If - LOOP = 0x43, // Loop - REP = 0x44, // Repeat - RET = 0x45 // Return + brk = 0x40, // Break + cal = 0x41, // Subroutine call + ife = 0x42, // If + loop = 0x43, // Loop + rep = 0x44, // Repeat + ret = 0x45 // Return }; inline opcode operator |(opcode lhs, u32 rhs) diff --git a/rsx_decompiler/rsx_vp_decompiler.cpp b/rsx_decompiler/rsx_vp_decompiler.cpp new file mode 100644 index 0000000..12a17d5 --- /dev/null +++ b/rsx_decompiler/rsx_vp_decompiler.cpp @@ -0,0 +1,27 @@ +#include "rsx_decompiler.h" + +namespace rsx +{ + namespace vertex_program + { + decompiled_program decompile(std::size_t offset, ucode_instr* instructions, decompile_language lang) + { + decompiled_program result; + + switch (lang) + { + case decompile_language::glsl: + //result = ...; + + result.code_language = decompile_language::glsl; + break; + + default: + throw; + } + + result.type = program_type::vertex; + return result; + } + } +} \ No newline at end of file diff --git a/rsx_decompiler/rsx_vp_ucode.cpp b/rsx_decompiler/rsx_vp_ucode.cpp new file mode 100644 index 0000000..9fb3a36 --- /dev/null +++ b/rsx_decompiler/rsx_vp_ucode.cpp @@ -0,0 +1,22 @@ +#include "rsx_vp_ucode.h" + +namespace rsx +{ + namespace vertex_program + { + const std::string rsx_vp_sca_op_names[0x20] = + { + "NOP", "MOV", "RCP", "RCC", "RSQ", "EXP", "LOG", + "LIT", "BRA", "BRI", "CAL", "CLI", "RET", "LG2", + "EX2", "SIN", "COS", "BRB", "CLB", "PSH", "POP" + }; + + const std::string rsx_vp_vec_op_names[0x20] = + { + "NOP", "MOV", "MUL", "ADD", "MAD", "DP3", "DPH", "DP4", + "DST", "MIN", "MAX", "SLT", "SGE", "ARL", "FRC", "FLR", + "SEQ", "SFL", "SGT", "SLE", "SNE", "STR", "SSG", nullptr, + nullptr, "TXL" + }; + } +} \ No newline at end of file diff --git a/rsx_decompiler/rsx_vp_ucode.h b/rsx_decompiler/rsx_vp_ucode.h new file mode 100644 index 0000000..6a4b261 --- /dev/null +++ b/rsx_decompiler/rsx_vp_ucode.h @@ -0,0 +1,197 @@ +#pragma once +#include +#include + +namespace rsx +{ + namespace vertex_program + { + using u32 = std::uint32_t; + enum class sca_opcode + { + nop = 0x00, + mov = 0x01, + rcp = 0x02, + rcc = 0x03, + rsq = 0x04, + exp = 0x05, + log = 0x06, + lit = 0x07, + bra = 0x08, + bri = 0x09, + cal = 0x0a, + cli = 0x0b, + ret = 0x0c, + lg2 = 0x0d, + ex2 = 0x0e, + sin = 0x0f, + cos = 0x10, + brb = 0x11, + clb = 0x12, + psh = 0x13, + pop = 0x14 + }; + + enum class vec_opcode + { + nop = 0x00, + mov = 0x01, + mul = 0x02, + add = 0x03, + mad = 0x04, + dp3 = 0x05, + dph = 0x06, + dp4 = 0x07, + dst = 0x08, + min = 0x09, + max = 0x0a, + slt = 0x0b, + sge = 0x0c, + arl = 0x0d, + frc = 0x0e, + flr = 0x0f, + seq = 0x10, + sfl = 0x11, + sgt = 0x12, + sle = 0x13, + sne = 0x14, + str = 0x15, + ssg = 0x16, + txl = 0x19 + }; + + union D0 + { + u32 HEX; + + struct + { + u32 addr_swz : 2; + u32 mask_w : 2; + u32 mask_z : 2; + u32 mask_y : 2; + u32 mask_x : 2; + u32 cond : 3; + u32 cond_test_enable : 1; + u32 cond_update_enable_0 : 1; + u32 dst_tmp : 6; + u32 src0_abs : 1; + u32 src1_abs : 1; + u32 src2_abs : 1; + u32 addr_reg_sel_1 : 1; + u32 cond_reg_sel_1 : 1; + u32 staturate : 1; + u32 index_input : 1; + u32: 1; + u32 cond_update_enable_1 : 1; + u32 vec_result : 1; + u32: 1; + }; + }; + + union D1 + { + u32 HEX; + + struct + { + u32 src0h : 8; + u32 input_src : 4; + u32 const_src : 10; + vec_opcode vec_opcode : 5; + sca_opcode sca_opcode : 5; + }; + }; + + union D2 + { + u32 HEX; + + struct + { + u32 src2h : 6; + u32 src1 : 17; + u32 src0l : 9; + }; + struct + { + u32 iaddrh : 6; + u32: 26; + }; + }; + + union D3 + { + u32 HEX; + + struct + { + u32 end : 1; + u32 index_const : 1; + u32 dst : 5; + u32 sca_dst_tmp : 6; + u32 vec_writemask_w : 1; + u32 vec_writemask_z : 1; + u32 vec_writemask_y : 1; + u32 vec_writemask_x : 1; + u32 sca_writemask_w : 1; + u32 sca_writemask_z : 1; + u32 sca_writemask_y : 1; + u32 sca_writemask_x : 1; + u32 src2l : 11; + }; + struct + { + u32: 29; + u32 iaddrl : 3; + }; + }; + + union SRC + { + union + { + u32 HEX; + + struct + { + u32 src0l : 9; + u32 src0h : 8; + }; + + struct + { + u32 src1 : 17; + }; + + struct + { + u32 src2l : 11; + u32 src2h : 6; + }; + }; + + struct + { + u32 reg_type : 2; + u32 tmp_src : 6; + u32 swz_w : 2; + u32 swz_z : 2; + u32 swz_y : 2; + u32 swz_x : 2; + u32 neg : 1; + }; + }; + + struct ucode_instr + { + D0 d0; + D1 d1; + D2 d2; + D3 d3; + }; + + extern const std::string rsx_vp_sca_op_names[0x20]; + extern const std::string rsx_vp_vec_op_names[0x20]; + } +} \ No newline at end of file diff --git a/rsx_program_decompiler/rsx_program_decompiler.cpp b/rsx_program_decompiler/rsx_program_decompiler.cpp index 5a49fec..069af10 100644 --- a/rsx_program_decompiler/rsx_program_decompiler.cpp +++ b/rsx_program_decompiler/rsx_program_decompiler.cpp @@ -272,7 +272,7 @@ void test(const std::string &shader) auto glGetShaderInfoLog = (glGetShaderInfoLog_t)wglGetProcAddress("glGetShaderInfoLog"); const char *shader_text = shader.data(); - const GLint length = shader.length(); + const GLint length = (GLint)shader.length(); GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &shader_text, &length); @@ -325,7 +325,7 @@ int main(int argc, char** argv) std::vector file = load_file("tmp.fp.ucode"); rsx::fragment_program::ucode_instr *instructions = (rsx::fragment_program::ucode_instr *)file.data(); - rsx::decompiled_program program = rsx::fragment_program::decompile(0, instructions); + rsx::decompiled_program program = rsx::fragment_program::decompile(0, instructions, rsx::decompile_language::glsl); print_info(program); /*