feat: Linux and SteamOS build (Vulkan through SDL_GPU, gamepad launcher)
Summary
Adds a Linux x86-64 build, aimed at SteamOS (Steam Deck), on top of the macOS
port from #99. No issue is open for this; it follows the offer in #99.
- Runtime:
win32_compat.cgains the Linux stat-time and xattr fields and
a case-insensitive path lookup;xbe/sha.cppgains a portable SHA-1/SHA-256
for hosts with neither bcrypt nor CommonCrypto. CMake uses the system SDL3,
or builds it whenRECOMP_FETCH_SDL3=ON(build_game.pysets it on Linux).
Hosts with neither still configure, so thecustodyjob stays offline. - Presenter: SDL_GPU selects Vulkan on Linux, which takes SPIR-V. The
presenter emits GLSL beside the MSL and compiles it at runtime with shaderc,
loaded withdlopen(no build-time dependency). - Per-draw cost (shared with macOS): pipelines are chosen when a draw is
queued; redundant binds, uniform pushes and dynamic state are skipped; MSAA
resolves once per frame rather than at each segment flush. - Uniform layout (shared with macOS): every draw pushes a 144-float header
that now holdsdirectional_flags; lit draws add the directional block;
vertex programs push their constants to a second slot. SDL's Vulkan backend
binds 4 KB per uniform slot, which the former 1048-float block exceeded. - Tools:
launcher.shon Linux runs a small SDL settings launcher
(tools/launcher_sdl.cpp: gamepad, keyboard, mouse, touch). SteamOS has no
compiler, so it is built beside the runner and started by
run_game.py --launcher.docs/building.mdgains a Linux and SteamOS
section. RECOMP_PERF_COUNTER=1prints an fps/gap/draws line once a second, in the
Direct3D 11 presenter's format.
Not included: custom soundtracks (Media Foundation only), SMAA, a Linux CI job,
and CHANGELOG / docs/public-status.md entries, which I left for the release
notes.
Verification
ctest(public tests): 22 pass on Linux x86-64 (Ubuntu 24.04 container,
SDL_VIDEODRIVER=offscreen) and on macOS arm64; custom music skipped.python -m unittest discover -s tools -p "test_*.py"and
python tools/public_export.py verify --require-public-treepass on macOS.
In an Ubuntu container without SDL3,test_build_game.py's CMake
authentication test passes.- Authenticated local build (recipe
46c5de9c…, manifesteedd0728…),
cross-built in an Ubuntu 24.04 container and run on a Steam Deck (SteamOS,
gamescope) at 720p, 4x MSAA, with scripted A presses: it reached save select
and the controller settings menu over the island backdrop, about 1550 draws
a frame, at 60 fps. Before the event-pump fix that landed in #99, the same
path ran at 46-58 fps. Frames captured at six swaps matched the previous
presenter build within timing differences. - The same runner starts and presents frames on a second SteamOS machine
(Navi 33). The macOS runner built from this branch reaches the same menus at
60 fps with no shader or pipeline errors. - These are smoke runs. No one has play-tested this branch by hand yet; a
hand play test on the Deck is still to come. The Deck and Steam Machine runs
used the build from before this branch was rebased ontomain; the runtime
sources are identical apart from the SDL3 lookup inCMakeLists.txt, and
the rebased tree builds and passes the tests above. - Not run: Windows CI for this branch (the D3D11 path is untouched).
Contribution checklist
- The change follows
AGENTS.mdandCONTRIBUTING.md. - No XBE, generated game C, retail instruction bytes, extracted assets or
filenames, saves, BIOS data, private paths, or run evidence are included. - Third-party material and its license are identified, if applicable:
SDL3 (zlib) is fetched at build time when the system has none; shaderc
(Apache-2.0) is loaded from the system at runtime. Neither is vendored.
Summary by cubic
Adds a Linux x86-64 build for SteamOS (Steam Deck) by extending the macOS SDL_GPU presenter to Vulkan, emitting GLSL compiled at runtime with shaderc, loaded via dlopen. Per-draw cost cuts keep an authenticated build at 60 fps on the Deck; custom soundtracks (Media Foundation only), SMAA, and a Linux CI job remain out. Verified with 22 passing public tests on Linux and macOS; smoke runs on a Steam Deck and a Navi 33 SteamOS machine at 720p/4x MSAA (~1550 draws/frame) are done, with no hand play test yet.
Runtime and build
win32_compatgains Linux stat-time and xattr fields plus case-insensitive path lookup, since the disc's filenames differ in case from the game's; storing READONLY now preserves the other attribute bits even when the file is already read-only, by granting owner write first.- Portable SHA-1/SHA-256 for hosts with neither bcrypt nor CommonCrypto.
- CMake uses the system SDL3, or fetches and builds it when
RECOMP_FETCH_SDL3=ON; the fetched SDL3 release is pinned by hash. launcher.shruns a new SDL settings launcher (tools/launcher_sdl.cpp) with gamepad, keyboard, mouse, and touch support; SteamOS has no compiler, so it is built beside the runner.
Presenter and fixes
- Pipelines resolve when a draw is queued; redundant binds, uniform pushes, and dynamic state are skipped at flush. MSAA resolves once a frame instead of at each segment flush.
- The 144-float uniform header now carries
directional_flags; vertex program constants move to a second slot that fits SDL's 4 KB Vulkan per-slot limit. - shaderc loads once, under
libshaderc_sharedor its Debian namelibshaderc.so.1, and failed shaders/pipelines are cached so they are not rebuilt or logged on every draw. SDL_GPU samplers now sample mipmaps (their default clamps to base level). - Vertex-program draws require FVF 0x112 with a translated program so shaders never read unwritten outputs.