Compare commits

...

5 commits

Author SHA1 Message Date
kd-11
b54c2124cf rsx: Refactor CgBinaryProgram to avoid namespace collision with X11
Some checks failed
Generate Translation Template / Generate Translation Template (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ubuntu-24.04 gcc (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ubuntu-24.04-arm clang (push) Has been cancelled
Build RPCS3 / RPCS3 Linux ubuntu-24.04 clang (push) Has been cancelled
Build RPCS3 / RPCS3 Windows (push) Has been cancelled
2025-05-21 16:51:11 +03:00
kd-11
9a150b677e rsx-debugger: Extra safety checks for FP decompiler 2025-05-21 16:51:11 +03:00
kd-11
01ec2671b3 rsx-debugger: Show disassembly of the currently active VP and FP 2025-05-21 16:51:11 +03:00
kd-11
dd0004f80d rsx: Use safe memory in RSX assembly decompiler 2025-05-21 16:51:11 +03:00
kd-11
e59c6c3c18 vk: Force VMA to comply with Vulkan 1.0 2025-05-21 16:51:11 +03:00
12 changed files with 421 additions and 251 deletions

View file

@ -511,6 +511,7 @@ target_sources(rpcs3_emu PRIVATE
RSX/Overlays/overlay_video.cpp
RSX/Overlays/Shaders/shader_loading_dialog.cpp
RSX/Overlays/Shaders/shader_loading_dialog_native.cpp
RSX/Program/CgBinaryProgram.cpp
RSX/Program/CgBinaryFragmentProgram.cpp
RSX/Program/CgBinaryVertexProgram.cpp
RSX/Program/FragmentProgramDecompiler.cpp

View file

@ -230,7 +230,7 @@ void CgBinaryDisasm::TaskFP()
{
m_size = 0;
u32* data = reinterpret_cast<u32*>(&m_buffer[m_offset]);
ensure((m_buffer_size - m_offset) % sizeof(u32) == 0);
ensure((m_buffer.size() - m_offset) % sizeof(u32) == 0);
enum
{

View file

@ -0,0 +1,246 @@
#include "stdafx.h"
#include "CgBinaryProgram.h"
#ifndef WITHOUT_OPENGL
#include "Emu/RSX/GL/GLVertexProgram.h"
#include "Emu/RSX/GL/GLFragmentProgram.h"
#endif
CgBinaryDisasm::CgBinaryDisasm(const std::string& path)
: m_path(path)
{
fs::file f(path);
if (!f)
{
return;
}
usz buffer_size = f.size();
m_buffer.resize(buffer_size);
f.read(m_buffer, buffer_size);
fmt::append(m_arb_shader, "Loading... [%s]\n", path.c_str());
}
std::string CgBinaryDisasm::GetCgParamType(u32 type)
{
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 CgBinaryDisasm::GetCgParamName(u32 offset) const
{
return std::string(&m_buffer[offset]);
}
std::string CgBinaryDisasm::GetCgParamRes(u32 /*offset*/) const
{
// rsx_log.warning("GetCgParamRes offset 0x%x", offset);
// TODO
return "";
}
std::string CgBinaryDisasm::GetCgParamSemantic(u32 offset) const
{
return std::string(&m_buffer[offset]);
}
std::string CgBinaryDisasm::GetCgParamValue(u32 offset, u32 end_offset) const
{
std::string offsets = "offsets:";
u32 num = 0;
offset += 6;
while (offset < end_offset)
{
fmt::append(offsets, " %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;
}
void CgBinaryDisasm::ConvertToLE(CgBinaryProgram& prog)
{
// BE payload, requires that data be swapped
const auto be_profile = prog.profile;
auto swap_be32 = [&](u32 start_offset, size_t size_bytes)
{
auto start = reinterpret_cast<u32*>(m_buffer.data() + start_offset);
auto end = reinterpret_cast<u32*>(m_buffer.data() + start_offset + size_bytes);
for (auto data = start; data < end; ++data)
{
*data = std::bit_cast<be_t<u32>>(*data);
}
};
// 1. Swap the header
swap_be32(0, sizeof(CgBinaryProgram));
// 2. Swap parameters
swap_be32(prog.parameterArray, sizeof(CgBinaryParameter) * prog.parameterCount);
// 3. Swap the ucode
swap_be32(prog.ucode, m_buffer.size() - prog.ucode);
// 4. Swap the domain header
if (be_profile == 7004u)
{
// Need to swap each field individually
auto& fprog = GetCgRef<CgBinaryFragmentProgram>(prog.program);
fprog.instructionCount = std::bit_cast<be_t<u32>>(fprog.instructionCount);
fprog.attributeInputMask = std::bit_cast<be_t<u32>>(fprog.attributeInputMask);
fprog.partialTexType = std::bit_cast<be_t<u32>>(fprog.partialTexType);
fprog.texCoordsInputMask = std::bit_cast<be_t<u16>>(fprog.texCoordsInputMask);
fprog.texCoords2D = std::bit_cast<be_t<u16>>(fprog.texCoords2D);
fprog.texCoordsCentroid = std::bit_cast<be_t<u16>>(fprog.texCoordsCentroid);
}
else
{
// Swap entire header block as all fields are u32
swap_be32(prog.program, sizeof(CgBinaryVertexProgram));
}
}
void CgBinaryDisasm::BuildShaderBody(bool include_glsl)
{
ParamArray param_array;
auto& prog = GetCgRef<CgBinaryProgram>(0);
if (const u32 be_profile = std::bit_cast<be_t<u32>>(prog.profile);
be_profile == 7003u || be_profile == 7004u)
{
ConvertToLE(prog);
ensure(be_profile == prog.profile);
}
if (prog.profile == 7004u)
{
auto& fprog = GetCgRef<CgBinaryFragmentProgram>(prog.program);
m_arb_shader += "\n";
fmt::append(m_arb_shader, "# binaryFormatRevision 0x%x\n", prog.binaryFormatRevision);
fmt::append(m_arb_shader, "# profile sce_fp_rsx\n");
fmt::append(m_arb_shader, "# parameterCount %d\n", prog.parameterCount);
fmt::append(m_arb_shader, "# instructionCount %d\n", fprog.instructionCount);
fmt::append(m_arb_shader, "# attributeInputMask 0x%x\n", fprog.attributeInputMask);
fmt::append(m_arb_shader, "# registerCount %d\n\n", fprog.registerCount);
CgBinaryParameterOffset offset = prog.parameterArray;
for (u32 i = 0; i < prog.parameterCount; i++)
{
auto& fparam = GetCgRef<CgBinaryParameter>(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);
fmt::append(m_arb_shader, "#%d%s%s%s%s\n", i, param_type, param_name, param_semantic, param_const);
offset += u32{sizeof(CgBinaryParameter)};
}
m_arb_shader += "\n";
m_offset = prog.ucode;
TaskFP();
if (!include_glsl)
{
return;
}
u32 unused;
std::vector<u32> be_data;
// Swap bytes. FP decompiler expects input in BE
for (u32* ptr = reinterpret_cast<u32*>(m_buffer.data() + m_offset),
*end = reinterpret_cast<u32*>(m_buffer.data() + m_buffer.size());
ptr < end; ++ptr)
{
be_data.push_back(std::bit_cast<be_t<u32>>(*ptr));
}
RSXFragmentProgram rsx_prog;
auto metadata = program_hash_util::fragment_program_utils::analyse_fragment_program(be_data.data());
rsx_prog.ctrl = (fprog.outputFromH0 ? 0 : 0x40) | (fprog.depthReplace ? 0xe : 0);
rsx_prog.offset = metadata.program_start_offset;
rsx_prog.ucode_length = metadata.program_ucode_length;
rsx_prog.total_length = metadata.program_ucode_length + metadata.program_start_offset;
rsx_prog.data = reinterpret_cast<u8*>(be_data.data()) + metadata.program_start_offset;
for (u32 i = 0; i < 16; ++i) rsx_prog.texture_state.set_dimension(rsx::texture_dimension_extended::texture_dimension_2d, i);
#ifndef WITHOUT_OPENGL
GLFragmentDecompilerThread(m_glsl_shader, param_array, rsx_prog, unused).Task();
#endif
}
else
{
const auto& vprog = GetCgRef<CgBinaryVertexProgram>(prog.program);
m_arb_shader += "\n";
fmt::append(m_arb_shader, "# binaryFormatRevision 0x%x\n", prog.binaryFormatRevision);
fmt::append(m_arb_shader, "# profile sce_vp_rsx\n");
fmt::append(m_arb_shader, "# parameterCount %d\n", prog.parameterCount);
fmt::append(m_arb_shader, "# instructionCount %d\n", vprog.instructionCount);
fmt::append(m_arb_shader, "# registerCount %d\n", vprog.registerCount);
fmt::append(m_arb_shader, "# attributeInputMask 0x%x\n", vprog.attributeInputMask);
fmt::append(m_arb_shader, "# attributeOutputMask 0x%x\n\n", vprog.attributeOutputMask);
CgBinaryParameterOffset offset = prog.parameterArray;
for (u32 i = 0; i < prog.parameterCount; i++)
{
auto& vparam = GetCgRef<CgBinaryParameter>(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);
fmt::append(m_arb_shader, "#%d%s%s%s%s\n", i, param_type, param_name, param_semantic, param_const);
offset += u32{sizeof(CgBinaryParameter)};
}
m_arb_shader += "\n";
m_offset = prog.ucode;
ensure((m_buffer.size() - m_offset) % sizeof(u32) == 0);
u32* vdata = reinterpret_cast<u32*>(&m_buffer[m_offset]);
m_data.resize(prog.ucodeSize / sizeof(u32));
std::memcpy(m_data.data(), vdata, prog.ucodeSize);
TaskVP();
if (!include_glsl)
{
return;
}
RSXVertexProgram rsx_prog;
program_hash_util::vertex_program_utils::analyse_vertex_program(vdata, 0, rsx_prog);
for (u32 i = 0; i < 4; ++i) rsx_prog.texture_state.set_dimension(rsx::texture_dimension_extended::texture_dimension_2d, i);
#ifndef WITHOUT_OPENGL
GLVertexDecompilerThread(rsx_prog, m_glsl_shader, param_array).Task();
#endif
}
}

View file

@ -7,11 +7,6 @@
#include "Emu/RSX/Program/ShaderParam.h"
#include "Utilities/File.h"
#ifndef WITHOUT_OPENGL
#include "Emu/RSX/GL/GLVertexProgram.h"
#include "Emu/RSX/GL/GLFragmentProgram.h"
#endif
using CGprofile = u32;
using CGbool = s32;
using CGresource = u32;
@ -133,8 +128,7 @@ class CgBinaryDisasm
std::string m_path; // used for FP decompiler thread, delete this later
u8* m_buffer = nullptr;
usz m_buffer_size = 0;
std::vector<char> m_buffer;
std::string m_arb_shader;
std::string m_glsl_shader;
std::string m_dst_reg_name;
@ -190,76 +184,17 @@ public:
void SetDSTScaDisasm(const std::string& code);
CgBinaryDisasm(const std::string& path)
: m_path(path)
{
fs::file f(path);
if (!f) return;
CgBinaryDisasm(const std::string& path);
m_buffer_size = f.size();
m_buffer = new u8[m_buffer_size];
f.read(m_buffer, m_buffer_size);
fmt::append(m_arb_shader, "Loading... [%s]\n", path.c_str());
template <typename T>
CgBinaryDisasm(const std::span<T>& data)
: m_path("<raw>")
{
m_buffer.resize(data.size_bytes());
std::memcpy(m_buffer.data(), data.data(), data.size_bytes());
}
~CgBinaryDisasm()
{
delete[] m_buffer;
}
static std::string GetCgParamType(u32 type)
{
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
{
return std::string(reinterpret_cast<char*>(&m_buffer[offset]));
}
std::string GetCgParamRes(u32 /*offset*/) const
{
// rsx_log.warning("GetCgParamRes offset 0x%x", offset);
// TODO
return "";
}
std::string GetCgParamSemantic(u32 offset) const
{
return std::string(reinterpret_cast<char*>(&m_buffer[offset]));
}
std::string GetCgParamValue(u32 offset, u32 end_offset) const
{
std::string offsets = "offsets:";
u32 num = 0;
offset += 6;
while (offset < end_offset)
{
fmt::append(offsets, " %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;
}
~CgBinaryDisasm() = default;
template<typename T>
T& GetCgRef(const u32 offset)
@ -267,163 +202,14 @@ public:
return reinterpret_cast<T&>(m_buffer[offset]);
}
void ConvertToLE(CgBinaryProgram& prog)
{
// BE payload, requires that data be swapped
const auto be_profile = prog.profile;
static std::string GetCgParamType(u32 type);
std::string GetCgParamName(u32 offset) const;
std::string GetCgParamRes(u32 /*offset*/) const;
std::string GetCgParamSemantic(u32 offset) const;
std::string GetCgParamValue(u32 offset, u32 end_offset) const;
auto swap_be32 = [&](u32 start_offset, size_t size_bytes)
{
auto start = reinterpret_cast<u32*>(m_buffer + start_offset);
auto end = reinterpret_cast<u32*>(m_buffer + start_offset + size_bytes);
for (auto data = start; data < end; ++data)
{
*data = std::bit_cast<be_t<u32>>(*data);
}
};
// 1. Swap the header
swap_be32(0, sizeof(CgBinaryProgram));
// 2. Swap parameters
swap_be32(prog.parameterArray, sizeof(CgBinaryParameter) * prog.parameterCount);
// 3. Swap the ucode
swap_be32(prog.ucode, m_buffer_size - prog.ucode);
// 4. Swap the domain header
if (be_profile == 7004u)
{
// Need to swap each field individually
auto& fprog = GetCgRef<CgBinaryFragmentProgram>(prog.program);
fprog.instructionCount = std::bit_cast<be_t<u32>>(fprog.instructionCount);
fprog.attributeInputMask = std::bit_cast<be_t<u32>>(fprog.attributeInputMask);
fprog.partialTexType = std::bit_cast<be_t<u32>>(fprog.partialTexType);
fprog.texCoordsInputMask = std::bit_cast<be_t<u16>>(fprog.texCoordsInputMask);
fprog.texCoords2D = std::bit_cast<be_t<u16>>(fprog.texCoords2D);
fprog.texCoordsCentroid = std::bit_cast<be_t<u16>>(fprog.texCoordsCentroid);
}
else
{
// Swap entire header block as all fields are u32
swap_be32(prog.program, sizeof(CgBinaryVertexProgram));
}
}
void BuildShaderBody()
{
ParamArray param_array;
auto& prog = GetCgRef<CgBinaryProgram>(0);
if (const u32 be_profile = std::bit_cast<be_t<u32>>(prog.profile);
be_profile == 7003u || be_profile == 7004u)
{
ConvertToLE(prog);
ensure(be_profile == prog.profile);
}
if (prog.profile == 7004u)
{
auto& fprog = GetCgRef<CgBinaryFragmentProgram>(prog.program);
m_arb_shader += "\n";
fmt::append(m_arb_shader, "# binaryFormatRevision 0x%x\n", prog.binaryFormatRevision);
fmt::append(m_arb_shader, "# profile sce_fp_rsx\n");
fmt::append(m_arb_shader, "# parameterCount %d\n", prog.parameterCount);
fmt::append(m_arb_shader, "# instructionCount %d\n", fprog.instructionCount);
fmt::append(m_arb_shader, "# attributeInputMask 0x%x\n", fprog.attributeInputMask);
fmt::append(m_arb_shader, "# registerCount %d\n\n", fprog.registerCount);
CgBinaryParameterOffset offset = prog.parameterArray;
for (u32 i = 0; i < prog.parameterCount; i++)
{
auto& fparam = GetCgRef<CgBinaryParameter>(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);
fmt::append(m_arb_shader, "#%d%s%s%s%s\n", i, param_type, param_name, param_semantic, param_const);
offset += u32{sizeof(CgBinaryParameter)};
}
m_arb_shader += "\n";
m_offset = prog.ucode;
TaskFP();
u32 unused;
std::vector<u32> be_data;
// Swap bytes. FP decompiler expects input in BE
for (u32* ptr = reinterpret_cast<u32*>(m_buffer + m_offset),
*end = reinterpret_cast<u32*>(m_buffer + m_buffer_size);
ptr < end; ++ptr)
{
be_data.push_back(std::bit_cast<be_t<u32>>(*ptr));
}
RSXFragmentProgram prog;
auto metadata = program_hash_util::fragment_program_utils::analyse_fragment_program(be_data.data());
prog.ctrl = (fprog.outputFromH0 ? 0 : 0x40) | (fprog.depthReplace ? 0xe : 0);
prog.offset = metadata.program_start_offset;
prog.ucode_length = metadata.program_ucode_length;
prog.total_length = metadata.program_ucode_length + metadata.program_start_offset;
prog.data = reinterpret_cast<u8*>(be_data.data()) + metadata.program_start_offset;
for (u32 i = 0; i < 16; ++i) prog.texture_state.set_dimension(rsx::texture_dimension_extended::texture_dimension_2d, i);
#ifndef WITHOUT_OPENGL
GLFragmentDecompilerThread(m_glsl_shader, param_array, prog, unused).Task();
#endif
}
else
{
const auto& vprog = GetCgRef<CgBinaryVertexProgram>(prog.program);
m_arb_shader += "\n";
fmt::append(m_arb_shader, "# binaryFormatRevision 0x%x\n", prog.binaryFormatRevision);
fmt::append(m_arb_shader, "# profile sce_vp_rsx\n");
fmt::append(m_arb_shader, "# parameterCount %d\n", prog.parameterCount);
fmt::append(m_arb_shader, "# instructionCount %d\n", vprog.instructionCount);
fmt::append(m_arb_shader, "# registerCount %d\n", vprog.registerCount);
fmt::append(m_arb_shader, "# attributeInputMask 0x%x\n", vprog.attributeInputMask);
fmt::append(m_arb_shader, "# attributeOutputMask 0x%x\n\n", vprog.attributeOutputMask);
CgBinaryParameterOffset offset = prog.parameterArray;
for (u32 i = 0; i < prog.parameterCount; i++)
{
auto& vparam = GetCgRef<CgBinaryParameter>(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);
fmt::append(m_arb_shader, "#%d%s%s%s%s\n", i, param_type, param_name, param_semantic, param_const);
offset += u32{sizeof(CgBinaryParameter)};
}
m_arb_shader += "\n";
m_offset = prog.ucode;
ensure((m_buffer_size - m_offset) % sizeof(u32) == 0);
u32* vdata = reinterpret_cast<u32*>(&m_buffer[m_offset]);
m_data.resize(prog.ucodeSize / sizeof(u32));
std::memcpy(m_data.data(), vdata, prog.ucodeSize);
TaskVP();
RSXVertexProgram prog;
program_hash_util::vertex_program_utils::analyse_vertex_program(vdata, 0, prog);
for (u32 i = 0; i < 4; ++i) prog.texture_state.set_dimension(rsx::texture_dimension_extended::texture_dimension_2d, i);
#ifndef WITHOUT_OPENGL
GLVertexDecompilerThread(prog, m_glsl_shader, param_array).Task();
#endif
}
}
void ConvertToLE(CgBinaryProgram& prog);
void BuildShaderBody(bool include_glsl = true);
static u32 GetData(const u32 d) { return d << 16 | d >> 16; }
void TaskFP();

View file

@ -1,4 +1,5 @@
#define VMA_IMPLEMENTATION
#define VMA_VULKAN_VERSION 1000000
#include "util/atomic.hpp"
#include "Utilities/mutex.h"

View file

@ -167,6 +167,7 @@ namespace vk
allocatorInfo.physicalDevice = pdev;
allocatorInfo.device = dev;
allocatorInfo.instance = inst;
allocatorInfo.vulkanApiVersion = VK_API_VERSION_1_0;
std::vector<VkDeviceSize> heap_limits;
const auto vram_allocation_limit = g_cfg.video.vk.vram_allocation_limit * 0x100000ull;

View file

@ -8,6 +8,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnullability-completeness"
#endif
#define VMA_VULKAN_VERSION 1000000
#include "3rdparty/GPUOpen/VulkanMemoryAllocator/include/vk_mem_alloc.h"
#ifdef __clang__
#pragma clang diagnostic pop

View file

@ -705,6 +705,11 @@ namespace rsx
return decode<NV4097_SET_USER_CLIP_PLANE_CONTROL>().clip_plane5();
}
u32 clip_planes_mask() const
{
return registers[NV4097_SET_USER_CLIP_PLANE_CONTROL];
}
front_face front_face_mode() const
{
return decode<NV4097_SET_FRONT_FACE>().front_face_mode();

View file

@ -454,6 +454,7 @@
<ClCompile Include="Emu\Io\usb_vfs.cpp" />
<ClCompile Include="Emu\RSX\Capture\rsx_capture.cpp" />
<ClCompile Include="Emu\RSX\Capture\rsx_replay.cpp" />
<ClCompile Include="Emu\RSX\Program\CgBinaryProgram.cpp" />
<ClCompile Include="Emu\RSX\Program\CgBinaryFragmentProgram.cpp" />
<ClCompile Include="Emu\RSX\Program\CgBinaryVertexProgram.cpp" />
<ClCompile Include="Emu\RSX\Common\BufferUtils.cpp" />

View file

@ -1099,6 +1099,9 @@
<ClCompile Include="Emu\RSX\Program\VertexProgramDecompiler.cpp">
<Filter>Emu\GPU\RSX\Program</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\Program\CgBinaryProgram.cpp">
<Filter>Emu\GPU\RSX\Program</Filter>
</ClCompile>
<ClCompile Include="Emu\RSX\Program\CgBinaryFragmentProgram.cpp">
<Filter>Emu\GPU\RSX\Program</Filter>
</ClCompile>

View file

@ -4,6 +4,8 @@
#include "table_item_delegate.h"
#include "Emu/RSX/RSXThread.h"
#include "Emu/RSX/gcm_printing.h"
#include "Emu/RSX/Common/BufferUtils.h"
#include "Emu/RSX/Program/CgBinaryProgram.h"
#include "Utilities/File.h"
#include <QHBoxLayout>
@ -123,17 +125,6 @@ rsx_debugger::rsx_debugger(std::shared_ptr<gui_settings> gui_settings, QWidget*
vbox_tools->addLayout(hbox_controls);
vbox_tools->addWidget(m_tw_rsx);
// State explorer
m_text_transform_program = new QLabel();
m_text_transform_program->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
m_text_transform_program->setFont(mono);
m_text_transform_program->setText("");
m_text_shader_program = new QLabel();
m_text_shader_program->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
m_text_shader_program->setFont(mono);
m_text_shader_program->setText("");
m_list_index_buffer = new QListWidget();
m_list_index_buffer->setFont(mono);
@ -208,11 +199,31 @@ rsx_debugger::rsx_debugger(std::shared_ptr<gui_settings> gui_settings, QWidget*
QWidget* buffers = new QWidget();
buffers->setLayout(buffer_layout);
auto xp_layout = new QHBoxLayout();
m_transform_disasm = new QTextEdit(this);
m_transform_disasm->setReadOnly(true);
m_transform_disasm->setWordWrapMode(QTextOption::NoWrap);
m_transform_disasm->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
xp_layout->addWidget(m_transform_disasm);
auto xp_tab = new QWidget();
xp_tab->setLayout(xp_layout);
auto fp_layout = new QHBoxLayout();
m_fragment_disasm = new QTextEdit(this);
m_fragment_disasm->setReadOnly(true);
m_fragment_disasm->setWordWrapMode(QTextOption::NoWrap);
m_fragment_disasm->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
fp_layout->addWidget(m_fragment_disasm);
auto fp_tab = new QWidget();
fp_tab->setLayout(fp_layout);
QTabWidget* state_rsx = new QTabWidget();
state_rsx->addTab(buffers, tr("RTTs and DS"));
state_rsx->addTab(m_text_transform_program, tr("Transform program"));
state_rsx->addTab(m_text_shader_program, tr("Shader program"));
state_rsx->addTab(m_list_index_buffer, tr("Index buffer"));
state_rsx->addTab(xp_tab, tr("Vertex Program"));
state_rsx->addTab(fp_tab, tr("Fragment Program"));
state_rsx->addTab(m_list_index_buffer, tr("Index Buffer"));
QHBoxLayout* main_layout = new QHBoxLayout();
main_layout->addLayout(vbox_tools, 1);
@ -592,10 +603,10 @@ void rsx_debugger::OnClickDrawCalls()
}
// Programs
m_text_transform_program->clear();
m_text_transform_program->setText(qstr(frame_debug.draw_calls[draw_id].programs.first));
m_text_shader_program->clear();
m_text_shader_program->setText(qstr(frame_debug.draw_calls[draw_id].programs.second));
m_transform_disasm->clear();
m_transform_disasm->setText(qstr(frame_debug.draw_calls[draw_id].programs.first));
m_fragment_disasm->clear();
m_fragment_disasm->setText(qstr(frame_debug.draw_calls[draw_id].programs.second));
m_list_index_buffer->clear();
//m_list_index_buffer->insertColumn(0, "Index", 0, 700);
@ -621,6 +632,8 @@ void rsx_debugger::UpdateInformation() const
{
GetMemory();
GetBuffers();
GetVertexProgram();
GetFragmentProgram();
}
void rsx_debugger::GetMemory() const
@ -1202,3 +1215,111 @@ void rsx_debugger::GetBuffers() const
m_buffer_tex->showImage(QImage(m_buffer_tex->cache.data(), width, height, QImage::Format_RGB32));
}
void rsx_debugger::GetVertexProgram() const
{
const auto render = rsx::get_current_renderer();
if (!render || !render->is_initialized || !render->local_mem_size || !render->is_paused())
{
return;
}
RSXVertexProgram vp;
vp.data.reserve(512 * 4);
const u32 vp_entry = rsx::method_registers.transform_program_start();
program_hash_util::vertex_program_utils::analyse_vertex_program
(
rsx::method_registers.transform_program.data(), // Input raw block
vp_entry, // Address of entry point
vp // [out] Program object
);
const u32 ucode_len = static_cast<u32>(vp.data.size() * sizeof(u32));
std::vector<u32> vp_blob = {
7003u, // Type
6u, // Revision
56u + ucode_len, // Total size
0, // paramCount
0, // paramArray
32u, // Program header offset
ucode_len, // Ucode length
56u, // Ucode start
::size32(vp.data) / 4, // Instruction count
0, // Slot
16u, // Registers used
rsx::method_registers.vertex_attrib_input_mask(),
rsx::method_registers.vertex_attrib_output_mask(),
rsx::method_registers.clip_planes_mask()
};
vp_blob.resize(vp_blob.size() + vp.data.size());
std::copy(vp.data.begin(), vp.data.end(), vp_blob.begin() + 14);
std::span<u32> vp_binary(vp_blob);
CgBinaryDisasm vp_disasm(vp_binary);
vp_disasm.BuildShaderBody(false);
m_transform_disasm->clear();
m_transform_disasm->setText(QString::fromStdString(vp_disasm.GetArbShader()));
}
void rsx_debugger::GetFragmentProgram() const
{
const auto render = rsx::get_current_renderer();
if (!render || !render->is_initialized || !render->local_mem_size || !render->is_paused())
{
return;
}
const auto [program_offset, program_location] = rsx::method_registers.shader_program_address();
const auto address = rsx::get_address(program_offset, program_location, 4);
if (!address)
{
m_fragment_disasm->clear();
return;
}
// NOTE: Reading through super ptr while crash-safe means we're probably reading incorrect bytes, but should be fine in 99% of cases
auto data_ptr = vm::get_super_ptr(address);
const auto fp_metadata = program_hash_util::fragment_program_utils::analyse_fragment_program(data_ptr);
const bool output_h0 = rsx::method_registers.shader_control() & CELL_GCM_SHADER_CONTROL_32_BITS_EXPORTS ? false : true;
const bool depth_replace = rsx::method_registers.shader_control() & CELL_GCM_SHADER_CONTROL_DEPTH_EXPORT ? true : false;
const u32 flags = 16u /* Reg count */ | ((output_h0 /* 16-bit exports? */ ? 1u : 0u) << 8u) | ((depth_replace /* Export depth? */ ? 1u : 0u) << 16u) | (0u /* Uses KILL? */ << 24u);
const auto ucode_len = fp_metadata.program_ucode_length;
std::vector<u32> blob = {
7004u, // Type
6u, // Revision
56u + ucode_len, // Total size
0, // paramCount
0, // paramArray
32u, // Program header offset
ucode_len, // Ucode length
56u, // Ucode start
ucode_len / 16, // Instruction count
rsx::method_registers.vertex_attrib_output_mask(), // Slot
0u, // Partial load
0u | (rsx::method_registers.texcoord_control_mask() << 16u), // Texcoord input mask | tex2d control
0u | (flags << 16u), // Centroid inputs (xor tex2d control) | flags (regs, 16-bit, fragDepth, KILL)
0u, // Padding
};
// Copy in the program bytes, swapped
const u32 start_offset_in_words = fp_metadata.program_start_offset / 4;
const u32 ucode_length_in_words = fp_metadata.program_ucode_length / 4;
blob.resize(blob.size() + ucode_length_in_words);
copy_data_swap_u32(blob.data() + 14, utils::bless<u32>(data_ptr) + start_offset_in_words, ucode_length_in_words);
//std::memcpy(blob.data() + 14, utils::bless<char>(data_ptr) + fp_metadata.program_start_offset, fp_metadata.program_ucode_length);
std::span<u32> fp_binary(blob);
CgBinaryDisasm fp_disasm(fp_binary);
fp_disasm.BuildShaderBody(false);
m_fragment_disasm->clear();
m_fragment_disasm->setText(QString::fromStdString(fp_disasm.GetArbShader()));
}

View file

@ -11,6 +11,7 @@
#include <QListWidget>
#include <QTableWidget>
#include <QTabWidget>
#include <QTextEdit>
#include <memory>
@ -61,8 +62,8 @@ class rsx_debugger : public QDialog
Buffer* m_buffer_tex;
QLabel* m_enabled_textures_label;
QLabel* m_text_transform_program;
QLabel* m_text_shader_program;
QTextEdit* m_transform_disasm;
QTextEdit* m_fragment_disasm;
u32 m_cur_texture = 0;
u32 m_texture_format_override = 0;
@ -87,4 +88,7 @@ protected:
private:
void PerformJump(u32 address);
void GetVertexProgram() const;
void GetFragmentProgram() const;
};