PPSSPP developer cracks the PSP GPU's fixed function maths, bit by bit
Henrik Rydgård explains how PPSSPP's software renderer now matches real PSP hardware pixel for pixel in most tests, after months of probing the console's GPU with an AI assistant and a USB cable.
AI-drafted from 2 sources. This story was written with AI from the outside reporting listed under Sources, and checked by automated tools before it went up. The originals have the full detail.
The PPSSPP emulator's software renderer, long used mainly as a debugging reference, can now reproduce 125 of 132 tested game frames bit-exact against a real PSP. That is according to a blog post by Henrik Rydgård, who maintains the open source PlayStation Portable emulator, describing a months-long effort to reverse engineer the exact arithmetic of the PSP's graphics chip, known as the GE.
The GE is the PSP's fixed function GPU: it handles transform, lighting, clipping, rasterisation, texturing and blending, all driven by a display list of commands rather than programmable shaders. That makes it, in Rydgård's words, a black box with many inputs and only two visible outputs: the colour buffer and the depth buffer. Emulating it closely enough to satisfy a casual player is one thing. Reproducing the exact pixel a real PSP would draw, including depth fighting in the distance, banding in fog, a seam in a skybox or a lens flare that reads back the depth buffer, is another.
Turning a real PSP into an oracle
According to the post, Rydgård had already run a similar project on the PSP's VFPU, its vector floating point math unit, with help from Claude, the AI model, which wrote its own test programs and ran them against the known-correct results of a software floating point library. The GPU project had no such shortcut. There was no independent authority that could compute the right answer for an arbitrary GE operation, so the real PSP, connected over USB, became the only source of truth.
The tool built for this, described in the post as geprobe, builds GE display lists on a PC from a Python description, sends them to a small program running on the PSP over a debug link called PSPLink, and reads back the framebuffer and depth buffer. Claude ran more than 170 of these probes. Because a colour channel is only 8 bits and depth only 16, while the GPU's internal math carries more precision than that, much of the work was designing readouts that could smuggle out the extra bits: single lit points to capture one pixel's exact depth and colour, narrow depth windows that stretch a tiny range of values across the whole 16-bit range, and textures whose texels encode position so that filtering reveals exactly where a texture coordinate landed.
Truncation, not rounding
The headline finding, as Claude's writeup in the post describes it, is that the GE's arithmetic is not standard floating point. Display list parameters are 32-bit floats with their low 8 bits discarded, and the chip computes internally in that reduced format too, truncating toward zero at every step rather than rounding. A matrix row sum, for instance, forms four products exactly, truncates each one to the precision of the largest, adds them exactly, then truncates once more, with no guard bits to soften the loss. The post contrasts this with the VFPU's dot product instruction, which keeps extra precision on its products and rounds the final sum, calling the VFPU “the more careful design of the two”.
The rasteriser held the biggest surprises. Depth values are not computed as a simple per-pixel ratio, nor walked incrementally along triangle edges. Instead each triangle gets fixed-point planes for depth, colour, fog and texture coordinates, set up using a division by the triangle's area performed through a lookup table with 256 segments. Pinning down that table needed actual games: a sequence from Blade Dancer and a single test triangle with 37 pixels off by one both helped narrow a formula down to “the exact reciprocal at the segment start, rounded down to a multiple of 16”, according to the post.
What is still open
Seven of the 132 test frames still do not match bit-exact, and the post does not say why, or whether those cases involve a different part of the pipeline entirely. The findings have been folded into PPSSPP's public GE documentation and a pull request implementing the fixes, though the post does not say when the change will reach a stable release. Readers can follow the original write-up at the PPSSPP blog post, and early reaction from emulator developers is gathering in the r/emulation discussion thread.
Sources
- Cracking the PSP GPU's transform and raster – PPSSPP (ppsspp.org)
- Discussion: Cracking the PSP GPU's transform and raster – PPSSPP (Reddit)
Image: Illustration: AI-generated for Gaming World