tools/make_game.py, the build players run through the launcher, read no profile: only tools/build_game.py did, which
does not build 1.1.1. A kit with data/pgo-v111.profdata (build/pgo/ in a checkout) now compiles the lifted code at -O2
with it and the runtime too, the prebuilt runtime library included. The profile is left out, and every command and
key is what it was, when the kit has none, when its record names another recomp/ than the kit's, when a compiler of
the build refuses the file in one trial compile before the others start, and with NX2NAVI_PGO=off. The record holds
recomp/ by content, which a kit without git can compare. The object keys hold the profile's content and not the kit's
folder, so a launcher update that moves the kit keeps the objects.
--pgo gen (NX2NAVI_PGO=gen from a launcher) builds the instrumented game into the game folder: it counts into
<out>/pgo, records what it was built from for the merge, and stops at once when the build tools lack the profile
runtime. tools/pgo.py merge takes that record, refuses a game built from another recomp/ and finds llvm-profdata in an
MSYS2 folder too. tools/check_content.py refuses profiles in a commit.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Ocarina anti-aliases every frame with its own SMAA in three compute passes after the bloom composite. The new aa
setting (nx2navi.ini aa, the Screen tab's Anti-aliasing row) keeps that by default. off, fxaa and smaa leave out the
game's edge and weight passes and replace its composition with a pass of our own that writes the same square-rooted
RGB9E5 words into the scene target: a plain copy, an edge smoothing written here, or the SMAA reference implementation
with its area and search textures (MIT). taa is DLSS or FSR 3.1 at the scene's size, which the row sets through the
upscaler; while either runs, the game's SMAA stays whatever aa says. The game's passes are known by a hash of their
guest code. The shader build now also rebuilds a header when a file its source includes changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bin/ at the top of a checkout is the release folder that tools/make_release.py writes and the build reads (the shader translator with its id file, hactool, ninja and the DLLs). Its exe and DLL files were ignored already; the translator's id file was the one that showed up as untracked.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The preparation translates every shader and builds each one's stage library, so a pipeline a draw meets for the first time is linked in 2 to 6 ms. A mesh pipeline has no stage libraries: on an install that never drew it, it compiles whole in play, 0.2 to 1.1 s with its draws left out of the picture (18 of them on a first visit of Kokiri Forest). Whole pipelines were built ahead only from the folder's own pipeline-keys.bin.
tools/pipeline_list.py merge folds the pipeline-keys.bin and shader-answers.bin of game folders that were played into one list file: every key record once, with the answers record of each shader it names. A record names its shaders by input id, which depends on the game's code and the answers alone, and holds the game's pipeline state; what depends on the GPU is derived when the pipeline is built, so a record made on one machine is good on another with the same game version and runtime. Four folders on this PC give 5,432 records, 317 of them mesh pipelines (147 KB of keys).
tools/prepare_shaders.py reads the list (tools/data/pipeline_list.bin, data/pipeline_list.bin in a release folder, or --pipeline-list), adds its shader variants to the seed and writes its records as seed-pipeline-keys.bin. nx2navi.exe --prepare-shaders builds the mesh pipelines among them after the seed is translated and saves the pipeline cache before the stage libraries are built (NX2NAVI_PREPARE_PIPELINES=all builds the vertex pipelines too); --warm-shaders does it again after an update or a new driver, and --prepare-pipelines is that step alone. The pipelines go into the caches and are not kept or added to the folder's own key file, so they cost no start-up time or video memory until a draw meets them. A marker file keeps a driver that crashes on one of them from failing every preparation.
The list is derived from game data and stays out of the repository: check_content.py refuses files recorded from a game folder by their first bytes, and make_kit.py, make_launcher.py and make_release.py carry a list given with --pipeline-list. All three now also carry pipeline_list.py, which prepare_shaders.py imports.
Checked without the game: tests/pipeline_list_test.py, and prepare_shaders.py --no-run on the game files with a list of 317 mesh pipelines (all 266 shader variants found in this game version, 177 new to the seed). The build step in nx2navi.exe compiles and links and has not been run.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
A player's AppImage build of 0.7.1 stopped in its compile step after eight minutes of RomFS work: four
files did not compile, because the kit builds with the system's compiler and headers and theirs were
older than the runtime needs. vk_pipeline.c and vk_device.c name VK_KHR_pipeline_binary,
VK_KHR_dynamic_rendering_local_read and VK_KHR_shader_maximal_reconvergence, which their Vulkan headers
did not declare, and ui/menu.cpp takes its font in with #embed, which clang has from version 19.
- Vulkan headers: tools/vulkan_pin.py holds the release the other builds use (1.4.363, the archive and
hash tools/make_release_linux.py already pinned, which now reads them from there). The AppImage carries
its C headers in toolchain/vulkan-headers and builds against them whatever the system has; a kit or a
source checkout uses the system's when they are at least that release and fetches the pinned ones
otherwise (build_game.py vulkan_include). Their folder goes first on the include path of the runtime's
sources only, so the lifted code's objects keep their keys, and a runtime object compiled before
against the system's headers is taken over instead of compiled again. AppRun no longer asks for the
system's Vulkan headers.
- #embed: tools/build_shaders.py also writes the font's bytes as runtime/ui/roboto_ttf.h (ignored by git,
carried by a kit), and menu.cpp includes that where the compiler has no __has_embed. With #embed
nothing changes.
- Before its long steps a Linux build that will compile now checks every runtime source that needs no
header of the install with the compile step's own command (clang -fsyntax-only) and links an empty
program against the game's libraries (make_game.py preflight): an old compiler, an old header or a
missing development package stops it in the first seconds with the compiler's own errors. A failed
compile names every file that failed, not only the three whose errors it prints.
Checked on Windows: tests/launcher_test.py passes with four new tests (117); the header of bytes and
#embed give the same 162,588 bytes, and both branches of menu.cpp compile; the early check passes on the
Windows file set with clang 23 and stops on files made to fail; the 34 local headers of release 363 are
the upstream tag's files by git blob id. Not run: anything on Linux, the pinned archive's download, a
compiler older than 23.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The fifteen headers of SPIR-V words under runtime/ are no longer tracked. tools/build_shaders.py builds all of them:
the GLSL with glslc, the SPIR-V assembly with spirv-as, and the two shaders assembled in Python from their scripts.
build_game.py builds the missing and the stale ones before it compiles the runtime, and the build kit, the launcher
release and the AppImage build them before they copy runtime/, so a player's PC still needs no shader compiler.
git ignores the headers and check_content.py refuses a compiled shader in a commit. The headers built this way are
byte for byte the ones removed here. vk_mvobj_fs.h is now mvobj_fs_spv.h, named like the others.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
make_launcher.py and make_kit.py take --prompt-art (folders holding XGamepad, P5Gamepad and Keyboard_Mouse, or those
folders) and copy only the pictures pad_prompts.py reads into the release's prompt-art folder; tools/prompt_art.py
holds the art tables and that list, with no imaging packages. make_game.py's new last step, prompts, runs
pad_prompts.py on that art into the game folder's romfs-patches-* folders. Its key names everything it reads (the
RomFS, the art, the tools, the bind_ lines of nx2navi.ini, the folders being there), so it runs again only when one of
those changed, and it makes no other step run. A kit without art skips it. The launcher's Python gets fonttools and
pillow, and its jobs show a Draw the Button Prompts row when the version has art.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comments say what the code does and why, docs/maxwell.md keeps the Maxwell facts, and fetch_refs.sh clones only the checkout the build reads. Code is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>