mirror of
https://github.com/RPCS3/rsx_program_decompiler.git
synced 2026-01-31 01:25:19 +01:00
Separate fp::decompiler and decompiler_base
Added vp_ucode
This commit is contained in:
@@ -1 +0,0 @@
|
||||
#include "pch.h"
|
||||
@@ -1,2 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
69
rsx_decompiler/rsx_decompiler_base.h
Normal file
69
rsx_decompiler/rsx_decompiler_base.h
Normal 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
709
rsx_decompiler/rsx_fp_decompiler.cpp
Normal file
709
rsx_decompiler/rsx_fp_decompiler.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
27
rsx_decompiler/rsx_vp_decompiler.cpp
Normal file
27
rsx_decompiler/rsx_vp_decompiler.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
rsx_decompiler/rsx_vp_ucode.cpp
Normal file
22
rsx_decompiler/rsx_vp_ucode.cpp
Normal 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"
|
||||
};
|
||||
}
|
||||
}
|
||||
197
rsx_decompiler/rsx_vp_ucode.h
Normal file
197
rsx_decompiler/rsx_vp_ucode.h
Normal 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];
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user