Vec4<float>::Cast<int>() used _mm_cvtps_epi32 under SSE, which rounds using
MXCSR's mode, while NEON's vcvtq_s32_f32 and the scalar (T2)x fallback both
truncate. Same split in Rasterizer's InterpolateI.
That's on depth interpolation, so the differing values are written to the depth
buffer and then compared - a one-LSB difference can flip a later GE_COMP_EQUAL
pass and change a whole surface's visibility, not just a shade. Following MXCSR
also meant anything that left a non-default rounding mode in the render thread
would have changed rasterized output.
Truncation is what two of the three paths already did, so SSE moves to match.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vd8ntC2brCUtCrDJMqLbs8
Rasterizer.cpp: the secondary color fallback does `prim_color[i] = ` where the
SSE and NEON branches add, so the base/texture color is thrown away and the
triangle renders as pure specular. Introduced by 250abe0d56 (Loongarch64 build
fixes) changing one character; the structurally identical block in DrawRectangle
still has the `+=`. Live on ARM32, LoongArch64, RISC-V64 and anything else that
isn't SSE2 or ARM64.
Lighting.cpp: IsLargerThanHalf's scalar path assigns instead of accumulating in
its loop, so it returns only `v[2] > 1` and ignores the other components, and the
NEON path computes a max where SSE computes a sum. All three disagreed. The
question being asked is "is this color factor non-zero" - the test this replaced
in fcc3b7684e was `!(colorFactor == ones)` - and since LightColorFactor produces
2*c+1, every component is >= 1 and the sum of four is >= 4, which is why the SSE
sum > 4 is the correct one. Made the other two match it.
Getting this wrong doesn't shift a shade, it enables or disables a whole light:
x86-32 and ARM64 were switching lights off that x86-64 left on.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vd8ntC2brCUtCrDJMqLbs8
Blending is slow, and often games do effects like smoke, sun, etc. that
has a lot of zero alpha in them. Many games do this with alpha testing
off, which is cheap compared to blending.
Since we're already checking the CLUT, we can tell if it doesn't contain
zero alpha, in which case a != 0 test will never fail. This is actually
pretty common, even when texture alpha is not always FF.
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.
* samplerjit: Remove unused x/y parameters.
Still need to tune the accuracy of filtering, but those were not the
right way.
* softgpu: Better approximate slope mip level mode.
This isn't exactly right, but it's closer.
* softgpu: Calculate auto from largest difference.
Direction shouldn't matter.