Accepted current-state audit, 2026-05-09.
This document records the GLx/OpenGL-lineage color rules used by the HDR-capable FBO path. The compatibility SDR path and r_hdr 1 with the default legacy tone mapper stay display-referred so retail Quake III visuals and demos do not shift. When a non-legacy tone mapper, color grading, or explicit HDR output selects scene-linear color, authored color textures are decoded from sRGB at sample time, lighting/post targets remain linear, and the final SDR output shader encodes to sRGB.
| Control | Default | Role |
|---|---|---|
r_hdr |
0 |
Selects display-referred SDR compatibility (0) or the HDR-capable high-precision FBO path (1). The default legacy tone mapper preserves Quake III display-referred lighting; non-legacy tone mapping, color grading, and explicit HDR output use scene-linear color. |
r_hdrPrecision |
0 |
Selects SDR/debug FBO storage precision; r_hdr 1 always requires an explicit floating-point RGBA16F scene target. |
r_hdrBloomFormat |
0 |
Selects positive-only bloom/extract intermediate storage while preserving RGBA16F for the main HDR scene target. Auto tries compact R11G11B10F for RGB bloom and falls back to RGBA16F; RG16F is reserved for positive two-channel intermediate roles. |
r_srgbTextures |
1 |
Allows hardware sRGB decode for authored color images when r_hdr 1 is actively using scene-linear color and the backend supports it. |
r_autoExposure |
1 |
Selects shared OpenGL auto-exposure behavior: 0 disabled, 1 elapsed-time time-constant adaptation with deterministic luminance reduction, 2 legacy frame-cadenced parity mode, or opt-in 3 modern-tier histogram percentile reduction with conservative fallback to mode 1 on older fixed-point tiers. |
r_glxAutoExposure |
0 |
Selects GLx scene-linear auto-exposure reduction: 0 manual, 1 tiered automatic, 2 force simple-average fallback, or 3 force histogram percentile on modern tiers with fallback on older tiers. |
r_glxAutoExposurePercentile |
80 |
Luminance percentile used by the GLx histogram auto-exposure path. |
r_screenshotCaptureMode |
0 |
Selects screenshot capture policy: 0 SDR sRGB byte output, reserved 1 scene-linear HDR export request, or reserved 2 HDR-output export request. HDR requests are explicit and currently resolve to SDR sRGB byte output until float/HDR image export is implemented. |
r_framebufferSRGB |
1 |
Allows GL_FRAMEBUFFER_SRGB only for actual sRGB-encoded draw targets. The current SDR final pass shader-encodes output, so the OpenGL path keeps this state disabled to avoid double encoding. |
r_glxColorPipelineDebug |
0 |
Emits per-frame color-pipeline metadata: 1 prints CSV rows, 2 prints JSON rows. |
r_colorGrade |
0 |
Enables the scene-linear grading stage for r_hdr 1; the default is disabled and identity-safe. |
r_colorGradeLUT |
empty | Optional 3D LUT atlas. Atlases are sampled as linear data and use width N*N, height N, with blue slices laid out horizontally. |
r_glxPostShaderTarget |
0 |
Selects the generated post/output GLSL target. 0 chooses conservatively from the GLx tier and context version, with GLSL 1.20 as the fallback; explicit values can request 120, 130, 150, 330, or 410. |
r_glxPostShaderExecute |
0 |
Opts in to binding the generated GLx post/output shader for eligible display-referred SDR gamma and scene-linear final passes. The legacy ARB path remains the default fallback. |
| Texture class | Examples | Declared space | Scene-linear sampling |
|---|---|---|---|
| Authored color maps | map textures, model skins, sky images, UI images, cinematic scratch frames, replacement default image | sRGB | OpenGL uses GL_SRGB8 / GL_SRGB8_ALPHA8 when r_hdr 1 is in scene-linear color mode and r_srgbTextures 1; Vulkan uses VK_FORMAT_R8G8B8A8_SRGB. |
| Lightmaps | BSP lightmaps, merged lightmap atlases, external maps/<map>/lm_* atlases |
linear lighting data | Never sRGB-decoded. Existing overbright/lightscale baking remains compatibility-sensitive. |
| Procedural light data | *dlight, *identityLight |
linear | Never sRGB-decoded. |
| Lookup/data textures | *fog, solid utility images |
data | Never sRGB-decoded. |
| Upload/capture targets | FBO color, bloom chain, screen-map/capture buffers | linear or SDR output as declared by pass | Not sampled as authored sRGB unless explicitly created as an authored color image. |
| Pass or target | Storage/encoding | Rule |
|---|---|---|
| Main scene FBO | SDR/debug storage uses GL_RGBA8, GL_RGBA16, or debug GL_RGBA4; r_hdr 1 uses GL_RGBA16F with upload format GL_RGBA and type GL_HALF_FLOAT |
Scene-linear rendering blends into a floating-point linear target. Init fails if r_hdr 1 does not get a float scene target. The legacy SDR path and default legacy HDR tone path remain display-referred for compatibility. |
| Bloom extraction and blur | Linear numeric storage; HDR RGB bloom uses role-based positive-only storage (R11G11B10F when complete, otherwise RGBA16F) |
Threshold, blur, and blend operate on linear scene values when r_hdr 1 is active. The main scene target is not narrowed; compact formats are limited to alpha-free positive bloom/extract intermediates. |
| Flares | Authored/display flare colors are decoded to scene-linear RGB when r_hdr 1 and r_flareSceneLinear 1 are active |
Flares enter the HDR scene before bloom extraction, so the modern path uses the same scene-linear bloom threshold, exposure, soft-knee, and final output contract. r_flareSceneLinear 0 preserves the previous display-referred flare color path for parity checks. |
| Color grading stage | Scene-linear math | Exposure is applied first, then optional lift/gamma/gain, Bradford white-point adaptation, and 3D LUT atlas grading. The stage runs before tone mapping and does not change the default image while controls remain identity. |
| Tone map stage | Scene-linear to display-referred scale | Simple Reinhard (x / (1 + x)) or the ACES-fitted curve is applied after grading when r_hdr 1 is active. Legacy names Reinhard and ACES remain accepted aliases for the current cvar values for one release cycle. |
| Output primaries stage | Linear output colorimetry | Linear sRGB/BT.709 is the default no-op, Display P3 and BT.2020 use explicit D65 matrix transforms, and native is constrained to the experimental compositor/native pass-through path where no primaries matrix is applied. unknown is not a valid selectable output state. |
| Final SDR output shader | Encodes to SDR sRGB | The shader applies the sRGB transfer function for scene-linear output. Legacy SDR keeps the existing gamma/overbright path. This is the selected SDR contract: shader encode, not fixed-function framebuffer encode. |
| Screenshot capture/export | SDR sRGB bytes by default | PNG, TGA, JPG, BMP, levelshot, cube-map screenshot, AVI frame capture, and GLx proof captures remain SDR sRGB byte outputs. HDR-aware capture modes are explicit policy markers reported through capture-request, capture-hdr-aware, and capture-supported; selected capture remains sdr-srgb until float/HDR export formats are implemented. |
GL_FRAMEBUFFER_SRGB |
Disabled in the current OpenGL final path | The final shader already writes sRGB-encoded values to the default framebuffer. Enabling fixed-function encode here would double-encode. |
Screenshot/video capture is classified as SDR sRGB evidence unless a future HDR export format is explicitly implemented and selected.
OpenGL advertises sRGB texture and framebuffer-sRGB capability from extension/core-version probes. r_speeds 7 and glxpostprocess report both the color pipeline and color audit state, including sRGB texture decode, framebuffer-sRGB state, capture color space, requested/resolved capture policy (capture-request, capture-hdr-aware, capture-supported), float scene-target state, final encode path, output primaries/gamut intent, requested versus resolved HDR precision, selected bloom storage policy/format, post-shader feature/hash diagnostics, post-shader source target diagnostics, post-shader cache compile/link/source/eviction diagnostics, direct-final shader bind diagnostics, and output-contract validity. Screenshot/video capture remains SDR sRGB unless a later explicit export mode records another selected capture space. When hardware HDR/EDR output is inactive, SDR output clamps max-output nits to paper white instead of reserving HDR headroom. The texture audit manifest reports authored sRGB, linear, data, and unknown image counts plus the number of images decoded through sRGB. Missing authored sRGB decode rows and unexpected decode on linear/data rows are proof failures. The GLx logic tests keep CPU reference helpers for sRGB transfer, simple Reinhard and ACES-fitted tone mapping, Display P3 and BT.2020 primaries transforms, native pass-through primaries constraints, PQ encoding, Bradford white-point adaptation, bloom thresholding, histogram-percentile exposure reduction, LUT atlas sizing, and trilinear LUT sampling in code/rendererglx/glx_color_math.h, with code/rendererglx/glx_post_output_reference.h composing those helpers through OutputTransform in grade -> tone-map -> output-primaries -> gamut-map -> output-transfer order and code/rendererglx/glx_post_shader_plan.h deriving the reusable shader feature key from that same transform. code/rendererglx/glx_post_shader_source.h now turns that key into deterministic GLSL 1.20 fallback source plus modern GL3X+/GL41+ compatibility-profile source targets, covering SDR sRGB, ACES-fitted/simple Reinhard, lift/gamma/gain, Bradford white-point hooks, LUT atlas sampling, bloom composite, greyscale, Display P3, BT.2020 conversion, gamut compression, HDR headroom output, and HDR10 PQ encode. The legacy ARB executor remains the default fallback while the generated shader path is disabled or rejected.
The canonical classification rows live in GLX_TEXTURE_CLASSIFICATION_MANIFEST.json. Runtime sweeps preserve color evidence in three places: compact r_speeds 7 counters, per-frame glx: color-frame-csv or glx: color-frame-json rows from r_glxColorPipelineDebug, and screenshot color/luma histograms attached to capture results. Captured PNGs also get .histogram.json, .luma-falsecolor.png, and .exposure-falsecolor.png sidecars for artifact review. Baseline comparisons report RMS, changed-pixel ratio, PSNR, and luma SSIM so small transfer drift and broad perceptual shifts are both visible. Diagnostic gates force JSON color-frame output for the run unless explicitly overridden, and then reject the artifact if per-frame color metadata is missing. Release gates require the color pipeline, color audit, output backend, texture audit, target-format metadata, performance color metadata, color-frame metadata, screenshot histogram metadata, and color-sweep luma/exposure false-color sidecar metadata to be present before accepting GLx proof.
The P0 color sweep matrix still executes texture-decode variants as separate GLx launches so latched controls such as r_srgbTextures are applied before renderer startup; output-backend request changes can now be exercised live. It includes a legacy SDR bloom-off reference, an HDR FBO legacy display-referred row, scene-linear SDR rows for simple Reinhard and ACES-fitted tone mapping, low/high exposure probes, an authored-texture sRGB-decode-off control row that must prove the compatibility path stays display-referred, an automatic output-backend row for native SDR/HDR platform evidence, and an explicit Windows scRGB request row that falls back safely when HDR output is unavailable. When the color sweep runs, the harness also emits deterministic shader-reference ramp PNGs plus histogram, luma false-color, and exposure false-color sidecars for every row. These CPU-reference artifacts are additive: offline tests can validate the evidence machinery without a live GPU, while runtime captures can later be compared against the same ramp contract.
Vulkan already selects an SDR nonlinear swapchain color space for normal presentation and HDR10 formats only behind native HDR checks. Task Q adds sRGB sampled-image formats for authored color images in the scene-linear path while keeping capture output as SDR sRGB bytes.