Without clip distances, the fragment shader's min/max Z discard only came on
when both minz and maxz were set, while the clip planes take each side on
its own. Pursuit Force's sky has minz 0 and maxz 300 and lies past 300, so
the PSP drops it (its replays of 11399 UCES00019 and 11216 UCUS98703 show
that), but GPUs without clip distances drew it over the scene: 13.9k px off
the PSP on a Mali-G71, now 8 as with clip distances. The discard can now
come with the fragment depth clamp, for the side that's at the limit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fragment shader's min/max Z discard declared it with the default
precision, the vertex shader with highp, and PowerVR refuses to link such a
program, so every draw that needed the discard was dropped. On a PowerVR
GE8320 that was Street Riders, Valkyria Chronicles, Flatout and Frontier
Gate, and with the discard taking one-sided depth ranges too (next commit),
most of Pursuit Force.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Picking a byte out of a 16-bit framebuffer needs more than the defaults. The
texcoord reached the shader as a mediump varying, which Adreno keeps in fp16:
its byte coordinate (u times up to 2048) then picks the wrong byte for every
other one past 512, and SOCOM Tactical Strike's night vision came out striped
in the right half. On a PowerVR GE8320 the default lowp in the rest of the
shader made SOCOM's night vision and full screen effect far too bright (53k
and 17k px off the Mac's Vulkan render, now 0). Vulkan already uses highp.
Where the fragment shader has full precision, the vertex shader now writes the
texcoord in highp, and CLUT8 depal reads it that way and computes in highp.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fallback set the rasterizer's Z to a constant so nothing would clip, which
lost the clipping at the PSP's near plane too. Hardware depth clamp loses it
the same way, which the vertex shader can only make up for with a clip plane.
Give the rasterizer (z + w) / 65536 instead: 0 at the near plane, so it clips
there, and past w only beyond 65535 * w. And without clip distances, use the
fragment fallback instead of hardware depth clamp.
With --disable-gpu-features for all three on the Mac, the framedumps set now
differs from the full render by 15570 px instead of 184229 (WALL-E, Hokuto no
Ken, Frontier Gate, FF Zero). The full render doesn't change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
For GPUs without depth clamp (old Mali), found by comparing with
--disable-gpu-features=depth-clamp on the Mac:
- Write Z / 65536 like the vertex shader, not / 65535.
- Pass the fragment shader Z after the vertex depth rounding, which hardware
clamp sees too (Puyo Puyo Fever 2's ground at Z = 0).
- Also clamp draws whose Z only touches 0 or 65535.
- Draws that skip the bounds check (skinned, morphed) get the fragment clamp
and min/max discard wherever the hardware versions would be on, since those
don't depend on the check either.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
HLSL types an unsuffixed hex literal as uint, so asint(x) - 0x3F800000
went unsigned and wrapped for every x below 1. The huge t then turned
into a NaN, and anything lit with specular or powered diffuse came out
black on D3D11 (the flags in Everybody's Golf's language select, #22418).
GLSL types it as int and was unaffected.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Read as an integer, a float's bits are its log2 with the mantissa a
straight line between powers of two, scaled by 2^23, and writing an
integer back is the matching exp2. That's exactly the GE's
approximation, without log2/exp2/floor. pspPow now also returns 1 for
e <= 0 itself, so the callers drop their checks. Shader languages
without integers fall back to a true pow.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
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>
The GE computes these powers as exp2(e * log2(x)), with log2 and exp2
each a straight line between powers of two (Mitchell's approximation),
and only uses the top 4 bits of the specular coefficient's mantissa.
Through a highlight's falloff a true pow is 10-30 steps of 255 brighter
at the exponents games use. Measured in gpu/lighting/specular.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Not sure if we can find a better way, I suppose
we could clamp vertex coordinates that are in this range instead, if it
turns out that this somehow breaks something.
This matches some earlier test results by Unknown where we could see
that only every second value was used, and is the only explanation I can
come up with for the Afterburner problem, having now traced all the
depth math properly.
Fixes#21785 - and hopefully doesn't break anything else.