WIP: feat(vk): a Steam Deck profile, on by itself on a Deck #225
No reviewers
Labels
No labels
area/accessibility
area/audio
area/input
area/install
area/menu
area/renderer
area/saves
area/upscaling
good first issue
gpu/amd
gpu/igpu
gpu/intel
gpu/nvidia
help wanted
kind
bug
kind
chore
kind
docs
kind
feature
kind
performance
kind
question
needs measurements
platform/linux
platform/steam-deck
platform/windows
preview
priority
critical
priority
high
priority
low
status
blocked
status
confirmed
status
duplicate
status
invalid
status
needs-info
status
wontfix
testing wanted
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!225
Loading…
Reference in a new issue
No description provided.
Delete branch "dukenheimer/Longshot:feat/steam-deck-profile"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
On a Steam Deck the game runs at about 15 frames a second and the full shader precompile can run the Deck out of memory
(v2.2.0-rc.1 was OOM-killed 4 times in 3 starts). This adds one switch, the Steam Deck profile, that turns on what
measured fastest there, and Auto (this PC) turns it on by itself on a Deck.
What changed
gpu/vk/auto_settings.h:Hardware::deck;deck_mode()(RX_VK_STEAM_DECKoversteam_deck=in the optionsfile, else Auto) and
deck_on()(Auto: on where a Deck is found,Hardware::handheld);choose()with the profile on:Minimum, the game's own upscaler,
Choice::framegen = 2andChoice::vram_auto, said in Auto'swhytext.fingerprint()mixes the profile in only when it is on, so no other PC's Auto chooses again with this update.gpu/vk/vk_main.cppdeck_profile_decide(), called at the end ofpick_device()(the options read, the cardknown, before the device and its first command buffer): decides the profile, logs
vk: Steam Deck profile: on|off (why),and asks for the recorder thread while
RX_VK_RECORDERis unset.auto_profile()passeshw.deck; the Deck detectionmoved into
deck_found()(same test as before).gpu/vk/vk_recorder.h:g_unset_mode/set_unset_mode()/requested(): the mode used whileRX_VK_RECORDERis unset, taken exactly as the variable would be (refused withRX_VK_PARALLELtoo).gpu/vk/vk_replay.cpprecorder_profile_thread()sets it toTHREAD.gpu/vk/vk_draw.cppprecompile_graphics_none()andprecompile_run():RX_VK_PRECOMPILE_GRAPHICS=nonekeeps no graphics pipeline (nor a program only they need) ready before the title; computes and the mesh fallback's
programs are kept ready as before. The notes stay noted, and no
.builtmarker is written when none were built. Unset:nonewith the Steam Deck profile, elseall. Never in a--prepare-shadersprocess. This is the Longshot form ofhttps://github.com/NotRexed/Hookshot/pull/2814.
gpu/vk/vk_menu.cppapply_auto(): with the profile, Frame generation 2x (unlessRX_VK_FRAMEGEN_MULT) and theVRAM budget row at Auto (a budget the player set stays theirs);
menu_init()readssteam_deck=, whichsave()keeps(no row writes it).
menu_steam_deck()invk_menu.h.tests/test_auto_settings.cpp: the switch's rule, the profile's choice (handheld and docked, and turned onelsewhere), and that the fingerprint with the profile off is the one from before.
tests/test_auto_settings.pycheck_deck_profile(): decided inpick_devicebeforecreate_device, the recorder and precompile parts,apply_auto.game cannot set itself);
docs/development.md:RX_VK_STEAM_DECK,RX_VK_PRECOMPILE_GRAPHICS,steam_deck.Left out on purpose, as Longshot doesn't have them: the low view distances and their rows
(https://github.com/NotRexed/Hookshot/pull/2669,
RX_OOT_*; with them and Render resolution 33% the Deck went to17.0-17.2 fps), game-ordered barriers (https://github.com/NotRexed/Hookshot/pull/2559,
RX_VK_GAME_ORDER, 15.8 fps) andpartial descriptors (https://github.com/NotRexed/Hookshot/pull/2644,
RX_VK_DESC_PARTIAL, 15.6 fps). The game's ownupscaler in Longshot steps 100/75/67/50%, so the profile keeps Minimum's 50%. The uniform-buffer copies on integrated GPUs
(https://github.com/NotRexed/Hookshot/pull/1795, 0 freezes in ~25 starts) are their own PR (
fix/igpu-ub-snapshot); theprofile assumes it.
Flags and settings
RX_VK_STEAM_DECK(new),steam_deck=inlongshot_options.ini(new, no row)RX_VK_PRECOMPILE_GRAPHICS(new)none: no graphics pipeline kept ready before the title;all: every noted onenonewith the Steam Deck profile, elseallRX_VK_RECORDER=thread(existing)RX_VK_PARALLEL(refused, as before)Measurements
Hardware: Steam Deck OLED (AMD Custom GPU 0932), SteamOS 3.9.5 (build 20261023.100, kernel 7.2.7-valve2), RADV Mesa
26.2.0-devel (git-5c65d4fc31)
Scene: Kokiri Forest from the same save, a scripted route (walk, pan, Saria's area), Gaming Mode, 1280x720 at 60 Hz
Method: real (game) and shown frame rate over the route, p95 frame time, peak memory sampled every second; warm caches
Not yet measured: main against this PR alone. No paired run of the two exists yet. What was run on Longshot:
39647e1a, no profile (full graphics precompile)e7ed2f7a+ this profile + #158/#164 + the memory-pressure fix + draw distances + static TLS (the user's Deck build)The first two rows are not a pair: the second has more than this PR, so they only show that the Deck ran out of memory
without
RX_VK_PRECOMPILE_GRAPHICS=noneand not with it. The log of the second has the profile's lines:The single changes that chose the profile were measured on the user's earlier Deck build (Hookshot upstream
6749ba21with Deck patches), each against the same build:
RX_VK_RECORDER=threadRX_VK_VRAM_MB=1200: dips to 6 fps panningTo do before ready: main @
0877184and this PR on the Deck, same route, at least 2 interleaved runs each.Testing
Tested:
python -I -B tests/run_all.pyon Linux (Ubuntu 22.04 container, clang), at0d372bfon main0877184: 490 passed, 6failed, 29 skipped; the same 6 fail on main in the same container, which has the source but not its git history
(
test_display_pointer,test_texture_staging_publication,test_cpp_preflight,test_prof_clock,test_dev_packs,byte-compile), so none is this PR's.
e7ed2f7a, in the user's Deck build (above): a cold and several warm starts,the profile's log lines, no out-of-memory kill, the frame rates above. The user plays on that build.
Not tested:
0877184in the game: the rebase dropped Auto's Output resolution part, which main removed in#172 (the profile never set the output). The Windows build. (The Linux build and a Deck A/B of main, this PR and #227
are running; results here when done.)
defaults), which wants one run on another PC to confirm the log has no
Steam Deck profileline and Auto keeps itssettings.
Needs other testers:
vk: Steam Deck profile: on (a Steam Deck),RX_VK_RECORDER=threadandgraphics pipelines: none kept ready, Auto chooses Frame generation 2x andthe VRAM budget Auto; expecting no OOM kill over 3 starts and the real fps at least main's (request to open)
Screenshots
Nothing on screen beyond the menu rows Auto sets (Frame generation, VRAM budget).
Risk and rollback
On the Deck: the recorder thread and the graphics-pipeline skip are the riskier parts (a recorder fault, or hitches at
first draws).
RX_VK_STEAM_DECK=0turns the whole profile off;RX_VK_RECORDER=offorRX_VK_PRECOMPILE_GRAPHICS=allturn one part off. Off the Deck nothing changes unless a switch is set. Reverting the commit is enough.
Reviewer notes
Start with
auto_settings.h(the rule and its comment, which has the measurements) anddeck_profile_decide()invk_main.cpp. No menu row for the switch yet: a row needs its texts in every language; Auto turning it on covers theDeck. A Deck owner who set Auto before gets the profile's Frame generation 2x and VRAM budget Auto once, as the profile
turns on (
deck_turned: the fingerprint Auto chose for is this hardware's without the profile); after that they are theplayer's when Auto chooses again (another screen or build). Turned off, the VRAM budget's Auto gives way to Minimum's cap.
Issues
See https://github.com/NotRexed/Hookshot/pull/2814 (the precompile part's original).
Checklist
For everyone
type(scope): description).main.python -I -B tests/run_all.pyrun and passing (any failure that also fails onmainis named above).third_party/untouched (a new folder with aPROVENANCE.mdinstead).If applicable
Assisted-by:/Co-Authored-By:trailers.docs/tested-configurations.mdfor each measured run.tests/that reads changed source updated in this PR, not weakened.Code review only: I didn't build or run anything.
The profile is easy to follow. It is decided in
pick_device()before the device exists, so the recorder mode is set before the first vkCmd*. Every renderer part gives way to its own variable, a PC that isn't a Deck keeps its fingerprint, andsteam_deck=survivessave()because it falls into the kept "others" lines. Four things to look at before this leaves WIP:vk_menu.cppapply_auto). The Frame generation row ispreset_exempt, so a player who turns it off stays on Auto. The fingerprint includes the screen size, so on a Deck the next re-choose (docking to another screen, a DLSS or FSR 4 build change, the first start after this update) sets 2x again. That breaks the rule in the comment just above: "Frame generation is the player's".SteamDeck=1on its own turns on the whole profile (deck_found). That variable comes from Steam's Deck-style session, not from the hardware. Other handhelds running a SteamOS-like gamescope session may set it too (ROG Ally or Legion Go on Bazzite or ChimeraOS, Legion Go S with SteamOS). Before this PR it only lowered Auto's tier. Now it also turns on the recorder thread and Frame generation 2x, drops the graphics precompile, and logs "a Steam Deck". Both real Decks already match ondeviceName, LCD (Custom GPU 0405) and OLED (Custom GPU 0932). Device IDs 0x1002:0x163F and 0x1002:0x1435 would be sturdier still.RX_VK_PRECOMPILE_GRAPHICS=nonenever runs in load-at-create mode (vk_draw.cppprecompile_run). Theprograms_load_at_create()branch returns beforegraphics_noneis checked.load_default.hpicks create wherever the GPU has native mesh shaders. In that mode a first start still warms every note into the pipeline cache before the title (precompile_warm, bounded and let go), and the "none kept ready" line never appears. Your Deck log shows the start-mode path. Please say whichvk: programs are loaded ...line that run printed, and either cover create mode or say in the docs and comments that the switch only applies in start mode.c.vram_autosetsopts.budget = 0, the same state as "the player set the VRAM budget row". AfterRX_VK_STEAM_DECK=0, the next re-choose hasown_budgettrue and keeps Auto.Smaller: with
RX_VK_PARALLELset, the profile's log still says the recorder is "on a thread of its own", butmode_initrefuses it and logsRX_VK_RECORDER=thread refusedalthough the variable is unset.Nothing touches
vk_api.h. The tests that read the source are updated, not weakened. The Measured box is rightly unticked: main and this PR still need a paired run on the Deck, and the LCD and off-Deck runs are already listed as needing testers.@ -8664,0 +8674,4 @@// ready, pushed a Steam Deck's 16 GB into earlyoom's kills; https://github.com/NotRexed/Hookshot/pull/2814 halved its memory// there (7.5 to 3.8 GB) and its hitches (99 to 12). Each is made at its first draw in play instead, from the driver's own// cache where it has it. The notes stay noted (notes_open_locked, above): play notes none twice)const bool graphics_none = precompile_graphics_none();This is only reached in start mode. The
programs_load_at_create()branch at the top ofprecompile_runreturns first, andload_default.hpicks create wherever the GPU has native mesh shaders. In that mode a first start still runsprecompile_warmover every note (built and let go), and the "none kept ready" line never appears. Whichvk: programs are loaded ...line did the Deck run print? Please either handle create mode or say in the docs and the comment that the switch only applies in start mode.@ -193,0 +197,4 @@/// (longshot) A Steam Deck, LCD or OLED: its APU as RADV names it, or SteamOS's own flagbool deck_found(const VkPhysicalDeviceProperties& p) {const char* deck = getenv("SteamDeck");return (deck && atoi(deck) == 1) || strstr(p.deviceName, "Custom GPU 0405") || strstr(p.deviceName, "Custom GPU 0932");SteamDeck=1comes from Steam's Deck-style session, not from the hardware. SteamOS-like gamescope sessions on other handhelds (ROG Ally or Legion Go on Bazzite or ChimeraOS, Legion Go S with SteamOS) may set it too. Before this PR it only lowered Auto's tier. Now it also turns on the recorder thread and Frame generation 2x, skips the graphics precompile, and logs "a Steam Deck". Both Deck models already match ondeviceNamehere (0405 LCD, 0932 OLED), so dropping the variable costs nothing on a real Deck. Matching vendor 0x1002 with device 0x163F or 0x1435 would be sturdier than the name.@ -193,0 +216,4 @@}const char* recorder = "the recorder as RX_VK_RECORDER says";if (!getenv("RX_VK_RECORDER"))recorder = recorder_profile_thread() ? "the command recorder on a thread of its own (RX_VK_RECORDER=thread)"Nit: with
RX_VK_PARALLELset,recorder_profile_thread()still returns true, so this line logs "on a thread of its own".mode_initthen refuses it and logsRX_VK_RECORDER=thread refused, although the variable is unset.@ -938,2 +939,3 @@if (own_budget || c.vram_auto) {opts.budget = 0;opts.vram_budget = vram_budget;opts.vram_budget = own_budget ? vram_budget : 0;c.vram_autoleavesopts.budget = 0, the same state the player gets by setting the VRAM budget row. If the profile is later turned off (RX_VK_STEAM_DECK=0), the re-choose seesown_budgettrue and keeps Auto, not Minimum's 2.2 GB. Turning the profile off doesn't fully undo it. That's probably fine on a 16 GB Deck, but the README says=0turns it off.@ -940,2 +942,4 @@vram_budget_sync();}// (Frame generation is the player's, as with every preset, but for the Steam Deck profile's 2x; RX_VK_FRAMEGEN_MULT still wins)if (c.framegen >= 0 && !getenv("RX_VK_FRAMEGEN_MULT")) opts.framegen = c.framegen;Frame generation is
preset_exempt, so a player who turns it off stays on Auto. This line then sets 2x again every time Auto chooses again. The fingerprint includes the screen size, so on a Deck that means docking to another screen, a DLSS or FSR 4 build change, or the first start after this update. That contradicts the comment above ("Frame generation is the player's"). Could it apply only on a first start or when Auto is picked on the row (!keep_own), the way the VRAM budget keeps the player's own?e26a8581300d372bf121Thanks, all five were right. Fixed in
0d372bf(on top of the profile commit, rebased onto main0877184):!keep_own), or the profile turned on since Auto last chose (auto_settings.hdeck_turned:auto_foris this hardware's fingerprint with the profile flipped). So an existing Deck install gets them once with this update. After that, when Auto chooses again for another screen or build, they are the player's. Unit checks are intest_auto_settings.cpp; the source checks intest_auto_settings.pynow ask for the gate.SteamDeck=1:deck_foundnow matches the APU only: vendor 0x1002 with 0x163F or 0x1435, or RADV'sCustom GPU 0405/0932.SteamDeck=1still lowers Auto's tier as before (handheld_foundfeedsHardware::handheld), but no longer turns on the profile. The test fails ifdeck_foundreadsSteamDeckagain.vk: programs are loaded before the title (start): this GPU draws mesh programs through the compute fallback, so it was start mode. In create mode the switch is now left as it is, and it logsRX_VK_PRECOMPILE_GRAPHICS=none ... left as it is: programs load at their create calls, which keep no graphics pipeline ready before the title anyway. The warm builds each pipeline and lets it go, so memory stays bounded there. The comment anddocs/development.mdsay it only applies in start mode.deck_settings(...).restore_cap), a VRAM budget at Auto is no longer taken for the player's, so Minimum's cap comes back. A budget value the player picked stays. Frame generation stays as it is, since no preset sets that row; the README says so.RX_VK_PARALLEL:recorder_profile_thread()no longer asks for the thread whenRX_VK_PARALLELis 2 or more, so there's no refusal of an unset variable. The profile line says "the recorder off, as RX_VK_PARALLEL records on several threads".Tests: Linux suite 490 passed, 6 failed. The same 6 fail on main
0877184in the same container (one is new from #147:test_dev_packsneeds a git checkout). Not yet run in the game: the Deck A/B (main0877184against this PR, with #227 on top) is building now and will run on these commits.View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.