diff --git a/rpcs3/Emu/RSX/CgBinaryProgram.h b/rpcs3/Emu/RSX/CgBinaryProgram.h index ff64e96e94..185c0002d6 100644 --- a/rpcs3/Emu/RSX/CgBinaryProgram.h +++ b/rpcs3/Emu/RSX/CgBinaryProgram.h @@ -273,7 +273,7 @@ public: void BuildShaderBody() { - GLParamArray param_array; + ParamArray param_array; auto& prog = GetCgRef(0); diff --git a/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.cpp b/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.cpp new file mode 100644 index 0000000000..eeefd1d3b9 --- /dev/null +++ b/rpcs3/Emu/RSX/Common/FragmentProgramDecompiler.cpp @@ -0,0 +1,593 @@ +#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 / $0"); break; + case RSX_FP_OPCODE_RSQ: SetDst("1.f / sqrt($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(getFloatTypeName(4) + "(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(getFloatTypeName(4) + "(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(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)"); + if (src2.end_offset != src1.else_offset) + 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; +} 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..ae30e4bf69 --- /dev/null +++ b/rpcs3/Emu/RSX/Common/ShaderParam.h @@ -0,0 +1,215 @@ +#pragma once +#include +#include + +enum class FUNCTION { + FUNCTION_DP2, + FUNCTION_DP2A, + FUNCTION_DP3, + FUNCTION_DP4, + FUNCTION_DPH, + 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..5505f4cde9 --- /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(getFunction(FUNCTION::FUNCTION_DPH)); 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(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLT, "$0", "$1") + ")"); break; + case RSX_VEC_OPCODE_SGE: SetDSTVec(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGE, "$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(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SEQ, "$0", "$1") + ")"); break; + case RSX_VEC_OPCODE_SFL: SetDSTVec(getFunction(FUNCTION::FUNCTION_SFL)); break; + case RSX_VEC_OPCODE_SGT: SetDSTVec(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SGT, "$0", "$1") + ")"); break; + case RSX_VEC_OPCODE_SLE: SetDSTVec(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SLE, "$0", "$1") + ")"); break; + case RSX_VEC_OPCODE_SNE: SetDSTVec(getFloatTypeName(4) + "(" + compareFunction(COMPARE::FUNCTION_SNE, "$0", "$1") + ")"); break; + case RSX_VEC_OPCODE_STR: SetDSTVec(getFunction(FUNCTION::FUNCTION_STR)); 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; +} 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/Emu/RSX/GL/GLCommonDecompiler.cpp b/rpcs3/Emu/RSX/GL/GLCommonDecompiler.cpp new file mode 100644 index 0000000000..808646b4d8 --- /dev/null +++ b/rpcs3/Emu/RSX/GL/GLCommonDecompiler.cpp @@ -0,0 +1,69 @@ +#include "stdafx.h" +#include "GLCommonDecompiler.h" + +std::string getFloatTypeNameImpl(size_t elementCount) +{ + switch (elementCount) + { + default: + abort(); + case 1: + return "float"; + case 2: + return "vec2"; + case 3: + return "vec3"; + case 4: + return "vec4"; + } +} + +std::string getFunctionImpl(FUNCTION f) +{ + switch (f) + { + default: + abort(); + case FUNCTION::FUNCTION_DP2: + return "vec4(dot($0.xy, $1.xy))"; + case FUNCTION::FUNCTION_DP2A: + return ""; + case FUNCTION::FUNCTION_DP3: + return "vec4(dot($0.xyz, $1.xyz))"; + case FUNCTION::FUNCTION_DP4: + return "vec4(dot($0, $1))"; + case FUNCTION::FUNCTION_DPH: + return "vec4(dot(vec4($0.xyz, 1.0), $1))"; + case FUNCTION::FUNCTION_SFL: + return "vec4(0., 0., 0., 0.)"; + case FUNCTION::FUNCTION_STR: + return "vec4(1., 1., 1., 1.)"; + case FUNCTION::FUNCTION_FRACT: + return "fract($0)"; + case FUNCTION::FUNCTION_TEXTURE_SAMPLE: + return "texture($t, $0.xy)"; + case FUNCTION::FUNCTION_DFDX: + return "dFdx($0)"; + case FUNCTION::FUNCTION_DFDY: + return "dFdy($0)"; + } +} + +std::string compareFunctionImpl(COMPARE f, const std::string &Op0, const std::string &Op1) +{ + switch (f) + { + case COMPARE::FUNCTION_SEQ: + return "equal(" + Op0 + ", " + Op1 + ")"; + case COMPARE::FUNCTION_SGE: + return "greaterThanEqual(" + Op0 + ", " + Op1 + ")"; + case COMPARE::FUNCTION_SGT: + return "greaterThan(" + Op0 + ", " + Op1 + ")"; + case COMPARE::FUNCTION_SLE: + return "lessThanEqual(" + Op0 + ", " + Op1 + ")"; + case COMPARE::FUNCTION_SLT: + return "lessThan(" + Op0 + ", " + Op1 + ")"; + case COMPARE::FUNCTION_SNE: + return "notEqual(" + Op0 + ", " + Op1 + ")"; + } +} \ No newline at end of file diff --git a/rpcs3/Emu/RSX/GL/GLCommonDecompiler.h b/rpcs3/Emu/RSX/GL/GLCommonDecompiler.h new file mode 100644 index 0000000000..6ae1d5065a --- /dev/null +++ b/rpcs3/Emu/RSX/GL/GLCommonDecompiler.h @@ -0,0 +1,6 @@ +#pragma once +#include "../Common/ShaderParam.h" + +std::string getFloatTypeNameImpl(size_t elementCount); +std::string getFunctionImpl(FUNCTION f); +std::string compareFunctionImpl(COMPARE f, const std::string &Op0, const std::string &Op1); \ No newline at end of file diff --git a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp index f203f3be05..de5a403275 100644 --- a/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLFragmentProgram.cpp @@ -4,332 +4,44 @@ #include "Emu/System.h" #include "GLFragmentProgram.h" -void GLFragmentDecompilerThread::SetDst(std::string code, bool append_mask) +#include "GLCommonDecompiler.h" + +std::string GLFragmentDecompilerThread::getFloatTypeName(size_t elementCount) { - if (!src0.exec_if_eq && !src0.exec_if_gr && !src0.exec_if_lt) return; + return getFloatTypeNameImpl(elementCount); +} - switch (src1.scale) +std::string GLFragmentDecompilerThread::getFunction(FUNCTION f) +{ + return getFunctionImpl(f); +} + +std::string GLFragmentDecompilerThread::saturate(const std::string & code) +{ + return "clamp(" + code + ", 0., 1.)"; +} + +std::string GLFragmentDecompilerThread::compareFunction(COMPARE f, const std::string &Op0, const std::string &Op1) +{ + return compareFunctionImpl(f, Op0, Op1); +} + +void GLFragmentDecompilerThread::insertHeader(std::stringstream & OS) +{ + OS << "#version 420" << std::endl; +} + +void GLFragmentDecompilerThread::insertIntputs(std::stringstream & OS) +{ + for (ParamType PT : m_parr.params[PF_PARAM_IN]) { - 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 = "clamp(" + code + ", 0.0, 1.0)"; - } - - code += (append_mask ? "$m" : ""); - - if (dst.no_dest) - { - if (dst.set_cond) - { - AddCode("$ifcond " + m_parr.AddParam(PARAM_NONE, "vec4", "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(PARAM_NONE, "vec4", "cc" + std::to_string(src0.cond_mod_reg_index)) + "$m = " + dest + ";"); + for (ParamItem PI : PT.items) + OS << "in " << PT.type << " " << PI.name << ";" << std::endl; } } -void GLFragmentDecompilerThread::AddCode(const std::string& code) +void GLFragmentDecompilerThread::insertOutputs(std::stringstream & OS) { - main.append(m_code_level, '\t') += Format(code) + "\n"; -} - -std::string GLFragmentDecompilerThread::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 GLFragmentDecompilerThread::AddReg(u32 index, int fp16) -{ - return m_parr.AddParam(PARAM_NONE, "vec4", std::string(fp16 ? "h" : "r") + std::to_string(index), "vec4(0.0)"); -} - -bool GLFragmentDecompilerThread::HasReg(u32 index, int fp16) -{ - return m_parr.HasParam(PARAM_NONE, "vec4", - std::string(fp16 ? "h" : "r") + std::to_string(index)); -} - -std::string GLFragmentDecompilerThread::AddCond() -{ - return m_parr.AddParam(PARAM_NONE, "vec4", "cc" + std::to_string(src0.cond_reg_index)); -} - -std::string GLFragmentDecompilerThread::AddConst() -{ - std::string name = std::string("fc") + std::to_string(m_size + 4 * 4); - if (m_parr.HasParam(PARAM_UNIFORM, "vec4", name)) - { - return name; - } - - auto data = vm::ptr::make(m_addr + m_size + 4 * sizeof(u32)); - - m_offset = 2 * 4 * sizeof(u32); - u32 x = 0;//GetData(data[0]); - u32 y = 0;//GetData(data[1]); - u32 z = 0;//GetData(data[2]); - u32 w = 0;//GetData(data[3]); - return m_parr.AddParam(PARAM_UNIFORM, "vec4", name, - std::string("vec4(") + std::to_string((float&)x) + ", " + std::to_string((float&)y) - + ", " + std::to_string((float&)z) + ", " + std::to_string((float&)w) + ")"); -} - -std::string GLFragmentDecompilerThread::AddTex() -{ - return m_parr.AddParam(PARAM_UNIFORM, "sampler2D", std::string("tex") + std::to_string(dst.tex_num)); -} - -std::string GLFragmentDecompilerThread::Format(const std::string& code) -{ - const std::pair> repl_list[] = - { - { "$$", []() -> std::string { return "$"; } }, - { "$0", [this]{ return GetSRC(src0); } }, - { "$1", [this]{ return GetSRC(src1); } }, - { "$2", [this]{ return GetSRC(src2); } }, - { "$t", [this]{ return AddTex(); } }, - { "$m", [this]{ return GetMask(); } }, - - { "$ifcond ", [this]() -> std::string - { - const std::string& cond = GetCond(); - if (cond == "true") return ""; - return "if(" + cond + ") "; - } - }, - - { "$cond", [this]{ return GetCond(); } }, - { "$c", [this]{ return AddConst(); } } - }; - - return fmt::replace_all(code, repl_list); -} - -std::string GLFragmentDecompilerThread::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 = "greaterThanEqual"; - } - else if (src0.exec_if_lt && src0.exec_if_eq) - { - cond = "lessThanEqual"; - } - else if (src0.exec_if_gr && src0.exec_if_lt) - { - cond = "notEqual"; - } - else if (src0.exec_if_gr) - { - cond = "greaterThan"; - } - else if (src0.exec_if_lt) - { - cond = "lessThan"; - } - else //if(src0.exec_if_eq) - { - cond = "equal"; - } - - return "any(" + cond + "(" + AddCond() + swizzle + ", vec4(0.0)))"; -} - -void GLFragmentDecompilerThread::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 = "greaterThanEqual"; - } - else if (src0.exec_if_lt && src0.exec_if_eq) - { - cond = "lessThanEqual"; - } - else if (src0.exec_if_gr && src0.exec_if_lt) - { - cond = "notEqual"; - } - else if (src0.exec_if_gr) - { - cond = "greaterThan"; - } - else if (src0.exec_if_lt) - { - cond = "lessThan"; - } - else //if(src0.exec_if_eq) - { - cond = "equal"; - } - - cond = cond + "(" + AddCond() + swizzle + ", vec4(0.0))"; - - ShaderVar dst_var(dst); - dst_var.symplify(); - - //const char *c_mask = f; - - if (dst_var.swizzles[0].length() == 1) - { - AddCode("if (" + cond + ".x) " + dst + " = vec4(" + src + ").x;"); - } - 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 GLFragmentDecompilerThread::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(PARAM_IN, "vec4", 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(PARAM_IN, "vec4", "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 GLFragmentDecompilerThread::BuildCode() -{ - //main += fmt::Format("\tgl_FragColor = %c0;\n", m_ctrl & 0x40 ? 'r' : 'h'); const std::pair table[] = { { "ocol0", m_ctrl & 0x40 ? "r0" : "h0" }, @@ -340,280 +52,70 @@ std::string GLFragmentDecompilerThread::BuildCode() for (int i = 0; i < sizeof(table) / sizeof(*table); ++i) { - if (m_parr.HasParam(PARAM_NONE, "vec4", table[i].second)) - AddCode(m_parr.AddParam(PARAM_OUT, "vec4", table[i].first, i) + " = " + table[i].second + ";"); + if (m_parr.HasParam(PF_PARAM_NONE, "vec4", table[i].second)) + OS << "out vec4 " << table[i].first << ";" << std::endl; + } +} + +void GLFragmentDecompilerThread::insertConstants(std::stringstream & OS) +{ + for (ParamType PT : m_parr.params[PF_PARAM_UNIFORM]) + { + if (PT.type != "sampler2D") + continue; + for (ParamItem PI : PT.items) + OS << "uniform " << PT.type << " " << PI.name << ";" << std::endl; + } - if (m_ctrl & 0xe) main += m_ctrl & 0x40 ? "\tgl_FragDepth = r1.z;\n" : "\tgl_FragDepth = h2.z;\n"; + for (ParamType PT : m_parr.params[PF_PARAM_UNIFORM]) + { + if (PT.type == "sampler2D") + continue; + for (ParamItem PI : PT.items) + OS << "uniform " << PT.type << " " << PI.name << ";" << std::endl; + } +} - std::string p; +void GLFragmentDecompilerThread::insertMainStart(std::stringstream & OS) +{ + OS << "void main ()" << std::endl; + OS << "{" << std::endl; - for (auto& param : m_parr.params) { - p += param.Format(); + for (ParamType PT : m_parr.params[PF_PARAM_NONE]) + { + for (ParamItem PI : PT.items) + { + OS << " " << PT.type << " " << PI.name; + if (!PI.value.empty()) + OS << " = " << PI.value; + OS << ";" << std::endl; + } + } +} + +void GLFragmentDecompilerThread::insertMainEnd(std::stringstream & OS) +{ + const std::pair table[] = + { + { "ocol0", m_ctrl & 0x40 ? "r0" : "h0" }, + { "ocol1", m_ctrl & 0x40 ? "r2" : "h4" }, + { "ocol2", m_ctrl & 0x40 ? "r3" : "h6" }, + { "ocol3", m_ctrl & 0x40 ? "r4" : "h8" }, + }; + + for (int i = 0; i < sizeof(table) / sizeof(*table); ++i) + { + if (m_parr.HasParam(PF_PARAM_NONE, "vec4", table[i].second)) + OS << " " << table[i].first << " = " << table[i].second << ";" << std::endl; } - return std::string("#version 420\n" - "\n" - + p + "\n" - "void main()\n{\n" + main + "}\n"); + OS << "};" << std::endl; } void GLFragmentDecompilerThread::Task() { - 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 found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size); - found != m_end_offsets.end(); - found = std::find(m_end_offsets.begin(), m_end_offsets.end(), m_size)) - { - m_end_offsets.erase(found); - m_code_level--; - AddCode("}"); - m_loop_count--; - } - - for (auto found = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size); - found != m_else_offsets.end(); - found = std::find(m_else_offsets.begin(), m_else_offsets.end(), m_size)) - { - m_else_offsets.erase(found); - 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("vec4(dot($0.xy, $1.xy))"); break; - case RSX_FP_OPCODE_DP3: SetDst("vec4(dot($0.xyz, $1.xyz))"); break; - case RSX_FP_OPCODE_DP4: SetDst("vec4(dot($0, $1))"); break; - case RSX_FP_OPCODE_DP2A: SetDst("vec4($0.x * $1.x + $0.y * $1.y + $2.x)"); 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("vec4(equal($0, $1))"); break; - case RSX_FP_OPCODE_SFL: SetDst("vec4(0.0)"); break; - case RSX_FP_OPCODE_SGE: SetDst("vec4(greaterThanEqual($0, $1))"); break; - case RSX_FP_OPCODE_SGT: SetDst("vec4(greaterThan($0, $1))"); break; - case RSX_FP_OPCODE_SLE: SetDst("vec4(lessThanEqual($0, $1))"); break; - case RSX_FP_OPCODE_SLT: SetDst("vec4(lessThan($0, $1))"); break; - case RSX_FP_OPCODE_SNE: SetDst("vec4(notEqual($0, $1))"); break; - case RSX_FP_OPCODE_STR: SetDst("vec4(1.0)"); 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("vec4(dot($0.xy, $1.xy))"); break; - case RSX_FP_OPCODE_DP3: SetDst("vec4(dot($0.xyz, $1.xyz))"); break; - case RSX_FP_OPCODE_DP4: SetDst("vec4(dot($0, $1))"); break; - case RSX_FP_OPCODE_DP2A: SetDst("vec4($0.x * $1.x + $0.y * $1.y + $2.x)"); 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("fract($0)"); 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("vec4(equal($0, $1))"); break; - case RSX_FP_OPCODE_SFL: SetDst("vec4(0.0)"); break; - case RSX_FP_OPCODE_SGE: SetDst("vec4(greaterThanEqual($0, $1))"); break; - case RSX_FP_OPCODE_SGT: SetDst("vec4(greaterThan($0, $1))"); break; - case RSX_FP_OPCODE_SIN: SetDst("sin($0.xxxx)"); break; - case RSX_FP_OPCODE_SLE: SetDst("vec4(lessThanEqual($0, $1))"); break; - case RSX_FP_OPCODE_SLT: SetDst("vec4(lessThan($0, $1))"); break; - case RSX_FP_OPCODE_SNE: SetDst("vec4(notEqual($0, $1))"); break; - case RSX_FP_OPCODE_STR: SetDst("vec4(1.0)"); break; - - default: - return false; - } - - return true; - }; - - auto TEX_SRB = [&]() - { - switch (opcode) - { - case RSX_FP_OPCODE_DDX: SetDst("dFdx($0)"); break; - case RSX_FP_OPCODE_DDY: SetDst("dFdy($0)"); 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("texture($t, $0.xy)"); 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)"); - if (src2.end_offset != src1.else_offset) - 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; - m_shader = BuildCode(); - main.clear(); -// m_parr.params.clear(); + m_shader = Decompile(); } GLFragmentProgram::GLFragmentProgram() @@ -651,6 +153,14 @@ void GLFragmentProgram::Decompile(RSXFragmentProgram& prog) { GLFragmentDecompilerThread decompiler(shader, parr, prog.addr, prog.size, prog.ctrl); decompiler.Task(); + for (const ParamType& PT : decompiler.m_parr.params[PF_PARAM_UNIFORM]) + { + for (const ParamItem PI : PT.items) + { + size_t offset = atoi(PI.name.c_str() + 2); + FragmentConstantOffsetCache.push_back(offset); + } + } } void GLFragmentProgram::DecompileAsync(RSXFragmentProgram& prog) @@ -707,25 +217,10 @@ void GLFragmentProgram::Compile() LOG_NOTICE(RSX, shader.c_str()); // Log the text of the shader that failed to compile Emu.Pause(); // Pause the emulator, we can't really continue from here } - for (const GLParamType& PT : parr.params) - { - if (PT.flag != PARAM_UNIFORM) continue; - for (const GLParamItem PI : PT.items) - { - size_t offset = atoi(PI.name.c_str() + 2); - FragmentConstantOffsetCache.push_back(offset); - } - } } void GLFragmentProgram::Delete() { - for (auto& param : parr.params) { - param.items.clear(); - param.type.clear(); - } - - parr.params.clear(); shader.clear(); if (id) diff --git a/rpcs3/Emu/RSX/GL/GLFragmentProgram.h b/rpcs3/Emu/RSX/GL/GLFragmentProgram.h index 3430e85a39..7b16b5974d 100644 --- a/rpcs3/Emu/RSX/GL/GLFragmentProgram.h +++ b/rpcs3/Emu/RSX/GL/GLFragmentProgram.h @@ -1,56 +1,34 @@ #pragma once -#include "GLShaderParam.h" +#include "../Common/FragmentProgramDecompiler.h" #include "Emu/RSX/RSXFragmentProgram.h" #include "Utilities/Thread.h" +#include "OpenGL.h" -struct GLFragmentDecompilerThread : public ThreadBase +struct GLFragmentDecompilerThread : public ThreadBase, public FragmentProgramDecompiler { - std::string main; std::string& m_shader; - GLParamArray& m_parr; - u32 m_addr; - u32& m_size; - u32 m_const_index; - u32 m_offset; - u32 m_location; - u32 m_ctrl; - u32 m_loop_count; - int m_code_level; - std::vector m_end_offsets; - std::vector m_else_offsets; - - GLFragmentDecompilerThread(std::string& shader, GLParamArray& parr, u32 addr, u32& size, u32 ctrl) - : ThreadBase("Fragment Shader Decompiler Thread") + ParamArray& m_parrDummy; +public: + GLFragmentDecompilerThread(std::string& shader, ParamArray& parr, u32 addr, u32& size, u32 ctrl) + : ThreadBase("Fragment Shader Decompiler Thread"), FragmentProgramDecompiler(addr, size, ctrl) , m_shader(shader) - , m_parr(parr) - , m_addr(addr) - , m_size(size) - , m_const_index(0) - , m_location(0) - , m_ctrl(ctrl) - { - m_size = 0; - } + , m_parrDummy(parr) + {} - std::string GetMask(); + void Task(); - 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); +protected: + virtual std::string getFloatTypeName(size_t elementCount) override; + virtual std::string getFunction(FUNCTION) override; + virtual std::string saturate(const std::string &code) override; + virtual std::string compareFunction(COMPARE, const std::string&, const std::string&) override; - void AddCodeCond(const std::string& dst, const std::string& src); - std::string GetCond(); - template std::string GetSRC(T src); - std::string BuildCode(); - - virtual void Task(); - - u32 GetData(const u32 d) const { return d << 16 | d >> 16; } + virtual void insertHeader(std::stringstream &OS) override; + virtual void insertIntputs(std::stringstream &OS) override; + virtual void insertOutputs(std::stringstream &OS) override; + virtual void insertConstants(std::stringstream &OS) override; + virtual void insertMainStart(std::stringstream &OS) override; + virtual void insertMainEnd(std::stringstream &OS) override; }; /** Storage for an Fragment Program in the process of of recompilation. @@ -62,7 +40,7 @@ public: GLFragmentProgram(); ~GLFragmentProgram(); - GLParamArray parr; + ParamArray parr; u32 id; std::string shader; std::vector FragmentConstantOffsetCache; diff --git a/rpcs3/Emu/RSX/GL/GLShaderParam.h b/rpcs3/Emu/RSX/GL/GLShaderParam.h deleted file mode 100644 index c30e283ab7..0000000000 --- a/rpcs3/Emu/RSX/GL/GLShaderParam.h +++ /dev/null @@ -1,228 +0,0 @@ -#pragma once -#include "OpenGL.h" - -enum GLParamFlag -{ - PARAM_IN, - PARAM_OUT, - PARAM_UNIFORM, - PARAM_CONST, - PARAM_NONE, -}; - -struct GLParamItem -{ - std::string name; - std::string location; - std::string value; - - GLParamItem(const std::string& _name, int _location, const std::string& _value = "") - : name(_name) - , value(_value) - { - if (_location > -1) - location = "layout (location = " + std::to_string(_location) + ") "; - else - location = ""; - } -}; - -struct GLParamType -{ - const GLParamFlag flag; - std::string type; - std::vector items; - - GLParamType(const GLParamFlag _flag, const std::string& _type) - : flag(_flag) - , type(_type) - { - } - - bool SearchName(const std::string& name) - { - for (u32 i = 0; i params; - - GLParamType* SearchParam(const std::string& type) - { - for (u32 i = 0; iSearchName(name); - } - - std::string AddParam(const GLParamFlag flag, std::string type, const std::string& name, const std::string& value) - { - type = GetParamFlag(flag) + type; - GLParamType* t = SearchParam(type); - - if (t) - { - if (!t->SearchName(name)) t->items.emplace_back(name, -1, value); - } - else - { - const u32 num = params.size(); - params.emplace_back(flag, type); - params[num].items.emplace_back(name, -1, value); - } - - return name; - } - - std::string AddParam(const GLParamFlag flag, std::string type, const std::string& name, int location = -1) - { - type = GetParamFlag(flag) + type; - GLParamType* t = SearchParam(type); - - if (t) - { - if (!t->SearchName(name)) t->items.emplace_back(name, location); - } - else - { - const u32 num = params.size(); - params.emplace_back(flag, type); - params[num].items.emplace_back(name, location); - } - - return name; - } -}; - -class ShaderVar -{ -public: - std::string name; - std::vector swizzles; - - ShaderVar() = default; - ShaderVar(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()); - } - } - - int get_vector_size() const - { - return swizzles[swizzles.size() - 1].length(); - } - - ShaderVar& 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/GL/GLVertexProgram.cpp b/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp index 291fc4d6e7..bba7fc2677 100644 --- a/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp +++ b/rpcs3/Emu/RSX/GL/GLVertexProgram.cpp @@ -3,766 +3,126 @@ #include "Emu/System.h" #include "GLVertexProgram.h" +#include "GLCommonDecompiler.h" -std::string GLVertexDecompilerThread::GetMask(bool is_sca) +std::string GLVertexDecompilerThread::getFloatTypeName(size_t elementCount) { - 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); + return getFloatTypeNameImpl(elementCount); } -std::string GLVertexDecompilerThread::GetVecMask() +std::string GLVertexDecompilerThread::getFunction(FUNCTION f) { - return GetMask(false); + return getFunctionImpl(f); } -std::string GLVertexDecompilerThread::GetScaMask() +std::string GLVertexDecompilerThread::compareFunction(COMPARE f, const std::string &Op0, const std::string &Op1) { - return GetMask(true); + return compareFunctionImpl(f, Op0, Op1); } -std::string GLVertexDecompilerThread::GetDST(bool isSca) +void GLVertexDecompilerThread::insertHeader(std::stringstream &OS) { - std::string ret; - - switch (isSca ? 0x1f : d3.dst) - { - case 0x1f: - ret += m_parr.AddParam(PARAM_NONE, "vec4", 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(PARAM_NONE, "vec4", std::string("dst_reg") + std::to_string(d3.dst), d3.dst == 0 ? "vec4(0.0f, 0.0f, 0.0f, 1.0f)" : "vec4(0.0)"); - break; - } - - return ret; + OS << "#version 420" << std::endl << std::endl; + OS << "uniform mat4 scaleOffsetMat = mat4(1.0);" << std::endl; } -std::string GLVertexDecompilerThread::GetSRC(const u32 n) +void GLVertexDecompilerThread::insertInputs(std::stringstream & OS, const std::vector& inputs) { - static const std::string reg_table[] = + for (const ParamType PT : inputs) { - "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(PARAM_NONE, "vec4", "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(PARAM_IN, "vec4", 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(PARAM_IN, "vec4", "in_unk", d1.input_src); - } - break; - case 3: //const - m_parr.AddParam(PARAM_UNIFORM, "vec4", 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 GLVertexDecompilerThread::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(PARAM_NONE, "vec4", "cc" + std::to_string(d0.cond_reg_sel_1), "vec4(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 GLVertexDecompilerThread::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 GLVertexDecompilerThread::GetTex() -{ - return m_parr.AddParam(PARAM_UNIFORM, "sampler2D", std::string("vtex") + std::to_string(/*?.tex_num*/0)); -} - -std::string GLVertexDecompilerThread::Format(const std::string& code) -{ - const std::pair> repl_list[] = - { - { "$$", []() -> std::string { return "$"; } }, - { "$0", [this]{ return GetSRC(0); } }, - { "$1", [this]{ return GetSRC(1); } }, - { "$2", [this]{ return GetSRC(2); } }, - { "$s", [this]{ return GetSRC(2); } }, - { "$am", [this]{ return AddAddrMask(); } }, - { "$a", [this]{ return AddAddrReg(); } }, - { "$t", [this]{ return GetTex(); } }, - { "$fa", [this]{ return std::to_string(GetAddr()); } }, - { "$f()", [this]{ return GetFunc(); } }, - - { "$ifcond ", [this]() -> std::string - { - const std::string& cond = GetCond(); - if (cond == "true") return ""; - return "if(" + cond + ") "; - } - }, - - { "$cond", [this]{ return GetCond(); } } - }; - - return fmt::replace_all(code, repl_list); -} - -std::string GLVertexDecompilerThread::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 GLVertexDecompilerThread::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 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; - - std::string cond = fmt::Format("%s(cc%d%s, vec4(0.0))", cond_string_table[d0.cond], d0.cond_reg_sel_1, swizzle.c_str()); - - ShaderVar dst_var(dst); - dst_var.symplify(); - - //const char *c_mask = f; - - if (dst_var.swizzles[0].length() == 1) - { - AddCode("if (" + cond + ".x) " + dst + " = vec4(" + src + ").x;"); - } - else - { - for (int i = 0; i < dst_var.swizzles[0].length(); ++i) - { - AddCode("if (" + cond + "." + f[i] + ") " + dst + "." + f[i] + " = " + src + "." + f[i] + ";"); - } + for (const ParamItem &PI : PT.items) + OS << "layout(location = " << PI.location << ") in " << PT.type << " " << PI.name << ";" << std::endl; } } - -std::string GLVertexDecompilerThread::AddAddrMask() +void GLVertexDecompilerThread::insertConstants(std::stringstream & OS, const std::vector & constants) { - static const char f[] = { 'x', 'y', 'z', 'w' }; - return std::string(".") + f[d0.addr_swz]; -} - -std::string GLVertexDecompilerThread::AddAddrReg() -{ - static const char f[] = { 'x', 'y', 'z', 'w' }; - return m_parr.AddParam(PARAM_NONE, "ivec4", "a" + std::to_string(d0.addr_reg_sel_1), "ivec4(0)") + AddAddrMask(); -} - -u32 GLVertexDecompilerThread::GetAddr() -{ - return (d2.iaddrh << 3) | d3.iaddrl; -} - -void GLVertexDecompilerThread::AddCode(const std::string& code) -{ - m_body.push_back(Format(code) + ";"); - m_cur_instr->body.push_back(Format(code)); -} - -void GLVertexDecompilerThread::SetDSTVec(const std::string& code) -{ - SetDST(false, code); -} - -void GLVertexDecompilerThread::SetDSTSca(const std::string& code) -{ - SetDST(true, code); -} - -std::string GLVertexDecompilerThread::BuildFuncBody(const FuncInfo& func) -{ - std::string result; - - for (uint i = func.offset; i & outputs) +{ for (auto &i : reg_table) { - if (m_parr.HasParam(PARAM_NONE, "vec4", i.src_reg)) - { - if (i.need_declare) - { - m_parr.AddParam(PARAM_OUT, "vec4", i.name); - } - - if (i.need_cast) - { - f += "\t" + i.name + " = vec4(" + i.src_reg + i.src_reg_mask + ");\n"; - } - else - { - f += "\t" + i.name + " = " + i.src_reg + i.src_reg_mask + ";\n"; - } - } + if (m_parr.HasParam(PF_PARAM_NONE, "vec4", i.src_reg) && i.need_declare) + OS << "out vec4 " << i.name << ";" << std::endl; } - - std::string p; - - for (auto& param : m_parr.params) { - p += param.Format(); - } - - std::string fp; - - for (int i = m_funcs.size() - 1; i > 0; --i) - { - fp += fmt::Format("void %s();\n", m_funcs[i].name.c_str()); - } - - f = fmt::Format("void %s()\n{\n\t%s();\n%s\tgl_Position = gl_Position * scaleOffsetMat;\n}\n", - m_funcs[0].name.c_str(), m_funcs[1].name.c_str(), f.c_str()); - - std::string main_body; - for (uint i = 0, lvl = 1; i < m_instr_count; i++) - { - lvl -= m_instructions[i].close_scopes; - if (lvl < 1) lvl = 1; - //assert(lvl >= 1); - for (uint j = 0; j < m_instructions[i].put_close_scopes; ++j) - { - --lvl; - if (lvl < 1) lvl = 1; - main_body.append(lvl, '\t') += "}\n"; - } - - for (uint 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; - } - - f += fmt::Format("\nvoid %s()\n{\n%s}\n", m_funcs[1].name.c_str(), main_body.c_str()); - - for (uint i = 2; iopen_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("inversesqrt(abs($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("vec4(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("vec4(dot($0.xyz, $1.xyz))"); break; - case RSX_VEC_OPCODE_DPH: SetDSTVec("vec4(dot(vec4($0.xyz, 1.0), $1))"); break; - case RSX_VEC_OPCODE_DP4: SetDSTVec("vec4(dot($0, $1))"); 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("fract($0)"); 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("}"); - } - - m_shader = BuildCode(); - - m_jump_lvls.clear(); - m_body.clear(); - if (m_funcs.size() > 2) - { - m_funcs.erase(m_funcs.begin() + 2, m_funcs.end()); - } + m_shader = Decompile(); } GLVertexProgram::GLVertexProgram() @@ -859,7 +219,6 @@ void GLVertexProgram::Compile() void GLVertexProgram::Delete() { - parr.params.clear(); shader.clear(); if (id) diff --git a/rpcs3/Emu/RSX/GL/GLVertexProgram.h b/rpcs3/Emu/RSX/GL/GLVertexProgram.h index 70848d93bf..d9664a9ede 100644 --- a/rpcs3/Emu/RSX/GL/GLVertexProgram.h +++ b/rpcs3/Emu/RSX/GL/GLVertexProgram.h @@ -1,83 +1,30 @@ #pragma once -#include "GLShaderParam.h" +#include "../Common/VertexProgramDecompiler.h" #include "Emu/RSX/RSXVertexProgram.h" #include "Utilities/Thread.h" -#include +#include "OpenGL.h" -struct GLVertexDecompilerThread : public ThreadBase +struct GLVertexDecompilerThread : public ThreadBase, public VertexProgramDecompiler { - struct FuncInfo + std::string &m_shader; +protected: + virtual std::string getFloatTypeName(size_t elementCount) override; + virtual std::string getFunction(FUNCTION) override; + virtual std::string compareFunction(COMPARE, const std::string&, const std::string&) override; + + virtual void insertHeader(std::stringstream &OS) override; + virtual void insertInputs(std::stringstream &OS, const std::vector &inputs) override; + virtual void insertConstants(std::stringstream &OS, const std::vector &constants) override; + virtual void insertOutputs(std::stringstream &OS, const std::vector &outputs) override; + virtual void insertMainStart(std::stringstream &OS) override; + virtual void insertMainEnd(std::stringstream &OS) override; +public: + GLVertexDecompilerThread(std::vector& data, std::string& shader, ParamArray& parr) + : ThreadBase("Vertex Shader Decompiler Thread"), VertexProgramDecompiler(data), m_shader(shader) { - 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::string& m_shader; - std::vector& m_data; - GLParamArray& m_parr; - - GLVertexDecompilerThread(std::vector& data, std::string& shader, GLParamArray& parr) - : ThreadBase("Vertex Shader Decompiler Thread") - , m_data(data) - , m_shader(shader) - , m_parr(parr) - { - 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 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(); - - virtual void Task(); + virtual void Task() override; }; class GLVertexProgram @@ -86,7 +33,7 @@ public: GLVertexProgram(); ~GLVertexProgram(); - GLParamArray parr; + ParamArray parr; u32 id; std::string shader; diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 59136ac526..70f6cb37cc 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -41,6 +41,10 @@ + + + + @@ -416,15 +420,18 @@ + + + + - diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 3b8b20c764..9adbdd03c6 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -866,6 +866,18 @@ Utilities + + Emu\GPU\RSX\Common + + + Emu\GPU\RSX\Common + + + Emu\GPU\RSX\Common + + + Emu\GPU\RSX\GL + @@ -1336,9 +1348,6 @@ Emu\GPU\RSX\GL - - Emu\GPU\RSX\GL - Emu\GPU\RSX\GL @@ -1555,5 +1564,17 @@ Emu\GPU\RSX\Common + + Emu\GPU\RSX\Common + + + Emu\GPU\RSX\Common + + + Emu\GPU\RSX\Common + + + Emu\GPU\RSX\GL + \ No newline at end of file