Answers question 3 of the 2026-09-02 play-test, from Canary's own source.
Gamma: Canary applies NONE. VdGetCurrentDisplayGamma is declared kStub and
only reports kernel_display_gamma_type (default 2, TV/BT.709) to the guest
so D3D can build a ramp. And the guest does not apply one either, for the
splash: its dumped pixel shader is four ALU ops -- tfetch2D, three muls, a
max -- with no pow, no ramp, no lookup.
Geometry: yes, the presenter resamples. present_letterbox defaults true and
present_safe_area_x/y default to 100, so the guest's 1280x720 is scaled to
fit the window and letterboxed with nothing cropped. That accounts for a
number the corpus has carried without explaining: captures measure the game
surface at 1279x675, and a single-pixel oracle placed at 1280x720
coordinates read the copyright line instead.
The structural consequence is the useful part. There are two measurement
paths and only one has Canary in it:
pixels guest draw -> EDRAM -> resolve -> front buffer ->
presenter (scale + letterbox) -> X11 -> screenshot -> PNG
=> carries a resample
vertex stream guest CPU writes a vertex buffer -> the draw logger reads
guest memory directly
=> carries nothing
The per-frame alpha series is the second path, which is why it can be
stated as the game's values rather than as pixels we measured. Everything
measured off a PNG carries the resample: every RMSE against a capture,
every glyph count, every surface mean.
Refutation attempt on this corpus's own founding principle, "the oracle is
the real game running in Canary, captured": it SURVIVES but needs a
qualifier it has never carried. A PNG capture is the oracle plus a
resample. Harmless for ordering, counts, durations and change; a filter in
the path for anything pixel-exact. The vertex stream is the stronger oracle
and should be preferred where the question can be asked of it.
Not measured: the resample's actual filter. I read the cvars saying scaling
happens, not the kernel doing it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jc4pciRArGHfxGGhEbwp5t
4.8 KiB
✅ What Canary does between the guest's draw and a captured pixel — and which of our measurements it touches
Status: ✅ decoded from Canary's own source. Instrument: ⟨canary-source⟩, with ⟨capture⟩ for the dumped guest shader. 2026-09-02.
Answers question 3 of ../agents/PLAYTEST-2026-09-02.md
— "present cadence, and any resolve, scale or gamma between the guest's draw and
a capture's pixels."
1 — gamma: Canary applies none
VdGetCurrentDisplayGamma is the export the play-test warns about. Reading it:
void VdGetCurrentDisplayGamma_entry(lpdword_t type_ptr, lpfloat_t power_ptr) {
*type_ptr = cvars::kernel_display_gamma_type; // default 2 = TV (BT.709)
*power_ptr = float(cvars::kernel_display_gamma_power);
}
DECLARE_XBOXKRNL_EXPORT1(VdGetCurrentDisplayGamma, kVideo, kStub);
It is declared kStub and it transforms nothing. It reports a gamma type to
the guest, which D3D would use to build a ramp. So the question becomes whether
the guest applies one — and for the splash it demonstrably does not. The
splash pixel shader, dumped from the running game, is four ALU ops:
tfetch2D r2, r1.xy, tf0
mul r1.___w, r2.wwww, r0.wwww
mul r0.xyz_, r2.xyzz, r0.xyzz
mul r1.xyz_, r0.xyzz, r1.wwww
max oC0, r1, r1
No pow, no ramp, no lookup. Nothing in the splash path applies gamma —
neither side of the emulation boundary.
2 — geometry: yes, the presenter resamples
presenter.cc defaults:
| cvar | default | effect |
|---|---|---|
present_letterbox |
true | aspect preserved, bars added rather than stretching |
present_safe_area_x / _y |
100 | nothing cropped |
So the guest's 1280×720 is scaled to fit the host window and letterboxed. That
is a resample, and it explains a number the corpus has carried without accounting
for it: captures measure the game surface at 1279×675, not 1280×720. A
single-pixel oracle placed at 1280×720 coordinates read the copyright line instead
— which is exactly the failure wait_title.sh was found to have.
3 — 🔴 So there are TWO measurement paths, and only one has Canary in it
| path | route | Canary processing |
|---|---|---|
| pixels | guest draw → EDRAM → resolve → front buffer → presenter (scale + letterbox) → X11 → screenshot → PNG |
a resample, plus whatever the window system does |
| vertex stream | guest CPU writes a vertex buffer → the draw logger reads guest memory directly | none |
📌 The per-frame alpha series is the second path. The k_8_8_8_8 colour is
read out of the guest's own vertex buffer before it reaches a shader, a render
target, a resolve or the presenter. There is no Canary processing between the
guest's intent and that number — which is why
splash-interpolates-every-frame.md can
state per-frame alphas as the game's values rather than as pixels we measured.
⚠️ Everything in this corpus measured off a PNG carries the resample. That
includes every RMSE against a capture, every glyph count, every surface mean, and
the motion-census numbers the play-test quotes for the port. It does not make
them wrong — a resample preserves change, which is what a motion census
measures — but it does mean a pixel-exact comparison is comparing through a
filter nobody has characterised.
Refutation attempt, recorded per the adversarial duty
Target: this corpus's own founding principle, stated at the top of three documents — "the oracle is the real game running in Xenia Canary, captured."
Result: it SURVIVES, but it needs a qualifier it has never carried. A capture
is the oracle plus a resample whenever it is a PNG. For ordering, counts,
durations and change — the quantities TEMPORAL-VERIFICATION.md already tells us
to prefer — the resample is harmless. For anything pixel-exact it is a filter in
the path, and the corpus has been treating PNG captures and vertex-stream captures
as the same kind of evidence when one has an uncharacterised transform in it and
the other has none.
The vertex stream is the stronger oracle and should be preferred wherever the question can be asked of it.
Reach
⟨canary-source⟩ for the gamma stub and the presenter defaults — these are facts
about this build, and a different --present_* or kernel_display_gamma_type
would change them. ⟨capture⟩ for the shader, which is the splash's; other
screens' shaders have not been checked for gamma and the negative in §1 covers
the splash only.
❔ Not measured here: the resample's actual filter. I read the cvars that say scaling happens, not the kernel that does it. What a 1280×720 → 1279×675 resample does to a thin glyph is unquantified, and that is the number anyone doing pixel-exact work against a PNG would need.