feat(vk): AMD Anti-Lag 2 with frame generation #2262
Closed
liska1304 wants to merge 2 commits from
liska1304/feat/antilag-framegen into main
pull from: liska1304/feat/antilag-framegen
merge into: MagicBeanDev:main
MagicBeanDev:main
MagicBeanDev:github/pr/2893
MagicBeanDev:github/pr/2890
MagicBeanDev:github/pr/2887
MagicBeanDev:github/pr/2878
MagicBeanDev:github/pr/2875
MagicBeanDev:github/pr/2874
MagicBeanDev:github/pr/2873
MagicBeanDev:github/pr/2871
MagicBeanDev:github/pr/2756
MagicBeanDev:github/pr/2897
MagicBeanDev:github/pr/2896
MagicBeanDev:github/pr/2718
MagicBeanDev:github/pr/2842
MagicBeanDev:github/pr/2691
MagicBeanDev:github/pr/2895
MagicBeanDev:github/pr/2743
MagicBeanDev:github/pr/2765
MagicBeanDev:github/pr/2708
MagicBeanDev:github/pr/2732
MagicBeanDev:github/pr/2755
MagicBeanDev:github/pr/2817
MagicBeanDev:github/pr/2876
MagicBeanDev:release-please--branches--main
MagicBeanDev:release-please--branches--main--release-notes
MagicBeanDev:github/pr/2778
MagicBeanDev:github/pr/2888
MagicBeanDev:github/pr/2866
MagicBeanDev:github/pr/2877
MagicBeanDev:github/pr/2800
MagicBeanDev:github/pr/2829
MagicBeanDev:github/pr/2850
MagicBeanDev:github/pr/2889
MagicBeanDev:github/pr/2882
MagicBeanDev:github/pr/2860
MagicBeanDev:github/pr/2849
MagicBeanDev:github/pr/2886
MagicBeanDev:github/pr/2794
MagicBeanDev:github/pr/2840
MagicBeanDev:github/pr/2830
MagicBeanDev:github/pr/2834
MagicBeanDev:github/pr/2719
MagicBeanDev:github/pr/2742
MagicBeanDev:github/pr/2741
MagicBeanDev:github/pr/2820
MagicBeanDev:github/pr/2771
MagicBeanDev:github/pr/2859
MagicBeanDev:github/pr/2858
MagicBeanDev:github/pr/2823
MagicBeanDev:github/pr/2757
MagicBeanDev:github/pr/2769
MagicBeanDev:github/pr/2844
MagicBeanDev:github/pr/2843
MagicBeanDev:github/pr/2673
MagicBeanDev:github/pr/2855
MagicBeanDev:github/pr/2739
MagicBeanDev:github/pr/2807
MagicBeanDev:github/pr/2851
MagicBeanDev:github/pr/2789
MagicBeanDev:github/pr/2814
MagicBeanDev:github/pr/2818
MagicBeanDev:github/pr/2774
MagicBeanDev:github/pr/2772
MagicBeanDev:github/pr/2750
MagicBeanDev:github/pr/2786
MagicBeanDev:github/pr/2785
MagicBeanDev:github/pr/2782
MagicBeanDev:github/pr/2779
MagicBeanDev:github/pr/2775
MagicBeanDev:github/pr/2706
MagicBeanDev:github/pr/2766
MagicBeanDev:github/pr/2747
MagicBeanDev:github/pr/2759
MagicBeanDev:github/pr/2745
MagicBeanDev:github/pr/2740
MagicBeanDev:github/pr/2733
MagicBeanDev:github/pr/2725
MagicBeanDev:github/pr/2727
MagicBeanDev:github/pr/2728
MagicBeanDev:github/pr/2729
MagicBeanDev:github/pr/2721
MagicBeanDev:github/pr/2722
MagicBeanDev:github/pr/2715
MagicBeanDev:github/pr/2714
MagicBeanDev:github/pr/2707
MagicBeanDev:github/pr/2703
MagicBeanDev:github/pr/2705
MagicBeanDev:github/pr/2697
MagicBeanDev:github/pr/2679
MagicBeanDev:github/pr/2676
MagicBeanDev:github/pr/2669
MagicBeanDev:docs/what-hookshot-connects-to
MagicBeanDev:fix/hdr-encode-test
MagicBeanDev:fix/main-tests
MagicBeanDev:fix/play-cache-files
MagicBeanDev:perf/game-barrier-order
MagicBeanDev:feat/heap-lazy
MagicBeanDev:feat/texture-detail
MagicBeanDev:perf/astc-opaque-bc1
MagicBeanDev:fix/fs-dir-lock
MagicBeanDev:fix/framegen-native-kept
MagicBeanDev:fix/update-no-downgrade
MagicBeanDev:port/k10-gcn-f16-mirrors
MagicBeanDev:fix/framegen-mult-cap
MagicBeanDev:chore/release-as-2.1.1
MagicBeanDev:perf/pcache-load-switch
MagicBeanDev:fix/prepare-progress-log
MagicBeanDev:fix/update-closes-stale-game
MagicBeanDev:fix/hdr-scaled-sampled
MagicBeanDev:perf/first-start-preset
MagicBeanDev:perf/zero-upload
MagicBeanDev:fix/vcore-escape-2s
MagicBeanDev:test/fake-gh-atomic
MagicBeanDev:perf/batch-idle-flush
MagicBeanDev:test/finish-stub-a
MagicBeanDev:chore/record-2.1.0
MagicBeanDev:fix/main-red-tests
MagicBeanDev:fix/test-display-startup-driver-cache
MagicBeanDev:feat/mesh-shader-mode
MagicBeanDev:fix/menu-probes-at-startup
MagicBeanDev:fix/fps-counter-classic
MagicBeanDev:fix/test-log-batch-flake
MagicBeanDev:fix/narrow-screen-hud
MagicBeanDev:ci/prereleases
MagicBeanDev:release/v2.0.15
MagicBeanDev:hotfix/v2.0.15
MagicBeanDev:fix/present-ahead-replay
MagicBeanDev:release/v2.0.13
MagicBeanDev:fix/bracket-paths-2013
MagicBeanDev:fix/hdr-overlays-2013
MagicBeanDev:fix/fps-counter-scale-2013
MagicBeanDev:fix/stick-seen-at-rest
MagicBeanDev:hotfix/menu-fits-screen
MagicBeanDev:fix/f1-always-opens-menu
MagicBeanDev:fix/ini-keep-lines
MagicBeanDev:claude/cranky-pike-7cf247
MagicBeanDev:perf/texture-answer-flat
MagicBeanDev:fix/stamp-test-modules
MagicBeanDev:fix/appimage-fonts
MagicBeanDev:fix/display-awake
MagicBeanDev:release/v2.0.12
MagicBeanDev:fix/discord-windows
MagicBeanDev:rc/v2.0.12-tests
MagicBeanDev:fix/dlss-keep-feature
MagicBeanDev:rc/v2.0.12
MagicBeanDev:button-icons
No reviewers
Labels
Clear labels
accessibility
Barrier affecting people with disabilities
autorelease: pending
autorelease: tagged
bug
Something isn't working
dependencies
Pull requests that update a dependency file
documentation
Improvements or additions to documentation
duplicate
This issue or pull request already exists
enhancement
New feature or request
github_actions
Pull requests that update GitHub Actions code
good first issue
Good for newcomers
help wanted
Extra attention is needed
invalid
This doesn't seem right
question
Further information is requested
testing wanted
wontfix
This will not be worked on
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
Milestone
Clear milestone
No items
No milestone
Assignees
Clear assignees
No assignees
2 participants
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2262
Loading…
Reference in a new issue
No description provided.
Delete branch "liska1304/feat/antilag-framegen"
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?
With frame generation on, AMD Anti-Lag 2 turned itself off, so on AMD GPUs the input delay that frame generation adds got no help: the menu row said On, but the log said
vk: AMD Anti-Lag 2: off. This keeps it on with frame generation.Rebased onto main after #2118, and now built the way #2118 gives NVIDIA Reflex its present markers with frame generation.
What changed
vk_antilag.cpp: frame generation no longer turns Anti-Lag 2 off. The input stage is where it was (after the game thread's present, with Reflex's sleep). The present stage now gives the index of the frame the present shows, not the latest one: with frame generation, the display thread shows a frame after the game thread has already given the next frame's input stage.vk_antilag.h:antilag_present_frame, athread_locallikereflex_present_frame:kAntiLagThisFrame(the default) is the frame being made, as without frame generation; the display thread sets a set's game frame for its game frame and 0 for a made frame.antilag_frame()is the frame whose input stage was given last, likereflex_frame().vk_framegen.cpp: each set keepsantilag_id = antilag_frame()next toreflex_idinframegen_record, anddisplay_mainsetsantilag_present_framewith the same choice asreflex_present_frame(timed ? game[k] : k == made) in both the direct path and present ahead. So only the set's game frame gives the present stage, at the same present that gets Reflex's present id and markers; the made frames before it give none. Auto, fixed 2x and up, FSR 3.1 interpolation and DLSS frame generation all go through this path.vk_replay.cpp/vk_framegen.h: a deferred present keeps it inPendingPresent::antilag_frame, andpresent_commitrestores it aroundpresent_out, as it does forreflex_frame.present_outstill gives the stage right beforevkQueuePresentKHR(and before a hand-off to the present thread). Without frame generation nothing changes.How it was tested
tests/test_antilag.pycompiles the real functions against the Vulkan headers with a recordingvkAntiLagUpdateAMD. It checks the stages without frame generation, a game frame's present stage given after the next frame's input stage with its own index, nothing for a made frame (0), nothing for a frame from before the row was turned on again, and Off told once. Its wiring checks coverframegen_record, both present paths indisplay_main, the deferred present inpresent_imageandpresent_commit. It fails onmain.python -I -B tests/run_all.py: 448 passed, 37 skipped, 10 failed on Windows. The same 10 fail on unmodifiedmainon this PC (builder, portable, updater and a few local-environment checks), not from this change.vk_antilag.cpp,vk_framegen.cpp,vk_replay.cppandvk_menu.cppcompile with llvm-mingw against Vulkan-Headers v1.4.345.vk: AMD Anti-Lag 2: on, nooffafter it), each switch took effect at once, 0 late frames (28,652 shown on time in the longest stretch with it on), about 58-59 fps with it on and off, no hang, no failed submit. The FPS counter's Breakdown shows more "Pacing" with the row on than off at the same frame time (it varies from frame to frame): the driver's wait at the input stage now happens with frame generation too.Not tested
Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.
Looks good to me. I went through the stage bookkeeping against the call sites:
framegen_queue, whichdo_presentcalls before itsReflexSleepdestructor gives the next input stage, so a set carries the frame it was made from, not the next one.do_present's own present does the same throughantilag_frame(), so without frame generation the order and the indices are what main sends.PendingPresentand gives the stage inpresent_outfrompresent_commit, still right beforevkQueuePresentKHR. If the commit fails because the swapchain was made again, that frame simply gets no present stage, which is fine.sincecorrectly drops a set stamped before the row was turned off and on again. All calls stay undera.mu, and nothing takesdev.present_muwhile holding it, so the display thread giving the present stage doesn't add a lock-order problem.display_main, so it's covered without its own path, as the description says.framegen_oncases because the gate is gone, and adds the out-of-order present, the made frame's 0, and the re-enable case, plus wiring checks for both present paths. That's an update, not a weakening. The four source checks only follow the new signatures.Notes, none blocking:
k == 0is often a made frame (game[0]false), so the present stage marks the first interpolated frame toward that game frame, not the game frame itself. The README line says so, and since the Vulkan extension has no frame-generation rule (unlike the DX12 SDK's frame-type calls), I think that's a fair choice. A PresentMon or AMD overlay latency number with the row off and on would show whether the driver's wait still helps when the present comes after the next input stage. Worth a follow-up if you have a minute.Job,PendingPresent, bothpresent_imagecalls indisplay_main,present_out/present_commit, andtest_present_ahead.py/test_present_semaphores.py. Whichever lands second needs a small rebase. Nothing conflicts in what they do.CI is green apart from the AppImage job, which is still pending.
@ -389,7 +389,8 @@ HDR peak brightness show only while HDR is on.detail. `RX_VK_UPSCALE_LOD_BIAS=0` turns it off.Nit: "Unlike Reflex" is true on main today, but it goes stale if #2118 lands (Reflex with DLSS frame generation). Something like "It works with Frame generation too: ..." would avoid that.
@ -389,7 +389,8 @@ HDR peak brightness show only while HDR is on.detail. `RX_VK_UPSCALE_LOD_BIAS=0` turns it off.Done, thanks!
Still good. The only change since my last review is the README line, which now reads "It works with Frame generation too" without the Reflex comparison, so it holds whether or not #2118 lands. The code is unchanged from
a603d66, so my earlier notes still apply as notes, not blockers: a PresentMon or AMD overlay latency comparison would be nice to have, and whichever of this and #2118 merges second will need a rebase onPendingPresent/present_outand the shared tests.CI: everything passes except
appimage, which timed out on a network fetch. Main's Linux AppImage run fails the same way right now, so that is not from this PR.Needs a rebase after #2118: it conflicts in
vk_framegen.cpp/.h,vk_replay.cpp,vk_menu.cpp,test_antilag.py,test_present_semaphores.pyandtested-configurations.md, aroundPendingPresent/present_out. The change is still needed (main still haswanted() && !framegen_on()atvk_antilag.cpp:58). After the rebase, the present-stage index needs re-checking through #2118's present path. A latency measurement would also show the driver's wait helps when the present comes after the next frame's input stage.github-sync referenced this pull request2026-11-12 16:54:48 +00:00
Rebased onto main after #2118 (now one commit, 8d16615). The present stage follows #2118's Reflex path:
antilag_present_frameworks likereflex_present_frame, each set keepsantilag_idnext toreflex_id, and only the set's game frame gives the present stage (also through present ahead'sPendingPresent). So the earlier note aboutk == 0being a made frame no longer applies.Retested in the game with the rebased build (main + this PR, built from source): RX 9070 XT, Adrenalin 26.10.2, Windows 11, 2560x1440 fullscreen, FSR 3.1, frame generation Auto (144 Hz, present ahead).
vk: AMD Anti-Lag 2: on, nooffafter it); switching the row off and on took effect at once each time.No latency number in milliseconds yet (no PresentMon run). The description is updated.
Still good after the rebase onto #2118. I went through it again against the new present path:
antilag_id = antilag_frame()next toreflex_idinframegen_record, which runs insidedo_presentbefore theReflexSleepdestructor gives the next input stage, so the id is the frame the set was made from.display_mainpicks the frame the same way as Reflex in both the direct path and present ahead (timed ? game[k] : k == made), so only the set's game frame gives the present stage. Made frames give 0. That settles my earlierk == 0note.PendingPresent::antilag_frame, andpresent_commitputs it back aroundpresent_outand restores it afterwards, the same way it handlesreflex_present_frame. The stage still goes out beforevkQueuePresentKHRand before a hand-off to the present thread.sincedrops sets stamped before the row was turned off and on again. Without frame generation the thread-local stays atkAntiLagThisFrame, so the order and the indices are what main sends.readyis false, soantilag_frame()returns 0 and the display thread gives nothing. Lock order is unchanged:a.muis only ever taken insidedev.present_mu, never the other way around.On Joey-Magnus's review: the rebase is done and the PR merges cleanly. The present-stage index follows #2118's Reflex path, as checked above. A millisecond latency number is still missing. The in-game run on the RX 9070 XT shows the driver's wait now happens with frame generation (more Pacing in the Breakdown, frame rate unchanged, 0 late frames). The row is opt-in and stays Off by default, so I'm fine landing it without that number. A PresentMon or AMD overlay comparison with the row off and on, with frame generation, would still be welcome as a follow-up.
CI is green on the new head, including the AppImage job.