github Djihads80/Eglantine build-189
Eglantine nx2 build 189 (7adcecc)

pre-releaseone hour ago

What's new in this build

Build 189 of nx2 (7adcecc) is build 188 plus two fixes in the shader compiler for Switch 2 ("native") shaders — and they are what makes the NVIDIA DLSS path that build 187 enabled actually work in Fire Emblem: Fortune's Weave.

With DLSS on, that game's network used to produce NaN by its second frame: half of one layer's output was never written, the encoder's skip connections never reached the decoder, and the upscaled image and its history froze. Both causes are fixed here.

10 files, +216/−24. A shader that hits neither case compiles exactly as before, and with the network off (FSR 3.1) the same scenes measure identical.

In short

  • What was wrong: when a Switch 2 shader reads or writes a "native" global through a pointer taken from a constant buffer, the compiler rewrites the access into a storage-buffer access on the buffer it thinks the pointer names. Two ways that guess went wrong: an address assigned under a predicate is a phi of two pointers, and only the first was used, so the other path's stores fell outside the chosen buffer and were dropped; and a pointer sitting in an 8-byte slot (0x168, 0x188, 0x208, …) was matched against the NVN test, which only accepts 16-byte-aligned slots between 0x100 and 0x700, so it picked a different slot — an input to the address arithmetic, a dimension, a stride or a neighbouring pointer — and rebased the access onto that buffer.
  • In Fortune's Weave: four programs of its DLSS network had 50–58 of the second kind each. The encoder layers' skip-connection outputs never arrived, so the decoder layers read zeros, the recurrent state filled with NaN after the second frame, and the last pass kept showing the previous history — the title screen's clock stood still.
  • The fix: for native programs every candidate pointer the address can come from is collected (following additions, selects, phis and CompositeExtract). One candidate is used directly; with several, all of them are bound, the access stays global, and the SPIR-V global-memory functions pick the closest base at or below the address; with none, the old NVN search runs as before.
  • Measured by the author (720p → 1440p, RTX 5080, network on): the title clock now changes 5–15 between captures where it changed 0.01 before, and the network's output changes by 0.011 between the 30 s and 45 s captures where it changed 0.00003.
  • With the network off nothing changed: the title, Kunlun Base on foot and the battle-intro cutscenes at FSR 3.1 measured identical to before, with no new log messages.
  • Scope: the DLSS setting itself is still only in the Windows MSVC zip (see 187's notes); these two fixes ship in every download and apply to any Switch 2 title whose shaders hit either case.

The two bugs, one at a time

A predicated pointer assignment lost the other path's writes. An address register assigned under a predicate becomes a phi of the new address and its old value, and that old value can point into another buffer entirely. The pass took the first pointer it reached, rebased the access on that buffer, and the other path's stores fell outside it. One of Fortune's Weave's DLSS layers had exactly that, half its output unwritten — and an upscaler whose inputs are NaN produces NaN.

An 8-byte pointer slot was matched to the wrong slot. The NVN storage-buffer search accepts 16-byte-aligned slots in 0x100–0x700. Native programs keep their pointers as raw 8-byte arguments, so a pointer at 0x168 sent the search to 0x160, which holds something else — an input to the address arithmetic, a dimension, a stride, a neighbouring pointer — and the access was rebased onto whatever that looked like.

Both now go through the candidate walk described above, so the buffer a native access lands in is the buffer its pointer actually names.

What this does not change

Anything that is not a Switch 2 native program, and any native access where the address comes from one pointer at a 16-byte-aligned slot: those compile to the same code as in build 188. In Fortune's Weave with DLSS off, the scenes that were measured are indistinguishable from before — this is a fix for a path that was broken, not a change to the FSR 3.1 or hand-written paths.

Coming from build 188?

You keep the NUP tools (dump, metadata edit, icon search), and from 187 the NVIDIA DLSS setting, the Eglantine-trained -pgo builds and the AMD sparse-tile revert. This build adds only the two shader fixes.

Coming from build 183 or earlier?

Everything from builds 187 and 188 is new to you, including DLSS. If you want to try it in Fortune's Weave, this is the first build where the network does not fall apart after a frame.

How it works, for the technically curious

  • Environment gains IsNativeProgram() and IsCbufStorageBufferPointer(index, offset), so the compiler can ask whether a constant is a mapped GPU address at all — native argument tables mix pointers with sizes and strides. ShaderEnvironment answers from a new storage_pointer_types cache.
  • NativePointerCandidates walks GetCbufU32/GetCbufU32x2, IAdd32, Phi, SelectU32 and CompositeExtractU32x2 (element 0 only), requires 8-byte alignment and de-duplicates the results into a small inline vector. The result is memoised per IR instruction in StorageInfo::native_candidates.
  • With more than one candidate the pass inserts all of them in the descriptor set (and in writes for stores), sets Info::native_global_bounds, and leaves the access unrebased. Emitting that flag makes DefineGlobalMemoryFunctions compute each buffer's base once, pick the candidate with the smallest addr - base that is still >= 0, and dispatch to that buffer; bounds come from the binding rather than an NVN size word.
  • Info gained the native_global_bounds field, so the serialised shader caches were versioned up (Vulkan 19 → 20, OpenGL 16 → 17): an existing pipeline cache is rebuilt once after updating, which is normal and expected here.

Automatic build of nx2 at 7adcecc.

Linux

Linux packages come as an AppImage (self-contained, needs glibc 2.39 or newer: Ubuntu 24.04, Fedora 40, Debian 13, Arch) and a tar.gz (uses your system libraries, so Qt 6 and the usual libraries must be installed).

Build Type Standard PGO (Recommended) Notes
amd64 AppImage · tar.gz AppImage · tar.gz Make the AppImage executable (chmod +x) and run it
ARM (aarch64) AppImage · tar.gz AppImage · tar.gz

Windows

Windows packages are in-place zip files: extract and run eglantine.exe, or eden-cli.exe from a terminal.

Build Type Standard PGO (Recommended) Notes
amd64 / x86_64 (MSVC) MSVC zip Use if you run into graphical issues on other builds
amd64 / x86_64 (MinGW) PGO zip Built with MinGW clang; bundles its own DLLs, no Visual C++ redistributable needed

macOS

Build Type Standard PGO (Recommended) Notes
arm64 (Apple Silicon) zip macOS 15 or newer. Not notarized: right-click and choose Open the first time, or run xattr -dr com.apple.quarantine eglantine.app. eden-cli is inside the app, in Contents/MacOS

The PGO builds are the same programs compiled with profile-guided optimization (clang plus Eglantine's profile); prefer them when your CPU supports it.

Don't miss a new Eglantine release

NewReleases is sending notifications on new releases.