Separate fp::decompiler and decompiler_base

Added vp_ucode
This commit is contained in:
DHrpcs3
2015-12-20 00:17:58 +02:00
parent f99047fc1f
commit 7a98f70db4
13 changed files with 1176 additions and 883 deletions

View File

@@ -1 +0,0 @@
#include "pch.h"

View File

@@ -1,2 +0,0 @@
#pragma once

View File

@@ -1,766 +1,54 @@
#include "pch.h"
#include "clike_builder.h"
#include <unordered_set>
#include <functional>
#include <memory>
#include <iostream>
#include <map>
#include "rsx_decompiler.h"
#include <unordered_map>
#include <string>
namespace rsx
{
static const std::string index_to_channel[4] = { "x", "y", "z", "w" };
static const std::unordered_map<char, int> 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<char, int> channel_to_index = { { 'x', 0 },{ 'y', 1 },{ 'z', 2 },{ 'w', 3 } };
const std::string mask = "xyzw";
template<typename Language>
struct decompiler_base : shader_code::clike_builder<Language>
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<type_class_t Type, int Count>
static expression_from<boolean_t<Count>> single_compare_function(compare_function function, expression_t<Type, Count> a, expression_t<Type, Count> 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<type_class_t Type, int Count>
static expression_from<boolean_t<Count>> vector_compare_function(compare_function function, expression_t<Type, Count> a, expression_t<Type, Count> 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<type_class_t Type, int Count>
static expression_from<boolean_t<Count>> custom_compare(compare_function function, int channel_count, expression_t<Type, Count> a, expression_t<Type, Count> 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<shader_code::glsl_language>
{
struct context_t
{
decompiled_program program;
bool is_next_is_constant;
u32 offset;
expression_from<float_point_t<4>> 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<float_point_t<4>> 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<float_point_t<4>> 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<float_point_t<4>> 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<typename SrcType>
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<float_point_t<4>> swizzle_as_dst(expression_from<float_point_t<4>> arg) const
{
std::string arg_mask;
for (char channel : destination_swizzle())
{
arg_mask += arg.mask[channel_to_index.at(channel)];
}
return expression_from<float_point_t<4>>(arg.text, arg_mask, arg.is_single, arg.base_count);
}
expression_from<float_point_t<4>> 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<float_point_t<4>> 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<float_point_t<4>> 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<float_point_t<4>> src(int index, bool is_swizzle_as_dst = false)
{
return instruction.src(context, index, is_swizzle_as_dst);
}
expression_from<float_point_t<4>> src_swizzled_as_dst(int index)
{
return src(index, instruction.data.dst.set_cond || !instruction.data.dst.no_dest);
}
expression_from<float_point_t<4>> modify_condition_register()
{
return context.condition(instruction.data.src0.cond_mod_reg_index);
}
expression_from<float_point_t<4>> 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<boolean_t<1>> 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<float_point_t<1>>(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<boolean_t<4>> compare(compare_function function, expression_from<float_point_t<4>> a, expression_from<float_point_t<4>> b)
{
return custom_compare(function, (int)instruction.destination_swizzle().size(), a, b);
}
expression_from<sampler2D_t> tex()
{
return{ "unk_tex" };
}
template<typename ExprType>
expression_from<void_t> 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<typename Type>
Type apply_instruction_modifiers(Type arg)
{
using float_t = expression_from<float_point_t<1>>;
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<float_point_t<4>>& 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<float_point_t<1>> zero(0.0f);
std::map<char, std::vector<std::pair<int, int>>> 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<float_point_t<4>>(arg.text, arg.mask, true, dest.mask.size()));
}
for (auto &entry : condition_map)
{
std::string src_swizzle;
std::string dst_swizzle;
for (std::pair<int, int> 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<float_point_t<4>> 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<boolean_t<1>>(operation, zero), expression);
}
}
}
else
{
expression_from<float_point_t<4>> 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<float_point_t<4>>(modify_cond.text, dest.mask) = src);
}
}
result += src;
}
return result;
}
writer_t set_dst(const expression_from<float_point_t<1>>& 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<float_point_t<4>>{ arg.to_string() }, flags | disable_swizzle_as_dst);
}
writer_t set_dst(const expression_from<boolean_t<4>>& 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<boolean_t<2>>{ arg.to_string() }).to_string(); break;
case 3: arg_string = float_point_t<3>::ctor(expression_from<boolean_t<3>>{ arg.to_string() }).to_string(); break;
case 4: arg_string = float_point_t<4>::ctor(expression_from<boolean_t<4>>{ arg.to_string() }).to_string(); break;
default:
throw;
}
return set_dst(expression_from<float_point_t<4>>{ 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<void_t>("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<void_t>("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<void_t>("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;
}
}

View File

@@ -1,7 +1,9 @@
#pragma once
#include "rsx_fp_ucode.h"
#include "rsx_vp_ucode.h"
#include <vector>
#include <unordered_set>
#include <unordered_map>
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<constant_info, hasher> constants;
std::unordered_set<texture_info, hasher> textures;
std::unordered_set<register_info, hasher> temporary_registers;
@@ -96,52 +112,21 @@ namespace rsx
std::string code;
};
extern const std::string index_to_channel[4];
extern const std::unordered_map<char, int> 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);
}
}

