mirror of
https://github.com/RPCS3/rsx_program_decompiler.git
synced 2026-01-31 01:25:19 +01:00
1069 lines
29 KiB
C++
1069 lines
29 KiB
C++
#include "pch.h"
|
|
#include "clike_builder.h"
|
|
#include <unordered_set>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <iostream>
|
|
|
|
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 } };
|
|
|
|
template<typename Language>
|
|
struct decompiler_base : shader_code::clike_builder<Language>
|
|
{
|
|
};
|
|
}
|
|
|
|
#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>
|
|
{
|
|
enum class variable_modifier
|
|
{
|
|
none,
|
|
in,
|
|
out,
|
|
constant
|
|
};
|
|
|
|
struct context_t
|
|
{
|
|
struct variable_info
|
|
{
|
|
std::string type;
|
|
std::string name;
|
|
};
|
|
|
|
u32 offset;
|
|
bool is_next_is_constant;
|
|
|
|
std::vector<u32> constants_offsets;
|
|
std::unordered_map<variable_modifier, variable_info> variables;
|
|
|
|
template<typename Type = float_point_t<4>>
|
|
expression_from<Type> variable(const std::string& name, variable_modifier modifier = variable_modifier::none)
|
|
{
|
|
variables[modifier] = { Type::name(), name };
|
|
|
|
return{ name };
|
|
}
|
|
|
|
expression_from<float_point_t<4>> constant()
|
|
{
|
|
return variable("fc" + std::to_string(offset + sizeof(instruction_t)));
|
|
}
|
|
|
|
expression_from<float_point_t<4>> temporary(bool is_fp16, int index)
|
|
{
|
|
return variable((is_fp16 ? "h" : "r") + std::to_string(index));
|
|
}
|
|
|
|
expression_from<float_point_t<4>> input(int index)
|
|
{
|
|
return variable(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:
|
|
context.is_next_is_constant = true;
|
|
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;
|
|
}
|
|
|
|
|
|
expression_from<float_point_t<4>> output(context_t& context) const
|
|
{
|
|
return{ "unk_output" };
|
|
}
|
|
|
|
std::string destination_swizzle() const
|
|
{
|
|
std::string swizzle;
|
|
static const std::string mask = "xyzw";
|
|
|
|
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<boolean_t<4>> modify_condition()
|
|
{
|
|
return{ "unk_modify_condition" };
|
|
}
|
|
|
|
expression_from<boolean_t<4>> execution_condition()
|
|
{
|
|
return{ "unk_execution_condition" };
|
|
}
|
|
|
|
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()), 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;
|
|
}
|
|
|
|
enum class compare_function
|
|
{
|
|
less,
|
|
greater,
|
|
equal,
|
|
less_equal,
|
|
greater_equal,
|
|
not_equal
|
|
};
|
|
|
|
static expression_from<boolean_t<4>> single_compare_function(compare_function function, expression_from<float_point_t<4>> a, expression_from<float_point_t<4>> b)
|
|
{
|
|
std::string operator_string;
|
|
|
|
switch (function)
|
|
{
|
|
case compare_function::less: operator_string = "<"; break;
|
|
case compare_function::greater: operator_string = ">"; break;
|
|
case compare_function::equal: operator_string = "=="; break;
|
|
case compare_function::less_equal: operator_string = "<="; break;
|
|
case compare_function::greater_equal: operator_string = ">="; break;
|
|
case compare_function::not_equal: operator_string = "!="; break;
|
|
|
|
default:
|
|
throw;
|
|
}
|
|
|
|
return a.to_string() + " " + operator_string + " " + b.to_string();
|
|
}
|
|
|
|
static expression_from<boolean_t<4>> vector_compare_function(compare_function function, expression_from<float_point_t<4>> a, expression_from<float_point_t<4>> b)
|
|
{
|
|
switch (function)
|
|
{
|
|
case compare_function::less: return less(a, b);
|
|
case compare_function::greater: return greater(a, b);
|
|
case compare_function::equal: return equal(a, b);
|
|
case compare_function::less_equal: return less_equal(a, b);
|
|
case compare_function::greater_equal: return greater_equal(a, b);
|
|
case compare_function::not_equal: return not_equal(a, b);
|
|
}
|
|
|
|
throw;
|
|
}
|
|
|
|
static expression_from<boolean_t<4>> compare(compare_function function, int channel_count, expression_from<float_point_t<4>> a, expression_from<float_point_t<4>> b)
|
|
{
|
|
if (channel_count == 1)
|
|
{
|
|
return single_compare_function(function, a, b);
|
|
}
|
|
|
|
return vector_compare_function(function, a, b);
|
|
}
|
|
|
|
expression_from<boolean_t<4>> compare(compare_function function, expression_from<float_point_t<4>> a, expression_from<float_point_t<4>> b)
|
|
{
|
|
return compare(function, instruction.destination_swizzle().size(), a, b);
|
|
}
|
|
|
|
writer_t set_dst(const expression_from<float_point_t<4>>& arg, u32 flags = none)
|
|
{
|
|
writer_t result;
|
|
|
|
auto modify_cond = modify_condition();
|
|
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();
|
|
|
|
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 += "=";
|
|
}
|
|
|
|
static const expression_from<float_point_t<1>> zero(0.0f);
|
|
|
|
auto set_channel = [&](int dest_swizzle, int src_swizzle)
|
|
{
|
|
auto src = expression_from<float_point_t<1>>{ arg.text, arg.mask, arg.is_single, arg.base_count };
|
|
|
|
if ((flags & disable_swizzle_as_dst) != 0 && dest.mask.size() == 1)
|
|
{
|
|
src.assign(src.without_scope());
|
|
}
|
|
else
|
|
{
|
|
src.assign(src.swizzle(src_swizzle));
|
|
}
|
|
|
|
src = apply_instruction_modifiers(src);
|
|
|
|
if (instruction.data.dst.set_cond)
|
|
{
|
|
if (instruction.data.dst.no_dest)
|
|
{
|
|
result += if_(cond.swizzle(src_swizzle).call_operator<boolean_t<1>>(operation, zero),
|
|
modify_cond.swizzle(src_swizzle) = boolean_t<1>::ctor(src));
|
|
}
|
|
else
|
|
{
|
|
result += if_(cond.swizzle(src_swizzle).call_operator(operation, zero),
|
|
modify_cond.swizzle(src_swizzle) = boolean_t<1>::ctor(dest.swizzle(dest_swizzle) = src));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result += if_(cond.swizzle(src_swizzle).call_operator(operation, zero), dest.swizzle(dest_swizzle) = src);
|
|
}
|
|
};
|
|
|
|
if (!instruction.data.dst.set_cond && instruction.data.dst.no_dest)
|
|
{
|
|
//condition must be already handled in instruction semantic (IFE, LOOP, etc)
|
|
result += comment("WARNING: extra condition test skipped");
|
|
result += arg;
|
|
}
|
|
else
|
|
{
|
|
if (flags & disable_swizzle_as_dst)
|
|
{
|
|
for (int i = 0; i < dest.mask.size(); ++i)
|
|
set_channel(i, i);
|
|
}
|
|
else
|
|
{
|
|
int dest_swizzle = 0;
|
|
if (instruction.data.dst.mask_x) set_channel(dest_swizzle++, 0);
|
|
if (instruction.data.dst.mask_y) set_channel(dest_swizzle++, 1);
|
|
if (instruction.data.dst.mask_z) set_channel(dest_swizzle++, 2);
|
|
if (instruction.data.dst.mask_w) set_channel(dest_swizzle, 3);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
|
|
switch (instruction.destination_swizzle().size())
|
|
{
|
|
case 1: arg_string = float_point_t<1>::ctor(expression_from<boolean_t<1>>{ arg.to_string() }).to_string(); 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"), true, 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(0));
|
|
case opcode::MUL: return set_dst(src_swizzled_as_dst(0) * src_swizzled_as_dst(1), disable_swizzle_as_dst);
|
|
case opcode::ADD: return set_dst(src_swizzled_as_dst(0) + src_swizzled_as_dst(1), disable_swizzle_as_dst);
|
|
case opcode::MAD: return set_dst((src_swizzled_as_dst(0) * src_swizzled_as_dst(1)).without_scope() + src_swizzled_as_dst(2), disable_swizzle_as_dst);
|
|
case opcode::DP3: return set_dst(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 (" + all(execution_condition()).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;
|
|
}
|
|
|
|
void 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;
|
|
}
|
|
}
|
|
};
|
|
|
|
decompiled_program decompile(std::size_t offset, ucode_instr *instructions)
|
|
{
|
|
decompiler dec;
|
|
dec.decompile(offset, (decompiler::instruction_t*)instructions);
|
|
|
|
decompiled_program result{};
|
|
|
|
//result.constant_offsets = ...;
|
|
//result.uniforms = ...;
|
|
//result.textures = ...;
|
|
//result.temporary_registers = ...;
|
|
//result.input_attributes = ...;
|
|
//result.output_attributes = ...;
|
|
|
|
result.code = dec.writer.build();
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
struct vp_shader : shader_code::clike_builder<shader_code::glsl_language>
|
|
{
|
|
writer_t writer;
|
|
|
|
struct variable_info
|
|
{
|
|
std::string type;
|
|
std::string name;
|
|
|
|
bool operator ==(const variable_info& rhs) const
|
|
{
|
|
return type == rhs.type && name == rhs.name;
|
|
}
|
|
|
|
struct hash
|
|
{
|
|
std::size_t operator()(const variable_info& arg) const
|
|
{
|
|
return
|
|
std::hash<std::string>()(arg.type) ^
|
|
(std::hash<std::string>()(arg.name) - 1);
|
|
}
|
|
};
|
|
};
|
|
|
|
std::unordered_set<variable_info, variable_info::hash> uniforms;
|
|
|
|
template<typename Type>
|
|
expression_from<Type> make_uniform(const std::string& name, expression_from<Type> initializer)
|
|
{
|
|
return{ name };
|
|
}
|
|
|
|
template<typename Type>
|
|
expression_from<Type> make_uniform(const std::string& name)
|
|
{
|
|
return{ name };
|
|
}
|
|
|
|
struct else_t : expression_base_t
|
|
{
|
|
|
|
};
|
|
|
|
struct if_t : expression_from<void_t>
|
|
{
|
|
if_t(expression_from<boolean_t<1>> condition, std::function<void()> body = nullptr)
|
|
: expression_t("if (" + condition.to_string() + ")")
|
|
{
|
|
}
|
|
|
|
std::string finalize() const override
|
|
{
|
|
return to_string();
|
|
}
|
|
};
|
|
|
|
vp_shader()
|
|
{
|
|
auto A = make_uniform<boolean_t<4>>("A", boolean_t<4>::ctor(true));
|
|
auto B = make_uniform<float_point_t<4>>("B", float_point_t<4>::ctor(0.f));
|
|
auto C = make_uniform<float_point_t<4>>("C");
|
|
auto K = make_uniform<float_point_t<2>>("K");
|
|
auto D = make_uniform<sampler2D_t>("D");
|
|
|
|
writer.lines(
|
|
texture(D).xyzw(),
|
|
float_point_t<4>::ctor(1),
|
|
float_point_t<1>::ctor(1.f) / float_point_t<1>::ctor(),
|
|
if_t(A.x()),
|
|
A.x() = !(K.xy().y() != C.x()),
|
|
K.xy(),
|
|
A.y() = B.x() != C.x()
|
|
);
|
|
}
|
|
};
|
|
|
|
#include <unordered_map>
|
|
|
|
struct rsx_fragment_shader
|
|
{
|
|
std::vector<unsigned int> data;
|
|
|
|
struct hash_t
|
|
{
|
|
std::size_t operator ()(const rsx_fragment_shader& arg) const
|
|
{
|
|
return 0;
|
|
//return arg.hash;
|
|
}
|
|
};
|
|
|
|
bool operator ==(const rsx_fragment_shader& rhs) const
|
|
{
|
|
if (data.size() != rhs.data.size())
|
|
return false;
|
|
|
|
for (std::size_t i = 0; i < data.size(); ++i)
|
|
{
|
|
if (data[i] != rhs.data[i])
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
struct decompiled_rsx_fragment_shader
|
|
{
|
|
std::vector<std::size_t> constant_offsets;
|
|
std::vector<std::string> uniforms;
|
|
int input_attributes;
|
|
int output_attributes;
|
|
|
|
std::string code;
|
|
};
|
|
|
|
struct glsl_shader_t
|
|
{
|
|
int id;
|
|
};
|
|
|
|
template<typename CompiledType>
|
|
struct finalized_rsx_fragment_shader
|
|
{
|
|
int input_attributes;
|
|
int output_attributes;
|
|
int control;
|
|
|
|
std::string code;
|
|
CompiledType shader;
|
|
};
|
|
|
|
template<typename Type>
|
|
struct rsx_program
|
|
{
|
|
std::shared_ptr<finalized_rsx_fragment_shader<glsl_shader_t>> fragment_shader;
|
|
std::shared_ptr<finalized_rsx_fragment_shader<glsl_shader_t>> vertex_shader;
|
|
};
|
|
|
|
/*
|
|
|
|
namespace fmt
|
|
{
|
|
bool mask_test(const std::string &source, const std::string &mask, std::size_t source_offset = 0, std::size_t mask_offset = 0)
|
|
{
|
|
while (char sym = mask[mask_offset])
|
|
{
|
|
if (source[source_offset] == '\0')
|
|
return false;
|
|
|
|
switch (sym)
|
|
{
|
|
case '*':
|
|
while (source[source_offset])
|
|
{
|
|
if (mask_test(source, mask, source_offset, mask_offset + 1))
|
|
return true;
|
|
|
|
++source_offset;
|
|
}
|
|
|
|
return false;
|
|
|
|
case '\\':
|
|
sym = mask[++mask_offset];
|
|
|
|
default:
|
|
if (sym != source[source_offset])
|
|
return false;
|
|
|
|
case '?':
|
|
++source_offset; ++mask_offset;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return source[source_offset] == '\0';
|
|
}
|
|
}
|
|
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
|
|
namespace fs = std::tr2::sys;
|
|
|
|
void _load_fragment_cache_file(const fs::path& data_path, const fs::path &source_path)
|
|
{
|
|
rsx_fragment_shader shader;
|
|
{
|
|
std::ifstream data_f(data_path, std::ios::ate | std::ios::binary);
|
|
if (!data_f)
|
|
return;
|
|
|
|
shader.data.resize(data_f.tellg() / sizeof(rsx_fragment_shader::data[0]));
|
|
data_f.seekg(0, data_f.beg);
|
|
|
|
data_f.read((char*)shader.data.data(), shader.data.size() * sizeof(rsx_fragment_shader::data[0]));
|
|
}
|
|
|
|
cache_t<rsx_fragment_shader, decompiled_rsx_fragment_shader> cache;
|
|
|
|
decompiled_rsx_fragment_shader &decompiled_shader = cache.entry(shader);
|
|
|
|
{
|
|
std::ifstream data_f(data_path, std::ios::ate);
|
|
if (!data_f)
|
|
return;
|
|
|
|
data_f
|
|
>> decompiled_shader.input_attributes
|
|
>> decompiled_shader.output_attributes;
|
|
|
|
|
|
int constants_count;
|
|
data_f >> constants_count;
|
|
|
|
if (constants_count)
|
|
{
|
|
decompiled_shader.constant_offsets.resize(constants_count);
|
|
|
|
for (auto &offset : decompiled_shader.constant_offsets)
|
|
{
|
|
data_f >> offset;
|
|
}
|
|
}
|
|
|
|
int uniforms_count;
|
|
data_f >> uniforms_count;
|
|
|
|
if (uniforms_count)
|
|
{
|
|
decompiled_shader.uniforms.resize(uniforms_count);
|
|
|
|
for (auto &uniform : decompiled_shader.uniforms)
|
|
{
|
|
data_f >> uniform;
|
|
}
|
|
}
|
|
|
|
auto from_pos = data_f.tellg();
|
|
data_f.seekg(0, data_f.end);
|
|
auto end_pos = data_f.tellg();
|
|
|
|
decompiled_shader.code.resize(end_pos - from_pos);
|
|
data_f.seekg(from_pos);
|
|
data_f.read((char*)decompiled_shader.code.data(), decompiled_shader.code.size());
|
|
}
|
|
}
|
|
|
|
void _save_fragment_cache_file(const fs::path& data_path, const fs::path &source_path, std::pair<const rsx_fragment_shader&, decompiled_rsx_fragment_shader&> data)
|
|
{
|
|
rsx_fragment_shader shader;
|
|
{
|
|
std::ifstream data_f(data_path, std::ios::ate | std::ios::binary);
|
|
if (!data_f)
|
|
return;
|
|
|
|
shader.data.resize(data_f.tellg() / sizeof(rsx_fragment_shader::data[0]));
|
|
data_f.seekg(0, data_f.beg);
|
|
|
|
data_f.read((char*)shader.data.data(), shader.data.size() * sizeof(rsx_fragment_shader::data[0]));
|
|
}
|
|
|
|
cache_t<rsx_fragment_shader, decompiled_rsx_fragment_shader> cache;
|
|
|
|
decompiled_rsx_fragment_shader &decompiled_shader = cache.entry(shader);
|
|
|
|
{
|
|
std::ifstream data_f(data_path, std::ios::ate);
|
|
if (!data_f)
|
|
return;
|
|
|
|
data_f
|
|
>> decompiled_shader.input_attributes
|
|
>> decompiled_shader.output_attributes;
|
|
|
|
|
|
int constants_count;
|
|
data_f >> constants_count;
|
|
|
|
if (constants_count)
|
|
{
|
|
decompiled_shader.constant_offsets.resize(constants_count);
|
|
|
|
for (auto &offset : decompiled_shader.constant_offsets)
|
|
{
|
|
data_f >> offset;
|
|
}
|
|
}
|
|
|
|
int uniforms_count;
|
|
data_f >> uniforms_count;
|
|
|
|
if (uniforms_count)
|
|
{
|
|
decompiled_shader.uniforms.resize(uniforms_count);
|
|
|
|
for (auto &uniform : decompiled_shader.uniforms)
|
|
{
|
|
data_f >> uniform;
|
|
}
|
|
}
|
|
|
|
auto from_pos = data_f.tellg();
|
|
data_f.seekg(0, data_f.end);
|
|
auto end_pos = data_f.tellg();
|
|
|
|
decompiled_shader.code.resize(end_pos - from_pos);
|
|
data_f.seekg(from_pos);
|
|
data_f.read((char*)decompiled_shader.code.data(), decompiled_shader.code.size());
|
|
}
|
|
}
|
|
|
|
|
|
void _load_shader_cache(const std::string &cache_path)
|
|
{
|
|
if (!fs::exists(cache_path))
|
|
{
|
|
if (!fs::create_directories(cache_path))
|
|
return;
|
|
}
|
|
|
|
for (auto entry : fs::directory_iterator{ fs::path(cache_path).parent_path() })
|
|
{
|
|
if (entry.status().type() != fs::file_type::regular)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (fmt::mask_test(entry.path().filename().string(), "*.vp.data"))
|
|
{
|
|
fs::path(entry.path()).replace_extension("glsl");
|
|
}
|
|
|
|
if (fmt::mask_test(entry.path().filename().string(), "*.fp.data"))
|
|
{
|
|
_load_fragment_cache_file(entry.path(), fs::path(entry.path()).replace_extension("glsl").string());
|
|
}
|
|
}
|
|
}
|
|
|
|
void load_shader_cache(const std::string &game_id)
|
|
{
|
|
_load_shader_cache("./data/cache/rsx/");
|
|
|
|
if (!game_id.empty())
|
|
_load_shader_cache("./data/" + game_id + "/cache/rsx/");
|
|
|
|
cache_t<rsx_fragment_shader, decompiled_rsx_fragment_shader> cache;
|
|
cache.each([](std::pair<const rsx_fragment_shader&, decompiled_rsx_fragment_shader&> elem)
|
|
{
|
|
elem.first.data;
|
|
});
|
|
}
|
|
*/
|