Skip to content

feat(vk): AMD Anti-Lag 2 with frame generation - #2262

Merged
hckrman101 merged 1 commit into
NotRexed:mainfrom
liska1304:feat/antilag-framegen
Nov 12, 2026
Merged

hckrman101 merged 1 commit into
NotRexed:mainfrom
liska1304:feat/antilag-framegen

Conversation

@liska1304

@liska1304 liska1304 commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

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, a thread_local like reflex_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, like reflex_frame().
  • vk_framegen.cpp: each set keeps antilag_id = antilag_frame() next to reflex_id in framegen_record, and display_main sets antilag_present_frame with the same choice as reflex_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 in PendingPresent::antilag_frame, and present_commit restores it around present_out, as it does for reflex_frame. present_out still gives the stage right before vkQueuePresentKHR (and before a hand-off to the present thread). Without frame generation nothing changes.
  • A frame from before the row was last turned on gets no present stage (its input stage went to the driver's earlier run).
  • The row's hint now reads like the Reflex row's ("With frame generation too: it paces the game's frames."), and its 13 translations take the Reflex row's second sentence in each language. The README line says it works with frame generation.

How it was tested

  • tests/test_antilag.py compiles the real functions against the Vulkan headers with a recording vkAntiLagUpdateAMD. 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 cover framegen_record, both present paths in display_main, the deferred present in present_image and present_commit. It fails on main.
  • The present-semaphores and present-source-crop fixtures got the new field and stub.
  • python -I -B tests/run_all.py: 448 passed, 37 skipped, 10 failed on Windows. The same 10 fail on unmodified main on this PC (builder, portable, updater and a few local-environment checks), not from this change.
  • vk_antilag.cpp, vk_framegen.cpp, vk_replay.cpp and vk_menu.cpp compile with llvm-mingw against Vulkan-Headers v1.4.345.
  • In the game, the rebased version (main + this PR, built from source): RX 9070 XT, Adrenalin 26.10.2, Windows 11, Ryzen 9 3900X, 2560x1440 fullscreen, FSR 3.1, frame generation Auto (144 Hz, present ahead). About 10 minutes with the row switched off and on several times: Anti-Lag 2 stays on with frame generation (vk: AMD Anti-Lag 2: on, no off after 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.
  • Before the rebase (v2.1.0 base, the present stage then at each set's first present), the same setup ran about 5 minutes: 41,769 frames on time, 0 late, no hang.

Not tested

  • Latency in milliseconds: no PresentMon or AMD overlay numbers yet, only the Breakdown's Pacing above.
  • Fixed 2x/3x and up were not run separately (the same display-thread path as Auto).
  • DLSS frame generation (NVIDIA only), Linux / RADV, other AMD GPUs.

Copilot AI balanced review requested due to automatic review settings November 11, 2026 12:26

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

@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.

Needs a rebase after #2118: it conflicts in vk_framegen.cpp/.h, vk_replay.cpp, vk_menu.cpp, test_antilag.py, test_present_semaphores.py and tested-configurations.md, around PendingPresent/present_out. The change is still needed (main still has wanted() && !framegen_on() at vk_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.

@liska1304
liska1304 force-pushed the feat/antilag-framegen branch from 844476a to 8d16615 Compare November 12, 2026 14:24
Frame generation no longer turns Anti-Lag 2 off. As NVIDIA Reflex's
markers with frame generation (NotRexed#2118), each set keeps the game frame it
was made at (antilag_frame), and only the set's game frame gives that
frame's present stage (antilag_present_frame, kept by a deferred present
until its commit); the made frames' presents give none. Without frame
generation nothing changes. The row's hint takes the Reflex row's
wording and translations.
@liska1304

Copy link
Copy Markdown
Contributor Author

Rebased onto main after #2118 (now one commit, 8d16615). The present stage follows #2118's Reflex path: antilag_present_frame works like reflex_present_frame, each set keeps antilag_id next to reflex_id, and only the set's game frame gives the present stage (also through present ahead's PendingPresent). So the earlier note about k == 0 being 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).

  • Anti-Lag 2 stays on with frame generation (vk: AMD Anti-Lag 2: on, no off after it); switching the row off and on took effect at once each time.
  • About 10 minutes: 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 frame to frame): the driver's wait at the input stage now happens with frame generation too.

No latency number in milliseconds yet (no PresentMon run). The description is updated.

@hckrman101
hckrman101 merged commit e2057b2 into NotRexed:main Nov 12, 2026
14 checks passed
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.

3 participants