|
| 1 | +#include "stdafx.h" |
| 2 | +#include "CgBinaryProgram.h" |
| 3 | + |
| 4 | +#ifndef WITHOUT_OPENGL |
| 5 | +#include "Emu/RSX/GL/GLVertexProgram.h" |
| 6 | +#include "Emu/RSX/GL/GLFragmentProgram.h" |
| 7 | +#endif |
| 8 | + |
| 9 | +CgBinaryDisasm::CgBinaryDisasm(const std::string& path) |
| 10 | + : m_path(path) |
| 11 | +{ |
| 12 | + fs::file f(path); |
| 13 | + if (!f) |
| 14 | + { |
| 15 | + return; |
| 16 | + } |
| 17 | + |
| 18 | + usz buffer_size = f.size(); |
| 19 | + m_buffer.resize(buffer_size); |
| 20 | + f.read(m_buffer, buffer_size); |
| 21 | + fmt::append(m_arb_shader, "Loading... [%s]\n", path.c_str()); |
| 22 | +} |
| 23 | + |
| 24 | +std::string CgBinaryDisasm::GetCgParamType(u32 type) |
| 25 | +{ |
| 26 | + switch (type) |
| 27 | + { |
| 28 | + case 1045: return "float"; |
| 29 | + case 1046: |
| 30 | + case 1047: |
| 31 | + case 1048: return fmt::format("float%d", type - 1044); |
| 32 | + case 1064: return "float4x4"; |
| 33 | + case 1066: return "sampler2D"; |
| 34 | + case 1069: return "samplerCUBE"; |
| 35 | + case 1091: return "float1"; |
| 36 | + |
| 37 | + default: return fmt::format("!UnkCgType(%d)", type); |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +std::string CgBinaryDisasm::GetCgParamName(u32 offset) const |
| 42 | +{ |
| 43 | + return std::string(&m_buffer[offset]); |
| 44 | +} |
| 45 | + |
| 46 | +std::string CgBinaryDisasm::GetCgParamRes(u32 /*offset*/) const |
| 47 | +{ |
| 48 | + // rsx_log.warning("GetCgParamRes offset 0x%x", offset); |
| 49 | + // TODO |
| 50 | + return ""; |
| 51 | +} |
| 52 | + |
| 53 | +std::string CgBinaryDisasm::GetCgParamSemantic(u32 offset) const |
| 54 | +{ |
| 55 | + return std::string(&m_buffer[offset]); |
| 56 | +} |
| 57 | + |
| 58 | +std::string CgBinaryDisasm::GetCgParamValue(u32 offset, u32 end_offset) const |
| 59 | +{ |
| 60 | + std::string offsets = "offsets:"; |
| 61 | + |
| 62 | + u32 num = 0; |
| 63 | + offset += 6; |
| 64 | + while (offset < end_offset) |
| 65 | + { |
| 66 | + fmt::append(offsets, " %d,", m_buffer[offset] << 8 | m_buffer[offset + 1]); |
| 67 | + offset += 4; |
| 68 | + num++; |
| 69 | + } |
| 70 | + |
| 71 | + if (num > 4) |
| 72 | + { |
| 73 | + return ""; |
| 74 | + } |
| 75 | + |
| 76 | + offsets.pop_back(); |
| 77 | + return fmt::format("num %d ", num) + offsets; |
| 78 | +} |
| 79 | + |
| 80 | +void CgBinaryDisasm::ConvertToLE(CgBinaryProgram& prog) |
| 81 | +{ |
| 82 | + // BE payload, requires that data be swapped |
| 83 | + const auto be_profile = prog.profile; |
| 84 | + |
| 85 | + auto swap_be32 = [&](u32 start_offset, size_t size_bytes) |
| 86 | + { |
| 87 | + auto start = reinterpret_cast<u32*>(m_buffer.data() + start_offset); |
| 88 | + auto end = reinterpret_cast<u32*>(m_buffer.data() + start_offset + size_bytes); |
| 89 | + |
| 90 | + for (auto data = start; data < end; ++data) |
| 91 | + { |
| 92 | + *data = std::bit_cast<be_t<u32>>(*data); |
| 93 | + } |
| 94 | + }; |
| 95 | + |
| 96 | + // 1. Swap the header |
| 97 | + swap_be32(0, sizeof(CgBinaryProgram)); |
| 98 | + |
| 99 | + // 2. Swap parameters |
| 100 | + swap_be32(prog.parameterArray, sizeof(CgBinaryParameter) * prog.parameterCount); |
| 101 | + |
| 102 | + // 3. Swap the ucode |
| 103 | + swap_be32(prog.ucode, m_buffer.size() - prog.ucode); |
| 104 | + |
| 105 | + // 4. Swap the domain header |
| 106 | + if (be_profile == 7004u) |
| 107 | + { |
| 108 | + // Need to swap each field individually |
| 109 | + auto& fprog = GetCgRef<CgBinaryFragmentProgram>(prog.program); |
| 110 | + fprog.instructionCount = std::bit_cast<be_t<u32>>(fprog.instructionCount); |
| 111 | + fprog.attributeInputMask = std::bit_cast<be_t<u32>>(fprog.attributeInputMask); |
| 112 | + fprog.partialTexType = std::bit_cast<be_t<u32>>(fprog.partialTexType); |
| 113 | + fprog.texCoordsInputMask = std::bit_cast<be_t<u16>>(fprog.texCoordsInputMask); |
| 114 | + fprog.texCoords2D = std::bit_cast<be_t<u16>>(fprog.texCoords2D); |
| 115 | + fprog.texCoordsCentroid = std::bit_cast<be_t<u16>>(fprog.texCoordsCentroid); |
| 116 | + } |
| 117 | + else |
| 118 | + { |
| 119 | + // Swap entire header block as all fields are u32 |
| 120 | + swap_be32(prog.program, sizeof(CgBinaryVertexProgram)); |
| 121 | + } |
| 122 | +} |
| 123 | + |
| 124 | +void CgBinaryDisasm::BuildShaderBody(bool include_glsl) |
| 125 | +{ |
| 126 | + ParamArray param_array; |
| 127 | + |
| 128 | + auto& prog = GetCgRef<CgBinaryProgram>(0); |
| 129 | + |
| 130 | + if (const u32 be_profile = std::bit_cast<be_t<u32>>(prog.profile); |
| 131 | + be_profile == 7003u || be_profile == 7004u) |
| 132 | + { |
| 133 | + ConvertToLE(prog); |
| 134 | + ensure(be_profile == prog.profile); |
| 135 | + } |
| 136 | + |
| 137 | + if (prog.profile == 7004u) |
| 138 | + { |
| 139 | + auto& fprog = GetCgRef<CgBinaryFragmentProgram>(prog.program); |
| 140 | + m_arb_shader += "\n"; |
| 141 | + fmt::append(m_arb_shader, "# binaryFormatRevision 0x%x\n", prog.binaryFormatRevision); |
| 142 | + fmt::append(m_arb_shader, "# profile sce_fp_rsx\n"); |
| 143 | + fmt::append(m_arb_shader, "# parameterCount %d\n", prog.parameterCount); |
| 144 | + fmt::append(m_arb_shader, "# instructionCount %d\n", fprog.instructionCount); |
| 145 | + fmt::append(m_arb_shader, "# attributeInputMask 0x%x\n", fprog.attributeInputMask); |
| 146 | + fmt::append(m_arb_shader, "# registerCount %d\n\n", fprog.registerCount); |
| 147 | + |
| 148 | + CgBinaryParameterOffset offset = prog.parameterArray; |
| 149 | + for (u32 i = 0; i < prog.parameterCount; i++) |
| 150 | + { |
| 151 | + auto& fparam = GetCgRef<CgBinaryParameter>(offset); |
| 152 | + |
| 153 | + std::string param_type = GetCgParamType(fparam.type) + " "; |
| 154 | + std::string param_name = GetCgParamName(fparam.name) + " "; |
| 155 | + std::string param_res = GetCgParamRes(fparam.res) + " "; |
| 156 | + std::string param_semantic = GetCgParamSemantic(fparam.semantic) + " "; |
| 157 | + std::string param_const = GetCgParamValue(fparam.embeddedConst, fparam.name); |
| 158 | + |
| 159 | + fmt::append(m_arb_shader, "#%d%s%s%s%s\n", i, param_type, param_name, param_semantic, param_const); |
| 160 | + |
| 161 | + offset += u32{sizeof(CgBinaryParameter)}; |
| 162 | + } |
| 163 | + |
| 164 | + m_arb_shader += "\n"; |
| 165 | + m_offset = prog.ucode; |
| 166 | + TaskFP(); |
| 167 | + |
| 168 | + if (!include_glsl) |
| 169 | + { |
| 170 | + return; |
| 171 | + } |
| 172 | + |
| 173 | + u32 unused; |
| 174 | + std::vector<u32> be_data; |
| 175 | + |
| 176 | + // Swap bytes. FP decompiler expects input in BE |
| 177 | + for (u32* ptr = reinterpret_cast<u32*>(m_buffer.data() + m_offset), |
| 178 | + *end = reinterpret_cast<u32*>(m_buffer.data() + m_buffer.size()); |
| 179 | + ptr < end; ++ptr) |
| 180 | + { |
| 181 | + be_data.push_back(std::bit_cast<be_t<u32>>(*ptr)); |
| 182 | + } |
| 183 | + |
| 184 | + RSXFragmentProgram rsx_prog; |
| 185 | + auto metadata = program_hash_util::fragment_program_utils::analyse_fragment_program(be_data.data()); |
| 186 | + rsx_prog.ctrl = (fprog.outputFromH0 ? 0 : 0x40) | (fprog.depthReplace ? 0xe : 0); |
| 187 | + rsx_prog.offset = metadata.program_start_offset; |
| 188 | + rsx_prog.ucode_length = metadata.program_ucode_length; |
| 189 | + rsx_prog.total_length = metadata.program_ucode_length + metadata.program_start_offset; |
| 190 | + rsx_prog.data = reinterpret_cast<u8*>(be_data.data()) + metadata.program_start_offset; |
| 191 | + for (u32 i = 0; i < 16; ++i) rsx_prog.texture_state.set_dimension(rsx::texture_dimension_extended::texture_dimension_2d, i); |
| 192 | +#ifndef WITHOUT_OPENGL |
| 193 | + GLFragmentDecompilerThread(m_glsl_shader, param_array, rsx_prog, unused).Task(); |
| 194 | +#endif |
| 195 | + } |
| 196 | + |
| 197 | + else |
| 198 | + { |
| 199 | + const auto& vprog = GetCgRef<CgBinaryVertexProgram>(prog.program); |
| 200 | + m_arb_shader += "\n"; |
| 201 | + fmt::append(m_arb_shader, "# binaryFormatRevision 0x%x\n", prog.binaryFormatRevision); |
| 202 | + fmt::append(m_arb_shader, "# profile sce_vp_rsx\n"); |
| 203 | + fmt::append(m_arb_shader, "# parameterCount %d\n", prog.parameterCount); |
| 204 | + fmt::append(m_arb_shader, "# instructionCount %d\n", vprog.instructionCount); |
| 205 | + fmt::append(m_arb_shader, "# registerCount %d\n", vprog.registerCount); |
| 206 | + fmt::append(m_arb_shader, "# attributeInputMask 0x%x\n", vprog.attributeInputMask); |
| 207 | + fmt::append(m_arb_shader, "# attributeOutputMask 0x%x\n\n", vprog.attributeOutputMask); |
| 208 | + |
| 209 | + CgBinaryParameterOffset offset = prog.parameterArray; |
| 210 | + for (u32 i = 0; i < prog.parameterCount; i++) |
| 211 | + { |
| 212 | + auto& vparam = GetCgRef<CgBinaryParameter>(offset); |
| 213 | + |
| 214 | + std::string param_type = GetCgParamType(vparam.type) + " "; |
| 215 | + std::string param_name = GetCgParamName(vparam.name) + " "; |
| 216 | + std::string param_res = GetCgParamRes(vparam.res) + " "; |
| 217 | + std::string param_semantic = GetCgParamSemantic(vparam.semantic) + " "; |
| 218 | + std::string param_const = GetCgParamValue(vparam.embeddedConst, vparam.name); |
| 219 | + |
| 220 | + fmt::append(m_arb_shader, "#%d%s%s%s%s\n", i, param_type, param_name, param_semantic, param_const); |
| 221 | + |
| 222 | + offset += u32{sizeof(CgBinaryParameter)}; |
| 223 | + } |
| 224 | + |
| 225 | + m_arb_shader += "\n"; |
| 226 | + m_offset = prog.ucode; |
| 227 | + ensure((m_buffer.size() - m_offset) % sizeof(u32) == 0); |
| 228 | + |
| 229 | + u32* vdata = reinterpret_cast<u32*>(&m_buffer[m_offset]); |
| 230 | + m_data.resize(prog.ucodeSize / sizeof(u32)); |
| 231 | + std::memcpy(m_data.data(), vdata, prog.ucodeSize); |
| 232 | + TaskVP(); |
| 233 | + |
| 234 | + if (!include_glsl) |
| 235 | + { |
| 236 | + return; |
| 237 | + } |
| 238 | + |
| 239 | + RSXVertexProgram rsx_prog; |
| 240 | + program_hash_util::vertex_program_utils::analyse_vertex_program(vdata, 0, rsx_prog); |
| 241 | + for (u32 i = 0; i < 4; ++i) rsx_prog.texture_state.set_dimension(rsx::texture_dimension_extended::texture_dimension_2d, i); |
| 242 | +#ifndef WITHOUT_OPENGL |
| 243 | + GLVertexDecompilerThread(rsx_prog, m_glsl_shader, param_array).Task(); |
| 244 | +#endif |
| 245 | + } |
| 246 | +} |
0 commit comments