Measured on hardware (ppsspp-re geprobe exp177-202, docs/ge-texture-cache.md): 64 lines of 16 bytes x 8
rows in 16 sets of 4 ways, LRU, the set index split between column and band bits by texel size. A line is
tagged by its position in the texture and its mip level only, so another texture read without TEXFLUSH
gets the first one's bytes where their lines coincide; lines load raw bytes on first read and nothing but
TEXFLUSH drops them, not rendering into the texture, block transfers or CPU writes.
TexCache steps every textured primitive through that, in submission order, from its texel footprint. A
line remembers where it came from and its bytes are copied only before that memory changes (drawing, block
transfers, copies, and anything at all when the CPU is about to run). While every line is from the one
texture read since TEXFLUSH and nothing has been copied, nothing read can differ from memory, so primitives
only log what they read, and TEXFLUSH usually drops the log unread. A primitive that reads bytes differing
from memory, or draws over what it reads, is drawn alone from its texture with what it read from the cache
laid over memory. That replaces SelfTextureSnapshot and its "fits in 8 KB" rule.
All the cache experiments match the PSP exactly except exp177, which samples a block transfer's
destination without TEXSYNC. Within a primitive this still loads every line before it draws; the GE loads
them as it rasterizes (serpentine rows, writes reaching memory a 16-byte block late), which Split
Second's blur (the one dump that changes) depends on. About 2% slower in Tekken 6 and God of War.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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 float24 truncations, reciprocal and rsqrt tables, GE adder and row sums,
the setup reciprocal for planes, the mip log2, texture coordinate precision,
the lighting pow and color scaling, and the antialiased line alpha, from
TransformUnit, Rasterizer and Lighting, so they can be unit tested. No
behavior change.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Mirrors the earlier Common extraction. The old "core" target folded in
all of GPU/ (~200 files) plus a few ext/ files wholesale; Windows
already treats GPU as its own project (GPU.vcxproj), so GPU/CMakeLists.txt
splits that out too. GPU has a genuine two-way dependency with Core
(Core/System.cpp calls GPU_Init(), GPU/* calls back into Core for
Memory/Config/CoreTiming/etc), so GPU is a CMake OBJECT library: its
object files are always included wherever consumed instead of being
lazily pulled from an archive, avoiding the GNU ld single-pass
archive-ordering problem a two-way STATIC dependency would hit.
Also fixed a few library misattributions discovered while tracing what
each file actually uses:
- GlslangLibs (glslang/spirv-cross) moved from Core to Common, since
it's Common/GPU/ShaderTranslation.cpp and VulkanContext.cpp that
call into it directly. It only worked before because Core happened
to always be linked after Common.
- ZSTD and OPENGL_LIBRARIES/X11_LIBRARIES moved from Core to GPU,
matching where they're actually called (GPU/Debugger/Record.cpp and
Playback.cpp for ZSTD, GPU/GLES for raw gl*() calls).
- GPU also needs Ext::Snappy directly (Playback.cpp calls
snappy_uncompress) and the libretro-common include dir under
LIBRETRO, both previously inherited for free by accident.
Also fixed USE_DISCORD's add_compile_definitions ordering: it was
being defined after ppsspp_ui's add_library call, so the UI target
never actually saw it on non-MSVC platforms.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSNaZnHCjmryS3ziVN9gZU
Want to get rid of direct accesses to GPUState in modules that may be reused in
my future next-gen backends, that will reformat display lists into command lists that will
then be optimized and executed, out of sync with the real GPUState.
Candidate modules that may be reused in full are Framebuffer and Depal, possibly TextureCache to some degree.
Avoid unnecessary state changes and reduces the amount of OpenGL calls.
It also puts all the interesting logic at the same place, reducing the
complexity a little.