Commit Graph
10192 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 f35ddce87f Vulkan: Do the GE-exact FIXA/FIXB blend math in highp
The fragment shader's default precision is lowp, which NVIDIA's Vulkan driver
runs as fp16, and the product (2c+1)(2f+1) reaches 511*511, past fp16's range.
Split/Second's bloom saturated whole channels; its two bloom dumps go from MSE
2354/9305 to 80/153, the same as OpenGL.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 18:43:26 +02:00
Henrik Rydgård b0ea555b79 Fix a couple of problems with recent commits in MSVC debug builds 2026-10-09 17:52:55 +02:00
Henrik RydgårdandClaude Opus 5.5 ed0d0c1aec GE dump playback: Copy a needed target snapshot at a depth mirror linearly
Me & My Katamari textures a 512x512 render target through 0x04288000, and later
snapshots read the textures past its depth buffer at linear addresses. Through
the swizzled depth mirror those came back permuted, which left a black band
over the horizon. Other snapshots at a mirror still go through it, since they
hold depth as the game saw it there. Software now matches the PSP on the three
Katamari dumps (#16131).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:57:14 +02:00
Henrik RydgårdandClaude Opus 5.5 2f4ce91a15 GE dump playback: Copy render target snapshots later ones rely on in software mode
The recorder leaves VRAM out of a snapshot as unchanged when an earlier snapshot
had it, and that earlier one can be a render target's, which software playback
skipped. Copy such a target snapshot (around what the replay drew), as the PSP
replayer now does. Software now matches the PSP on the three Madden NFL 10 dumps
(#16131), Need for Speed ProStreet (#16175) and Hammerin' Hero, and no dump gets
worse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:57:14 +02:00
Henrik Rydgård 7ec4d02b24 Merge pull request #22472 from hrydgard/msaa-fixes
MSAA fixes by Claude
2026-10-09 16:53:35 +02:00
Henrik RydgårdandClaude Opus 5.5 ed7de3ea76 Readback: Only scale a framebuffer down when it's rendered above maxScaleFactor
ReadFramebufferForDebug scaled the buffer down, cropped to its drawn size,
whenever the render width was more than the drawn width times maxScaleFactor.
At 1x that's any buffer wider than what has been drawn in it, though nothing
needs scaling.

Headless compares at maxScaleFactor 1, so this cut off most of the screen in
Gangs of London, which draws a 256 px wide region: its dumps went from 78 to
62,017 pixels differing from the PSP.

Now it only scales down when the render scale is above maxScaleFactor, and
then scales the whole buffer, since a display at an offset can sit past the
drawn width.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 16:22:05 +02:00
Henrik RydgårdandClaude Opus 5.5 896eb31e87 Copy depth for offset rendering with MSAA too
ShiftDepthForOffsetRendering copied through a single-sampled temp buffer, so
with MSAA CopyDepthRect fell back to a depth blit, which Vulkan can't do with
multisampled images (PerformBlit asserts). Give the temp buffer the same
samples and copy; multisampled depth is never blitted or drawn.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:49:15 +02:00
Henrik RydgårdandClaude Opus 5.5 4202368b1e Vulkan: Look up multisampled framebuffers through a CLUT by their first sample
With MSAA, a framebuffer textured as CLUT indices was read resolved, so the
indices along edges were averages of the samples and looked up wrong colors.
Dragon Ball Z: Tenkaichi Tag Team draws its outlines from a CLUT32 lookup of
its framebuffer, and they came out noisy and miscolored. Copy each pixel's
first sample with texelFetch into a temporary buffer and look up that.

This needs the multisampled color images to be sampled, and transitioned
back to attachment layout before rendering to them again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:49:15 +02:00
Henrik RydgårdandClaude Opus 5.5 70c5b8f07f Vulkan: Interpolate texture coordinates and colors at the centroid
With MSAA, a pixel that a primitive only partly covers otherwise
extrapolates them to the pixel center, outside the primitive, and samples
textures outside its UV range. Without MSAA nothing changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:49:15 +02:00
Henrik RydgårdandClaude Opus 5.5 71367037c0 Copy depth between framebuffers instead of drawing it, where supported
BlitFramebuffer drew depth with a raster copy. With MSAA that reads the
single-sampled depth, which is never resolved: Silent Hill: Origins resizes
its light buffer mid-frame, and with garbage depth its depth-EQUAL light
passes drew nothing, so the flashlight was missing. The raster copy isn't
exact without MSAA either: Burnout Dominator's recursive rendering (#11100)
goes from 112k to 6k pixels off the PSP with a copy. Now depth is copied
wherever the backend can copy just part of it (Vulkan).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:49:15 +02:00
Henrik Rydgård 06fbbf1218 Merge pull request #22471 from hrydgard/parallel-loop-max-threads
Allow specifying a cap on the number of threads used in ParallelLoop
2026-10-09 15:38:09 +02:00
Henrik Rydgård 8b626e0648 Merge pull request #22466 from hrydgard/hw-framebuffer-fixes
HW backends: Various framebuffer fixes
2026-10-09 14:50:05 +02:00
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 7563b43818 Keep 5551 stencil marks when an EQUAL 0 test fails
ConvertStencilFunc5551 rewrites INCR to INVERT when the test is EQUAL 0,
which is only right where the test passed and the value is known to be 0.
It rewrote sFail too, so a pixel that failed against a set stencil got it
cleared again. Warriors Orochi 2 draws its sky in layers that each test
EQUAL 0 and INCR on pass and fail, so every other layer drew again over
the previous one and covered the mountains (#20867): 28650 -> 10285
pixels off the PSP on Vulkan and OpenGL. NOTEQUAL 0's INVERT to ZERO had
the same problem on sFail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:05:20 +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 9ce774d9f9 SoftGPU: Let the quad sampler skip the level check when all is level 0
Staged spans without mip levels pass no level arrays, and the sampler goes
straight to its one level path. 4% fewer instructions in God of War.

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 cf3848d073 SoftGPU: Lerp two channels per lane in the four-pixel bilinear sampler
The texels come out corner by corner, so no transpose, and R with B, G with
A, share 32-bit lanes as 16-bit halves: no lerp of 8-bit values needs more
than 16 bits, and masking after each shift truncates both halves as before.
2.5% fewer instructions in God of War.

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 0787732168 SoftGPU: Skip the level of detail when nothing depends on it
Without mip levels and with one filter for both, a span's texturing is the
quad sampler at level 0: staged spans call it directly instead of going
through ApplyTexturing, which also skips the level calculation. God of War
retires 7% fewer instructions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 11:39:28 +02:00
Henrik RydgårdandClaude Opus 5.5 2cca146b7e SoftGPU: Filter the four-pixel bilinear samples a channel per vector
SampleLinearQuadT filtered each pixel's four texels as 16-bit channels in a 64-bit integer, then
transposed the results into a vector per channel for the texture function. Now the lerps run on a
channel of all four pixels at a time, producing that layout directly. God of War about 6% faster.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 11:39:28 +02:00
Henrik Rydgård bdf05ae874 Merge pull request #22464 from hrydgard/softgpu-spans
SoftGPU: Render in 4x1 spans instead of 2x2 quads, like the PSP. Optimize span functions with templating
2026-10-09 11:38:52 +02:00
Henrik RydgårdandClaude Opus 5.5 f317ca76d9 Copy in shared depth for a pass that tests against it, across frames too
CheckDepthUsage marked the framebuffer's depth as used this frame before
SetDepthFrameBuffer looked for newer depth in a framebuffer sharing it, so
depth from an earlier frame never counted as newer. Brooktown High clears
depth through another framebuffer as the last thing in a frame, and the
next frame's draws test against it: everything failed the depth test and
the hardware backends showed a black screen (#7223).

Now a pass whose first depth use tests against the depth checks for newer
depth first. One that only writes depth keeps the old order, so it doesn't
copy what it overwrites anyway. Of the benchmark savestates, God of War and
Daxter now copy depth once a frame, and God of War comes closer to the
software renderer for it; Burnout and LocoRoco would have copied too
without that restriction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:15:46 +02:00
Henrik RydgårdandClaude Opus 5.5 35de8e5d97 Screenshot the display from wherever in a framebuffer it is
The display can point inside a larger framebuffer: Silent Hill flips
between two halves of a 1024-wide 565 buffer. Presenting it found the
framebuffer and the offset, but screenshots looked up only an exact
address match, and otherwise read RAM, which with a hardware renderer
holds something stale (Silent Hill: Origins' savestate thumbnails showed
the loading screen). Screenshots of the display now use the same lookup
and read back the 480x272 at the offset.

Headless screenshots go the same way: eight Silent Hill frame dumps
(Origins and Shattered Memories) now match the PSP exactly on Vulkan and
OpenGL, from 0.8k-104k differing pixels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:15:46 +02:00
Henrik RydgårdandClaude Opus 5.5 6cbba6ee24 Fix Silent Hill: Origins' flashlight shadows on the hardware backends
The game does shadow mapping without depth textures (#16126):
- It renders the scene once into 44170000 with depth, then draws it again
  into 44170400 (the same buffer 256 pixels to the right) with the same
  depth address and an EQUAL depth test. We render 44170400 into the
  same framebuffer at an X offset, which moves the depth test along with
  the color, so it compared against the wrong depth and the shadow pass
  drew nothing. Copy the depth over to the offset once per pass.
- It reads its 256x256 565 light-depth map as CLUT32. The reinterpret to
  8888 went into an existing 8888 buffer at the same address that was
  only 128x128, which cut the map off below row 128. Grow it first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:15:46 +02:00
Henrik RydgårdandClaude Opus 5.5 e12c4ca0eb Size the render target viewport from the whole buffer, not the guessed drawing size
The viewport (and u_xywh, which must match it) used the framebuffer's
estimated drawing size, so anything drawn past it was clipped even though the
buffer has the rows and the scissor allows them. Tokimeki Memorial 4 draws its
characters to y 271 into a buffer whose height was guessed from a 256 tall
viewport: the bottom 16 rows were cut off, a line through every character, and
its arms broke when the two halves were stitched (#6379, #11249).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:15:46 +02:00
Henrik RydgårdandClaude Opus 5.5 8ff41f3cb2 Keep the render target when a block transfer upload resizes another buffer
ResizeFramebufFBO makes the resized buffer the current render target, and
the rebind after the upload then kept drawing to it. The game's draws to its
own render target went into the uploaded buffer until it changed the
framebuffer pointer. God of War: Ghost of Sparta and Chains of Olympus lost
most of their scenes this way (#12215, #11953, #16170, #11123, #12169).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:15:46 +02:00
Henrik RydgårdandClaude Opus 5.5 5df1fd70eb Set the framebuffer format before binding the target for immediate prims
FlushImm bound the render target with whatever format the last regular prim
left in gstate_c.framebufFormat. ThrillVille draws immediate prims first, so
its 5551 framebuffer was created as 565 and displayed with the wrong colours.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:15:46 +02:00
Henrik RydgårdandClaude Opus 5.5 d9b36b7e46 CrossSIMD: Keep the C++ fallback's integer ops defined, like the SIMD ones
The fallback (which RISC-V uses) did its integer arithmetic in plain int,
where signed overflow and left shifts of negative values are undefined,
and converted out of range floats with a cast, which is undefined too:
RISC-V gives INT_MAX for NaN where SSE gives INT_MIN and NEON 0. With GCC
on RISC-V that broke GEUVSpanCore in the GEMath unit test. Now the
arithmetic wraps through uint32_t, like the SIMD instructions, and an out
of range or NaN conversion gives INT_MIN, like SSE.

Also skip GERowSum's zero terms before computing their shift, as their
exponent can be INT_MIN. Found with -fsanitize=undefined on the fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:12:48 +02:00
Henrik Rydgård bd8f37bf0f Merge pull request #22465 from hrydgard/hw-ge-blends
HW backends: Improve accuracy of blending
2026-10-09 11:05:28 +02:00
Henrik RydgårdandClaude Opus 5.5 10f5de6ded SoftGPU: Find each edge's row bounds once
The coverage and the drawn range took the same three edge bounds, each a
64-bit division. 1% fewer cycles in God of War.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 bb72b748e1 SoftGPU: Pick the span function by blend kind, and decide the vector path once
The span functions come in three blend kinds (none, source alpha over the
destination, and the general one), and states with logic ops or signed blend
factors get the pixel at a time function up front instead of checking per
span. 4.5% fewer instructions in God of War.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 770dc0fe43 SoftGPU: Early depth test of staged spans in vector lanes
CheckDepthTestPassed4 only has an SSE path, so on ARM it compared lane by
lane. Adds Vec4S32::LoadU16 for the depth values.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 d979f4fe3c SoftGPU: Leave out a zero secondary color, and fill constant span buffers once
A flat secondary color of zero (no specular) isn't added to each span, and
a flat secondary color or absent fog fills its buffer once per triangle
rather than once per row. 3% fewer instructions in God of War.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 52aa78f5ae SoftGPU: Draw rectangles in stages like triangle rows
DrawRectangle computed each span's UVs from the planes in 64 bits through the general GEUVSpan. It
now fills the staged rows' buffers (flat colors, z and fog, the coverage and the early depth test)
and draws them with DrawStagedSpans, which takes the 32-bit UV path when the planes allow it. Each
pixel still picks its mip level from its own q.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 c9ddd26cd2 SoftGPU: Walk the staged rows' planes four pixels at a time
When a plane's values at both ends of the chunk fit in 32 bits, so do all between, and 32-bit
wrapping arithmetic gets them exactly: then in vector lanes, with the clamp.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 6223f1926b SoftGPU: Compute the staged rows' coverage in vector lanes
The coverage stage was a scalar loop per pixel with the snap edge as a branch. A chunk starts a span,
so relative to it the bounds fit in 32 bits and the snap edge's pixel is a fixed offset per lane.

Also read the depth buffer for the early depth test with one load where SSE isn't used.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 a0c57e190f SoftGPU: Pass a span's colors a channel at a time
The span pixel function and the four-pixel bilinear sampler work in vector lanes, a channel per
vector, but took and returned the colors a pixel at a time, so every span was transposed twice on the
way. Now they take a channel at a time with a stride. The staged rows keep each chunk's colors that
way from the start (their stage walks a channel at a time anyway), and DrawRectangle builds them so.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 9cbce22773 SoftGPU: Draw clear mode, stencil and color test spans in vector lanes
DrawSpanVector now covers everything but logic ops and the signed blend factors. Stencil tests pick
the SFail, ZFail or ZPass op per lane, and lanes that only get a new stencil keep their old color.
Pixels are read and written in the framebuffer's own format, converting the whole span at once, and
a span with all four pixels drawn is stored in one go.

Only drawn pixels are written: spans are aligned in screen coordinates, so with a draw offset one can
reach into a tile another thread is drawing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 2ec3212d92 SoftGPU: Specialize the staged rows' texturing for the triangle
The last stage of a staged row (texturing, the secondary color and the
pixels) is now a template on what's fixed per triangle: textured or not,
the UV planes fitting 32 bits, the mip level's q and through mode.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 6edaf432ad SoftGPU: Check a triangle's UV planes once instead of every span
When a triangle's q, s and t plane sums fit 32 bits wherever its spans
are walked, and the planes' exponents keep the GE's UV arithmetic in the
normal float range (GEUVSpanSafe), a span's plane values are one vector
add each and GEUVSpanCore runs without its range checks. Tested bit for
bit against the scalar code under those conditions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 1d60f375d7 SoftGPU: Draw triangle rows in stages
Where the pixel function is the C++ one (and textures have planes), a
row is now drawn in stages over up to 64 pixels at a time: coverage,
depth and the early depth test, then the colors, secondary colors and fog
of all its pixels, then texturing and the pixels span by span. Each stage
keeps little state, where the single span loop spilled most of its.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 68c9593fb6 SoftGPU: Apply the texture function to a span's four pixels in lanes
The four-pixel sampler now runs the texture function (modulate, decal,
blend, replace and add, with color doubling) on all four pixels at once,
one vector per channel, instead of GetTextureFunctionOutput per pixel.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 0d6bb8b198 SoftGPU: Draw the pixels of a span together, in vector lanes
Where the pixel function is the C++ one, triangles and sprites now hand
it a whole span of four pixels. Unless the state has a stencil or color
test, a logic op or a signed blend factor, the span goes through one pixel
per vector lane: clamping, alpha test, fog, depth range, test and write,
blending, dither and the color write mask, as the per-pixel code does
them. Checked against the per-pixel code span by span in 8 games and 263
frame dumps.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 d729df2ecd SoftGPU: Step a triangle's planes along the row
Depth, colors, fog and the UV planes are now started once per row and
stepped a span at a time, instead of evaluated from scratch at every
span.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 0223fbcce3 SoftGPU: Walk triangle rows between their exact ends
Each row's covered pixels now come from the three edges directly, as a
range from 64-bit integer bounds at the pixel centers, and only the spans
in it are visited, with lane masks from the range. The edge functions
per span (32-bit, with a coarse row narrowing) are gone. The mip level's
q comes from the row's covered range instead of edge tests per pixel. A
tall triangle's long edge, which the GE checks at the other end of a span
for its first or last pixel, is still checked per pixel.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 cb48381ada SoftGPU: Compute a span's exact texture coordinates in vector lanes
GEUVSpan does the GE's per-pixel UV arithmetic for four pixels at once:
the plane values to float24s by exponent arithmetic, GERecip with its
table entries gathered, and the truncated 24-bit products from the 16-bit
significands' product. Lanes it doesn't cover (values past 2^24,
denormals, results out of range) send the span to the scalar code.
Tested bit for bit against it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 d24d420057 SoftGPU: Sample a span's four pixels in vector lanes
When the four pixels share their mip level (always, for a triangle's
span), their texel coordinates, clamping or wrapping and texel offsets
are computed in vector lanes, and only the texel and palette reads go
one by one. Adds Vec4S32::Min.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 b8690d543e SoftGPU: Draw triangles and sprites in spans of four pixels in a row
The GE walks rows in spans of four pixels and picks the mip level's q
once per span, so the rasterizer now does the same instead of 2x2
quads: triangle spans are aligned like the GE's, so the four pixels
share their level, which is computed once. Auto LOD without planes takes
its vertical derivative from the row below. Sprites, which always have
planes, lose their unused incremental UVs.

Without a sampler JIT, the four bilinear samples of a span are now taken
in one call. Same results on 263 frame dumps and 8 games.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 8c7d600b74 SoftGPU: Fewer address and plane computations per textured pixel
A texel's byte offset is a sum of one for its row and one for its column,
swizzled or not, so the four texels of a bilinear sample now take two of
each, and the CLUT offset of a mip level is found once per sample. The
UV planes at a quad's four pixels are one sum plus steps, as for depth.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 3b26ba54ba SoftGPU: Inline GERecip, and pick the mip level's q once per quad row
The rasterizer takes a GERecip per pixel, so it's inline now, with its
table exported. The q for the mip level is the same for the two pixels
of a quad row unless they're in different 4-pixel spans, so it's only
looked up again then.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00
Henrik RydgårdandClaude Opus 5.5 1e591405b6 SoftGPU: Specialize the bilinear sampler per texture format without a JIT
Where there's no sampler JIT (everything but x86-64), bilinear sampling
switched on the texture format per pixel, on the CLUT format per texel,
and went through swizzle checks and vector-to-stack round trips. Now a
template instance per texture format, swizzling and CLUT format is
picked with the state, and the filter does all four channels at once in
16-bit lanes of a uint64_t. Same results (checked on 263 frame dumps).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 10:34:39 +02:00