2026-09-11 - Pixel and vertex stages, the hardware that confirmed the header, and the constants I would not ship

Two things arrived together: obSCEne resolved the differential-probe ask (9f4c) with per-field hardware verdicts, and filed a new ask (4d1f) to extend selfish-shader past compute. Doing the second honestly meant not doing all of what it asked.

The probe confirmed the header, field by field

166-agc/shader-differential (sweep 20260911-002219) built a clean container and perturbed one field at a time on RDNA2 silicon. It is the hardware oracle the emulator-derived layout was waiting for, and it landed in the table's header:

"Tolerated at create" is not "may be wrong" - shader_size/target/alignment are enforced later, so they are still written correctly. The probe measures what sceAgcCreateShader guards.

Pixel and vertex: clean, because only one byte changes

The container format is stage-agnostic - only the type byte at 0x5a differs. So Container::pixel (1) and Container::vertex (2) are the compute constructor with a different value, and a test pins that a pixel or vertex container differs from a compute one at that one byte and nowhere else. Both values are sourced: craziiEmu maps 1=PS, 2=ES (the export-shader hardware stage a vertex shader runs in), and obSCEne's 166-agc/primitive-draw draws with them.

The constants I would not ship, and why

4d1f asked for six stage constants: COMPUTE=0, PIXEL=1, VERTEX=2, GEOMETRY=3, EXPORT=6, HULL=7. Three of those do not survive the only citable source for the header's type byte, craziiEmu's register switch (0=compute, 1=PS, 2/6=ES, 4=GS, 7=LS):

None of 3/6/7 appear in the 9f4c evidence either. So I shipped constants only for the three values a source establishes (0/1/2), and for anything else added Container::for_stage(type, ...)

This is the same line the whole crate draws, one level up: the format is ours to know, and a stage value is a fact that needs a source like any other. A named constant is a claim; for_stage is the honest way to build what is real without making one. (D103)

The CLI came along

selfish shader --stage compute|pixel|vertex (or a raw type value) exposes the new constructors to the C consumer, since obSCEne reaches the builder only through the binary. Same warning as before when the SH register table is too thin, now stage-aware - compute's program pair is 0x20c/0x20d, other stages' pairs are their own.