ApplyGESkinning computes skinned positions and normals the way the GE
does and overwrites what the decoder skinned in float, so that was
wasted, 4% of the emulation thread in God of War. A new decoder option,
callerSkins, leaves out the weights, position and normal steps for
weighted vertices outside through mode and only reserves the float
position and normal. The software renderer's transform unit gets its
own decoders with it; bounding boxes, splines and the debugger still
use the ones that skin. Checked to give the same output with the vertex
decoder JIT as without the option.
God of War takes 4.0 s for five emulated seconds (from 4.1 s).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
God of War draws thousands of short strips per frame, each continuing
the previous one's vertices with nothing changed in between. At the
first draw of such a run, SoftGPU now looks ahead in the display list
for the draws that follow it, past only commands that can't change
anything (NOPs, unchanged state, VADDR to where the vertices continue),
and the transform unit decodes the whole run at once and then skins and
transforms it in chunks that two helper threads take alongside the
emulation thread. The draws then read their vertices from that, and the
UV and normal they carry to the next draw are updated as reading their
last vertex would.
The helpers wait a moment for the next run before their tasks end, as
the drawing threads do: waking them costs the emulation thread a system
call. The thresholds were picked under WSL2, where that costs more than
on bare metal, so they're worth measuring again there.
God of War takes 4.2 s for five emulated seconds (from 4.5 s).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A draw's vertices now go through the position stage of ReadVertex (the
clip coordinates, the viewport, the screen rounding and the GE's fog)
four at a time, a vertex per lane, before the rest. GERowSumLanes is
GERowSum4 with a lane per vertex instead of per matrix row, sharing the
positions' parts between the five rows, and GERecip4 is GERecip for four
w's. A group with a lane those don't handle (non-finite coordinates, a
w that's zero, denormal or too large) goes through ReadVertex as before.
Wipeout takes 3.8 s for five emulated seconds (from 4.2 s), God of War
4.7 s (from 5.0 s).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
GERowSum4 split every matrix entry into exponent, fraction and sign for
each vertex. GERowSumRows does that once when the transform state is
computed, for the clip matrix, the world matrix's normal rows, the fog
row and the texture matrix.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ReadVertex took the GE's viewport arithmetic one coordinate at a time,
with a reciprocal of w for each, and stored the position, the UVs and
the whole vertex in pieces that were then reloaded with wider loads,
each a store forwarding stall. x, y and z now go through one GERecip and
the four-lane multiply and add (the scalar path stays for non-finite
input and zero or denormal w), the position is loaded as a vector, and
the vertex is written straight into its destination.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The position is read once as (x, y, z, 1) for the clip, fog, texgen and
light vector rows, and lighting works on Vec4F32 throughout, without
going through arrays. Each light's constant term, its position minus
the world translation, is computed once per state instead of per
vertex. The texture matrix rows are now prepared with the transform
state, so loading that matrix dirties it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
gpu/vertices/carry, recorded on a PSP: a draw whose format has no UVs uses the last ones read, by any
earlier draw in draw order, textured or not. They're kept as read and get the scale and offset of the
draw that uses them. Normals carry over the same way.
We kept the UVs after scaling, only read them with texturing on, and after a cached indexed draw
carried the highest vertex instead of the last one drawn. The carried values move from statics in
ReadVertex to TransformUnit.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
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>
The near-plane clipper's new vertices get their colors and fog (both 8-bit
per vertex) by lerping with t rounded to the nearest 1/256, not truncated
(gpu/probe exp136: every clipped color and fog pixel exact; the colors were
recovered exactly from the PSP's plane values per clipped triangle).
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>
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>
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>
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>
These own GPU objects, memory or refcounts in their destructors (or assert
there that they were torn down), so a copy would double-free. Nothing copies
them today; this keeps it that way. The manager base classes cover every
backend's subclass.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>