Commit Graph
81 Commits
Author SHA1 Message Date
Henrik Rydgård 831acb099e Allow specifying a cap on the number of threads used in ParallelLoop 2026-10-09 14:33:58 +02:00
Henrik RydgårdandClaude Opus 5.5 8a7a67ae25 SoftGPU: Use larger tiles now that pixels are cheaper
With the span drawing and sampling faster, the setup a triangle repeats
for each tile it touches weighs more, so the rule is now eight tiles per
thread instead of sixteen (64x32 on eight threads). Against sixteen, in
wall time on five games: God of War -2.6%, Wipeout -1.7%, LocoRoco -5.2%,
Tekken 6 -5.6%, Burnout -3.3%, with 2.5-8% less CPU. Four per thread used
the least CPU but was slower than eight in wall time in most of them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 11:39:29 +02:00
Henrik RydgårdandClaude Opus 5.5 a11fd22553 SoftGPU: Store depth in VRAM as the GE does
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>
2026-10-08 18:35:48 +02:00
Henrik RydgårdandClaude Opus 5.5 6e5258f570 SoftGPU: Declare the wake chain as u64 for LeastSignificantSetBit
Linux's uint64_t is unsigned long, which neither of its u32 and u64
(unsigned long long) overloads takes exactly, so the call was ambiguous.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 15:28:21 -06:00
Henrik RydgårdandClaude Opus 5.5 aa39b6460a SoftGPU: Grow the state ring when it fills
A full state ring flushed the binner, and the next state change filled
it again in the same way. Now the flush also doubles the ring, up to
32768 states, so it happens at most a few times. Nothing draws right
after a flush, so the ring can move then. In the benchmark games, no
more than 512 states were queued between flushes, so 4096 stays the
starting size.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 14:55:17 -06:00
Henrik RydgårdandClaude Opus 5.5 9d842d74af SoftGPU: Track whether anything was queued since a flush with a flag
The binner kept a bounding box of every queued item's range, updated for
each item, only to tell whether anything had been queued since the last
flush. A flag says the same. Expand, which grew the box, is now
ItemQueued.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 14:55:17 -06:00
Henrik RydgårdandClaude Opus 5.5 f015bd111b SoftGPU: Don't change a state the threads may be drawing with
Each queued primitive adds its vertex flags (alpha, fog, colors) to its
state, and states are optimized for those flags before their items are
distributed. A state can still gain primitives after some of its items
are in tiles. Re-optimizing it then changed its pixel and sampler
functions and IDs while threads drew with them. With the x86 pixel JIT,
the span function could turn null between checking and calling it, as
in Coded Arms' frame dump. God of War hit this about four times a frame.

Now a state is live once any of its items is in a tile, until the tiles
are drawn and reset, and a live state isn't changed. A primitive that
would change a live state's flags starts a new, unoptimized state from
the same registers instead, optimized for its own primitives only.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 14:55:17 -06:00
Henrik RydgårdandClaude Opus 5.5 15d0972f86 SoftGPU: Fix merging pending write ranges that start lower in the buffer
Expanding a pending range by a rectangle starting below its start added
a negative row count to an unsigned height, which wrapped to millions of
rows. Overlap tests then overflowed and missed real overlaps. Now a
merged range is whole rows from the lower start to the higher end.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 14:55:04 -06:00
Henrik RydgårdandClaude Opus 5.5 c6c953ff47 SoftGPU: Order texturing from pending drawing per primitive, not by flushing
A draw whose texture rectangle overlapped what queued primitives write
flushed, once per draw. NeedsOrder already orders each primitive by the
texels it actually reads, so let it. God of War's bloom passes texture
from the main framebuffer declared as 512x512, which reaches into the
buffer they draw to, and flushed every 30 primitives: 6% faster there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 14:55:04 -06:00
Henrik RydgårdandClaude Opus 5.5 4bec735331 SoftGPU: Pick the tile size from the thread count
Every tile a triangle touches sets it up again, so larger tiles do less
of that. The tile size is now picked at startup, as large as it can be
with at least sixteen tiles per thread over the screen. With fewer, a
scene whose drawing is concentrated on part of the screen leaves threads
idle: on eight threads, 128x32 tiles (four per thread) took LocoRoco a
third longer than 64x16, and God of War 5% longer. So eight threads keep
64x16, and four or fewer get larger tiles.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 14:55:04 -06:00
Henrik RydgårdandClaude Opus 5.5 1228837688 SoftGPU: Wake only threads with tiles to draw, and chain the wakes
Waking a worker is a condition variable signal, a system call, and the main thread paid one per idle
worker every 48 queued items. With Tekken 6's tiny draws (about 5 vertices each) the workers keep
running dry, and those signals were about 10% of the main thread, which is what limits it there.

