feat(vk): add selectable CRT monitor and TV filters #109

Open
mooki wants to merge 7 commits from mooki/Longshot:feat/crt-filters into main
Member

Why

Players who want a CRT picture can choose a clean aperture grille, a curved consumer-TV shadow mask or two lighter scanline looks, without an external injector. Off remains the default. This first slice follows the proposal in #70; its three maintainer questions are still unanswered, so the selection and defaults may change with those answers.

What changed

  • Graphics menu: gpu/vk/vk_menu.cpp adds CRT filter and CRT lines immediately after Window scaling filter. They apply without restarting, use the normal options-file reader/writer and do not change the quality preset.
  • Four choices, three upstream algorithms: Lottes aperture grille, Lottes consumer TV, Hyllian Fast and zfast CRT. The two Lottes profiles use the same complete shader with different mask/curvature parameters. The flat grille is professional-monitor-style, not a calibrated Sony PVM/BVM model.
  • Output processing: gpu/vk/vk_framegen.cpp and crt_policy.h reduce the finished picture to a virtual raster, then run the selected CRT shader. Existing window scaling, sharpness and ultrawide-side treatment come first, including the mandatory downscale when the checked source rectangle is larger than the destination. CRT reads that destination-size result. The game's HUD is filtered; Longshot's menu and overlays come afterwards. Generated frames take the same output path, rather than putting a mask into optical flow; that windowed path still needs runtime testing.
    When the finished game picture is larger than the destination, the output-effects path first applies main's filtered downscale from #307, even with Window scaling filter Off and Sharpness 0, then feeds that destination-size picture into the CRT virtual raster. This can change fine-detail sampling compared with this pull request's earlier head; the CRT mask and scanlines are still generated at the final destination size, not shrunk afterwards. At 1982a687, windowless captures on Linux with an RTX 4090 and the capture's effects switch on (RX_VK_SHOT_FX=1) showed Off, all four filters and Off again at 1920x1080 to 1920x1080 and at 3840x2160 to 1920x1080: the inspected 1:1 crops keep the expected mask and scanline patterns with no doubled grid and no obvious extra moire. A real window and motion were not tested for this.
  • Default Off: current main's blit/scale path remains, including its mandatory downscale, with no CRT dispatch or CRT resource creation. Images and pipelines are created only when a filter is first used, then reused. Resources already created remain cached after returning to Off until exit.
  • Colour: these are SDR shaders. Expanded HDR can expand their SDR output through the existing conversion. Actual Native HDR/PQ bypasses them and explains the bypass in the help text, keeping the saved selection. Neither HDR mode was judged on an HDR display here.
  • Sources and licensing: unchanged originals, their hashes and notices are in the new third_party/crt_shaders/ folder. Timothy Lottes' public-domain shader, Hyllian's MIT Fast shader (cgwg's mask; DariusG/hunterk ports), and Greg Hogan / SoltanGris42's GPL-2.0-or-later zfast implementation are pinned to 2345ee91602a7404d3a1a6a5df6108d7305fdad4. Compute adaptations stay outside that folder; their differences are documented in its provenance file.
  • Build and checks: four shader sources have glslang-generated SPIR-V headers. Tests check upstream-byte identity, retained filter math/defaults, shader-header identity, option persistence/clamping, preset preservation, virtual-raster geometry, output ordering and allocation-failure handling. No new runtime dependency or host/renderer API field.

Current-main integration: the carry keeps all six existing commits, then adds one separate test/documentation commit. Main's downscale fix in #307 conflicts with the CRT entry path in gpu/vk/vk_framegen.cpp. The resolution keeps its checked-crop shrink decision and makes it part of the scaling decision: a larger source is filtered to the destination first, then CRT reduces that result to its virtual raster and applies the mask. CRT Off retains main's downscale. Same-size output still avoids the scale pass when no other window effect needs it. Native PQ still scales in 10 bits and bypasses SDR CRT.

Main's Intel compiler guard from #252 remains around shader and pipeline creation, including CRT's use of the shared constructor. The resource-failure fixture follows those guarded calls without removing its allocation checks. The options fixture also follows the Plus/Minus definitions from #137 and the real restart-value helper from #136. No production menu behavior or shader algorithm was changed in the separate test commit.

Picture check still needed: shrinking with Window scaling filter Off and Sharpness 0 now passes through main's filtered downscale before CRT, instead of feeding the larger source directly to CRT. The extra stage and destination-sized input can change fine detail and moire. The carried head needs pictures of this combination before publication; the earlier pictures and timings remain evidence only for their named older heads. The order after frame generation and before Longshot overlays is unchanged. Main's results files and every imported shader remain unchanged.

Flags and settings

Flag What it does Default now Recommendation Helps on Doesn't help on Untested on
Graphics → CRT filter Off, Lottes aperture grille, Lottes consumer TV, Hyllian Fast, zfast CRT; changes live Off Off by default: an aesthetic choice, not an image-quality fix Players wanting a CRT presentation Actual Native HDR/PQ is bypassed Windows, AMD, Intel, Steam Deck, HDR displays, windowed presentation
crt_filter Saves the choices above as 0, 1, 2, 3, 4 0 Off by default: preserve the original picture Persistent selection Does not load arbitrary Slang presets Same runtime gaps as the row
Graphics → CRT lines Chooses 240, 480 or 720 virtual lines, capped at half the destination height, with width following aspect 240 Off by default: inactive while CRT filter is Off; 240 is the initial look when enabled A consistent scanline density independent of Render resolution Does not force 4:3 or integer scaling; non-integer ratios can show moire Other line choices in the game, ultrawide runtime, portrait displays
crt_lines Saves 240 / 480 / 720 as 0 / 1 / 2; applies live 0 Off by default: has no effect without a CRT filter Persistent virtual-raster choice Cannot reproduce a particular monitor's measured characteristics Same runtime gaps as the row

The remake is not a 240-line game: this deliberately downsamples its finished picture for the selected CRT look. More virtual lines preserve more of its small text. The shader-regeneration helper also has --glslang to select a compiler and --check to compare headers rather than write them; ordinary player builds use the checked-in headers.

Measurements

Quiet unused-feature cost: 2026-11-13

CRT Off showed no measurable frame-time penalty in this scene. The median of the two run means was 10.648 ms on main and 10.562 ms with this PR, a difference of -0.086 ms (-0.80%). That difference is smaller than either build's repeat spread; it is not a speed-up claim or proof of literally zero cycles. It meets the predeclared no-penalty band of main + max(0.10 ms, 2%); ambiguous or unstable pairs would not pass.

main: 0c74253d4c, v3.0.0-rc.3, source cb01b5a3
this PR: move/feat/crt-filters-m0c74253d @ 2ad245bd1b7f5c1154090d6d1ece66e484adb769, v3.0.0-rc.3-5-g2ad245bd1, source 9aeb0cbf
Hardware: RTX 4090, NVIDIA 615.78.08; Core i9-13900K, 94 GiB RAM; Arch Linux 7.2.15-1-omarchy. The later 0b4cd574 carry changes documentation, not the measured code.

Scene/settings: the same copied Deku Tree save, stationary input at the central web; 1920×1080 output; 50% (960×540), game upscaler; uncapped; sdr; frame generation, v-sync, frame pacing and crt off; game speed per frame; no mods; prepared/warmed caches; timestamp profiler off. Both Linux builds use matching compiler/profile inputs; main was reused, not rebuilt for this pair. Both shader-preparation commands completed, each logging one failed shader program. Their selected scene was then warmed and inspected; the measured phases' complete renderer log blocks reported 0.0 draw omissions and 0.00 shader waits.

Method: A1 main / B1 PR Off / A2 main / B2 PR Off, in one quiet window after a four-minute cooldown. Each process ran for 75 seconds. After observed stable play, 18 seconds let the boot input and scene settle, then a 10-second neutral-input phase supplied the game-present-return intervals. These are CPU-clock present-return intervals, not scan-out, display latency or a labelled CRT GPU span. The GPU column is the existing rounded renderer counter averaged over seven complete 120-present blocks per phase; it is a separate population. Before/after pictures were inspected. The paired worker lasted 309.9 seconds.

At admission, load was 1.86, five-second CPU use 0.586 cores and GPU use 6% (the agreed ceiling was 12%). No other game or compiler was observed. Hyprland, Xwayland, quickshell/qs, tern and one terminal-tooling Chromium GPU process were left running. Each measured phase stayed below the unchanged 3-core mean / 6-core peak other-work limits; each build's repeat spread stayed below 5%. Later load averages include the preceding game and are shown rather than described as idle.

Statistic main @ 0c74253d this PR @ 2ad245bd
Median of the two run-mean frame times 10.648 ms 10.562 ms
Same-build repeat spread 0.88% 1.25%
Run Build Mean / p99 (ms) GPU counter (ms) Other CPU: mean / peak 5 s (cores) Load: start → end >1 s present gaps
A1 main 0c74253d 10.694 / 13.654 3.971 0.496 / 0.581 1.81 → 10.66 0
B1 PR, CRT Off 2ad245bd 10.496 / 13.059 4.000 0.608 / 0.615 10.66 → 7.73 0
A2 main 0c74253d 10.601 / 13.370 3.986 0.574 / 0.600 7.73 → 6.50 0
B2 PR, CRT Off 2ad245bd 10.628 / 13.599 4.000 0.585 / 0.608 6.50 → 6.50 0

All four runs were valid, with complete journal coverage and no fatal/device/kernel fault. No present-return gap over one second occurred anywhere in these four traces. B2 did retain one PlayReporter ... ran without a core scheduler warning near present 10,560, before its measured 13,232–14,172 interval. That warning's two-second wait is not a two-second gap between presents.

How to reproduce: use the settings above and the same stationary scene on both builds, warm it first, leave CRT Off, and alternate main/PR/main/PR with the same observed phase. This run also used RX_VK_PRECOMPILE=0, RX_VK_NO_WARM=1, RX_VK_NO_PREDICT=1, RX_VK_AUTO_PRESET=0, RX_VK_AUTO_TUNE=0 and RX_VK_PASS_TIMING=0 on both; the Uncapped row was used without an HOS_FPS_MAX override.

Not measured in this window: any enabled filter, 4K, windowed or generated-frame output, HDR, or synchronized-input pixel equality. Only the unused-feature comparison fit the one complete repeated window; the older per-filter GPU spans below remain diagnostics, not those missing measurements.

Measured run rows (kept here, not in the shared results file)
| Date | Build | OS / desktop | GPU / driver | CPU / RAM | Settings | Where | Result |
|---|---|---|---|---|---|---|---|
| 2026-11-13 | main 0c74253d; longshot v3.0.0-rc.3 (source cb01b5a3) | Arch Linux 7.2.15-1-omarchy / Hyprland; windowless | RTX 4090 / NVIDIA 615.78.08 | i9-13900K / 94 GiB | 1920×1080 output; 50% (960×540), game upscaler; Uncapped; SDR; frame generation, V-Sync, frame pacing and CRT Off; Game speed Per frame; no mods; prepared/warmed caches; timestamp profiler Off | Deku Tree, stationary input at the central web | A1, quiet A/B/A/B: 10.694 ms mean, 13.654 ms p99; 934 present-return intervals / 9.988 s; GPU log counter 3.971 ms. Other CPU 0.496 mean / 0.581 peak cores; load 1.81→10.66. Valid; zero >1 s present gaps, fatal/device/kernel faults; complete journal; bounded stop (124). |
| 2026-11-13 | CRT PR 2ad245bd; longshot v3.0.0-rc.3-5-g2ad245bd1 (source 9aeb0cbf) | Arch Linux 7.2.15-1-omarchy / Hyprland; windowless | RTX 4090 / NVIDIA 615.78.08 | i9-13900K / 94 GiB | 1920×1080 output; 50% (960×540), game upscaler; Uncapped; SDR; frame generation, V-Sync, frame pacing and CRT Off; Game speed Per frame; no mods; prepared/warmed caches; timestamp profiler Off | Deku Tree, stationary input at the central web | B1, quiet A/B/A/B: 10.496 ms mean, 13.059 ms p99; 951 present-return intervals / 9.982 s; GPU log counter 4.000 ms. Other CPU 0.608 mean / 0.615 peak cores; load 10.66→7.73. Valid; zero >1 s present gaps, fatal/device/kernel faults; complete journal; bounded stop (124). |
| 2026-11-13 | main 0c74253d; longshot v3.0.0-rc.3 (source cb01b5a3) | Arch Linux 7.2.15-1-omarchy / Hyprland; windowless | RTX 4090 / NVIDIA 615.78.08 | i9-13900K / 94 GiB | 1920×1080 output; 50% (960×540), game upscaler; Uncapped; SDR; frame generation, V-Sync, frame pacing and CRT Off; Game speed Per frame; no mods; prepared/warmed caches; timestamp profiler Off | Deku Tree, stationary input at the central web | A2, quiet A/B/A/B: 10.601 ms mean, 13.370 ms p99; 942 present-return intervals / 9.986 s; GPU log counter 3.986 ms. Other CPU 0.574 mean / 0.600 peak cores; load 7.73→6.50. Valid; zero >1 s present gaps, fatal/device/kernel faults; complete journal; bounded stop (124). |
| 2026-11-13 | CRT PR 2ad245bd; longshot v3.0.0-rc.3-5-g2ad245bd1 (source 9aeb0cbf) | Arch Linux 7.2.15-1-omarchy / Hyprland; windowless | RTX 4090 / NVIDIA 615.78.08 | i9-13900K / 94 GiB | 1920×1080 output; 50% (960×540), game upscaler; Uncapped; SDR; frame generation, V-Sync, frame pacing and CRT Off; Game speed Per frame; no mods; prepared/warmed caches; timestamp profiler Off | Deku Tree, stationary input at the central web | B2, quiet A/B/A/B: 10.628 ms mean, 13.599 ms p99; 940 present-return intervals / 9.990 s; GPU log counter 4.000 ms. Other CPU 0.585 mean / 0.608 peak cores; load 6.50→6.50. Valid; zero >1 s present gaps, fatal/device/kernel faults; complete journal; bounded stop (124). One virtual-core scheduler warning before the measured phase. |

Branch state: on main 5e072fc8c2adadba5ea610148be1c47800ba0cca; head e94d24624d11798c61ebed51e3f82cd998abc8be; whole game-free suite on this head: 546 passed, 2 failed, 16 skipped (main 5e072fc8c2adadba5ea610148be1c47800ba0cca: 542 passed, 3 failed, 16 skipped). The 2 failing tests fail on main the same way: tests/test_module_ids.py, tests/test_pad_layout.py. The branch was carried onto this main by hand; what differs from a mechanical carry is named in the comment posted with this update.

Historical filter-on evidence: the following table and reference pictures retain the actual older commits that produced them. The new unused-feature comparison above is separate; enabled-filter and 4K costs have not been measured in that window.

main: d725b363 (2026-11-12), built as v2.2.0-rc.1-20-gd725b3631, source 33703252
this PR: move/feat/crt-filters @ b6c14ce6, historical pre-rebase build, source f600c365; not the rebased head above
Hardware: RTX 4090, NVIDIA 615.78.08; Core i9-13900K, 94 GiB RAM; Arch Linux 7.2.15-1-omarchy; 2026-11-13 UTC
Scene: the same Deku Tree save and Items pause page; 1920×1080 and 3840×2160 output/captures; game's own upscaler, 50% render resolution, frame generation Off, 60 cap, SDR, 240 virtual lines, no mods
Method: one reference and one candidate session at each size, windowless. The candidate selects Off, each filter and restored Off, with 1,200 further game presents per selection. Full frames and nearest-neighbour pixel crops were inspected. RX_VK_PASS_TIMING=1 supplies the GPU spans below; only complete, single-filter 600-present windows are listed.
How to reproduce: load a Deku Tree save, open Items, choose the output size, Render resolution 50%, frame generation Off and SDR; change CRT filter through each choice with CRT lines at 240. For the diagnostic lines, set RX_VK_PASS_TIMING=1 and wait beyond 600 presents after each change.

These are diagnostics under shared load, not performance measurements. These older sessions were not a quiet, interleaved comparison; the new Off result is reported separately above. Other GPU work was present before admission; its exact overlap with the 1080p sessions was not recorded. The 4K sessions excluded other test games but still shared heavily loaded CPUs. This is not an FPS, display-latency or generated-frame speed claim.

Observation main @ d725b363 this PR @ b6c14ce6
1080p session 2,891 presents, exit 0; load 27.54 → 34.85 8,069 presents, exit 0; load 60.51 → 18.31
4K session 3,006 presents, exit 0; load 137.20 → 72.78 8,057 presents, exit 0; load 111.51 → 124.82
Fatal, crash, device-loss and kernel-fault counts 0 in both; journal coverage complete 0 in both; journal coverage complete
CRT option regression Fails: missing production CRT filter row Passes
Off path, production fixture Existing blit/scale route Same route; no CRT call or allocation

Diagnostic GPU span per present, downsample plus CRT; not a measurement:

Filter this PR @ b6c14ce6, 1920×1080 this PR @ b6c14ce6, 3840×2160
Lottes aperture grille 0.687 / 0.546 ms 0.999 ms
Lottes consumer TV 0.624 ms 0.900 / 0.775 ms
Hyllian Fast 0.181 ms 0.239 ms
zfast CRT 0.201 ms 0.221 ms

Each slash lists separate complete windows, not paired repetitions. The timestamp profiler orders work and can change its cost; these numbers are not a prediction for normal presentation.

vk: CRT crt-lottes-aperture: 427x240 virtual raster, 1920x1080 output; after scaling, before Longshot overlays
vk:     m crt-lottes-aperture   0.687 ms  15.3%      1.0 spans       1.0 items     0.00 MB    686.5 us a span
vk:     m crt-lottes-tv   0.624 ms  14.3%      1.0 spans       1.0 items     0.00 MB    624.4 us a span
vk:     m crt-hyllian   0.181 ms   5.4%      1.0 spans       1.0 items     0.00 MB    181.4 us a span
vk:     m crt-zfast   0.201 ms   6.0%      1.0 spans       1.0 items     0.00 MB    200.6 us a span

Off identity: the production-path fixture checks that Off adds no CRT work and preserves the existing scaling/cropping/HDR routes. The inspected captures show the unfiltered picture returning. They are not bit-identical independent game snapshots: exact full-frame comparison at 1080p finds differences in the separately captured world background; switching back to Off differs in the animated selection region. The new quiet Off comparison above supplies timing evidence at 1080p only. Synchronized-input pixel proof is still missing; the captures do not establish byte-for-byte equality.

Testing

Tested: the focused Linux coverage is 24 passing driver invocations: 10 compiled drivers with both GCC and Clang, plus four source/shader drivers. These cover the CRT tests, every main test that reads the hand-merged frame-generation file, and main's downscale shader check. The corrected options driver passes at 1982a687; the other positive checks passed at 1dfd7d62, before that options-fixture-only amendment, and were not repeated. Both compilers also reject the old CRT scaling function and a version that puts CRT before the required downscale, four expected runtime failures in total. No assertion or warning was weakened. These are focused results, not a whole-suite total.

The new cases cover CRT Off and all four filters while shrinking in either dimension, scale-before-CRT ordering and input size, checked cropping, unavailable-filter fallback, same-size/upscale behavior and Native-PQ bypass. All four CRT shader arrays match local recompilation and their original imports match the pinned hashes. Main's scale shader passes its source-hash and compilation checks; its different glslang version means its generated bytes were not compared.

The CRT options and raster-policy fixtures pass Windows-target syntax checks. The resource and scaling fixtures remain blocked by the syntax check's rule against their inherited conditional warning definitions; those definitions were not changed to make the check pass. None of the changed C++ fixtures was executed on Windows. The initial options compile failures are retained in the update history, not counted as passes.

Earlier focused checks at 2ad245bd remain historical evidence. The original imports and shader algorithms are unchanged, while the new downscale integration still needs a picture check.

Linux builds at main 0c74253d and this PR 2ad245bd now have matched preparation, an inspected ordinary warm-up each, and the four valid Off-only 1080p runs under Measurements. The built source stamps are cb01b5a3 / 9aeb0cbf. Both preparation commands completed with one failed shader program reported; the selected measured phases logged no draw omissions or shader waits. The eight timing captures were inspected. No fatal/device/kernel faults or >1 s present gaps were observed in those four runs; B2's one virtual-core scheduler warning preceded its measured phase.

The older Linux builds and four windowless game sessions below remain historical reference evidence: Off and all four filters at 1080p/4K, live changes, return to Off and no observed fatal/device/kernel faults with complete journal coverage. The old full-suite result (513 passed, 1 failed, 20 skipped at 4c673972) is not a result for this rebase. Likewise, the two older Windows-target fixture compiles are not a Windows build of this head.

The same tests/test_crt_options.py was now pointed at main 0c74253d: it fails with FAIL: missing production CRT filter row before compilation, because this feature is absent. It passes on 2ad245bd, whose source is unchanged by the later documentation-only carry to 0b4cd574.

Built for Windows and Linux at the current head, not run: At e94d24624d11798c61ebed51e3f82cd998abc8be, the Windows x86-64 Vulkan renderer DLL and all 47 selected host translation units were cross-built on Linux with llvm-mingw 20261104 (Clang 23.1.5), using the project's production flags and pinned dependencies (renderer: 35 newly compiled / 170 reused; host: 1 newly compiled / 46 reused; lifted C: 0 newly compiled / 80 reused; all Windows units: 36 newly compiled / 610 reused from matching object-cache entries), the renderer DLL linked and the full game executable linked; nothing was run on Windows or under Wine, and this is neither the project's hosted builder run nor a Windows UTF-8 run-time path test.

The exact same head e94d24624d11798c61ebed51e3f82cd998abc8be also compiled and linked its full native Linux executable and renderer with Clang 23.1.3 through the documented resume path (200 newly compiled units, 288 matching Linux-cache units, source stamp 95758e73), using the verified target-neutral C from lift 5c175a4727d82f85e425e85400dca17fea0750fb rather than carrying the patch onto another commit; neither Linux binary was run.

Built for Windows and Linux at the previous head, not run: At 1982a68738c83c6f67b312c99ba5ee9ac507652a, the Windows x86-64 Vulkan renderer DLL and all 47 selected host translation units were cross-built on Linux with llvm-mingw 20261104 (Clang 23.1.5), using the project's production flags and pinned dependencies (renderer: 35 newly compiled / 170 reused; host: 1 newly compiled / 46 reused; lifted C: 0 newly compiled / 80 reused; all Windows units: 36 newly compiled / 610 reused from matching object-cache entries), the renderer DLL linked and the full game executable linked; nothing was run on Windows or under Wine, and this is neither the project's hosted builder run nor a Windows UTF-8 run-time path test. The same head also compiled and linked its full native Linux executable and renderer with Clang 23.1.3 through the documented resume path (37 newly compiled units, 451 matching Linux-cache units, source stamp 5f26c88f), using the verified target-neutral C from lift 76699aad3a6bdc4f75520c0f2c9d9c80d5731a5f rather than carrying the patch onto another commit; neither Linux binary was run.

Not tested: the carried head has not been run on either platform (it was built and linked for both, above). No picture check covers the new mandatory-downscale and CRT combination yet. The Linux game runs and measurements name earlier heads; they are not reruns of this carry. Nothing was run on Windows, including the changed C++ fixtures. Enabled-filter cost and 4K cost remain unmeasured, as does synchronized-input pixel identity. The earlier coverage gaps remain: a real window/swapchain, generated-frame output, motion/long play, other areas, ultrawide runtime, the 480/720-line choices in game, an HDR display and physical RGB/BGR/OLED subpixel layouts. No physical CRT calibration is claimed.

Needs other testers:

  • Any PC other than Linux with NVIDIA (Windows with any GPU, or Linux with AMD, Intel or a Steam Deck): Toggle each filter and return to Off, compare the result with the reference pictures, check that the Longshot menu stays clean, and copy the full vk: CRT crt-lottes-aperture: log line (or a CRT filter unavailable line). If available, also note Expanded HDR / Native-PQ bypass, ultrawide or frame generation. These are optional notes on the same request, not separate requests (T-L5M7).

Screenshots

All panels were inspected. Overview panels reduce complete frames; mask panels use 240×140 output-pixel crops enlarged 3× with nearest-neighbour sampling. They demonstrate the rendered patterns, not a monitor calibration.

main @ d725b363 and this PR @ b6c14ce6: Off and all four filters at 1920×1080, the same Items page. The TV profile curves the edges; the other profiles stay flat.

main @ d725b363 and this PR @ b6c14ce6: native-output-pixel close-ups showing the RGB grille, TV shadow mask, Hyllian modulation and zfast's lighter mask/scanlines.

main @ d725b363 and this PR @ b6c14ce6: complete 3840×2160 frames, reduced for the overview; Off, every filter and the same pause page.

main @ d725b363 and this PR @ b6c14ce6: 4K output-pixel crops at 3× nearest enlargement. The masks keep their output-pixel grid while the virtual raster stays at 240 lines.

Risk and rollback

A low virtual line count makes the game's small HUD/text coarser; non-integer ratios can show moire. These fixed upstream profiles assume particular colour/gamma and mask layouts, not a calibrated display. First use creates pipelines/images and can hitch. Enabled filters add GPU work and two reusable images; those resources stay cached after returning to Off. Set CRT filter to Off (crt_filter=0) to restore the old output path; reverting the change also removes it.

Reviewer notes

Start with framegen_scale() / record_crt(), the checked source rectangle, the virtual-raster policy and the provenance file. The generated headers are compiled from generic shader sources, not from the game. Existing third-party folders are untouched. No MkwVkHost or MkwVkRenderer field changed.

The unused-feature cost at 1080p is now measured; enabled-filter cost at both sizes, synchronized-input Off pixel proof and the platform/output coverage named under Testing remain outstanding. The selection/default/dependency questions in #70 remain open and may change this slice. Arbitrary presets, LUT artwork, NTSC, phosphor feedback, bezels and Native-HDR CRT processing remain deliberately outside it; a wider preset runtime is still a maintainer dependency decision, not an added dependency here.

Issues

See #70

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main.
    Branch base: cf605c85b; the actual measured source pair is named above.
  • 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).
    The combined request is T-L5M7, as named under Testing.
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
    The quiet Off-only 1080p comparison is reported above. Enabled-filter and 4K costs are not covered; the older GPU spans remain diagnostics.
  • No game files, dumps, build output or generated files.
  • 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.
  • The SPIR-V headers regenerated with glslang (a shader changed).
  • A game-free test added for the fix, failing on main and passing here.
    The earlier negative control at main 0c74253d fails with FAIL: missing production CRT filter row; its passing counterpart was 2ad245bd. The current options fixture passes with GCC, Clang and Windows-target syntax at 1982a687, after adapting its extraction prerequisites to current main. The new ordering cases also reject the old scaling function with both native compilers.
  • 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. Built and linked for both at e94d24624d11798c61ebed51e3f82cd998abc8be (under Testing); nothing was run on Windows at this head, so UTF-8 path behavior at run time is not claimed for it.
    The earlier Linux game runs retain their named heads. Neither the carried head nor the changed C++ fixtures has been run on Windows, and no Windows run-time path test is claimed.
  • Docs updated.

