WIP: feat(vk): a Colour filter row for colour-blind players #139

Draft
simonwjackson wants to merge 9 commits from simonwjackson/Longshot:feat/colour-filter into main
Contributor

Why

Colour-coded hearts, maps and effects can be hard to distinguish for colour-blind players. This adds optional correction, not a replacement for non-colour cues.

What changed

  • Graphics: Colour filter after the HDR rows, with Off / Protanopia / Deuteranopia / Tritanopia. Saved as colour_filter=0/1/2/3, independent of quality presets.
  • Renderer: linear-RGB correction of the final game picture and HUD, before host overlays and Expanded HDR encoding. Only an actual PQ picture bypasses correction; Native selected on an SDR display still permits it.
  • Review 1090: remove the unused host option enum and replay-table entry, freeing ID 56; compare all nine matrix entries for identity; move the include outside the shader-header block; move the row after HDR; format the Python test and read text explicitly as UTF-8.
  • Merge current main snapshots normally, most recently f87557ae73df279128686b274055e6345dedc102 in 5c2175e8. Keep main's minification requirement and regenerate both SPIR-V headers from the combined shader. No history rewrite or force-push.
  • README and development guide document the menu row, saved values, PQ limitation and SDR fallback.

Flags and settings

Flag What it does Default now Recommendation Helps on Doesn't help on Untested on
Graphics → Colour filter; colour_filter=0/1/2/3 Off / Protanopia / Deuteranopia / Tritanopia correction Off (0) Keep Off by default; opt-in accessibility choice Colour-blind players, subject to individual assessment Actual Native HDR/PQ; does not replace non-colour cues Windows, NVIDIA/Intel, HDR display, colour-blind-player assessment, full playthrough

Measurements

main: f87557ae73 (2026-11-14)
this PR: feat/colour-filter @ 5c2175e8df

The pinned comparison completed on aka: main → PR → main → PR, copied Castle Town save at the wooden door, no movement/camera input. Each process warmed at the scene, then sampled 30 seconds; the last five native-presents FPS readings per setting determine each run median. The table reports the median of the two run medians. PR runs sampled Off then Protanopia after another 10-second warm-up. No FPS claim is made for other scenes or 1% lows.

Hardware: RX 7900 XT / RADV Mesa 26.2.3, Ryzen 5 7600X, 32 GB, NixOS 26.11 / Linux 7.2.7. Private headless Sway/Xwayland, 1920x1080, resolution 100%, game upscaler, window filter/sharpness/FG/HDR Off, Uncapped, VSync Auto; mesh fallback, udmabuf, audio off. The shared GPU lock was held and the owner's game was never stopped; an earlier shader-preparation attempt yielded when the owner started a game and is excluded from measurements.

main @ f87557a this PR @ 5c2175e8, Off this PR @ 5c2175e8, Protanopia
Run medians, FPS 56.1 / 56.7 56.6 / 56.8 55.7 / 56.7
Median of run medians, FPS 56.40 56.70 56.20
Run Commit Setting Last five FPS readings Median FPS
colour-main-1 f87557ae off 56.1, 56.1, 56.5, 56.0, 55.9 56.10
colour-pr-1 5c2175e8 off 56.7, 56.6, 56.4, 56.0, 56.8 56.60
colour-pr-1 5c2175e8 protanopia 55.8, 55.7, 55.8, 55.7, 55.7 55.70
colour-main-2 f87557ae off 56.6, 56.7, 56.6, 56.7, 56.8 56.70
colour-pr-2 5c2175e8 off 57.4, 56.5, 56.8, 56.9, 56.2 56.80
colour-pr-2 5c2175e8 protanopia 56.3, 56.5, 56.7, 56.9, 57.1 56.70
Exact presents-log readings
colour-main-1 off
[hos Presentation Thread] vk: 26760 presents, 56.1 fps; a frame: 1281 draws (0 skipped), 105 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (2710 ms), 0 programs (10 failed), 1317 textures (3134 MB)
[hos Presentation Thread] vk: 26880 presents, 56.1 fps; a frame: 1293 draws (0 skipped), 106 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (2710 ms), 0 programs (10 failed), 1320 textures (3121 MB)
[hos Presentation Thread] vk: 27000 presents, 56.5 fps; a frame: 1294 draws (0 skipped), 106 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (2710 ms), 0 programs (10 failed), 1320 textures (3121 MB)
[hos Presentation Thread] vk: 27120 presents, 56.0 fps; a frame: 1290 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (2710 ms), 0 programs (10 failed), 1317 textures (3093 MB)
[hos Presentation Thread] vk: 27240 presents, 55.9 fps; a frame: 1290 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (2710 ms), 0 programs (10 failed), 1320 textures (3097 MB)

colour-pr-1 off
[hos Presentation Thread] vk: 27720 presents, 56.7 fps; a frame: 1288 draws (0 skipped), 106 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1310 textures (3090 MB)
[hos Presentation Thread] vk: 27840 presents, 56.6 fps; a frame: 1291 draws (0 skipped), 104 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1309 textures (3081 MB)
[hos Presentation Thread] vk: 27960 presents, 56.4 fps; a frame: 1292 draws (0 skipped), 104 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1310 textures (3081 MB)
[hos Presentation Thread] vk: 28080 presents, 56.0 fps; a frame: 1293 draws (0 skipped), 104 dispatches, 159 passes, 157 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1318 textures (3105 MB)
[hos Presentation Thread] vk: 28200 presents, 56.8 fps; a frame: 1283 draws (0 skipped), 104 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1317 textures (3095 MB)

colour-pr-1 protanopia
[hos Presentation Thread] vk: 30000 presents, 55.8 fps; a frame: 1284 draws (0 skipped), 106 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1323 textures (3099 MB)
[hos Presentation Thread] vk: 30120 presents, 55.7 fps; a frame: 1287 draws (0 skipped), 105 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1321 textures (3080 MB)
[hos Presentation Thread] vk: 30240 presents, 55.8 fps; a frame: 1285 draws (0 skipped), 105 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1323 textures (3092 MB)
[hos Presentation Thread] vk: 30360 presents, 55.7 fps; a frame: 1292 draws (0 skipped), 105 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1325 textures (3110 MB)
[hos Presentation Thread] vk: 30480 presents, 55.7 fps; a frame: 1293 draws (0 skipped), 105 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1324 textures (3101 MB)

