From 2416d49dba12df2f7857b872dd0ef4b21067d55d Mon Sep 17 00:00:00 2001 From: vlj Date: Tue, 19 May 2015 18:17:08 +0200 Subject: [PATCH] RSX: Add a class factorizing decompiler code --- .../RSX/Common/FragmentProgramDecompiler.cpp | 592 +++++++++++++++ .../RSX/Common/FragmentProgramDecompiler.h | 92 +++ rpcs3/Emu/RSX/Common/ShaderParam.cpp | 2 + rpcs3/Emu/RSX/Common/ShaderParam.h | 214 ++++++ .../RSX/Common/VertexProgramDecompiler.cpp | 703 ++++++++++++++++++ .../Emu/RSX/Common/VertexProgramDecompiler.h | 118 +++ rpcs3/emucore.vcxproj | 6 + rpcs3/emucore.vcxproj.filters | 18 + 8 files changed, 1745 insertions(+) create mode 100644 rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.cpp create mode 100644 rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.h create mode 100644 rpcs3/Emu/RSX/Common/ShaderParam.cpp create mode 100644 rpcs3/Emu/RSX/Common/ShaderParam.h create mode 100644 rpcs3/Emu/RSX/Common/VertexProgramDecompiler.cpp create mode 100644 rpcs3/Emu/RSX/Common/VertexProgramDecompiler.h diff --git a/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.cpp b/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.cpp new file mode 100644 index 0000000000..efa3224dac --- /dev/null +++ b/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.cpp @@ -0,0 +1,592 @@ +#include "stdafx.h" +#include "FragmentProgramDecompiler.h" + +#include "Utilities/Log.h" +#include "Emu/Memory/Memory.h" +#include "Emu/System.h" + +FragmentProgramDecompiler::FragmentProgramDecompiler(u32 addr, u32& size, u32 ctrl) : + m_addr(addr), + m_size(size), + m_const_index(0), + m_location(0), + m_ctrl(ctrl) +{ + m_size = 0; +} + + +void FragmentProgramDecompiler::SetDst(std::string code, bool append_mask) +{ + if (!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) return; + + switch (src1.scale) + { + case 0: break; + case 1: code = "(" + code + " * 2.0)"; break; + case 2: code = "(" + code + " * 4.0)"; break; + case 3: code = "(" + code + " * 8.0)"; break; + case 5: code = "(" + code + " / 2.0)"; break; + case 6: code = "(" + code + " / 4.0)"; break; + case 7: code = "(" + code + " / 8.0)"; break; + + default: + LOG_ERROR(RSX, "Bad scale: %d", fmt::by_value(src1.scale)); + Emu.Pause(); + break; + } + + if (dst.saturate) + { + code = saturate(code); + } + + code += (append_mask ? "$m" : ""); + + if (dst.no_dest) + { + if (dst.set_cond) + { + AddCode("$ifcond " + m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "cc" + std::to_string(src0.cond_mod_reg_index)) + "$m = " + code + ";"); + } + else + { + AddCode("$ifcond " + code + ";"); + } + + return; + } + + std::string dest = AddReg(dst.dest_reg, dst.fp16) + "$m"; + + AddCodeCond(Format(dest), code); + //AddCode("$ifcond " + dest + code + (append_mask ? "$m;" : ";")); + + if (dst.set_cond) + { + AddCode(m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "cc" + std::to_string(src0.cond_mod_reg_index)) + "$m = " + dest + ";"); + } +} + +void FragmentProgramDecompiler::AddCode(const std::string& code) +{ + main.append(m_code_level, '\t') += Format(code) + "\n"; +} + +std::string FragmentProgramDecompiler::GetMask() +{ + std::string ret; + + static const char dst_mask[4] = + { + 'x', 'y', 'z', 'w', + }; + + if (dst.mask_x) ret += dst_mask[0]; + if (dst.mask_y) ret += dst_mask[1]; + if (dst.mask_z) ret += dst_mask[2]; + if (dst.mask_w) ret += dst_mask[3]; + + return ret.empty() || strncmp(ret.c_str(), dst_mask, 4) == 0 ? "" : ("." + ret); +} + +std::string FragmentProgramDecompiler::AddReg(u32 index, int fp16) +{ + return m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), std::string(fp16 ? "h" : "r") + std::to_string(index), getFloatTypeName(4) + "(0.0)"); +} + +bool FragmentProgramDecompiler::HasReg(u32 index, int fp16) +{ + return m_parr.HasParam(PF_PARAM_NONE, getFloatTypeName(4), + std::string(fp16 ? "h" : "r") + std::to_string(index)); +} + +std::string FragmentProgramDecompiler::AddCond() +{ + return m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "cc" + std::to_string(src0.cond_reg_index)); +} + +std::string FragmentProgramDecompiler::AddConst() +{ + std::string name = std::string("fc") + std::to_string(m_size + 4 * 4); + if (m_parr.HasParam(PF_PARAM_UNIFORM, getFloatTypeName(4), name)) + { + return name; + } + + auto data = vm::ptr::make(m_addr + m_size + 4 * sizeof(u32)); + + m_offset = 2 * 4 * sizeof(u32); + u32 x = GetData(data[0]); + u32 y = GetData(data[1]); + u32 z = GetData(data[2]); + u32 w = GetData(data[3]); + return m_parr.AddParam(PF_PARAM_UNIFORM, getFloatTypeName(4), name, + std::string(getFloatTypeName(4) + "(") + std::to_string((float&)x) + ", " + std::to_string((float&)y) + + ", " + std::to_string((float&)z) + ", " + std::to_string((float&)w) + ")"); +} + +std::string FragmentProgramDecompiler::AddTex() +{ + return m_parr.AddParam(PF_PARAM_UNIFORM, "sampler2D", std::string("tex") + std::to_string(dst.tex_num)); +} + +std::string FragmentProgramDecompiler::Format(const std::string& code) +{ + const std::pair> repl_list[] = + { + { "$$", []() -> std::string { return "$"; } }, + { "$0", [this]() -> std::string {return GetSRC(src0);} },//std::bind(std::mem_fn(&GLFragmentDecompilerThread::GetSRC), *this, src0) }, + { "$1", [this]() -> std::string {return GetSRC(src1);} },//std::bind(std::mem_fn(&GLFragmentDecompilerThread::GetSRC), this, src1) }, + { "$2", [this]() -> std::string {return GetSRC(src2);} },//std::bind(std::mem_fn(&GLFragmentDecompilerThread::GetSRC), this, src2) }, + { "$t", std::bind(std::mem_fn(&FragmentProgramDecompiler::AddTex), this) }, + { "$m", std::bind(std::mem_fn(&FragmentProgramDecompiler::GetMask), this) }, + { "$ifcond ", [this]() -> std::string + { + const std::string& cond = GetCond(); + if (cond == "true") return ""; + return "if(" + cond + ") "; + } + }, + { "$cond", std::bind(std::mem_fn(&FragmentProgramDecompiler::GetCond), this) }, + { "$c", std::bind(std::mem_fn(&FragmentProgramDecompiler::AddConst), this) } + }; + + return fmt::replace_all(code, repl_list); +} + +std::string FragmentProgramDecompiler::GetCond() +{ + if (src0.exec_if_gr && src0.exec_if_lt && src0.exec_if_eq) + { + return "true"; + } + else if (!src0.exec_if_gr && !src0.exec_if_lt && !src0.exec_if_eq) + { + return "false"; + } + + static const char f[4] = { 'x', 'y', 'z', 'w' }; + + std::string swizzle, cond; + swizzle += f[src0.cond_swizzle_x]; + swizzle += f[src0.cond_swizzle_y]; + swizzle += f[src0.cond_swizzle_z]; + swizzle += f[src0.cond_swizzle_w]; + swizzle = swizzle == "xyzw" ? "" : "." + swizzle; + + if (src0.exec_if_gr && src0.exec_if_eq) + cond = compareFunction(COMPARE::FUNCTION_SGE, AddCond() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); + else if (src0.exec_if_lt && src0.exec_if_eq) + cond = compareFunction(COMPARE::FUNCTION_SLE, AddCond() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); + else if (src0.exec_if_gr && src0.exec_if_lt) + cond = compareFunction(COMPARE::FUNCTION_SNE, AddCond() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); + else if (src0.exec_if_gr) + cond = compareFunction(COMPARE::FUNCTION_SGT, AddCond() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); + else if (src0.exec_if_lt) + cond = compareFunction(COMPARE::FUNCTION_SLT, AddCond() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); + else //if(src0.exec_if_eq) + cond = compareFunction(COMPARE::FUNCTION_SEQ, AddCond() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); + + return "any(" + cond + ")"; +} + +void FragmentProgramDecompiler::AddCodeCond(const std::string& dst, const std::string& src) +{ + if (src0.exec_if_gr && src0.exec_if_lt && src0.exec_if_eq) + { + AddCode(dst + " = " + src + ";"); + return; + } + + if (!src0.exec_if_gr && !src0.exec_if_lt && !src0.exec_if_eq) + { + AddCode("//" + dst + " = " + src + ";"); + return; + } + + static const char f[4] = { 'x', 'y', 'z', 'w' }; + + std::string swizzle, cond; + swizzle += f[src0.cond_swizzle_x]; + swizzle += f[src0.cond_swizzle_y]; + swizzle += f[src0.cond_swizzle_z]; + swizzle += f[src0.cond_swizzle_w]; + swizzle = swizzle == "xyzw" ? "" : "." + swizzle; + + if (src0.exec_if_gr && src0.exec_if_eq) + cond = compareFunction(COMPARE::FUNCTION_SGE, AddCond() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); + else if (src0.exec_if_lt && src0.exec_if_eq) + cond = compareFunction(COMPARE::FUNCTION_SLE, AddCond() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); + else if (src0.exec_if_gr && src0.exec_if_lt) + cond = compareFunction(COMPARE::FUNCTION_SNE, AddCond() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); + else if (src0.exec_if_gr) + cond = compareFunction(COMPARE::FUNCTION_SGT, AddCond() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); + else if (src0.exec_if_lt) + cond = compareFunction(COMPARE::FUNCTION_SLT, AddCond() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); + else //if(src0.exec_if_eq) + cond = compareFunction(COMPARE::FUNCTION_SEQ, AddCond() + swizzle, getFloatTypeName(4) + "(0., 0., 0., 0.)"); + + ShaderVariable dst_var(dst); + dst_var.symplify(); + + //const char *c_mask = f; + + if (dst_var.swizzles[0].length() == 1) + { + AddCode("if (" + cond + ".x) " + dst + " = " + src + ";"); + } + else + { + for (int i = 0; i < dst_var.swizzles[0].length(); ++i) + { + AddCode("if (" + cond + "." + f[i] + ") " + dst + "." + f[i] + " = " + src + "." + f[i] + ";"); + } + } +} + +template std::string FragmentProgramDecompiler::GetSRC(T src) +{ + std::string ret; + + switch (src.reg_type) + { + case 0: //tmp + ret += AddReg(src.tmp_reg_index, src.fp16); + break; + + case 1: //input + { + static const std::string reg_table[] = + { + "gl_Position", + "diff_color", "spec_color", + "fogc", + "tc0", "tc1", "tc2", "tc3", "tc4", "tc5", "tc6", "tc7", "tc8", "tc9", + "ssa" + }; + + switch (dst.src_attr_reg_num) + { + case 0x00: ret += reg_table[0]; break; + default: + if (dst.src_attr_reg_num < sizeof(reg_table) / sizeof(reg_table[0])) + { + ret += m_parr.AddParam(PF_PARAM_IN, getFloatTypeName(4), reg_table[dst.src_attr_reg_num]); + } + else + { + LOG_ERROR(RSX, "Bad src reg num: %d", fmt::by_value(dst.src_attr_reg_num)); + ret += m_parr.AddParam(PF_PARAM_IN, getFloatTypeName(4), "unk"); + Emu.Pause(); + } + break; + } + } + break; + + case 2: //const + ret += AddConst(); + break; + + default: + LOG_ERROR(RSX, "Bad src type %d", fmt::by_value(src.reg_type)); + Emu.Pause(); + break; + } + + static const char f[4] = { 'x', 'y', 'z', 'w' }; + + std::string swizzle = ""; + swizzle += f[src.swizzle_x]; + swizzle += f[src.swizzle_y]; + swizzle += f[src.swizzle_z]; + swizzle += f[src.swizzle_w]; + + if (strncmp(swizzle.c_str(), f, 4) != 0) ret += "." + swizzle; + + if (src.abs) ret = "abs(" + ret + ")"; + if (src.neg) ret = "-" + ret; + + return ret; +} + +std::string FragmentProgramDecompiler::BuildCode() +{ + //main += fmt::Format("\tgl_FragColor = %c0;\n", m_ctrl & 0x40 ? 'r' : 'h'); + + if (m_ctrl & 0xe) main += m_ctrl & 0x40 ? "\tgl_FragDepth = r1.z;\n" : "\tgl_FragDepth = h2.z;\n"; + + std::stringstream OS; + insertHeader(OS); + OS << std::endl; + insertConstants(OS); + OS << std::endl; + insertIntputs(OS); + OS << std::endl; + insertOutputs(OS); + OS << std::endl; + insertMainStart(OS); + OS << main << std::endl; + insertMainEnd(OS); + + return OS.str(); +} + +std::string FragmentProgramDecompiler::Decompile() +{ + auto data = vm::ptr::make(m_addr); + m_size = 0; + m_location = 0; + m_loop_count = 0; + m_code_level = 1; + + enum + { + FORCE_NONE, + FORCE_SCT, + FORCE_SCB, + }; + + int forced_unit = FORCE_NONE; + + while (true) + { + for (auto finded = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size); + finded != m_end_offsets.end(); + finded = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size)) + { + m_end_offsets.erase(finded); + m_code_level--; + AddCode("}"); + m_loop_count--; + } + + for (auto finded = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size); + finded != m_else_offsets.end(); + finded = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size)) + { + m_else_offsets.erase(finded); + m_code_level--; + AddCode("}"); + AddCode("else"); + AddCode("{"); + m_code_level++; + } + + dst.HEX = GetData(data[0]); + src0.HEX = GetData(data[1]); + src1.HEX = GetData(data[2]); + src2.HEX = GetData(data[3]); + + m_offset = 4 * sizeof(u32); + + const u32 opcode = dst.opcode | (src1.opcode_is_branch << 6); + + auto SCT = [&]() + { + switch (opcode) + { + case RSX_FP_OPCODE_ADD: SetDst("($0 + $1)"); break; + case RSX_FP_OPCODE_DIV: SetDst("($0 / $1)"); break; + case RSX_FP_OPCODE_DIVSQ: SetDst("($0 / sqrt($1).xxxx)"); break; + case RSX_FP_OPCODE_DP2: SetDst(getFunction(FUNCTION::FUNCTION_DP2)); break; + case RSX_FP_OPCODE_DP3: SetDst(getFunction(FUNCTION::FUNCTION_DP3)); break; + case RSX_FP_OPCODE_DP4: SetDst(getFunction(FUNCTION::FUNCTION_DP4)); break; + case RSX_FP_OPCODE_DP2A: SetDst(getFunction(FUNCTION::FUNCTION_DP2A)); break; + case RSX_FP_OPCODE_MAD: SetDst("($0 * $1 + $2)"); break; + case RSX_FP_OPCODE_MAX: SetDst("max($0, $1)"); break; + case RSX_FP_OPCODE_MIN: SetDst("min($0, $1)"); break; + case RSX_FP_OPCODE_MOV: SetDst("$0"); break; + case RSX_FP_OPCODE_MUL: SetDst("($0 * $1)"); break; + case RSX_FP_OPCODE_RCP: SetDst("1 / $0"); break; + case RSX_FP_OPCODE_RSQ: SetDst("inversesqrt(abs($0))"); break; + case RSX_FP_OPCODE_SEQ: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SEQ, "$0", "$1") + ")"); break; + case RSX_FP_OPCODE_SFL: SetDst(getFunction(FUNCTION::FUNCTION_SFL)); break; + case RSX_FP_OPCODE_SGE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGE, "$0", "$1") + ")"); break; + case RSX_FP_OPCODE_SGT: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGT, "$0", "$1") + ")"); break; + case RSX_FP_OPCODE_SLE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLE, "$0", "$1") + ")"); break; + case RSX_FP_OPCODE_SLT: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLT, "$0", "$1") + ")"); break; + case RSX_FP_OPCODE_SNE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SNE, "$0", "$1") + ")"); break; + case RSX_FP_OPCODE_STR: SetDst(getFunction(FUNCTION::FUNCTION_STR)); break; + + default: + return false; + } + + return true; + }; + + auto SCB = [&]() + { + switch (opcode) + { + case RSX_FP_OPCODE_ADD: SetDst("($0 + $1)"); break; + case RSX_FP_OPCODE_COS: SetDst("cos($0.xxxx)"); break; + case RSX_FP_OPCODE_DP2: SetDst(getFunction(FUNCTION::FUNCTION_DP2)); break; + case RSX_FP_OPCODE_DP3: SetDst(getFunction(FUNCTION::FUNCTION_DP3)); break; + case RSX_FP_OPCODE_DP4: SetDst(getFunction(FUNCTION::FUNCTION_DP4)); break; + case RSX_FP_OPCODE_DP2A: SetDst(getFunction(FUNCTION::FUNCTION_DP2A)); break; + case RSX_FP_OPCODE_DST: SetDst("vec4(distance($0, $1))"); break; + case RSX_FP_OPCODE_REFL: LOG_ERROR(RSX, "Unimplemented SCB instruction: REFL"); break; // TODO: Is this in the right category? + case RSX_FP_OPCODE_EX2: SetDst("exp2($0.xxxx)"); break; + case RSX_FP_OPCODE_FLR: SetDst("floor($0)"); break; + case RSX_FP_OPCODE_FRC: SetDst(getFunction(FUNCTION::FUNCTION_FRACT)); break; + case RSX_FP_OPCODE_LIT: SetDst("vec4(1.0, $0.x, ($0.x > 0.0 ? exp($0.w * log2($0.y)) : 0.0), 1.0)"); break; + case RSX_FP_OPCODE_LIF: SetDst("vec4(1.0, $0.y, ($0.y > 0 ? pow(2.0, $0.w) : 0.0), 1.0)"); break; + case RSX_FP_OPCODE_LRP: LOG_ERROR(RSX, "Unimplemented SCB instruction: LRP"); break; // TODO: Is this in the right category? + case RSX_FP_OPCODE_LG2: SetDst("log2($0.xxxx)"); break; + case RSX_FP_OPCODE_MAD: SetDst("($0 * $1 + $2)"); break; + case RSX_FP_OPCODE_MAX: SetDst("max($0, $1)"); break; + case RSX_FP_OPCODE_MIN: SetDst("min($0, $1)"); break; + case RSX_FP_OPCODE_MOV: SetDst("$0"); break; + case RSX_FP_OPCODE_MUL: SetDst("($0 * $1)"); break; + case RSX_FP_OPCODE_PK2: SetDst("packSnorm2x16($0)"); break; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) + case RSX_FP_OPCODE_PK4: SetDst("packSnorm4x8($0)"); break; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) + case RSX_FP_OPCODE_PK16: LOG_ERROR(RSX, "Unimplemented SCB instruction: PK16"); break; + case RSX_FP_OPCODE_PKB: LOG_ERROR(RSX, "Unimplemented SCB instruction: PKB"); break; + case RSX_FP_OPCODE_PKG: LOG_ERROR(RSX, "Unimplemented SCB instruction: PKG"); break; + case RSX_FP_OPCODE_SEQ: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SEQ, "$0", "$1") + ")"); break; + case RSX_FP_OPCODE_SFL: SetDst(getFloatTypeName(4) + "(" + getFunction(FUNCTION::FUNCTION_SFL)); break; + case RSX_FP_OPCODE_SGE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGE, "$0", "$1") + ")"); break; + case RSX_FP_OPCODE_SGT: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGT, "$0", "$1") + ")"); break; + case RSX_FP_OPCODE_SIN: SetDst("sin($0.xxxx)"); break; + case RSX_FP_OPCODE_SLE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLE, "$0", "$1") + ")"); break; + case RSX_FP_OPCODE_SLT: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLT, "$0", "$1") + ")"); break; + case RSX_FP_OPCODE_SNE: SetDst(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SNE, "$0", "$1") + ")"); break; + case RSX_FP_OPCODE_STR: SetDst(getFunction(FUNCTION::FUNCTION_STR)); break; + + default: + return false; + } + + return true; + }; + + auto TEX_SRB = [&]() + { + switch (opcode) + { + case RSX_FP_OPCODE_DDX: SetDst(getFunction(FUNCTION::FUNCTION_DFDX)); break; + case RSX_FP_OPCODE_DDY: SetDst(getFunction(FUNCTION::FUNCTION_DFDY)); break; + case RSX_FP_OPCODE_NRM: SetDst("normalize($0)"); break; + case RSX_FP_OPCODE_BEM: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: BEM"); break; + case RSX_FP_OPCODE_TEX: SetDst(getFunction(FUNCTION::FUNCTION_TEXTURE_SAMPLE)); break; + case RSX_FP_OPCODE_TEXBEM: SetDst("texture($t, $0.xy, $1.x)"); break; + case RSX_FP_OPCODE_TXP: SetDst("textureProj($t, $0.xyz, $1.x)"); break; //TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478) and The Simpsons Arcade Game (NPUB30563)) + case RSX_FP_OPCODE_TXPBEM: SetDst("textureProj($t, $0.xyz, $1.x)"); break; + case RSX_FP_OPCODE_TXD: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: TXD"); break; + case RSX_FP_OPCODE_TXB: SetDst("texture($t, $0.xy, $1.x)"); break; + case RSX_FP_OPCODE_TXL: SetDst("textureLod($t, $0.xy, $1.x)"); break; + case RSX_FP_OPCODE_UP2: SetDst("unpackSnorm2x16($0)"); break; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) + case RSX_FP_OPCODE_UP4: SetDst("unpackSnorm4x8($0)"); break; // TODO: More testing (Sonic The Hedgehog (NPUB-30442/NPEB-00478)) + case RSX_FP_OPCODE_UP16: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: UP16"); break; + case RSX_FP_OPCODE_UPB: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: UPB"); break; + case RSX_FP_OPCODE_UPG: LOG_ERROR(RSX, "Unimplemented TEX_SRB instruction: UPG"); break; + + default: + return false; + } + + return true; + }; + + auto SIP = [&]() + { + switch (opcode) + { + case RSX_FP_OPCODE_BRK: SetDst("break"); break; + case RSX_FP_OPCODE_CAL: LOG_ERROR(RSX, "Unimplemented SIP instruction: CAL"); break; + case RSX_FP_OPCODE_FENCT: forced_unit = FORCE_SCT; break; + case RSX_FP_OPCODE_FENCB: forced_unit = FORCE_SCB; break; + case RSX_FP_OPCODE_IFE: + AddCode("if($cond)"); + m_else_offsets.push_back(src1.else_offset << 2); + m_end_offsets.push_back(src2.end_offset << 2); + AddCode("{"); + m_code_level++; + break; + case RSX_FP_OPCODE_LOOP: + if (!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) + { + AddCode(fmt::Format("$ifcond for(int i%u = %u; i%u < %u; i%u += %u) {} //-> %u //LOOP", + m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment, src2.end_offset)); + } + else + { + AddCode(fmt::Format("$ifcond for(int i%u = %u; i%u < %u; i%u += %u) //LOOP", + m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment)); + m_loop_count++; + m_end_offsets.push_back(src2.end_offset << 2); + AddCode("{"); + m_code_level++; + } + break; + case RSX_FP_OPCODE_REP: + if (!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) + { + AddCode(fmt::Format("$ifcond for(int i%u = %u; i%u < %u; i%u += %u) {} //-> %u //REP", + m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment, src2.end_offset)); + } + else + { + AddCode(fmt::Format("if($cond) for(int i%u = %u; i%u < %u; i%u += %u) //REP", + m_loop_count, src1.init_counter, m_loop_count, src1.end_counter, m_loop_count, src1.increment)); + m_loop_count++; + m_end_offsets.push_back(src2.end_offset << 2); + AddCode("{"); + m_code_level++; + } + break; + case RSX_FP_OPCODE_RET: SetDst("return"); break; + + default: + return false; + } + + return true; + }; + + switch (opcode) + { + case RSX_FP_OPCODE_NOP: break; + case RSX_FP_OPCODE_KIL: SetDst("discard", false); break; + + default: + if (forced_unit == FORCE_NONE) + { + if (SIP()) break; + if (SCT()) break; + if (TEX_SRB()) break; + if (SCB()) break; + } + else if (forced_unit == FORCE_SCT) + { + forced_unit = FORCE_NONE; + if (SCT()) break; + } + else if (forced_unit == FORCE_SCB) + { + forced_unit = FORCE_NONE; + if (SCB()) break; + } + + LOG_ERROR(RSX, "Unknown/illegal instruction: 0x%x (forced unit %d)", opcode, forced_unit); + break; + } + + m_size += m_offset; + + if (dst.end) break; + + assert(m_offset % sizeof(u32) == 0); + data += m_offset / sizeof(u32); + } + + // flush m_code_level + m_code_level = 1; + std::string m_shader = BuildCode(); + main.clear(); + // m_parr.params.clear(); + return m_shader; +} \ No newline at end of file diff --git a/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.h b/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.h new file mode 100644 index 0000000000..e916ad6944 --- /dev/null +++ b/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.h @@ -0,0 +1,92 @@ +#pragma once +#include "ShaderParam.h" +#include "Emu/RSX/RSXFragmentProgram.h" +#include + +/** + * This class is used to translate RSX Fragment program to GLSL/HLSL code + * Backend with text based shader can subclass this class and implement : + * - virtual std::string getFloatTypeName(size_t elementCount) = 0; + * - virtual std::string getFunction(enum class FUNCTION) = 0; + * - virtual std::string saturate(const std::string &code) = 0; + * - virtual std::string compareFunction(enum class COMPARE, const std::string &, const std::string &) = 0; + * - virtual void insertHeader(std::stringstream &OS) = 0; + * - virtual void insertIntputs(std::stringstream &OS) = 0; + * - virtual void insertOutputs(std::stringstream &OS) = 0; + * - virtual void insertConstants(std::stringstream &OS) = 0; + * - virtual void insertMainStart(std::stringstream &OS) = 0; + * - virtual void insertMainEnd(std::stringstream &OS) = 0; + */ +class FragmentProgramDecompiler +{ + std::string main; + u32 m_addr; + u32& m_size; + u32 m_const_index; + u32 m_offset; + u32 m_location; + + u32 m_loop_count; + int m_code_level; + std::vector m_end_offsets; + std::vector m_else_offsets; + + std::string GetMask(); + + void SetDst(std::string code, bool append_mask = true); + void AddCode(const std::string& code); + std::string AddReg(u32 index, int fp16); + bool HasReg(u32 index, int fp16); + std::string AddCond(); + std::string AddConst(); + std::string AddTex(); + std::string Format(const std::string& code); + + void AddCodeCond(const std::string& dst, const std::string& src); + std::string GetCond(); + template std::string GetSRC(T src); + std::string BuildCode(); + + u32 GetData(const u32 d) const { return d << 16 | d >> 16; } +protected: + u32 m_ctrl; + /** returns the type name of float vectors. + */ + virtual std::string getFloatTypeName(size_t elementCount) = 0; + + /** returns string calling function where arguments are passed via + * $0 $1 $2 substring. + */ + virtual std::string getFunction(FUNCTION) = 0; + + /** returns string calling saturate function. + */ + virtual std::string saturate(const std::string &code) = 0; + /** returns string calling comparaison function on 2 args passed as strings. + */ + virtual std::string compareFunction(COMPARE, const std::string &, const std::string &) = 0; + + /** Insert header of shader file (eg #version, "system constants"...) + */ + virtual void insertHeader(std::stringstream &OS) = 0; + /** Insert global declaration of fragments inputs. + */ + virtual void insertIntputs(std::stringstream &OS) = 0; + /** insert global declaration of fragments outputs. + */ + virtual void insertOutputs(std::stringstream &OS) = 0; + /** insert declaration of shader constants. + */ + virtual void insertConstants(std::stringstream &OS) = 0; + /** insert beginning of main (signature, temporary declaration...) + */ + virtual void insertMainStart(std::stringstream &OS) = 0; + /** insert end of main function (return value, output copy...) + */ + virtual void insertMainEnd(std::stringstream &OS) = 0; +public: + ParamArray m_parr; + FragmentProgramDecompiler() = delete; + FragmentProgramDecompiler(u32 addr, u32& size, u32 ctrl); + std::string Decompile(); +}; diff --git a/rpcs3/Emu/RSX/Common/ShaderParam.cpp b/rpcs3/Emu/RSX/Common/ShaderParam.cpp new file mode 100644 index 0000000000..dbd162adcc --- /dev/null +++ b/rpcs3/Emu/RSX/Common/ShaderParam.cpp @@ -0,0 +1,2 @@ +#include "stdafx.h" +#include "ShaderParam.h" \ No newline at end of file diff --git a/rpcs3/Emu/RSX/Common/ShaderParam.h b/rpcs3/Emu/RSX/Common/ShaderParam.h new file mode 100644 index 0000000000..2437a4377c --- /dev/null +++ b/rpcs3/Emu/RSX/Common/ShaderParam.h @@ -0,0 +1,214 @@ +#pragma once +#include +#include + +enum class FUNCTION { + FUNCTION_DP2, + FUNCTION_DP2A, + FUNCTION_DP3, + FUNCTION_DP4, + FUNCTION_SFL, // Set zero + FUNCTION_STR, // Set One + FUNCTION_FRACT, + FUNCTION_DFDX, + FUNCTION_DFDY, + FUNCTION_TEXTURE_SAMPLE, +}; + +enum class COMPARE { + FUNCTION_SEQ, + FUNCTION_SGE, + FUNCTION_SGT, + FUNCTION_SLE, + FUNCTION_SLT, + FUNCTION_SNE, +}; + +enum ParamFlag +{ + PF_PARAM_IN, + PF_PARAM_OUT, + PF_PARAM_UNIFORM, + PF_PARAM_CONST, + PF_PARAM_NONE, + PF_PARAM_COUNT, +}; + +struct ParamItem +{ + std::string name; + std::string value; + int location; + + ParamItem(const std::string& _name, int _location, const std::string& _value = "") + : name(_name) + , value(_value), + location(_location) + { } +}; + +struct ParamType +{ + const ParamFlag flag; + std::string type; + std::vector items; + + ParamType(const ParamFlag _flag, const std::string& _type) + : flag(_flag) + , type(_type) + { + } + + bool SearchName(const std::string& name) + { + for (u32 i = 0; i params[PF_PARAM_COUNT]; + + ParamType* SearchParam(const ParamFlag &flag, const std::string& type) + { + for (u32 i = 0; iSearchName(name); + } + + std::string AddParam(const ParamFlag flag, std::string type, const std::string& name, const std::string& value) + { + ParamType* t = SearchParam(flag, type); + + if (t) + { + if (!t->SearchName(name)) t->items.emplace_back(name, -1, value); + } + else + { + params[flag].emplace_back(flag, type); + params[flag].back().items.emplace_back(name, -1, value); + } + + return name; + } + + std::string AddParam(const ParamFlag flag, std::string type, const std::string& name, int location = -1) + { + ParamType* t = SearchParam(flag, type); + + if (t) + { + if (!t->SearchName(name)) t->items.emplace_back(name, location); + } + else + { + params[flag].emplace_back(flag, type); + params[flag].back().items.emplace_back(name, location); + } + + return name; + } +}; + +class ShaderVariable +{ +public: + std::string name; + std::vector swizzles; + + ShaderVariable() = default; + ShaderVariable(const std::string& var) + { + auto var_blocks = fmt::split(var, { "." }); + + if (var_blocks.size() == 0) + { + assert(0); + } + + name = var_blocks[0]; + + if (var_blocks.size() == 1) + { + swizzles.push_back("xyzw"); + } + else + { + swizzles = std::vector(var_blocks.begin() + 1, var_blocks.end()); + } + } + + size_t get_vector_size() const + { + return swizzles[swizzles.size() - 1].length(); + } + + ShaderVariable& symplify() + { + std::unordered_map swizzle; + + static std::unordered_map pos_to_swizzle = + { + { 0, 'x' }, + { 1, 'y' }, + { 2, 'z' }, + { 3, 'w' } + }; + + for (auto &i : pos_to_swizzle) + { + swizzle[i.second] = swizzles[0].length() > i.first ? swizzles[0][i.first] : 0; + } + + for (int i = 1; i < swizzles.size(); ++i) + { + std::unordered_map new_swizzle; + + for (auto &sw : pos_to_swizzle) + { + new_swizzle[sw.second] = swizzle[swizzles[i].length() <= sw.first ? '\0' : swizzles[i][sw.first]]; + } + + swizzle = new_swizzle; + } + + swizzles.clear(); + std::string new_swizzle; + + for (auto &i : pos_to_swizzle) + { + if (swizzle[i.second] != '\0') + new_swizzle += swizzle[i.second]; + } + + swizzles.push_back(new_swizzle); + + return *this; + } + + std::string get() const + { + if (swizzles.size() == 1 && swizzles[0] == "xyzw") + { + return name; + } + + return name + "." + fmt::merge({ swizzles }, "."); + } +}; \ No newline at end of file diff --git a/rpcs3/Emu/RSX/Common/VertexProgramDecompiler.cpp b/rpcs3/Emu/RSX/Common/VertexProgramDecompiler.cpp new file mode 100644 index 0000000000..6df8cf9d42 --- /dev/null +++ b/rpcs3/Emu/RSX/Common/VertexProgramDecompiler.cpp @@ -0,0 +1,703 @@ +#include "stdafx.h" +#include "VertexProgramDecompiler.h" +#include "Utilities/Log.h" +#include "Emu/System.h" + +std::string VertexProgramDecompiler::GetMask(bool is_sca) +{ + std::string ret; + + if (is_sca) + { + if (d3.sca_writemask_x) ret += "x"; + if (d3.sca_writemask_y) ret += "y"; + if (d3.sca_writemask_z) ret += "z"; + if (d3.sca_writemask_w) ret += "w"; + } + else + { + if (d3.vec_writemask_x) ret += "x"; + if (d3.vec_writemask_y) ret += "y"; + if (d3.vec_writemask_z) ret += "z"; + if (d3.vec_writemask_w) ret += "w"; + } + + return ret.empty() || ret == "xyzw" ? "" : ("." + ret); +} + +std::string VertexProgramDecompiler::GetVecMask() +{ + return GetMask(false); +} + +std::string VertexProgramDecompiler::GetScaMask() +{ + return GetMask(true); +} + +std::string VertexProgramDecompiler::GetDST(bool isSca) +{ + std::string ret; + + switch (isSca ? 0x1f : d3.dst) + { + case 0x1f: + ret += m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), std::string("tmp") + std::to_string(isSca ? d3.sca_dst_tmp : d0.dst_tmp)); + break; + + default: + if (d3.dst > 15) + LOG_ERROR(RSX, fmt::Format("dst index out of range: %u", d3.dst)); + ret += m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), std::string("dst_reg") + std::to_string(d3.dst), d3.dst == 0 ? getFloatTypeName(4) + "(0.0f, 0.0f, 0.0f, 1.0f)" : getFloatTypeName(4) + "(0.0, 0.0, 0.0, 0.0)"); + break; + } + + return ret; +} + +std::string VertexProgramDecompiler::GetSRC(const u32 n) +{ + static const std::string reg_table[] = + { + "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" + }; + + std::string ret; + + switch (src[n].reg_type) + { + case 1: //temp + ret += m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "tmp" + std::to_string(src[n].tmp_src)); + break; + case 2: //input + if (d1.input_src < (sizeof(reg_table) / sizeof(reg_table[0]))) + { + ret += m_parr.AddParam(PF_PARAM_IN, getFloatTypeName(4), reg_table[d1.input_src], d1.input_src); + } + else + { + LOG_ERROR(RSX, "Bad input src num: %d", fmt::by_value(d1.input_src)); + ret += m_parr.AddParam(PF_PARAM_IN, getFloatTypeName(4), "in_unk", d1.input_src); + } + break; + case 3: //const + m_parr.AddParam(PF_PARAM_UNIFORM, getFloatTypeName(4), std::string("vc[468]")); + ret += std::string("vc[") + std::to_string(d1.const_src) + (d3.index_const ? " + " + AddAddrReg() : "") + "]"; + break; + + default: + LOG_ERROR(RSX, fmt::Format("Bad src%u reg type: %d", n, fmt::by_value(src[n].reg_type))); + Emu.Pause(); + break; + } + + static const std::string f = "xyzw"; + + std::string swizzle; + + swizzle += f[src[n].swz_x]; + swizzle += f[src[n].swz_y]; + swizzle += f[src[n].swz_z]; + swizzle += f[src[n].swz_w]; + + if (swizzle != f) ret += '.' + swizzle; + + bool abs; + + switch (n) + { + case 0: abs = d0.src0_abs; break; + case 1: abs = d0.src1_abs; break; + case 2: abs = d0.src2_abs; break; + } + + if (abs) ret = "abs(" + ret + ")"; + if (src[n].neg) ret = "-" + ret; + + return ret; +} + +void VertexProgramDecompiler::SetDST(bool is_sca, std::string value) +{ + if (d0.cond == 0) return; + + enum + { + lt = 0x1, + eq = 0x2, + gt = 0x4, + }; + + std::string mask = GetMask(is_sca); + + value += mask; + + if (is_sca && d0.vec_result) + { + //value = "vec4(" + value + ")"; + } + + if (d0.staturate) + { + value = "clamp(" + value + ", 0.0, 1.0)"; + } + + std::string dest; + + if (d0.cond_update_enable_0 && d0.cond_update_enable_1) + { + dest = m_parr.AddParam(PF_PARAM_NONE, getFloatTypeName(4), "cc" + std::to_string(d0.cond_reg_sel_1), getFloatTypeName(4) + "(0.0)") + mask; + } + else if (d3.dst != 0x1f || (is_sca ? d3.sca_dst_tmp != 0x3f : d0.dst_tmp != 0x3f)) + { + dest = GetDST(is_sca) + mask; + } + + //std::string code; + //if (d0.cond_test_enable) + // code += "$ifcond "; + //code += dest + value; + //AddCode(code + ";"); + + AddCodeCond(Format(dest), value); +} + +std::string VertexProgramDecompiler::GetFunc() +{ + std::string name = "func$a"; + + for (const auto& func : m_funcs) { + if (func.name.compare(name) == 0) { + return name + "()"; + } + } + + m_funcs.emplace_back(); + FuncInfo &idx = m_funcs.back(); + idx.offset = GetAddr(); + idx.name = name; + + return name + "()"; +} + +std::string VertexProgramDecompiler::GetTex() +{ + return m_parr.AddParam(PF_PARAM_UNIFORM, "sampler2D", std::string("vtex") + std::to_string(/*?.tex_num*/0)); +} + +std::string VertexProgramDecompiler::Format(const std::string& code) +{ + const std::pair> repl_list[] = + { + { "$$", []() -> std::string { return "$"; } }, + { "$0", std::bind(std::mem_fn(&VertexProgramDecompiler::GetSRC), this, 0) }, + { "$1", std::bind(std::mem_fn(&VertexProgramDecompiler::GetSRC), this, 1) }, + { "$2", std::bind(std::mem_fn(&VertexProgramDecompiler::GetSRC), this, 2) }, + { "$s", std::bind(std::mem_fn(&VertexProgramDecompiler::GetSRC), this, 2) }, + { "$am", std::bind(std::mem_fn(&VertexProgramDecompiler::AddAddrMask), this) }, + { "$a", std::bind(std::mem_fn(&VertexProgramDecompiler::AddAddrReg), this) }, + + { "$t", std::bind(std::mem_fn(&VertexProgramDecompiler::GetTex), this) }, + + { "$fa", [this]()->std::string { return std::to_string(GetAddr()); } }, + { "$f()", std::bind(std::mem_fn(&VertexProgramDecompiler::GetFunc), this) }, + { "$ifcond ", [this]() -> std::string + { + const std::string& cond = GetCond(); + if (cond == "true") return ""; + return "if(" + cond + ") "; + } + }, + { "$cond", std::bind(std::mem_fn(&VertexProgramDecompiler::GetCond), this) } + }; + + return fmt::replace_all(code, repl_list); +} + +std::string VertexProgramDecompiler::GetCond() +{ + enum + { + lt = 0x1, + eq = 0x2, + gt = 0x4, + }; + + if (d0.cond == 0) return "false"; + if (d0.cond == (lt | gt | eq)) return "true"; + + static const char* cond_string_table[(lt | gt | eq) + 1] = + { + "error", + "lessThan", + "equal", + "lessThanEqual", + "greaterThan", + "notEqual", + "greaterThanEqual", + "error" + }; + + static const char f[4] = { 'x', 'y', 'z', 'w' }; + + std::string swizzle; + swizzle += f[d0.mask_x]; + swizzle += f[d0.mask_y]; + swizzle += f[d0.mask_z]; + swizzle += f[d0.mask_w]; + + swizzle = swizzle == "xyzw" ? "" : "." + swizzle; + + return fmt::Format("any(%s(cc%d%s, vec4(0.0)%s))", cond_string_table[d0.cond], d0.cond_reg_sel_1, swizzle.c_str(), swizzle.c_str()); +} + +void VertexProgramDecompiler::AddCodeCond(const std::string& dst, const std::string& src) +{ + enum + { + lt = 0x1, + eq = 0x2, + gt = 0x4, + }; + + + if (!d0.cond_test_enable || d0.cond == (lt | gt | eq)) + { + AddCode(dst + " = " + src + ";"); + return; + } + + if (d0.cond == 0) + { + AddCode("//" + dst + " = " + src + ";"); + return; + } + + static const COMPARE cond_string_table[(lt | gt | eq) + 1] = + { + COMPARE::FUNCTION_SLT, // "error" + COMPARE::FUNCTION_SLT, + COMPARE::FUNCTION_SEQ, + COMPARE::FUNCTION_SLE, + COMPARE::FUNCTION_SGT, + COMPARE::FUNCTION_SNE, + COMPARE::FUNCTION_SGE, + }; + + static const char f[4] = { 'x', 'y', 'z', 'w' }; + + std::string swizzle; + swizzle += f[d0.mask_x]; + swizzle += f[d0.mask_y]; + swizzle += f[d0.mask_z]; + swizzle += f[d0.mask_w]; + + swizzle = swizzle == "xyzw" ? "" : "." + swizzle; + + std::string cond = compareFunction(cond_string_table[d0.cond], "cc" + std::to_string(d0.cond_reg_sel_1) + swizzle.c_str(), getFloatTypeName(4) + "(0., 0., 0., 0.)"); + + ShaderVariable dst_var(dst); + dst_var.symplify(); + + //const char *c_mask = f; + + if (dst_var.swizzles[0].length() == 1) + { + AddCode("if (" + cond + ".x) " + dst + " = " + getFloatTypeName(4) + "(" + src + ".xxxx).x;"); + } + else + { + for (int i = 0; i < dst_var.swizzles[0].length(); ++i) + { + AddCode("if (" + cond + "." + f[i] + ") " + dst + "." + f[i] + " = " + src + "." + f[i] + ";"); + } + } +} + + +std::string VertexProgramDecompiler::AddAddrMask() +{ + static const char f[] = { 'x', 'y', 'z', 'w' }; + return std::string(".") + f[d0.addr_swz]; +} + +std::string VertexProgramDecompiler::AddAddrReg() +{ + static const char f[] = { 'x', 'y', 'z', 'w' }; + return m_parr.AddParam(PF_PARAM_NONE, "ivec4", "a" + std::to_string(d0.addr_reg_sel_1), "ivec4(0)") + AddAddrMask(); +} + +u32 VertexProgramDecompiler::GetAddr() +{ + return (d2.iaddrh << 3) | d3.iaddrl; +} + +void VertexProgramDecompiler::AddCode(const std::string& code) +{ + m_body.push_back(Format(code) + ";"); + m_cur_instr->body.push_back(Format(code)); +} + +void VertexProgramDecompiler::SetDSTVec(const std::string& code) +{ + SetDST(false, code); +} + +void VertexProgramDecompiler::SetDSTSca(const std::string& code) +{ + SetDST(true, code); +} + +std::string VertexProgramDecompiler::BuildFuncBody(const FuncInfo& func) +{ + std::string result; + + for (uint i = func.offset; i= 1); + for (int j = 0; j < m_instructions[i].put_close_scopes; ++j) + { + --lvl; + if (lvl < 1) lvl = 1; + main_body.append(lvl, '\t') += "}\n"; + } + + for (int j = 0; j < m_instructions[i].do_count; ++j) + { + main_body.append(lvl, '\t') += "do\n"; + main_body.append(lvl, '\t') += "{\n"; + lvl++; + } + + for (uint j = 0; j < m_instructions[i].body.size(); ++j) + { + main_body.append(lvl, '\t') += m_instructions[i].body[j] + "\n"; + } + + lvl += m_instructions[i].open_scopes; + } + + std::stringstream OS; + insertHeader(OS); + + insertInputs(OS, m_parr.params[PF_PARAM_IN]); + OS << std::endl; + insertOutputs(OS, m_parr.params[PF_PARAM_NONE]); + OS << std::endl; + insertConstants(OS, m_parr.params[PF_PARAM_UNIFORM]); + OS << std::endl; + + insertMainStart(OS); + OS << main_body.c_str() << std::endl; + insertMainEnd(OS); + + return OS.str(); +} + +VertexProgramDecompiler::VertexProgramDecompiler(std::vector& data) : + m_data(data) +{ + m_funcs.emplace_back(); + m_funcs[0].offset = 0; + m_funcs[0].name = "main"; + m_funcs.emplace_back(); + m_funcs[1].offset = 0; + m_funcs[1].name = "func0"; + //m_cur_func->body = "\tgl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);\n"; +} + +std::string VertexProgramDecompiler::Decompile() +{ + for (unsigned i = 0; i < PF_PARAM_COUNT; i++) + m_parr.params[i].clear(); + m_instr_count = 0; + + for (int i = 0; i < m_max_instr_count; ++i) + { + m_instructions[i].reset(); + } + + bool is_has_BRA = false; + + for (u32 i = 1; m_instr_count < m_max_instr_count; m_instr_count++) + { + m_cur_instr = &m_instructions[m_instr_count]; + + if (is_has_BRA) + { + d3.HEX = m_data[i]; + i += 4; + } + else + { + d1.HEX = m_data[i++]; + + switch (d1.sca_opcode) + { + case 0x08: //BRA + LOG_ERROR(RSX, "BRA found. Please report to RPCS3 team."); + is_has_BRA = true; + m_jump_lvls.clear(); + d3.HEX = m_data[++i]; + i += 4; + break; + + case 0x09: //BRI + d2.HEX = m_data[i++]; + d3.HEX = m_data[i]; + i += 2; + m_jump_lvls.emplace(GetAddr()); + break; + + default: + d3.HEX = m_data[++i]; + i += 2; + break; + } + } + + if (d3.end) + { + m_instr_count++; + + if (i < m_data.size()) + { + LOG_ERROR(RSX, "Program end before buffer end."); + } + + break; + } + } + + uint jump_position = 0; + + if (is_has_BRA || !m_jump_lvls.empty()) + { + m_cur_instr = &m_instructions[0]; + AddCode("int jump_position = 0;"); + AddCode("while (true)"); + AddCode("{"); + m_cur_instr->open_scopes++; + + AddCode(fmt::Format("if (jump_position <= %u)", jump_position++)); + AddCode("{"); + m_cur_instr->open_scopes++; + } + + for (u32 i = 0; i < m_instr_count; ++i) + { + m_cur_instr = &m_instructions[i]; + + d0.HEX = m_data[i * 4 + 0]; + d1.HEX = m_data[i * 4 + 1]; + d2.HEX = m_data[i * 4 + 2]; + d3.HEX = m_data[i * 4 + 3]; + + src[0].src0l = d2.src0l; + src[0].src0h = d1.src0h; + src[1].src1 = d2.src1; + src[2].src2l = d3.src2l; + src[2].src2h = d2.src2h; + + if (i && (is_has_BRA || std::find(m_jump_lvls.begin(), m_jump_lvls.end(), i) != m_jump_lvls.end())) + { + m_cur_instr->close_scopes++; + AddCode("}"); + AddCode(""); + + AddCode(fmt::Format("if (jump_position <= %u)", jump_position++)); + AddCode("{"); + m_cur_instr->open_scopes++; + } + + if (!d1.sca_opcode && !d1.vec_opcode) + { + AddCode("//nop"); + } + + switch (d1.sca_opcode) + { + case RSX_SCA_OPCODE_NOP: break; + case RSX_SCA_OPCODE_MOV: SetDSTSca("$s"); break; + case RSX_SCA_OPCODE_RCP: SetDSTSca("(1.0 / $s)"); break; + case RSX_SCA_OPCODE_RCC: SetDSTSca("clamp(1.0 / $s, 5.42101e-20, 1.884467e19)"); break; + case RSX_SCA_OPCODE_RSQ: SetDSTSca("1.f / sqrt($s)"); break; + case RSX_SCA_OPCODE_EXP: SetDSTSca("exp($s)"); break; + case RSX_SCA_OPCODE_LOG: SetDSTSca("log($s)"); break; + case RSX_SCA_OPCODE_LIT: SetDSTSca(getFloatTypeName(4) + "(1.0, $s.x, ($s.x > 0.0 ? exp($s.w * log2($s.y)) : 0.0), 1.0)"); break; + case RSX_SCA_OPCODE_BRA: + { + AddCode("$if ($cond)"); + AddCode("{"); + m_cur_instr->open_scopes++; + AddCode("jump_position = $a$am;"); + AddCode("continue;"); + m_cur_instr->close_scopes++; + AddCode("}"); + } + break; + /* This triggers opengl driver lost connection error code 7 + case RSX_SCA_OPCODE_BRI: // works differently (BRI o[1].x(TR) L0;) + { + uint jump_position; + + if (is_has_BRA) + { + jump_position = GetAddr(); + } + else + { + int addr = GetAddr(); + + jump_position = 0; + for (auto pos : m_jump_lvls) + { + if (addr == pos) + break; + + ++jump_position; + } + } + + AddCode("$ifcond "); + AddCode("{"); + m_cur_instr->open_scopes++; + AddCode(fmt::Format("jump_position = %u;", jump_position)); + AddCode("continue;"); + m_cur_instr->close_scopes++; + AddCode("}"); + } + break; + */ + case RSX_SCA_OPCODE_CAL: + // works same as BRI + AddCode("$ifcond $f(); //CAL"); + break; + case RSX_SCA_OPCODE_CLI: + // works same as BRI + AddCode("$ifcond $f(); //CLI"); + break; + case RSX_SCA_OPCODE_RET: + // works like BRI but shorter (RET o[1].x(TR);) + AddCode("$ifcond return;"); + break; + case RSX_SCA_OPCODE_LG2: SetDSTSca("log2($s)"); break; + case RSX_SCA_OPCODE_EX2: SetDSTSca("exp2($s)"); break; + case RSX_SCA_OPCODE_SIN: SetDSTSca("sin($s)"); break; + case RSX_SCA_OPCODE_COS: SetDSTSca("cos($s)"); break; + case RSX_SCA_OPCODE_BRB: + // works differently (BRB o[1].x !b0, L0;) + LOG_ERROR(RSX, "Unimplemented sca_opcode BRB"); + break; + case RSX_SCA_OPCODE_CLB: break; + // works same as BRB + LOG_ERROR(RSX, "Unimplemented sca_opcode CLB"); + break; + case RSX_SCA_OPCODE_PSH: break; + // works differently (PSH o[1].x A0;) + LOG_ERROR(RSX, "Unimplemented sca_opcode PSH"); + break; + case RSX_SCA_OPCODE_POP: break; + // works differently (POP o[1].x;) + LOG_ERROR(RSX, "Unimplemented sca_opcode POP"); + break; + + default: + AddCode(fmt::Format("//Unknown vp sca_opcode 0x%x", fmt::by_value(d1.sca_opcode))); + LOG_ERROR(RSX, "Unknown vp sca_opcode 0x%x", fmt::by_value(d1.sca_opcode)); + Emu.Pause(); + break; + } + + switch (d1.vec_opcode) + { + case RSX_VEC_OPCODE_NOP: break; + case RSX_VEC_OPCODE_MOV: SetDSTVec("$0"); break; + case RSX_VEC_OPCODE_MUL: SetDSTVec("($0 * $1)"); break; + case RSX_VEC_OPCODE_ADD: SetDSTVec("($0 + $2)"); break; + case RSX_VEC_OPCODE_MAD: SetDSTVec("($0 * $1 + $2)"); break; + case RSX_VEC_OPCODE_DP3: SetDSTVec(getFunction(FUNCTION::FUNCTION_DP3)); break; + case RSX_VEC_OPCODE_DPH: SetDSTVec("dot(float4($0.xyz, 1.0), $1).xxxx"); break; + case RSX_VEC_OPCODE_DP4: SetDSTVec(getFunction(FUNCTION::FUNCTION_DP4)); break; + case RSX_VEC_OPCODE_DST: SetDSTVec("vec4(distance($0, $1))"); break; + case RSX_VEC_OPCODE_MIN: SetDSTVec("min($0, $1)"); break; + case RSX_VEC_OPCODE_MAX: SetDSTVec("max($0, $1)"); break; + case RSX_VEC_OPCODE_SLT: SetDSTVec("vec4(lessThan($0, $1))"); break; + case RSX_VEC_OPCODE_SGE: SetDSTVec("vec4(greaterThanEqual($0, $1))"); break; + case RSX_VEC_OPCODE_ARL: AddCode("$ifcond $a = ivec4($0)$am;"); break; + case RSX_VEC_OPCODE_FRC: SetDSTVec(getFunction(FUNCTION::FUNCTION_FRACT)); break; + case RSX_VEC_OPCODE_FLR: SetDSTVec("floor($0)"); break; + case RSX_VEC_OPCODE_SEQ: SetDSTVec("vec4(equal($0, $1))"); break; + case RSX_VEC_OPCODE_SFL: SetDSTVec("vec4(equal($0, vec4(0.0)))"); break; + case RSX_VEC_OPCODE_SGT: SetDSTVec("vec4(greaterThan($0, $1))"); break; + case RSX_VEC_OPCODE_SLE: SetDSTVec("vec4(lessThanEqual($0, $1))"); break; + case RSX_VEC_OPCODE_SNE: SetDSTVec("vec4(notEqual($0, $1))"); break; + case RSX_VEC_OPCODE_STR: SetDSTVec("vec4(equal($0, vec4(1.0)))"); break; + case RSX_VEC_OPCODE_SSG: SetDSTVec("sign($0)"); break; + case RSX_VEC_OPCODE_TXL: SetDSTVec("texture($t, $0.xy)"); break; + + default: + AddCode(fmt::Format("//Unknown vp opcode 0x%x", fmt::by_value(d1.vec_opcode))); + LOG_ERROR(RSX, "Unknown vp opcode 0x%x", fmt::by_value(d1.vec_opcode)); + Emu.Pause(); + break; + } + } + + if (is_has_BRA || !m_jump_lvls.empty()) + { + m_cur_instr = &m_instructions[m_instr_count - 1]; + m_cur_instr->close_scopes++; + AddCode("}"); + AddCode("break;"); + m_cur_instr->close_scopes++; + AddCode("}"); + } + + std::string result = BuildCode(); + + m_jump_lvls.clear(); + m_body.clear(); + if (m_funcs.size() > 2) + { + m_funcs.erase(m_funcs.begin() + 2, m_funcs.end()); + } + return result; +} \ No newline at end of file diff --git a/rpcs3/Emu/RSX/Common/VertexProgramDecompiler.h b/rpcs3/Emu/RSX/Common/VertexProgramDecompiler.h new file mode 100644 index 0000000000..c34e2c14a0 --- /dev/null +++ b/rpcs3/Emu/RSX/Common/VertexProgramDecompiler.h @@ -0,0 +1,118 @@ +#pragma once +#include "Emu/RSX/RSXVertexProgram.h" +#include +#include +#include "ShaderParam.h" + +/** +* This class is used to translate RSX Vertex program to GLSL/HLSL code +* Backend with text based shader can subclass this class and implement : +* - virtual std::string getFloatTypeName(size_t elementCount) = 0; +* - virtual std::string getFunction(enum class FUNCTION) = 0; +* - virtual std::string compareFunction(enum class COMPARE, const std::string &, const std::string &) = 0; +* - virtual void insertHeader(std::stringstream &OS) = 0; +* - virtual void insertIntputs(std::stringstream &OS) = 0; +* - virtual void insertOutputs(std::stringstream &OS) = 0; +* - virtual void insertConstants(std::stringstream &OS) = 0; +* - virtual void insertMainStart(std::stringstream &OS) = 0; +* - virtual void insertMainEnd(std::stringstream &OS) = 0; +*/ +struct VertexProgramDecompiler +{ + struct FuncInfo + { + u32 offset; + std::string name; + }; + + struct Instruction + { + std::vector body; + int open_scopes; + int close_scopes; + int put_close_scopes; + int do_count; + + void reset() + { + body.clear(); + put_close_scopes = open_scopes = close_scopes = do_count = 0; + } + }; + + static const size_t m_max_instr_count = 512; + Instruction m_instructions[m_max_instr_count]; + Instruction* m_cur_instr; + size_t m_instr_count; + + std::set m_jump_lvls; + std::vector m_body; + std::vector m_funcs; + + //wxString main; + + std::vector& m_data; + ParamArray m_parr; + + std::string GetMask(bool is_sca); + std::string GetVecMask(); + std::string GetScaMask(); + std::string GetDST(bool is_sca = false); + std::string GetSRC(const u32 n); + std::string GetFunc(); + std::string GetTex(); + std::string GetCond(); + std::string AddAddrMask(); + std::string AddAddrReg(); + u32 GetAddr(); + std::string Format(const std::string& code); + + void AddCodeCond(const std::string& dst, const std::string& src); + void AddCode(const std::string& code); + void SetDST(bool is_sca, std::string value); + void SetDSTVec(const std::string& code); + void SetDSTSca(const std::string& code); + std::string BuildFuncBody(const FuncInfo& func); + std::string BuildCode(); + +protected: + /** returns the type name of float vectors. + */ + virtual std::string getFloatTypeName(size_t elementCount) = 0; + + /** returns string calling function where arguments are passed via + * $0 $1 $2 substring. + */ + virtual std::string getFunction(FUNCTION) = 0; + + /** returns string calling comparaison function on 2 args passed as strings. + */ + virtual std::string compareFunction(COMPARE, const std::string &, const std::string &) = 0; + + /** Insert header of shader file (eg #version, "system constants"...) + */ + virtual void insertHeader(std::stringstream &OS) = 0; + + /** Insert vertex declaration. + */ + virtual void insertInputs(std::stringstream &OS, const std::vector &inputs) = 0; + + /** insert global declaration of vertex shader outputs. + */ + virtual void insertConstants(std::stringstream &OS, const std::vector &constants) = 0; + + /** insert declaration of shader constants. + */ + virtual void insertOutputs(std::stringstream &OS, const std::vector &outputs) = 0; + + /** insert beginning of main (signature, temporary declaration...) + */ + virtual void insertMainStart(std::stringstream &OS) = 0; + + /** insert end of main function (return value, output copy...) + */ + virtual void insertMainEnd(std::stringstream &OS) = 0; +public: + VertexProgramDecompiler(std::vector& data); + std::string Decompile(); +}; \ No newline at end of file diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 59136ac526..030d9c7e45 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -41,6 +41,9 @@ + + + @@ -416,7 +419,10 @@ + + + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 3b8b20c764..6f4766907f 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -866,6 +866,15 @@ Utilities + + Emu\GPU\RSX\Common + + + Emu\GPU\RSX\Common + + + Emu\GPU\RSX\Common + @@ -1555,5 +1564,14 @@ Emu\GPU\RSX\Common + + Emu\GPU\RSX\Common + + + Emu\GPU\RSX\Common + + + Emu\GPU\RSX\Common + \ No newline at end of file