PCSX2 Documentation/GSdx Debug
The goal of this page is to describe the GSdx debug capabilities and to explain how to use them efficiently. After this tutorial, you should be able to
- generate a small reproducible testcase
- reproduce the bug
- understand what happen under the hood
Requirement: understand the standard GPU rendering pipeline
You can represent the emotion engine (CPU) and the graphics synthesiser (GPU) as 2 separates black-boxed blocks linked together with an unique channel. GSdump is a recording of all communications + an initial GS reset state (AKA GS save state).
The EE/GS state depends on the user input (from the keyboard/pad). It is quite difficult to replay the same scene, angle of the 3d scene could be different. Some lighting effects could be also different. The GSdump saves a couple of frames to allow constant replay. This way you can compare renderer precisely. For example you can check that pixel (32, 456) of draw call 45 is exactly the same colour. If the value is slightly different it is likely a rounding bug. If the value is completely different, it is a bug.
As GSdump only record the communication, you can change all GSdx options during the replay. You can test a new hack, a new renderer, new resolution etc...
Last but not least, GSdump avoid all the shortcoming of the core emulator. For example the core remaps segmentation fault handler which isn't supported by all debug tools. Without the core, you can easily run external tools such as Valgrind/GPU debugger/CPU debugger.
However there are two limitations
- Bug can be present in the initial state of the GS (typically in the GS memory). For example, you can take a GSdump after a wrong upload of texture
- GS replayer isn't a responder. EE can read the GS memory, do some operations and write it back to the GS memory. In normal rendering, the "write back" part will depends on the previous GS rendering. On the replayer it will be always the recorded data. For example, let's imagine you take a GSdump with the HW renderer of such situation. Hypothesis, in normal emulation, HW renderer is buggy but SW is fine. The recorded data of the dump will be bad. If you replay it with the SW renderer, you will see corrupt data. On the contrary, if you take a dump with the SW renderer in the first place, you will get good rendering during the replay even for the HW renderer.
How to generate a GSdump
See forum thread. (need copy past)
Extra note: recent GSdx will compress single-frame dump directly to xz format
How to replay GSdump
* Build PCSX2 with this option -DBUILD_REPLAY_LOADERS=TRUE
It will create the pcsx2_GSReplayLoader executable Note: I would advice to save it somewhere.
- The executable requires 3 arguments (TODO check it, some arg can be optional)
1/ the GSdx plugin => bin/plugins/libGSdx-0.1.16.so 2/ the .gs file => bin/snaps/god_of_war.gs 3/ the directory that contains the ini file => bin/inis
Advance debug capabilities
=> gs register state dump + other txt dump
=> gs texture/buffer dump. Explain naming convention
- linux_replay = n <= replay the trace n times
- dump = 1 <= Enable dumping infrastructure
- save = 1 <= dump render (color) target
- savez = 1 <= dump depth target
- savet = 1 <= dump input texture
- savef = 1 <= dump renderer frame (before GS CRTC processing)
- saven = nnn <= first draw call to dump
- savel = nnn <= number of draw call to dump
s = format("%05d_f%lld_rt0_%05x_%s.bmp", s_n, frame, context->FRAME.Block(), psm_str(context->FRAME.PSM));
Texture dump naming convention <draw_call_number>_f<frame_number>_<keyword>_<block_address>_<format>
Possible keyword are (search format("%05d keyword in source, mostly in GSRenderer*);
- rt0: color buffer before draw
- rt1: color buffer after draw
- ... to be continued ...
Possible format are (TODO formatting) // Normal color case PSM_PSMCT32: return "C_32"; case PSM_PSMCT24: return "C_24"; case PSM_PSMCT16: return "C_16"; case PSM_PSMCT16S: return "C_16S";
// Palette color case PSM_PSMT8: return "P_8"; case PSM_PSMT4: return "P_4"; case PSM_PSMT8H: return "P_8H"; case PSM_PSMT4HL: return "P_4HL"; case PSM_PSMT4HH: return "P_4HH";
// Depth case PSM_PSMZ32: return "Z_32"; case PSM_PSMZ24: return "Z_24"; case PSM_PSMZ16: return "Z_16"; case PSM_PSMZ16S: return "Z_16S";
case PSM_PSGPU24: return "PS24";
Note: frame_number starts at 5000
=> OpenGL trace