Commit Graph
270 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 20620da37f SoftGPU: Cull primitives entirely outside an x or y plane
Like z, a primitive whose vertices are all beyond the same x or y plane
(|x| > w) isn't drawn, for every primitive type and with clipping on or off;
a vertex on the plane is inside (gpu/probe exp176, gpu/clipping/xycull).
Immediate-mode vertices carry screen coordinates in their clip position, so
only z culls them.

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 8ca6f7697f SoftGPU: A vertex on the near plane still needs its screen x/y in range
A vertex with z exactly -w isn't clipped, so with clipping on the GE discards
a triangle whose such vertex lands outside the 4096 guard band. The check
skipped it (gpu/clipping/guardband).

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 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 ecaa961850 SoftGPU: A zero normal gets no diffuse or specular
It used (0, 0, 1) instead. The PSP keeps it zero, under every light kind
and type (gpu/probe exp173). SOCOM (UCES01242) lights meshes whose s8
normals are all zero; its PSP replay becomes 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 7df47b336b SoftGPU: Light vectors as one matrix row from the model position
The GE never forms a world position for lighting: the vector to a point
or spot light is the light position minus the world translation, minus
the model position times the world matrix, summed like a matrix row.
gpu/probe exp153-158 (point lights 0.02-20 units away, spot exponents up
to 40, game-like world matrices; exp153 is a Syphon Filter vertex) are
exact, and Syphon Filter (#13568) becomes 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 6050c6fe02 SoftGPU: Light with the unnormalized normal, GE shade mapping
Found by the probe regression run on the first lighting probes:

- The normal stays as the world matrix leaves it, and N.L and N.H are
  scaled by its GE reciprocal length, like the spot direction. Exact with
  scaled, sheared and rotated world matrices (gpu/probe exp3).
- A negative factor (a spot pointing away with cutoff -1) scales the light
  by 0 instead of subtracting (exp3, exp4).
- Shade mapping UVs use the same GE arithmetic as lighting (exp2-4).
- The half vector uses the GE's add.

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 c8819d5087 SoftGPU: Make arm64 and x86 render the GE probes identically
Found by the probe regression run (ppsspp-re geprobe/regress.py), which
gave different counts on the two:

- Vec4<int> >> was a logical shift on SSE, arithmetic elsewhere. A negative
  spot factor lit pixels white on x86 (gpu/probe exp100).
- Vec4<float>::ToRGBA rounded on SSE and truncated on NEON and the generic
  path. Spline colors were one lower on ARM (exp48-51, the PSP rounds).
- Texel coordinates past the int range: the C++ sampler now gives INT_MIN
  like the x86 JIT's conversion, as the PSP does with a repeating texture (exp5).
- The viewer direction is normalized with the GE's normalization instead of
  the SIMD float one (exp2, exp3).

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 df5b55d5b8 SoftGPU: Integer GE row products, clz for the setup exponent
GEProduct multiplies the significands as integers instead of going through
frexpf/ldexp/trunc in doubles, and GERowSum aligns and adds them as integers.
About 4.5x faster per matrix row on arm64, same results.

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 32c41a3417 SoftGPU: Gather the GE arithmetic in GEMath.h
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>
2026-10-02 15:09:43 -06:00
Henrik RydgårdandClaude Opus 5.5 23acecbe30 SoftGPU: Morph UVs and colors like the GE, and morph before skinning
UVs and vertex colors are morphed with the GE's float24 products and adder
too, colors per channel expanded to 8 bits and the sum floored (gpu/probe
exp107). With both morph targets and skinning weights, the GE morphs
everything first, weights included, then skins once (exp108). All 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 e03f6b80cd SoftGPU: Keep textured rectangles as triangles unless they'd match
Each triangle of a quad has its own UV planes, which a sprite built from the
corners doesn't reproduce. Textured triangle rectangles now only become
sprites in through mode with nearest filtering. Gears of Destiny's text boxes
become bit exact against the PSP, and no dump compared gets worse.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 497f5628fd SoftGPU: Keep rectangles as triangles where sprite coverage differs
Strips, fans and triangle pairs that form rectangles are drawn as sprites,
but a sprite's first column is (x + 6) >> 4 and a triangle's (x + 7) >> 4.
For a left edge 9/16 into a pixel, draw them as triangles (NBA 2K13's menu
boxes, now 99.99% exact against the PSP).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 bc8a1370e3 SoftGPU: Compute the fog factor like the GE
The view z as a row of the combined world-view matrix, then
float24(GEAdd(z, fog end) * fog slope). Bit exact on gpu/probe exp20 (all 22
sweeps, from 99.6-99.95%); infinite and NaN parameters keep their old path.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 0f0e31e373 SoftGPU: Skin and morph like the GE
Skinning scales each bone matrix by its weight (float24 products) and runs
one accumulator through all the bones' translations and terms with the GE's
adder; morphing sums the weighted frames the same way. The software renderer
recomputes the decoder's float results for positions and normals. Bit exact
on gpu/probe exp67, exp68, exp70 and exp71.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 cfaa7a0d55 SoftGPU: Normalize, dot and transform normals like the GE
The GE normalizes with its own reciprocal square root, a 2 x 128 segment
table like its reciprocal's (measured on gpu/probe exp69), and sums dot
products and the world matrix rows like clip space rows. Used for normals,
light vectors, the half vector, the normalized normal texgen source, and the
world position. Bit exact on exp61, exp64 and exp69; point light attenuation
is 99.9%.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 2d3c8fa0cb SoftGPU: Apply UV scale and offset like the GE
In UV gen mode 0 the software renderer now decodes UVs without the prescale
and applies u * scale + offset itself: the product as a float24, then the
GE's guard-bit-less add. Bit exact on gpu/probe exp66 for float, 16-bit and
8-bit UVs (which are unsigned).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 abd4c21b9b SoftGPU: Texture matrix and projected UVs like the GE
The texture matrix is summed like a position row, and projected texture
coordinates go through the same s, t, q planes as perspective ones, with q
the texture matrix's q over w. Points use the planes too. Bit exact on
gpu/probe exp64-65, except the normalized normal source, whose normalization
isn't matched.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 0f2b5613fb SoftGPU: Interpolate Gouraud color and perspective UVs like the GE
Both use the GE's fixed point depth plane. Color is screen-linear, also in
transform mode, with 8-bit channels as the plane values (gpu/probe exp54). For
perspective UVs, s = u * R(w), t and q = R(w) become 15-bit integers at the
triangle's largest exponent, go through the plane, and u = s * R(q) per pixel
(exp55-56, bit exact at 1/16 texel).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 524fac1c32 SoftGPU: Clip at the near plane like the GE
Measured with gpu/probe exp43-47, bit exact:
- New vertices are interpolated from the inside vertex, t = d_in / (d_in -
  d_out) with d = z + w through the GE adder, using the GE's reciprocal;
  out - in is truncated to float24 before the multiply by t, and t and each
  coordinate use float24 products and adds.
- With one vertex outside, the quad is split from the vertex before it in the
  GE's order: (p, a, b) and (p, b, n). The GE takes odd strip triangles in
  reverse and fan triangles in order, so SendTriangle now says when the
  vertices it gets are reversed from that.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 913777f948 SoftGPU: Sum matrix rows and combine matrices like the GE
Measured with gpu/probe (exp30, exp32, exp34, exp35, exp42), bit exact on
all of it (51911 points through general world, view and perspective
matrices, position and depth):
- The vertex position is truncated to a float24.
- Each product in a matrix row keeps the exact value truncated at a fixed
  bit weight, 2^-15 below the two inputs' exponents combined, so a
  significand product of 2 or more keeps a 17th bit.
- A row is one multi-operand sum: every product and the translation are
  truncated to the bit weight of the term with the largest exponent, added
  exactly, and the result becomes a float24. There's no order.
- World, view and projection are combined as (world * view) * projection,
  each entry summed the same way and kept as a float24.

The Coded Arms PSP replay gets worse (MSE 26 to 52). Its decals depend on
per-pixel depth, whose interpolation hasn't been measured yet.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 3bf25586bd SoftGPU: Compute X and Y like the GE, and sample at pixel centers
X and Y now go through the same float24 math as Z: clip coordinates from the
combined matrix (also when lighting needs the world position), the GE's
reciprocal, and the viewport offset added without guard bits, which around
2048 lands on the 1/16 subpixel grid. The 12.4 conversion truncates instead of
rounding up at 10/16.

Triangles are sampled exactly at pixel centers, not 1/16 before them, with left
and top edges inclusive. Sprites start at (x + 6) >> 4 horizontally and
(y + 7) >> 4 vertically and end at (v + 7) >> 4, and their 2x2 loop no longer
treats ties differently by pixel parity. Measured with gpu/probe; against PSP
replays, Coded Arms goes from MSE 33 to 26 and the Test Drive map from 17 to 7.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 c35f7527a3 SoftGPU: Quantize fog per vertex, like the GE
The GE converts the fog factor to 8 bits per vertex, min(floor(256 * f), 255),
and interpolates that linearly in screen space. Clamping after interpolation
instead was up to 127 levels off on triangles that cross the fog range.
Measured with gpu/probe; points and wacky values still match exactly.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 6106280707 SoftGPU: Do the GE's float24 adds with bit masks
Truncating both terms to the precision of the larger one is clearing the
low 8 + (exponent difference) bits of each, after which a float sum is
exact. Only the products still go through doubles, since a product of two
float24s needs up to 32 bits before it's truncated. Same results.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 64fce1bb3f SoftGPU: Compute depth with the GE's precision
Measured on hardware (gpu/depth/transformprecision, gpu/depth/precision):
clip Z and W come out of the combined matrix as float24s, z/w uses the
GE's reciprocal, which linearly interpolates 128 table segments and isn't
quite 1/w, and the viewport's z * scale + center adds without guard bits:
the smaller term is truncated to the precision of the larger one.

Fixes the Test Drive Unlimited map (#12786) and the missing background in
Fuuun Shinsengumi Bakumatsuden (from #16131), where vertices land right at
the minimum depth.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 a6849661ba Shade mapping: Use the light vector as lighting sees it
Environment map S and T are (N.L + 1) / 2 with L the light's vector as
lighting uses it: from the vertex to the light for point and spot lights,
a zero vector staying zero, and the half vector for a light that does
specular. Whether lighting or the light is enabled still doesn't matter
(gpu/lighting/shademap).

The vertex shader ID now carries the type and computation of the shade
mapping lights (the ubershader reads them from u_lightControl), so both
shader caches get a new version.

Fixes the hair shine in iDOLM@STER SP (#12376).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 15:47:16 -06:00
Henrik RydgårdandClaude Opus 5.5 6f6d3e2a81 Lighting: Take the specular viewer direction from the view matrix
The viewer is at infinity along view space +z, so in world space, where
lighting happens, it's the view matrix's third column rather than
(0,0,1): turning the camera moves the highlights (gpu/lighting/specular).
The shaders read it from u_view.

In a Need for Speed Carbon frame replayed on a PSP, this and the pow
bring the error of the cars from MSE 957 to 120 (Vulkan).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 15:47:16 -06:00
Henrik Rydgård 61e1ef8f7a Remove the "Software skinning" option. Now always on. 2026-07-14 17:02:57 +02:00
Henrik Rydgård 6d2948a09b Remove the cached UVScale in gstate_c. Conversion is cheap enough to do directly from gstate, no point in caching. 2026-07-03 13:17:35 +02:00
Henrik Rydgård 195455a7f4 Break out VertexReader, prepare VertexReader for CrossSIMD use in software transform 2026-06-04 12:45:17 +02:00
Henrik Rydgård 078197d790 Remove unnecessary scaling and then un-scaling of through-mode Z coordinates 2026-06-03 20:31:05 +02:00
Henrik Rydgård 2ea8a9ac1f Revert "Remove unnecessary scaling and then un-scaling of through-mode Z coordinates"
Didn't mean to commit this, it was supposed to go in a PR.

This reverts commit 6d4c70c67d.
2026-06-03 17:18:10 +02:00
Henrik Rydgård 6d4c70c67d Remove unnecessary scaling and then un-scaling of through-mode Z coordinates 2026-06-03 16:00:40 +02:00
Henrik Rydgård 0dc6e36d19 Delete redundant implementation of vertex preview in SoftGpu 2026-06-02 15:43:11 +02:00
Henrik Rydgård c46774c923 Fix the ImGe debugger vertex list 2026-06-02 14:53:37 +02:00
Henrik Rydgård bb573e6e0c Well, it builds, but doesn't work yet. 2026-06-02 12:33:53 +02:00
Henrik Rydgård 44ece6dfa2 Determine the clip flags directly when culling, simplifying the code. 2026-05-30 19:07:59 +02:00
Henrik Rydgård 8b84fd555d Apply triangle near-clipping in software transform.
Fixes, but only in SW transform, #10914

Re-enable range culling, oops.
2026-05-30 19:07:59 +02:00
Henrik Rydgård b1723086be Plumb through depth information (although not yet always generated) 2026-05-30 19:07:58 +02:00
Henrik Rydgård 5808afef10 Implement minZ/maxZ through user clip planes 2026-05-30 19:07:58 +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
Herman Semenoff 400d136f51 GPU: modernize use C++17 constexpr for precalculate compilation 2026-05-19 21:57:45 +03:00
Henrik Rydgård e71ffc59a6 Remove unnecessary IsStarted method 2026-02-17 10:18:08 +01:00
Henrik Rydgård 991d7bdfab VertexDecoder: Refactor away lowerbound/upperbound parameters 2026-02-05 13:26:17 +01:00
Henrik Rydgård 32df2f7a03 Remove unused functions 2025-01-28 10:54:43 +01:00
Henrik Rydgård d5bfc1b5eb Finish up this cleanup (move _M_SSE compat define out of Common.h) 2024-12-19 16:23:20 +01:00
Henrik Rydgård 3e198c53b2 More include cleanup 2024-12-18 13:57:26 +01:00
Henrik Rydgård ee297e974d Move more code into SoftwareTransformCommon, cleanup 2024-12-18 13:57:26 +01:00
Henrik Rydgård a07da939ef Some NormalizeVertices interface cleanup 2024-12-17 19:13:38 +01:00
Henrik Rydgård fec232f8a8 GPU: Move things around a little, out of DrawEngine. 2024-12-17 19:13:38 +01:00
Henrik Rydgård e74101a2fb applySkinInDecode belongs in the VertexTypeID, not in the options. 2024-12-17 18:24:18 +01:00