New on this forge; not opened on GitHub.

Assisted-by: GPT-6 Astra
Co-Authored-By: GPT-6 Astra [email protected]

## Why Players who want a CRT picture can choose a clean aperture grille, a curved consumer-TV shadow mask or two lighter scanline looks, without an external injector. Off remains the default. This first slice follows the proposal in #70; its three maintainer questions are still unanswered, so the selection and defaults may change with those answers. ## What changed - **Graphics menu:** `gpu/vk/vk_menu.cpp` adds **CRT filter** and **CRT lines** immediately after Window scaling filter. They apply without restarting, use the normal options-file reader/writer and do not change the quality preset. - **Four choices, three upstream algorithms:** Lottes aperture grille, Lottes consumer TV, Hyllian Fast and zfast CRT. The two Lottes profiles use the same complete shader with different mask/curvature parameters. The flat grille is professional-monitor-style, **not a calibrated Sony PVM/BVM model**. - **Output processing:** `gpu/vk/vk_framegen.cpp` and `crt_policy.h` reduce the finished picture to a virtual raster, then run the selected CRT shader. Existing window scaling, sharpness and ultrawide-side treatment come first, including the mandatory downscale when the checked source rectangle is larger than the destination. CRT reads that destination-size result. The game's HUD is filtered; Longshot's menu and overlays come afterwards. Generated frames take the same output path, rather than putting a mask into optical flow; that windowed path still needs runtime testing. When the finished game picture is larger than the destination, the output-effects path first applies main's filtered downscale from https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/307, even with Window scaling filter Off and Sharpness 0, then feeds that destination-size picture into the CRT virtual raster. This can change fine-detail sampling compared with this pull request's earlier head; the CRT mask and scanlines are still generated at the final destination size, not shrunk afterwards. At `1982a687`, windowless captures on Linux with an RTX 4090 and the capture's effects switch on (`RX_VK_SHOT_FX=1`) showed Off, all four filters and Off again at 1920x1080 to 1920x1080 and at 3840x2160 to 1920x1080: the inspected 1:1 crops keep the expected mask and scanline patterns with no doubled grid and no obvious extra moire. A real window and motion were not tested for this. - **Default Off:** current main's blit/scale path remains, including its mandatory downscale, with no CRT dispatch or CRT resource creation. Images and pipelines are created only when a filter is first used, then reused. Resources already created remain cached after returning to Off until exit. - **Colour:** these are SDR shaders. Expanded HDR can expand their SDR output through the existing conversion. Actual Native HDR/PQ bypasses them and explains the bypass in the help text, keeping the saved selection. Neither HDR mode was judged on an HDR display here. - **Sources and licensing:** unchanged originals, their hashes and notices are in the new `third_party/crt_shaders/` folder. Timothy Lottes' [public-domain shader](https://github.com/libretro/slang-shaders/blob/2345ee91602a7404d3a1a6a5df6108d7305fdad4/crt/shaders/crt-lottes.slang), Hyllian's [MIT Fast shader](https://github.com/libretro/slang-shaders/blob/2345ee91602a7404d3a1a6a5df6108d7305fdad4/crt/shaders/hyllian/crt-hyllian-fast.slang) (cgwg's mask; DariusG/hunterk ports), and Greg Hogan / SoltanGris42's [GPL-2.0-or-later zfast implementation](https://github.com/libretro/slang-shaders/blob/2345ee91602a7404d3a1a6a5df6108d7305fdad4/crt/shaders/zfast_crt/zfast_crt_impl.inc) are pinned to `2345ee91602a7404d3a1a6a5df6108d7305fdad4`. Compute adaptations stay outside that folder; their differences are documented in its provenance file. - **Build and checks:** four shader sources have glslang-generated SPIR-V headers. Tests check upstream-byte identity, retained filter math/defaults, shader-header identity, option persistence/clamping, preset preservation, virtual-raster geometry, output ordering and allocation-failure handling. No new runtime dependency or host/renderer API field. **Current-main integration:** the carry keeps all six existing commits, then adds one separate test/documentation commit. Main's downscale fix in https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/307 conflicts with the CRT entry path in `gpu/vk/vk_framegen.cpp`. The resolution keeps its checked-crop shrink decision and makes it part of the scaling decision: a larger source is filtered to the destination first, then CRT reduces that result to its virtual raster and applies the mask. CRT Off retains main's downscale. Same-size output still avoids the scale pass when no other window effect needs it. Native PQ still scales in 10 bits and bypasses SDR CRT. Main's Intel compiler guard from https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/252 remains around shader and pipeline creation, including CRT's use of the shared constructor. The resource-failure fixture follows those guarded calls without removing its allocation checks. The options fixture also follows the Plus/Minus definitions from https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/137 and the real restart-value helper from https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/136. No production menu behavior or shader algorithm was changed in the separate test commit. **Picture check still needed:** shrinking with Window scaling filter Off and Sharpness 0 now passes through main's filtered downscale before CRT, instead of feeding the larger source directly to CRT. The extra stage and destination-sized input can change fine detail and moire. The carried head needs pictures of this combination before publication; the earlier pictures and timings remain evidence only for their named older heads. The order after frame generation and before Longshot overlays is unchanged. Main's results files and every imported shader remain unchanged. ## Flags and settings | Flag | What it does | Default now | Recommendation | Helps on | Doesn't help on | Untested on | |---|---|---|---|---|---|---| | Graphics → CRT filter | Off, Lottes aperture grille, Lottes consumer TV, Hyllian Fast, zfast CRT; changes live | Off | Off by default: an aesthetic choice, not an image-quality fix | Players wanting a CRT presentation | Actual Native HDR/PQ is bypassed | Windows, AMD, Intel, Steam Deck, HDR displays, windowed presentation | | `crt_filter` | Saves the choices above as 0, 1, 2, 3, 4 | 0 | Off by default: preserve the original picture | Persistent selection | Does not load arbitrary Slang presets | Same runtime gaps as the row | | Graphics → CRT lines | Chooses 240, 480 or 720 virtual lines, capped at half the destination height, with width following aspect | 240 | Off by default: inactive while CRT filter is Off; 240 is the initial look when enabled | A consistent scanline density independent of Render resolution | Does not force 4:3 or integer scaling; non-integer ratios can show moire | Other line choices in the game, ultrawide runtime, portrait displays | | `crt_lines` | Saves 240 / 480 / 720 as 0 / 1 / 2; applies live | 0 | Off by default: has no effect without a CRT filter | Persistent virtual-raster choice | Cannot reproduce a particular monitor's measured characteristics | Same runtime gaps as the row | The remake is not a 240-line game: this deliberately downsamples its finished picture for the selected CRT look. More virtual lines preserve more of its small text. The shader-regeneration helper also has `--glslang` to select a compiler and `--check` to compare headers rather than write them; ordinary player builds use the checked-in headers. ## Measurements ### Quiet unused-feature cost: 2026-11-13 **CRT Off showed no measurable frame-time penalty in this scene.** The median of the two run means was **10.648 ms on main** and **10.562 ms with this PR**, a difference of **-0.086 ms (-0.80%)**. That difference is smaller than either build's repeat spread; it is **not a speed-up claim or proof of literally zero cycles**. It meets the predeclared no-penalty band of main + max(0.10 ms, 2%); ambiguous or unstable pairs would not pass. main: 0c74253d4c6ed08d57e4f3aa8ce8634c4c017085, `v3.0.0-rc.3`, source `cb01b5a3` this PR: move/feat/crt-filters-m0c74253d @ 2ad245bd1b7f5c1154090d6d1ece66e484adb769, `v3.0.0-rc.3-5-g2ad245bd1`, source `9aeb0cbf` Hardware: RTX 4090, NVIDIA 615.78.08; Core i9-13900K, 94 GiB RAM; Arch Linux 7.2.15-1-omarchy. The later `0b4cd574` carry changes documentation, not the measured code. Scene/settings: the same copied Deku Tree save, stationary input at the central web; 1920×1080 output; 50% (960×540), game upscaler; uncapped; sdr; frame generation, v-sync, frame pacing and crt off; game speed per frame; no mods; prepared/warmed caches; timestamp profiler off. Both Linux builds use matching compiler/profile inputs; main was reused, not rebuilt for this pair. Both shader-preparation commands completed, each logging one failed shader program. Their selected scene was then warmed and inspected; the measured phases' complete renderer log blocks reported 0.0 draw omissions and 0.00 shader waits. Method: **A1 main / B1 PR Off / A2 main / B2 PR Off**, in one quiet window after a four-minute cooldown. Each process ran for 75 seconds. After observed stable play, 18 seconds let the boot input and scene settle, then a 10-second neutral-input phase supplied the **game-present-return** intervals. These are CPU-clock present-return intervals, **not scan-out, display latency or a labelled CRT GPU span**. The GPU column is the existing rounded renderer counter averaged over seven complete 120-present blocks per phase; it is a separate population. Before/after pictures were inspected. The paired worker lasted 309.9 seconds. At admission, load was 1.86, five-second CPU use 0.586 cores and GPU use 6% (the agreed ceiling was 12%). No other game or compiler was observed. Hyprland, Xwayland, quickshell/qs, tern and one terminal-tooling Chromium GPU process were left running. Each measured phase stayed below the unchanged 3-core mean / 6-core peak other-work limits; each build's repeat spread stayed below 5%. Later load averages include the preceding game and are shown rather than described as idle. | Statistic | main @ 0c74253d | this PR @ 2ad245bd | |---|---|---| | Median of the two run-mean frame times | **10.648 ms** | **10.562 ms** | | Same-build repeat spread | 0.88% | 1.25% | | Run | Build | Mean / p99 (ms) | GPU counter (ms) | Other CPU: mean / peak 5 s (cores) | Load: start → end | >1 s present gaps | |---|---|---|---|---|---|---| | A1 | main `0c74253d` | 10.694 / 13.654 | 3.971 | 0.496 / 0.581 | 1.81 → 10.66 | 0 | | B1 | PR, CRT Off `2ad245bd` | 10.496 / 13.059 | 4.000 | 0.608 / 0.615 | 10.66 → 7.73 | 0 | | A2 | main `0c74253d` | 10.601 / 13.370 | 3.986 | 0.574 / 0.600 | 7.73 → 6.50 | 0 | | B2 | PR, CRT Off `2ad245bd` | 10.628 / 13.599 | 4.000 | 0.585 / 0.608 | 6.50 → 6.50 | 0 | All four runs were valid, with complete journal coverage and no fatal/device/kernel fault. **No present-return gap over one second occurred anywhere in these four traces.** B2 did retain one `PlayReporter ... ran without a core` scheduler warning near present 10,560, before its measured 13,232–14,172 interval. That warning's two-second wait is not a two-second gap between presents. How to reproduce: use the settings above and the same stationary scene on both builds, warm it first, leave CRT Off, and alternate main/PR/main/PR with the same observed phase. This run also used `RX_VK_PRECOMPILE=0`, `RX_VK_NO_WARM=1`, `RX_VK_NO_PREDICT=1`, `RX_VK_AUTO_PRESET=0`, `RX_VK_AUTO_TUNE=0` and `RX_VK_PASS_TIMING=0` on both; the Uncapped row was used without an `HOS_FPS_MAX` override. **Not measured in this window:** any enabled filter, 4K, windowed or generated-frame output, HDR, or synchronized-input pixel equality. Only the unused-feature comparison fit the one complete repeated window; the older per-filter GPU spans below remain diagnostics, not those missing measurements. <details> <summary>Measured run rows (kept here, not in the shared results file)</summary> ```markdown | Date | Build | OS / desktop | GPU / driver | CPU / RAM | Settings | Where | Result | |---|---|---|---|---|---|---|---| | 2026-11-13 | main 0c74253d; longshot v3.0.0-rc.3 (source cb01b5a3) | Arch Linux 7.2.15-1-omarchy / Hyprland; windowless | RTX 4090 / NVIDIA 615.78.08 | i9-13900K / 94 GiB | 1920×1080 output; 50% (960×540), game upscaler; Uncapped; SDR; frame generation, V-Sync, frame pacing and CRT Off; Game speed Per frame; no mods; prepared/warmed caches; timestamp profiler Off | Deku Tree, stationary input at the central web | A1, quiet A/B/A/B: 10.694 ms mean, 13.654 ms p99; 934 present-return intervals / 9.988 s; GPU log counter 3.971 ms. Other CPU 0.496 mean / 0.581 peak cores; load 1.81→10.66. Valid; zero >1 s present gaps, fatal/device/kernel faults; complete journal; bounded stop (124). | | 2026-11-13 | CRT PR 2ad245bd; longshot v3.0.0-rc.3-5-g2ad245bd1 (source 9aeb0cbf) | Arch Linux 7.2.15-1-omarchy / Hyprland; windowless | RTX 4090 / NVIDIA 615.78.08 | i9-13900K / 94 GiB | 1920×1080 output; 50% (960×540), game upscaler; Uncapped; SDR; frame generation, V-Sync, frame pacing and CRT Off; Game speed Per frame; no mods; prepared/warmed caches; timestamp profiler Off | Deku Tree, stationary input at the central web | B1, quiet A/B/A/B: 10.496 ms mean, 13.059 ms p99; 951 present-return intervals / 9.982 s; GPU log counter 4.000 ms. Other CPU 0.608 mean / 0.615 peak cores; load 10.66→7.73. Valid; zero >1 s present gaps, fatal/device/kernel faults; complete journal; bounded stop (124). | | 2026-11-13 | main 0c74253d; longshot v3.0.0-rc.3 (source cb01b5a3) | Arch Linux 7.2.15-1-omarchy / Hyprland; windowless | RTX 4090 / NVIDIA 615.78.08 | i9-13900K / 94 GiB | 1920×1080 output; 50% (960×540), game upscaler; Uncapped; SDR; frame generation, V-Sync, frame pacing and CRT Off; Game speed Per frame; no mods; prepared/warmed caches; timestamp profiler Off | Deku Tree, stationary input at the central web | A2, quiet A/B/A/B: 10.601 ms mean, 13.370 ms p99; 942 present-return intervals / 9.986 s; GPU log counter 3.986 ms. Other CPU 0.574 mean / 0.600 peak cores; load 7.73→6.50. Valid; zero >1 s present gaps, fatal/device/kernel faults; complete journal; bounded stop (124). | | 2026-11-13 | CRT PR 2ad245bd; longshot v3.0.0-rc.3-5-g2ad245bd1 (source 9aeb0cbf) | Arch Linux 7.2.15-1-omarchy / Hyprland; windowless | RTX 4090 / NVIDIA 615.78.08 | i9-13900K / 94 GiB | 1920×1080 output; 50% (960×540), game upscaler; Uncapped; SDR; frame generation, V-Sync, frame pacing and CRT Off; Game speed Per frame; no mods; prepared/warmed caches; timestamp profiler Off | Deku Tree, stationary input at the central web | B2, quiet A/B/A/B: 10.628 ms mean, 13.599 ms p99; 940 present-return intervals / 9.990 s; GPU log counter 4.000 ms. Other CPU 0.585 mean / 0.608 peak cores; load 6.50→6.50. Valid; zero >1 s present gaps, fatal/device/kernel faults; complete journal; bounded stop (124). One virtual-core scheduler warning before the measured phase. | ``` </details> <!-- branch-state --> Branch state: on main `5e072fc8c2adadba5ea610148be1c47800ba0cca`; head `e94d24624d11798c61ebed51e3f82cd998abc8be`; whole game-free suite on this head: `546 passed, 2 failed, 16 skipped` (main `5e072fc8c2adadba5ea610148be1c47800ba0cca`: `542 passed, 3 failed, 16 skipped`). The 2 failing tests fail on main the same way: `tests/test_module_ids.py`, `tests/test_pad_layout.py`. The branch was carried onto this main by hand; what differs from a mechanical carry is named in the comment posted with this update. <!-- /branch-state --> **Historical filter-on evidence:** the following table and reference pictures retain the actual older commits that produced them. The new unused-feature comparison above is separate; enabled-filter and 4K costs have not been measured in that window. main: d725b363 (2026-11-12), built as `v2.2.0-rc.1-20-gd725b3631`, source `33703252` this PR: move/feat/crt-filters @ b6c14ce6, historical pre-rebase build, source `f600c365`; not the rebased head above Hardware: RTX 4090, NVIDIA 615.78.08; Core i9-13900K, 94 GiB RAM; Arch Linux 7.2.15-1-omarchy; 2026-11-13 UTC Scene: the same Deku Tree save and Items pause page; 1920×1080 and 3840×2160 output/captures; game's own upscaler, 50% render resolution, frame generation Off, 60 cap, SDR, 240 virtual lines, no mods Method: one reference and one candidate session at each size, windowless. The candidate selects Off, each filter and restored Off, with 1,200 further game presents per selection. Full frames and nearest-neighbour pixel crops were inspected. `RX_VK_PASS_TIMING=1` supplies the GPU spans below; only complete, single-filter 600-present windows are listed. How to reproduce: load a Deku Tree save, open Items, choose the output size, Render resolution 50%, frame generation Off and SDR; change CRT filter through each choice with CRT lines at 240. For the diagnostic lines, set `RX_VK_PASS_TIMING=1` and wait beyond 600 presents after each change. **These are diagnostics under shared load, not performance measurements.** These older sessions were not a quiet, interleaved comparison; the new Off result is reported separately above. Other GPU work was present before admission; its exact overlap with the 1080p sessions was not recorded. The 4K sessions excluded other test games but still shared heavily loaded CPUs. This is not an FPS, display-latency or generated-frame speed claim. | Observation | main @ d725b363 | this PR @ b6c14ce6 | |---|---|---| | 1080p session | 2,891 presents, exit 0; load 27.54 → 34.85 | 8,069 presents, exit 0; load 60.51 → 18.31 | | 4K session | 3,006 presents, exit 0; load 137.20 → 72.78 | 8,057 presents, exit 0; load 111.51 → 124.82 | | Fatal, crash, device-loss and kernel-fault counts | 0 in both; journal coverage complete | 0 in both; journal coverage complete | | CRT option regression | Fails: missing production CRT filter row | Passes | | Off path, production fixture | Existing blit/scale route | Same route; no CRT call or allocation | **Diagnostic GPU span per present, downsample plus CRT; not a measurement:** | Filter | this PR @ b6c14ce6, 1920×1080 | this PR @ b6c14ce6, 3840×2160 | |---|---|---| | Lottes aperture grille | 0.687 / 0.546 ms | 0.999 ms | | Lottes consumer TV | 0.624 ms | 0.900 / 0.775 ms | | Hyllian Fast | 0.181 ms | 0.239 ms | | zfast CRT | 0.201 ms | 0.221 ms | Each slash lists separate complete windows, not paired repetitions. The timestamp profiler orders work and can change its cost; these numbers are not a prediction for normal presentation. ```text vk: CRT crt-lottes-aperture: 427x240 virtual raster, 1920x1080 output; after scaling, before Longshot overlays vk: m crt-lottes-aperture 0.687 ms 15.3% 1.0 spans 1.0 items 0.00 MB 686.5 us a span vk: m crt-lottes-tv 0.624 ms 14.3% 1.0 spans 1.0 items 0.00 MB 624.4 us a span vk: m crt-hyllian 0.181 ms 5.4% 1.0 spans 1.0 items 0.00 MB 181.4 us a span vk: m crt-zfast 0.201 ms 6.0% 1.0 spans 1.0 items 0.00 MB 200.6 us a span ``` **Off identity:** the production-path fixture checks that Off adds no CRT work and preserves the existing scaling/cropping/HDR routes. The inspected captures show the unfiltered picture returning. They are **not bit-identical independent game snapshots**: exact full-frame comparison at 1080p finds differences in the separately captured world background; switching back to Off differs in the animated selection region. The new quiet Off comparison above supplies timing evidence at 1080p only. Synchronized-input pixel proof is still missing; the captures do not establish byte-for-byte equality. ## Testing **Tested:** the focused Linux coverage is 24 passing driver invocations: 10 compiled drivers with both GCC and Clang, plus four source/shader drivers. These cover the CRT tests, every main test that reads the hand-merged frame-generation file, and main's downscale shader check. The corrected options driver passes at `1982a687`; the other positive checks passed at `1dfd7d62`, before that options-fixture-only amendment, and were not repeated. Both compilers also reject the old CRT scaling function and a version that puts CRT before the required downscale, four expected runtime failures in total. No assertion or warning was weakened. These are focused results, not a whole-suite total. The new cases cover CRT Off and all four filters while shrinking in either dimension, scale-before-CRT ordering and input size, checked cropping, unavailable-filter fallback, same-size/upscale behavior and Native-PQ bypass. All four CRT shader arrays match local recompilation and their original imports match the pinned hashes. Main's scale shader passes its source-hash and compilation checks; its different glslang version means its generated bytes were not compared. The CRT options and raster-policy fixtures pass Windows-target syntax checks. The resource and scaling fixtures remain blocked by the syntax check's rule against their inherited conditional warning definitions; those definitions were not changed to make the check pass. None of the changed C++ fixtures was executed on Windows. The initial options compile failures are retained in the update history, not counted as passes. Earlier focused checks at `2ad245bd` remain historical evidence. The original imports and shader algorithms are unchanged, while the new downscale integration still needs a picture check. Linux builds at main `0c74253d` and this PR `2ad245bd` now have matched preparation, an inspected ordinary warm-up each, and the four valid Off-only 1080p runs under Measurements. The built source stamps are `cb01b5a3` / `9aeb0cbf`. Both preparation commands completed with one failed shader program reported; the selected measured phases logged no draw omissions or shader waits. The eight timing captures were inspected. No fatal/device/kernel faults or >1 s present gaps were observed in those four runs; B2's one virtual-core scheduler warning preceded its measured phase. The older Linux builds and four windowless game sessions below remain **historical reference evidence**: Off and all four filters at 1080p/4K, live changes, return to Off and no observed fatal/device/kernel faults with complete journal coverage. The old full-suite result (`513 passed, 1 failed, 20 skipped` at `4c673972`) is not a result for this rebase. Likewise, the two older Windows-target fixture compiles are not a Windows build of this head. The same `tests/test_crt_options.py` was now pointed at main `0c74253d`: it fails with `FAIL: missing production CRT filter row` before compilation, because this feature is absent. It passes on `2ad245bd`, whose source is unchanged by the later documentation-only carry to `0b4cd574`. **Built for Windows and Linux at the current head, not run:** At `e94d24624d11798c61ebed51e3f82cd998abc8be`, the Windows x86-64 Vulkan renderer DLL and all 47 selected host translation units were cross-built on Linux with llvm-mingw 20261104 (Clang 23.1.5), using the project's production flags and pinned dependencies (renderer: 35 newly compiled / 170 reused; host: 1 newly compiled / 46 reused; lifted C: 0 newly compiled / 80 reused; all Windows units: 36 newly compiled / 610 reused from matching object-cache entries), the renderer DLL linked and the full game executable linked; nothing was run on Windows or under Wine, and this is neither the project's hosted builder run nor a Windows UTF-8 run-time path test. The exact same head `e94d24624d11798c61ebed51e3f82cd998abc8be` also compiled and linked its full native Linux executable and renderer with Clang 23.1.3 through the documented resume path (200 newly compiled units, 288 matching Linux-cache units, source stamp `95758e73`), using the verified target-neutral C from lift `5c175a4727d82f85e425e85400dca17fea0750fb` rather than carrying the patch onto another commit; neither Linux binary was run. **Built for Windows and Linux at the previous head, not run:** At `1982a68738c83c6f67b312c99ba5ee9ac507652a`, the Windows x86-64 Vulkan renderer DLL and all 47 selected host translation units were cross-built on Linux with llvm-mingw 20261104 (Clang 23.1.5), using the project's production flags and pinned dependencies (renderer: 35 newly compiled / 170 reused; host: 1 newly compiled / 46 reused; lifted C: 0 newly compiled / 80 reused; all Windows units: 36 newly compiled / 610 reused from matching object-cache entries), the renderer DLL linked and the full game executable linked; nothing was run on Windows or under Wine, and this is neither the project's hosted builder run nor a Windows UTF-8 run-time path test. The same head also compiled and linked its full native Linux executable and renderer with Clang 23.1.3 through the documented resume path (37 newly compiled units, 451 matching Linux-cache units, source stamp `5f26c88f`), using the verified target-neutral C from lift `76699aad3a6bdc4f75520c0f2c9d9c80d5731a5f` rather than carrying the patch onto another commit; neither Linux binary was run. **Not tested:** the carried head has not been run on either platform (it was built and linked for both, above). No picture check covers the new mandatory-downscale and CRT combination yet. The Linux game runs and measurements name earlier heads; they are not reruns of this carry. Nothing was run on Windows, including the changed C++ fixtures. Enabled-filter cost and 4K cost remain unmeasured, as does synchronized-input pixel identity. The earlier coverage gaps remain: a real window/swapchain, generated-frame output, motion/long play, other areas, ultrawide runtime, the 480/720-line choices in game, an HDR display and physical RGB/BGR/OLED subpixel layouts. No physical CRT calibration is claimed. **Needs other testers:** - Any PC other than Linux with NVIDIA (Windows with any GPU, or Linux with AMD, Intel or a Steam Deck): Toggle each filter and return to Off, compare the result with the reference pictures, check that the Longshot menu stays clean, and copy the full `vk: CRT crt-lottes-aperture:` log line (or a `CRT filter unavailable` line). If available, also note Expanded HDR / Native-PQ bypass, ultrawide or frame generation. These are optional notes on the same request, not separate requests (T-L5M7). ## Screenshots All panels were inspected. Overview panels reduce complete frames; mask panels use 240×140 output-pixel crops enlarged 3× with nearest-neighbour sampling. They demonstrate the rendered patterns, not a monitor calibration. ![main @ d725b363 and this PR @ b6c14ce6: Off and all four filters at 1920×1080, the same Items page. The TV profile curves the edges; the other profiles stay flat.](https://git.magicbean.dev/attachments/1697f0af-e889-49f5-bea7-753454ac7b7c) ![main @ d725b363 and this PR @ b6c14ce6: native-output-pixel close-ups showing the RGB grille, TV shadow mask, Hyllian modulation and zfast's lighter mask/scanlines.](https://git.magicbean.dev/attachments/15deaa2d-d60b-46b8-bd02-3bb6c0a9463e) ![main @ d725b363 and this PR @ b6c14ce6: complete 3840×2160 frames, reduced for the overview; Off, every filter and the same pause page.](https://git.magicbean.dev/attachments/1de2b73c-ee9a-42f9-91b9-611669ed53e0) ![main @ d725b363 and this PR @ b6c14ce6: 4K output-pixel crops at 3× nearest enlargement. The masks keep their output-pixel grid while the virtual raster stays at 240 lines.](https://git.magicbean.dev/attachments/4e15f337-00ca-464d-9293-936ecdfae504) ## Risk and rollback A low virtual line count makes the game's small HUD/text coarser; non-integer ratios can show moire. These fixed upstream profiles assume particular colour/gamma and mask layouts, not a calibrated display. First use creates pipelines/images and can hitch. Enabled filters add GPU work and two reusable images; those resources stay cached after returning to Off. Set **CRT filter to Off** (`crt_filter=0`) to restore the old output path; reverting the change also removes it. ## Reviewer notes Start with `framegen_scale()` / `record_crt()`, the checked source rectangle, the virtual-raster policy and the provenance file. The generated headers are compiled from generic shader sources, not from the game. Existing third-party folders are untouched. No `MkwVkHost` or `MkwVkRenderer` field changed. The unused-feature cost at 1080p is now measured; enabled-filter cost at both sizes, synchronized-input Off pixel proof and the platform/output coverage named under Testing remain outstanding. The selection/default/dependency questions in #70 remain open and may change this slice. Arbitrary presets, LUT artwork, NTSC, phosphor feedback, bezels and Native-HDR CRT processing remain deliberately outside it; a wider preset runtime is still a maintainer dependency decision, not an added dependency here. ## Issues See #70 ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. Branch base: `cf605c85b`; the actual measured source pair is named above. - [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). The combined request is T-L5M7, as named under Testing. - [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. The quiet Off-only 1080p comparison is reported above. Enabled-filter and 4K costs are not covered; the older GPU spans remain diagnostics. - [x] No game files, dumps, build output or generated files. - [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] The SPIR-V headers regenerated with glslang (a shader changed). - [x] A game-free test added for the fix, failing on `main` and passing here. The earlier negative control at main `0c74253d` fails with `FAIL: missing production CRT filter row`; its passing counterpart was `2ad245bd`. The current options fixture passes with GCC, Clang and Windows-target syntax at `1982a687`, after adapting its extraction prerequisites to current main. The new ordering cases also reject the old scaling function with both native compilers. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened. - [x] Host code builds on both Windows (llvm-mingw) and Linux, with UTF-8 paths. Built and linked for both at `e94d24624d11798c61ebed51e3f82cd998abc8be` (under Testing); nothing was run on Windows at this head, so UTF-8 path behavior at run time is not claimed for it. The earlier Linux game runs retain their named heads. Neither the carried head nor the changed C++ fixtures has been run on Windows, and no Windows run-time path test is claimed. - [x] Docs updated. --- New on this forge; not opened on GitHub. Assisted-by: GPT-6 Astra Co-Authored-By: GPT-6 Astra <[email protected]>
Author
Member

