Repository navigation
SoftGPU: Implement depth swizzle (address translation), also for CPU access - #22435
Merged
Merged
Conversation
hrydgard
marked this pull request as draft
October 6, 2026 17:14
hrydgard
force-pushed
the
softgpu-depth-swizzle
branch
from
October 7, 2026 00:42
11346e6 to
41f942c
Compare
hrydgard
marked this pull request as ready for review
October 7, 2026 00:44
Where the GE stores each depth pixel, from the EDRAM address translation and the color format: with 32-bit color, address bits 5 up to log2(T) - 1 rotate left by one, then (T << 3) | 0x40 is XORed in (0x600, and bits 9 and 10 swapped, at T 0). The CPU sees the same mapping through VRAM's 0x04200000 and 0x04600000 mirrors. Measured in ppsspp-re geprobe exp8-10 (every pixel of 512x272 buffers at each T) and exp88; the unit test checks it against the model fitted there. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The software renderer kept depth linear in VRAM, where the GE stores it in the layout GPU/Common/DepthSwizzle.h describes, from the EDRAM address translation and the color format. So a texture or anything else sharing VRAM with a depth buffer saw depth land in the wrong places (the player's shadow in Silent Hill: Origins, #22415). Now every depth access goes through the layout: the generic pixel functions, the 2x2 depth test, the depth clear in 16-pixel runs, and the x86 pixel JIT, whose IDs carry the translation so the layout's constants are compiled in. It changes with the color format and sceGeEdramSetAddrTranslation, after a flush. Reading VRAM through its 0x04200000 and 0x04600000 mirrors undoes the layout, as the GE does: texturing from them (drawn from a linear copy made after a flush), and block transfers from or to them (Hayate no Gotoku restores its depth buffer through 0x04600000, #17878). Saved and debugger depth buffers read through the layout, so they're linear. Socom's lens flare dump now matches a PSP replay where it changes. About 2-3% more cycles in God of War, Tekken 6, Wipeout and Burnout. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…layout With the software renderer, which now stores depth as the GE does, a 16-bit read or write at VRAM's 0x04200000 or 0x04600000 mirror goes to where the GE's 16-bit or 32-bit color layout puts that depth pixel (ppsspp-re geprobe exp88: the mirrors are the layout's exact inverse). Games access depth there with nothing else, so other widths stay plain. The accessors check the address, then whether the GPU core is the software renderer (CoreParameter); the hardware renderers keep depth plain and handle the layout their own way. The renderer keeps the layouts up to date with the EDRAM address translation. With it, the JITs send 16-bit loads and stores to the interpreter, and the IR passes don't change a load's width to or from 16 bits. The pspautotests that read depth through the mirrors now do so 16 bits at a time, re-recorded on hardware unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… PSP does A dump records a texture at a swizzled VRAM mirror as the game sampled it, and the PSP's replayer writes it back with the CPU, which the PSP sends through the depth layout at any access width. PPSSPP's playback wrote it raw, so with depth stored in the GE's layout the texture came back permuted: IL-2's depth texture at 0x04710000 and the two Gensou Suikoden swizzle dumps. Texture copies, memcpys and memsets into a mirror now go through the layout, 32 aligned bytes at a time. Against PSP replays: both Suikoden dumps are now exact (from 1912 and 8083 pixels off), Star Wars #15832 is 67 off (from 2590) and IL-2 894 (from 70505, the rest from a write the next draw reads before it lands). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…r when the display is off The recorder saves the CLUT the GE had loaded right after INIT, as a CLUT command with no LOADCLUT to follow, unlike the CLUTs it records at a LOADCLUT. Playback only pointed the CLUT address at it, so draws before the game's first LOADCLUT used whatever CLUT was loaded before: HotBrain (16131, ULUS10268) drew its save screen background black. Load it, and put the game's CLUT address back. A dump whose last DISPLAY turns the display off (address 0), or has none, now shows the last framebuffer drawn to (Auditorium 9213). The PSP replayer does both the same way. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Before 71210f3, a second dump request during a recording started it again, writing another INIT (and initial CLUT) into the dump: the GE state of that moment, which already includes the register writes the dump only has after it. Playback applied it, so the last draws ran with the wrong state: GOD EATER BURST (13950) and GOD EATER 2 (6343) came out black and Street Riders (14746) garbled. The recorder only writes an INIT when it starts, so skip any later one. The 15 such dumps in the GitHub issue set now match the PSP's replay, 10 of them exactly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Older recorders reused data at any offset in the dump, and Prince of Persia: Rival Swords (11587, 12943) has a CLUT stored one byte into the previous one. The GE ignores the low four bits of a CLUT or texture address, so the LOADCLUT read 15 bytes early and the palette channel-swap pass came out red. Map CLUTs, textures and transfer sources that aren't 16-byte aligned to a separate aligned copy. Also updates LocoRoco 12058's software reference, which now matches the PSP's replay exactly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Dumps recorded before savedContextVersion 1 hold PPSSPP's old context layout, with the matrices as raw floats, but GEState::Restore picks the layout from savedContextVersion, which only a savestate sets. Playback restored them as the new layout, so the matrices became garbage and the scene vanished (4140 drew only its HUD). Tell the layouts apart by the END the new one ends with, as the PSP replayer does. Other callers are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
At the end of the 256 KB list buffer, playback jumped back to its start and kept writing, so when the GE was more than a lap behind, the next lap overwrote commands it hadn't reached. Warriors (14660) lost half its draws, and GTA LCS (15149) some. Finish the list, wait for it, and start a new one instead, as the PSP replayer does. Both now match the PSP's replay exactly. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…p is on The software renderer drops a color test that only rejects black under blending that adds to the destination, since rejecting black then only skips adding nothing. With dithering on it doesn't: a rejected pixel keeps the destination, an accepted black one gets the dither added. Ridge Racer 2 (20839) adds a glow that way and came out one step darker wherever the dither matrix was negative; Burnout Legends' bloom (16119) too. Fog and logic ops would change the destination the same way. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
hrydgard
force-pushed
the
softgpu-depth-swizzle
branch
from
October 8, 2026 16:37
41f942c to
50ba8e4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This replaces #22415, with a much more complete implementation.
Close collaboration with Claude. Possibly a 1-2% slowdown with softgpu, but we'll claw it back in upcoming commits. Plus this really is needed for accuracy.
This also includes a large number of fixes for framedump playback, which was heavily used on a large assortment of framedumps to debug this.