From c46388989ff46bf90455196ca153bb8d71df08bf Mon Sep 17 00:00:00 2001 From: Viktor Ferenczi Date: Fri, 21 Aug 2026 22:20:49 +0200 Subject: [PATCH] Building and distributing libSDL3 --- .github/workflows/build.yml | 21 +++- Licenses/README.txt | 7 +- Licenses/SDL3-LICENSE.txt | 18 +++ Licenses/SDL3-README.txt | 56 +++++++++ Licenses/se2/README.txt | 7 +- README.md | 2 + Scripts/build_sdl3.sh | 227 ++++++++++++++++++++++++++++++------ build.sh | 73 +++++++----- docs/architecture.md | 6 +- docs/building.md | 33 ++++-- docs/consuming.md | 36 +++++- docs/dependencies.md | 89 ++++++++------ docs/maintenance.md | 31 +++-- docs/release-archive.md | 25 +++- 14 files changed, 499 insertions(+), 132 deletions(-) create mode 100644 Licenses/SDL3-LICENSE.txt create mode 100644 Licenses/SDL3-README.txt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1be1f38..7afd368 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,9 +58,18 @@ jobs: # mingw-w64-tools -> widl, the IDL compiler vkd3d-proton uses to # generate its COM headers (Scripts/build_vkd3d_proton.sh # shims the x86_64-w64-mingw32-widl name onto PATH) - # cmake -> the pinned SDL3 that DXVK compiles against (ubuntu-24.04 - # has no libsdl3-dev, so Scripts/build_sdl3.sh builds one) - # and OpenAL Soft + # cmake -> the pinned SDL3 that DXVK compiles against and that we + # ship (ubuntu-24.04 has no libsdl3-dev, so + # Scripts/build_sdl3.sh builds one), and OpenAL Soft + # libx11-dev/... + libwayland-dev/wayland-protocols/libdecor-0-dev/ + # libxkbcommon-dev/libegl-dev -> SDL3's X11 and Wayland + # video drivers. Same trap as OpenAL's audio backends: SDL + # compiles a video driver in only when its headers are + # present and dlopens the library at runtime, and cmake + # disables a driver silently when they are not. Without + # these the shipped libSDL3 could not open a window; + # build_sdl3.sh asserts on the generated build config so a + # missing package fails the build instead. # libpulse-dev/libasound2-dev/libpipewire-0.3-dev -> OpenAL compiles # a backend in only when its headers are present at build # time, then dlopens the library at runtime. Without these @@ -72,7 +81,11 @@ jobs: build-essential pkg-config make cmake curl tar git \ nasm patchelf binutils zlib1g-dev \ meson ninja-build glslang-tools libvulkan-dev mingw-w64-tools \ - libpulse-dev libasound2-dev libpipewire-0.3-dev + libpulse-dev libasound2-dev libpipewire-0.3-dev \ + libx11-dev libxext-dev libxcursor-dev libxi-dev libxfixes-dev \ + libxrandr-dev libxrender-dev libxss-dev libxtst-dev \ + libwayland-dev wayland-protocols libdecor-0-dev \ + libxkbcommon-dev libegl-dev libdrm-dev libgbm-dev - name: Set up .NET SDK uses: actions/setup-dotnet@v5 diff --git a/Licenses/README.txt b/Licenses/README.txt index c6e0ea5..be5e91f 100644 --- a/Licenses/README.txt +++ b/Licenses/README.txt @@ -17,6 +17,9 @@ Files in this directory: OpenAL-Soft-README.txt Build provenance + LGPL relinking notes OpenAL-Soft-NOTICES.txt BSD-3-Clause and PFFFT notices for the portions of OpenAL Soft under those terms + SDL3-LICENSE.txt zlib licence covering libSDL3.so (unmodified + upstream SDL3) + SDL3-README.txt Build provenance + replacement notes EOS-NOTICE.txt Attribution for libEOSSDK-Linux-Shipping.so (proprietary, Epic Games) @@ -29,4 +32,6 @@ text. The Space Engineers 2 companion archive (se2-dependencies.tar.gz) and the Steam companion archive (steam-dependencies.tar.gz) carry their own LICENSES directories, sourced from the se2/ and steam/ subdirectories of the -repository's Licenses/ folder; none of those notices ship here. +repository's Licenses/ folder (plus the DXVK and SDL3 notices above, which +cover binaries shared between the two game archives); none of the +SE2-specific or Steam-specific notices ship here. diff --git a/Licenses/SDL3-LICENSE.txt b/Licenses/SDL3-LICENSE.txt new file mode 100644 index 0000000..e9adee4 --- /dev/null +++ b/Licenses/SDL3-LICENSE.txt @@ -0,0 +1,18 @@ +Copyright (C) 1997-2026 Sam Lantinga + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + diff --git a/Licenses/SDL3-README.txt b/Licenses/SDL3-README.txt new file mode 100644 index 0000000..62e8874 --- /dev/null +++ b/Licenses/SDL3-README.txt @@ -0,0 +1,56 @@ +SDL3 (Simple DirectMedia Layer) +=============================== + +The following shared library shipped alongside this notice is SDL 3.4.12: + + libSDL3.so (SONAME: libSDL3.so.0) + +License +------- +zlib license. See SDL3-LICENSE.txt in this directory for the full text. + +The library is unmodified upstream SDL3 — no patches are applied. The only +post-build change to the binary is a patchelf step setting DT_RUNPATH=$ORIGIN, +matching every other library in this archive. + +Source code +----------- +The library is built from the upstream release tag: + + https://github.com/libsdl-org/SDL @ release-3.4.12 + +The exact build configuration used to produce the shipped binary is in the +CometWorks/linux-dependencies repository at Scripts/build_sdl3.sh: + + https://github.com/CometWorks/linux-dependencies + +The cmake options and the post-build patchelf step are reproduced verbatim +there. + +Why it is here +-------------- +DXVK Native's window-system integration compiles against SDL3 and dlopens +"libSDL3.so.0" at runtime; the shipped libdxvk_*.so therefore have no NEEDED +entry for it. Bundling the same SDL3 the DXVK binaries were compiled against +means the pair is always a matched set, rather than depending on whatever +SDL3 — if any — the host distribution ships. + +Video and audio backends +------------------------ +X11 and Wayland video support is compiled in (both are required at build time, +so a build cannot silently produce a headless SDL), along with Vulkan surface +support, KMSDRM, and the usual audio backends. Those system libraries are +dlopened at runtime rather than linked, so the shipped binary depends only on +glibc and uses whichever display server and audio stack the host provides. + +Replacing it +------------ +The library is loaded by file name from the bundle directory. To use your own +SDL3, replace the shipped libSDL3.so with your build, keeping the SONAME +libSDL3.so.0 — that is the name DXVK dlopens, and it is what makes the +preloaded file satisfy the request. + +Copyright +--------- +SDL is copyright (c) 1997-2026 Sam Lantinga and the SDL contributors. +See https://libsdl.org/ for details. diff --git a/Licenses/se2/README.txt b/Licenses/se2/README.txt index f07fb23..cfc08b3 100644 --- a/Licenses/se2/README.txt +++ b/Licenses/se2/README.txt @@ -11,6 +11,10 @@ Files in this directory: from upstream DXVK plus the patch series published under Patches/dxvk/ in the CometWorks/linux-dependencies repository) + SDL3-LICENSE.txt zlib licence covering libSDL3.so + (unmodified upstream SDL3, dlopened by + DXVK's window-system integration) + SDL3-README.txt Build provenance + replacement notes VKD3D-LGPL-2.1.txt LGPL-2.1 text covering libvkd3d-proton-d3d12*.so vkd3d-proton-README.txt Build provenance + LGPL relinking notes @@ -22,4 +26,5 @@ Files in this directory: This is the SE2 companion archive (se2-dependencies.tar.gz). The Space Engineers 1 libraries and their licences ship separately in -se1-dependencies.tar.gz; the patched DXVK binaries are identical in both. +se1-dependencies.tar.gz; the patched DXVK binaries and libSDL3.so are +identical in both. diff --git a/README.md b/README.md index 76c4818..7c3ba0f 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ their own archive, consumed alongside either game archive. | --- | --- | --- | | FFmpeg (`libavcodec`, `libavformat`, `libavutil`, `libswresample`, `libswscale`) | 8.1 | LGPL-2.1-or-later | | DXVK Native (`libdxvk_d3d11.so`, `libdxvk_dxgi.so`) + [Patches/dxvk/](Patches/dxvk/) | 2.7.1 | zlib | +| SDL3 (`libSDL3.so`) — dlopened by DXVK's window-system integration | 3.4.12 | zlib | | OpenAL Soft (`libopenal.so`) | 1.25.2 | LGPL-2.0-or-later | | `libEOSSDK-Linux-Shipping.so` | vendor blob | proprietary (Epic) | @@ -28,6 +29,7 @@ their own archive, consumed alongside either game archive. | Artefact | Version | Licence | | --- | --- | --- | | DXVK Native — same patched build as above | 2.7.1 | zlib | +| SDL3 — same build as above | 3.4.12 | zlib | | vkd3d-proton (`libvkd3d-proton-d3d12.so`, `libvkd3d-proton-d3d12core.so`) + [Patches/vkd3d-proton/](Patches/vkd3d-proton/) | pinned commit | LGPL-2.1 | | FMOD Engine (`libfmod.so`, `libfmodstudio.so`) | 2.03.11, matching the game | proprietary (Firelight) | diff --git a/Scripts/build_sdl3.sh b/Scripts/build_sdl3.sh index e88c701..08b57e5 100755 --- a/Scripts/build_sdl3.sh +++ b/Scripts/build_sdl3.sh @@ -1,46 +1,67 @@ #!/usr/bin/env bash # build_sdl3.sh # -# Builds SDL3 from upstream sources into a build-local prefix, purely so that -# DXVK Native has SDL3 headers and a pkg-config file to compile against. +# Builds SDL3 from upstream sources into a build-local prefix and stages +# libSDL3.so into the build/Libraries (SE1) and build/Libraries-SE2 (SE2) +# staging folders. # -# SDL3 is NOT a shipped artefact. DXVK's meson build takes it as +# SDL3 serves two purposes here: # -# lib_sdl3.partial_dependency(compile_args: true, includes: true) +# 1. Build time: DXVK Native's window-system integration compiles against +# SDL3 headers. DXVK takes it as # -# i.e. headers and compile flags only — the produced libdxvk_*.so have no -# NEEDED entry for libSDL3 and dlopen "libSDL3.so.0" at runtime instead. Pulsar -# supplies SDL3 from the user's system (it sets DXVK_WSI_DRIVER=SDL3 and -# preloads libSDL3.so; see Legacy/Loader/NativeLibraryPreloader.cs there), so -# nothing about SDL3 needs to end up in our release archive. +# lib_sdl3.partial_dependency(compile_args: true, includes: true) +# +# i.e. headers and compile flags only, so the produced libdxvk_*.so have +# no NEEDED entry for libSDL3 and dlopen "libSDL3.so.0" at runtime. +# +# 2. Run time: that dlopen has to find an SDL3. Leaving it to the host was +# the previous arrangement and it is not dependable — SDL3 is new enough +# that plenty of otherwise current distributions ship no libSDL3 at all, +# and the ones that do ship whatever version they happened to freeze. +# Shipping the same pinned build the DXVK binaries were compiled against +# removes that variable, exactly like the bundled OpenAL removes it for +# audio. # # Why build it instead of using the host's SDL3: Ubuntu 24.04 — the pinned CI # runner image — has no libsdl3-dev package at all, and a developer machine may # have any SDL3 version or none. Building one pinned version here means CI and -# local builds compile DXVK against identical headers. +# local builds compile DXVK against identical headers AND ship that exact +# library. +# +# Because it is now a shipped artefact, the video backends matter: this build +# requires X11 and Wayland (see the assertion after the install step). Both are +# dlopened by SDL at runtime rather than linked, so the shipped library still +# depends on nothing but glibc — but their development headers must be present +# at build time or SDL silently produces a library that cannot open a window. # # Source layout (under the gitignored build/ folder of this repo): # # build/ +# ├── Libraries/ staging dir all SE1 dep scripts populate +# ├── Libraries-SE2/ staging dir for the SE2 archive # ├── SDL/ shallow clone of libsdl-org/SDL at the pinned tag # ├── sdl3-prefix/ install prefix (headers + lib + pkgconfig) # └── sdl3.stamp last-built tag (cache key) # # Usage: -# ./build_sdl3.sh Build (or no-op if cached). +# ./build_sdl3.sh Build (or no-op if cached) and stage. # ./build_sdl3.sh --clean Wipe build dirs and rebuild from scratch. # # On success the prefix is at $SDL3_PREFIX and its pkgconfig dir is # $SDL3_PREFIX/lib/pkgconfig (build_dxvk.sh prepends that to PKG_CONFIG_PATH). # # Env-var overrides (defaults shown): -# SDL3_VERSION = 3.4.12 (upstream tag is release-$SDL3_VERSION) -# SDL3_REPO = https://github.com/libsdl-org/SDL.git -# BUILD_DIR = /build -# SDL3_PREFIX = $BUILD_DIR/sdl3-prefix -# JOBS = $(nproc) +# SDL3_VERSION = 3.4.12 (upstream tag is release-$SDL3_VERSION) +# SDL3_REPO = https://github.com/libsdl-org/SDL.git +# BUILD_DIR = /build +# SDL3_PREFIX = $BUILD_DIR/sdl3-prefix +# LIBRARIES_DIR = $BUILD_DIR/Libraries +# LIBRARIES_SE2_DIR = $BUILD_DIR/Libraries-SE2 +# JOBS = $(nproc) # -# Requirements: git, cmake, ninja (or make), gcc, g++, pkg-config. +# Requirements: git, cmake, ninja (or make), gcc, g++, pkg-config, patchelf, +# readelf, plus the X11 and Wayland development packages (see docs/building.md). set -euo pipefail @@ -53,46 +74,66 @@ REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" BUILD_DIR="${BUILD_DIR:-$REPO_DIR/build}" SDL3_PREFIX="${SDL3_PREFIX:-$BUILD_DIR/sdl3-prefix}" +LIBRARIES_DIR="${LIBRARIES_DIR:-$BUILD_DIR/Libraries}" +LIBRARIES_SE2_DIR="${LIBRARIES_SE2_DIR:-$BUILD_DIR/Libraries-SE2}" JOBS="${JOBS:-$(nproc)}" SDL3_SRC_DIR="$BUILD_DIR/SDL" SDL3_BUILD_DIR="$SDL3_SRC_DIR/_build" STAMP_FILE="$BUILD_DIR/sdl3.stamp" +# The SONAME upstream produces. Asserted after the build: DXVK dlopens this +# exact name, and the shipped file is named libSDL3.so, so the two only meet +# because the consumer preloads the file and the SONAME then satisfies the +# dlopen. A major-version bump would break that silently. +EXPECTED_SONAME="libSDL3.so.0" + +# Bumped whenever the cmake options below change in a way that alters the +# produced library. It is part of the stamp so an existing build/ tree from +# before the change is rebuilt rather than re-staged: the tag alone would +# still match and the old, differently-configured library would ship. +CONFIG_REV="2" +STAMP_VALUE="$SDL3_TAG config$CONFIG_REV" + CLEAN=0 for arg in "$@"; do case "$arg" in --clean) CLEAN=1 ;; - -h|--help) sed -n '2,43p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + -h|--help) sed -n '2,64p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; *) echo "ERROR: unknown arg: $arg" >&2; exit 2 ;; esac done # ---- preflight -------------------------------------------------------------- -for tool in git cmake gcc g++ pkg-config; do +for tool in git cmake gcc g++ pkg-config patchelf readelf; do command -v "$tool" >/dev/null 2>&1 || { echo "ERROR: required tool not found in PATH: $tool" >&2 exit 1 } done -mkdir -p "$BUILD_DIR" +mkdir -p "$BUILD_DIR" "$LIBRARIES_DIR" "$LIBRARIES_SE2_DIR" # ---- cache check ------------------------------------------------------------ +# Only the build is cached; staging below always runs, so a wiped Libraries +# tree is repopulated without a rebuild. PC_FILE="$SDL3_PREFIX/lib/pkgconfig/sdl3.pc" +SKIP_BUILD=0 if [ "$CLEAN" = "1" ]; then rm -rf "$SDL3_SRC_DIR" "$SDL3_PREFIX" elif [ -f "$PC_FILE" ] \ && [ -f "$STAMP_FILE" ] \ - && [ "$(cat "$STAMP_FILE")" = "$SDL3_TAG" ]; then + && [ "$(cat "$STAMP_FILE")" = "$STAMP_VALUE" ]; then echo "==> Cached SDL3 matches $SDL3_TAG; skipping rebuild" echo "==> Prefix: $SDL3_PREFIX" - exit 0 + SKIP_BUILD=1 fi +if [ "$SKIP_BUILD" = "0" ]; then + # ---- clone (cached) -------------------------------------------------------- if [ ! -d "$SDL3_SRC_DIR/.git" ]; then @@ -106,17 +147,21 @@ else fi # ---- configure + build + install ------------------------------------------- -# Only the headers and the pkg-config file are actually wanted. Tests and -# examples are off because nothing here runs them and they roughly double the -# build time. -# -# SDL_UNIX_CONSOLE_BUILD=ON suppresses SDL's "could not find X11 or Wayland -# development libraries" hard error. That check exists to stop someone -# accidentally building an SDL that cannot open a window, which is exactly the -# situation we do not care about: DXVK compiles against the headers and never -# links or loads this SDL, and the public headers are identical either way. The -# alternative would be installing the whole X11 + Wayland dev stack on the CI -# runner to build a library we then throw away. +# Tests and examples are off because nothing here runs them and they roughly +# double the build time. Everything else is upstream's default configuration +# for a Linux desktop build, with the two video drivers that matter turned on +# explicitly: +# +# SDL_X11 / SDL_WAYLAND are ON by default on Unix, but CMake quietly turns +# each off again when its development headers are missing — the same trap as +# OpenAL's audio backends. Passing them explicitly does not prevent that (SDL +# has no REQUIRE_* equivalent), so the generated SDL_build_config.h is +# asserted after the install instead. +# +# SDL_X11_SHARED / SDL_WAYLAND_SHARED (both ON by default) keep the display +# libraries dlopened rather than linked, so the shipped libSDL3.so needs +# nothing but glibc and adapts to whichever display server the user is on. +# The NEEDED check below enforces that. echo "==> Configuring SDL3 $SDL3_VERSION -> $SDL3_PREFIX" cmake -S "$SDL3_SRC_DIR" -B "$SDL3_BUILD_DIR" \ @@ -128,7 +173,11 @@ cmake -S "$SDL3_SRC_DIR" -B "$SDL3_BUILD_DIR" \ -DSDL_TESTS=OFF \ -DSDL_EXAMPLES=OFF \ -DSDL_INSTALL_TESTS=OFF \ - -DSDL_UNIX_CONSOLE_BUILD=ON + -DSDL_X11=ON \ + -DSDL_X11_SHARED=ON \ + -DSDL_WAYLAND=ON \ + -DSDL_WAYLAND_SHARED=ON \ + -DSDL_VULKAN=ON echo "==> Building SDL3 with -j$JOBS" cmake --build "$SDL3_BUILD_DIR" -j "$JOBS" @@ -139,12 +188,118 @@ cmake --install "$SDL3_BUILD_DIR" >/dev/null [ -f "$PC_FILE" ] || { echo "ERROR: SDL3 install did not produce $PC_FILE" >&2 echo " DXVK resolves SDL3 through pkg-config, so the .pc file is" >&2 - echo " the part that actually matters here." >&2 + echo " what the DXVK build actually consumes." >&2 + exit 1 +} + +# ---- verify the video drivers ---------------------------------------------- +# The whole point of shipping this library is that DXVK's SDL3 WSI driver can +# open a window on the user's machine. A build host without the X11 or Wayland +# headers produces an SDL that loads fine and then fails at SDL_CreateWindow, +# which is a miserable thing to debug downstream. Fail here instead. + +CONFIG_HEADER="$(find "$SDL3_BUILD_DIR" -name SDL_build_config.h -print -quit)" +[ -n "$CONFIG_HEADER" ] || { + echo "ERROR: could not locate the generated SDL_build_config.h under" >&2 + echo " $SDL3_BUILD_DIR" >&2 exit 1 } -printf '%s\n' "$SDL3_TAG" > "$STAMP_FILE" +DRIVER_MISSING=0 +for driver in SDL_VIDEO_DRIVER_X11 SDL_VIDEO_DRIVER_WAYLAND SDL_VIDEO_VULKAN; do + if ! grep -qE "^#define $driver 1\$" "$CONFIG_HEADER"; then + echo " not enabled: $driver" >&2 + DRIVER_MISSING=1 + fi +done +if [ "$DRIVER_MISSING" = "1" ]; then + echo "ERROR: SDL3 was configured without a video driver we ship for." >&2 + echo " CMake disables X11/Wayland silently when their development" >&2 + echo " headers are absent. Install them and rebuild --clean; see" >&2 + echo " docs/building.md for the package list." >&2 + echo " Config header: $CONFIG_HEADER" >&2 + exit 1 +fi +echo "==> Video drivers present: X11, Wayland, Vulkan" + +printf '%s\n' "$STAMP_VALUE" > "$STAMP_FILE" + +fi # SKIP_BUILD + +# ---- verify the SONAME ------------------------------------------------------ +# DXVK dlopens "libSDL3.so.0" by name; the shipped file is libSDL3.so, so the +# dlopen only resolves to it because the consumer preloads the file first and +# the SONAME inside it matches. A bump here would silently leave the bundled +# copy unused. + +REAL_LIB="$(readlink -f "$SDL3_PREFIX/lib/libSDL3.so")" +[ -f "$REAL_LIB" ] || { + echo "ERROR: $SDL3_PREFIX/lib/libSDL3.so missing after install" >&2 + exit 1 +} + +SONAME="$(readelf -d "$REAL_LIB" 2>/dev/null \ + | awk '/\(SONAME\)/ {match($0, /\[.*\]/); print substr($0, RSTART+1, RLENGTH-2)}')" +if [ "$SONAME" != "$EXPECTED_SONAME" ]; then + echo "ERROR: expected SONAME '$EXPECTED_SONAME', got '$SONAME'." >&2 + echo " DXVK dlopens the old name, so a bump means the bundled" >&2 + echo " library is never used. Update EXPECTED_SONAME here, the" >&2 + echo " expected-file lists in build.sh, and the consumers together." >&2 + exit 1 +fi + +# ---- patch DT_RUNPATH=$ORIGIN ---------------------------------------------- +# Parity with the FFmpeg, DXVK and OpenAL payloads. Patch a private copy so +# the prefix that DXVK compiles against stays exactly as installed. + +STAGE_LIB="$BUILD_DIR/libSDL3.so.staged" +install -m 0755 "$REAL_LIB" "$STAGE_LIB" + +echo "==> Patching DT_RUNPATH=\$ORIGIN onto libSDL3" +patchelf --set-rpath '$ORIGIN' "$STAGE_LIB" + +# ---- verify runtime dependencies ------------------------------------------- +# X11, Wayland, xkbcommon, libdecor, libdrm/gbm, the audio backends and Vulkan +# are all dlopened by SDL at runtime, so none of them may appear as a NEEDED +# entry: one that did would make the whole bundle hard-require that display or +# audio stack on every user's machine. + +echo "==> Verifying minimal runtime dependencies" +ALLOWED_RE='^(linux-vdso|ld-linux|libc|libm|libpthread|libdl|librt|libgcc_s|libstdc\+\+|libatomic)(-[a-z0-9_.-]+)?\.so' +DEP_LEAK=0 +while IFS= read -r line; do + bare="$(printf '%s' "$line" | awk '{print $1}')" + bare="${bare##*/}" + [ -z "$bare" ] && continue + if ! [[ "$bare" =~ $ALLOWED_RE ]]; then + echo " unexpected dep: $bare" >&2 + DEP_LEAK=1 + fi +done < <(ldd "$STAGE_LIB" | sed 's/^[[:space:]]*//') +if [ "$DEP_LEAK" = "1" ]; then + echo "ERROR: libSDL3 has an unexpected runtime dependency." >&2 + echo " Display and audio backends are meant to be dlopened, not" >&2 + echo " linked. Check that SDL_X11_SHARED / SDL_WAYLAND_SHARED and" >&2 + echo " the other SDL_*_SHARED options are still ON." >&2 + exit 1 +fi + +# ---- stage ------------------------------------------------------------------ +# Ship the real file under the bare unversioned name — the archives carry no +# symlinks and no version-suffixed filenames. Both game archives get the same +# bytes: SE1's DXVK and SE2's DXVK are the same build and dlopen the same name. +# Clear any previous build's files first so a version bump cannot leave a +# stale copy behind. + +for dest in "$LIBRARIES_DIR" "$LIBRARIES_SE2_DIR"; do + echo "==> Staging SDL3 into $dest" + find "$dest" -maxdepth 1 -name 'libSDL3.so*' -delete + install -m 0755 "$STAGE_LIB" "$dest/libSDL3.so" +done + +rm -f "$STAGE_LIB" echo echo "==> SDL3 $SDL3_VERSION available at $SDL3_PREFIX" PKG_CONFIG_PATH="$SDL3_PREFIX/lib/pkgconfig" pkg-config --modversion sdl3 +echo "==> Staged libSDL3.so into $LIBRARIES_DIR and $LIBRARIES_SE2_DIR" diff --git a/build.sh b/build.sh index 7f008aa..fab6812 100755 --- a/build.sh +++ b/build.sh @@ -13,7 +13,8 @@ # consumed alongside either of the above # # The SE1 archive's DXVK files are the patched build (Patches/dxvk/), shared -# byte-identically with the SE2 archive, and nothing is built twice. +# byte-identically with the SE2 archive, as is libSDL3.so — the library DXVK's +# WSI driver dlopens — and nothing is built twice. # Everything new with the SE2 port (vkd3d-proton, FMOD) ships in the SE2 # archive only; the Steam bits ship in their own archive so a Steamworks # update does not republish the game-specific payloads. @@ -21,27 +22,30 @@ # Pipeline (in order): # # 1. Scripts/build_ffmpeg.sh FFmpeg 8.1 (libav*.so / libsw*.so) -# 2. Scripts/build_dxvk.sh DXVK Native v2.7.1 + Patches/dxvk/ +# 2. Scripts/build_sdl3.sh SDL3 (libSDL3.so), staged into BOTH +# game staging trees; also supplies the +# headers the DXVK build compiles against +# 3. Scripts/build_dxvk.sh DXVK Native v2.7.1 + Patches/dxvk/ # (libdxvk_d3d11.so + libdxvk_dxgi.so) -# 3. Scripts/build_vkd3d_proton.sh vkd3d-proton (pinned commit) + +# 4. Scripts/build_vkd3d_proton.sh vkd3d-proton (pinned commit) + # Patches/vkd3d-proton/, staged straight # into Libraries-SE2/ -# 4. Scripts/build_openal.sh OpenAL Soft 1.25.2 (libopenal.so) -# 5. Scripts/build_steamworks_net.sh Steamworks.NET.dll, staged straight +# 5. Scripts/build_openal.sh OpenAL Soft 1.25.2 (libopenal.so) +# 6. Scripts/build_steamworks_net.sh Steamworks.NET.dll, staged straight # into Libraries-Steam/ -# 6. Shared-artefact copy: the patched DXVK files from +# 7. Shared-artefact copy: the patched DXVK files from # Libraries/ into Libraries-SE2/ -# 7. Vendor copy: libEOSSDK-Linux-Shipping.so -> SE1, +# 8. Vendor copy: libEOSSDK-Linux-Shipping.so -> SE1, # libsteam_api.so -> Steam # (proprietary, committed under Vendor/) -# 8. SE2 vendor copy: the FMOD runtime from Vendor/, staged +# 9. SE2 vendor copy: the FMOD runtime from Vendor/, staged # under the bare names (libfmod.so + # libfmodstudio.so) -# 9. License copy: Licenses/*.txt -> LICENSES/ (SE1); +# 10. License copy: Licenses/*.txt -> LICENSES/ (SE1); # DXVK + Licenses/se2/*.txt -> LICENSES/ (SE2); # Licenses/steam/*.txt -> LICENSES/ (Steam) -# 10. Final assertion: every expected artefact is present -# 11. Package: all three archives under dist/ +# 11. Final assertion: every expected artefact is present +# 12. Package: all three archives under dist/ # # The native wrapper libraries (libD3DCompiler.so, libHavok.so, # libRecastDetour.so, libVRageNative.so) are deliberately NOT part of this @@ -108,7 +112,7 @@ for arg in "$@"; do --no-package) DO_PACKAGE=0 ;; --only=*) ONLY="${arg#--only=}" ;; --skip=*) SKIP="${arg#--skip=}" ;; - -h|--help) sed -n '2,50p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; + -h|--help) sed -n '2,53p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; *) echo "ERROR: unknown arg: $arg" >&2; exit 2 ;; esac done @@ -121,7 +125,7 @@ fi # Reject unknown step names. Without this a typo (--only=steamworks_net with an # underscore) matches nothing, silently skips every build, and reports only # that staging is incomplete. -STEP_NAMES="ffmpeg dxvk vkd3d-proton openal steamworks-net" +STEP_NAMES="ffmpeg sdl3 dxvk vkd3d-proton openal steamworks-net" for spec in "$ONLY" "$SKIP"; do [ -n "$spec" ] || continue IFS=',' read -ra names <<< "$spec" @@ -177,7 +181,7 @@ echo "==> SE2 staging dir : $LIBRARIES_SE2_DIR" echo "==> Steam staging dir : $LIBRARIES_STEAM_DIR" echo "==> Output dir : $OUTPUT_DIR" -# ---- 1..5. per-dependency build scripts ------------------------------------ +# ---- 1..6. per-dependency build scripts ------------------------------------ run_step() { local name="$1"; shift @@ -195,12 +199,17 @@ run_step() { } run_step ffmpeg "$SCRIPTS_DIR/build_ffmpeg.sh" +# Before dxvk on purpose: the DXVK build needs SDL3's headers and pkg-config +# file, and invokes build_sdl3.sh itself if they are not there yet. Running it +# as its own step first makes the shipped artefact explicit (and skippable), +# and leaves that inner invocation a cached no-op. +run_step sdl3 "$SCRIPTS_DIR/build_sdl3.sh" run_step dxvk "$SCRIPTS_DIR/build_dxvk.sh" run_step vkd3d-proton "$SCRIPTS_DIR/build_vkd3d_proton.sh" run_step openal "$SCRIPTS_DIR/build_openal.sh" run_step steamworks-net "$SCRIPTS_DIR/build_steamworks_net.sh" -# ---- 6. shared artefacts: patched DXVK -> Libraries-SE2/ -------------------- +# ---- 7. shared artefacts: patched DXVK -> Libraries-SE2/ -------------------- # The patched DXVK libraries ship in BOTH archives but are built only once, # into Libraries/. Copy them into the SE2 staging tree. Skipped file-by-file # under --only / --skip filters, when the source files were not staged this @@ -226,7 +235,7 @@ for f in "${SHARED_FILES[@]}"; do fi done -# ---- 7. Vendor blobs -------------------------------------------------------- +# ---- 8. Vendor blobs -------------------------------------------------------- echo echo "############################################################" @@ -248,7 +257,7 @@ for spec in "libEOSSDK-Linux-Shipping.so:$LIBRARIES_DIR" \ echo " copied $blob -> ${dest##*/}/" done -# ---- 8. SE2 vendor blobs (FMOD) --------------------------------------------- +# ---- 9. SE2 vendor blobs (FMOD) --------------------------------------------- # The proprietary FMOD Engine runtime (login-gated download, committed under # Vendor/ like EOS and Steamworks). SE1 does not use FMOD, so it ships only # in the SE2 archive. The committed files keep their upstream SONAME names @@ -276,11 +285,12 @@ for name in libfmod libfmodstudio; do echo " copied $name.so.14 -> $name.so" done -# ---- 9. Licenses ------------------------------------------------------------ -# SE1 archive: every top-level Licenses/*.txt. SE2 archive: the shared DXVK -# licence plus the SE2-specific notices under Licenses/se2/. Steam archive: -# the notices under Licenses/steam/. The subdirectories exist precisely so -# the SE1 glob below does not pick their contents up. +# ---- 10. Licenses ------------------------------------------------------------ +# SE1 archive: every top-level Licenses/*.txt. SE2 archive: the notices for +# the artefacts shared with SE1 (DXVK, SDL3) plus the SE2-specific ones under +# Licenses/se2/. Steam archive: the notices under Licenses/steam/. The +# subdirectories exist precisely so the SE1 glob below does not pick their +# contents up. echo echo "############################################################" @@ -291,9 +301,10 @@ for f in "$LICENSES_SRC"/*.txt; do install -m 0644 "$f" "$LIBRARIES_DIR/LICENSES/$(basename "$f")" echo " copied $(basename "$f")" done -install -m 0644 "$LICENSES_SRC/DXVK-LICENSE.txt" \ - "$LIBRARIES_SE2_DIR/LICENSES/DXVK-LICENSE.txt" -echo " copied DXVK-LICENSE.txt (SE2)" +for f in DXVK-LICENSE.txt SDL3-LICENSE.txt SDL3-README.txt; do + install -m 0644 "$LICENSES_SRC/$f" "$LIBRARIES_SE2_DIR/LICENSES/$f" + echo " copied $f (SE2, shared with SE1)" +done for f in "$LICENSES_SRC"/se2/*.txt; do install -m 0644 "$f" "$LIBRARIES_SE2_DIR/LICENSES/$(basename "$f")" echo " copied $(basename "$f") (SE2)" @@ -304,7 +315,7 @@ for f in "$LICENSES_SRC"/steam/*.txt; do done shopt -u nullglob -# ---- 10. final assertion ---------------------------------------------------- +# ---- 11. final assertion ---------------------------------------------------- # Confirm every artefact every consumer expects is present. Missing files here # are far easier to debug than a cryptic failure inside a consumer's build. # @@ -321,6 +332,8 @@ EXPECTED_FILES=( # DXVK (patched, shared with the SE2 archive) libdxvk_d3d11.so libdxvk_dxgi.so + # SDL3 (shared with the SE2 archive; DXVK's WSI driver dlopens it) + libSDL3.so # OpenAL libopenal.so # Vendor @@ -334,6 +347,8 @@ EXPECTED_FILES=( LICENSES/OpenAL-Soft-NOTICES.txt LICENSES/OpenAL-Soft-README.txt LICENSES/README.txt + LICENSES/SDL3-LICENSE.txt + LICENSES/SDL3-README.txt ) EXPECTED_FILES_STEAM=( @@ -348,6 +363,8 @@ EXPECTED_FILES_SE2=( # DXVK (patched, identical to the SE1 archive's copy) libdxvk_d3d11.so libdxvk_dxgi.so + # SDL3 (identical to the SE1 archive's copy) + libSDL3.so # vkd3d-proton (patched) libvkd3d-proton-d3d12.so libvkd3d-proton-d3d12core.so # FMOD (vendor blobs staged under bare names) @@ -358,6 +375,8 @@ EXPECTED_FILES_SE2=( LICENSES/FMOD-EULA.txt LICENSES/FMOD-NOTICE.txt LICENSES/README.txt + LICENSES/SDL3-LICENSE.txt + LICENSES/SDL3-README.txt LICENSES/VKD3D-LGPL-2.1.txt LICENSES/vkd3d-proton-README.txt ) @@ -400,7 +419,7 @@ echo echo "==> All expected artefacts present in $LIBRARIES_STEAM_DIR" ( cd "$LIBRARIES_STEAM_DIR" && ls -lh | sed 's/^/ /' ) -# ---- 11. package ------------------------------------------------------------ +# ---- 12. package ------------------------------------------------------------ # Each archive mirrors its staging tree exactly: every library at the archive # root plus a LICENSES/ subdir, no symlinks, no version-suffixed filenames. # Consumers extract it straight into their own Libraries staging folder, so diff --git a/docs/architecture.md b/docs/architecture.md index c230095..872238f 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -33,6 +33,7 @@ for the PE-loader shims. | `libavcodec` / `libavformat` / `libavutil` / `libswresample` / `libswscale` | SE1 | FFmpeg 8.1, built from the upstream release tarball | | `libdxvk_d3d11.so`, `libdxvk_dxgi.so` | both | DXVK Native 2.7.1 + the `Patches/dxvk/` series, built once from the upstream git tag | | `libvkd3d-proton-d3d12.so`, `libvkd3d-proton-d3d12core.so` | SE2 | vkd3d-proton at a pinned commit + the `Patches/vkd3d-proton/` series | +| `libSDL3.so` | both | SDL3 3.4.12, built once from the upstream release tag; DXVK compiles against its headers and dlopens it at runtime | | `libopenal.so` | SE1 | OpenAL Soft 1.25.2, built from the upstream release tarball | | `Steamworks.NET.dll` | Steam | Built from a pinned commit of rlabrecque/Steamworks.NET | | `libEOSSDK-Linux-Shipping.so` | SE1 | Proprietary Epic blob, committed under `Vendor/` | @@ -44,7 +45,10 @@ Three policies shape the archive split: * **Patched libraries are built once and shipped as the same bytes everywhere they appear.** DXVK (see `Patches/dxvk/`) ships identically in - the SE1 and SE2 archives — one build to test, no variant drift. + the SE1 and SE2 archives — one build to test, no variant drift. The same + holds for `libSDL3.so`, which DXVK dlopens: shipping the exact SDL3 the + DXVK binaries were compiled against keeps the pair a matched set instead of + depending on whatever the host distribution has. * **Everything new with the SE2 port ships only in the SE2 archive.** vkd3d-proton and FMOD go to `se2-dependencies.tar.gz` alone, so SE1 consumers never download or ship libraries they cannot use. diff --git a/docs/building.md b/docs/building.md index 4a17eda..b6688fe 100644 --- a/docs/building.md +++ b/docs/building.md @@ -10,7 +10,7 @@ opening a pull request. On Debian / Ubuntu: ```bash -sudo apt install build-essential pkg-config make cmake curl tar git nasm patchelf binutils zlib1g-dev meson ninja-build glslang-tools libvulkan-dev mingw-w64-tools libpulse-dev libasound2-dev libpipewire-0.3-dev +sudo apt install build-essential pkg-config make cmake curl tar git nasm patchelf binutils zlib1g-dev meson ninja-build glslang-tools libvulkan-dev mingw-w64-tools libpulse-dev libasound2-dev libpipewire-0.3-dev libx11-dev libxext-dev libxcursor-dev libxi-dev libxfixes-dev libxrandr-dev libxrender-dev libxss-dev libxtst-dev libwayland-dev wayland-protocols libdecor-0-dev libxkbcommon-dev libegl-dev libdrm-dev libgbm-dev ``` You also need the **.NET SDK** (8.0 or newer, with the `net8.0` targeting pack) @@ -24,7 +24,9 @@ What each group is for: | `build-essential`, `pkg-config`, `make`, `nasm`, `zlib1g-dev` | FFmpeg (`nasm` provides the x86 SIMD assembler; `yasm` also works) | | `meson`, `ninja-build`, `glslang-tools`, `libvulkan-dev` | DXVK Native and vkd3d-proton | | `mingw-w64-tools` | `widl`, the Wine IDL compiler vkd3d-proton generates its COM headers with (`wine64-tools` works too; the build script shims whichever name is found) | -| `cmake` | the pinned SDL3 that DXVK compiles against, and OpenAL Soft | +| `cmake` | the pinned SDL3 that DXVK compiles against and that we ship, and OpenAL Soft | +| `libx11-dev`, `libxext-dev`, `libxcursor-dev`, `libxi-dev`, `libxfixes-dev`, `libxrandr-dev`, `libxrender-dev`, `libxss-dev`, `libxtst-dev` | SDL3's X11 video driver — see below | +| `libwayland-dev`, `wayland-protocols`, `libdecor-0-dev`, `libxkbcommon-dev`, `libegl-dev`, `libdrm-dev`, `libgbm-dev` | SDL3's Wayland and KMSDRM video drivers — see below | | `libpulse-dev`, `libasound2-dev`, `libpipewire-0.3-dev` | OpenAL's audio backends — see below | | `patchelf`, `binutils` | `DT_RUNPATH=$ORIGIN` patching and the `readelf` verification | | `curl`, `tar`, `git` | Fetching sources | @@ -56,7 +58,7 @@ Subsequent runs are near-instant when nothing changed — see | --- | --- | | `--clean` | Passes `--clean` to every sub-build: wipes cached source and build trees and rebuilds from scratch | | `--no-package` | Stages `build/Libraries/` but skips the tarball | -| `--only=ffmpeg,dxvk` | Runs only the listed sub-builds. Valid names: `ffmpeg`, `dxvk`, `vkd3d-proton`, `openal`, `steamworks-net`. An unknown name is rejected rather than silently skipping everything | +| `--only=ffmpeg,dxvk` | Runs only the listed sub-builds. Valid names: `ffmpeg`, `sdl3`, `dxvk`, `vkd3d-proton`, `openal`, `steamworks-net`. An unknown name is rejected rather than silently skipping everything | | `--skip=dxvk` | Runs every sub-build except the listed ones | | `-h`, `--help` | Prints the header comment | @@ -94,9 +96,10 @@ The scripts under `Scripts/` are standalone and can be run directly: ``` They stage into the same `build/Libraries/` folder (`build_vkd3d_proton.sh` -into `build/Libraries-SE2/` and `build_steamworks_net.sh` into -`build/Libraries-Steam/`, matching the archive each ships in). This is -usually the fastest way to iterate on one dependency. +into `build/Libraries-SE2/`, `build_steamworks_net.sh` into +`build/Libraries-Steam/`, and `build_sdl3.sh` into both game trees, matching +the archive each ships in). This is usually the fastest way to iterate on one +dependency. ## Caching @@ -108,18 +111,21 @@ rerun does no work: | FFmpeg | `build/ffmpeg-8.1.tar.xz` (download), `build/ffmpeg-8.1/` (source), `build/ffmpeg-8.1/_build/` (objects) | A changed `configure` flag set, tracked by a hash in `_build/.configure_flags`; otherwise an incremental `make` | | DXVK | `build/dxvk/` (clone), `build/dxvk.stamp` | A changed `DXVK_VERSION` **or** any change to the `Patches/dxvk/*.patch` series (its SHA-256 is part of the stamp) | | vkd3d-proton | `build/vkd3d-proton/` (clone), `build/vkd3d-proton.stamp` | A changed `VKD3D_PROTON_COMMIT` **or** any change to the `Patches/vkd3d-proton/*.patch` series | -| SDL3 | `build/SDL/` (clone), `build/sdl3-prefix/`, `build/sdl3.stamp` | A changed `SDL3_VERSION` | +| SDL3 | `build/SDL/` (clone), `build/sdl3-prefix/`, `build/sdl3.stamp` | A changed `SDL3_VERSION`, or a bumped `CONFIG_REV` in `build_sdl3.sh` (its cmake options are part of the stamp) | | OpenAL | `build/openal-soft-1.25.2.tar.bz2`, `build/openal-soft-1.25.2/`, `build/openal.stamp` | A changed `OPENAL_VERSION` | | Steamworks.NET | `build/Steamworks.NET/` (clone), `build/steamworks-net.stamp` | A changed `STEAMWORKS_NET_COMMIT` | -SDL3 is built only when DXVK actually rebuilds — it is a build-time-only -dependency for DXVK's headers and is never shipped. See -[dependencies.md](dependencies.md#sdl3-is-a-build-time-dependency). +Only SDL3's *build* is cached; its staging step runs every time, so a wiped +`build/Libraries*/` is repopulated without a rebuild. `build_dxvk.sh` also +invokes `build_sdl3.sh` itself (it needs the headers), which is a no-op when +the top-level `sdl3` step already ran. See +[dependencies.md](dependencies.md#sdl3-3412). To force specific work: ```bash rm build/dxvk.stamp # rebuild DXVK only +rm build/sdl3.stamp # rebuild SDL3 only rm build/vkd3d-proton.stamp # rebuild vkd3d-proton only rm -rf build/ffmpeg-8.1 # re-extract and reconfigure FFmpeg rm build/ffmpeg-8.1.tar.xz # re-download the FFmpeg tarball @@ -157,6 +163,13 @@ missing. Install `libpulse-dev`, `libasound2-dev` and `libpipewire-0.3-dev`. This failure is deliberate: without it the build would quietly produce an OpenAL with no audio backends. +**`SDL3 was configured without a video driver we ship for`** — the X11 or +Wayland development headers are missing, and SDL's CMake disabled that driver +without saying so. Install the two `libx*`/`libwayland*` groups from the table +above and rerun with `--clean`. Same rationale as the OpenAL check: SDL3 now +ships, so a library that loads fine and then fails at `SDL_CreateWindow` on +every user's machine has to be caught here. + **`: unexpected dep `** — an FFmpeg configure flag stopped suppressing something and the library now depends on a host package. Do not just widen the allow-list: find the flag that regressed, because that diff --git a/docs/consuming.md b/docs/consuming.md index 85b6aa5..06388dc 100644 --- a/docs/consuming.md +++ b/docs/consuming.md @@ -65,7 +65,7 @@ LINUX_DEPENDENCIES_TAG=v1.0.7 ./build.sh ``` Scripts/fetch_linux_dependencies.sh -> se1-dependencies.tar.gz (FFmpeg, - DXVK, OpenAL, EOS, LICENSES/) + + DXVK, SDL3, OpenAL, EOS, LICENSES/) + steam-dependencies.tar.gz (Steamworks.NET.dll, libsteam_api.so) Scripts/fetch_native_wrappers.sh -> libD3DCompiler.so, libHavok.so, @@ -83,17 +83,37 @@ Pulsar's own `Scripts/build_ffmpeg.sh`, `build_dxvk.sh` and `Scripts/Licenses/`. A clean build no longer spends roughly fifteen minutes compiling FFmpeg and DXVK. +### SDL3 now comes from the bundle + +`libSDL3.so` is new in the SE1 archive (and the SE2 one). Pulsar already +preloads `libSDL3.so` — `Legacy/Loader/NativeLibraryPreloader.cs` — because +DXVK's SDL3 WSI driver `dlopen`s `libSDL3.so.0` and Pulsar sets +`DXVK_WSI_DRIVER=SDL3`. What changes is only *which* file that preload picks +up: the bundled one next to the other libraries, instead of whatever SDL3 the +host distribution happens to provide. + +That preload is not optional any more. The shipped file is named +`libSDL3.so`, while DXVK asks for the SONAME `libSDL3.so.0`; the two meet +because the preloaded object satisfies the later `dlopen` by SONAME. Without +the preload, DXVK falls back to searching the system for `libSDL3.so.0` — +which on many distributions is not installed at all. It is the same +arrangement `libopenal.so` already relies on. + +Nothing else changes for a consumer that stages the whole archive: the new +file arrives with the rest, and `LICENSES/SDL3-LICENSE.txt` and +`LICENSES/SDL3-README.txt` come with it. + ## Magnetar Magnetar is headless, so from the SE1 archive it takes only -`libEOSSDK-Linux-Shipping.so` — FFmpeg, DXVK and OpenAL stay unused in +`libEOSSDK-Linux-Shipping.so` — FFmpeg, DXVK, SDL3 and OpenAL stay unused in `build/linux-deps/` and never reach the bundle — and it takes the whole Steam archive (`Steamworks.NET.dll` + `libsteam_api.so`). Its `build.sh` fetches the releases and then stages only the files it wants into `build/Libraries/`. That applies to the licence texts too. Copying `LICENSES/` wholesale would put -FFmpeg, DXVK and OpenAL attribution into a bundle containing none of those +FFmpeg, DXVK, SDL3 and OpenAL attribution into a bundle containing none of those libraries, and the archive's own `LICENSES/README.txt` index would list files that are not there. Magnetar therefore stages just the three notices covering what it ships — `Steamworks.NET-LICENSE.txt`, `Steam-NOTICE.txt` and @@ -122,8 +142,9 @@ exactly which path each file came from. ## The SE2 archive The Space Engineers 2 Linux port consumes `se2-dependencies.tar.gz`, -which carries the patched DXVK build (byte-identical to the SE1 archive's -copy), the patched vkd3d-proton build, and the FMOD Engine runtime — see +which carries the patched DXVK build and `libSDL3.so` (both byte-identical +to the SE1 archive's copies), the patched vkd3d-proton build, and the FMOD +Engine runtime — see [release-archive.md](release-archive.md#layout-se2-archive) for the exact contents. It follows the same fetch pattern (same release, second asset name) and the same rules: extract with symlink-preserving `tar`, keep `LICENSES/` @@ -131,13 +152,16 @@ next to the binaries. The SE2 native wrappers (`libVRage.*.Native.so`) come from the linux-native-wrappers release, fetched separately — the same split as for SE1. -Two SE2-specific notes: +Three SE2-specific notes: * The FMOD blobs ship **unmodified** (no patchelf) under the bare names, so `libfmodstudio.so`'s internal `NEEDED` entry still references the upstream SONAME `libfmod.so.14`, which no shipped file carries. **Load `libfmod.so` (globally) before `libfmodstudio.so`** — the already-loaded library then satisfies the reference by SONAME. +* `libSDL3.so` needs the same preload as in SE1: load it from the bundle + before DXVK initialises, so DXVK's `dlopen("libSDL3.so.0")` resolves to the + bundled copy by SONAME rather than searching the host. * The asset exists only from the release that introduced it onward; a fetch script should fail with a clear message when the asset is missing from an older pinned tag. diff --git a/docs/dependencies.md b/docs/dependencies.md index 9fa469c..3aa4e56 100644 --- a/docs/dependencies.md +++ b/docs/dependencies.md @@ -5,14 +5,14 @@ pinned. To change any of these, see [maintenance.md](maintenance.md). ## Summary -Every library is built (or staged) **once**. The SE1 archive keeps exactly -its pre-SE2 library set; its DXVK files are the patched build, shared -byte-identically with the SE2 archive. +Every library is built (or staged) **once**. The DXVK files and `libSDL3.so` +are shared byte-identically between the two game archives. | Dependency | Archives | Version / pin | Licence | Built by | | --- | --- | --- | --- | --- | | FFmpeg | SE1 | 8.1 (release tarball) | LGPL-2.1-or-later | `Scripts/build_ffmpeg.sh` | | DXVK Native + `Patches/dxvk/` series | both | tag `v2.7.1` + patch-series hash | zlib | `Scripts/build_dxvk.sh` | +| SDL3 | both | tag `release-3.4.12` | zlib | `Scripts/build_sdl3.sh` | | vkd3d-proton + `Patches/vkd3d-proton/` series | SE2 | commit `3dfc6f07…` + patch-series hash | LGPL-2.1 | `Scripts/build_vkd3d_proton.sh` | | OpenAL Soft | SE1 | 1.25.2 (release tarball) | LGPL-2.0-or-later | `Scripts/build_openal.sh` | | Steamworks.NET | Steam | commit `68e72a49caf03a07722d4d4b471bbc7c0785f80b` | MIT | `Scripts/build_steamworks_net.sh` | @@ -20,9 +20,9 @@ byte-identically with the SE2 archive. | Steamworks SDK | Steam | vendor blob (manual) | proprietary (Valve) | committed under `Vendor/` | | FMOD Engine | SE2 | vendor blob (manual), 2.03.11 to match the game | proprietary (Firelight) | committed under `Vendor/` | -`Scripts/build_sdl3.sh` builds SDL3 3.4.12 as well, but nothing from it is -shipped — it exists only so DXVK has headers to compile against. See -[SDL3 is a build-time dependency](#sdl3-is-a-build-time-dependency). +SDL3 has a double role: it supplies the headers DXVK compiles against *and* +ships as `libSDL3.so`, because DXVK's window-system integration dlopens it at +runtime. See [SDL3](#sdl3-3412). --- @@ -131,35 +131,6 @@ so an upstream rearrangement (`usr/lib64/`, a multiarch subdirectory) does not silently break staging. They then get the same `DT_RUNPATH=$ORIGIN` treatment as the FFmpeg libraries. -### SDL3 is a build-time dependency - -DXVK's meson build refuses to configure without SDL3, SDL2 or GLFW, because -its window-system integration needs one of them. Pulsar sets -`DXVK_WSI_DRIVER=SDL3` and preloads `libSDL3.so` from the user's system, so it -is specifically the SDL3 backend we need. - -SDL3 is a *headers-only* dependency here. DXVK takes it as -`lib_sdl3.partial_dependency(compile_args: true, includes: true)` and dlopens -`libSDL3.so.0` at runtime, so the produced `libdxvk_*.so` have no `NEEDED` -entry for SDL3 and nothing SDL-related ships in the archive. - -Ubuntu 24.04 — the pinned CI runner — has no `libsdl3-dev` package, and a -developer machine may have any SDL3 version or none. `Scripts/build_sdl3.sh` -therefore builds a pinned SDL3 (**3.4.12**) into `build/sdl3-prefix/` and -`build_dxvk.sh` puts that on `PKG_CONFIG_PATH`, so CI and local builds compile -against identical headers. - -That SDL3 is configured with `SDL_UNIX_CONSOLE_BUILD=ON`, which skips SDL's -"could not find X11 or Wayland development libraries" hard error. The check -guards against accidentally building an SDL that cannot open a window — which -does not matter here, since nothing links or loads this SDL and the public -headers are the same either way. It saves installing the entire X11 and -Wayland dev stack on the runner to build a library that is then discarded. - -Both choices were verified to be neutral: DXVK built against the pinned, -console-only 3.4.12 is byte-identical to DXVK built against a full system -SDL3 3.5.0. - ### The patch series The series under [Patches/dxvk/](../Patches/dxvk/) is applied onto the @@ -186,6 +157,54 @@ Mechanics worth knowing: --- +## SDL3 3.4.12 + +DXVK's meson build refuses to configure without SDL3, SDL2 or GLFW, because +its window-system integration needs one of them. Pulsar sets +`DXVK_WSI_DRIVER=SDL3`, so it is specifically the SDL3 backend we need. + +At build time SDL3 is a *headers-only* dependency. DXVK takes it as +`lib_sdl3.partial_dependency(compile_args: true, includes: true)`, so the +produced `libdxvk_*.so` have no `NEEDED` entry for SDL3. At runtime they +`dlopen("libSDL3.so.0")` instead — and that call has to find something. + +Leaving that to the host was the previous arrangement and it is not +dependable: SDL3 is new enough that plenty of otherwise current distributions +ship no `libSDL3` at all, and the ones that do ship whatever version they +froze. Ubuntu 24.04 — the pinned CI runner — has no `libsdl3-dev` package +either. So `Scripts/build_sdl3.sh` builds a pinned SDL3 (**3.4.12**) into +`build/sdl3-prefix/`, `build_dxvk.sh` puts that on `PKG_CONFIG_PATH`, and the +same build is staged into **both** game archives as `libSDL3.so`. DXVK and the +SDL3 it was compiled against are then always a matched set. + +**Produces:** `libSDL3.so` — the real file under the bare name, with +`DT_RUNPATH=$ORIGIN`, in the SE1 and SE2 archives (identical bytes). The +binary is unmodified upstream SDL3; no patch series applies to it. + +**The SONAME matters here.** The shipped file is `libSDL3.so`, but DXVK +dlopens `libSDL3.so.0` — the SONAME inside the binary. Those only meet +because the consumer preloads the file first, after which the dlopen resolves +against the already-loaded object by SONAME. It is exactly the arrangement the +bundled OpenAL relies on, and `build_sdl3.sh` asserts the SONAME so an +upstream major bump cannot leave the bundled copy silently unused. + +**Video drivers are required, not autodetected.** Because the library now +ships, it has to be able to open a window. SDL's CMake enables X11 and Wayland +by default but turns each off *silently* when its development headers are +missing — the same trap as OpenAL's audio backends, and the reason the old +`SDL_UNIX_CONSOLE_BUILD=ON` shortcut (which suppressed SDL's own "no X11 or +Wayland" error) is gone. SDL has no `REQUIRE_*` equivalent, so `build_sdl3.sh` +greps the generated `SDL_build_config.h` for `SDL_VIDEO_DRIVER_X11`, +`SDL_VIDEO_DRIVER_WAYLAND` and `SDL_VIDEO_VULKAN` and fails the build if any is +absent. The X11 and Wayland dev packages are correspondingly part of the +toolchain now; see [building.md](building.md). + +Those display libraries are *dlopened* by SDL at runtime rather than linked +(`SDL_X11_SHARED` / `SDL_WAYLAND_SHARED`, both on), so the shipped +`libSDL3.so` still has no `NEEDED` entry beyond glibc and picks up whichever +display server and audio stack the host provides. `build_sdl3.sh` verifies +that with the same `ldd` allow-list the FFmpeg and OpenAL builds use. + ## vkd3d-proton (patched, SE2 archive only) **Produces:** `libvkd3d-proton-d3d12.so` and `libvkd3d-proton-d3d12core.so`, diff --git a/docs/maintenance.md b/docs/maintenance.md index 931bade..679309f 100644 --- a/docs/maintenance.md +++ b/docs/maintenance.md @@ -66,14 +66,29 @@ Patches are applied with `git apply` in byte-wise filename order. ## Bumping SDL3 -SDL3 is not shipped — it only supplies headers for the DXVK build. Set -`SDL3_VERSION` in `Scripts/build_sdl3.sh` (the upstream tag is -`release-$SDL3_VERSION`) and rebuild DXVK. - -A bump is usually only needed when a newer DXVK requires newer SDL3 headers. -Because DXVK dlopens `libSDL3.so.0` at runtime rather than linking it, the -version built here does not constrain what users need installed — but building -against much newer headers than the SDL3 in the wild is still worth avoiding. +SDL3 both supplies headers for the DXVK build and ships as `libSDL3.so` in +**both game archives**, so a bump changes what users run, not just what DXVK +compiles against. Test accordingly. + +1. Set `SDL3_VERSION` in `Scripts/build_sdl3.sh` (the upstream tag is + `release-$SDL3_VERSION`). +2. Build. If upstream bumps the SONAME past `libSDL3.so.0`, the script fails + with an explicit message; update `EXPECTED_SONAME` there — and check the + consumers, because DXVK dlopens the SONAME and the shipped file is named + `libSDL3.so`, so the two only meet through the consumer's preload. +3. The shipped file name stays bare, so no expected-file list changes. +4. Refresh `Licenses/SDL3-README.txt` — it names the version and the tag — + and re-copy `Licenses/SDL3-LICENSE.txt` from upstream's `LICENSE.txt` if + the copyright line moved. + +If you change the cmake options in `build_sdl3.sh` rather than the version, +bump `CONFIG_REV` in the same file. It is part of the cache stamp, and without +a bump an existing `build/` tree re-stages the old, differently configured +library instead of rebuilding. + +Since DXVK dlopens SDL3 rather than linking it, the version built here does +not constrain the host — but it is now the copy users actually get, so prefer +a released tag over a development one. ## Bumping OpenAL Soft diff --git a/docs/release-archive.md b/docs/release-archive.md index 531c5f0..e89ef5a 100644 --- a/docs/release-archive.md +++ b/docs/release-archive.md @@ -15,9 +15,9 @@ steam-dependencies.tar.gz Steamworks.NET.dll + libsteam_api.so, consumed alongside either game archive ``` -All are gzip-compressed tars built by `build.sh` (roughly 24 MB, 7 MB and -250 kB). The patched DXVK binaries are **byte-identical across the two game -archives** — built once, copied into both staging trees. +All are gzip-compressed tars built by `build.sh` (roughly 26 MB, 8 MB and +250 kB). The patched DXVK binaries and `libSDL3.so` are **byte-identical +across the two game archives** — built once, staged into both trees. Download the latest with (same pattern for the other two assets): @@ -43,6 +43,9 @@ libswresample.so libswscale.so libdxvk_d3d11.so (patched, see Patches/dxvk/) libdxvk_dxgi.so (patched) +libSDL3.so (unmodified upstream; DXVK's WSI driver + dlopens it — see the SONAME note below) +libopenal.so libEOSSDK-Linux-Shipping.so LICENSES/DXVK-LICENSE.txt LICENSES/EOS-NOTICE.txt @@ -52,6 +55,8 @@ LICENSES/OpenAL-Soft-LGPL-2.0.txt LICENSES/OpenAL-Soft-NOTICES.txt LICENSES/OpenAL-Soft-README.txt LICENSES/README.txt +LICENSES/SDL3-LICENSE.txt +LICENSES/SDL3-README.txt ``` ### Guarantees @@ -69,6 +74,13 @@ LICENSES/README.txt other next to themselves and no `LD_LIBRARY_PATH` manipulation is needed. * **The FFmpeg libraries depend only on glibc and libz.** Verified by an `ldd` allow-list at build time. +* **`libSDL3.so` is loaded by file name, resolved by SONAME.** The file is + named `libSDL3.so`; DXVK's SDL3 WSI driver `dlopen`s `libSDL3.so.0`, the + SONAME inside the binary. The consumer must therefore **load + `libSDL3.so` from the bundle before DXVK initialises** — the already-loaded + object then satisfies DXVK's request, and the bundled SDL3 is used instead + of whatever the host has (or does not have). Consumers that already preload + it need no change; the file simply comes from the bundle now. * **The archive is byte-reproducible** for a given set of inputs: `tar` is invoked with `--sort=name`, a fixed `--mtime`, and numeric owner 0:0. * **`libD3DCompiler.so`, `libHavok.so`, `libRecastDetour.so` and @@ -86,6 +98,7 @@ extraction into the consumer's staging directory is the whole staging step. ``` libdxvk_d3d11.so identical to the SE1 archive's copy libdxvk_dxgi.so identical to the SE1 archive's copy +libSDL3.so identical to the SE1 archive's copy libvkd3d-proton-d3d12.so patched, see Patches/vkd3d-proton/ (SE2 only) libvkd3d-proton-d3d12core.so patched (SE2 only) libfmod.so FMOD Core API runtime (unmodified vendor blob) @@ -94,10 +107,15 @@ LICENSES/DXVK-LICENSE.txt LICENSES/FMOD-EULA.txt LICENSES/FMOD-NOTICE.txt LICENSES/README.txt +LICENSES/SDL3-LICENSE.txt +LICENSES/SDL3-README.txt LICENSES/VKD3D-LGPL-2.1.txt LICENSES/vkd3d-proton-README.txt ``` +The SDL3 SONAME note from the SE1 section applies here too: the SE2 port must +load `libSDL3.so` from the bundle before DXVK initialises. + The FMOD blobs are shipped **unmodified** (no patchelf), matching how the EOS and Steamworks blobs are handled — only their file names are the bare ones. That means `libfmodstudio.so`'s internal `NEEDED` entry still @@ -140,6 +158,7 @@ maintained forever. | --- | :---: | :---: | | FFmpeg libraries | yes | no | | DXVK libraries | yes | no | +| `libSDL3.so` | yes | no | | OpenAL library | yes | no | | `libEOSSDK-Linux-Shipping.so` | yes | yes | | `LICENSES/` | yes | the subset covering what it ships |