re: the UI blend mode is MEASURED -- the frames are drawn ADDITIVE

Closes the one route t32-blend-mode-not-on-disc.md left open: the executable's
draw path. Canary's UI draw capture now logs RB_BLENDCONTROL0 per draw, and the
game was driven to the main menu and to EXTRAS with F10 at each.

The title-side UI uses two blend states and ONE pixel shader:

  0x07010701  src=ONE dst=1-SRC_ALPHA  alpha-over (premultiplied)
              ptbase, pteff05, the fade quad, ptmsg, ptmsg2, pttitle, buttons
  0x01010101  src=ONE dst=ONE          ADDITIVE
              ptframe1, ptframe2, ptframe3, pteff20, both rotated sweep strips

Two controls, both run before the result was read:

* the NDC->pixel conversion that identifies a draw by its quad size reproduces
  1134 and 1303 px for the two rotated sweep strips -- numbers measured by a
  different tool in a different session -- on BOTH screens. The tool prints
  PASS/FAIL and disclaims its own output on FAIL.
* pixel shader 0xE59B2B3DA4AA9008 is used with BOTH states, 12 draws additive
  and 18 alpha-over. ptframe1 and ptbase run the same shader; only the blend
  register differs. So this is a blend result, not a shader result.

This confirms the port's independent measurement -- it solved the composite per
pixel from two backgrounds and found additive halves alpha-over's error on both
frames -- by a route with nothing in common with it.

So the blend is no longer authored: 'any blend you choose is authored' was true
of the disc and is not true of the game. What is still unknown is which field
selects it; elements sharing a mode are batched into one draw call, so the
selection happens before the draw.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
This commit is contained in:
sylph-decoder
2026-08-31 06:06:30 +00:00
parent 2c7c6c255e
commit e61c60029a
5 changed files with 339 additions and 5 deletions

View File