colour-main-2 off
[hos Presentation Thread] vk: 28440 presents, 56.6 fps; a frame: 1292 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (871 ms), 0 programs (10 failed), 1305 textures (3084 MB)
[hos Presentation Thread] vk: 28560 presents, 56.7 fps; a frame: 1289 draws (0 skipped), 105 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (871 ms), 0 programs (10 failed), 1304 textures (3075 MB)
[hos Presentation Thread] vk: 28680 presents, 56.6 fps; a frame: 1290 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (871 ms), 0 programs (10 failed), 1311 textures (3085 MB)
[hos Presentation Thread] vk: 28800 presents, 56.7 fps; a frame: 1293 draws (0 skipped), 104 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (871 ms), 0 programs (10 failed), 1313 textures (3104 MB)
[hos Presentation Thread] vk: 28920 presents, 56.8 fps; a frame: 1285 draws (0 skipped), 104 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (871 ms), 0 programs (10 failed), 1312 textures (3094 MB)

colour-pr-2 off
[hos Presentation Thread] vk: 27600 presents, 57.4 fps; a frame: 1294 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1309 textures (3081 MB)
[hos Presentation Thread] vk: 27720 presents, 56.5 fps; a frame: 1291 draws (0 skipped), 106 dispatches, 158 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1310 textures (3082 MB)
[hos Presentation Thread] vk: 27840 presents, 56.8 fps; a frame: 1291 draws (0 skipped), 106 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1311 textures (3084 MB)
[hos Presentation Thread] vk: 27960 presents, 56.9 fps; a frame: 1291 draws (0 skipped), 105 dispatches, 158 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1310 textures (3075 MB)
[hos Presentation Thread] vk: 28080 presents, 56.2 fps; a frame: 1298 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1311 textures (3084 MB)

colour-pr-2 protanopia
[hos Presentation Thread] vk: 30000 presents, 56.3 fps; a frame: 1292 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1335 textures (3109 MB)
[hos Presentation Thread] vk: 30120 presents, 56.5 fps; a frame: 1287 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1335 textures (3109 MB)
[hos Presentation Thread] vk: 30240 presents, 56.7 fps; a frame: 1290 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1333 textures (3091 MB)
[hos Presentation Thread] vk: 30360 presents, 56.9 fps; a frame: 1298 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1334 textures (3100 MB)
[hos Presentation Thread] vk: 30480 presents, 57.1 fps; a frame: 1297 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1334 textures (3100 MB)

The attached SDR captures compare HDR Off and Native selected, with the filter Off and Protanopia. The exact arithmetic proof below is game-free; these animated game captures are not claimed pixel-identical.

Game-free review-fix proof:

Check Before review fix, merged feature @ 4cdeeff This PR @ 5c2175e8
Native selected on SDR: correction remains enabled Fails Passes
Unit diagonal with any nonzero off-diagonal entry is not identity Fails Passes
Old/new correction functions, 100,000 RGB samples per supported mode Baseline All 400,000 bit-exact; Off equals its input
before: Assertion `correction(pq) == (pq ? 0 : mode)' failed
before: Assertion `!identity()' failed
PASS: correction, grey axis, exact Off, all-nine identity, SDR/PQ and sampled-view gates
PASS: 400000 SDR RGB samples, modes Off/Protanopia/Deuteranopia/Tritanopia: old/new correction is bit-exact; Off is input-exact

The last comparison compiles the actual old/new GLSL correction functions as C++, following the existing shader-math fixture approach. It proves unchanged SDR correction arithmetic, not a GPU screenshot or an FPS result. The production scaler fixture also checks SDR/PQ dispatch and pushed matrices.

Earlier game measurements remain in the PR's history at their named commits. They are not represented as measurements of this head.

Testing

Tested: On zao, with /tmp/longshot-carry/env.sh and the shared suite lock: 533 passed, 2 failed, 16 skipped in 736 s, 8 jobs, at 5c2175e8. Both failures reproduce on untouched main f87557a: test_linux_tools.py fixes PATH to /usr/bin:/bin and misses Nix tools; test_pad_layout.py lacks the extracted binding table's HoldToggle include after #137 merged. The latter is repaired by separate #338. No new failures.

Focused colour/scaler/HDR/README/ID checks: 6 passed, 0 failed. Regression checks failed before the fixes. Both shader headers were regenerated with glslang and pass hash, compilation and byte-for-byte checks. Native fixtures compile with warnings as errors. git diff --check passes; no separate formatter or typechecker is configured. Python formatting follows the neighboring tests.

Pinned main and PR Linux release builds from the NUP succeeded in isolated folders on aka. Tracked source hashes were checked before and after the current builds. Shader preparation and the four isolated gameplay runs completed. All attached screenshots were inspected. No owner game or live save was changed.

Not tested: Windows builds/gameplay and non-ASCII Windows paths, physical controllers, other GPU families, real HDR/PQ output, frame-generation combinations, colour-blind-player assessment and a full playthrough. No Windows machine is available. Requests are not passing results.

Needs other testers:

  • Windows, llvm-mingw, NVIDIA or Intel, non-ASCII game path: build main and this PR, compare Off and all filters, live changes and persistence, interleaved Off/On performance, HDR and frame generation where supported (T-RB23).
  • Combined #139 and #109: check final-image filter order, no double filtering, missing effect or new artifacts; compare HDR/upscaling/frame generation and performance where supported (T-F3WV).

Both requests are open but waiting for a maintainer's testing wanted label before testers can claim them.

Screenshots

PR attachments, verified by anonymous byte-identical downloads.

main @ f87557a: colour-main-1/off.png

this PR @ 5c2175e8: colour-pr-1/off.png

this PR @ 5c2175e8: colour-pr-1/protanopia.png

main @ f87557a: colour-main-2/off.png

this PR @ 5c2175e8: colour-pr-2/off.png

this PR @ 5c2175e8: colour-pr-2/protanopia.png

this PR @ 5c2175e8: colour-pr-2/sdr-hdr0-filter0.png

this PR @ 5c2175e8: colour-pr-2/sdr-native-filter0.png

this PR @ 5c2175e8: colour-pr-2/sdr-hdr0-filter1.png

this PR @ 5c2175e8: colour-pr-2/sdr-native-filter1.png

this PR @ 5c2175e8: colour-pr-2/filter2.png

this PR @ 5c2175e8: colour-pr-2/filter3.png

Risk and rollback

Correction can alter readability or clip colours. Actual Native HDR/PQ bypasses it. Select Off to restore the uncorrected picture, or revert the feature. Interaction with #109 and real HDR output remains unverified.

Reviewer notes

No MkwVkHost or MkwVkRenderer structure changed, so no API-version bump is needed. ID 56 is no longer consumed. Main was merged, not rebased; the template's literal rebase box remains unticked rather than claiming otherwise. The six new measurement records are in docs/tested/ under main's current format; docs/tested-configurations.md now holds a frozen historical table.

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main. Current main f87557a was merged as 5c2175e8, not rebased; no force-push.
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing.
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • No game files, dumps, build output or generated files. The generated SPIR-V headers come from authored shaders, not the dump.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

If applicable

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • Flags noted, and the switch reference updated (docs/development.md, the README).
  • Screenshots for a change that shows on screen.
  • A file in docs/tested/ for each measured run.
  • The SPIR-V headers regenerated with glslang (a shader changed).
  • A check under tests/ that reads changed source updated in this PR, not weakened.
  • Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. Linux built; Windows remains unverified (T-RB23).
  • Docs updated.
## Why Colour-coded hearts, maps and effects can be hard to distinguish for colour-blind players. This adds optional correction, not a replacement for non-colour cues. ## What changed - Graphics: Colour filter after the HDR rows, with Off / Protanopia / Deuteranopia / Tritanopia. Saved as `colour_filter=0/1/2/3`, independent of quality presets. - Renderer: linear-RGB correction of the final game picture and HUD, before host overlays and Expanded HDR encoding. Only an actual PQ picture bypasses correction; Native selected on an SDR display still permits it. - Review 1090: remove the unused host option enum and replay-table entry, freeing ID 56; compare all nine matrix entries for identity; move the include outside the shader-header block; move the row after HDR; format the Python test and read text explicitly as UTF-8. - Merge current main snapshots normally, most recently `f87557ae73df279128686b274055e6345dedc102` in `5c2175e8`. Keep main's minification requirement and regenerate both SPIR-V headers from the combined shader. No history rewrite or force-push. - README and development guide document the menu row, saved values, PQ limitation and SDR fallback. ## Flags and settings | Flag | What it does | Default now | Recommendation | Helps on | Doesn't help on | Untested on | |---|---|---|---|---|---|---| | Graphics → Colour filter; `colour_filter=0/1/2/3` | Off / Protanopia / Deuteranopia / Tritanopia correction | Off (0) | Keep Off by default; opt-in accessibility choice | Colour-blind players, subject to individual assessment | Actual Native HDR/PQ; does not replace non-colour cues | Windows, NVIDIA/Intel, HDR display, colour-blind-player assessment, full playthrough | ## Measurements main: f87557ae73df279128686b274055e6345dedc102 (2026-11-14) this PR: feat/colour-filter @ 5c2175e8dfb8536c4dd0e4a4227c102dd4715b2b The pinned comparison completed on aka: main → PR → main → PR, copied Castle Town save at the wooden door, no movement/camera input. Each process warmed at the scene, then sampled 30 seconds; the last five native-presents FPS readings per setting determine each run median. The table reports the median of the two run medians. PR runs sampled Off then Protanopia after another 10-second warm-up. No FPS claim is made for other scenes or 1% lows. Hardware: RX 7900 XT / RADV Mesa 26.2.3, Ryzen 5 7600X, 32 GB, NixOS 26.11 / Linux 7.2.7. Private headless Sway/Xwayland, 1920x1080, resolution 100%, game upscaler, window filter/sharpness/FG/HDR Off, Uncapped, VSync Auto; mesh fallback, udmabuf, audio off. The shared GPU lock was held and the owner's game was never stopped; an earlier shader-preparation attempt yielded when the owner started a game and is excluded from measurements. | | main @ f87557a | this PR @ 5c2175e8, Off | this PR @ 5c2175e8, Protanopia | |---|---|---|---| | Run medians, FPS | 56.1 / 56.7 | 56.6 / 56.8 | 55.7 / 56.7 | | Median of run medians, FPS | 56.40 | 56.70 | 56.20 | | Run | Commit | Setting | Last five FPS readings | Median FPS | |---|---|---|---|---| | colour-main-1 | f87557ae | off | 56.1, 56.1, 56.5, 56.0, 55.9 | 56.10 | | colour-pr-1 | 5c2175e8 | off | 56.7, 56.6, 56.4, 56.0, 56.8 | 56.60 | | colour-pr-1 | 5c2175e8 | protanopia | 55.8, 55.7, 55.8, 55.7, 55.7 | 55.70 | | colour-main-2 | f87557ae | off | 56.6, 56.7, 56.6, 56.7, 56.8 | 56.70 | | colour-pr-2 | 5c2175e8 | off | 57.4, 56.5, 56.8, 56.9, 56.2 | 56.80 | | colour-pr-2 | 5c2175e8 | protanopia | 56.3, 56.5, 56.7, 56.9, 57.1 | 56.70 | <details><summary>Exact presents-log readings</summary> ```text colour-main-1 off [hos Presentation Thread] vk: 26760 presents, 56.1 fps; a frame: 1281 draws (0 skipped), 105 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (2710 ms), 0 programs (10 failed), 1317 textures (3134 MB) [hos Presentation Thread] vk: 26880 presents, 56.1 fps; a frame: 1293 draws (0 skipped), 106 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (2710 ms), 0 programs (10 failed), 1320 textures (3121 MB) [hos Presentation Thread] vk: 27000 presents, 56.5 fps; a frame: 1294 draws (0 skipped), 106 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (2710 ms), 0 programs (10 failed), 1320 textures (3121 MB) [hos Presentation Thread] vk: 27120 presents, 56.0 fps; a frame: 1290 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (2710 ms), 0 programs (10 failed), 1317 textures (3093 MB) [hos Presentation Thread] vk: 27240 presents, 55.9 fps; a frame: 1290 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (2710 ms), 0 programs (10 failed), 1320 textures (3097 MB) colour-pr-1 off [hos Presentation Thread] vk: 27720 presents, 56.7 fps; a frame: 1288 draws (0 skipped), 106 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1310 textures (3090 MB) [hos Presentation Thread] vk: 27840 presents, 56.6 fps; a frame: 1291 draws (0 skipped), 104 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1309 textures (3081 MB) [hos Presentation Thread] vk: 27960 presents, 56.4 fps; a frame: 1292 draws (0 skipped), 104 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1310 textures (3081 MB) [hos Presentation Thread] vk: 28080 presents, 56.0 fps; a frame: 1293 draws (0 skipped), 104 dispatches, 159 passes, 157 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1318 textures (3105 MB) [hos Presentation Thread] vk: 28200 presents, 56.8 fps; a frame: 1283 draws (0 skipped), 104 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1317 textures (3095 MB) colour-pr-1 protanopia [hos Presentation Thread] vk: 30000 presents, 55.8 fps; a frame: 1284 draws (0 skipped), 106 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1323 textures (3099 MB) [hos Presentation Thread] vk: 30120 presents, 55.7 fps; a frame: 1287 draws (0 skipped), 105 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1321 textures (3080 MB) [hos Presentation Thread] vk: 30240 presents, 55.8 fps; a frame: 1285 draws (0 skipped), 105 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1323 textures (3092 MB) [hos Presentation Thread] vk: 30360 presents, 55.7 fps; a frame: 1292 draws (0 skipped), 105 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1325 textures (3110 MB) [hos Presentation Thread] vk: 30480 presents, 55.7 fps; a frame: 1293 draws (0 skipped), 105 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24463 pipelines (2816 ms), 0 programs (10 failed), 1324 textures (3101 MB) colour-main-2 off [hos Presentation Thread] vk: 28440 presents, 56.6 fps; a frame: 1292 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (871 ms), 0 programs (10 failed), 1305 textures (3084 MB) [hos Presentation Thread] vk: 28560 presents, 56.7 fps; a frame: 1289 draws (0 skipped), 105 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (871 ms), 0 programs (10 failed), 1304 textures (3075 MB) [hos Presentation Thread] vk: 28680 presents, 56.6 fps; a frame: 1290 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.1 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (871 ms), 0 programs (10 failed), 1311 textures (3085 MB) [hos Presentation Thread] vk: 28800 presents, 56.7 fps; a frame: 1293 draws (0 skipped), 104 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (871 ms), 0 programs (10 failed), 1313 textures (3104 MB) [hos Presentation Thread] vk: 28920 presents, 56.8 fps; a frame: 1285 draws (0 skipped), 104 dispatches, 160 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24461 pipelines (871 ms), 0 programs (10 failed), 1312 textures (3094 MB) colour-pr-2 off [hos Presentation Thread] vk: 27600 presents, 57.4 fps; a frame: 1294 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1309 textures (3081 MB) [hos Presentation Thread] vk: 27720 presents, 56.5 fps; a frame: 1291 draws (0 skipped), 106 dispatches, 158 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1310 textures (3082 MB) [hos Presentation Thread] vk: 27840 presents, 56.8 fps; a frame: 1291 draws (0 skipped), 106 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1311 textures (3084 MB) [hos Presentation Thread] vk: 27960 presents, 56.9 fps; a frame: 1291 draws (0 skipped), 105 dispatches, 158 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1310 textures (3075 MB) [hos Presentation Thread] vk: 28080 presents, 56.2 fps; a frame: 1298 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1311 textures (3084 MB) colour-pr-2 protanopia [hos Presentation Thread] vk: 30000 presents, 56.3 fps; a frame: 1292 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1335 textures (3109 MB) [hos Presentation Thread] vk: 30120 presents, 56.5 fps; a frame: 1287 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1335 textures (3109 MB) [hos Presentation Thread] vk: 30240 presents, 56.7 fps; a frame: 1290 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1333 textures (3091 MB) [hos Presentation Thread] vk: 30360 presents, 56.9 fps; a frame: 1298 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1334 textures (3100 MB) [hos Presentation Thread] vk: 30480 presents, 57.1 fps; a frame: 1297 draws (0 skipped), 105 dispatches, 159 passes, 157 barriers, 6 submits, 0.0 uploads (0.0 MB), 82 ub updates (57 overlays), GPU-bound wait 0.0 ms; 24465 pipelines (971 ms), 0 programs (10 failed), 1334 textures (3100 MB) ``` </details> The attached SDR captures compare HDR Off and Native selected, with the filter Off and Protanopia. The exact arithmetic proof below is game-free; these animated game captures are not claimed pixel-identical. Game-free review-fix proof: | Check | Before review fix, merged feature @ 4cdeeff | This PR @ 5c2175e8 | |---|---|---| | Native selected on SDR: correction remains enabled | Fails | Passes | | Unit diagonal with any nonzero off-diagonal entry is not identity | Fails | Passes | | Old/new correction functions, 100,000 RGB samples per supported mode | Baseline | All 400,000 bit-exact; Off equals its input | ```text before: Assertion `correction(pq) == (pq ? 0 : mode)' failed before: Assertion `!identity()' failed PASS: correction, grey axis, exact Off, all-nine identity, SDR/PQ and sampled-view gates PASS: 400000 SDR RGB samples, modes Off/Protanopia/Deuteranopia/Tritanopia: old/new correction is bit-exact; Off is input-exact ``` The last comparison compiles the actual old/new GLSL correction functions as C++, following the existing shader-math fixture approach. It proves unchanged SDR correction arithmetic, not a GPU screenshot or an FPS result. The production scaler fixture also checks SDR/PQ dispatch and pushed matrices. Earlier game measurements remain in the PR's history at their named commits. They are not represented as measurements of this head. ## Testing **Tested:** On zao, with `/tmp/longshot-carry/env.sh` and the shared suite lock: **533 passed, 2 failed, 16 skipped in 736 s**, 8 jobs, at `5c2175e8`. Both failures reproduce on untouched main `f87557a`: `test_linux_tools.py` fixes PATH to `/usr/bin:/bin` and misses Nix tools; `test_pad_layout.py` lacks the extracted binding table's `HoldToggle` include after #137 merged. The latter is repaired by separate #338. No new failures. Focused colour/scaler/HDR/README/ID checks: **6 passed, 0 failed**. Regression checks failed before the fixes. Both shader headers were regenerated with glslang and pass hash, compilation and byte-for-byte checks. Native fixtures compile with warnings as errors. `git diff --check` passes; no separate formatter or typechecker is configured. Python formatting follows the neighboring tests. Pinned main and PR Linux release builds from the NUP succeeded in isolated folders on aka. Tracked source hashes were checked before and after the current builds. Shader preparation and the four isolated gameplay runs completed. All attached screenshots were inspected. No owner game or live save was changed. **Not tested:** Windows builds/gameplay and non-ASCII Windows paths, physical controllers, other GPU families, real HDR/PQ output, frame-generation combinations, colour-blind-player assessment and a full playthrough. No Windows machine is available. Requests are not passing results. **Needs other testers:** - Windows, llvm-mingw, NVIDIA or Intel, non-ASCII game path: build main and this PR, compare Off and all filters, live changes and persistence, interleaved Off/On performance, HDR and frame generation where supported (T-RB23). - Combined #139 and #109: check final-image filter order, no double filtering, missing effect or new artifacts; compare HDR/upscaling/frame generation and performance where supported (T-F3WV). Both requests are open but waiting for a maintainer's `testing wanted` label before testers can claim them. ## Screenshots PR attachments, verified by anonymous byte-identical downloads. ![main @ f87557a: colour-main-1/off.png](https://git.magicbean.dev/attachments/c300cccf-e428-465c-9bbe-d9684034d767) ![this PR @ 5c2175e8: colour-pr-1/off.png](https://git.magicbean.dev/attachments/cdf4442f-110e-43c8-8210-b3f2137a2866) ![this PR @ 5c2175e8: colour-pr-1/protanopia.png](https://git.magicbean.dev/attachments/26b43a5d-1c25-4575-abd9-056dfa8f22b2) ![main @ f87557a: colour-main-2/off.png](https://git.magicbean.dev/attachments/e721875a-4925-4b2a-857e-1dd9f05bd913) ![this PR @ 5c2175e8: colour-pr-2/off.png](https://git.magicbean.dev/attachments/92da4205-db7f-4ad9-ac3a-4cca8bccbda2) ![this PR @ 5c2175e8: colour-pr-2/protanopia.png](https://git.magicbean.dev/attachments/dffbcd3c-9b6a-4147-9ac8-0041543f5413) ![this PR @ 5c2175e8: colour-pr-2/sdr-hdr0-filter0.png](https://git.magicbean.dev/attachments/eade0843-c0f3-411f-892a-a2c0ae0c71f4) ![this PR @ 5c2175e8: colour-pr-2/sdr-native-filter0.png](https://git.magicbean.dev/attachments/3c36d8c1-25b4-404a-81d3-c0142e5a01b6) ![this PR @ 5c2175e8: colour-pr-2/sdr-hdr0-filter1.png](https://git.magicbean.dev/attachments/15ce1ec7-099f-44cf-8c76-1b964dffaf48) ![this PR @ 5c2175e8: colour-pr-2/sdr-native-filter1.png](https://git.magicbean.dev/attachments/b5210fc1-3499-4d9b-a60b-32a1d5714cdf) ![this PR @ 5c2175e8: colour-pr-2/filter2.png](https://git.magicbean.dev/attachments/8fec5616-d84d-497e-b2f5-4fad5dcd2c5b) ![this PR @ 5c2175e8: colour-pr-2/filter3.png](https://git.magicbean.dev/attachments/c132c22d-bcd3-4c4f-85a0-baf1facff87b) ## Risk and rollback Correction can alter readability or clip colours. Actual Native HDR/PQ bypasses it. Select Off to restore the uncorrected picture, or revert the feature. Interaction with #109 and real HDR output remains unverified. ## Reviewer notes No `MkwVkHost` or `MkwVkRenderer` structure changed, so no API-version bump is needed. ID 56 is no longer consumed. Main was merged, not rebased; the template's literal rebase box remains unticked rather than claiming otherwise. The six new measurement records are in `docs/tested/` under main's current format; `docs/tested-configurations.md` now holds a frozen historical table. ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [ ] Rebased on current `main`. Current main `f87557a` was merged as `5c2175e8`, not rebased; no force-push. - [x] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [x] Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] No game files, dumps, build output or generated files. The generated SPIR-V headers come from authored shaders, not the dump. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### If applicable - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] Flags noted, and the switch reference updated (docs/development.md, the README). - [x] Screenshots for a change that shows on screen. - [x] A file in `docs/tested/` for each measured run. - [x] The SPIR-V headers regenerated with glslang (a shader changed). - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [ ] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. Linux built; Windows remains unverified (T-RB23). - [x] Docs updated.
feat(vk): a Colour filter row for colour-blind players
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 6s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
60d8d09509
Also proposed on Hookshot: simonwjackson/Hookshot feat-colour-filter.
Cherry-picked source: 89c5a69, e4f5407.
Resolve rename/context conflicts without changing feature behaviour.
Keep measured configurations in the PR description, not the shared table.

Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
HumanBean left a comment