View File

@@ -142,7 +142,7 @@
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
</ClCompile>
@@ -157,7 +157,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
</ClCompile>
@@ -172,7 +172,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|arm'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
</ClCompile>
@@ -187,7 +187,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|arm'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
</ClCompile>
@@ -202,7 +202,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
</ClCompile>
@@ -217,7 +217,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<CompileAsWinRT>false</CompileAsWinRT>
<SDLCheck>true</SDLCheck>
</ClCompile>
@@ -233,19 +233,15 @@
<ItemGroup>
<ClInclude Include="cache.h" />
<ClInclude Include="rsx_decompiler.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="rsx_decompiler_base.h" />
<ClInclude Include="rsx_vp_ucode.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="rsx_decompiler.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="rsx_fp_ucode.cpp" />
<ClCompile Include="rsx_fp_decompiler.cpp" />
<ClCompile Include="rsx_vp_decompiler.cpp" />
<ClCompile Include="rsx_vp_ucode.cpp" />
<ClInclude Include="rsx_fp_ucode.h">
<FileType>CppCode</FileType>
</ClInclude>

View File

@@ -8,13 +8,16 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="rsx_decompiler.cpp" />
<ClCompile Include="pch.cpp" />
<ClCompile Include="rsx_fp_ucode.cpp" />
<ClCompile Include="rsx_fp_decompiler.cpp" />
<ClCompile Include="rsx_vp_decompiler.cpp" />
<ClCompile Include="rsx_vp_ucode.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="rsx_decompiler.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="cache.h" />
<ClInclude Include="rsx_fp_ucode.h" />
<ClInclude Include="rsx_vp_ucode.h" />
<ClInclude Include="rsx_decompiler_base.h" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,69 @@
#pragma once
#include "rsx_decompiler.h"
#include <clike_builder.h>
namespace rsx
{
template<typename Language>
struct decompiler_base : shader_code::clike_builder<Language>
{
enum class compare_function
{
less,
greater,
equal,
less_equal,
greater_equal,
not_equal
};
template<type_class_t Type, int Count>
static expression_from<boolean_t<Count>> single_compare_function(compare_function function, expression_t<Type, Count> a, expression_t<Type, Count> 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<type_class_t Type, int Count>
static expression_from<boolean_t<Count>> vector_compare_function(compare_function function, expression_t<Type, Count> a, expression_t<Type, Count> 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<type_class_t Type, int Count>
static expression_from<boolean_t<Count>> custom_compare(compare_function function, int channel_count, expression_t<Type, Count> a, expression_t<Type, Count> b)
{
if (channel_count == 1)
{
return single_compare_function(function, a, b);
}
return vector_compare_function(function, a, b);
}
};
}

View File

@@ -0,0 +1,709 @@
#include "rsx_decompiler_base.h"
#include "rsx_fp_ucode.h"
#include <glsl_language.h>
#include <map>
namespace rsx
{
namespace fragment_program
{
template<typename Language>
class decompiler : public decompiler_base<Language>
{
struct context_t
{
decompiled_program program;
bool is_next_is_constant;
u32 offset;
expression_from<float_point_t<4>> 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<float_point_t<4>> 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<float_point_t<4>> 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<float_point_t<4>> 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<typename SrcType>
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<float_point_t<4>> swizzle_as_dst(expression_from<float_point_t<4>> arg) const
{
std::string arg_mask;
for (char channel : destination_swizzle())
{
arg_mask += arg.mask[channel_to_index.at(channel)];
}
return expression_from<float_point_t<4>>(arg.text, arg_mask, arg.is_single, arg.base_count);
}
expression_from<float_point_t<4>> 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<float_point_t<4>> 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<float_point_t<4>> 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<float_point_t<4>> src(int index, bool is_swizzle_as_dst = false)
{
return instruction.src(context, index, is_swizzle_as_dst);
}
expression_from<float_point_t<4>> src_swizzled_as_dst(int index)
{
return src(index, instruction.data.dst.set_cond || !instruction.data.dst.no_dest);
}
expression_from<float_point_t<4>> modify_condition_register()
{
return context.condition(instruction.data.src0.cond_mod_reg_index);
}
expression_from<float_point_t<4>> 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<boolean_t<1>> 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<float_point_t<1>>(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<boolean_t<4>> compare(compare_function function, expression_from<float_point_t<4>> a, expression_from<float_point_t<4>> b)
{
return custom_compare(function, (int)instruction.destination_swizzle().size(), a, b);
}
expression_from<sampler2D_t> tex()
{
return{ "unk_tex" };
}
template<typename ExprType>
expression_from<void_t> 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<typename Type>
Type apply_instruction_modifiers(Type arg)
{
using float_t = expression_from<float_point_t<1>>;
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<float_point_t<4>>& 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<float_point_t<1>> zero(0.0f);
std::map<char, std::vector<std::pair<int, int>>> 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<float_point_t<4>>(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<int, int> 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<float_point_t<4>> 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<boolean_t<1>>(operation, zero), expression);
}
}
}
else
{
expression_from<float_point_t<4>> 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<float_point_t<4>>(modify_cond.text, dest.mask) = src);
}
}
result += src;
}
return result;
}
writer_t set_dst(const expression_from<float_point_t<1>>& 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<float_point_t<4>>{ arg.to_string() }, flags | disable_swizzle_as_dst);
}
writer_t set_dst(const expression_from<boolean_t<4>>& 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<boolean_t<2>>{ arg.to_string() }).to_string(); break;
case 3: arg_string = float_point_t<3>::ctor(expression_from<boolean_t<3>>{ arg.to_string() }).to_string(); break;
case 4: arg_string = float_point_t<4>::ctor(expression_from<boolean_t<4>>{ arg.to_string() }).to_string(); break;
default:
throw;
}
return set_dst(expression_from<float_point_t<4>>{ 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<void_t>("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<void_t>("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<void_t>("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<typename Language>
decompiled_program decompile(std::size_t offset, ucode_instr *instructions)
{
return decompiler<Language>{}.decompile(offset, (decompiler<Language>::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<shader_code::glsl_language>(offset, instructions);
result.code_language = decompile_language::glsl;
break;
default:
throw;
}
result.type = program_type::fragment;
return result;
}
}
}

View File

@@ -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)

View File

@@ -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;
}
}
}

View File

@@ -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"
};
}
}

View File

@@ -0,0 +1,197 @@
#pragma once
#include <cstdint>
#include <string>
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];
}
}

View File

@@ -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<char> 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);
/*