Files
tabnulandClaude Opus 5.5 0544c7c74c DLAA: NVIDIA DLSS at native resolution on the game's own motion vectors
The sky pass (ps 0x5fce13a4) writes the game's motion vectors; DLSS runs before the first post
pass (cs 0x252f2091) with a sub-pixel jitter on the scene draws and is blitted back into the
scene. Foliage and thin edges stop shimmering. The GoW3 scene path is skipped for DLSS.

- Works under the resolution patch (jitter depth = the sky pass depth size).
- tlg.ini dlaa / aa_label, TLG_DLAA; menu Image > Anti-aliasing (greyed out without DLSS);
  F8 toggles DLAA / game SMAA with a short label; launcher checkbox; on by default.
- Packaging: build_dlss.sh picks the Visual Studio version, package.sh ships tlg_dlss.dll,
  nvngx_dlss.dll and NVIDIA's license when present (TLG_REQUIRE_DLSS=1 makes it required);
  CI builds the bridge from NVIDIA/DLSS v310.9.16. NVIDIA attribution in README-player.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-21 09:14:57 +02:00

6.0 KiB
Raw Permalink Blame History

tlg on Windows

The port builds and runs natively on 64-bit Windows 10 (1903+) / 11 with the same sources as on Linux. Players: see README-Windows.txt (it ships in the package).

Build

Install MSYS2 (winget install MSYS2.MSYS2), open the MSYS2 CLANG64 shell and install the toolchain and libraries (the counterpart of shell.nix):

pacman -S --needed git patch \
  mingw-w64-clang-x86_64-{toolchain,cmake,ninja,pkgconf,python} \
  mingw-w64-clang-x86_64-{vulkan-headers,vulkan-loader,vulkan-memory-allocator,glslang} \
  mingw-w64-clang-x86_64-{sdl3,fmt,boost,robin-map,xxhash,ffmpeg,zydis} \
  mingw-w64-clang-x86_64-{spirv-tools,spirv-headers,spirv-cross}

magic_enum, miniz and xbyak (not packaged by MSYS2) are submodules under gpu/third_party/. Then, in the repository:

git submodule update --init --recursive
bash build.sh                      # out/tlg-probe.exe, out/tlg-gpu-capabilities.exe
TLG_GAME_DIR=F:/path/to/CUSA01623 python run.py
python launcher/tlg_launcher_win.py
bash packaging/windows/package.sh  # dist/tlg-windows-x86_64.zip: The Last Guardian.exe + DLLs

run.py is the Windows run.sh (same variables; works from a plain Windows Python too). The package freezes the launcher with PyInstaller (a private venv in out/pyenv, from a python.org Python) into The Last Guardian.exe, which also runs run.py (--run), the preparation scripts (--script) and the game without the window (--play): players need no Python. Play The Last Guardian.exe (packaging/windows/play.c) is a small native program that runs The Last Guardian.exe --play from its folder and waits for it, for players who want to start the game directly with the saved settings. patches.py applies the built-in patches only to the game build they were checked against (CUSA01623 01.02, by eboot.bin sha256; TLG_FORCE_PATCHES=1 overrides); other builds run unpatched. From a source tree it adds C:\msys64\clang64\bin (or $MSYS2_ROOT) to PATH for the DLLs. bash tools/fetch_fsr4_assets.sh works in the CLANG64 shell as on Linux.

DLAA (optional, NVIDIA RTX): DLSS_SDK_ROOT=<checkout of github.com/NVIDIA/DLSS, tag v310.9.16> bash packaging/windows/build_dlss.sh builds tlg_dlss.dll (gpu/dlss_bridge, the only code that uses the NVIDIA SDK, compiled with MSVC 2022 or 2026) and copies it with nvngx_dlss.dll and NVIDIA-DLSS-LICENSE.txt next to out/tlg-probe.exe; package.sh ships all three when present (TLG_REQUIRE_DLSS=1 makes their absence an error, as in CI, which fetches the SDK itself). The renderer loads the bridge at run time (vk_dlss.cpp). DLSS runs at native resolution (DLAA) on the game's own motion vectors (sky pass), with a sub-pixel jitter on the scene draws, before the first post pass. Without the DLLs, or on a GPU without DLSS, the menu greys the DLAA setting out and the game keeps its own anti-aliasing. Settings: dlaa in tlg.ini (menu: Image › Anti-aliasing; F8 toggles in game), TLG_DLAA=0, TLG_DLSS=0.

The NVIDIA DLSS SDK is NVIDIA's proprietary software under its own license (NVIDIA-DLSS-LICENSE.txt), not under this repository's license; only the NVIDIA binaries ship, and the SDK sources are not part of this repository.

The launcher texts are written in English with the Russian beside them; the other languages are in launcher/tlg_lang.py (one list per language in the order of KEYS; a missing or empty text falls back to English). The icon (launcher/tlg.ico/.png) is drawn by packaging/windows/make_icon.py and built into God of War III.exe, tlg-probe.exe (the game window) and tlg-gpu-capabilities.exe.

What is different from Linux

Linux Windows
mov rax, fs:[0] rewritten to gs:[0], GS base set with arch_prctl GS is the TEB and cannot move. The loader rewrites the displacement to gs:[0x1480 + 8*slot] (the TEB's TlsSlots[slot], same 9-byte instruction) and each guest thread keeps its TCB in that TLS slot. The slot is claimed first thing in main (Vulkan drivers take many).
One memfd for direct + flexible memory, mmap(MAP_FIXED) views A pagefile-backed section; the PS4 range 64 GiB–1 TiB is reserved as one placeholder and views replace placeholders (VirtualAlloc2/MapViewOfFile3, as in shadPS4). Partial unmaps split views and restore page protections (GPU tracking).
-no-pie, heap below 1 TiB --disable-high-entropy-va: heaps and thread stacks stay low and out of the PS4 range.
SIGSEGV handler, sigsetjmp recovery Vectored exception handler; tlg_setjmp/tlg_longjmp (src/compat_win.c) restore all Win64 callee-saved registers without SEH unwinding (guest frames have no unwind data; clang's __builtin_longjmp restores a wrong frame pointer on Win64).
Guest thread stacks from runtime_low_map Windows allocates them, fully committed (pthread_attr_setstacksize): guest code has no stack probes and would skip the guard page. The main thread stack is 16 MiB committed.
glibc nanosleep, monotonic condition variables High-resolution waitable timers (compat_sleep_ns; winpthreads' sleep rounds to 15.6 ms); semaphore deadlines use CLOCK_REALTIME (winpthreads rejects monotonic condition variables).
libtlggpu.so libtlggpu.a, linked into tlg-probe.exe by CMake (gpu/CMakeLists.txt), with a manifest for UTF-8 paths and long paths.
execlp bash run.sh (in-game restart) run.py puts its command line in TLG_RESTART_COMMAND; the new launch waits for the old process (--after PID). The launcher keeps the game in a job object.
Mod overlay with symlinks Symlinks when allowed (Developer Mode), else junctions for folders and hard links for files.

Diagnostics added on the way: TLG_FRAME_DUMP_TRIGGER=<file> writes the guest display buffer and the presented image of the next frame to TLG_DUMP_DIR (raw 32-bit pixels), which also works for menus and movies.

Not ported: the watchdog/--timeout thread dumps (Linux signals), userfaultfd tracking (TLG_UFFD), MangoHud, PGO (GCC flags) and the tests/ programs (several use Linux-only APIs).