Code review only. I read the diff and the code around it, and ran no tests or builds. The change is tidy. The correction matrix is right: I + E(I - S) with Machado's S and Fidaner's E, and each row is forced to sum to 1 so grey stays grey. The push constants come to 56 bytes in both the C struct and the shader's std430 block. Off keeps the old bypass. Both SPIR-V headers name the SHA-256 of the new fg_scale.comp (4c93fe5b…). third_party/ is untouched. The new row is saved under the colour_filter ini key and is exempt from presets. The branch also merges cleanly onto current main, and main hasn't touched these files since 316f9319.

I found no blockers in the code. It isn't ready to merge yet because of what the PR already says is missing: a Longshot measurement against main, the Windows test request, and the rebased base. The inline comments are suggestions:

  • HOS_OPT_COLOUR_FILTER has no reader on the host side, so it spends option ID 56 for nothing.
  • The Native HDR gate also turns correction off when Native is selected on an SDR display.
  • The shader's identity check only looks at the diagonal.
  • Row placement and test nits.

None of these needs a MKWVK_API_VERSION bump: the PR adds an enum value only, and neither MkwVkHost nor MkwVkRenderer changes. The pr status hint about the bump doesn't apply.

Code review only. I read the diff and the code around it, and ran no tests or builds. The change is tidy. The correction matrix is right: I + E(I - S) with Machado's S and Fidaner's E, and each row is forced to sum to 1 so grey stays grey. The push constants come to 56 bytes in both the C struct and the shader's std430 block. Off keeps the old bypass. Both SPIR-V headers name the SHA-256 of the new `fg_scale.comp` (4c93fe5b…). `third_party/` is untouched. The new row is saved under the `colour_filter` ini key and is exempt from presets. The branch also merges cleanly onto current main, and main hasn't touched these files since 316f9319. I found no blockers in the code. It isn't ready to merge yet because of what the PR already says is missing: a Longshot measurement against main, the Windows test request, and the rebased base. The inline comments are suggestions: - `HOS_OPT_COLOUR_FILTER` has no reader on the host side, so it spends option ID 56 for nothing. - The Native HDR gate also turns correction off when Native is selected on an SDR display. - The shader's identity check only looks at the diagonal. - Row placement and test nits. None of these needs a `MKWVK_API_VERSION` bump: the PR adds an enum value only, and neither `MkwVkHost` nor `MkwVkRenderer` changes. The `pr status` hint about the bump doesn't apply.
@ -87,0 +87,4 @@
// Correction follows scaling/sharpening, before Expanded HDR encoding and host overlays.
vec3 accessible(vec3 c) {
// Keep the old shader path exact with neutral settings, even while scaling is active.
if (pc.colour[0] == 1.0 && pc.colour[4] == 1.0 && pc.colour[8] == 1.0) return c;
Owner

The bypass only checks the diagonal. That works for today's three matrices: protan and deutan both have m[0] == 1, but m[4] and m[8] are not 1. Still, a future matrix with a unit diagonal and nonzero off-diagonal terms would be skipped without anyone noticing. Comparing all nine entries is safer, or the C side could pass an explicit on/off (for example in an unused bit of side). Nit: changing it means regenerating both headers.

The bypass only checks the diagonal. That works for today's three matrices: protan and deutan both have m[0] == 1, but m[4] and m[8] are not 1. Still, a future matrix with a unit diagonal and nonzero off-diagonal terms would be skipped without anyone noticing. Comparing all nine entries is safer, or the C side could pass an explicit on/off (for example in an unused bit of `side`). Nit: changing it means regenerating both headers.
gpu/vk/vk_api.h Outdated
@ -55,2 +55,4 @@
90, 120, 144, 165, 240, 6 Uncapped; a change applies in play) */
enum { HOS_OPT_FPS_CAP = 53 };
/* Graphics: live colour correction, 0 Off, 1 Protanopia, 2 Deuteranopia, 3 Tritanopia. */
enum { HOS_OPT_COLOUR_FILTER = 56 };
Owner

