re(ui): close the gamma chain from canary's defaults -- the game writes a ramp
Continues the previous iteration, where the game was measured calling VdGetCurrentDisplayGamma at video init. The remaining link -- does it then WRITE the ramp -- is a GPU register operation (XE_GPU_REG_DC_LUT_RW_INDEX in CommandProcessor::WriteRegister), unlogged and invisible to kernel logging. Two facts from the source close it without instrumenting. 1. The swap-path gamma stage is a PURE LUT. apply_gamma_table.xesli is the whole transform: index by input*255, fetch from a 256-entry ramp buffer, output. No sRGB encode, no second transfer function. 2. The table DEFAULTS TO IDENTITY. CommandProcessor::Initialize fills it with value = i * 0x3FF / 0xFF, and its own comment says the linear default is "what games set when starting with the sRGB (return value 1) VdGetCurrentDisplayGamma". An unwritten ramp is a no-op. So the only transform is a LUT, the LUT is identity unless written, the game queries the display gamma at init, and the capture differs from our composite by gamma 1.34-1.49 -- which identity cannot produce. The guest wrote a non-identity ramp. Labelled an inference, with its weak joint named: it assumes our composite reproduces the PRE-RAMP framebuffer, which it does not exactly. What carries it is the shape -- a systematic ~1.4 fitted on flat patches across three screens is not a compositor bug. The obvious alternative, a fixed sRGB stage in the presenter, fits neither direction: an encode (^0.45) brightens and we measured darkening; a decode (^2.2) darkens far more than 1.4. Direct observation remains available and cheap, and needs the emulator only to boot: a GPU trace records gamma ramps as their own command type, or one log line at the DC_LUT register write would settle it outright. Not done. METHOD: a default value is evidence; and name the weak joint of an inference in the same breath as the conclusion.
This commit is contained in:
@@ -243,8 +243,18 @@ authored version can be deleted.
|
||||
`VdGetSystemCommandBuffer` and `VdSetDisplayMode` — the moment a ramp-builder
|
||||
would ask. Control in the same log: 359 `VdRetrainEDRAM` lines, so an absent
|
||||
call would have shown.
|
||||
🟡 Still open: whether it then *writes* the ramp, and whether γ ≈ 1.4 is it.
|
||||
That write is a GPU register operation (`DC_LUT`), invisible to kernel logs.
|
||||
🟡 **That it then writes the ramp is inferred, not observed** — but the chain
|
||||
is closed: canary's swap-path gamma stage is a **pure 256-entry LUT** with no
|
||||
other transfer (`apply_gamma_table.xesli`), and that LUT **defaults to
|
||||
identity** (`CommandProcessor::Initialize`, whose comment says the linear
|
||||
default is "what games set when starting with the sRGB return value"). Identity
|
||||
cannot produce the measured γ ≈ 1.4, so a non-identity ramp was written.
|
||||
⚠️ The weak joint is that this assumes our composite reproduces the *pre-ramp*
|
||||
framebuffer; what carries it is that a systematic ~1.4 across three screens is
|
||||
not the shape of a compositor bug. A fixed sRGB stage does not fit either
|
||||
direction (encode brightens; decode darkens far more).
|
||||
**Practical upshot for you is unchanged:** the darkening is the game's own
|
||||
display ramp, so it belongs in a port as a display profile, not baked in.
|
||||
⚠️ Note the ramp depends on the display type the game is *told*; canary
|
||||
hard-codes TV/BT.709, which on hardware is a console setting. **So this is a
|
||||
display profile, not a fixed property of the game** — reasonable to expose as a
|
||||
|
||||
Reference in New Issue
Block a user