The GE samples through its texture cache, so a primitive that textures from
its own render target mostly reads the old contents (geprobe exp81, exp159,
exp160). Give each such primitive a snapshot, refreshing only the rows the
previous one drew.
3rd Birthday (ULUS10567) bloom becomes exact on PSP replays, Burnout
Dominator's glare 93% -> 98% and Ridge Racer's blur 40% -> 94% of pixels.
The remaining differences are rows where the cache's lazily loaded 8-row
blocks already see the primitive's own writes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With vertices far apart in the 4096 pixel screen space the cross product
overflowed 32 bits, flipping its sign, so both windings of a large triangle
were dropped (gpu/probe exp132: the PSP draws it).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A triangle with collinear vertices lights no pixels on the PSP, even when its
edge runs through pixel centers (gpu/probe exp118, through and transform
mode). The binner only dropped them when all x or all y were equal, so
degenerate strip triangles drew lines of pixels along shared edges. Fixes
the last differences in two frame dumps (Issue 15129, ULUS10041).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Flush() on an empty queue now trims the state and CLUT rings, since its
callers flush because one is full and push right after.
- BinQueue::Full() uses >=, so an overshoot can't go unnoticed.
- IsExactSelfRender compares against the target the queued draws were
binned for, not gstate, which already has the next one during a flush.
- A depth test without depth writes marks the depth buffer as read.
- The DarkStalkers untextured sprite recomputes the binner state around it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The block transfer overlap check passed the stride in pixels where bytes
are expected, so it only covered part of the rectangle.
- A selfrender/selfdepth flush in UpdateState dropped the current draw's
pending writes and reads, so later transfers didn't wait for it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A code space clear frees the functions the current state points to, but
the state was kept as long as the GE registers didn't change. Track the
clear generations and recompute, also when a compile during the state
computation clears the caches.
Also skip the binner flush for compiles on builds without the software
JIT, where Compile() does nothing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Might be some other ways, like doing this directly in a vertex reader.
Also am thinking about doing things regarding UVs or positions.
Flags not yet used, keeping separate for perf checks.
When we flush we mark all pending writes zero, but we rely on this being
set to detect self-render.
TRANSFORM_ALL was wrong as well, sometimes clearing BINNER_RANGE.
Even if X is not equal, if all Y are equal, we can still skip.
This seems to happen more often than I expected with far away objects.
Skipping here avoids filling up the queue.
This generally detects overlap more accurately using a dirty rectangles
approach. Also detects render to self much more accurately, including
with depth.
Some games (i.e. VC3) benefit from an early drain, since they get more
done while processing more verts. Others finish the draw quickly, and
then cause significant overhead in queueing new threads.
This attempts to balance the two, and improves Call of Duty and Blade
Dancer.