Skip to content

feat(vk): AMD FSR 3.1 as the default Upscaler without matrix hardware - #2110

Closed
addisonbair wants to merge 1 commit into
NotRexed:mainfrom
addisonbair:fsr-default
Closed

addisonbair wants to merge 1 commit into
NotRexed:mainfrom
addisonbair:fsr-default

Conversation

@addisonbair

Copy link
Copy Markdown
Contributor

On GPUs without matrix hardware (AMD RDNA2 such as the Steam Deck, Intel), the game's own upscaler runs its network's matrix multiplies in a shader function. On the Steam Deck that single dispatch costs ~12 ms of the frame (#1000). Switching the Upscaler to AMD FSR 3.1 removes it: 11.9 → 13.9 fps docked in the #1000 measurements, and the network dispatch drops out of the GPU profile. Today a Deck user has to find that menu row themselves.

What changes

  • When the matrix multiplies run in software (coopmat_software(), i.e. mma_software) and FSR 3.1 is supported, an Upscaler nobody has chosen defaults to AMD FSR 3.1. A line in the log says so.
  • "Nobody has chosen" means no hookshot_options.ini yet, or a file from before options_version=5. Older files wrote dlss= whether or not it was chosen, which is the same reasoning as the options_version 4 step for Descriptor reuse. Saving now writes options_version=5, so a choice made in the menu after this is kept.
  • Where the cooperative matrices run on hardware (NVIDIA RTX, for one), nothing changes and the game's own upscaler stays.
  • The decision runs at the end of create_device, after the push-descriptor limit is read, because fsr3::supported() needs that limit and caches its answer.

Testing

  • tests/run_all.py: the same results on clean main (1a43a1e) and with the patch. Every failure is the same on both and comes from tools missing in my container (glslang etc.); none are new.
  • Headless start-up on a ThinkPad P16 (Intel Arc ARL iGPU, NVIDIA RTX PRO 2000):
    • Intel, no options file → FSR 3.1 by default
    • NVIDIA, no options file → the game's own upscaler, unchanged
    • Intel, options_version=5, dlss=0 → kept (chosen)
    • Intel, options_version=4, dlss=0 → FSR 3.1
    • Intel, options_version=4, dlss=1 → kept (not the game's own)

🤖 Generated with Claude Code

@Joey-Magnus Joey-Magnus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic holds up, and the reasoning matches the options_version 4 step. I checked the ordering:

  • menu_init() runs from pick_device() before create_device(), so upscaler_unchosen is already set when menu_upscaler_default() runs at the end of create_device() (gpu/vk/vk_main.cpp:501).
  • By then coopmat_init() has run and dev.max_push_descriptors is read, so fsr3::supported() gives an answer it can cache.
  • A chosen DLSS or FSR 3.1 is left alone. Hardware matrices change nothing.
  • fsr3_wanted() still gives way to an upscaler chosen through the environment (temporal_env_chosen()).

The menu tests that read vk_menu.cpp and vk_main.cpp pass at this head (51 checks).

What it still needs:

  • blocking Merge conflict with main in gpu/vk/vk_menu.cpp and gpu/vk/vk_menu.h, as @hckrman101 noted. Main has moved a lot there: FSR 4 (#2342), DLSS frame generation and others. When you rebase, check that upscaler_rows::GAME/FSR3 and the row indices still mean the same thing. Also check that no other PR has taken options_version=5 in the meantime. CI hasn't run on this PR yet (only the title check), so it will get its first full run after the rebase.
  • should fix It needs an in-game check on a Steam Deck. The Deck is Tier 1 in docs/workflow.md and the main hardware this targets. So far the testing is headless start-up on Intel Arc and NVIDIA, plus fps numbers taken from #1000. This changes what every Deck and Intel player sees on first start, so please add a run on a Deck (or an RDNA2 card) showing the picture with FSR 3.1 by default. CONTRIBUTING asks for screenshots when a change shows on screen. A before/after at the same spot, plus the log line, would cover it.
  • question Players with an older options file who picked "the game's own" on purpose will be moved to FSR 3.1 once. That's the same trade as the Descriptor reuse step, and probably the right call. It's worth a line in the release notes so nobody thinks their setting was lost.
  • minor gpu/vk/vk_menu.cpp:39 adds a second #include "vk_fsr3.h"; it's already included on line 41. The comment above options_version (line 1260) lists what 2, 3 and 4 mean, so add 5 ("the Upscaler: settled once the device is known, see menu_upscaler_default").

@Joey-Magnus Joey-Magnus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic is right (coopmat_init() runs before the new call, and menu_env_on's version < since leaves Descriptor reuse alone), but the scope is wider than the description says:

  • "Without matrix hardware" covers RDNA2 and older, many Intel cards, and GTX 10/16 (vk_coopmat.cpp:342). NVIDIA is only unchanged for RTX. Existing users with options_version 4 who chose the game's upscaler are switched once.
  • The default is saved on the first row change, so a test run with RX_VK_MMA_SOFTWARE=1 on RTX makes FSR 3.1 permanent.
  • It's never reconsidered after a GPU upgrade.
  • There are no before/after screenshots.
  • vk_fsr3.h is included twice in vk_menu.cpp.
  • It conflicts with main's vk_menu.cpp (options_version=4 at :1430, migration at :3113), vk_menu.h and vk_coopmat.cpp. options_version=5 is also used by #2119 and #2197.

Whether this should cover every non-matrix GPU or only Deck/RDNA2 is a maintainer call.

On a GPU without cooperative-matrix hardware (AMD RDNA2 such as the
Steam Deck, Intel), the game's own upscaler runs its network's matrix
multiplies in a shader function: about 12 ms of a Steam Deck frame for
that one dispatch (NotRexed#1000), which FSR 3.1 replaces. There the Upscaler
now defaults to AMD FSR 3.1 when FSR 3.1 runs; with matrix hardware the
game's own stays. The default is settled at the end of create_device,
after the push-descriptor limit fsr3::supported() reads.

Until the player moves the Upscaler row, the file keeps
upscaler_default=1 and each start settles the default again, so another
GPU, or a run with RX_VK_MMA_SOFTWARE=1, gets its own. A file from
before options_version 7 with the game's own Upscaler wrote it whether
chosen or not, so it is treated as not chosen, as Descriptor reuse was
at version 4. tests/test_upscaler_default.py checks the order and the
flag.

Release note: on the Steam Deck and other GPUs without matrix hardware,
the Upscaler now starts on AMD FSR 3.1. If you had the game's own
upscaler selected, it moves to FSR 3.1 once; choose it again in
Graphics > Upscaler and it stays.

Assisted-by: Claude Opus 5.5 (Anthropic)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@addisonbair

Copy link
Copy Markdown
Contributor Author

Closing this one: on current main it makes the Steam Deck slower, the opposite of what it was for.

I did the Deck check you asked for, and also measured what each upscaler costs. Setup: main (eaef146 game, this PR's renderer for both rows, only the Upscaler row changed), the first-start Minimum preset, the docked profile (what a first start sets), the standing Kokiri scene at a 1200 MHz GPU clock, two passes each:

Deck (Van Gogh, RADV) the game's own FSR 3.1 FSR's extra GPU
2048x1152 output 12.4 / 12.3 fps, GPU 39.5 ms 11.2 / 11.2 fps, GPU 47.5 ms +8.0 ms
1280x800 output 13.2 / 13.2 fps, GPU 34.5 ms 12.2 / 12.2 fps, GPU 38.5 ms +4.0 ms

The handheld profile shows the same thing (2048x1152: 14.1 vs 13.2 fps, +5 ms GPU).

The default itself works on the Deck. "Upscaler AMD FSR 3.1 by default" is logged on a first start, again on the next start from the file it wrote, and from a file main wrote. Intel ARL behaves the same in start-up checks. The pictures are equivalent.

The reason the premise no longer holds: the ~12 ms this was meant to save was the game's TAAU network on v2.0.14, where FSR 3.1 really was a win on the Deck (#1000). On current main, AMD runs a cheap bilinear stand-in in its place (the context of #1439), so on these GPUs FSR 3.1 only adds cost. For image quality on AMD, #1439 (FSR 1 EASU + RCAS over the stand-in, ~0.5 ms) looks like the better path. I couldn't measure Intel in-game here (pipelines fail with -13 on this ARL iGPU), so I have no evidence to narrow it to Intel either.

Thanks for the careful reviews. The ordering bug you caught would have been a real regression.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants