Implemented vertex program decompiler

This commit is contained in:
DHrpcs3
2015-12-20 08:59:49 +02:00
parent 6b988b54f5
commit 46dd50682e
9 changed files with 691 additions and 32 deletions

View File

@@ -21,7 +21,23 @@ namespace rsx
for (const register_info& temporary : program.temporary_registers)
{
result.code += "vec4 " + temporary.name + " = vec4(0.0);\n";
std::string type;
switch (temporary.type)
{
case register_type::half_float_point:
case register_type::single_float_point:
type += "vec4";
break;
case register_type::integer:
type += "ivec4";
break;
default:
throw;
}
result.code += type + " " + temporary.name + " = " + type + "(0);\n";
}
result.code += "\n";
@@ -31,12 +47,30 @@ namespace rsx
result.code += "uniform sampler2D " + texture.name + ";\n";
}
for (std::size_t index = 0; index < std::size(rsx::fragment_program::input_attrib_map); ++index)
switch (program.type)
{
if (program.input_attributes & (1 << index))
case program_type::fragment:
for (std::size_t index = 0; index < std::size(rsx::fragment_program::input_attrib_map); ++index)
{
result.code += "in vec4 " + rsx::fragment_program::input_attrib_map[index] + ";\n";
if (program.input_attributes & (1 << index))
{
result.code += "in vec4 " + rsx::fragment_program::input_attrib_map[index] + ";\n";
}
}
break;
case program_type::vertex:
for (std::size_t index = 0; index < std::size(rsx::vertex_program::input_registers_table); ++index)
{
if (program.input_attributes & (1 << index))
{
result.code += "in vec4 " + rsx::vertex_program::input_registers_table[index] + ";\n";
}
}
break;
default:
throw;
}
result.code += "\n";

View File

@@ -267,8 +267,6 @@ namespace rsx
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;
@@ -279,6 +277,8 @@ namespace rsx
return false;
}
auto cond = execution_condition_register();
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)
@@ -417,7 +417,7 @@ namespace rsx
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 += warning("extra condition test skipped");
result += arg;
}
else
@@ -432,7 +432,7 @@ namespace rsx
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;
auto src = apply_instruction_modifiers(arg);
if (flags & disable_swizzle_as_dst)
{

View File

@@ -1,6 +1,7 @@
#include "rsx_decompiler.h"
#include <glsl_language.h>
#include "rsx_decompiler_base.h"
#include <map>
namespace rsx
{
@@ -11,6 +12,72 @@ namespace rsx
struct context_t
{
decompiled_program program;
std::string address_register(u32 address_register)
{
register_info info;
info.id = address_register;
info.name = "a" + std::to_string(info.id);
info.type = register_type::integer;
program.temporary_registers.insert(info);
return info.name;
}
std::string address_mask(u32 swizzle)
{
return std::string(1, mask[swizzle]);
}
float_point_expr<4> constant(u32 id, u32 index_constant, u32 address_register_, u32 swizzle)
{
constant_info info;
info.id = 468;
info.name = "vc[468]";
program.constants.insert(info);
return "vc[" + std::to_string(id) +
(index_constant ? " + " + address_register(address_register_) + "." + address_mask(swizzle) : "")
+ "]";
}
float_point_expr<4> output(u32 index)
{
program.output_attributes |= (1 << index);
register_info info;
info.id = index;
info.type = register_type::single_float_point;
info.name = "o" + std::to_string(index);
program.temporary_registers.insert(info);
return info.name;
}
float_point_expr<4> temporary(u32 index)
{
register_info info;
info.id = index;
info.type = register_type::single_float_point;
info.name = "r" + std::to_string(index);
program.temporary_registers.insert(info);
return info.name;
}
float_point_expr<4> condition(int index)
{
register_info info;
info.id = index;
info.type = register_type::single_float_point;
info.name = "rc" + std::to_string(index);
program.temporary_registers.insert(info);
return info.name;
}
float_point_expr<4> input(int index)
{
program.input_attributes |= (1 << index);
return input_registers_table[index];
}
};
public:
@@ -18,6 +85,33 @@ namespace rsx
{
ucode_instr data;
SRC unpack_src(int index) const
{
SRC result;
switch (index)
{
case 0:
result.src0l = data.d2.src0l;
result.src0h = data.d1.src0h;
break;
case 1:
result.src1 = data.d2.src1;
break;
case 2:
result.src2l = data.d3.src2l;
result.src2h = data.d2.src2h;
break;
default:
throw;
}
return result;
}
static u32 swap_endianess(u32 data)
{
return
@@ -27,7 +121,7 @@ namespace rsx
((data << 24) & 0xff000000);
}
instruction_t unpack()
instruction_t unpack() const
{
instruction_t result;
@@ -42,31 +136,511 @@ namespace rsx
static_assert(sizeof(instruction_t) == 16, "Bad instruction_t implementation");
private:
instruction_t instruction;
context_t context;
bool is_vec;
u32 temporary_destination_register_index() const
{
if (is_vec)
{
return instruction.data.d0.dst_tmp;
}
return instruction.data.d3.sca_dst_tmp;
}
u32 address_value() const
{
return (instruction.data.d2.iaddrh << 3) | instruction.data.d3.iaddrl;
}
integer_expr<1> address_register()
{
return{ context.address_register(instruction.data.d0.addr_reg_sel_1), context.address_mask(instruction.data.d0.addr_swz), true, 4 };
}
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);
}
float_point_expr<4> src(int index, bool is_swizzle_as_dst = false)
{
SRC src = instruction.unpack_src(index);
u32 is_abs;
switch (index)
{
case 0: is_abs = instruction.data.d0.src0_abs; break;
case 1: is_abs = instruction.data.d0.src1_abs; break;
case 2: is_abs = instruction.data.d0.src2_abs; break;
}
auto get_register = [&]()
{
switch (src.register_type)
{
case src_register_type::temporary:
return context.temporary(src.tmp_src);
case src_register_type::input:
return context.input(instruction.data.d1.input_src);
case src_register_type::constant:
return context.constant(instruction.data.d1.const_src,
instruction.data.d3.index_const,
instruction.data.d0.addr_reg_sel_1,
instruction.data.d0.addr_swz);
}
throw;
};
float_point_expr<4> result = get_register();
result.assign(result.swizzle(src.swz_x, src.swz_y, src.swz_z, src.swz_w));
if (is_swizzle_as_dst)
{
result.assign(swizzle_as_dst(result));
}
if (is_abs)
{
result.assign(abs(result));
}
if (src.neg)
{
result.assign(-result);
}
return result;
}
float_point_expr<4> src_swizzled_as_dst(int index)
{
return src(index, true);
}
std::string destination_swizzle() const
{
std::string result;
if (is_vec)
{
if (instruction.data.d3.vec_writemask_x) result += "x";
if (instruction.data.d3.vec_writemask_y) result += "y";
if (instruction.data.d3.vec_writemask_z) result += "z";
if (instruction.data.d3.vec_writemask_w) result += "w";
}
else
{
if (instruction.data.d3.sca_writemask_x) result += "x";
if (instruction.data.d3.sca_writemask_y) result += "y";
if (instruction.data.d3.sca_writemask_z) result += "z";
if (instruction.data.d3.sca_writemask_w) result += "w";
}
return result;
}
std::pair<bool, float_point_expr<4>> destination_register()
{
if (is_vec && instruction.data.d0.vec_result)
{
return{ true, { context.output(instruction.data.d3.dst).text, destination_swizzle() } };
}
u32 tmp = temporary_destination_register_index();
bool no_dest = tmp == 63;
if (no_dest)
{
return{ false, {} };
}
return{ true,{ context.temporary(tmp).text, destination_swizzle() } };
}
float_point_expr<4> update_condition_register()
{
return{ context.condition(instruction.data.d0.cond_reg_sel_1).text, destination_swizzle() };
}
float_point_expr<4> execution_condition_register()
{
std::string swizzle;
swizzle += mask[instruction.data.d0.mask_x];
swizzle += mask[instruction.data.d0.mask_y];
swizzle += mask[instruction.data.d0.mask_z];
swizzle += mask[instruction.data.d0.mask_w];
return{ context.condition(instruction.data.d0.cond_reg_sel_1).text, swizzle };
}
boolean_expr<4> compare(compare_function function, float_point_expr<4> a, float_point_expr<4> b)
{
return custom_compare(function, (int)destination_swizzle().size(), a, b);
}
float_point_expr<4> apply_instruction_modifiers(float_point_expr<4> arg)
{
if (instruction.data.d0.staturate)
{
return clamp(arg, -1.0f, 1.0f);
}
return arg;
}
enum
{
lt = 0x1,
eq = 0x2,
gt = 0x4,
always = (lt | eq | gt),
never = 0
};
writer_t set_dst(float_point_expr<4> arg)
{
auto dst_pair = destination_register();
bool has_dst = dst_pair.first;
bool has_result = has_dst || (instruction.data.d0.cond_update_enable_0 && instruction.data.d0.cond_update_enable_1);
float_point_expr<4> dest{ dst_pair.second };
writer_t result;
if (instruction.data.d0.cond_test_enable && instruction.data.d0.cond != always)
{
if (!has_result)
{
result += warning("Extra condition skipped");
result += arg;
}
else
{
static const std::string operations[0x8] =
{
"error",
"<",
"==",
"<=",
">",
"!=",
">=",
"error"
};
auto cond = execution_condition_register();
std::string operation = operations[instruction.data.d0.cond];
static const float_point_expr<1> zero(0.0f);
std::map<char, std::vector<std::pair<int, int>>> condition_map;
int channel_index = 0;
if (is_vec)
{
if (instruction.data.d3.vec_writemask_x) condition_map[cond.mask[0]].push_back({ 0, channel_index++ });
if (instruction.data.d3.vec_writemask_y) condition_map[cond.mask[0]].push_back({ 1, channel_index++ });
if (instruction.data.d3.vec_writemask_z) condition_map[cond.mask[0]].push_back({ 2, channel_index++ });
if (instruction.data.d3.vec_writemask_w) condition_map[cond.mask[0]].push_back({ 3, channel_index });
}
else
{
if (instruction.data.d3.sca_writemask_x) condition_map[cond.mask[0]].push_back({ 0, channel_index++ });
if (instruction.data.d3.sca_writemask_y) condition_map[cond.mask[0]].push_back({ 1, channel_index++ });
if (instruction.data.d3.sca_writemask_z) condition_map[cond.mask[0]].push_back({ 2, channel_index++ });
if (instruction.data.d3.sca_writemask_w) condition_map[cond.mask[0]].push_back({ 3, channel_index });
}
auto src = apply_instruction_modifiers(arg);
src.assign(float_point_expr<4>(arg.text, arg.mask, true, 4));
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(channels.second).mask[0];
dst_swizzle += dest.swizzle(channels.second).mask[0];
}
float_point_expr<4> expression{ src.with_mask(src_swizzle) };
if (has_dst)
{
expression.assign(dest.with_mask(dst_swizzle) = expression);
}
if (instruction.data.d0.cond_update_enable_0 && instruction.data.d0.cond_update_enable_1)
{
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
{
float_point_expr<4> expression{ apply_instruction_modifiers(arg.without_scope()) };
if (has_dst)
{
expression.assign(dest = expression);
}
if (instruction.data.d0.cond_update_enable_0 && instruction.data.d0.cond_update_enable_1)
{
expression.assign(update_condition_register() = expression);
}
result += expression;
}
return result;
}
writer_t set_dst(expression_from<float_point_t<1>> arg)
{
if (destination_swizzle().size() != 1)
{
return set_dst(float_point_t<4>::ctor(arg));
}
return set_dst(float_point_expr<4>{ arg.to_string() });
}
writer_t set_dst(boolean_expr<4> arg)
{
std::string arg_string;
bool is_single = true;
switch (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(boolean_expr<2>{ arg.to_string() }).to_string(); break;
case 3: arg_string = float_point_t<3>::ctor(boolean_expr<3>{ arg.to_string() }).to_string(); break;
case 4: arg_string = float_point_t<4>::ctor(boolean_expr<4>{ arg.to_string() }).to_string(); break;
default:
throw;
}
return set_dst(float_point_expr<4>{ arg_string, std::string("xyzw"), is_single, 4 });
}
enum class condition_operation
{
all,
any
};
compare_function execution_condition_function() const
{
switch (instruction.data.d0.cond)
{
case gt | eq: return compare_function::greater_equal;
case lt | eq: return compare_function::less_equal;
case lt | gt: return compare_function::not_equal;
case gt: return compare_function::greater;
case lt: return compare_function::less;
case eq: return compare_function::equal;
}
throw;
}
boolean_expr<1> execution_condition(condition_operation operation)
{
if (instruction.data.d0.cond == always)
{
return true;
}
if (instruction.data.d0.cond == never)
{
return false;
}
auto cond = execution_condition_register();
if (instruction.data.d0.mask_x == instruction.data.d0.mask_y &&
instruction.data.d0.mask_y == instruction.data.d0.mask_z &&
instruction.data.d0.mask_z == instruction.data.d0.mask_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;
}
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;
}
expression_base_t decode_sca_instruction()
{
is_vec = false;
switch (instruction.data.d1.sca_opcode)
{
case sca_opcode::mov: return set_dst(src_swizzled_as_dst(2));
case sca_opcode::rcp: return set_dst(float_point_t<1>::ctor(1.0f) / src_swizzled_as_dst(2));
case sca_opcode::rcc: return set_dst(clamp(float_point_t<1>::ctor(1.0f) / src_swizzled_as_dst(2), 5.42101e-20f, 1.884467e19f));
case sca_opcode::rsq: return set_dst(rsqrt(src_swizzled_as_dst(2)));
case sca_opcode::exp: return set_dst(exp(src_swizzled_as_dst(2)));
case sca_opcode::log: return set_dst(log(src_swizzled_as_dst(2)));
case sca_opcode::lit:
{
auto t = src(2);
float_point_expr<1> z_value{ (t.x() > 0.0f).text };
z_value.assign("(" + z_value.text + " ? " + exp2(t.w() * log2(t.y())).text + " : 0.0)");
return set_dst(swizzle_as_dst(float_point_t<4>::ctor(1.0f, t.x(), z_value, 1.0f)));
}
case sca_opcode::bra: break;
case sca_opcode::bri:
{
std::size_t from = writer.position;
std::size_t to = address_value();
boolean_expr<1> condition{ execution_condition(condition_operation::all) };
if (to > from)
{
writer.before(from, "if (!(" + condition.to_string() + "))\n{\n");
writer.before(to, "}\n");
}
else
{
writer.before(from, "}\nwhile (" + condition.to_string() + ");\n");
writer.before(to, "do\n{\n");
}
}
return "";
case sca_opcode::cal: break;
case sca_opcode::cli: break;
case sca_opcode::ret: return conditional(void_expr{ "return" });
case sca_opcode::lg2: return set_dst(log2(src_swizzled_as_dst(2)));
case sca_opcode::ex2: return set_dst(exp2(src_swizzled_as_dst(2)));
case sca_opcode::sin: return set_dst(sin(src_swizzled_as_dst(2)));
case sca_opcode::cos: return set_dst(cos(src_swizzled_as_dst(2)));
case sca_opcode::brb: break;
case sca_opcode::clb: break;
case sca_opcode::psh: break;
case sca_opcode::pop: break;
default:
throw;
}
return unimplemented("sca " + sca_op_names[(int)instruction.data.d1.sca_opcode]);
}
expression_base_t decode_vec_instruction()
{
is_vec = true;
switch (instruction.data.d1.vec_opcode)
{
case vec_opcode::mov: return set_dst(src_swizzled_as_dst(0));
case vec_opcode::mul: return set_dst(src_swizzled_as_dst(0) * src_swizzled_as_dst(1));
case vec_opcode::add: return set_dst(src_swizzled_as_dst(0) + src_swizzled_as_dst(2));
case vec_opcode::mad: return set_dst(src_swizzled_as_dst(0) * src_swizzled_as_dst(1).without_scope() + src_swizzled_as_dst(2));
case vec_opcode::dp3: return set_dst(dot(src(0).xyz(), src(1).xyz()));
case vec_opcode::dph: break;
case vec_opcode::dp4: return set_dst(dot(src(0), src(1)));
case vec_opcode::dst: break;
case vec_opcode::min: return set_dst(min(src_swizzled_as_dst(0), src_swizzled_as_dst(1)));
case vec_opcode::max: return set_dst(max(src_swizzled_as_dst(0), src_swizzled_as_dst(1)));
case vec_opcode::slt: return set_dst(compare(compare_function::less, src_swizzled_as_dst(0), src_swizzled_as_dst(1)));
case vec_opcode::sge: return set_dst(compare(compare_function::greater_equal, src_swizzled_as_dst(0), src_swizzled_as_dst(1)));
case vec_opcode::arl: return writer_t{} += address_register() = integer_t<1>::ctor(src(0).x());
case vec_opcode::frc: return set_dst(fract(src_swizzled_as_dst(0)));
case vec_opcode::flr: return set_dst(floor(src_swizzled_as_dst(0)));;
case vec_opcode::seq: return set_dst(compare(compare_function::equal, src_swizzled_as_dst(0), src_swizzled_as_dst(1)));
case vec_opcode::sfl: return set_dst(0.0f);
case vec_opcode::sgt: return set_dst(compare(compare_function::greater, src_swizzled_as_dst(0), src_swizzled_as_dst(1)));;
case vec_opcode::sle: return set_dst(compare(compare_function::less_equal, src_swizzled_as_dst(0), src_swizzled_as_dst(1)));
case vec_opcode::sne: return set_dst(compare(compare_function::not_equal, src_swizzled_as_dst(0), src_swizzled_as_dst(1)));
case vec_opcode::str: return set_dst(1.0f);
case vec_opcode::ssg: break;
case vec_opcode::txl: break;
default:
throw;
}
return unimplemented("vec " + vec_op_names[(int)instruction.data.d1.vec_opcode]);
}
expression_base_t decode_instruction()
{
if (instruction.data.d1.sca_opcode == sca_opcode::nop && instruction.data.d1.vec_opcode == vec_opcode::nop)
{
return comment("NOP");
}
writer_t result;
if (instruction.data.d1.sca_opcode != sca_opcode::nop)
{
result += decode_sca_instruction();
}
if (instruction.data.d1.vec_opcode != vec_opcode::nop)
{
result += decode_vec_instruction();
}
return result;
}
public:
decompiled_program decompile(std::size_t offset, instruction_t *instructions)
{
for (std::size_t i = offset; i < 512; ++i, writer.next())
{
instruction = instructions[i].unpack();
if (instruction.data.d1.sca_opcode == sca_opcode::nop && instruction.data.d1.vec_opcode == vec_opcode::nop)
{
writer += comment("NOP");
}
else
{
if (instruction.data.d1.sca_opcode != sca_opcode::nop)
{
writer += comment(sca_op_names[(int)instruction.data.d1.sca_opcode]);
}
if (instruction.data.d1.vec_opcode != vec_opcode::nop)
{
writer += comment(vec_op_names[(int)instruction.data.d1.vec_opcode]);
}
}
writer += decode_instruction();
if (instruction.data.d3.end)
break;
@@ -105,4 +679,4 @@ namespace rsx
return result;
}
}
}
}

View File

@@ -4,6 +4,16 @@ namespace rsx
{
namespace vertex_program
{
const std::string input_registers_table[0x10] =
{
"in_pos", "in_weight", "in_normal",
"in_diff_color", "in_spec_color",
"in_fog",
"in_point_size", "in_7",
"in_tc0", "in_tc1", "in_tc2", "in_tc3",
"in_tc4", "in_tc5", "in_tc6", "in_tc7"
};
const std::string sca_op_names[0x20] =
{
"NOP", "MOV", "RCP", "RCC", "RSQ", "EXP", "LOG",

View File

@@ -7,7 +7,7 @@ namespace rsx
namespace vertex_program
{
using u32 = std::uint32_t;
enum class sca_opcode
enum class sca_opcode : u32
{
nop = 0x00,
mov = 0x01,
@@ -32,7 +32,7 @@ namespace rsx
pop = 0x14
};
enum class vec_opcode
enum class vec_opcode : u32
{
nop = 0x00,
mov = 0x01,
@@ -60,6 +60,13 @@ namespace rsx
txl = 0x19
};
enum class src_register_type : u32
{
temporary = 1,
input = 2,
constant = 3
};
union D0
{
u32 _u32;
@@ -173,7 +180,7 @@ namespace rsx
struct
{
u32 reg_type : 2;
src_register_type register_type : 2;
u32 tmp_src : 6;
u32 swz_w : 2;
u32 swz_z : 2;
@@ -191,6 +198,7 @@ namespace rsx
D3 d3;
};
extern const std::string input_registers_table[0x10];
extern const std::string sca_op_names[0x20];
extern const std::string vec_op_names[0x20];
}

View File

@@ -328,16 +328,18 @@ int main(int argc, char** argv)
if (0)
{
using namespace rsx::fragment_program;
std::vector<char> file = load_file("tmp.fp.ucode");
rsx::fragment_program::ucode_instr *instructions = (rsx::fragment_program::ucode_instr *)file.data();
program = rsx::fragment_program::decompile(0, instructions, rsx::decompile_language::glsl);
ucode_instr *instructions = (ucode_instr *)file.data();
program = decompile(0, instructions, rsx::decompile_language::glsl);
}
else
{
using namespace rsx::vertex_program;
std::vector<char> file = load_file("tmp.vp.ucode");
rsx::vertex_program::ucode_instr *instructions = (rsx::vertex_program::ucode_instr *)file.data();
ucode_instr *instructions = (ucode_instr *)file.data();
program = rsx::vertex_program::decompile(0, instructions, rsx::decompile_language::glsl);
}

View File

@@ -153,6 +153,11 @@ namespace shader_code
template<size_t Count>
using float_point_t = typename type_t<type_class_t::type_float, Count>;
using void_expr = expression_from<void_t>;
template<int Count> using float_point_expr = expression_from<float_point_t<Count>>;
template<int Count> using boolean_expr = expression_from<boolean_t<Count>>;
template<int Count> using integer_expr = expression_from<integer_t<Count>>;
static expression_from<float_point_t<4>> texture(const expression_from<sampler1D_t>& texture, const expression_from<float_point_t<1>>& coord)
{
return function_t<float_point_t<4>, language::function_name_t<function_class_t::function_texture>>::invoke(texture, coord);
@@ -308,6 +313,16 @@ namespace shader_code
return function_t<float_point_t<Count>, language::function_name_t<function_class_t::function_pow>>::invoke(a, b);
}
template<int Count>
static expression_from<float_point_t<Count>> exp(const expression_t<type_class_t::type_float, Count>& arg)
{
return function_t<float_point_t<Count>, language::function_name_t<function_class_t::function_exp>>::invoke(arg);
}
template<int Count>
static expression_from<float_point_t<Count>> log(const expression_t<type_class_t::type_float, Count>& arg)
{
return function_t<float_point_t<Count>, language::function_name_t<function_class_t::function_log>>::invoke(arg);
}
template<int Count>
static expression_from<float_point_t<Count>> exp2(const expression_t<type_class_t::type_float, Count>& arg)
{
return function_t<float_point_t<Count>, language::function_name_t<function_class_t::function_exp2>>::invoke(arg);

View File

@@ -88,6 +88,8 @@ namespace shader_code
function_abs,
function_fract,
function_floor,
function_exp,
function_log,
function_exp2,
function_log2,
function_pow,
@@ -131,6 +133,8 @@ namespace shader_code
bool is_single;
int base_count = Count;
expression_helper_t() = default;
expression_helper_t(const std::string& text, bool is_single = true, int base_count = Count)
: expression_base_t{ text }
, is_single(is_single)

View File

@@ -253,6 +253,18 @@ namespace shader_code
static constexpr char *name = "inversesqrt";
};
template<>
struct function_name_t<clike_language::function_class_t::function_exp>
{
static constexpr char *name = "exp";
};
template<>
struct function_name_t<clike_language::function_class_t::function_log>
{
static constexpr char *name = "log";
};
template<>
struct function_name_t<clike_language::function_class_t::function_exp2>
{