fix(vk): opt-in RGBA16F route for PsychoV35 HDR with AMD FSR 4 #247
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!247
Loading…
Reference in a new issue
No description provided.
Delete branch "MustardTiger/Longshot:fix/psychov35-fsr4"
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
With HDR on Native, the HDR tone mapper on PsychoV35 and AMD FSR 4 (or its Native AA) as the upscaler, FSR 4 silently drops out and the game's own upscale runs instead: jagged edges, a heavily oversharpened picture, and the Sharpness row doing nothing (#226). #108 added an opt-in RGBA16F route for DLSS/DLAA only; this is the FSR 4 follow-up its review asked for.
What changed
takes_psychov35()ingpu/vk/upscaler_rows.h: DLSS/DLAA and FSR 4 / its Native AA, the engines the frame chain gives linear light in an HDR context. FSR 3.1 (its own packed inputs), the game's network and Off are unchanged.temporal_scene_format()andtemporal_upscale()'spsychoguard ingpu/vk/vk_temporal.cppuse it in place ofur::is_dlss(). The route waits for FSR 4's HDR context as it does for DLSS's HDR feature, and the route log line names the engine:vk: PsychoV35 FSR 4: RGBA16F linear route. The DLSS line is unchanged.tests/test_psychov35_dlss.pychecks the gate over every upscaler (DLSS, DLAA, FSR 4, FSR 4 AA in; game, FSR 3.1, Off out) and the history log for FSR 4. It fails on main and passes here.docs/temporal-history.md,docs/development.mdand the README's HDR tone mapper row.No new shader, no change to #108's RGBA16F output pass, view binding or history reset: FSR 4 goes through them as DLSS does.
Flags and settings
RX_VK_PSYCHOV35_DLSS=1(existing, from #108)RX_VK_PSYCHOV35_RGBA16F) is left to the maintainersRX_VK_FSR4_HDR=0prevents the route for FSR 4, asRX_VK_DLSS_HDR=0does for DLSS: it needs the HDR context.Measurements
main:
9eae480e(2026-11-13)this PR: fix/psychov35-fsr4 @ ea988050 (the first pair of runs at 1ba927d0, the same change on #108's branch before it merged)
Rebased since: the branch is now
acd851c8(the code) ande72f4077(the tested-configurations rows) on main2e1e2e8c. Its three new commits (#107's clustered-light shadow maps, #55's render scale across upscaler switches, #159's docs) leave the PsychoV35 gate and the route alone:git range-diffshows both commits unchanged, the renderer builds atacd851c8, and the checks below pass there, so the numbers still hold.Baseline: this PR @ ea988050,
RX_VK_PSYCHOV35_DLSS=0(main's route). With the switch off the chain takes exactly main's route (main's gate leaves FSR 4 out with the switch on or off), so it isolates this change on one build.Hardware: AMD Radeon RX 9070 XT 16 GB, Adrenalin 26.10.2, Ryzen 7 9800X3D, 32 GB, Windows 11 Pro 26300
Scene: Forest Temple, indoors, standing; 2560x1440 output, fullscreen, HDR10 (paper white 200, peak 700 nits), HDR Native, HDR tone mapper PsychoV35, Upscaler AMD FSR 4 Native AA (FSR 4.1.2), Sharpness 50, no frame generation
Method: the game folder of main
fdc2840bbuilt with FSR 4 (make_game.py --fsr); the renderer built alone (RX_VK_ONLY, llvm-mingw,LONGSHOT_FSR=1) from this branch and put in its place. A cold start per run (load the save, about a minute standing), runs interleaved ON / OFF / OFF / ON / Vanilla. Counts from the 600-framestatus:lines oflongshot_log.txt; only complete intervals after loading are counted (the first interval holds the title screen and the load).How to reproduce: Native HDR, HDR tone mapper PsychoV35, Upscaler AMD FSR 4 Native AA; start once with
RX_VK_PSYCHOV35_DLSS=1and once with it unset; checkPsychoV35 FSR 4: RGBA16F linear routeand theAMD FSR 4 Native AA: N frames drawncounts rather than the menu row.RX_VK_PSYCHOV35_DLSS=0(main's route)RX_VK_PSYCHOV35_DLSS=1the game's upscale runs (no scene camera this frame)every time)The long ON run is the first one, where the tone mapper and HDR mode were also switched during play. Its eight intervals under 600: 595 to 599 around those switches (each logs
the game's upscale runs (unexpected scene colour format)for the one to three frames the game takes to change its scene buffer, then the new route); 532 and 522 after moving to another area, following a history reset and a burst of new textures, with no new fallback reason logged; and 351 in one interval whose cause the log doesn't show. One of its complete intervals ran FSR 4 (not Native AA) at 600 of 600. This is a correctness fix; no frame rate is claimed.Testing
Tested: On the RX 9070 XT above, in the game: PsychoV35 with FSR 4 Native AA and the switch on keeps FSR 4 drawing: smooth edges, the Sharpness row works again (0 / 50 / 100 checked by eye), and HDR brightness looks right on an HDR10 display (white 200, peak 700 nits). With the switch off the jagged, oversharpened picture comes back. During play: the tone mapper switched Vanilla / PsychoV35 and HDR Native / Extended / Native several times; each switch logs the route change (
packed/RGBA16F linear) and FSR 4 is back at 600/600 within 1 to 3 frames. Vanilla with the switch on is unchanged (600/600, no PsychoV35 line). Game-free checks:python -I -B tests/run_all.py --jobs 12on Windows (the launcher's Python and llvm-mingw, glslang 16.5.0): 489 passed, 5 failed, 33 skipped. Three of the failures fail the same way on main9eae480ehere (run at ea988050; the touched checks again atacd851c8, all passing, withtest_upscaler_round_tripfrom #55) (test_check_third_party,test_gpu_hang_watch,test_release); the other two (test_dlss_sdk_cache,test_portable_builder_home) failed only under the 12-way load and pass run alone, and pass on main too. The checks this PR touches pass:test_psychov35_dlss(fails on main),test_temporal_history,test_reactive_mask,test_upscaler_rows,test_hdr_encode,test_dlss_actor.Not tested: FSR 4 (not Native AA) at a lower render size beyond one 600-frame interval of the first run; DLSS/DLAA on this branch (no NVIDIA card here; the DLSS path is #108's unchanged code, and its test still covers it); Linux (Mesa RADV), including a Linux build of the renderer: there is no Linux system here (the change is portable C++ with no platform code, as #108's was); other RDNA 4 cards and drivers.
Needs other testers:
RX_VK_PSYCHOV35_DLSS=1, the renderer builds, and expectingPsychoV35 FSR 4: RGBA16F linear routeand 600/600AMD FSR 4 ... frames drawn(T-NV7J; needs thetesting wantedlabel to be claimed)Screenshots
Nothing captured: an HDR10 (PQ) screenshot does not show the difference faithfully on an SDR viewer. The difference is the one #226 describes (jagged and oversharpened with the switch off, FSR 4's anti-aliasing with it on); the log counts above show which upscaler drew each frame.
Risk and rollback
Only runs with
RX_VK_PSYCHOV35_DLSS=1, Native HDR, PsychoV35 and FSR 4 or its Native AA: nobody gets it by default. Unsetting the switch restores main's behaviour;RX_VK_FSR4_HDR=0also keeps FSR 4 off this route. Reverting the commit is enough.Reviewer notes
The change is the gate:
takes_psychov35()and its two uses invk_temporal.cpp. Everything the route needs past the gate (the RGBA16F views, the typed output pass, the HDR decode, the history reset) is #108's and engine-neutral; FSR 4's HDR context comes from https://github.com/NotRexed/Hookshot/pull/2342.Left for a follow-up on purpose: the menu saying that PsychoV35 makes the upscaler fall back while the switch is off (#226's "silently"), which needs the help text in every menu language; and whether the route should become the default. Separately, one run logged
program 946589f514448dc6: scene tone map left as the game's: a fit's channel not found by the luminancein one area: PsychoV35 itself (https://github.com/NotRexed/Hookshot/pull/2481) not finding the game's fit in one shader, unrelated to this change.Issues
Fixes #226
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.mainand passing here.tests/that reads changed source updated in this PR, not weakened.Test request T-NV7J is open for a Linux RDNA 4 run (and a Linux build of the renderer); it can be claimed once the testing wanted label is on this PR. Measured and tested on an RX 9070 XT (Windows, Adrenalin 26.10.2) as in the description; rebased onto main
2e1e2e8cwith both commits unchanged.WIP: fix(vk): opt-in RGBA16F route for PsychoV35 HDR with AMD FSR 4to fix(vk): opt-in RGBA16F route for PsychoV35 HDR with AMD FSR 4e72f407782f4033932f9I rebased this onto main @
fbd42024to clear the conflict; it's pushed asf4033932, with your two commits unchanged otherwise. The code applied cleanly. The only conflict wasdocs/tested-configurations.md, where main had added a row at the same place as your five FSR 4 rows. I kept all of them. I didn't run the test suite. Forgejo now shows the PR as mergeable.Thanks for the rebase. Checked at
f4033932(mainfbd42024): the fix commit is unchanged (git range-diff), and all five tested-configurations rows are kept. Full suite on Windows: 493 passed, 3 failed, 33 skipped; the three (test_check_third_party, test_gpu_hang_watch, test_release) fail the same way on main here. The renderer builds with llvm-mingw and FSR 4, 0 failures.Read through end to end against main
fbd42024(the fork point, so "Rebased on current main" holds). Approving: the change is small and correct, and the evidence backs it.What's good
temporal_scene_format()is what the camera (temporal_note_scene_draw), actor motion (skin_check), the reactive copy (reactive_scene_draw),temporal_colour_pair()and bothtemporal_stand_inandtemporal_rejected_network(the RGBA16F sample and storage views) all ask. So switching it fromis_dlsstotakes_psychov35moves every consumer together, and thepsychoguard intemporal_upscalefollows. I found no format decision left that still assumes DLSS.reactive_scene_drawzeroes its size while FSR 4 draws on the chain, so the wider gate copies nothing extra.FFX_UPSCALE_ENABLE_HIGH_DYNAMIC_RANGE | AUTO_EXPOSURE,preExposureis 1 and no sRGB flag is set. The only difference is that the typed RGBA16F output pass does not square again, which is engine-neutral.hdr()keys offpresent_native_pqandRX_VK_FSR4_HDR, so "waits for the native HDR FSR 4 context" is the right gate.s.drawing, and the route log now names the engine.scene_tonemap_on().logfstub matches the new signature.acd851c8that the rebase note cites.Must change
Nothing.
Should change
fsr3_inputs::note_scene_drawaccepts only a B10G11R11 scene, so with the RGBA16F scenefsr3_inputs::preparehas no camera andfsr3fi::generatemakes no frames. This is not a regression: on main, PsychoV35 had no FI inputs either. But FSR 4 with FSR 3.1 frame interpolation is the pairing AMD players are likely to use, and the docs only say "there is no NVIDIA frame generation to check". One line in temporal-history.md and in the Flags row would set expectations: FSR 3.1 frame interpolation is not fed on this route. Widening its gate could be a follow-up.RX_VK_PSYCHOV35_DLSSnow also covers FSR 4. The description already leaves renaming it to maintainers. A follow-up could add a neutral alias (e.g.RX_VK_PSYCHOV35_RGBA16F) and keep the old name working.Not checked: I ran no build or tests (code review only), and I could not inspect the tested commits ea988050 and 1ba927d0 locally. For those I relied on the author's range-diff note, plus the empty interdiff I did confirm for
acd851c8.@ -134,3 +133,1 @@The successful upscale supplies NVIDIA frame generation with depth, camera and actor motion as usual. It requiresthe native-HDR DLSS feature (`RX_VK_DLSS_HDR=0` prevents this path). FSR and TAAU are not enabled by this switch.See [the format and history rules](temporal-history.md#psychov35-with-dlssdlaa-opt-in) for validation and shader regeneration.`RX_VK_PSYCHOV35_DLSS=1` enables the experimental RGBA16F scene path for DLSS/DLAA and AMD FSR 4 (and its Native AA)Nit, fine for this PR: the switch's name now says DLSS but covers FSR 4 too. A follow-up could add a neutral alias (e.g.
RX_VK_PSYCHOV35_RGBA16F) and keep this name working.@ -54,1 +54,3 @@brightness on an HDR display, edges, actor motion, history resets and validation errors. A transitional packed-formatbrightness on an HDR display, edges, actor motion, history resets and validation errors. With FSR 4 or its Native AA, the logline is `vk: PsychoV35 FSR 4: RGBA16F linear route`, the status lines count `AMD FSR 4 ...: N frames drawn`, and no`the game's upscale runs` follows; there is no NVIDIA frame generation to check. A transitional packed-formatThere is no NVIDIA frame generation here, but AMD FSR 3.1 frame interpolation is the likely pairing with FSR 4, and it gets no inputs on this route.
fsr3_inputs::note_scene_draw(vk_fsr3_inputs.cpp) accepts only a B10G11R11 scene, sopreparefinds no camera andfsr3fi::generatemakes no frames. This isn't a regression (main's PsychoV35 is the same), but a sentence saying FSR 3.1 frame interpolation is not fed on this route would save someone a confused test.