Nothing on the host side asks for HOS_OPT_COLOUR_FILTER. host/ never calls rx_option for it, and the control bridge finds rows by ini key, not by ID. The renderer reads opts.colour_filter directly, so this enum and the fill_replay_table line (vk_replay.cpp:5053) are dead, and they take option ID 56 from the IDs being shared out between the open PRs. Unless something is planned to read it, consider dropping both (and the matching asserts in tests/test_colour_filter.py). If it stays, please add it to the ID list in the option comment further down this file. Either way no API version bump is needed.

Nothing on the host side asks for `HOS_OPT_COLOUR_FILTER`. `host/` never calls `rx_option` for it, and the control bridge finds rows by ini key, not by ID. The renderer reads `opts.colour_filter` directly, so this enum and the `fill_replay_table` line (vk_replay.cpp:5053) are dead, and they take option ID 56 from the IDs being shared out between the open PRs. Unless something is planned to read it, consider dropping both (and the matching asserts in `tests/test_colour_filter.py`). If it stays, please add it to the ID list in the `option` comment further down this file. Either way no API version bump is needed.
@ -34,6 +34,7 @@
#include "shaders/fg_luma_comp_spv.h"
#include "shaders/fg_median_comp_spv.h"
#include "shaders/fg_scale_comp_spv.h"
#include "colour_filter.h"
Owner

