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.