Now no more threads are woken than tiles have work, and only the first is woken from the main thread;
it wakes the rest.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 14:55:04 -06:00
Henrik RydgårdandClaude Opus 5.5 bec1eacd77 SoftGPU: Keep threads for drawing that textures from its target
A state that textures from what it draws to, or whose scissor reaches
past the stride, used to switch the binner to one thread, and every
primitive was drawn alone, copying the whole texture for its snapshot.
In Wipeout, God of War and GTA that was a third to half of the time.

Now it's decided per primitive. One is drawn after everything queued
and before what follows only if it needs that: it textures from texels
a queued primitive (or itself) writes, it writes texels a queued one
still has to read, or it reaches past the stride. Reads are bounded by
the vertices' texture coordinates plus the filter's neighbors and
mapped to tiles of the target; queued writes and reads are tracked per
tile. Everything else goes to the tiles as usual. The in-order ones are
split into bands of rows when large, since each pixel reads only the
snapshot, and the snapshot now only copies the previous primitive's
columns.

Same results on 263 frame dumps and 8 games.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 14:55:04 -06:00
Henrik RydgårdandClaude Opus 5.5 f718e89112 SoftGPU: Bin queued primitives into tiles that any thread can draw
Each thread used to own a band of the screen, fixed when the work was
first split, and got its own copies of the primitives touching it. Bands
were rarely balanced (edge bands had half the work of middle ones, and
on big.LITTLE CPUs half the threads are slower), and primitives queued
up to the 2048 limit before the threads started.

