Repository navigation
feat(vk): AMD FSR 3.1 as the default Upscaler without matrix hardware - #2110
addisonbair wants to merge 1 commit into
Conversation
|
Heads-up: this conflicts with main now. Could you rebase onto main and resolve the conflicts? Thanks! |
Joey-Magnus
left a comment
There was a problem hiding this comment.
The logic holds up, and the reasoning matches the options_version 4 step. I checked the ordering:
menu_init()runs frompick_device()beforecreate_device(), soupscaler_unchosenis already set whenmenu_upscaler_default()runs at the end ofcreate_device()(gpu/vk/vk_main.cpp:501).- By then
coopmat_init()has run anddev.max_push_descriptorsis read, sofsr3::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.cppandgpu/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 thatupscaler_rows::GAME/FSR3and the row indices still mean the same thing. Also check that no other PR has takenoptions_version=5in 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.mdand 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:39adds a second#include "vk_fsr3.h"; it's already included on line 41. The comment aboveoptions_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").
…rt, F1 or Esc") (#2197) On PC, Esc is the key that opens a game's menu, and players expect it: after #1998 added Esc as an opt-in Menu shortcut choice, null asked in #dev-work why Esc closes the Insert menu but doesn't open it. This makes that choice the default, for existing players too. **What changed** - `gpu/vk/vk_menu.h`: `menu_shortcut` defaults to 2, **Insert, F1 or Esc**. On a closed menu Esc opens it; while it is open Esc still goes back a page or closes it. F1 always works, and the row still offers Insert or F1 and F1 only. - **Existing options move too (`options_version` 5).** null is an existing player, and so is nearly everyone who would notice this. The menu writes every row on save, so the old default, `menu_shortcut=0` (Insert or F1), is in every options file whether the player chose it or not. A default that only new installs get wouldn't reach them. Like Game speed (version 2) and Descriptor reuse (version 4), `menu_init` now moves an older file's 0 to 2. Only 0 moves: - Insert and F1 still open the menu after the move, so nothing is taken away; Esc is added. - F1 only (1) can only have been chosen on purpose, so it stays. 2 stays. - `save()` writes `options_version=5`, so a player who picks Insert or F1 from now on keeps it. - #2110 (FSR 3.1 as the default Upscaler without matrix hardware) also takes version 5 for its own move. Whichever lands second takes 6; it's a one-line renumber plus its test, and I'll do it here if this one is second. - Play is unchanged. Esc still lets a captured mouse go first, and the menu it opens gives the capture back when it closes (`esc_had_capture`, #1998 and #2035, unchanged here). No game control can be bound to Esc (`bindable_keys`, `host/nn_hid.c` defaults), and a gamepad's back button counts as Esc only while the menu is open. - `readme.md`: the menu keys paragraph (with the move of older options), and the Menu shortcut row's values and default in the options table. - `tests/test_menu_keys.py`: - It compiles `menu_tick`'s Esc block with the real `Options` from `vk_menu.h` and the row's names (`tx_menu_shortcut`). Out of the box the shortcut is "Insert, F1 or Esc", and Esc opens a closed menu without the same press going back a page. With Insert or F1 or F1 only, Esc leaves it closed. Both polled and queued presses. - It also compiles `menu_init`'s reading of `hookshot_options.ini` and loads real files through it. From before version 5 (versions 2 to 4, or no `options_version` line): 0 loads as 2, 1 and 2 stay, no line gets 2. At the version `save()` writes now, read from `save()` itself: 0, 1 and 2 all stay. - This replaces the text check that pinned the old default (`int menu_shortcut = 0;`). **Testing** - `tests/test_menu_keys.py` passes, and each part fails without its change: - default put back to 0: the "Insert, F1 or Esc" check fails; - migration line removed: `loaded_as(0, old) == 2` fails; - `save()` left writing `options_version=4`: `loaded_as(mode, 4) == mode` fails (a choice made after the change would be moved again). - The other tests that read the menu's sources, its options or the window's Esc handling pass: `test_menu_env_on`, `test_audio_output_menu`, `test_framegen_preset`, `test_rumble_menu`, `test_vsync_row`, `test_audio_volume`, `test_menu_size`, `test_stick_dead_zone`, `test_menu_hit`, `test_menu_restart`, `test_menu_restart_slots`, `test_gpu_choice`, `test_display_pointer`, `test_screensaver`. - `tests/run_all.py` on Windows (llvm-mingw), on main `e19d969` with this change: 341 passed, 2 failed, 30 skipped. The two failures fail the same way on untouched main on this PC: `test_precompile_compute_guard` (this toolchain has no `windows.h` for its fixture) and `test_recorder_sync` (its exit case; its `vk_menu.cpp` source checks pass). **Not tested**: in game (no build of this branch was run; the Esc behaviour itself is #1998's, which was played), Linux. **Screenshots**: nothing new on screen. The row and its values are #1998's; it now starts on "Insert, F1 or Esc". 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Joey-Magnus
left a comment
There was a problem hiding this comment.
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 withoptions_version4 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=1on RTX makes FSR 3.1 permanent. - It's never reconsidered after a GPU upgrade.
- There are no before/after screenshots.
vk_fsr3.his included twice invk_menu.cpp.- It conflicts with main's
vk_menu.cpp(options_version=4at:1430, migration at:3113),vk_menu.handvk_coopmat.cpp.options_version=5is also used by #2119 and #2197.
Whether this should cover every non-matrix GPU or only Deck/RDNA2 is a maintainer call.
…ters' motion (#2305) AMD FSR 3.1 makes its own inputs (`vk_fsr3_inputs.cpp`), and the only motion it gets is the camera's. So anything that moves differently from the camera is given the motion of what's behind it: Link while he walks, his swinging arm, his shield, and the people around him. DLSS and FSR 4 already get those characters' own motion on `vk_temporal.cpp`'s chain. This PR puts FSR 3.1 on that chain too, as a third engine, so all three temporal upscalers share the same inputs. **What changed** - `vk_fsr3.cpp/.h`: `kChainEngine`, FSR 3.1 as a `TemporalEngine` (slot 2). - It takes the chain's colour, depth, motion (with the actor pass drawn over it), jitter and jitter phase count. - It builds FSR's depth constants from the unjittered projection. - Its picture is copied into the chain's output and squared back by `dlss_out.comp` as before. - `fsr3_chosen()` is "the row or `RX_VK_FSR=1` picks FSR 3.1". `fsr3_wanted()` now means "FSR 3.1 on its own inputs", which is false while it's on the chain. - `fsr3::prepare_sizes` and `fsr3::release` let the chain size and free FSR's images. - `vk_temporal.cpp/.h`: - `temporal_mode` returns `FSR3` for the row's FSR 3.1 before it falls back to the game's own; `engine_of` gets a third slot. - Two new `TemporalEngine` fields: - `fsr_depth`: the chain reverses depth for FSR's passes (built with `INVERTED_DEPTH`) where the projection puts the near plane at 0. - `retry`: a failed evaluation hands that frame to the network instead of turning the engine off for good. FSR 3.1's constants come from the frame's stream, which can run out for one frame. - `TemporalFrame` carries the unjittered projection and the jitter phase count. - The chain's output image gets `TRANSFER_DST`. - `RX_VK_FSR_JITTER=0` still turns FSR 3.1's jitter off on the chain. - `shaders/dlss_prep.comp`: - Flag bit 3 reverses depth (`1 - d`). - A NaN in the scene colour is written as black, following `fsr3_prep.comp`'s rule, because one NaN spreads through an upscaler's history. - The header was regenerated with `glslangValidator -V --target-env vulkan1.3 --vn DLSS_PREP_COMP_SPV`, the command `test_dlss_actor.py` checks. - `RX_VK_FSR3_CHAIN=0` gives FSR 3.1 its own inputs, as before, for comparisons (`docs/development.md`). The readme's FSR 3.1 section describes the new inputs, and says what still trails: things that aren't skinned, such as Navi's body. - Tests: - `temporal_jitter_test.cpp` covers FSR 3.1's slot and its failure gate, and a new run (`fsr-jitter-off`) where only `RX_VK_FSR_JITTER=0` is set: FSR 3.1 on the chain gets no jitter, and DLSS and FSR 4 keep theirs. - `test_upscaler_rows.py` checks that the row's FSR 3.1 runs on the chain before the fallback to the game's own, that `fsr3_wanted` is off while it does, and that `engine_of` has its slot. - Both fail on main. A mutated jitter gate fails `fsr-jitter-off`. **Testing** - `python -I -B tests/run_all.py`: 363 passed, 0 failed, 11 skipped (Linux; the skips are Windows-only, need a Vulkan 1.3 device, or need dotnet). - In game: RX 9070 XT 16 GB, Mesa RADV 26.2.6, Gentoo Linux (kernel 7.3.3), KDE Plasma Wayland, windowed, 2560x1440 output, 60 fps cap. - Host v2.1.0-main-local (source fc2ed0d0). Renderer: main 49f75dc plus this branch. - "Own" means the same build with `RX_VK_FSR3_CHAIN=0`, which is what main does. - Every chain run logged `vk: fsr3 on the chain: 600 frames drawn ... depth reversed for FSR by the chain`, with no evaluation failures. | FSR 3.1 | Kokiri Forest, 28 s camera turn (4 runs each) | Castle Town market at noon (3 runs each) | |---|---|---| | Own inputs (as main) | 59.2 fps, 1% low 46.4 | 53.1 fps | | On the chain | 59.3 fps, 1% low 48.1 | 52.5 fps | Market runs vary by about 0.9 fps between runs, so the chain costs nothing measurable. - Picture: consecutive 2560x1440 shots at Kokiri's lost-woods path, standing and walking, compared on the same build. Edge flicker is the mean frame-to-frame change of edge pixels, 0-255, while standing. | FSR 3.1 | Edge flicker, standing | 1-px outlines per MP | Halos per MP | |---|---|---|---| | 100%, own | 2.17 | 32 | 45 | | 100%, chain | 2.20 | 29 | 50 | | Quality (67%), own | 3.01 | 12 | 25 | | Quality (67%), chain | 1.67 | 2 | 9 | Four independent reviewers of the walking sheets saw no visible difference at 100%. At 67%, they found the chain slightly better: smoother edges on Link's shield and tunic hem, and slightly softer shield texture. No new artefacts. The clearest evidence is the motion field itself (below). On main, Link isn't in it at all. **Not tested:** - other GPUs and drivers; - Windows; - Steam Deck, where #2110 would make FSR 3.1 the default; - frame interpolation (`RX_VK_FSR_FG=1`), which keeps using `vk_fsr3_inputs.cpp`; - switching upscalers repeatedly while playing (release and remake are code-reviewed, not exercised in game); - HDR: the chain still clamps FSR 3.1's colour at the paper white, as main does (see below). **Screenshots** (from the game's output image before the present path, 2560x1440) The motion vectors the upscaler gets while Link walks (`RX_VK_DLSS_DEBUG=motion`). Left: camera motion only (`RX_VK_DLSS_ACTORS=0`, which is what FSR 3.1 got on main). Right: on the chain, with Link's body, arm and shield carrying their own motion:  Link walking: own inputs vs the chain, at 100% and at 67% (2x zoom crops):  **Merge notes** (from test merges against every open PR that touches these files; textual conflicts show up in git, semantic ones don't) - **Depth is reversed for FSR 3.1 on the chain.** `TemporalFrame::depth_reversed` says so. Anything that reads the chain's depth must check it rather than assume plain depth: - #2118 (DLSS frame generation through NGX, now on main): done in this PR. Commit ca2e302 passes `depth_reversed` to NGX's `depthInverted`, and `test_upscaler_rows` checks it. - #1944 (DLSS frame generation through Streamline) needs the same: `depthInverted` from `depth_reversed`, not a hard-coded false. - #1831/#1833: `nr_pre` passes `s.depth` on, and the NGX backend's `DepthInverted` should follow it too. - **#2145** adds `bool native_aa` to `TemporalEngine::prepare`. `chain_prepare` in `vk_fsr3.cpp` needs the same parameter, or the build breaks. git merges the two cleanly. FSR 3.1 never has native AA. - **#1653** (reactive mask) is on main now and merged into this branch. Commit 7ceae45 makes the mask in `chain_evaluate` the same way DLSS does on the chain, and `test_reactive_mask.py` checks it. Checked in game with `RX_VK_REACTIVE=1`: the mask is made, and FSR 3.1 draws all 600 frames on the chain. - **#2243** (DLSS HDR, linear light) is on main now (in #2342's squash, a0dfd28) and is merged into this branch. - `dlss_prep.comp` scrubs NaN first, then applies #2243's `max`/`kSquare`/`min`, and keeps the `flags & 8` depth flip. The header was regenerated; glslang 16.4.0 gives the same SPIR-V words as CI's 16.6.0 for `dlss_prep` and `dlss_out`. - `TemporalEngine` is ordered `memory`, `hdr`, `fsr_depth`, `retry`: DLSS's and FSR 4's engines are positional, and `kChainEngine` is designated. - FSR 3.1 doesn't report `hdr` yet, so it keeps the LDR pair. Giving it HDR colour on #2243's mechanism is a follow-up. - **#1831**: - It reads `fsr3_wanted()` to ask whether FSR 3.1 is chosen. After this PR that question is `fsr3_chosen()`. Its `fsr4 = !fsr3_wanted() && fsr4_wanted()` guard and its jitter guard should use `fsr3_chosen()`. The merge is textually clean, so git won't flag it. - In `vk_temporal.cpp`, the output image's usage should become `TRANSFER_DST | TRANSFER_SRC`, and its `proj[16]` and this PR's `projection[16]` should become one field. - **#2166** only matters with `RX_VK_FSR3_CHAIN=0`. - `docs/tested-configurations.md` and `docs/development.md`: rows next to other PRs' rows; keep both. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
708f7c7 to
5e9f7a7
Compare
hckrman101
left a comment
There was a problem hiding this comment.
Thanks for the rebase. The new upscaler_default flag is a nice answer to two of Joey-Magnus's earlier points: an RX_VK_MMA_SOFTWARE=1 test run on RTX no longer makes FSR 3.1 stick, and the default is worked out again on another GPU until the player touches the row. The duplicate vk_fsr3.h include is gone, and the options_version comment now covers the new step. I also checked the other ways the Upscaler can change: the launcher bridge goes through set_row, so it clears the flag too. Presets don't touch opts.dlss.
One thing breaks it, though:
- blocking The call in
create_devicenow runs beforedev.max_push_descriptorsis read (it's set in theif (dev.push_descriptor)block a few lines below, and nowhere else). When the matrices run in software and no Upscaler has been chosen,fsr3::supported()is asked while the limit is still 0. It fails the< 15check, and becausedev.deviceis already set it caches thatfalsefor the whole run. So on the Deck and on Intel, FSR 3.1 is never picked by default. Worse, it also drops out of the Upscaler row for that run, which is a regression from main. The description says the call runs after the limit is read, and Joey's review of the first version said the same, so I think the rebase moved it. Moving the call after the push-descriptor block (just beforereturn true;) fixes it. Details inline. - should fix
options_version=6is also used by #2119 (still open). Whichever lands second needs to bump to 7 and move itsversion < Ncheck with it. - The description still talks about
options_version=5throughout, and the tests in it were run before the rebase. Please update them, and re-run the Intel "no options file → FSR 3.1" case after the fix. It would have caught the problem above. - Still open from Joey's review: an in-game run on a Deck (or another RDNA2 card) with before/after screenshots and the log line, and a release-note line for players whose game-upscaler setting moves once. Whether this should apply to every GPU without matrix hardware (GTX 10/16 and older Intel too), or only to the Deck/RDNA2, is still @NotRexed's call.
CI has only run the title check so far, so the full build and tests haven't run on this head yet.
| if (compute_family != UINT32_MAX) vkGetDeviceQueue(dev.device, compute_family, 0, &dev.present_queue), dev.present_family = compute_family; | ||
| if (dev.buffer_marker) buffer_markers_init(); | ||
| coopmat_init(); | ||
| menu_upscaler_default(coopmat_software()); |
There was a problem hiding this comment.
dev.max_push_descriptors is still 0 here: it's read in the if (dev.push_descriptor) block below. When the flag is set and the matrices run in software, fsr3::supported() logs "fewer than 15 (0)" and caches false for the run (its static const bool). That turns FSR 3.1 off on exactly the GPUs this targets, including in the menu. Moving this line after that block, just before return true;, fixes it.
…ape by default (#2119) A player on an ultrawide, 16:10 or 4:3 screen starts with the game's 16:9 picture until they find the Graphics tab's **Ultrawide** row, change it and restart. The name also hides that the same row shapes the picture on screens narrower than 16:9. PC games usually follow the monitor's shape from the first start, with 16:9 as a choice. **What changed** - The **Ultrawide** row is now **Aspect ratio**, and **Monitor's shape** is its default (`gpu/vk/vk_menu.h`; `host/hos_main.c` reads an options file with no `ultrawide=` line as Monitor's shape). 16:9 stays a choice. On a 16:9 screen nothing changes. - The saved key stays `ultrawide=` (`ini_key`), so a saved `ultrawide=0` still means 16:9. - **Older options files on a screen narrower than 16:9.** The game saves every row, so a file from before this PR holds `ultrawide=0`, the old default, whether chosen or not. On a screen narrower than 16:9 (16:10 such as the Steam Deck's 1280x800, or 4:3), where 16:9 only adds bars above and below, that file's 16:9 moves to Monitor's shape; an ultrawide keeps its 16:9 (often a real choice there). The rule is one function, `hos_aspect_on` in `host/hos_wide_size.h`: `host/hos_main.c` starts the game with it, and the menu's row follows what the host started with (`host->aspect_ratio()`, in `menu_init` before `restart_init`), so the two agree on Linux too, where the menu has no screen size of its own. `save()` writes `options_version=6`, so a 16:9 chosen after the update stays. - The other value reads **16:9** again instead of #2078's "Off (16:9)". #2078 added "Off" because "Ultrawide: 16:9" read as if ultrawide were already on; under the name Aspect ratio the value is a shape, and "Aspect ratio: Off (16:9)" would not read well. Happy to keep "Off (16:9)" if you prefer it. - `recomplauncher.json`: the same label and help, and its default is `1`, so the launcher does not write 16:9 over the new default. - New help text for the row, and for **Ultrawide menus**, whose text named the old row. The readme's settings table and Output resolution notes, `menu_restart.h` and the comments that named the row follow (#2052's wording kept, with the new name). - Tests: `test_menu_restart.py` and `test_menu_restart.cpp` use the new name; `test_audio_output_menu.py` and `test_stick_dead_zone.py` give their option stubs the `ultrawide` field that `ini_key` now reads. New `test_aspect_default.py`: `hos_aspect_on` over 16:10, 4:3, 5:4, 16:9, 1366x768, 21:9, 32:9, portrait and no screen size, plus the host and menu source checks. **Tested** - In game on Windows 11, RTX 5090 (driver 621.14), 5120x2160 (21:9) monitor, in one build with the widescreen cap change from #2120: the picture takes the monitor's shape, and Kokiri Forest and Castle Town load. That run was before the merge with current main (#2052, #2041, #2078). This PR only moves a default and renames a row; the code that sizes the picture is unchanged. - `tests/run_all.py` on Windows (llvm-mingw) after merging main at 06c30bd (with #2197): 392 passed, 31 skipped, 1 failed: `test_vram_budget_row`, which fails the same way on main's own sources on this PC (clang stops on the unused `kVramMinimumOnly`). - A build of this branch compiled (`hos_main.c`, `vk_menu.cpp`) before that merge; the merge itself only adds the version 6 move next to #2197's. The move for older files was not run in game: this PC has a 21:9 screen, where an older 16:9 stays. The tests this PR touches pass, and so do the others that build `ini_key` or the row: `test_framegen_preset`, `test_menu_env_on`, `test_recomplauncher_json`, `test_rumble_menu`, `test_upscaler_rows`, `test_vsync_row`, `test_output_resolution`, `test_widescreen_shapes`. **Not tested** - 16:10, 4:3, 32:9 and 48:9 screens in game (`test_widescreen_shapes.py` covers their sizes). - Linux and the Steam Deck: SDL gives the screen's size there too, so a Steam Deck (16:10) now starts with a 16:10 picture instead of 16:9. - If #1864's translations name the row, they would need the new name. **Screenshots** From a headless run of this branch (head 2a74ec4) on Windows 11, RTX 5090, with a 5120x2160 (21:9) monitor and no options file, so a first start (`HOS_HEADLESS=1`, `RX_VK_SHOTS` at 2560x1080; the save loaded with `HOS_INPUT_SCRIPT` presses). The log: `widescreen: picture 3416x1440, scene 2560x1080, camera ratio 2.3722`.   _The menu shot is the middle 16:9 of the frame: behind it the title movie stays 16:9 with black sides. Its build had one extra line so the list scrolls headless (`ease_step = 1.0f` in `menu_record_image`), not part of this PR._ The widescreen cap PR (#2120) edited the same readme paragraph; it has merged, and the paragraph is merged with it here. **`options_version`:** #2197 (merged) took 5 for the Esc default, so this writes 6 and moves an older file's 16:9 from files before 6 (`version < 6` in `hos_aspect_on` and `menu_init`). #2110 also writes 5 today; whichever of the two lands later takes the next number. #2197's `test_menu_keys.py` compiles `menu_init`'s options read, which now also reads `host->aspect_ratio`, so its `host` stub gains that member. Its check of older files expected every version below the one `save()` writes to move Insert or F1 to Insert, F1 or Esc, which would move a version 5 file's chosen Insert or F1 again; it now reads the move's version from `menu_init` and checks that files from it up to `save()`'s version keep their choice. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: hckrman101 <239247847+hckrman101@users.noreply.github.com>
5e9f7a7 to
f7d8840
Compare
f7d8840 to
090bec6
Compare
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>
090bec6 to
ca43fa1
Compare
|
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:
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 |
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
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.hookshot_options.iniyet, or a file from beforeoptions_version=5. Older files wrotedlss=whether or not it was chosen, which is the same reasoning as theoptions_version4 step for Descriptor reuse. Saving now writesoptions_version=5, so a choice made in the menu after this is kept.create_device, after the push-descriptor limit is read, becausefsr3::supported()needs that limit and caches its answer.Testing
tests/run_all.py: the same results on cleanmain(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.options_version=5,dlss=0→ kept (chosen)options_version=4,dlss=0→ FSR 3.1options_version=4,dlss=1→ kept (not the game's own)🤖 Generated with Claude Code