mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 17:09:57 -04:00
Include NVIDIA's pinned DLSS library for Linux in the AppImage, and place it beside the built game.
On Linux the game looks for libnvidia-ngx-dlss.so.<version> beside the executable, and the driver brings only NGX's core, so the DLSS modes did nothing in every Linux build. tools/dlss_pin.py pins the Linux library of the DLSS version it pins for Windows, with its own size and SHA-256; tools/make_appimage.py puts it in the kit's bin/ with its notice (downloaded from NVIDIA's DLSS SDK repository and checked, or --dlss; --no-dlss leaves it out), and tools/make_game.py copies it beside the game on Linux as it copies the DLL on Windows, keeping a library the player put there. Pull request #297 by mooki000, taken as it is; the builder's description is merged with the button art option that came with the release workflow. On Windows place_dlss does what it did. No NVIDIA file is in the repository. tests/launcher_test.py passes with its two new tests (112 tests, Python 3.12 on Windows); the AppImage was not built here. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
@@ -123,6 +123,7 @@ folder's `licenses/` and in each component's own folder.
|
||||
| zlib 1.4 (MSYS2 `mingw-w64-ucrt-x86_64-zlib`), static library | https://zlib.net, Copyright (C) Jean-loup Gailly and Mark Adler | zlib | linked into `NX2navi-Setup.exe` (its unzip) |
|
||||
| mingw-w64 CRT startup code and winpthreads 14.0.0, GCC 16.2.0's `libgcc` (MSYS2 packages), static | https://www.mingw-w64.org, https://gcc.gnu.org | mingw-w64: ZPL-2.1 / MIT / public domain; libgcc: GPL-3.0 with the GCC Runtime Library Exception | linked into `NX2navi.exe` and `NX2navi-Setup.exe` |
|
||||
| NVIDIA DLSS 310.9.1 (`nvngx_dlss.dll`, NVIDIA's own build, unmodified; pinned with its size and sha256 in `tools/dlss_pin.py`) | https://github.com/NVIDIA/DLSS @ `v310.9.1` (`lib/Windows_x86_64/rel/nvngx_dlss.dll`), Copyright NVIDIA Corporation | NVIDIA RTX SDKs License (https://github.com/NVIDIA/DLSS/blob/v310.9.1/LICENSE.txt); licensed for systems with NVIDIA GPUs only | `app/<version>/bin/` with `nvngx_dlss-NOTICE.txt`, also in the build kit's `bin/`; `tools/make_game.py` copies both beside the built game, where NGX loads the library (`runtime/vk_dlaa.c`) |
|
||||
| NVIDIA DLSS 310.9.1 for Linux (`libnvidia-ngx-dlss.so.310.9.1`, NVIDIA's own build, unmodified; pinned with its size and sha256 in `tools/dlss_pin.py`) | https://github.com/NVIDIA/DLSS @ `v310.9.1` (`lib/Linux_x86_64/rel/libnvidia-ngx-dlss.so.310.9.1`), Copyright NVIDIA Corporation | NVIDIA RTX SDKs License (https://github.com/NVIDIA/DLSS/blob/v310.9.1/LICENSE.txt), the one licence of the repository's Windows and Linux libraries; licensed for systems with NVIDIA GPUs only | the Linux build kit's `bin/` (the AppImage of `tools/make_appimage.py`, unless made with `--no-dlss`) with `libnvidia-ngx-dlss-NOTICE.txt`; `tools/make_game.py` copies both beside the built game, where NGX loads the library (`runtime/vk_dlaa.c`) |
|
||||
| NVIDIA Streamline 2.14.16 runtime (`sl.interposer.dll`, `sl.common.dll`, `sl.dlss_g.dll`, `sl.pcl.dll`, `sl.reflex.dll`, NVIDIA's own builds, unmodified) and NVIDIA DLSS Frame Generation 310.9.16 (`nvngx_dlssg.dll`, as the Streamline SDK ships it) | https://github.com/NVIDIA-RTX/Streamline, SDK release 2.14.16 (`bin/x64/`), Copyright NVIDIA Corporation | Streamline: MIT; `nvngx_dlssg.dll`: NVIDIA RTX SDKs License, licensed for systems with NVIDIA GPUs only | `app/<version>/bin/dlssg/` with both licence texts and `dlssg-NOTICE.txt` (each file's sha256), when the release is made with `tools/make_launcher.py --dlssg`, and the build kit's `bin/dlssg/` with `tools/make_kit.py --dlssg` (the SDK release pinned with its size and sha256 and each file's sha256 in `tools/streamline_pin.py`); `tools/make_game.py` copies them beside the built game, where `nx2navi_dlssg.dll` (this repository's `runtime/dlssg/`, built with the Streamline SDK's MIT headers) loads them (`runtime/vk_dlssg.c`) |
|
||||
| Button prompt art: the 128x128 PNGs of the Input Prompts Pack V7 (`XGamepad`, `P5Gamepad` and `Keyboard_Mouse` styles), unmodified | the pack as licensed to the repository owner; never in this repository | the pack's licence, which allows redistributing these pictures in NX2navi's releases (confirmed by the repository owner, 2026-11-09) | `app/<version>/prompt-art/` and the build kit's `prompt-art/` when made with `--prompt-art` (`tools/prompt_art.py`); `tools/pad_prompts.py` draws the game's Xbox, PlayStation and keyboard and mouse prompts from them on the player's PC |
|
||||
|
||||
|
||||
@@ -114,6 +114,15 @@ AMD FSR 3.1 loads AMD's `amd_fidelityfx_vk.dll`, which only the Windows build ca
|
||||
FSR 3.1 upscaling nor FSR 3.1 frame generation, and `taa = fsr3` or `framegen = fsr3` in `nx2navi.ini` leave the
|
||||
game's own scaling and swapchain as they are (the log says so).
|
||||
|
||||
NVIDIA DLSS (RTX cards) needs the driver's own `libnvidia-ngx.so.1` on the library path (the proprietary driver's
|
||||
utilities package, `nvidia-utils` on Arch) and NVIDIA's DLSS library `libnvidia-ngx-dlss.so.<version>` beside
|
||||
`ocarina`. The AppImage's kit has the library in `bin/` with `libnvidia-ngx-dlss-NOTICE.txt`, which names its licence,
|
||||
and the build copies both beside `ocarina` (`tools/make_appimage.py --no-dlss` leaves it out). From a source checkout,
|
||||
or to use another DLSS version, put the library there yourself: NVIDIA publishes it as
|
||||
`lib/Linux_x86_64/rel/libnvidia-ngx-dlss.so.<version>` in [its DLSS repository](https://github.com/NVIDIA/DLSS), tagged
|
||||
`v<version>`, and `tools/dlss_pin.py` names the version the kit carries. Later builds keep a library they did not
|
||||
place. Without it DLSS does nothing on Linux.
|
||||
|
||||
`tools/make_appimage.py` packs this kit into one `NX2navi-Linux-<version>-x86_64.AppImage` with its own Python, for
|
||||
players without a source checkout: it checks for the packages above, installs zbic with pip on its first run, picks
|
||||
`--jobs` from the memory, builds into `~/Games/Ocarina` (or the folder given after the dump) and writes `play.sh`
|
||||
@@ -138,7 +147,8 @@ beside the game.
|
||||
`py -I tools\make_game.py --rebuild --out "C:\Games\Ocarina"` to draw them again (it skips everything else).
|
||||
- The build copies NVIDIA's DLSS library `nvngx_dlss.dll` (with `nvngx_dlss-NOTICE.txt`, which names its licence)
|
||||
and FSR 3.1's `amd_fidelityfx_vk.dll` beside `ocarina.exe` from the kit's `bin\`. To use another DLSS version, put
|
||||
your own `nvngx_dlss.dll` there: later builds keep it.
|
||||
your own `nvngx_dlss.dll` there: later builds keep it. On Linux the library is `libnvidia-ngx-dlss.so.<version>`
|
||||
(see "Linux").
|
||||
- A kit with `bin\dlssg\` (made with `tools/make_kit.py --dlssg`) also brings DLSS Frame Generation on GeForce RTX 40
|
||||
and 50 cards: the build copies `nx2navi_dlssg.dll`, NVIDIA Streamline's runtime, `nvngx_dlssg.dll` and their licence
|
||||
texts beside `ocarina.exe`, and Frame generation in the Screen menu then offers DLSS too.
|
||||
|
||||
@@ -1712,6 +1712,119 @@ class MakeGameTest(Tmp):
|
||||
self.assertEqual(self.mg.notice_sha256(self.tmp / 'n.txt'), dlss_pin.SHA256)
|
||||
self.assertEqual((dlss_pin.NAME, dlss_pin.NOTICE), (self.mg.DLSS_DLL, self.mg.DLSS_NOTICE))
|
||||
|
||||
def test_dlss_library_beside_the_game_on_linux(self):
|
||||
"""The kit's libnvidia-ngx-dlss.so.<version> and its notice go beside the game. The name carries the DLSS
|
||||
version, so a kit with another version replaces the file an earlier build placed under its old name, and a
|
||||
library the player put there, of any version, is kept. Each platform places only its own library."""
|
||||
from unittest import mock
|
||||
kit, out = self.tmp / 'kit', self.tmp / 'game'
|
||||
(kit / 'bin').mkdir(parents=True)
|
||||
out.mkdir()
|
||||
note = out / 'libnvidia-ngx-dlss-NOTICE.txt'
|
||||
|
||||
def kit_lib(version, data, when):
|
||||
for old in (kit / 'bin').glob('libnvidia-ngx-dlss.so.*'):
|
||||
old.unlink()
|
||||
lib = kit / 'bin' / f'libnvidia-ngx-dlss.so.{version}'
|
||||
lib.write_bytes(data)
|
||||
os.utime(lib, (when, when))
|
||||
(kit / 'bin' / 'libnvidia-ngx-dlss-NOTICE.txt').write_text(
|
||||
f'NVIDIA DLSS {version}\n\nsha256 {hashlib.sha256(data).hexdigest()}\n')
|
||||
|
||||
def beside(folder=out):
|
||||
return sorted(p.name for p in folder.iterdir())
|
||||
|
||||
with mock.patch.object(self.mg, 'KIT', kit):
|
||||
self.assertIsNone(self.mg.place_dlss(out, linux=True)) # a kit without DLSS: nothing
|
||||
self.assertEqual(beside(), [])
|
||||
(kit / 'bin' / 'nvngx_dlss.dll').write_bytes(b'the Windows DLL')
|
||||
self.assertIsNone(self.mg.place_dlss(out, linux=True)) # the Windows DLL is not a Linux build's
|
||||
self.assertEqual(beside(), [])
|
||||
kit_lib('310.9.1', b'dlss one', 1_700_000_000)
|
||||
self.assertIsNone(self.mg.place_dlss(out, linux=True))
|
||||
self.assertEqual(beside(), ['libnvidia-ngx-dlss-NOTICE.txt', 'libnvidia-ngx-dlss.so.310.9.1'])
|
||||
self.assertEqual((out / 'libnvidia-ngx-dlss.so.310.9.1').read_bytes(), b'dlss one')
|
||||
self.assertIn(hashlib.sha256(b'dlss one').hexdigest(), note.read_text())
|
||||
self.assertIsNone(self.mg.place_dlss(out, linux=True)) # unchanged
|
||||
self.assertEqual(len(beside()), 2)
|
||||
kit_lib('310.10.0', b'dlss two', 1_700_100_000) # another version: the earlier build's file goes
|
||||
self.assertIsNone(self.mg.place_dlss(out, linux=True))
|
||||
self.assertEqual(beside(), ['libnvidia-ngx-dlss-NOTICE.txt', 'libnvidia-ngx-dlss.so.310.10.0'])
|
||||
self.assertEqual((out / 'libnvidia-ngx-dlss.so.310.10.0').read_bytes(), b'dlss two')
|
||||
self.assertIn(hashlib.sha256(b'dlss two').hexdigest(), note.read_text())
|
||||
# a library the player put there first, of another version: kept, and the kit's is not placed beside it
|
||||
mine = self.tmp / 'mine'
|
||||
mine.mkdir()
|
||||
(mine / 'libnvidia-ngx-dlss.so.310.4.0').write_bytes(b"the player's own DLSS")
|
||||
self.assertIn('libnvidia-ngx-dlss.so.310.4.0 kept', self.mg.place_dlss(mine, linux=True))
|
||||
self.assertEqual(beside(mine), ['libnvidia-ngx-dlss.so.310.4.0'])
|
||||
# the same version with other bytes (the player's own build) is kept too
|
||||
(mine / 'libnvidia-ngx-dlss.so.310.4.0').unlink()
|
||||
(mine / 'libnvidia-ngx-dlss.so.310.10.0').write_bytes(b"the player's own build")
|
||||
self.assertIn('kept', self.mg.place_dlss(mine, linux=True))
|
||||
self.assertEqual((mine / 'libnvidia-ngx-dlss.so.310.10.0').read_bytes(), b"the player's own build")
|
||||
# a player's library put beside a library an earlier build placed: both stay
|
||||
(out / 'libnvidia-ngx-dlss.so.310.4.0').write_bytes(b"the player's own DLSS")
|
||||
self.assertIn('kept', self.mg.place_dlss(out, linux=True))
|
||||
self.assertEqual(len(beside()), 3)
|
||||
# and a Windows build takes the DLL, not the library
|
||||
win = self.tmp / 'win'
|
||||
win.mkdir()
|
||||
self.assertIsNone(self.mg.place_dlss(win))
|
||||
self.assertEqual(beside(win), ['nvngx_dlss.dll'])
|
||||
|
||||
def test_dlss_pin_for_linux(self):
|
||||
"""The Linux library is pinned beside the Windows DLL, at the same DLSS version with its own size and sha256.
|
||||
A file on disk must be the pinned one, a missing one is downloaded into the cache under its own name, and the
|
||||
notice names the library and carries the sha256 line make_game.py reads."""
|
||||
import dlss_pin
|
||||
from unittest import mock
|
||||
p = dlss_pin.pin(linux=True)
|
||||
self.assertEqual(p.name, f'libnvidia-ngx-dlss.so.{dlss_pin.VERSION}')
|
||||
self.assertTrue(p.name.startswith(self.mg.DLSS_SO_PREFIX))
|
||||
self.assertFalse(p.notice.startswith(self.mg.DLSS_SO_PREFIX)) # the runtime takes that prefix for the library
|
||||
self.assertEqual(p.notice, self.mg.DLSS_SO_NOTICE)
|
||||
self.assertEqual(dlss_pin.pin().name, dlss_pin.NAME) # the default is still the Windows DLL
|
||||
self.assertNotEqual((p.size, p.sha256), (dlss_pin.SIZE, dlss_pin.SHA256))
|
||||
(self.tmp / 'n.txt').write_text(dlss_pin.notice_text(linux=True), encoding='utf-8')
|
||||
self.assertEqual(self.mg.notice_sha256(self.tmp / 'n.txt'), p.sha256)
|
||||
self.assertIn(p.name, dlss_pin.notice_text(linux=True))
|
||||
self.assertNotIn('nvngx_dlss.dll', dlss_pin.notice_text(linux=True))
|
||||
data = b'a DLSS library'
|
||||
lib, cache = self.tmp / 'file.so', self.tmp / 'dl'
|
||||
lib.write_bytes(data)
|
||||
fetched = []
|
||||
|
||||
def download(url, dest, sha256=None, size=None):
|
||||
fetched.append((url, Path(dest).name, sha256, size))
|
||||
Path(dest).parent.mkdir(parents=True, exist_ok=True)
|
||||
Path(dest).write_bytes(data)
|
||||
return dest
|
||||
|
||||
pinned = dict(LINUX_SIZE=len(data), LINUX_SHA256=hashlib.sha256(data).hexdigest(),
|
||||
SIZE=len(data) + 1, SHA256=hashlib.sha256(data + b'!').hexdigest())
|
||||
with mock.patch.multiple(dlss_pin, **pinned), mock.patch('launcher.net.download', download), \
|
||||
contextlib.redirect_stdout(io.StringIO()):
|
||||
self.assertEqual(dlss_pin.resolve(lib, cache, linux=True), lib)
|
||||
with self.assertRaises(SystemExit): # the Windows DLL's pin does not accept the Linux file
|
||||
dlss_pin.resolve(lib, cache)
|
||||
lib.write_bytes(b'another DLSS!')
|
||||
with self.assertRaises(SystemExit):
|
||||
dlss_pin.resolve(lib, cache, linux=True)
|
||||
with self.assertRaises(SystemExit):
|
||||
dlss_pin.resolve(self.tmp / 'missing.so', cache, linux=True)
|
||||
self.assertEqual(fetched, [])
|
||||
self.assertEqual(dlss_pin.resolve(None, cache, linux=True), cache / p.name)
|
||||
self.assertEqual(fetched, [(p.url, p.name, hashlib.sha256(data).hexdigest(), len(data))])
|
||||
self.assertEqual(dlss_pin.resolve(None, cache), cache / f'nvngx_dlss-{dlss_pin.VERSION}.dll')
|
||||
self.assertEqual(fetched[1][:2], (dlss_pin.URL, f'nvngx_dlss-{dlss_pin.VERSION}.dll'))
|
||||
bin_dir = self.tmp / 'bin'
|
||||
bin_dir.mkdir()
|
||||
dlss_pin.place(cache / p.name, bin_dir, linux=True)
|
||||
self.assertEqual((bin_dir / p.notice).read_text(encoding='utf-8'), dlss_pin.notice_text(linux=True))
|
||||
self.assertEqual(sorted(x.name for x in bin_dir.iterdir()), sorted([p.notice, p.name]))
|
||||
self.assertEqual((bin_dir / p.name).read_bytes(), data)
|
||||
|
||||
def test_compile_key_ignores_file_time_and_sysroot(self):
|
||||
"""The compiler's id leaves its file time out (a tool update that puts the same compiler back keeps every
|
||||
object), and the key leaves --sysroot out (the fast compiler's objects are the UCRT64 compiler's)."""
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
"""NVIDIA's DLSS library (nvngx_dlss.dll) for the releases: tools/make_launcher.py and tools/make_kit.py put it in the
|
||||
release's bin/ beside AMD's FSR 3.1 library, and tools/make_game.py copies it beside the game, where NGX loads it
|
||||
(runtime/vk_dlaa.c). Without it DLSS runs only where the NVIDIA driver supplies the model itself.
|
||||
"""NVIDIA's DLSS library for the releases: nvngx_dlss.dll for Windows, libnvidia-ngx-dlss.so.<version> for Linux.
|
||||
tools/make_launcher.py and tools/make_kit.py put the DLL in the release's bin/ beside AMD's FSR 3.1 library,
|
||||
tools/make_appimage.py puts the Linux library in the AppImage's kit bin/, and tools/make_game.py copies either beside
|
||||
the game, where NGX loads it (runtime/vk_dlaa.c). Without it DLSS runs only where the NVIDIA driver supplies the model
|
||||
itself (Windows), and on Linux not at all (runtime/resolution.c dlss_dll).
|
||||
|
||||
The file is NVIDIA's own build from the DLSS SDK repository at a pinned tag, unmodified and checked against its size
|
||||
and SHA-256. NVIDIA's RTX SDKs License lets an application ship it in object form; the notice written beside it names
|
||||
the version and the licence that covers it.
|
||||
the version and the licence that covers it. Both platforms' files are published under the one licence file of that tag.
|
||||
"""
|
||||
from pathlib import Path
|
||||
from typing import NamedTuple
|
||||
|
||||
NAME = 'nvngx_dlss.dll'
|
||||
NOTICE = 'nvngx_dlss-NOTICE.txt' # beside the DLL; make_game.py reads its sha256 line
|
||||
@@ -14,18 +17,41 @@ VERSION = '310.9.1'
|
||||
URL = f'https://raw.githubusercontent.com/NVIDIA/DLSS/v{VERSION}/lib/Windows_x86_64/rel/{NAME}'
|
||||
SIZE = 58956912
|
||||
SHA256 = '3975567b8943c53acce397f2b72380092f84f162d00b0d2c7d08a1025c563983'
|
||||
# The Linux library of the same release. Its name ends in the DLSS version; NGX finds it by that name beside the
|
||||
# executable (runtime/resolution.c dlss_dll looks for the prefix).
|
||||
LINUX_NAME = f'libnvidia-ngx-dlss.so.{VERSION}'
|
||||
LINUX_NOTICE = 'libnvidia-ngx-dlss-NOTICE.txt' # beside the library; make_game.py reads its sha256 line
|
||||
LINUX_URL = f'https://raw.githubusercontent.com/NVIDIA/DLSS/v{VERSION}/lib/Linux_x86_64/rel/{LINUX_NAME}'
|
||||
LINUX_SIZE = 59481944
|
||||
LINUX_SHA256 = '7561f16cab74e2b7ccf791bf44bb9cc43abb44bcf78f33c5663520ad4e857e02'
|
||||
LICENSE_URL = f'https://github.com/NVIDIA/DLSS/blob/v{VERSION}/LICENSE.txt'
|
||||
|
||||
|
||||
def notice_text():
|
||||
return (f'{NAME} is NVIDIA DLSS {VERSION}, unmodified, as published in NVIDIA\'s DLSS SDK\n'
|
||||
class Pin(NamedTuple):
|
||||
name: str
|
||||
notice: str
|
||||
url: str
|
||||
size: int
|
||||
sha256: str
|
||||
|
||||
|
||||
def pin(linux=False):
|
||||
"""The pinned file of a platform."""
|
||||
if linux:
|
||||
return Pin(LINUX_NAME, LINUX_NOTICE, LINUX_URL, LINUX_SIZE, LINUX_SHA256)
|
||||
return Pin(NAME, NOTICE, URL, SIZE, SHA256)
|
||||
|
||||
|
||||
def notice_text(linux=False):
|
||||
p = pin(linux)
|
||||
return (f'{p.name} is NVIDIA DLSS {VERSION}, unmodified, as published in NVIDIA\'s DLSS SDK\n'
|
||||
f'(https://github.com/NVIDIA/DLSS, tag v{VERSION}). Copyright NVIDIA Corporation.\n'
|
||||
'\n'
|
||||
'It is not part of NX2navi and NX2navi\'s licence does not cover it. It is distributed under the\n'
|
||||
f'NVIDIA RTX SDKs License, and its use is subject to that licence: {LICENSE_URL}\n'
|
||||
'NVIDIA licenses DLSS only for use on systems with NVIDIA GPUs.\n'
|
||||
'\n'
|
||||
f'sha256 {SHA256}\n')
|
||||
f'sha256 {p.sha256}\n')
|
||||
|
||||
|
||||
def sha256_file(path):
|
||||
@@ -37,30 +63,31 @@ def sha256_file(path):
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
def resolve(local, cache_dir):
|
||||
"""The pinned DLL: local when given (it must be the pinned file), else the copy in cache_dir, downloaded when
|
||||
missing (launcher/net.py: resumed, checked against the size and SHA-256)."""
|
||||
def resolve(local, cache_dir, linux=False):
|
||||
"""The pinned library: local when given (it must be the pinned file), else the copy in cache_dir, downloaded when
|
||||
missing (launcher/net.py: resumed, checked against the size and SHA-256). linux: the Linux library, else the DLL."""
|
||||
p = pin(linux)
|
||||
if local:
|
||||
local = Path(local)
|
||||
if not local.is_file():
|
||||
raise SystemExit(f'{local} does not exist')
|
||||
if local.stat().st_size != SIZE or sha256_file(local) != SHA256:
|
||||
if local.stat().st_size != p.size or sha256_file(local) != p.sha256:
|
||||
raise SystemExit(f'{local} is not NVIDIA DLSS {VERSION} (size or SHA-256 differ); a newer DLSS needs '
|
||||
'VERSION, SIZE and SHA256 in tools/dlss_pin.py changed first')
|
||||
return local
|
||||
import sys
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
from launcher import net
|
||||
dest = Path(cache_dir) / f'nvngx_dlss-{VERSION}.dll'
|
||||
if not (dest.is_file() and sha256_file(dest) == SHA256):
|
||||
print(f'download {URL}', flush=True)
|
||||
net.download(URL, dest, sha256=SHA256, size=SIZE)
|
||||
dest = Path(cache_dir) / (p.name if linux else f'nvngx_dlss-{VERSION}.dll')
|
||||
if not (dest.is_file() and sha256_file(dest) == p.sha256):
|
||||
print(f'download {p.url}', flush=True)
|
||||
net.download(p.url, dest, sha256=p.sha256, size=p.size)
|
||||
return dest
|
||||
|
||||
|
||||
def place(src, bin_dir):
|
||||
"""The DLL and its notice into a release's bin/."""
|
||||
def place(src, bin_dir, linux=False):
|
||||
"""The library and its notice into a release's or the kit's bin/."""
|
||||
import shutil
|
||||
bin_dir = Path(bin_dir)
|
||||
shutil.copy2(src, bin_dir / NAME)
|
||||
(bin_dir / NOTICE).write_text(notice_text(), encoding='utf-8')
|
||||
p, bin_dir = pin(linux), Path(bin_dir)
|
||||
shutil.copy2(src, bin_dir / p.name)
|
||||
(bin_dir / p.notice).write_text(notice_text(linux), encoding='utf-8')
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
player's PC from their own dump with tools/make_game.py, as NX2navi-Setup.exe does on Windows.
|
||||
|
||||
python3 tools/make_appimage.py --python <standalone CPython 3.14> --appimagetool <appimagetool> [--out dist]
|
||||
[--dlss libnvidia-ngx-dlss.so.<version> | --no-dlss]
|
||||
|
||||
The AppImage holds no game files: the kit's tools/, recomp/, runtime/ and docs, the shader translator built by
|
||||
tools/build_xlate.py (bin/nx2navi_xlate), the pinned Vulkan Memory Allocator header, and a relocatable CPython 3.14
|
||||
(python-build-standalone, as `uv python install 3.14` puts in ~/.local/share/uv/python) with numpy, lz4, zstandard,
|
||||
fonttools and pillow, and with --prompt-art the button art make_game.py's prompts step draws from (as make_kit.py
|
||||
--prompt-art: the pictures tools/prompt_art.py art_files names, into the kit's prompt-art folder; without it that step
|
||||
is skipped and the game keeps the Switch prompts). zbic, the GPL package that unpacks the game's program files, is not
|
||||
bundled: the AppImage installs it with pip into ~/.local/share/nx2navi/pydeps the first time it runs, as the Windows
|
||||
launcher downloads it.
|
||||
tools/build_xlate.py (bin/nx2navi_xlate), NVIDIA's DLSS library with its notice (bin/, tools/dlss_pin.py: downloaded
|
||||
from NVIDIA's DLSS SDK repository and checked, or --dlss; not with --no-dlss), the pinned Vulkan Memory Allocator
|
||||
header, and a relocatable CPython 3.14 (python-build-standalone, as `uv python install 3.14` puts in
|
||||
~/.local/share/uv/python) with numpy, lz4, zstandard, fonttools and pillow, and with --prompt-art the button art
|
||||
make_game.py's prompts step draws from (as make_kit.py --prompt-art: the pictures tools/prompt_art.py art_files names,
|
||||
into the kit's prompt-art folder; without it that step is skipped and the game keeps the Switch prompts). zbic, the GPL
|
||||
package that unpacks the game's program files, is not bundled: the AppImage installs it with pip into
|
||||
~/.local/share/nx2navi/pydeps the first time it runs, as the Windows launcher downloads it.
|
||||
|
||||
The compiler and the libraries come from the system (README-BuildKit.md, "Linux"): the AppImage checks for clang,
|
||||
clang++, lld and the headers before it starts, and names the packages for Arch, Fedora and Debian/Ubuntu.
|
||||
@@ -134,6 +136,9 @@ def main():
|
||||
ap.add_argument('--python', type=Path, required=True,
|
||||
help='a relocatable CPython 3.14 folder (python-build-standalone, ~/.local/share/uv/python/cpython-3.14*)')
|
||||
ap.add_argument('--appimagetool', type=Path, required=True, help='appimagetool-x86_64.AppImage')
|
||||
ap.add_argument('--dlss', type=Path, help="NVIDIA's libnvidia-ngx-dlss.so.<version> from disk instead of "
|
||||
"downloading it from NVIDIA's DLSS SDK repository")
|
||||
ap.add_argument('--no-dlss', action='store_true', help='leave NVIDIA DLSS out of the AppImage')
|
||||
ap.add_argument('--out', type=Path, default=ROOT / 'dist', help='where the AppImage goes (default dist/)')
|
||||
ap.add_argument('--prompt-art', type=Path, nargs='+', metavar='FOLDER',
|
||||
help='folders holding XGamepad, P5Gamepad and Keyboard_Mouse (or those folders): the button art, as '
|
||||
@@ -146,12 +151,14 @@ def main():
|
||||
a.python / 'bin' / 'python3'):
|
||||
if not need.exists():
|
||||
sys.exit(f'{need} is missing (tools/build_xlate.py builds the translator; tools/build_game.py fetches VMA)')
|
||||
sys.path.insert(0, str(ROOT / 'tools'))
|
||||
import dlss_pin
|
||||
dlss = None if a.no_dlss else dlss_pin.resolve(a.dlss, ROOT / 'build' / 'dl', linux=True)
|
||||
|
||||
app = ROOT / 'build' / 'appimage' / 'NX2navi.AppDir'
|
||||
shutil.rmtree(app, ignore_errors=True)
|
||||
kit = app / 'usr' / 'kit'
|
||||
kit.mkdir(parents=True)
|
||||
sys.path.insert(0, str(ROOT / 'tools'))
|
||||
import build_shaders
|
||||
build_shaders.build(ROOT / 'runtime') # the image carries the runtime's shaders compiled (runtime/*_spv.h)
|
||||
for d in KIT_DIRS:
|
||||
@@ -164,6 +171,8 @@ def main():
|
||||
(kit / 'bin').mkdir()
|
||||
for f in ('nx2navi_xlate', 'nx2navi_xlate.id'):
|
||||
shutil.copy2(xlate / f, kit / 'bin' / f)
|
||||
if dlss: # DLSS: NGX loads it from beside the game (runtime/vk_dlaa.c); make_game.py copies it there
|
||||
dlss_pin.place(dlss, kit / 'bin', linux=True)
|
||||
(kit / 'toolchain' / 'vma').mkdir(parents=True)
|
||||
shutil.copy2(ROOT / 'toolchain' / 'vma' / 'vk_mem_alloc.h', kit / 'toolchain' / 'vma' / 'vk_mem_alloc.h')
|
||||
if a.prompt_art:
|
||||
|
||||
@@ -554,6 +554,9 @@ def copy_if_changed(src, dst):
|
||||
|
||||
|
||||
DLSS_DLL, DLSS_NOTICE = 'nvngx_dlss.dll', 'nvngx_dlss-NOTICE.txt' # tools/dlss_pin.py, in the kit's bin/
|
||||
# The Linux library's name ends in the DLSS version (tools/dlss_pin.py): it is found by its prefix, as the runtime
|
||||
# finds it beside the game (runtime/resolution.c dlss_dll).
|
||||
DLSS_SO_PREFIX, DLSS_SO_NOTICE = 'libnvidia-ngx-dlss.so.', 'libnvidia-ngx-dlss-NOTICE.txt'
|
||||
# DLSS Frame Generation (runtime/vk_dlssg.c) loads these from beside the game: nx2navi_dlssg.dll (runtime/dlssg/,
|
||||
# tools/build_dlssg.py), NVIDIA Streamline's interposer and the plugins it uses, and NGX's frame generation library.
|
||||
# A release carries them with their licence texts in bin/dlssg/ (tools/make_launcher.py --dlssg); without the whole set
|
||||
@@ -564,7 +567,7 @@ DLSSG_FILES = ('nx2navi_dlssg.dll', 'sl.interposer.dll', 'sl.common.dll', 'sl.dl
|
||||
|
||||
|
||||
def notice_sha256(path):
|
||||
"""The sha256 line of a DLSS notice: the DLL a kit placed beside the game."""
|
||||
"""The sha256 line of a DLSS notice: the library a kit placed beside the game."""
|
||||
try:
|
||||
for line in path.read_text(encoding='utf-8').splitlines():
|
||||
if line.startswith('sha256 '):
|
||||
@@ -574,22 +577,35 @@ def notice_sha256(path):
|
||||
return None
|
||||
|
||||
|
||||
def place_dlss(out):
|
||||
def dlss_files(folder, linux):
|
||||
"""The DLSS libraries in folder, by name: nvngx_dlss.dll, or on Linux every libnvidia-ngx-dlss.so.<version>."""
|
||||
return sorted(p for p in folder.glob(DLSS_SO_PREFIX + '*' if linux else DLSS_DLL) if p.is_file())
|
||||
|
||||
|
||||
def place_dlss(out, linux=False):
|
||||
"""NVIDIA's DLSS library from the kit's bin/ beside the game, where NGX loads it (runtime/vk_dlaa.c), with its
|
||||
notice. A different nvngx_dlss.dll the player put there (another DLSS version) is kept; the one an earlier build
|
||||
placed, named by the notice beside it, is replaced. Returns a line for the build's log when it kept the player's."""
|
||||
src, dst, note = KIT / 'bin' / DLSS_DLL, out / DLSS_DLL, KIT / 'bin' / DLSS_NOTICE
|
||||
if not src.is_file():
|
||||
notice. A different library the player put there (another DLSS version) is kept; the one an earlier build placed,
|
||||
named by the notice beside it, is replaced. linux: the Linux library, whose name carries the DLSS version, so a
|
||||
kit with another version replaces the earlier build's file under its old name. Returns a line for the build's
|
||||
log when it kept the player's."""
|
||||
note_name = DLSS_SO_NOTICE if linux else DLSS_NOTICE
|
||||
srcs, note = dlss_files(KIT / 'bin', linux), KIT / 'bin' / note_name
|
||||
if not srcs:
|
||||
return None
|
||||
if dst.is_file():
|
||||
src = srcs[-1]
|
||||
beside = dlss_files(out, linux)
|
||||
for dst in beside:
|
||||
s, d = os.stat(src), os.stat(dst)
|
||||
if d.st_size != s.st_size or int(d.st_mtime) != int(s.st_mtime):
|
||||
if dst.name != src.name or d.st_size != s.st_size or int(d.st_mtime) != int(s.st_mtime):
|
||||
have = sha256_file(dst)
|
||||
if have != notice_sha256(out / DLSS_NOTICE) and have != sha256_file(src):
|
||||
return f"{DLSS_DLL} kept: the one already beside the game is not the kit's (another DLSS version)"
|
||||
copy_if_changed(src, dst)
|
||||
if note.is_file() and (not (out / DLSS_NOTICE).is_file() or (out / DLSS_NOTICE).read_bytes() != note.read_bytes()):
|
||||
shutil.copy2(note, out / DLSS_NOTICE) # by content: two versions' notices can share size and second
|
||||
if have != notice_sha256(out / note_name) and have != sha256_file(src):
|
||||
return f"{dst.name} kept: the one already beside the game is not the kit's (another DLSS version)"
|
||||
copy_if_changed(src, out / src.name)
|
||||
for dst in beside:
|
||||
if not dst.samefile(out / src.name): # another version's library, placed by an earlier build: replaced
|
||||
dst.unlink()
|
||||
if note.is_file() and (not (out / note_name).is_file() or (out / note_name).read_bytes() != note.read_bytes()):
|
||||
shutil.copy2(note, out / note_name) # by content: two versions' notices can share size and second
|
||||
return None
|
||||
|
||||
|
||||
@@ -1331,7 +1347,7 @@ def step_compile(out, cache_dir, msys, jobs, romfs_job=None, update=False):
|
||||
copy_if_changed(xlate / name, out / name)
|
||||
if (KIT / 'bin' / 'amd_fidelityfx_vk.dll').is_file(): # FSR 3.1 (runtime/vk_ffx.cpp)
|
||||
copy_if_changed(KIT / 'bin' / 'amd_fidelityfx_vk.dll', out / 'amd_fidelityfx_vk.dll')
|
||||
kept = place_dlss(out) if WIN else None # NVIDIA DLSS (runtime/vk_dlaa.c): its library is a Windows DLL
|
||||
kept = place_dlss(out, linux=not WIN) # NVIDIA DLSS (runtime/vk_dlaa.c): this platform's library from the kit
|
||||
if kept:
|
||||
say(f' {kept}')
|
||||
if WIN and place_dlssg(out): # DLSS Frame Generation (runtime/vk_dlssg.c): Windows DLLs too
|
||||
|
||||
Reference in New Issue
Block a user