github Djihads80/Eglantine build-158
Eglantine nx2 build 158 (efa7c19)

latest releases: build-167, build-163
pre-release2 hours ago

What's new in this build

Build 158 of nx2 (efa7c19) is build 142 plus everything merged into nx2 since — 72 commits (17 of them merges), 85 files. Most of it is invisible plumbing, but six titles behave differently: Hyrule Warriors: Age of Imprisonment draws gameplay again, Splatoon Raiders gets its shadows, reticle and steam back, Mario Tennis Fever draws its stadium, Donkey Kong Bananza stops crashing, Pokopia gets past its first frame, and Mario Kart World stops stalling on present and no longer runs stale command lists. Defaults changed too (ScaleForce + FXAA, handheld mode), so check the settings section if you had a working configuration.

Builds 143–157 have no release; the numbers are CI run ids and they skip when a run fails.

First launch after updating will rebuild its shaders once — see the cache-version section below before you report a stutter.

Hyrule Warriors: Age of Imprisonment: gameplay renders again (PR #304)

Three Amplifier→Maxwell lowering paths were missing, so those programs were rejected and their draws skipped. All three are in the game's own code, not in an optional feature:

  • TXD with any channel mask other than all four. The game's gradient-sampling programs fetch one channel into Rd, or three with the third landing in the second destination, and were rejected with native gradient texture mode.
  • TLD.SCR and TLD4 with .F16 (bit 79), which return their components packed in pairs. In this game they are compute programs that run once gameplay starts, which is why gameplay stayed black while the pause menu — drawn another way — showed the scene.
  • F2I from a half with the signed bit (72) or the high-half selector (.H1); the game has 24 of these. Rejected as native float-to-integer conversion mode.

The F16 forms now lower through their F32 equivalents and pack or pick halves exactly the way the paths that already existed do; TXD passes its channel mask on to Maxwell and only falls back to the scratch register when the enabled channels aren't consecutive.

Splatoon Raiders: shadows, reticle and steam (PR #305)

Build 142 shipped the FSEL operand-modifier fix, but the disk shader cache keeps lowered native code, and the fix did not bump the cache version. Anyone who had run the game on 142 kept the broken programs, where the depth linearization ends in FSEL R0, -R0, 1.0, P and comes out negated: the tutorial island and the dock had no shadows, there was no aiming reticle, and the steam particle quads collapsed to a point.

The cache versions now go Vulkan 18 → 19 and OpenGL 15 → 16, so those entries are discarded automatically instead of requiring a manual cache wipe. Note that this clears the whole cache for every title — one slower first launch, then it is rebuilt. If you never ran 142 this changes nothing for you.

Mario Tennis Fever: the arch and the castle stadium (PR #295)

X8Z24 was only mapped with UINT in its last three components. The game samples its depth buffer as X8Z24 with UNORM, UINT, UNORM, UNORM, which fell through to the texture format=42 fallback to A8B8G8R8: every frame the depth image was copied into a colour image of the wrong format (with Unimplemented layer-count asserts) and then read as colour, so distant scenery around the court — the arch and the castle stadium — was never drawn. It now maps to X8_D24_UNORM like the other X8Z24 entry.

Donkey Kong Bananza: no more storage-buffer overrun (PR #113)

Native compute shaders here bind up to 18 global memory ranges per stage against a limit of 16, which produced

HW.GPU <Error> buffer_cache.h:BindComputeStorageBuffer: Storage buffer index 16 exceeds maximum storage buffer count

followed by a GPU driver access violation (0xc0000005). NUM_STORAGE_BUFFERS is now 32; the enabled/written bitmasks were already u32, so nothing else had to move.

Mario Kart World: no more 4-second present stalls, and no stale command lists

  • PresentClientBuffer waited for the current frame's fence whenever the previous one hadn't been reached. This game's GPU waits on semaphores that its CPU only releases after the present returns, so in that state the current fence cannot be reached until the present returns — each such present stalled up to the 4 s deadline. It still waits for the current frame by default, but gives up after a few ms once a GPU channel has been seen suspended on a semaphore acquire, and waits for the previous frame instead. EDEN_NX2_FRAMES_AHEAD=1 forces the previous-frame wait, =0 the current-frame one.
  • Games submit command lists before writing them: the list waits behind a semaphore acquire and the commands are written just before the release. Executing each entry from a copy of its words taken at submit read stale commands. The copies are now refreshed when the channel's wait has been reached. (This is also what stopped Pokopia after its first frame on macOS.)
  • The recommended-settings prompt is described under Settings below.

Pokopia and Low GPU accuracy: GPU memory is mapped before later command lists are read

Queued GMMU maps ran on the GPU thread's back end (RunInOrder), but with the GPU split the front end reads the command lists — so lists submitted after a map were read through GPU addresses that weren't mapped yet, and the game stopped after its first frame. A new GPU::MapInOrder runs the maps and the sparse tile remaps on the front end after draining the back, before further command lists are read. The service thread still never takes the texture cache lock.

macOS: 69-byte vertices and pipeline stutter

  • 69-byte vertex strides are repacked to 72, but only with dynamic vertex input — and MoltenVK has no VK_EXT_vertex_input_dynamic_state, while Metal needs strides that are a multiple of 4. The static path now switches on automatically when the device's minimum vertex binding stride alignment doesn't divide 69 (MoltenVK reports 4). EGLANTINE_EXPERIMENTAL_STATIC_VERTEX_REPACK=1 / =0 still forces it on or off.
  • Pipeline rebuilds on MoltenVK mean a SPIR-V→MSL translation plus a Metal compile, which shows as a stutter even with a VkPipelineCache. On MoltenVK the previous eight sessions' pipelines are kept in vulkan_used.bin and built at boot, and are no longer evicted after 9000 frames. Other drivers keep the two-session window and the eviction; EDEN_NX2_PRELOAD_ALL_PIPELINES=1 still builds everything cached.

Settings: two default changes and the Mario Kart World prompt

  • ScaleForce and FXAA are now the defaults on new configurations, and both are part of the accepted Mario Kart World preset. Saved preferences still win — if you had set them yourself, nothing changes.
  • Handheld mode is the default on every platform. Previously only some platforms defaulted to it.
  • The Mario Kart World settings prompt only appears when you boot Mario Kart World, once per session, and applying it now affects that session instead of overwriting the global settings for every title. Before, it asked at every startup whatever you launched, and Yes wrote the MKW preset into the global configuration.

Crashes and hangs fixed

  • Creating a game shortcut no longer crashes when the game can't be loaded: the loader is validated before the title and the icon are read, the title falls back to the program ID and a missing icon is logged instead of dereferenced (issue #274, PR #320).
  • Stopping a game, switching games or quitting can no longer hang forever. Build 142's asynchronous teardown waited for the emulation thread with no limit; it now keeps the 10 s bound and the terminate() fallback, and logs a terminated thread as an error.
  • An unregistered ASID no longer indexes registered_processes out of bounds (PR #235).
  • Game startup aborting during early thread synchronisation is fixed: the kernel's TLS implementation had cross-module lookups that ThinLTO migrated, which aborted thread synchronisation. That one translation unit is now compiled without LTO.
  • The CPU/GPU calibration worker could use its owner's complete_event after the owner was destroyed; it is now retained and joined instead of detached.
  • The .nup reader rejects record tables with wrapping counts and offsets, out-of-bounds TOC/name/extent/meta ranges, and directories that list an ancestor (which used to recurse until the stack overflowed), and the nx2:gpu 216 sparse map checks are overflow-safe against guest-controlled sizes (PR #337).
  • Reading a multi-GB .nup RomFS in small chunks is no longer unusably slow: zstd members larger than half the cache are streamed once into a temporary file instead of being fully decompressed on every Read().
  • Loose RomFS directories with mixed case are found consistently, so Linux no longer misses the base version metadata that Windows picked up.

Performance

  • Small uniform buffers that don't change are reused within a command buffer instead of being re-streamed.
  • Bitmap scans in SlotVector and the kernel priority queue skip empty words, so cache and scheduler traversal stops walking dead ranges.
  • Shader-cache memory-write invalidations are batched (one lock, one removal pass) and large removal batches use sorted membership instead of a linear scan.
  • Descriptor-table invalidation clears only newly exposed bitmap words instead of zeroing preallocated storage.
  • The 69-byte vertex repack now re-dirties only on a layout change rather than on every draw, which removes a compute pass and a render-pass break per draw on tiled GPUs.
  • Native command lists are submitted sooner, macros execute their parameters without a second FIFO copy, and the register-write history ring is opt-in in release builds (EDEN_NX2_3D_HISTORY=1 to get it back) instead of recording every method during normal play.
  • Storage buffers go from 16 to 32 per stage (see Donkey Kong Bananza), the Vulkan sparse-image path backs an image whole when its sparse tiles can't be set up instead of leaving it unbound on AMD, and a split instance loop now continues at its own instance index.

Other fixes worth knowing

  • Texture handles are read as header offsets only on Switch 2 again; that behaviour had leaked into Switch 1 games, where an out-of-range index used to give a null view and now bound an unrelated texture.
  • Handle-program descriptors are compared, so two per-draw textures of the same type and depth no longer share one descriptor (the second sampling the first), and image/texture-buffer descriptors get handle programs instead of reading constant buffer 0 word 0.
  • sRGB is decoded in the upscale fallback when the program does it (Splatoon Raiders' outdoor scenes were washed out with a pale sky and white haze).
  • Native shader capture flushes GPU-written code again — reading a page at a time kept the speed-up without capturing stale programs.
  • The Steam Deck log no longer floods with the same gyro message (over 3000 lines in one session); motion capabilities are logged only when they change.
  • Three documentation typos from @lunar-me (PRs #307, #308, #309), and the test target now builds with MSVC (PR #327).

How it works, for the technically curious

  • git diff build-142..build-158 is 85 files, 2241 insertions, 292 deletions; the largest are nup_container.cpp (+188), tests/video_core/descriptor_table.cpp (+170), and a new buffer_cache/uniform_stream_reuse.h (+121).
  • 72 commits, 17 merges: #113, #235, #284, #295, #304, #305, #307, #308, #309, #320, #327, #334, #337, #338, #354, plus the settings, coherency and LTO commits merged directly.
  • lower_to_maxwell.h gains the three F16/channel-mask cases in TXD, TLD/TLD4 and F2I; the cache bumps are CACHE_VERSION 18 → 19 in vk_pipeline_cache.cpp and 15 → 16 in gl_shader_cache.cpp.
  • NUM_STORAGE_BUFFERS 16 → 32 in buffer_cache_base.h; GPU::MapInOrder is new next to RunInOrder in video_core/gpu.cpp, with the map path in memory.cpp and the sparse remaps in nx2_gsv.h.
  • The MKW prompt moved to first-boot-of-a-session, session-scoped application, with src/tests/common/game_presets.cpp covering the defaults and saved-preference overrides.
  • New test targets: tests/common/game_presets.cpp, slot_vector.cpp, priority_queue.cpp, uniform_stream_reuse.cpp, native_macro_fifo.cpp, video_core/shader_invalidation.cpp, plus Windows fixes in the existing ones.
  • Full detail in docs/performance/MKW-INTEGRATION.md.

Automatic build of nx2 at efa7c19.

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 Eden's profile); prefer them when your CPU supports it.

Don't miss a new Eglantine release

NewReleases is sending notifications on new releases.