fix(linux): a Linux build with AMD FSR 4 compiles its own Vulkan back end, from AMD's FSR API headers the AppImage now carries #174
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 milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Longshot!174
Loading…
Reference in a new issue
No description provided.
Delete branch "HumanBean/Longshot:fix/linux-fsr4-own-backend"
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
AMD FSR 4 can't be had on Linux from the launcher or the AppImage: their builds use the AppImage's prebuilt Vulkan back end, compiled without FSR 4, so
--fsrbuilt a game whose Upscaler row never offers it (make_game.py only warned). The game's Linux FSR 4 path (gpu/vk/vk_fsr4.cpp, a Vulkan FSR 4 library throughdlopen) has been there all along. The builds just never compiled it in.What changed
prebuilt_renderer()innx2/build.pysetsLONGSHOT_VK_PREBUILTaside whenLONGSHOT_FSR=1, as it already does for DLSS, so a build with FSR 4 compiles its ownlongshot_vk.sowith AMD's FSR API headers (FSR_HEADERS).main()innx2/make_game.pyworks outfsr_wanted()and sets or clearsLONGSHOT_FSRin one place, beforecheck_cpu()andcheck_windows_sdk(), so aLONGSHOT_FSR=1left in the shell is never taken for this build's.check_cpu(fsr)takes the build's decision rather than reading the environment: a build with FSR 4 compiles its own back end for this PC, so the prebuilt one's AVX2 doesn't stop it. The Linux prerequisite check (check_linux_prerequisites()) looks for the Vulkan headers the compile needs rather than the prebuilt.so, and lists AMD's FSR API headers as missing when a tool tree lacks them, before the game is read. (An AppImage made before this change carries the old make_game.py and build.py, which use the prebuilt back end without FSR 4 as before, so it never gets that far.) The old "prebuilt has no FSR 4" warning now says the back end is compiled here.scripts/linux/appimage.shcarriesthird_party/fidelityfx(AMD's MIT FSR API headers, already in the repository) and its licence underlicenses/fidelityfx/. AMD's runtime is not among them: on Linux it's the player's ownamd_fidelityfx_upscaler_vk.so, as before.scripts/linux/check_player_build.shruns the extracted image'sbuild.py --dry-runa second time withLONGSHOT_FSR=1, and requires the "with AMD FSR 4" line.tests/test_linux_preflight.pygetstest_fsr_overrides_prebuilt_rendererandtest_fsr_without_its_headers_says_so_first;tests/test_cpu_target.pygetstest_fsr_build_without_avx2_goes_onandtest_fsr_in_the_shell_is_not_the_builds, clearsLONGSHOT_FSR, and followscheck_cpu(fsr);tests/test_appimage_player_build.pygetstest_fsr_compiles_the_back_end_from_the_packaged_tree(a dry run from a packaged AppImage tree withLONGSHOT_FSR=1, like the DLSS one);tests/test_appimage_licenses.pycovers the fidelityfx licence and headers;tests/test_make_game_ngx.pyfollows the newprebuilt_renderer()line.The launcher side, its AMD FSR 4 switch on Linux, is MagicBeanDev/LongshotLauncher#24. It passes
--fsronly when the tools carrythird_party/fidelityfx, so it does nothing until a Longshot release with this change is out.Flags and settings
make_game.py --fsr/LONGSHOT_FSR=1(Linux)Measurements
This changes how a Linux build is put together, not frame time, so the game-free checks are the measure for now. A game run on an AMD card is still owed (Testing).
main:
839e85ff(2026-11-13)this PR: fix/linux-fsr4-own-backend @
8f674e95Hardware: build machine only (Linux, CachyOS, kernel 7.3.3)
Method:
python -I -B tests/run_all.py(outside the sandbox), then the 2 failures re-run on a clean export of main839e85ff8f674e95build.py --dry-run,LONGSHOT_FSR=1,LONGSHOT_VK_PREBUILTsetTesting
Tested: the game-free checks above on Linux, including the new ones: test_linux_preflight (19), test_cpu_target, test_appimage_player_build (the FSR 4 dry run from a packaged AppImage tree passes here: the back end is compiled with AMD FSR 4 and no file is missing), test_appimage_licenses, test_make_game_ngx, test_shader_stamp_cache. In CI, the existing "longshot_vk.so with AMD FSR 4 (build check)" job compiles the back end with FSR 4, and check_player_build.sh's new step does the dry run from the real image.
Not tested: a full game build from the AppImage with
--fsr, and FSR 4 in the game on Linux: this PC has an NVIDIA card. The game-side Linux FSR 4 path is unchanged and was run on an RX 9070 XT with fsr4-vk (docs/tested-configurations.md, 2026-11-11).Needs other testers:
--cli … --fsr, putamd_fidelityfx_upscaler_vk.sobeside the game, and check that the Upscaler row offers AMD FSR 4 and the log'svk: AMD FSR 4:line names it (T-MARB)Screenshots
Nothing on screen.
Risk and rollback
Only builds that ask for FSR 4 change. A Linux
--fsrbuild now compiles the Vulkan back end (a few minutes longer) where it used the prebuilt one before, and it needs the build tools' Vulkan headers, as DLSS builds already do. Nothing changes for builds without--fsror on Windows. The AppImage grows by AMD's headers (about 100 KB). Reverting the commit is enough.Reviewer notes
Start with
prebuilt_renderer()in nx2/build.py and the move of theLONGSHOT_FSRswitch in make_game.py'smain(). After the self-review: the switch is set or cleared in one place before the checks,check_cputakes the decision as an argument, and the missing-headers check moved from build.py to make_game.py's Linux prerequisites.Issues
See MagicBeanDev/LongshotLauncher#24 (the launcher's AMD FSR 4 switch on Linux).
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.mainand passing here.tests/that reads changed source updated in this PR, not weakened.🤖 Generated with Claude Code
Linked the launcher side: MagicBeanDev/LongshotLauncher#24 (its AMD FSR 4 switch on Linux, which passes --fsr only when the tools carry third_party/fidelityfx). Added LONGSHOT_VK_PREBUILT to docs/development.md's build switches.
Test request T-MARB is open for the Linux AMD run with FSR 4.
Code review only: I read the diff and the code around it, and ran no tests or builds.
Verdict: the approach holds.
prebuilt_renderer()is the single switch,buildis imported lazily incheck_linux_prerequisites()so it readsLONGSHOT_FSRaftermain()sets it, and the AppImage's FSR dry run incheck_player_build.shshould pass: AppRun'sbuild_toolsgivesRX_CPMand clang++ from tools.lock, andcp -a third_party/fidelityfxcarries all four headersvk_fsr4.cppincludes. Some things to fix before ready, most important first:LONGSHOT_FSRfrom the environment is cleared too late (make_game.pymain()). Theelse: os.environ.pop("LONGSHOT_FSR")now runs aftercheck_cpu()andcheck_windows_sdk(). SoLONGSHOT_FSR=1exported, with--no-fsror without--fsr, makescheck_cpu()skip the AVX2 stop and makes the preflight look for headers instead of the prebuilt. The build itself then uses the prebuilt back end, and on a CPU without AVX2 that is the "40 minutes in" failure issue 363 was about. Set the variable or pop it in one place, before the checks (or passfsrtocheck_cpu).elif build_vk:). An AppImage made before this change carries the old build.py, which uses its prebuilt and never reaches this line, and with this change the image always carries the headers. If you keep it, the preflight (check_linux_prerequisites, which already readsbuild.prebuilt_renderer()) is the place that runs before the game is read. Either way, the "an AppImage made before this change" wording in the description and the comment should go.check_cpubranch and the move ofLONGSHOT_FSRahead ofcheck_cpu()inmain()have no check.tests/test_cpu_target.pyAppImageCheck.run_checkclearsLONGSHOT_VK_PREBUILTandRX_MARCHbut notLONGSHOT_FSR, so withLONGSHOT_FSR=1in the shell,test_appimage_without_avx2_stopsnow fails.tests/test_appimage_player_build.pyhastest_dlss_compiles_the_back_end_from_the_packaged_tree, and an FSR twin of it would cover the image path without CI..forgejo/workflows/build-linux.yml("a build option the AppImage leaves off") is now half true.@ -927,2 +927,3 @@says so and offers the rest. On Linux, a Vulkan FSR 4 library (`amd_fidelityfx_upscaler_vk.so`, such as fsr4-vk's)takes their place.takes their place: AMD publishes none for Linux, so you put it beside the game yourself. A Linux build with FSR 4(the launcher's **AMD FSR 4** switch, or `--fsr`) compiles its own Vulkan back end, as with DLSS, since the AppImage's(nit) The launcher's AMD FSR 4 switch on Linux is LongshotLauncher#24 (MagicBeanDev/LongshotLauncher#24), not merged yet. Maybe "(
--fsr, or the launcher's AMD FSR 4 switch where it offers it)".@ -1158,6 +1160,11 @@ def main():return 1elif build_vk:print("the Vulkan back end: Vulkan %s, SPIR-V %s, VMA %s" % (vk_headers, spirv_headers, vma_headers), flush=True)if FSR and not os.path.isfile(os.path.join(FSR_HEADERS, "api", "include", "ffx_api.h")):(low) An AppImage from before this change has the old build.py, which takes its prebuilt and never gets here, and from now on the image always carries
third_party/fidelityfx. So this guards a case that can't happen through the AppImage. If it stays,check_linux_prerequisites()is where it would be said before the game is read. The comment, and the PR description's "an AppImage made before this change", should be reworded either way.@ -593,2 +593,2 @@its start. Said now, before the game is read, not 40 minutes in (issue 363)"""if not os.environ.get("LONGSHOT_VK_PREBUILT") or os.environ.get("RX_MARCH"):its start. Said now, before the game is read, not 40 minutes in (issue 363). A build with AMD FSR 4 compiles its own"""if not os.environ.get("LONGSHOT_VK_PREBUILT") or os.environ.get("RX_MARCH") or os.environ.get("LONGSHOT_FSR") == "1":Reads the environment rather than the build's decision, which is what makes the ordering in
main()matter (see the comment there). Passingfsrin, or popping the variable before this runs, would make it robust. A case intests/test_cpu_target.py(FSR set + prebuilt +x86-64-v2goes on) would pin it down, andrun_checkthere should also clearLONGSHOT_FSR.@ -2182,1 +2182,4 @@check_writable(out)# (AMD FSR 4: the build's switch for nx2/build.py, set before the checks: on Linux it compiles its own Vulkan back end, as# with DLSS, so the AppImage's prebuilt one is neither checked nor used)fsr = fsr_wanted(a, out)(medium)
fsris decided here, butLONGSHOT_FSRis only popped in theelseat the end of this block, aftercheck_cpu()andcheck_windows_sdk(). WithLONGSHOT_FSR=1inherited and--no-fsr(or no--fsr),check_cpu()skips the AVX2 stop and the preflight checks headers rather than the prebuilt, yet build.py then runs with the prebuilt back end. Suggestif fsr: os.environ[...] = "1"/else: os.environ.pop("LONGSHOT_FSR", None)right here, keeping only the mark handling below.@ -58,0 +59,4 @@echo "== the same with AMD FSR 4 (make_game.py --fsr): the Vulkan back end compiled from the image's sources and AMD's headers"(cd "$T" && in_image env OOT_WORK="$TMP/work-fsr" RX_RELEASE=1 A64_HOT_CHUNK=1 PYTHONIOENCODING=utf-8 LONGSHOT_FSR=1 \"$R/usr/python/bin/python3" -I -B -u -X utf8 nx2/build.py --standalone --jobs 1 --dry-run) | tee "$TMP/fsr.txt"grep -q "with AMD FSR 4" "$TMP/fsr.txt" || { echo "a build with AMD FSR 4 did not compile its own Vulkan back end"; exit 1; }This looks sound:
build_toolsgivesRX_CPMand clang++, thegrepstring only comes from theif FSR:print in the compile block, andpipefailcarries the dry run's exit code throughtee. A game-free twin intests/test_appimage_player_build.py, liketest_dlss_compiles_the_back_end_from_the_packaged_tree, would cover the image path locally too.68e659a4b28f674e95abSelf-review fixes (
8f674e95): LONGSHOT_FSR is set or cleared in one place before the checks, so one left in the shell no longer skips check_cpu for a build that uses the prebuilt back end; check_cpu takes the build's FSR decision; the missing-headers check moved from build.py (where it couldn't fire) to make_game.py's Linux prerequisites, and the description no longer claims it covers old AppImages; new tests in test_cpu_target, test_linux_preflight and test_appimage_player_build (an FSR 4 dry run from a packaged AppImage tree, passing); README and build-linux.yml comment corrected. run_all: 465 passed, 2 failed, the same 2 as main.WIP: fix(linux): a Linux build with AMD FSR 4 compiles its own Vulkan back end, from AMD's FSR API headers the AppImage now carriesto fix(linux): a Linux build with AMD FSR 4 compiles its own Vulkan back end, from AMD's FSR API headers the AppImage now carriesGuep referenced this pull request2026-11-13 12:49:22 +00:00
Guep referenced this pull request2026-11-13 12:49:24 +00:00
Guep referenced this pull request2026-11-13 12:49:27 +00:00