feat(vk): AMD CAS as a Sharpening method #1979

Open
FIocker wants to merge 2 commits from FIocker/super-build/amd-cas into main
FIocker commented 2026-11-10 22:29:32 +00:00 (Migrated from github.com)

This adds a Sharpening method row under Sharpness in the Graphics tab. It offers Gentle (today's sharpen) or AMD CAS, is saved as sharpening_method, and is left out of the Quality preset. It's a standalone PR on main (rebuilt on 51da0464).

Why

Temporal upscalers soften the picture. DLSS, DLAA and FSR rebuild each frame from jittered samples, and the result is noticeably softer than a native render, especially textures, foliage and distant detail. The window pass's current sharpen is RCAS-like and never goes past the light and dark around a pixel. That keeps it free of halos, but it also means it barely recovers detail an upscaler has smoothed over.

AMD's Contrast Adaptive Sharpening suits this job. It is designed to sharpen most where the picture is soft and least where edges already have contrast. That brings back the texture detail DLSS and FSR blur without overshooting edges that are already crisp. It's the sharpener many games pair with their upscalers for exactly this reason.

How

  • shaders/fg_scale.comp: adds cas(), AMD FidelityFX CAS (MIT, ffx_cas.h), using its filter without scaling.
    • It reads the 3×3 neighbourhood one picture pixel apart, with the same spacing the existing sharpen uses.
    • The lobe is weighted by how far the neighbourhood is from black and white, softened by a square root.
    • The Sharpness slider maps to CAS's sharpness, a peak of −1/8 to −1/5.
    • main() uses it when pc.method == 1; otherwise the existing sharpen() runs unchanged.
  • Push constants gain int method at the end and grow from 20 to 24 bytes, for both of fg_scale.comp's pipelines: the plain one and the FG_SCALE_PQ one for native HDR (#1942/#2342). vk_framegen.cpp makes both with 24, and tests/upscale_independent_test.cpp checks both.
  • Both headers, fg_scale_comp_spv.h and fg_scale_pq_comp_spv.h, are regenerated with tests/test_fg_scale_sharpen.py --write (glslang 11.12.0). That's the script that writes the pair; scripts/host_shaders.py covers only gpu/vk/host_shaders/.
  • HDR: on the PQ path CAS works on PQ codes, which stay within 0..1, so its clamp still holds. An in-game check with HDR on is still to come; see Testing.
  • Menu: opts.sharpen_method, the row, and tx_sharpen_method. Gentle is the default, so nothing changes until someone picks AMD CAS.
  • Slider start: CAS's own sharpness 0 is already a full −1/8 lobe. So that the slider's first step doesn't jump from nothing to that floor, the lobe fades in over 0 to 10%; from 10% on, it's AMD's mapping.
  • Licence: AMD's copyright line sits with the port, pointing to the MIT notice in third_party/fidelityfx-sdk/LICENSE.txt.

Testing

  • Shader: compiles with glslang 11.12.0, and both headers name this source's hash.
  • Tests: tests/run_all.py, the whole suite, locally (clang targeting MSVC, the CPM Vulkan headers, glslang 11.12): 433 passed. The 24 failures are exactly the ones main at 51da0464 has on the same machine, all down to the local toolchain. test_fg_scale_sharpen.py (cas() included) and test_upscale_independent.py (the method, on both pipelines) pass.
  • In game: the CAS pass has been played in the combined build with DLSS at Sharpness 50 (SDR). Still to come on this head: a run with HDR (Native) on, and before/after screenshots.

🤖 Generated with Claude Code

This adds a **Sharpening method** row under Sharpness in the Graphics tab. It offers **Gentle** (today's sharpen) or **AMD CAS**, is saved as `sharpening_method`, and is left out of the Quality preset. It's a standalone PR on `main` (rebuilt on 51da0464). ## Why Temporal upscalers soften the picture. DLSS, DLAA and FSR rebuild each frame from jittered samples, and the result is noticeably softer than a native render, especially textures, foliage and distant detail. The window pass's current sharpen is RCAS-like and never goes past the light and dark around a pixel. That keeps it free of halos, but it also means it barely recovers detail an upscaler has smoothed over. AMD's **Contrast Adaptive Sharpening** suits this job. It is designed to sharpen most where the picture is soft and least where edges already have contrast. That brings back the texture detail DLSS and FSR blur without overshooting edges that are already crisp. It's the sharpener many games pair with their upscalers for exactly this reason. ## How - **`shaders/fg_scale.comp`**: adds `cas()`, AMD FidelityFX CAS (MIT, `ffx_cas.h`), using its filter without scaling. - It reads the 3×3 neighbourhood one picture pixel apart, with the same spacing the existing sharpen uses. - The lobe is weighted by how far the neighbourhood is from black and white, softened by a square root. - The Sharpness slider maps to CAS's sharpness, a peak of −1/8 to −1/5. - `main()` uses it when `pc.method == 1`; otherwise the existing `sharpen()` runs unchanged. - **Push constants** gain `int method` at the end and grow from 20 to 24 bytes, for **both** of `fg_scale.comp`'s pipelines: the plain one and the `FG_SCALE_PQ` one for native HDR (#1942/#2342). `vk_framegen.cpp` makes both with 24, and `tests/upscale_independent_test.cpp` checks both. - **Both headers**, `fg_scale_comp_spv.h` and `fg_scale_pq_comp_spv.h`, are regenerated with `tests/test_fg_scale_sharpen.py --write` (glslang 11.12.0). That's the script that writes the pair; `scripts/host_shaders.py` covers only `gpu/vk/host_shaders/`. - **HDR**: on the PQ path CAS works on PQ codes, which stay within 0..1, so its clamp still holds. An in-game check with HDR on is still to come; see Testing. - **Menu**: `opts.sharpen_method`, the row, and `tx_sharpen_method`. Gentle is the default, so nothing changes until someone picks AMD CAS. - **Slider start**: CAS's own sharpness 0 is already a full −1/8 lobe. So that the slider's first step doesn't jump from nothing to that floor, the lobe fades in over 0 to 10%; from 10% on, it's AMD's mapping. - **Licence**: AMD's copyright line sits with the port, pointing to the MIT notice in `third_party/fidelityfx-sdk/LICENSE.txt`. ## Testing - **Shader**: compiles with glslang 11.12.0, and both headers name this source's hash. - **Tests**: `tests/run_all.py`, the whole suite, locally (clang targeting MSVC, the CPM Vulkan headers, glslang 11.12): 433 passed. The 24 failures are exactly the ones main at 51da0464 has on the same machine, all down to the local toolchain. `test_fg_scale_sharpen.py` (`cas()` included) and `test_upscale_independent.py` (the method, on both pipelines) pass. - **In game**: the CAS pass has been played in the combined build with DLSS at Sharpness 50 (SDR). Still to come on this head: a run with HDR (Native) on, and before/after screenshots. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
hckrman101 (Migrated from github.com) reviewed 2026-11-10 23:57:09 +00:00
hckrman101 (Migrated from github.com) left a comment

Thanks, this is a small, clean change. The description matches the diff: of the −477 lines, 469 are the regenerated SPIR-V header, and the rest are one-line replacements (push size 20→24, the pc struct, the test stub). No features are dropped and nothing else is rewritten.

What I checked:

  • CAS math: cas() matches ffx_cas.h's no-scaling path: soft min/max over the cross plus the 3×3, amp = sqrt(saturate(min(mn, 2 − mx) / mx)), peak = −1/lerp(8, 5, sharpness), the cross-shaped lobe normalised by 1 + 4w, then saturate. The max(mx, 1/1024) guard stands in for the bare rcp and avoids a divide by zero on black. The neighbour spacing and edge handling are the same as the existing sharpen (one picture pixel, clamp-to-edge sampler).
  • Colour space / HDR: it runs on the 8-bit picture (rgba8 dst), and the HDR10 pass encodes after it from framegen_scaled_view() (vk_replay.cpp present). So the 0..1 assumptions in 2 − mx and the final clamp hold. Like the Gentle path, it sharpens the encoded values, not linear light, which is also how most CAS integrations use it.
  • Push constants: the SPIR-V puts method at offset 20 and the block is 24 bytes. That matches init_scale, the C++ struct (both dispatches, side 3 and the main one, push sizeof pc) and the test stub.
  • Generated header: the hash on the first line is this .comp's. I rebuilt it with glslang 16.6.0 (main's header was made with 16.6.0, this one with 11.12.0). Same word count, spirv-val passes, and the disassembly differs only in the order of three OpDecorate lines. So the header is this shader. test_fg_scale_sharpen.py will pass without comparing bytes because of the version mismatch. Nit: regenerating with 16.6.0 would keep main's header on one glslang.
  • Menu: no new HOS_OPT_* id, so no collision with main or the open PRs. The value is saved by name as sharpening_method, it is preset_exempt as described, and it takes no restart. I checked the tests that cut the row table or functions out of vk_menu.cpp (test_upscaler_rows, test_menu_restart_slots, test_framegen_preset, the Row rows[] cutters): none depends on the row after Sharpness, so I don't expect breaks.

Still open:

  1. Licence notice (inline): MIT asks for the copyright and permission notice to go with substantial portions. cas() is a direct port of AMD's filter, so please add AMD's copyright line and point to the MIT text. third_party/fidelityfx-sdk/LICENSE.txt already has it in the tree.
  2. No CI and no in-game run yet: both workflows on this head are action_required (they wait for a maintainer to approve the run). The description says the C++ fixtures were left to CI, so for now nothing has compiled upscale_independent_test.cpp or the shader tests on this branch. This port onto main also hasn't been played. A run in the game with AMD CAS at a couple of Sharpness values, plus a before/after screenshot (CONTRIBUTING asks for screenshots when a change shows on screen), would settle it.
  3. Nit: test_fg_scale_sharpen.py checks sharpen()'s properties as compiled C++ (unchanged at 0, within 0..1, no halos past the neighbours), but nothing covers cas(). The same harness could check that a flat neighbourhood is unchanged and the output stays in 0..1. upscale_independent_test could also check that pc.method follows opts.sharpen_method.
  4. Heads-up: #1488 (screen filters) also appends to this push-constant block at offset 20 (int fx; float strength; float time). Whichever lands second has to move its members and regenerate the header. #1942 adds a PQ variant of this shader, so fg_scale_pq_comp_spv.h would need regenerating too.
Thanks, this is a small, clean change. The description matches the diff: of the −477 lines, 469 are the regenerated SPIR-V header, and the rest are one-line replacements (push size 20→24, the pc struct, the test stub). No features are dropped and nothing else is rewritten. What I checked: - **CAS math**: `cas()` matches ffx_cas.h's no-scaling path: soft min/max over the cross plus the 3×3, `amp = sqrt(saturate(min(mn, 2 − mx) / mx))`, `peak = −1/lerp(8, 5, sharpness)`, the cross-shaped lobe normalised by `1 + 4w`, then saturate. The `max(mx, 1/1024)` guard stands in for the bare `rcp` and avoids a divide by zero on black. The neighbour spacing and edge handling are the same as the existing sharpen (one picture pixel, clamp-to-edge sampler). - **Colour space / HDR**: it runs on the 8-bit picture (rgba8 `dst`), and the HDR10 pass encodes after it from `framegen_scaled_view()` (vk_replay.cpp present). So the 0..1 assumptions in `2 − mx` and the final clamp hold. Like the Gentle path, it sharpens the encoded values, not linear light, which is also how most CAS integrations use it. - **Push constants**: the SPIR-V puts `method` at offset 20 and the block is 24 bytes. That matches `init_scale`, the C++ struct (both dispatches, side 3 and the main one, push `sizeof pc`) and the test stub. - **Generated header**: the hash on the first line is this .comp's. I rebuilt it with glslang 16.6.0 (main's header was made with 16.6.0, this one with 11.12.0). Same word count, `spirv-val` passes, and the disassembly differs only in the order of three `OpDecorate` lines. So the header is this shader. `test_fg_scale_sharpen.py` will pass without comparing bytes because of the version mismatch. Nit: regenerating with 16.6.0 would keep main's header on one glslang. - **Menu**: no new `HOS_OPT_*` id, so no collision with main or the open PRs. The value is saved by name as `sharpening_method`, it is `preset_exempt` as described, and it takes no restart. I checked the tests that cut the row table or functions out of vk_menu.cpp (`test_upscaler_rows`, `test_menu_restart_slots`, `test_framegen_preset`, the `Row rows[]` cutters): none depends on the row after Sharpness, so I don't expect breaks. Still open: 1. **Licence notice** (inline): MIT asks for the copyright and permission notice to go with substantial portions. `cas()` is a direct port of AMD's filter, so please add AMD's copyright line and point to the MIT text. `third_party/fidelityfx-sdk/LICENSE.txt` already has it in the tree. 2. **No CI and no in-game run yet**: both workflows on this head are `action_required` (they wait for a maintainer to approve the run). The description says the C++ fixtures were left to CI, so for now nothing has compiled `upscale_independent_test.cpp` or the shader tests on this branch. This port onto main also hasn't been played. A run in the game with AMD CAS at a couple of Sharpness values, plus a before/after screenshot (CONTRIBUTING asks for screenshots when a change shows on screen), would settle it. 3. Nit: `test_fg_scale_sharpen.py` checks `sharpen()`'s properties as compiled C++ (unchanged at 0, within 0..1, no halos past the neighbours), but nothing covers `cas()`. The same harness could check that a flat neighbourhood is unchanged and the output stays in 0..1. `upscale_independent_test` could also check that `pc.method` follows `opts.sharpen_method`. 4. Heads-up: #1488 (screen filters) also appends to this push-constant block at offset 20 (`int fx; float strength; float time`). Whichever lands second has to move its members and regenerate the header. #1942 adds a PQ variant of this shader, so `fg_scale_pq_comp_spv.h` would need regenerating too.
@ -59,6 +60,26 @@ vec3 sharpen(vec3 c, vec3 n, vec3 s, vec3 w, vec3 e, float amount) {
// (and no further than the lightest and darkest pixel around it: an edge gets steeper, no halo beside it)
hckrman101 (Migrated from github.com) commented 2026-11-10 23:57:09 +00:00

Please keep AMD's notice with the port, e.g. // Copyright (c) 2019-2020 Advanced Micro Devices, Inc. MIT licence: third_party/fidelityfx-sdk/LICENSE.txt (or the full permission text). Crediting ffx_cas.h is good, but MIT asks for the copyright line itself to go with substantial portions.

Please keep AMD's notice with the port, e.g. `// Copyright (c) 2019-2020 Advanced Micro Devices, Inc. MIT licence: third_party/fidelityfx-sdk/LICENSE.txt` (or the full permission text). Crediting ffx_cas.h is good, but MIT asks for the copyright line itself to go with substantial portions.
hckrman101 (Migrated from github.com) commented 2026-11-10 23:57:09 +00:00

Nit, UX: the pass only runs at pc.sharp > 0, and CAS's sharpness 0 is already a full −1/8 lobe (kernel centre 2, sides −0.25 where amp is 1). So the slider's first step (0 → 10%) goes from none straight to near CAS's floor, while Gentle starts from nothing. AMD's mapping is fine. A hint line for the row could say that the first step is already noticeable, or you could fade the lobe in over the first step.

Nit, UX: the pass only runs at `pc.sharp > 0`, and CAS's sharpness 0 is already a full −1/8 lobe (kernel centre 2, sides −0.25 where `amp` is 1). So the slider's first step (0 → 10%) goes from none straight to near CAS's floor, while Gentle starts from nothing. AMD's mapping is fine. A hint line for the row could say that the first step is already noticeable, or you could fade the lobe in over the first step.
FIocker commented 2026-11-11 09:27:06 +00:00 (Migrated from github.com)

Thanks for the careful review. The branch is rebased on main (d7a73062), and these are addressed in 7856f59b:

  1. Licence: AMD's copyright line now sits with cas(): Copyright (C) 2024 Advanced Micro Devices, Inc. (FidelityFX SDK 1.1.4), the year in the SDK's ffx_cas.h I ported from, plus a pointer to the MIT text in third_party/fidelityfx-sdk/LICENSE.txt.
  2. Slider's first step: I went with the fade. The lobe ramps in over 0 to 10% (w *= clamp(amount * 10, 0, 1)), so CAS starts from nothing like Gentle does; from 10% on it's AMD's mapping. The comment says this part isn't AMD's.
  3. Tests:
    • test_fg_scale_sharpen.py now compiles cas() too and checks: the picture unchanged at 0; finite and inside 0..1 over 200k random neighbourhoods at every setting; flat black, grey and white unchanged at full strength; a blurred edge steeper as the setting rises.
    • upscale_independent_test checks the pushed method is 1 for AMD CAS and 0 for Gentle.
    • I ran those fixtures, test_upscaler_rows and test_menu_restart_slots locally (clang targeting MSVC); all pass.
  4. glslang: this PC only has 11.12.0, so the header is still made with it. If you'd like it on 16.6.0, I'm happy to regenerate once I have that build, or a maintainer can run --write.
  5. Not done yet: CI still waits for approval, and this port onto main hasn't been played. Screenshots will follow from an in-game run.
  6. #1488 / #1942: noted. Neither has landed yet. Whichever lands first, the other moves its push-constant members and regenerates the header(s).
Thanks for the careful review. The branch is rebased on main (d7a73062), and these are addressed in 7856f59b: 1. **Licence**: AMD's copyright line now sits with `cas()`: `Copyright (C) 2024 Advanced Micro Devices, Inc. (FidelityFX SDK 1.1.4)`, the year in the SDK's `ffx_cas.h` I ported from, plus a pointer to the MIT text in `third_party/fidelityfx-sdk/LICENSE.txt`. 2. **Slider's first step**: I went with the fade. The lobe ramps in over 0 to 10% (`w *= clamp(amount * 10, 0, 1)`), so CAS starts from nothing like Gentle does; from 10% on it's AMD's mapping. The comment says this part isn't AMD's. 3. **Tests**: - `test_fg_scale_sharpen.py` now compiles `cas()` too and checks: the picture unchanged at 0; finite and inside 0..1 over 200k random neighbourhoods at every setting; flat black, grey and white unchanged at full strength; a blurred edge steeper as the setting rises. - `upscale_independent_test` checks the pushed `method` is 1 for AMD CAS and 0 for Gentle. - I ran those fixtures, `test_upscaler_rows` and `test_menu_restart_slots` locally (clang targeting MSVC); all pass. 4. **glslang**: this PC only has 11.12.0, so the header is still made with it. If you'd like it on 16.6.0, I'm happy to regenerate once I have that build, or a maintainer can run `--write`. 5. **Not done yet**: CI still waits for approval, and this port onto main hasn't been played. Screenshots will follow from an in-game run. 6. **#1488 / #1942**: noted. Neither has landed yet. Whichever lands first, the other moves its push-constant members and regenerates the header(s).
hckrman101 commented 2026-11-11 15:50:58 +00:00 (Migrated from github.com)

Heads-up: this conflicts with main now. Could you rebase onto main and resolve the conflicts? Thanks!

Heads-up: this conflicts with main now. Could you rebase onto main and resolve the conflicts? Thanks!
Joey-Magnus (Migrated from github.com) reviewed 2026-11-11 21:46:38 +00:00
Joey-Magnus (Migrated from github.com) left a comment

The review points are all addressed in 7856f59b (AMD's notice, the fade-in, the cas() and method tests). What's left is mechanical, but it needs doing before this can go in.

It conflicts with main in gpu/vk/shaders/fg_scale_comp_spv.h, gpu/vk/vk_menu.cpp and tests/upscale_independent_test.cpp. Since #1942/#2342, fg_scale.comp is also compiled with FG_SCALE_PQ into fg_scale_pq_comp_spv.h, which writes RGB10A2 for native HDR. Adding int method to the push block means both headers have to be regenerated with scripts/host_shaders.py, not just the one in this branch. Otherwise the PQ pipeline is left with a 20-byte block while the C++ pushes 24. On the PQ path CAS works on PQ codes, which stay within 0..1, so the clamp still holds, but please say in the description what you saw with HDR on.

The PR title also fails the Conventional Commit check: something like feat(vk): AMD CAS as a Sharpening method would pass. The in-game run and the before/after screenshots are still to come.

The review points are all addressed in 7856f59b (AMD's notice, the fade-in, the `cas()` and `method` tests). What's left is mechanical, but it needs doing before this can go in. It conflicts with main in `gpu/vk/shaders/fg_scale_comp_spv.h`, `gpu/vk/vk_menu.cpp` and `tests/upscale_independent_test.cpp`. Since #1942/#2342, `fg_scale.comp` is also compiled with `FG_SCALE_PQ` into `fg_scale_pq_comp_spv.h`, which writes RGB10A2 for native HDR. Adding `int method` to the push block means both headers have to be regenerated with `scripts/host_shaders.py`, not just the one in this branch. Otherwise the PQ pipeline is left with a 20-byte block while the C++ pushes 24. On the PQ path CAS works on PQ codes, which stay within 0..1, so the clamp still holds, but please say in the description what you saw with HDR on. The PR title also fails the Conventional Commit check: something like `feat(vk): AMD CAS as a Sharpening method` would pass. The in-game run and the before/after screenshots are still to come.
github-sync changed title from AMD CAS sharpening: unsoften DLSS / FSR upscaled pictures to feat(vk): AMD CAS as a Sharpening method 2026-11-12 13:39:09 +00:00
Owner

@FIocker commented on GitHub · 2026-11-12 12:42 UTC · original

Rebuilt on main at 51da0464 (81e31ce7):

  • PQ pipeline: thanks, Joey, it was exactly that. vk_framegen.cpp made the FG_SCALE_PQ pipeline with 20 bytes. Both pipelines are now made with 24, and both headers are regenerated. One correction: the pair comes from tests/test_fg_scale_sharpen.py --write, since scripts/host_shaders.py only builds gpu/vk/host_shaders/.
  • Tests: upscale_independent_test now checks the 24-byte block on both pipelines. The whole local run_all.py matches main at 51da0464: 433 passed, and the 24 failures are main's own on this machine.
  • Title: now a Conventional Commit.
  • Still to come: an HDR run (the description says what to expect on PQ codes) and the before/after screenshots.
> [@FIocker](https://github.com/FIocker) commented on GitHub · 2026-11-12 12:42 UTC · [original](https://github.com/NotRexed/Hookshot/pull/1979#issuecomment-6823918528) Rebuilt on main at 51da0464 (81e31ce7): - **PQ pipeline**: thanks, Joey, it was exactly that. `vk_framegen.cpp` made the `FG_SCALE_PQ` pipeline with 20 bytes. Both pipelines are now made with 24, and both headers are regenerated. One correction: the pair comes from `tests/test_fg_scale_sharpen.py --write`, since `scripts/host_shaders.py` only builds `gpu/vk/host_shaders/`. - **Tests**: `upscale_independent_test` now checks the 24-byte block on both pipelines. The whole local `run_all.py` matches main at 51da0464: 433 passed, and the 24 failures are main's own on this machine. - **Title**: now a Conventional Commit. - **Still to come**: an HDR run (the description says what to expect on PQ codes) and the before/after screenshots. <!-- github-sync:c:6823918528 -->
Owner

@Joey-Magnus reviewed on GitHub · 2026-11-12 14:32 UTC · original

Everything from my last review is fixed. Both FG_SCALE pipelines are now made with a 24-byte push block (vk_framegen.cpp:263 and :576), both headers are regenerated, upscale_independent_test checks 24 on both, and the title passes. You were right that the pair comes from tests/test_fg_scale_sharpen.py --write. I had pointed you at the wrong script.

I merged it into current main. test_fg_scale_sharpen, test_upscale_independent, test_framegen_resources, test_upscaler_rows, test_menu_restart_slots, test_menu_i18n and test_framegen_preset all pass.

Still open:

  • No game run yet. CONTRIBUTING asks for before/after screenshots for a change you can see on screen, and the HDR run you mention is also still to come.
  • #2105 (likely to merge soon) adds tests/test_readme_menu_rows.py, which needs a readme option-table line for every menu row. With both merged, it fails on "Sharpening method". Please add a line after Sharpness, for example | | Sharpening method | Gentle, AMD CAS | Gentle |.
  • #1488 also appends to the same push block at offset 20, so whichever lands second has to move its members and regenerate both headers.
> [@Joey-Magnus](https://github.com/Joey-Magnus) reviewed on GitHub · 2026-11-12 14:32 UTC · [original](https://github.com/NotRexed/Hookshot/pull/1979#pullrequestreview-5876204459) Everything from my last review is fixed. Both `FG_SCALE` pipelines are now made with a 24-byte push block (`vk_framegen.cpp:263` and `:576`), both headers are regenerated, `upscale_independent_test` checks 24 on both, and the title passes. You were right that the pair comes from `tests/test_fg_scale_sharpen.py --write`. I had pointed you at the wrong script. I merged it into current main. `test_fg_scale_sharpen`, `test_upscale_independent`, `test_framegen_resources`, `test_upscaler_rows`, `test_menu_restart_slots`, `test_menu_i18n` and `test_framegen_preset` all pass. Still open: - No game run yet. CONTRIBUTING asks for before/after screenshots for a change you can see on screen, and the HDR run you mention is also still to come. - #2105 (likely to merge soon) adds `tests/test_readme_menu_rows.py`, which needs a readme option-table line for every menu row. With both merged, it fails on "Sharpening method". Please add a line after Sharpness, for example `| | Sharpening method | Gentle, AMD CAS | Gentle |`. - #1488 also appends to the same push block at offset 20, so whichever lands second has to move its members and regenerate both headers. <!-- github-sync:r:5876204459 -->
Commenting is not possible because the repository is archived.
No description provided.