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:
@@ -631,3 +631,16 @@ agent's loop prompt, i.e. nowhere durable. See [`README.md`](README.md) for the
|
||||
any title screen. A blocker that stops one experiment does not stop every
|
||||
experiment in the same area, and it is worth re-reading the parked list against
|
||||
what each item really requires rather than against the area it belongs to.
|
||||
* **A default value is evidence.** Whether the game writes a gamma ramp looked
|
||||
like it needed a GPU trace. It mostly did not: canary initialises the ramp
|
||||
table to **identity** and applies it through a shader that is a pure LUT lookup
|
||||
with no other transfer. An unwritten ramp is therefore a no-op, and any
|
||||
non-identity transfer in the output implies a write. Reading what a field holds
|
||||
when nobody has touched it turns "I cannot observe the write" into "the write
|
||||
must have happened" — cheaper than instrumenting, though it stays an inference
|
||||
and should be labelled one.
|
||||
* **Name the weak joint of an inference in the same breath as the conclusion.**
|
||||
The chain above assumes our composite reproduces the pre-ramp framebuffer,
|
||||
which is the one step that could be wrong. Writing that down beside the
|
||||
conclusion — rather than only the supporting facts — is what lets a later
|
||||
reader attack it instead of inheriting it.
|
||||
|
||||
Reference in New Issue
Block a user