Now primitives go to 64x16 tiles, as indices into the shared queue, in
batches of 16. A thread takes any tile with work and draws its items in
order; only one thread holds a tile at a time, so per-pixel order is
kept. A flush or a full queue has the main thread draw tiles too instead
of waiting, and when there's only a little work it does that alone
rather than wake the threads. Items are reclaimed once every tile has
drawn them. This also drops the per-thread queues, up to 23 MB.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 14:55:04 -06:00
Henrik RydgårdandClaude Opus 5.5 fb79979e01 SoftGPU: Small self-textures stay cached until TEXFLUSH
A texture that fits in the GE's 8 KB texture cache keeps what was first
read until TEXFLUSH, across primitives and draws, so a later self-textured
draw doesn't see an earlier one's writes (gpu/probe exp174: three passes
over a 16x16 4444 buffer differ in 177 pixels with and without a flush
between them, both now exact). FF Type-0's (NPJH50443) blur; two of its
PSP replays become exact.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:43 -06:00
Henrik RydgårdandClaude Opus 5.5 e7b21b82ac SoftGPU: Draw single-threaded when the scissor reaches past the stride
Pixels past the framebuffer's stride land at the start of the next row,
which another binning task may be drawing, so the result raced. Tokimeki
Memorial (NPJH50127) blurs a 128 wide buffer under a full screen scissor;
its PSP replay is now exact (gpu/probe exp166).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:43 -06:00
Henrik RydgårdandClaude Opus 5.5 74e2c8f7ee SoftGPU: Self-texturing primitives sample the texture as it was before them
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>
2026-10-02 15:09:43 -06:00
Henrik RydgårdandClaude Opus 5.5 9dd9076f38 SoftGPU: Triangle winding test in 64 bits
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>
2026-10-02 15:09:43 -06:00
Henrik RydgårdandClaude Opus 5.5 d8434424b1 SoftGPU: Drop zero-area triangles
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>
2026-10-02 15:09:43 -06:00
Henrik RydgårdandClaude Opus 5.5 9d3022642b SoftGPU: Fix bin queue bookkeeping and dependency tracking
- 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>
2026-09-29 13:16:40 -06:00
Henrik RydgårdandClaude Opus 5.5 d6f8615d2a SoftGPU: Fix block transfer and self-render overlap tracking
- 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>
2026-09-29 10:33:32 -06:00
Henrik RydgårdandClaude Opus 5.5 a9c782bfcf SoftGPU: Recompute the raster state after a JIT cache clear
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>
2026-09-29 09:35:17 -06:00
Henrik Rydgård 2096179dce Drive-by code cleanup 2026-08-15 18:31:20 +02:00
Henrik Rydgård f60e27a9b7 Just some refactoring of the GPUStatistics struct, and more use of StringWriter 2026-05-29 14:40:31 +02:00
Henrik Rydgård 9218fe9b59 Rework the shutdown logic in the OpenGL backend. Eliminated race conditions. 2025-08-31 10:57:06 +02:00
Henrik Rydgård 45cf6d423e Buildfixes 2024-12-18 13:57:29 +01:00
Henrik Rydgård 3e198c53b2 More include cleanup 2024-12-18 13:57:26 +01:00
Unknown W. Brackets 59fb374c38 softgpu: Small optimization to clut updates. 2023-04-16 10:16:06 -07:00
Unknown W. Brackets 88ba003f46 ThreadManager: Add a simple priority field.
Currently, not actually respected.
2023-02-02 17:08:24 -08:00
Unknown W. Brackets 3a6fa9b4ba ThreadManager: Don't allow reordering of queue.
Allowing a priority item is faster, but can cause confusion when you
expect things to run in the same sequence they're enqueued.
2023-01-14 16:35:01 -08:00
Unknown W. Brackets a7b7bf7826 Global: Set many read-only params as const.
This makes what they do and which args to use clearer, if nothing else.
2022-12-10 21:13:36 -08:00
Unknown W. Brackets 87fb9eef37 softgpu: Remove std::function usage.
Wanted to avoid coupling these, but don't like the std::function
construct/destructs showing in profiles...
2022-12-06 19:15:57 -08:00
Unknown W. Brackets 4d92533907 softgpu: Apply optimizations to states generically.
This is for optimizations we can only do when we know the vertex values.
2022-12-02 21:30:53 -08:00
Unknown W. Brackets 6bd0eec54d softgpu: Calc flags on state as we queue verts.
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.
2022-12-02 21:28:50 -08:00
Henrik Rydgård fd110ef6a9 Merge pull request #16408 from unknownbrackets/jit-wx
softgpu: Fix compile hazard while running
2022-11-20 21:42:10 +01:00
Unknown W. Brackets 4d06400548 softgpu: Fix compile hazard while running.
This prevents any clearing of cache while other threads may be using
previously cached funcs, and avoids wx exclusive hazards.
2022-11-20 12:04:02 -08:00
Henrik Rydgård 250ea5e592 Merge pull request #16407 from unknownbrackets/jit-wx
In jits, protect and unprotect using better estimates
2022-11-20 20:39:04 +01:00
Unknown W. Brackets ce51942508 softgpu: Correct WX-exclusive platform hazards.
Should mainly affect BSD at this point.
2022-11-20 10:55:35 -08:00
Unknown W. Brackets 7e7eabf86a softgpu: Use threads on self-render if safe.
Some games, such as Prince of Persia, self-render 1:1 to colorize, rather
than to scale or blur.  We can still use threads in these cases.
2022-11-13 12:34:31 -08:00
Unknown W. Brackets 58a4376998 GPU: Normalize framebuf addresses.
In VRAM, always store without mirror.  In RAM, always store without
cache/kernel bits.
2022-10-02 21:28:53 -07:00
Unknown W. Brackets 24560eef5c softgpu: Avoid waiting for a thread to drain.
If we can, we want to keep the thread queues healthy, but not full.
Reduce the amount we push on a typical drain to avoid the Wait().
2022-09-24 20:01:00 -07:00
Henrik Rydgård d743bfac93 Merge pull request #16085 from unknownbrackets/softgpu-vert
softgpu: Cache reused indexed verts
2022-09-24 00:00:26 +02:00
Unknown W. Brackets 66b6dfd0a5 softgpu: Fix self-render detect in Ridge Racer.
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.
2022-09-22 20:36:15 -07:00
Unknown W. Brackets 84a3f6de71 softgpu: Remove unnecessary state param.
Oops, meant to remove this when refactoring imm prims.
2022-09-22 18:18:49 -07:00
Unknown W. Brackets e2929fb395 softgpu: Skip flushing on no change.
Minor tweak, we sometimes try to flush even without anything to flush, and
were previously rechecking a lot of things.
2022-09-19 08:38:11 -07:00
Unknown W. Brackets 259abf79dd softgpu: Skip more triangles with equal verts.
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.
2022-09-19 08:37:47 -07:00
Unknown W. Brackets 596b07bd2e softgpu: Support fog and color1 on imm verts. 2022-09-18 06:16:26 -07:00
Unknown W. Brackets 167213c746 softgpu: Cache texture bufws at 16 bit.
Reducing the size of state a bit.
2022-09-12 21:57:00 -07:00
Unknown W. Brackets 5f2e20d8ca softgpu: Reduce some minor bin item field sizes. 2022-09-11 18:39:14 -07:00
Unknown W. Brackets 7a83f8bab5 softgpu: Use vertType prim override for flags.
These parameters are a real shame, was so clean before...
2022-09-06 22:20:45 -07:00
Unknown W. Brackets 88e8f95293 softgpu: Flush on transfer to pending tex read.
Potentially could use these for self-render, but so far we should be
detecting that so leaving it alone.
2022-08-20 23:22:21 -07:00