@@ -0,0 +1,109 @@
# The UI blend mode, MEASURED from the running game. 2026-08-31.
#
# Question: the port's renderer composites every UI element with straight
# alpha-over, and four elements come out too dark against the capture --
# ptframe1/ptframe2 on the main menu and ptframe3/ptframe4 on EXTRAS -- with the
# shortfall correlating with the BACKGROUND. Nothing on the disc selects a
# per-element mode (docs/re/structures/t32-blend-mode-not-on-disc.md, reach:
# declaration entry, T8aD header word- and bit-wise, keyframe record).
#
# Method: Canary's CaptureUiDrawForRE was extended to log RB_BLENDCONTROL0,
# RB_COLORCONTROL and RB_COLOR_MASK per draw. One emulator, driven to the main
# menu and then to EXTRAS, F10 at each. Elements are identified by the PIXEL SIZE
# of their quad (NDC extents x the 1280x720 surface) against sprite dimensions
# read off the disc -- the log names no elements.
#
# Xenos enum (xenos.h): BlendFactor kZero=0 kOne=1 kSrcAlpha=6 kOneMinusSrcAlpha=7;
# BlendOp kAdd=0. RB_BLENDCONTROL0 = src | op<<5 | dst<<8, colour in the low half
# and alpha in the high half.
#
# 0x00010001 src=ONE dst=ZERO opaque -- blending effectively off
# 0x07010701 src=ONE dst=ONE_MINUS_SRC_A alpha-over, PREMULTIPLIED (src is ONE)
# 0x01010101 src=ONE dst=ONE ADDITIVE
#
# CONTROL, run before reading anything below: the two rotated sweep strips were
# measured independently at 1134 and 1303 px tall
# (docs/re/data/title-sweep-drawn-at-rest.txt). The NDC->pixel conversion here
# reproduces BOTH, on BOTH screens. The tool prints PASS/FAIL and refuses to be
# trusted otherwise.
#
# SECOND CONTROL, and it is the one that makes this a blend result rather than a
# shader result: ONE pixel shader, 0xE59B2B3DA4AA9008, is used with BOTH states
# on the main menu -- 12 draws additive and 18 alpha-over. The frames and ptbase
# run the same shader. Only the blend register differs.
=== main menu (GP_TITLE build 5), 36 draws over 5 frames ===
draw prim idx blend state quad px (w x h) best name match
1 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
2 13 8 0x01010101 ADDITIVE 889.6 x 1134.0 (no match, nearest pteff12.t32 @2x off 610)
2 13 8 0x01010101 ADDITIVE 1299.2 x 1303.2 (no match, nearest ptbase.t32 @2x off 602)
3 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
4 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
5 13 4 0x01010101 ADDITIVE 819.2 x 720.0 (no match, nearest pteff12.t32 @2x off 125)
6 13 4 0x01010101 ADDITIVE 691.2 x 720.0 pteff12.t32 @2x
7 13 8 0x01010101 ADDITIVE 243.2 x 280.8 ptframe1.t32 @1x
7 13 8 0x01010101 ADDITIVE 256.0 x 309.6 ptframe2.t32 @1x
8 13 4 0x07010701 alpha-over(premul) 224.0 x 39.6 ptmsg.t32 @1x
9 13 24 0x07010701 alpha-over(premul) 51.2 x 46.8 (no match, nearest ptbtneff01.t32 @1x off 10)
9 13 24 0x07010701 alpha-over(premul) 211.2 x 54.0 ptbtn01f.t32 @1x
13 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
14 13 8 0x01010101 ADDITIVE 889.6 x 1134.0 (no match, nearest pteff12.t32 @2x off 610)
14 13 8 0x01010101 ADDITIVE 1299.2 x 1303.2 (no match, nearest ptbase.t32 @2x off 602)
15 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
16 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
17 13 4 0x01010101 ADDITIVE 819.2 x 720.0 (no match, nearest pteff12.t32 @2x off 125)
18 13 4 0x01010101 ADDITIVE 691.2 x 720.0 pteff12.t32 @2x
19 13 8 0x01010101 ADDITIVE 243.2 x 280.8 ptframe1.t32 @1x
19 13 8 0x01010101 ADDITIVE 256.0 x 309.6 ptframe2.t32 @1x
20 13 4 0x07010701 alpha-over(premul) 224.0 x 39.6 ptmsg.t32 @1x
21 13 24 0x07010701 alpha-over(premul) 64.0 x 61.2 (no match, nearest ptbtneff01.t32 @1x off 37)
21 13 24 0x07010701 alpha-over(premul) 211.2 x 54.0 ptbtn01f.t32 @1x
25 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
26 13 8 0x01010101 ADDITIVE 889.6 x 1134.0 (no match, nearest pteff12.t32 @2x off 610)
26 13 8 0x01010101 ADDITIVE 1299.2 x 1303.2 (no match, nearest ptbase.t32 @2x off 602)
27 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
28 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
29 13 4 0x01010101 ADDITIVE 819.2 x 720.0 (no match, nearest pteff12.t32 @2x off 125)
30 13 4 0x01010101 ADDITIVE 691.2 x 720.0 pteff12.t32 @2x
31 13 8 0x01010101 ADDITIVE 243.2 x 280.8 ptframe1.t32 @1x
31 13 8 0x01010101 ADDITIVE 256.0 x 309.6 ptframe2.t32 @1x
32 13 4 0x07010701 alpha-over(premul) 224.0 x 39.6 ptmsg.t32 @1x
33 13 24 0x07010701 alpha-over(premul) 51.2 x 54.0 (no match, nearest ptbtneff01.t32 @1x off 17)
33 13 24 0x07010701 alpha-over(premul) 211.2 x 54.0 ptbtn01f.t32 @1x
CONTROL — the two rotated sweep strips measured independently at 1134 and 1303 px:
tall quads found: ['1134', '1303']
PASS
=== EXTRAS (GP_TITLE build 6), 22 draws over 4 frames ===
draw prim idx blend state quad px (w x h) best name match
1 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
2 13 4 0x01010101 ADDITIVE 883.2 x 1134.0 (no match, nearest ptbase.t32 @2x off 811)
3 13 4 0x01010101 ADDITIVE 1299.2 x 1303.2 (no match, nearest ptbase.t32 @2x off 602)
4 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
5 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
6 13 4 0x01010101 ADDITIVE 819.2 x 720.0 (no match, nearest pteff03.t32 @2x off 381)
7 13 24 0x01010101 ADDITIVE 614.4 x 518.4 pteff20.t32 @2x
7 13 24 0x01010101 ADDITIVE 243.2 x 219.6 ptframe3.t32 @1x
8 13 24 0x07010701 alpha-over(premul) 352.0 x 39.6 ptmsg2.t32 @1x
8 13 24 0x07010701 alpha-over(premul) 179.2 x 32.4 pttitle.t32 @1x
12 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
13 13 4 0x01010101 ADDITIVE 889.6 x 1134.0 (no match, nearest ptbase.t32 @2x off 804)
14 13 4 0x01010101 ADDITIVE 1299.2 x 1303.2 (no match, nearest ptbase.t32 @2x off 602)
15 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
16 13 4 0x07010701 alpha-over(premul) 1280.0 x 720.0 ptbase.t32 @2x
17 13 4 0x01010101 ADDITIVE 819.2 x 720.0 (no match, nearest pteff03.t32 @2x off 381)
18 13 24 0x01010101 ADDITIVE 614.4 x 518.4 pteff20.t32 @2x
18 13 24 0x01010101 ADDITIVE 243.2 x 219.6 ptframe3.t32 @1x
19 13 24 0x07010701 alpha-over(premul) 352.0 x 39.6 ptmsg2.t32 @1x
19 13 24 0x07010701 alpha-over(premul) 179.2 x 32.4 pttitle.t32 @1x
CONTROL — the two rotated sweep strips measured independently at 1134 and 1303 px:
tall quads found: ['1134', '1303']
PASS
=== shader x blend cross-tab, main menu ===
3 ps=0x2E372EA28CC404B7 blend=0x00010001
3 ps=0x5773DC18083C4C20 blend=0x07010701
12 ps=0xE59B2B3DA4AA9008 blend=0x01010101
18 ps=0xE59B2B3DA4AA9008 blend=0x07010701