Carried onto main dd02c6cca (head 0b4cd574b) by hand: main's rewritten README and development guide are kept and the CRT rows and sections placed into them; the fixture follows main's new output and Auto contract; nothing is added to the results table. The whole game-free suite on this head: 508 passed, 2 failed, 15 skipped. Still WIP: the cost of the filters is measured next against a build of the same main, then one test request for other hardware is opened.

Carried onto main `dd02c6cca` (head `0b4cd574b`) by hand: main's rewritten README and development guide are kept and the CRT rows and sections placed into them; the fixture follows main's new output and Auto contract; nothing is added to the results table. The whole game-free suite on this head: 508 passed, 2 failed, 15 skipped. Still WIP: the cost of the filters is measured next against a build of the same main, then one test request for other hardware is opened.
mooki force-pushed feat/crt-filters from 4c67397247
All checks were successful
PR description / Description follows the template (pull_request_target) Successful in 4s
to 0b4cd574b7
Some checks failed
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
2026-11-13 15:20:55 +00:00
Compare
Author
Member

Updated to head fe6bebefa on main 514591a30; the whole game-free suite on it: 520 passed, 1 failed, 15 skipped.

New in the description: the cost with the filter Off, measured on 13 November at the commits it names (main 0c74253d, this change 2ad245bd; two alternating runs each on a quiet PC, 1920x1080, no cap, no present gap over one second): the median of the run means is 10.648 ms on main and 10.562 ms with this change. The difference (-0.8%) is smaller than either build's repeat spread, so no penalty shows for the unused feature; that is not a speed-up claim. The cost with a filter switched on, and 4K, were not measured.

