GS alpha test and blend: all four rules the item names are now present
Finishes the rule layer. Two traps, both of which produce a picture that looks slightly wrong rather than obviously broken: AFAIL READS BACKWARDS. The PS2's alpha-test register holds an enable, a 7-bit reference, and a bit that looks like "fail if alpha is above the reference". On the hardware it means the opposite -- AFAIL = 0 discards where alpha is BELOW the reference. passes_alpha_test takes the side explicitly rather than encoding the register, and both directions are tested so the convention is pinned in code rather than in a comment. THE OPAQUE BIT MUST STAY OUT OF THE COMPARISON. 0x80 is fully opaque, but it is also numerically 128, so a test that compares the raw byte against a 0x40 reference would have every fully-opaque fragment pass every threshold, and nothing would ever be discarded. passes_alpha_test clamps the opaque bit off first, and there is a test that asserts exactly that -- the failure mode here is a scene that renders correctly with the wrong shading, which is not something anyone notices by looking. Blend is the four GS primitive types, decoded from the primitive-type byte and named by what they do rather than by the documentation's IIIB/IIIA/IIIC labels, which name two different modes identically. Out-of-range types decode to Off rather than panicking, because the byte comes from asset data. Addition clamps at 255 rather than rescaling, matching the GS's 7-bit clamp; a caller wanting a true average blends before widening, and the doc says so. That completes the four rules the roadmap names for this item: alpha 0x80 == 1.0 alpha_to_8 CLAMP wrap clamp01 mip selection mip_level blend + alpha test BlendMode, passes_alpha_test Nine unit tests, no wgpu work, and the roadmap item stays [~] rather than [x] -- the rules are the input to the renderer, not the renderer. Worth noting the pattern in both traps: each one produces output that is plausible and slightly-off, with no error anywhere. That is the same shape as the sign inversion in mip_level and the byte-order bug in the anchor search. The tests are the only thing that catches this class, which is the argument for having them even though nothing here needs a GPU.
This commit is contained in:
+9
-4
@@ -711,10 +711,15 @@ lookup, and it is the main thing inherited from ReRAC.
|
||||
`0x80` is opaque, and `0x80 & 0x7F == 0` is exactly why it reads as half to
|
||||
an 8-bit reader), `clamp01` (CLAMP wrap, with `1.0` kept at the far edge),
|
||||
and `mip_level` -- `round(log2(image / on_screen))` clamped to the chain,
|
||||
where a *smaller* on-screen size selects a *higher* level. **Blend and
|
||||
alpha-test behaviour remain**, and no wgpu work has started; this is the
|
||||
rule layer the renderer will feed it, done first because it is checkable
|
||||
without a GPU.
|
||||
where a *smaller* on-screen size selects a *higher* level. **All four rules
|
||||
are now there**, plus `passes_alpha_test` and `BlendMode` for the
|
||||
alpha-test and blend halves of the item. Two traps worth naming:
|
||||
the GS's `AFAIL` bit reads as "fail if above the reference" and on the
|
||||
hardware means the opposite -- it discards where alpha is **below**; and the
|
||||
opaque bit must be kept out of the comparison, or `0x80` satisfies every
|
||||
threshold and nothing is ever discarded. **No wgpu work has started** --
|
||||
this is the rule layer the renderer will feed it, done first because it is
|
||||
checkable without a GPU.
|
||||
- [ ] VU0/VU1 skinning and lighting on the PS2 float model
|
||||
- [ ] LOD/morph/cull per `TfragProc` and `TieProc`
|
||||
- [ ] Occlusion culling, fog, shadows
|
||||
|
||||
Reference in New Issue
Block a user