#pragma once #include #include "Emu/Memory/Memory.h" #include "Emu/RSX/GL/GLVertexProgram.h" #include "Emu/RSX/GL/GLFragmentProgram.h" typedef be_t CGprofile; typedef be_t CGbool; typedef be_t CGresource; typedef be_t CGenum; typedef be_t CGtype; typedef be_t CgBinaryOffset; typedef CgBinaryOffset CgBinaryEmbeddedConstantOffset; typedef CgBinaryOffset CgBinaryFloatOffset; typedef CgBinaryOffset CgBinaryStringOffset; typedef CgBinaryOffset CgBinaryParameterOffset; // a few typedefs typedef struct CgBinaryParameter CgBinaryParameter; typedef struct CgBinaryEmbeddedConstant CgBinaryEmbeddedConstant; typedef struct CgBinaryVertexProgram CgBinaryVertexProgram; typedef struct CgBinaryFragmentProgram CgBinaryFragmentProgram; typedef struct CgBinaryProgram CgBinaryProgram; // fragment programs have their constants embedded in the microcode struct CgBinaryEmbeddedConstant { be_t ucodeCount; // occurances be_t ucodeOffset[1]; // offsets that need to be patched follow }; // describe a binary program parameter (CgParameter is opaque) struct CgBinaryParameter { CGtype type; // cgGetParameterType() CGresource res; // cgGetParameterResource() CGenum var; // cgGetParameterVariability() be_t resIndex; // cgGetParameterResourceIndex() CgBinaryStringOffset name; // cgGetParameterName() CgBinaryFloatOffset defaultValue; // default constant value CgBinaryEmbeddedConstantOffset embeddedConst; // embedded constant information CgBinaryStringOffset semantic; // cgGetParameterSemantic() CGenum direction; // cgGetParameterDirection() be_t paramno; // 0..n: cgGetParameterIndex() -1: globals CGbool isReferenced; // cgIsParameterReferenced() CGbool isShared; // cgIsParameterShared() }; // attributes needed for vshaders struct CgBinaryVertexProgram { be_t instructionCount; // #instructions be_t instructionSlot; // load address (indexed reads!) be_t registerCount; // R registers count be_t attributeInputMask; // attributes vs reads from be_t attributeOutputMask; // attributes vs writes (uses SET_VERTEX_ATTRIB_OUTPUT_MASK bits) be_t userClipMask; // user clip plane enables (for SET_USER_CLIP_PLANE_CONTROL) }; typedef enum { CgBinaryPTTNone = 0, CgBinaryPTT2x16 = 1, CgBinaryPTT1x32 = 2 } CgBinaryPartialTexType; // attributes needed for pshaders struct CgBinaryFragmentProgram { be_t instructionCount; // #instructions be_t attributeInputMask; // attributes fp reads (uses SET_VERTEX_ATTRIB_OUTPUT_MASK bits) be_t partialTexType; // texid 0..15 use two bits each marking whether the texture format requires partial load: see CgBinaryPartialTexType be_t texCoordsInputMask; // tex coords used by frag prog. (tex is bit n) be_t texCoords2D; // tex coords that are 2d (tex is bit n) be_t texCoordsCentroid; // tex coords that are centroid (tex is bit n) u8 registerCount; // R registers count u8 outputFromH0; // final color from R0 or H0 u8 depthReplace; // fp generated z epth value u8 pixelKill; // fp uses kill operations }; struct CgBinaryProgram { // vertex/pixel shader identification (BE/LE as well) CGprofile profile; // binary revision (used to verify binary and driver structs match) be_t binaryFormatRevision; // total size of this struct including profile and totalSize field be_t totalSize; // parameter usually queried using cgGet[First/Next]LeafParameter be_t parameterCount; CgBinaryParameterOffset parameterArray; // depending on profile points to a CgBinaryVertexProgram or CgBinaryFragmentProgram struct CgBinaryOffset program; // raw ucode data be_t ucodeSize; CgBinaryOffset ucode; // variable length data follows u8 data[1]; }; class CgBinaryDisasm { OPDEST dst; SRC0 src0; SRC1 src1; SRC2 src2; D0 d0; D1 d1; D2 d2; D3 d3; SRC src[3]; std::string m_path; // used for FP decompiler thread, delete this later u8* m_buffer; size_t m_buffer_size; std::string m_arb_shader; std::string m_glsl_shader; std::string m_dst_reg_name; // FP members u32 m_offset; u32 m_opcode; u32 m_step; u32 m_size; std::vector m_end_offsets; std::vector m_else_offsets; std::vector m_loop_end_offsets; // VP members u32 m_sca_opcode; u32 m_vec_opcode; static const size_t m_max_instr_count = 512; size_t m_instr_count; std::vector m_data; public: std::string GetArbShader() const { return m_arb_shader; } std::string GetGlslShader() const { return m_glsl_shader; } // FP functions std::string GetMask(); void AddCodeAsm(const std::string& code); std::string AddRegDisAsm(u32 index, int fp16); std::string AddConstDisAsm(); std::string AddTexDisAsm(); std::string FormatDisAsm(const std::string& code); std::string GetCondDisAsm(); template std::string GetSrcDisAsm(T src); // VP functions std::string GetMaskDisasm(bool is_sca); std::string GetVecMaskDisasm(); std::string GetScaMaskDisasm(); std::string GetDSTDisasm(bool is_sca = false); std::string GetSRCDisasm(const u32 n); std::string GetTexDisasm(); std::string GetCondDisasm(); std::string AddAddrMaskDisasm(); std::string AddAddrRegDisasm(); u32 GetAddrDisasm(); std::string FormatDisasm(const std::string& code); void AddScaCodeDisasm(const std::string& code = ""); void AddVecCodeDisasm(const std::string& code = ""); void AddCodeCondDisasm(const std::string& dst, const std::string& src); void AddCodeDisasm(const std::string& code); void SetDSTDisasm(bool is_sca, std::string value); void SetDSTVecDisasm(const std::string& code); void SetDSTScaDisasm(const std::string& code); CgBinaryDisasm(const std::string& path) : m_path(path) , m_buffer(nullptr) , m_buffer_size(0) , m_offset(0) , m_opcode(0) , m_step(0) , m_size(0) , m_arb_shader("") , m_dst_reg_name("") { fs::file f(path); if (!f) return; m_buffer_size = f.size(); m_buffer = new u8[m_buffer_size]; f.read(m_buffer, m_buffer_size); m_arb_shader += fmt::format("Loading... [%s]\n", path.c_str()); } ~CgBinaryDisasm() { delete[] m_buffer; } std::string GetCgParamType(u32 type) const { switch (type) { case 1045: return "float"; case 1046: case 1047: case 1048: return fmt::format("float%d", type - 1044); case 1064: return "float4x4"; case 1066: return "sampler2D"; case 1069: return "samplerCUBE"; case 1091: return "float1"; default: return fmt::format("!UnkCgType(%d)", type); } } std::string GetCgParamName(u32 offset) const { std::stringstream str_stream; std::string name = ""; while (m_buffer[offset] != 0) { str_stream << m_buffer[offset]; offset++; } name += str_stream.str(); return name; } std::string GetCgParamRes(u32 offset) const { // LOG_WARNING(GENERAL, "GetCgParamRes offset 0x%x", offset); // TODO return ""; } std::string GetCgParamSemantic(u32 offset) const { std::stringstream str_stream; std::string semantic = ""; while (m_buffer[offset] != 0) { str_stream << m_buffer[offset]; offset++; } semantic += str_stream.str(); return semantic; } std::string GetCgParamValue(u32 offset, u32 end_offset) const { std::string offsets = "offsets:"; u32 num = 0; offset += 6; while (offset < end_offset) { offsets += fmt::format(" %d,", m_buffer[offset] << 8 | m_buffer[offset + 1]); offset += 4; num++; } if (num > 4) return ""; offsets.pop_back(); return fmt::format("num %d ", num) + offsets; } template T& GetCgRef(const u32 offset) { return reinterpret_cast(m_buffer[offset]); } void BuildShaderBody() { ParamArray param_array; auto& prog = GetCgRef(0); if (prog.profile == 7004) { auto& fprog = GetCgRef(prog.program); m_arb_shader += "\n"; m_arb_shader += fmt::format("# binaryFormatRevision 0x%x\n", (u32)prog.binaryFormatRevision); m_arb_shader += fmt::format("# profile sce_fp_rsx\n"); m_arb_shader += fmt::format("# parameterCount %d\n", (u32)prog.parameterCount); m_arb_shader += fmt::format("# instructionCount %d\n", (u32)fprog.instructionCount); m_arb_shader += fmt::format("# attributeInputMask 0x%x\n", (u32)fprog.attributeInputMask); m_arb_shader += fmt::format("# registerCount %d\n\n", (u32)fprog.registerCount); CgBinaryParameterOffset offset = prog.parameterArray; for (u32 i = 0; i < (u32)prog.parameterCount; i++) { auto& fparam = GetCgRef(offset); std::string param_type = GetCgParamType(fparam.type) + " "; std::string param_name = GetCgParamName(fparam.name) + " "; std::string param_res = GetCgParamRes(fparam.res) + " "; std::string param_semantic = GetCgParamSemantic(fparam.semantic) + " "; std::string param_const = GetCgParamValue(fparam.embeddedConst, fparam.name); m_arb_shader += fmt::format("#%d ", i) + param_type + param_name + param_semantic + param_const + "\n"; offset += sizeof32(CgBinaryParameter); } m_arb_shader += "\n"; m_offset = prog.ucode; TaskFP(); // reload binary data in the virtual memory, temporary solution { u32 ptr; { fs::file f(m_path); if (!f) return; size_t size = f.size(); vm::ps3::init(); ptr = vm::alloc(size, vm::main); f.read(vm::base(ptr), size); } auto& vmprog = vm::ps3::_ref(ptr); auto& vmfprog = vm::ps3::_ref(ptr + vmprog.program); u32 size; u32 ctrl = (vmfprog.outputFromH0 ? 0 : 0x40) | (vmfprog.depthReplace ? 0xe : 0); std::vector td; GLFragmentDecompilerThread(m_glsl_shader, param_array, ptr + vmprog.ucode, size, ctrl, td).Task(); vm::close(); } } else { auto& vprog = GetCgRef(prog.program); m_arb_shader += "\n"; m_arb_shader += fmt::format("# binaryFormatRevision 0x%x\n", (u32)prog.binaryFormatRevision); m_arb_shader += fmt::format("# profile sce_vp_rsx\n"); m_arb_shader += fmt::format("# parameterCount %d\n", (u32)prog.parameterCount); m_arb_shader += fmt::format("# instructionCount %d\n", (u32)vprog.instructionCount); m_arb_shader += fmt::format("# registerCount %d\n", (u32)vprog.registerCount); m_arb_shader += fmt::format("# attributeInputMask 0x%x\n", (u32)vprog.attributeInputMask); m_arb_shader += fmt::format("# attributeOutputMask 0x%x\n\n", (u32)vprog.attributeOutputMask); CgBinaryParameterOffset offset = prog.parameterArray; for (u32 i = 0; i < (u32)prog.parameterCount; i++) { auto& vparam = GetCgRef(offset); std::string param_type = GetCgParamType(vparam.type) + " "; std::string param_name = GetCgParamName(vparam.name) + " "; std::string param_res = GetCgParamRes(vparam.res) + " "; std::string param_semantic = GetCgParamSemantic(vparam.semantic) + " "; std::string param_const = GetCgParamValue(vparam.embeddedConst, vparam.name); m_arb_shader += fmt::format("#%d ", i) + param_type + param_name + param_semantic + param_const + "\n"; offset += sizeof32(CgBinaryParameter); } m_arb_shader += "\n"; m_offset = prog.ucode; u32* vdata = (u32*)&m_buffer[m_offset]; assert((m_buffer_size - m_offset) % sizeof(u32) == 0); for (u32 i = 0; i < (m_buffer_size - m_offset) / sizeof(u32); i++) { vdata[i] = se_storage::swap(vdata[i]); // WTF, cannot use be_t<> there? } for (u32 i = 0; i < prog.ucodeSize / sizeof(u32); i++) { m_data.push_back(vdata[i]); } TaskVP(); GLVertexDecompilerThread(m_data, m_glsl_shader, param_array).Task(); } } u32 GetData(const u32 d) const { return d << 16 | d >> 16; } void TaskFP(); void TaskVP(); };