#include "stdafx.h" #include "GLVertexProgram.h" wxString GLVertexDecompilerThread::GetMask(bool is_sca) { wxString ret = wxEmptyString; 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.IsEmpty() || ret == "xyzw" ? wxString(wxEmptyString) : ("." + ret); } wxString GLVertexDecompilerThread::GetVecMask() { return GetMask(false); } wxString GLVertexDecompilerThread::GetScaMask() { return GetMask(true); } wxString GLVertexDecompilerThread::GetDST(bool isSca) { static const std::string reg_table[] = { "gl_Position", "col0", "col1", "bfc0", "bfc1", "gl_ClipDistance[%d]", "gl_ClipDistance[%d]", "tc0", "tc1", "tc2", "tc3", "tc4", "tc5", "tc6", "tc7" }; wxString ret = wxEmptyString; switch(isSca ? 0x1f : d3.dst) { case 0x0: case 0x5: case 0x6: ret += reg_table[d3.dst]; break; 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 < WXSIZEOF(reg_table)) { ret += m_parr.AddParam(PARAM_OUT, "vec4", reg_table[d3.dst]); } else { ConLog.Error("Bad dst reg num: %d", d3.dst); ret += m_parr.AddParam(PARAM_OUT, "vec4", "unk"); } break; } return ret; } wxString GLVertexDecompilerThread::GetSRC(const u32 n, bool isSca) { static const std::string reg_table[] = { "in_pos", "in_weight", "in_normal", "in_col0", "in_col1", "in_fogc", "in_6", "in_7", "in_tc0", "in_tc1", "in_tc2", "in_tc3", "in_tc4", "in_tc5", "in_tc6", "in_tc7" }; wxString ret = wxEmptyString; switch(src[n].reg_type) { case 1: //temp ret += m_parr.AddParam(PARAM_NONE, "vec4", std::string("tmp") + std::to_string(src[n].tmp_src)); break; case 2: //input if(d1.input_src < WXSIZEOF(reg_table)) { ret += m_parr.AddParam(PARAM_IN, "vec4", reg_table[d1.input_src], d1.input_src); } else { ConLog.Error("Bad input src num: %d", d1.input_src); ret += m_parr.AddParam(PARAM_IN, "vec4", "in_unk", d1.input_src); } break; case 3: //const ret += m_parr.AddParam(PARAM_UNIFORM, "vec4", std::string("vc") + std::to_string(d1.const_src)); break; default: ConLog.Error("Bad src%u reg type: %d", n, src[n].reg_type); Emu.Pause(); break; } static const wxString f = "xyzw"; if (isSca) { assert(src[n].swz_x == src[n].swz_y); assert(src[n].swz_z == src[n].swz_w); assert(src[n].swz_x == src[n].swz_z); ret += '.'; ret += f[src[n].swz_x]; } else { wxString swizzle = wxEmptyString; 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::AddCode(bool is_sca, wxString code, bool src_mask, bool set_dst) { if(d0.cond == 0) return; enum { lt = 0x1, eq = 0x2, gt = 0x4, }; wxString cond; if(d0.cond != (lt | gt | eq)) { if((d0.cond & (lt | gt)) == (lt | gt)) { cond = "!="; } else { if(d0.cond & lt) cond = "<"; else if(d0.cond & gt) cond = ">"; else if(d0.cond & eq) cond = "="; if(d0.cond & eq) cond += "="; } //ConLog.Error("cond! %d (%d %s %d %d)", d0.cond, d0.dst_tmp, cond, d1.input_src, d1.const_src); cond = wxString::Format("if(rc %s 0) ", cond.mb_str()); } wxString value = src_mask ? code + GetMask(is_sca) : code; if(d0.staturate) { value = "clamp(" + value + ", 0.0, 1.0)"; } if(set_dst) { wxString dest; if(d0.cond_update_enable_0) { dest = m_parr.AddParam(PARAM_NONE, "float", "rc"); } else if(d3.dst == 5 || d3.dst == 6) { int num = d3.dst == 5 ? 0 : 3; if(d3.vec_writemask_x) { ConLog.Error("Bad clip mask."); } //if(d3.vec_writemask_y) num += 0; if(d3.vec_writemask_z) num += 1; else if(d3.vec_writemask_w) num += 2; dest = wxString::Format(GetDST(is_sca), num); } else { dest = GetDST(is_sca) + GetMask(is_sca); } code = cond + dest + " = " + value; } else { code = cond + value; } m_body.Add(code + wxString::Format(";//%d %d %d %d", d0.cond_reg_sel_1, d0.cond_test_enable, d0.cond_update_enable_0, d0.cond_update_enable_1)); } wxString GLVertexDecompilerThread::GetFunc() { u32 offset = (d2.iaddrh << 3) | d3.iaddrl; wxString name = wxString::Format("func%u", offset); for(uint i=0; i=0; --i) { fp += wxString::Format("void %s();\n", m_funcs[i].name.mb_str()); } wxString f = wxEmptyString; f += wxString::Format("void %s()\n{\n\tgl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f);\n%s}\n", m_funcs[0].name.mb_str(), BuildFuncBody(m_funcs[0]).mb_str()); for(uint i=1; iIsAlive()) { m_decompiler_thread->Stop(); } delete m_decompiler_thread; m_decompiler_thread = nullptr; } Delete(); } void GLVertexProgram::Decompile(RSXVertexProgram& prog) { #if 0 GLVertexDecompilerThread(data, shader, parr).Entry(); #else if(m_decompiler_thread) { Wait(); if(m_decompiler_thread->IsAlive()) { m_decompiler_thread->Stop(); } delete m_decompiler_thread; m_decompiler_thread = nullptr; } m_decompiler_thread = new GLVertexDecompilerThread(prog.data, shader, parr); m_decompiler_thread->Start(); #endif } void GLVertexProgram::Compile() { if(id) glDeleteShader(id); id = glCreateShader(GL_VERTEX_SHADER); const char* str = shader.c_str(); const int strlen = shader.Len(); glShaderSource(id, 1, &str, &strlen); glCompileShader(id); GLint r = GL_FALSE; glGetShaderiv(id, GL_COMPILE_STATUS, &r); if(r != GL_TRUE) { glGetShaderiv(id, GL_INFO_LOG_LENGTH, &r); if(r) { char* buf = new char[r+1]; GLsizei len; memset(buf, 0, r+1); glGetShaderInfoLog(id, r, &len, buf); ConLog.Error("Failed to compile vertex shader: %s", buf); delete[] buf; } ConLog.Write(shader); Emu.Pause(); } //else ConLog.Write("Vertex shader compiled successfully!"); } void GLVertexProgram::Delete() { parr.params.Clear(); shader.Clear(); if(id) { glDeleteShader(id); id = 0; } }