The box for the game-free test is settled: tests/test_crt_options.py fails on main 0c74253d for the missing CRT rows and passes here. The test request is T-L5M7 (the four filters and Off on Windows, AMD, Intel or a Steam Deck). The Windows-and-Linux build box stays unticked until this code has been linked for Windows.

One adaptation to today's main is in a new commit, test(vk): the CRT options fixture stands in for detect_pad_layout: main's set_row now calls detect_pad_layout() (from #266), and this pull request's fixture compiles set_row from the menu's source, so it gets the same one-line stand-in main's own fixtures have. No check changed; the measured code is untouched.

Updated to head `fe6bebefa` on main `514591a30`; the whole game-free suite on it: 520 passed, 1 failed, 15 skipped. New in the description: the cost with the filter Off, measured on 13 November at the commits it names (main 0c74253d, this change 2ad245bd; two alternating runs each on a quiet PC, 1920x1080, no cap, no present gap over one second): the median of the run means is 10.648 ms on main and 10.562 ms with this change. The difference (-0.8%) is smaller than either build's repeat spread, so no penalty shows for the unused feature; that is not a speed-up claim. The cost with a filter switched on, and 4K, were not measured. The box for the game-free test is settled: `tests/test_crt_options.py` fails on main 0c74253d for the missing CRT rows and passes here. The test request is T-L5M7 (the four filters and Off on Windows, AMD, Intel or a Steam Deck). The Windows-and-Linux build box stays unticked until this code has been linked for Windows. One adaptation to today's main is in a new commit, `test(vk): the CRT options fixture stands in for detect_pad_layout`: main's `set_row` now calls `detect_pad_layout()` (from #266), and this pull request's fixture compiles `set_row` from the menu's source, so it gets the same one-line stand-in main's own fixtures have. No check changed; the measured code is untouched.
mooki force-pushed feat/crt-filters from 0b4cd574b7
Some checks failed
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
to fe6bebefaa
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 4s
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
2026-11-13 20:40:16 +00:00
Compare
Author
Member

This builds on both platforms. At fe6bebef the Windows renderer and the full game executable cross-built and linked on Linux with llvm-mingw 20261104 (Clang 23.1.5), and the native Linux executable and renderer built and linked; neither compiler rejected anything, and nothing was run from either build. With the Windows-and-Linux build box ticked on that basis, every box is done and this is marked ready for review. Still not exercised, as the description says: any run on Windows, a real window, the filters' own cost when switched on, and other GPUs (test request T-L5M7).

This builds on both platforms. At `fe6bebef` the Windows renderer and the full game executable cross-built and linked on Linux with llvm-mingw 20261104 (Clang 23.1.5), and the native Linux executable and renderer built and linked; neither compiler rejected anything, and nothing was run from either build. With the Windows-and-Linux build box ticked on that basis, every box is done and this is marked ready for review. Still not exercised, as the description says: any run on Windows, a real window, the filters' own cost when switched on, and other GPUs (test request T-L5M7).
mooki changed title from WIP: feat(vk): add selectable CRT monitor and TV filters to feat(vk): add selectable CRT monitor and TV filters 2026-11-13 21:19:36 +00:00
mooki force-pushed feat/crt-filters from fe6bebefaa
Some checks failed
PR description / Description follows the template (pull_request_target) Successful in 4s
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
to 1982a68738
All checks were successful
PR description / Description follows the template (pull_request_target) Successful in 6s
Website / Website on Cloudflare (pull_request_target) Has been skipped
2026-11-14 03:49:25 +00:00
Compare
Author
Member

Updated to head 1982a6873 on main cf605c85b; the whole game-free suite on it: 544 passed, 2 failed, 16 skipped.

Main gained the downscale fix in #307. It conflicts with this change at the start of framegen_scale(). All six existing commits are kept with their authors, author dates, messages and trailers; one separate test/documentation commit covers the combined behavior and current fixture dependencies.

The result keeps main's rule: a checked source rectangle larger than the destination takes the filtered scaling pass even with Window scaling filter Off and Sharpness 0. CRT then reads that destination-size result. CRT Off still keeps main's downscale; same-size output can still go straight to CRT when no window effect needs scaling. The game's HUD is filtered, Longshot overlays come afterwards, and the mask stays out of frame-generation history. Native PQ keeps its 10-bit scale path and bypasses SDR CRT.

The Intel compiler guard from #252 remains in the shared pipeline constructor. The resource fixture now exercises its guarded calls with the existing allocation failures. The CRT option fixture also keeps up with hold-to-toggle from #137 and Mono audio's restart helper from #136. No check was dropped or weakened.

The focused checks pass in 24 driver invocations: 10 compiled drivers with both GCC and Clang and four source/shader drivers. The corrected options driver ran at 1982a687; the other positive checks ran at 1dfd7d62, before that fixture-only amendment. Both compilers reject the old scaling function and a version that moves CRT ahead of the mandatory downscale. Windows-target syntax passes for the options and raster-policy fixtures; the resource and scaling fixtures retain their existing warning-policy refusal. No changed fixture was executed on Windows. The known main pad-layout failure was left alone.

A picture can change when the source is larger than the destination: main's filtered downscale now comes before CRT's virtual-raster reduction, even when the two window effects are disabled. That changes the input samples and can change fine detail and moire. This combination needs a picture check before publication, with CRT Off and each filter, then return to Off, plus same-size output as a control. No new picture or timing result is claimed. Real window and generated-frame output remain untested.

The carried head has not been built for Linux or Windows, so the build box is open. The earlier build, screenshots and timing results retain the heads they actually used.

Because main now shrinks an oversized picture before this stage, the carried head's picture was looked at before pushing: the same scene with the filter off, with each of the four filters and off again, at an output equal to the destination and at twice its size. In 1:1 crops the masks and scanlines sit on destination pixels at the same pitch in both cases, with no doubled pattern. The description's Output processing entry says what changed for oversized output and what was not tested (a real window, motion).

Updated to head `1982a6873` on main `cf605c85b`; the whole game-free suite on it: 544 passed, 2 failed, 16 skipped. Main gained the downscale fix in https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/307. It conflicts with this change at the start of `framegen_scale()`. All six existing commits are kept with their authors, author dates, messages and trailers; one separate test/documentation commit covers the combined behavior and current fixture dependencies. The result keeps main's rule: a checked source rectangle larger than the destination takes the filtered scaling pass even with Window scaling filter Off and Sharpness 0. CRT then reads that destination-size result. CRT Off still keeps main's downscale; same-size output can still go straight to CRT when no window effect needs scaling. The game's HUD is filtered, Longshot overlays come afterwards, and the mask stays out of frame-generation history. Native PQ keeps its 10-bit scale path and bypasses SDR CRT. The Intel compiler guard from https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/252 remains in the shared pipeline constructor. The resource fixture now exercises its guarded calls with the existing allocation failures. The CRT option fixture also keeps up with hold-to-toggle from https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/137 and Mono audio's restart helper from https://git.magicbean.dev/MagicBeanDev/Longshot/pulls/136. No check was dropped or weakened. The focused checks pass in 24 driver invocations: 10 compiled drivers with both GCC and Clang and four source/shader drivers. The corrected options driver ran at `1982a687`; the other positive checks ran at `1dfd7d62`, before that fixture-only amendment. Both compilers reject the old scaling function and a version that moves CRT ahead of the mandatory downscale. Windows-target syntax passes for the options and raster-policy fixtures; the resource and scaling fixtures retain their existing warning-policy refusal. No changed fixture was executed on Windows. The known main pad-layout failure was left alone. A picture can change when the source is larger than the destination: main's filtered downscale now comes before CRT's virtual-raster reduction, even when the two window effects are disabled. That changes the input samples and can change fine detail and moire. This combination needs a picture check before publication, with CRT Off and each filter, then return to Off, plus same-size output as a control. No new picture or timing result is claimed. Real window and generated-frame output remain untested. The carried head has not been built for Linux or Windows, so the build box is open. The earlier build, screenshots and timing results retain the heads they actually used. Because main now shrinks an oversized picture before this stage, the carried head's picture was looked at before pushing: the same scene with the filter off, with each of the four filters and off again, at an output equal to the destination and at twice its size. In 1:1 crops the masks and scanlines sit on destination pixels at the same pitch in both cases, with no doubled pattern. The description's Output processing entry says what changed for oversized output and what was not tested (a real window, motion).
Author
Member

The carried head builds on both platforms. At 1982a687 the Windows renderer and the full game executable cross-built and linked on Linux, and the native Linux executable and renderer built and linked; neither compiler rejected anything, and nothing was run from either build. The Windows-and-Linux build box is ticked again on that basis, so every box is done and this is ready for review again.

The carried head builds on both platforms. At `1982a687` the Windows renderer and the full game executable cross-built and linked on Linux, and the native Linux executable and renderer built and linked; neither compiler rejected anything, and nothing was run from either build. The Windows-and-Linux build box is ticked again on that basis, so every box is done and this is ready for review again.
Author
Member

Carried onto main 5e072fc8c; new head e94d24624. This carry is mechanical: every commit replays unchanged, with no conflicts. The whole suite on this exact head: 546 passed, 2 failed, 16 skipped; its only failures are main's own tests/test_pad_layout.py, tests/test_module_ids.py and tests/test_framegen_resources.py. The build box is unticked until this pushed head has been built for Windows and Linux; it is ticked again then.

Carried onto main `5e072fc8c`; new head `e94d24624`. This carry is mechanical: every commit replays unchanged, with no conflicts. The whole suite on this exact head: 546 passed, 2 failed, 16 skipped; its only failures are main's own `tests/test_pad_layout.py`, `tests/test_module_ids.py` and `tests/test_framegen_resources.py`. The build box is unticked until this pushed head has been built for Windows and Linux; it is ticked again then.
mooki force-pushed feat/crt-filters from 1982a68738
All checks were successful
PR description / Description follows the template (pull_request_target) Successful in 6s
Website / Website on Cloudflare (pull_request_target) Has been skipped
to e94d24624d
All checks were successful
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Successful in 7s
2026-11-14 15:06:24 +00:00
Compare
Author
Member

The pushed head e94d24624d11798c61ebed51e3f82cd998abc8be is now built and linked for Windows (cross-built with llvm-mingw 20261104, Clang 23.1.5) and for Linux (Clang 23.1.3); the details are under Testing and the build box is ticked again. Nothing was run on Windows.

The pushed head `e94d24624d11798c61ebed51e3f82cd998abc8be` is now built and linked for Windows (cross-built with llvm-mingw 20261104, Clang 23.1.5) and for Linux (Clang 23.1.3); the details are under Testing and the build box is ticked again. Nothing was run on Windows.
All checks were successful
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Has been skipped
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Has been skipped
Third-party parts / The same as upstream (pull_request) Has been skipped
Website / Website on Cloudflare (pull_request) Has been skipped
CI / Conventional Commit title (pull_request) Successful in 4s
CI / Workflow lint (actionlint) (pull_request) Successful in 6s
Third-party parts / As their notes say (pull_request) Successful in 8s
PR description / Description follows the template (pull_request_target) Successful in 7s
This pull request has changes conflicting with the target branch.
  • docs/development.md
  • tests/framegen_resources_test.cpp
View command line instructions

Manual merge helper

Use this merge commit message when completing the merge manually.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u 'feat/crt-filters:mooki-feat/crt-filters'
git switch 'mooki-feat/crt-filters'
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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!109
No description provided.