re(ui): the gamma confound is refuted from source -- canary applies none of its own

I had parked the tone-curve finding behind "this may be the emulator, not
the game: canary applies kernel_display_gamma_type = 2 (BT.709) on
output", with a planned run setting it to 0 and re-fitting. Reading the
source kills both the confound and the experiment.

VdGetCurrentDisplayGamma_entry is a kStub GETTER the guest calls
(xboxkrnl_video.cc). Its own comment: "Used in D3D SetGammaRamp/
SetPWLGamma to adjust the ramp for the display." The cvar is a value
REPORTED TO THE GAME, which then builds its own ramp. Canary's role is
downstream: the guest writes DC_LUT, command_processor.cc reads it into
gamma_ramp_256_entry_table_, and the swap path applies it via
swap_apply_gamma_pipeline_layout with apply_gamma_table.ps /
apply_gamma_pwl.ps compiled in.

So there is no emulator-side BT.709 post-process to subtract, and any
gamma in a captured frame is a ramp the game installed.

What is NOT established, and the reach is stated: that this game installs
a ramp at all, or that the measured 1.34-1.49 is it. The run logs cannot
say -- kernel exports log at Debug and this harness masks Kernel logging
(log_mask = 13, per boot_menu.sh's own comment), so their silence is
guaranteed regardless of what the game did.

The planned experiment was wrong in design: changing the cvar changes
what the GUEST is told and therefore which ramp the GAME builds, so it
could never isolate a stage that does not exist -- and it perturbs the
capture harness, since skip_intro classifies movie-vs-static on an
absolute rmse threshold that a brighter frame biases. The right run
changes nothing about the output: LOG_MASK=12 LOG_LEVEL=3 and look for
the call and the DC_LUT writes.

Also for the port: the ramp depends on the display type the game is told,
and canary hard-codes TV/BT.709 where hardware uses a console setting. So
this is a display profile, not a fixed property of the game.

METHOD: read what a cvar does before building an experiment around it;
and an absence in a log is only evidence if the log would have shown it.
This commit is contained in:
Sylpheed RE agent
2026-08-29 02:44:30 +00:00
parent 3490fba9e3
commit 93b0a6b20f
4 changed files with 87 additions and 13 deletions

View File

@@ -61,18 +61,61 @@ So `capture ≈ 255·(render/255)^γ` with **γ ≈ 1.34 1.49**.
* **Nothing here constrains midtones or highlights**, which is exactly where a
γ = 1.4 curve does its visible work.
## The confound: this may be the emulator, not the game
## 🔴 The confound as I stated it is REFUTED — canary applies no gamma of its own
The comparison is our composite against **what Xenia Canary displays**, and
canary applies its own output transform:
`kernel_display_gamma_type = 2` — BT.709 (HDTV) — in this container's config
(`0` linear, `1` sRGB, `3` a power set by `kernel_display_gamma_power`).
I wrote that "canary applies its own output transform: `kernel_display_gamma_type
= 2` — BT.709". **That is not what the cvar does.** Reading the source:
So the measured exponent may belong to canary's display stage rather than to the
game or the console. **The discriminating experiment is cheap and was not run:**
set `kernel_display_gamma_type = 0`, re-capture the same screen, and re-fit. If
the exponent collapses toward 1.0 it is canary's; if it survives it is upstream.
That needs one emulator run reaching the main menu.
```cpp
void VdGetCurrentDisplayGamma_entry(lpdword_t type_ptr, lpfloat_t power_ptr) {
// 1 - sRGB. 2 - TV (BT.709). 3 - use the power written to *power_ptr.
// Anything else - linear.
// Used in D3D SetGammaRamp/SetPWLGamma to adjust the ramp for the display.
*type_ptr = cvars::kernel_display_gamma_type;
...
```
It is a **getter the guest calls** (`xboxkrnl_video.cc`, exported `kStub`). The
cvar is a value *reported to the game*, which then builds its own ramp. The
emulator's role is downstream and faithful:
* the guest writes its ramp to the `DC_LUT` registers;
* `command_processor.cc` reads them into `gamma_ramp_256_entry_table_`;
* the **swap** (present) path applies them —
`swap_apply_gamma_pipeline_layout`, with `apply_gamma_table.ps` /
`apply_gamma_pwl.ps` compiled in.
So there is no emulator-side BT.709 post-process to subtract. Any gamma in the
captured frame is a ramp **the game installed**.
### 🟡 What that does and does not settle
✅ The stated confound is gone: the measured exponent is not an emulator artefact
bolted onto the game's output.
🟡 **Not established: that this game installs a ramp at all**, or that
γ ≈ 1.341.49 *is* that ramp. The run logs cannot say — kernel exports log at
Debug and this harness masks Kernel logging (`log_mask = 13`, per
`boot_menu.sh`'s own comment), so the absence of gamma lines in them is expected
and means nothing. That is the reach of this negative.
⚠️ And the ramp a game builds depends on the display type it is *told*. Canary
hard-codes `2` (TV/BT.709); on hardware that is the console's display setting. So
the exponent is display-profile dependent **by design**, not a fixed property of
the game.
### ✅ The corrected next experiment
My planned run — set `kernel_display_gamma_type = 0` and re-fit — was the wrong
design: it changes what the *guest* is told and therefore which ramp the *game*
builds, so it could never isolate an emulator stage that does not exist. It also
perturbs the capture harness, because `skip_intro.sh` classifies movie-vs-static
on an **absolute** rmse threshold and a brighter frame biases it (see
[capture-harness-status](../capture-harness-status.md)).
The right run changes nothing about the output: boot with `LOG_MASK=12
LOG_LEVEL=3` (both are needed — kernel calls log at Debug) and look for
`VdGetCurrentDisplayGamma` and the `DC_LUT` writes. Same frames, no perturbation.
## What a port should do with this