Nit: this include splits the shaders/*_spv.h block in two, between fg_scale_comp_spv.h and fg_scale_pq_comp_spv.h and its comment. Move it above or below the block.

Nit: this include splits the `shaders/*_spv.h` block in two, between `fg_scale_comp_spv.h` and `fg_scale_pq_comp_spv.h` and its comment. Move it above or below the block.
@ -595,2 +596,3 @@
const WideSides wide = wide_now.load(std::memory_order_relaxed); // (wide_sides_tick, each present)
if (!opts.upscale && !opts.sharpness && !wide.side) return VK_NULL_HANDLE;
// Native HDR supplies PQ codes, not sRGB. Leave that path unchanged, including its SDR fallback.
const int colour = opts.hdr == 2 ? 0 : opts.colour_filter;
Owner

opts.hdr == 2 also turns correction off when Native is selected but the display isn't HDR (or dev.hdr is off). In that case the game presents SDR and pq is false, so correction would be correct, but a player who left HDR on Native silently gets none. Gating on the actual PQ picture fits better: const int colour = pq ? 0 : opts.colour_filter;, with vk_replay.cpp:3480 using !(dev.hdr && opts.hdr == 2) to match. If keeping the menu wording simple matters more, a hint on the row for when Native HDR is active would at least show the player why nothing changes.

`opts.hdr == 2` also turns correction off when Native is selected but the display isn't HDR (or `dev.hdr` is off). In that case the game presents SDR and `pq` is false, so correction would be correct, but a player who left HDR on Native silently gets none. Gating on the actual PQ picture fits better: `const int colour = pq ? 0 : opts.colour_filter;`, with vk_replay.cpp:3480 using `!(dev.hdr && opts.hdr == 2)` to match. If keeping the menu wording simple matters more, a hint on the row for when Native HDR is active would at least show the player why nothing changes.
@ -1241,6 +1245,8 @@ Row rows[] = {
"tone mapper, the same mid grey with highlights that roll off into the peak and keep their colour.",
nullptr, false, true},
{TAB_GRAPHICS, "Detail", nullptr, 0, 0, 0, nullptr, nullptr, nullptr, false, true, -1, true},
{TAB_GRAPHICS, "Colour filter", &opts.colour_filter, 0, 3, 1, tx_colour_filter,
Owner

Nit: this lands under the "Detail" heading, next to anisotropic filtering and texture detail. It reads more naturally under "Screen", or just after the HDR rows that its help text refers to. The README row would move with it.

Nit: this lands under the "Detail" heading, next to anisotropic filtering and texture detail. It reads more naturally under "Screen", or just after the HDR rows that its help text refers to. The README row would move with it.
@ -0,0 +8,4 @@
def main():
p=argparse.ArgumentParser();p.add_argument('--cxx',default='clang++');a=p.parse_args()
header = (ROOT / 'gpu/vk/vk_menu.h').read_text()
menu = (ROOT / 'gpu/vk/vk_menu.cpp').read_text()
Owner

Nit: read_text() without encoding="utf-8". vk_menu.cpp has non-ASCII bytes, so on a Windows locale code page this can fail or misread. tests/test_option_ids.py passes the encoding. The file's style (p=argparse...;a=p.parse_args(), no spaces) also differs from the other tests, so a plain reformat would help the next reader.

Nit: `read_text()` without `encoding="utf-8"`. `vk_menu.cpp` has non-ASCII bytes, so on a Windows locale code page this can fail or misread. `tests/test_option_ids.py` passes the encoding. The file's style (`p=argparse...;a=p.parse_args()`, no spaces) also differs from the other tests, so a plain reformat would help the next reader.
Author
Contributor

Added a fresh interleaved main/PR Linux measurement session at the named commits, the last-five-sample FPS medians, feature proof and inspected screenshots. Windows testing requests are open; the description names the remaining gaps. The branch also rebases cleanly onto main 1be180bb with an identical stable patch ID and passing focused tests; the force-with-lease push is still gated, so the measured remote head stays pinned. No new baseline FPS is claimed for the local rebase. The #109 interaction has its own open request.

Added a fresh interleaved main/PR Linux measurement session at the named commits, the last-five-sample FPS medians, feature proof and inspected screenshots. Windows testing requests are open; the description names the remaining gaps. The branch also rebases cleanly onto main 1be180bb with an identical stable patch ID and passing focused tests; the force-with-lease push is still gated, so the measured remote head stays pinned. No new baseline FPS is claimed for the local rebase. The #109 interaction has its own open request.
simonwjackson changed title from WIP: feat(vk): a Colour filter row for colour-blind players to feat(vk): a Colour filter row for colour-blind players 2026-11-13 07:49:56 +00:00
Merge current Longshot main into feat/colour-filter
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 9s
Website / Website on Cloudflare (pull_request_target) Has been skipped
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
12f6f1208d
Keep current HDR documentation and the Colour filter row in menu order.

Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
Author
Contributor

Merged main snapshot dd02c6cca9 with a merge commit and normal push; resolved README by keeping main’s HDR text and Colour filter in menu order. Four focused checks pass; full suite: 504 passed, 3 failures reproduced on untouched main, 16 skipped; Linux build and isolated Castle Town smoke pass, and newer main fdc2840b still merges cleanly (details in Testing).

Merged main snapshot dd02c6cca9582f5bb1b992d5c43268bf5d846c3f with a merge commit and normal push; resolved README by keeping main’s HDR text and Colour filter in menu order. Four focused checks pass; full suite: 504 passed, 3 failures reproduced on untouched main, 16 skipped; Linux build and isolated Castle Town smoke pass, and newer main fdc2840b still merges cleanly (details in Testing).
Resolve README HDR wording with main’s current text and retain both independent option IDs.

Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
Address review 1090: free the unused host option ID, gate correction and sampled views on actual PQ output, test all identity entries, keep shader includes together and move the menu row after HDR. Cover Native-on-SDR and identity regressions; document the ini key.

Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
Match the expected correction matrix to the dispatched SDR/PQ pipeline, not the saved HDR selection. Retain the PQ identity checks and cover Native selected with an SDR source.

Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
Merge current Longshot main f87557a into colour filter
Some checks failed
Website / Website on Cloudflare (pull_request_target) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 4s
CI / Conventional Commit title (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been cancelled
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been cancelled
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been cancelled
CI / Workflow lint (actionlint) (pull_request) Has been cancelled
Third-party parts / As their notes say (pull_request) Has been cancelled
Third-party parts / The same as upstream (pull_request) Has been cancelled
Website / Website on Cloudflare (pull_request) Has been cancelled
5c2175e8df
Combine main’s minification requirement with colour correction in the scaler bypass; regenerate both shader headers from the combined source.

Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
simonwjackson changed title from feat(vk): a Colour filter row for colour-blind players to WIP: feat(vk): a Colour filter row for colour-blind players 2026-11-14 02:51:55 +00:00
Author
Contributor

Main merged at f87557a as 5c2175e8; resolved README/API and later scaler-bypass conflicts, regenerated both shaders; six focused checks pass, full suite 533 pass/2 inherited failures/16 skip; current-head GPU comparison queued.

Main merged at f87557a as 5c2175e8; resolved README/API and later scaler-bypass conflicts, regenerated both shaders; six focused checks pass, full suite 533 pass/2 inherited failures/16 skip; current-head GPU comparison queued.
Author
Contributor

Review 1090 addressed in 473cc95 and 6e79347, with main merged normally through 5c2175e8 (main f87557a). No force-push.

  1. Unused ID: removed HOS_OPT_COLOUR_FILTER and its replay-table entry; the renderer still reads opts.colour_filter. ID 56 is free. No ABI structure changed.
  2. Actual PQ gate: const int colour = pq ? 0 : opts.colour_filter;; the sampled-view gate uses !(dev.hdr && opts.hdr == 2). Native selected on SDR now retains correction. Regression fails before and passes after.
  3. Identity bypass: checks all nine matrix entries. Tests perturb every entry, including unit-diagonal/off-diagonal cases. Both SPIR-V headers regenerated with glslang, including main's subsequently merged minification change.
  4. Include: moved colour_filter.h above the shader-header block.
  5. Row and docs: Colour filter follows the HDR rows, before Detail. README order matches; README and development guide document colour_filter, all values, default Off, PQ bypass and Native-on-SDR behavior.
  6. Python: explicit encoding="utf-8", normal formatting, and compiled production gate/identity checks. Updated the scaler fixture's expected matrix to follow the actual SDR/PQ pipeline.
  7. SDR arithmetic proof: the actual old/new shader correction functions compiled as C++ produce bit-exact results for 400,000 RGB samples across Off and all three filters; Off equals its input. This is shader-math proof, not a GPU or FPS claim.
  8. Checks: current pinned suite on zao under the lock: 533 passed, 2 failed, 16 skipped. Both failures reproduce on untouched main: Nix PATH in test_linux_tools, and HoldToggle missing from test_pad_layout after #137 merged (fixed separately by #338). Six focused checks pass. Pinned Linux main/PR builds succeeded on aka; source hashes match. git diff --check passes.
  9. Remaining measurement blocker: the interleaved main/PR Off/Protanopia run and SDR captures are queued after longshot-winperf-before. The owner's active game is untouched. The Measured and Screenshots boxes remain unticked; the description no longer claims old runs measure this head. Captures will be PR attachments, not private-fork links.
  10. Windows: T-RB23 already exists, but awaits a maintainer's testing wanted label. Please add that label so testers can claim it. No Windows result is claimed, and its checklist box remains unticked. T-F3WV covers the #109 combination.

The template's exact rebase line is present but unticked: main was merged, not rebased. The branch stays WIP while the pending checks and measurements remain. There is no re-request-review command documented in docs/forge.md.

Review 1090 addressed in `473cc95` and `6e79347`, with main merged normally through `5c2175e8` (main `f87557a`). No force-push. 1. **Unused ID:** removed `HOS_OPT_COLOUR_FILTER` and its replay-table entry; the renderer still reads `opts.colour_filter`. ID 56 is free. No ABI structure changed. 2. **Actual PQ gate:** `const int colour = pq ? 0 : opts.colour_filter;`; the sampled-view gate uses `!(dev.hdr && opts.hdr == 2)`. Native selected on SDR now retains correction. Regression fails before and passes after. 3. **Identity bypass:** checks all nine matrix entries. Tests perturb every entry, including unit-diagonal/off-diagonal cases. Both SPIR-V headers regenerated with glslang, including main's subsequently merged minification change. 4. **Include:** moved `colour_filter.h` above the shader-header block. 5. **Row and docs:** Colour filter follows the HDR rows, before Detail. README order matches; README and development guide document `colour_filter`, all values, default Off, PQ bypass and Native-on-SDR behavior. 6. **Python:** explicit `encoding="utf-8"`, normal formatting, and compiled production gate/identity checks. Updated the scaler fixture's expected matrix to follow the actual SDR/PQ pipeline. 7. **SDR arithmetic proof:** the actual old/new shader correction functions compiled as C++ produce bit-exact results for 400,000 RGB samples across Off and all three filters; Off equals its input. This is shader-math proof, not a GPU or FPS claim. 8. **Checks:** current pinned suite on zao under the lock: **533 passed, 2 failed, 16 skipped**. Both failures reproduce on untouched main: Nix PATH in `test_linux_tools`, and `HoldToggle` missing from `test_pad_layout` after #137 merged (fixed separately by #338). Six focused checks pass. Pinned Linux main/PR builds succeeded on aka; source hashes match. `git diff --check` passes. 9. **Remaining measurement blocker:** the interleaved main/PR Off/Protanopia run and SDR captures are queued after `longshot-winperf-before`. The owner's active game is untouched. The Measured and Screenshots boxes remain unticked; the description no longer claims old runs measure this head. Captures will be PR attachments, not private-fork links. 10. **Windows:** T-RB23 already exists, but awaits a maintainer's `testing wanted` label. Please add that label so testers can claim it. No Windows result is claimed, and its checklist box remains unticked. T-F3WV covers the #109 combination. The template's exact rebase line is present but unticked: main was merged, not rebased. The branch stays WIP while the pending checks and measurements remain. There is no re-request-review command documented in `docs/forge.md`.
docs: record pinned colour-filter comparison
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 3s
4f0af1ef2a
Assisted-by: OpenAI GPT-6 Astra
Co-Authored-By: OpenAI GPT-6 Astra <[email protected]>
Author
Contributor

The queued main/PR comparison is done (main f87557a against 5c2175e8; the new head 4f0af1e adds only the six docs/tested/ records). Off: 56.40 against 56.70 fps, Protanopia 56.20, two interleaved runs each, so within noise. The description now has the raw log lines, the inspected screenshots as attachments (Off, the three filters, and SDR output with HDR Off against Native selected, which shows correction still applies on an SDR display), and the per-run records. Windows is still untested (T-RB23, waiting for the testing wanted label).

The queued main/PR comparison is done (main f87557a against 5c2175e8; the new head 4f0af1e adds only the six `docs/tested/` records). Off: 56.40 against 56.70 fps, Protanopia 56.20, two interleaved runs each, so within noise. The description now has the raw log lines, the inspected screenshots as attachments (Off, the three filters, and SDR output with HDR Off against Native selected, which shows correction still applies on an SDR display), and the per-run records. Windows is still untested (T-RB23, waiting for the `testing wanted` label).
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request) Blocked by required conditions
PR description / Description follows the template (pull_request_target) Successful in 3s
This pull request is marked as a work in progress.
Some workflows are waiting to be reviewed.
This branch is out-of-date with the base branch
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'feat/colour-filter:simonwjackson-feat/colour-filter'
git switch 'simonwjackson-feat/colour-filter'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!139
No description provided.