EmuNews

suyu

dougchansan/suyu-v0.0.4

10 tracked updates · latest 23 days ago

GitHub

suyu v14.1.0

v14.1.0 release
Release notes · 12 files

suyu v14.1.0

This minor update fixes two graphics compatibility problems while retaining the safer defaults introduced in v14.0.1.

Graphics corrections

  • Fixed corrupted NVIDIA rendering with graphics pipeline libraries enabled. Fragment-output libraries now retain the full pipeline state when cached.
  • Fixed stretched character geometry in Tears of the Kingdom on macOS MoltenVK. Packed vertex weights use a supported three-component format when the unused fourth component would cross the vertex stride.

Graphics pipeline libraries, Fast GPU Time, async shader building and SPIR-V optimization remain off by default. Explicit user settings are preserved.

Re-export existing Static and Hybrid packages to receive the corrected host. Translated game code remains part of those exports; official downloads contain no games, keys or firmware.

Tested scope

MK8D 4.0.0 and TOTK 1.4.3 native Static opening replays completed on Linux RTX 3090 (NVIDIA 580.159.03), Windows Radeon RX 9070 XT and macOS Apple M5. The 6,001-frame MK8D and 12,001-frame TOTK checks used default settings and optional pipeline-library/Fast GPU settings. All Static cases exited normally with zero JIT transitions and zero unresolved imports; inspected gameplay captures showed coherent geometry, textures and HUDs.

Linux RTX 3090 checks verified the NVIDIA correction; Windows Radeon checks covered the same change as a regression test. The later Mac tests also verified the MoltenVK correction, including an ordinary JIT TOTK replay. The final Mac-only change also passed the Windows CLI/export-kit build. MoltenVK on the tested Mac lacks pipeline-library support and uses the normal fallback when that option is requested.

Later Mac MK8D race samples were around 60 FPS. Mac TOTK cave samples were around 16 FPS by default and 9–10 FPS with Fast GPU Time set to Medium. These are functional observations, not controlled benchmarks or speedup claims. Full playthroughs and other GPUs/drivers remain outside these checks.

Downloads

Windows, macOS arm64, Android arm64, Linux x86-64 tarball and Ubuntu 24.04 AppImage packages are provided, along with libretro cores and corresponding project, dependency and AppImage source archives.

suyu v14.0.1

v14.0.1 release
Release notes · 12 files

suyu v14.0.1

This update supersedes v0.0.14 with safer graphics defaults and complete graphics settings in the launcher. Releases now use major.minor.patch numbering.

Graphics fixes

  • Graphics pipeline libraries are now off by default and remain available as an experimental option.
  • Fast GPU Time is now off by default. Its checkbox now actually controls GPU timing.
  • Graphics → Advanced now exposes the previously hidden graphics hacks and Vulkan extension options.
  • Added descriptive labels and complete choices for dynamic state, sample shading, GPU timing, and texture unswizzling.
  • The Advanced page scrolls on smaller windows. Settings can be saved globally or overridden per game.

Async shader building and SPIR-V optimization remain off by default. Normal disk and driver shader caches remain enabled. Explicit user opt-ins are preserved; profiles using the application defaults adopt the new defaults.

Re-export existing Static and Hybrid packages to receive the corrected embedded host. Updating the launcher alone does not update those packages.

Downloads

Windows, macOS, Android, Linux tarball, and Linux AppImage downloads are provided, along with libretro cores and corresponding sources. The AppImage targets Ubuntu 24.04 x86-64.

chmod +x suyu-linux-x86_64.AppImage
./suyu-linux-x86_64.AppImage

Without FUSE:

./suyu-linux-x86_64.AppImage --appimage-extract
./squashfs-root/AppRun

Testing

Native tests verify the actual Qt controls, INI save/reload, per-game overrides, and restoration of global settings. GPU timing tests verify that disabling Fast GPU Time preserves normal ticks while explicit opt-ins retain their existing behavior.

Windows Static opening-scene playtests passed for Mario Kart 8 Deluxe, Super Mario Odyssey, Tears of the Kingdom, and Xenoblade Chronicles 3 with pipeline libraries off. These checks covered rendering, input response, and clean shutdown on an AMD Radeon RX 9070 XT; they were not full playthroughs or performance benchmarks.

These changes do not establish the cause of every reported graphics issue; compatibility can still depend on the game and GPU driver.

suyu v0.0.14

v0.0.14 release
Release notes · 12 files

Superseded by suyu v14.0.1, which disables experimental graphics pipeline libraries by default and fixes graphics settings. Re-export existing Static and Hybrid packages with the new version.

suyu v0.0.14

This release fixes startup and export-related crashes and adds a Linux AppImage.

Fixes

  • Fixed a startup crash caused by motion callbacks running before initialization finished.
  • Fixed a crash when closing suyu after a Source export or while the game list was loading.
  • Fixed Source exports for game titles containing spaces, Unicode, or special characters.
  • Corrected the version displayed by release builds.

Re-export existing Static and Hybrid packages. Updating suyu alone does not update the emulator embedded in those packages.

Linux AppImage

The new suyu-linux-x86_64.AppImage supports Ubuntu 24.04 x86-64. The existing Linux tarball remains available.

chmod +x suyu-linux-x86_64.AppImage
./suyu-linux-x86_64.AppImage

To use the command frontend:

./suyu-linux-x86_64.AppImage --suyu-cmd --help

Without FUSE, extract and launch it:

./suyu-linux-x86_64.AppImage --appimage-extract
./squashfs-root/AppRun

Corresponding sources are provided in suyu-v0.0.14-appimage-sources.tar.gz. Licenses and notices are included in the AppImage. The project and dependency source archives are also available with the release.

Testing

All platform builds and final package checks passed. The AppImage also passed extraction, command-help, and offscreen GUI startup checks.

Windows Static playtests confirmed rendering, input response, clean exits, and zero JIT fallback in these opening gameplay areas:

Game Version Tested area Average gameplay FPS
Mario Kart 8 Deluxe 4.0.0 Mario Kart Stadium opening race 60.00
Super Mario Odyssey 1.3.0 Cap Kingdom: running, jumping, and landing 59.46
Tears of the Kingdom 1.4.3 Beneath Hyrule Castle cave 29.90
Xenoblade Chronicles 3 1.0.0 Opening combat and Everblight Plain 29.72

These checks cover the listed opening areas, not full playthroughs or performance comparisons. FPS averages are sampled gameplay results; startup and loading are excluded, and shader compilation can still cause stutters.

suyu v0.0.13

v0.0.13 release
Release notes · 10 files

suyu v0.0.13

Faster Vulkan pipeline setup, smaller game exports that run from your own game file, a new optional portable export type, and clearer key and firmware setup.

Faster Vulkan pipeline setup

On supported Vulkan drivers, suyu now reuses compiled graphics pipeline parts and links a new pipeline quickly when it is first needed. This reduces long waits caused by graphics pipeline creation. Suyu builds a fully optimized version in the background after the first use.

The change is enabled only when the driver reports fast graphics pipeline library support. Other devices keep the previous path automatically; this includes the tested macOS MoltenVK build. Android also keeps the previous path by default. If a supported driver has a rendering problem, turn off Use graphics pipeline libraries in the Vulkan settings.

This improves pipeline waits; loading transitions can still pause for other reasons.

Game exports run from your game file

Exports no longer copy the game's files. An export records the game file you selected and opens it at every launch with the keys installed in suyu, the same way suyu itself plays it. A JIT export is now tens of megabytes instead of the size of the game. If the game file has moved, the export says so and names the path it expected; export the game again from its new location.

Exports are built in a temporary folder, checked, and only then moved into place. An existing export is never silently replaced.

Portable exports (optional)

Package type → Portable copies your game file, its installed update and its installed DLC into the export unchanged, sealed to the console your keys come from. The export then runs without the original file. On first launch on a PC without keys it asks for your key files and installs them for that user account, as Install Decryption Keys does; later launches start directly. Keys from a different console are refused. Keys and firmware are never copied into an export.

Portable needs an sd_seed entry in your keys. The export dialog shows whether it is present, along with your keys, firmware, game file, update and DLC.

Keys and installed content

  • Launching a game without keys shows a prompt with Install Decryption Keys… instead of failing. An export started with keys that don't match the game says so and names the keys folder.
  • suyu reads keys only from the key files you install, and keeps them in memory.
  • Content installed from NSP files keeps its tickets, so DLC and updates installed that way keep working after suyu restarts.

Faster on CPUs with extra cache

On processors whose cores have different amounts of L3 cache, such as Ryzen X3D chips with 3D V-Cache on one set of cores, suyu now runs games on the cores with the largest cache. On a Ryzen 9 9950X3D this made a Static export of TOTK 1.4.3 about 40% faster and the JIT about 60% faster with the frame-rate limit off. Other processors are not affected. To turn it off, clear Prefer the cores with the largest L3 cache in the CPU settings.

Static and Hybrid exports

Larger games now export and run as Static or Hybrid builds. These fixes came from testing The Legend of Zelda: Tears of the Kingdom.

  • Recompiled code is matched to the right game module. Hybrid exports that left a module to the JIT could attach recompiled code to the wrong module. Recompiled code is now matched by build ID. Exports also check that every recompiled module found its place before the game starts, and a Static export refuses to start if one did not, instead of crashing later.
  • Newer game builds load correctly. Recompiled modules from recent game updates (for example TOTK 1.4.3) were not recognised, so Hybrid exports silently ran fully on the JIT and Static exports stopped at startup.
  • Smaller, faster-to-build exports. The exact floating-point fallback code is now shared instead of repeated at every use. On Mario Kart 8 Deluxe a Static export is 26% smaller and builds in half the time, at the same speed. On TOTK this brings the Static image under the 2 GB size Windows allows for one program.
  • Faster floating-point code in more games. Games that run with flush-to-zero enabled, such as TOTK, now use the fast native floating-point path when no tiny (subnormal) values are involved. Results stay identical to the console. On TOTK 1.4.3 a Static export now holds 30 fps in-game, up from 20.
  • Faster Hybrid exports. Hybrid exports now also compile one family of vector shift instructions that they used to hand to the JIT. On TOTK 1.4.3 this took a Hybrid export from about 23 to 29 fps in-game.
  • Fixed a code-generation error in a type of memory load (PC-relative literal loads) that could crash Static games at startup.

Re-export Static and Hybrid games to get these fixes. Cached builds from older versions are rebuilt automatically.

Mods in Static and Hybrid exports

Code patches (.ips / .pchtxt in a mod's exefs folder), including frame-rate patches, are now built into Static and Hybrid exports. The patches you have enabled in suyu when you export are compiled in. At every launch the export checks that the same mods are still installed in suyu and unchanged; if not, it says which mod changed. Re-export after changing your mods.

Mods that add their own code modules and patch the game while it runs (for example UltraCam) can't run in Static exports. Use JIT, or a Hybrid export.

UltraCam 3.0 on TOTK 1.4.3: UltraCam 3.0 (from NX Optimizer) does not support game version 1.4.3, and the game crashes shortly after loading a save. tools/mods/ultracam_totk143_fix.py updates one of its code patterns for 1.4.3 in your own copy of the mod; see tools/mods/README.md. With the fix, TOTK 1.4.3 runs at 60 fps with UltraCam on JIT and Hybrid. UltraCam is by MaxLastBreath (CC BY-NC 2.0).

Other fixes

  • Fixed a crash in the CPU emulator when a game ran a half-precision FCMLA instruction, which the Switch's CPU does not support. It now raises an undefined-instruction exception, as the console does.
  • Fixed a case where an installed update could be ignored, so the game silently started an older version.

Downloads

Each release now includes a source archive of the bundled dependencies, and every package carries LICENSES/ and THIRD-PARTY-NOTICES.txt. Release packages are checked automatically before upload.

Re-export existing games to get the smaller exports; older exports keep working as they are.

suyu v0.0.12

v0.0.12 release
Release notes

suyu v0.0.12

Hybrid exports can now complete the final Windows link and return safely to JIT when generated code changes. The Windows download includes a matched build kit, and standalone games include their matching Control metadata.

Portable exports configure their host build for Release and normalize Windows paths. Windows release builds use the VS 2022 toolset baseline. The kit records its producer compiler version and rejects an older toolchain with an upgrade message before compilation.

Hybrid exports that build and launch reliably

Hybrid exports now recover when a generated module fails compilation or exceeds Windows executable size limits: the affected module uses Dynamic JIT while the remaining native modules stay compiled. The export still fails if it cannot produce the genuine Hybrid executable. Strict Static AOT retains strict failure behavior.

When a game changes translated code, Hybrid returns to JIT before executing stale instructions. This fixes the early TOTK guard abort. Re-export existing games to regenerate their code and executable; updating suyu alone does not repair an older standalone package.

Generated C is packed into translation units capped at 32 MiB, alongside the existing block-count limit. All emitted instructions are retained. A single block larger than the cap occupies its own unit. This reduces compiler memory pressure for large games.

Standalone exports now include and register the game's small matching Control metadata. This fixes Xenoblade Chronicles 3 initializing to a black screen and preserves the Control version for an explicitly selected update. Malformed, unreadable or mismatched metadata fails with an error before game initialization. Windows installed-data lookup also respects a valid process APPDATA directory.

Windows downloads include the export build kit

The Windows ZIP includes matched, relocatable host link inputs for Hybrid and Static AOT exports. A separate suyu source checkout and configured host build are no longer required. Keep the export-build-kit directory beside suyu.

Building an executable still requires an installed MSVC x64 C++ toolchain (updated VS 2022 17.14 / toolset 14.44, or newer), Windows SDK, CMake and Ninja. LLVM/Clang is optional. The bundled kit uses the default ABI 6 recompiler features; customized feature configurations require a matching source build. Personal game files, keys and firmware are not included.

Faster Vulkan pipeline setup

Drivers reporting fast graphics pipeline library support reuse compiled pipeline parts and link initial pipelines quickly, then optimize in the background. Other drivers retain the previous path; Android keeps it by default. If a supported driver has rendering problems, disable Use graphics pipeline libraries in Vulkan settings. Other loading work can still cause pauses.

Verification

Standalone Windows Hybrid exports were launched without the original game container as an argument. The CI-produced Windows ZIP was also used to relink Odyssey's four native modules and run that package for six minutes, followed by a three-minute camera-input test.

Game Scripted runtime check Observed result
Super Mario Odyssey 6 minutes staged, 6 minutes from CI kit, plus 3 minutes with camera input Cap Kingdom scene; all four native modules active; clean exits
Xenoblade Chronicles 3 10 minutes Everblight Plain gameplay; clean exit
The Legend of Zelda: Tears of the Kingdom Two 6-minute runs Opening cave gameplay; clean exits; oversized main module uses JIT

These are opening-gameplay smoke tests, not full playthroughs or a guarantee for every game, driver or hardware configuration. The recompiler smoke variants, coverage loop, module selection, source partitioning, Control-version selection, Windows data-path resolution and build-kit relocation/rejection checks passed. The final code also passed all eight platform/source preflight jobs for Linux, Windows, macOS, Android and libretro. Tagged binaries are produced by the release workflow only after every required platform build succeeds.

suyu v0.0.11

v0.0.11 release
Release notes

suyu v0.0.11

v0.0.11 makes Export Game something a player can use end to end: pick a game, get a package you can double-click or launch from Steam, with your controllers, settings and shader cache carried over. It also brings Apple Silicon Macs back, adds a RetroArch (libretro) core, and makes pre-compiled (static) games much faster: Mario Kart 8 Deluxe's race goes from about 24 to about 56 fps on Windows and to about 60 on macOS and Linux, with identical results.

Before you upgrade: re-export your old builds. Every static or Hybrid game build made with v0.0.10 or earlier must be exported again. This release refuses older builds on purpose instead of running them incorrectly. Re-exporting also picks up the speed-ups and the fixes below (real update version, shader cache, firmware, controllers).

Which CPU mode should I use?

  • suyu Dynarmic JIT (Baseline): the normal emulator, and now the default. It is the most compatible and runs Mario Kart 8 Deluxe at 60 fps (see Performance).
  • suyu Hybrid JIT + AOT (experimental): runs pre-compiled code and falls back to the JIT for anything the pre-compiled code doesn't cover. Performance varies by game; compare it with the Dynarmic JIT export.
  • suyu static AOT (Experimental): pre-compiled code only, with no JIT fallback. It now runs Mario Kart 8 Deluxe close to the JIT's speed, but it is tested on that one game; treat other games as experimental.

Downloads

File What it is
suyu-windows-x86_64.zip Windows 10/11 x64: suyu and suyu-cmd
suyu-linux-x86_64.tar.gz Linux x64: suyu and suyu-cmd (uses your distribution's Qt, FFmpeg and SDL)
suyu-macos-arm64.zip macOS on Apple Silicon: suyu.app, with suyu-cmd inside it
suyu-android-arm64.apk Android, 64-bit ARM
suyu-libretro-windows-x86_64.zip RetroArch core for Windows
suyu-libretro-linux-x86_64.tar.gz RetroArch core for Linux
suyu-libretro-macos-arm64.zip RetroArch core for macOS: put suyu_libretro.dylib and the suyu_libretro_libs folder in RetroArch's cores folder together
suyu-libretro-android-arm64.zip RetroArch core for Android (development build)
suyu-v0.0.11-source.tar.gz The complete source these files were built from, submodules included
  • macOS: the app is not notarized by Apple. The first time, right-click suyu.app and choose Open, or run xattr -dr com.apple.quarantine suyu.app.
  • Android: install the APK directly. Android asks you to allow installs from the app you open it with. The APK is signed with the public test key from the source code (see Known limitations). If another suyu APK is already installed, uninstall it first.

Highlights

Faster pre-compiled games

  • Static and Hybrid exports run much faster. On Windows, Mario Kart 8 Deluxe's race goes from about 24 to about 56 fps; on macOS and Linux it runs at about 60 fps, using about half the CPU it did. Game behaviour is unchanged: every speed-up is checked to give bit-identical results.
  • On Windows, Build exports compile the game's code with Clang when it is installed (winget install LLVM.LLVM, or Visual Studio's "C++ Clang tools for Windows"). Clang alone makes the game about 45% faster than Microsoft's compiler, and the export takes about 10 minutes instead of 22. Without Clang the export still works with Microsoft's compiler.
  • Re-export a game to get this.

Helping more games run pre-compiled

  • Playing a Hybrid export now records any code the pre-compiled part couldn't run and had to hand to the JIT: code loaded later, or instructions not supported yet. It is kept in the package and in your installed suyu, and grows with every session.
  • The export dialog shows what's known for the selected game, for example "no untranslated code in 5 Hybrid runs" (static AOT should work) or "7 instruction types still need Hybrid".
  • A new export of the same game version includes any recorded addresses it can translate.
  • Import / Export coverage file lets players pool this per game. The file holds only module IDs, code offsets and counts: no game code and no personal paths.

Export Game (Windows)

  • The CPU backend list now starts with suyu Dynarmic JIT (Baseline), which is selected by default. Build is the default format on Windows. Linux and macOS targets still produce Source only.
  • An Update row shows which game update the export will use, and a "From:" line shows where it comes from.
  • Install Update File… installs a game update (.nsp) after checking that it belongs to the selected game. If no update is installed, exporting asks whether to install one or export without it.
  • A game file with a bundled update, while updates are turned off for that game, is stopped before the export starts instead of failing at the end.
  • The default output folder is exports inside the source folder when suyu runs from a source checkout (Git ignores it). Otherwise it is your Downloads folder.
  • The progress bar follows the real work. The status line shows, for example, "Compiling 312/940 files (main)" or "Copying game data: X of Y MB".
  • Packages copy your suyu settings (graphics, CPU, audio, system, applets). Debug and unsafe settings, folder paths, input devices and personal data are left out. With Include custom game configuration ticked, the game's own settings are added on top.
  • Include transferable shader cache now works. Before, the cache went to the wrong folder, the JIT package had its disk cache turned off, and the dialog could untick the save, shader and config boxes before you saw it, so a package started with an empty cache and a new profile. On the JIT package this removed a 5-second freeze at race start.
  • Packages report the update's real version. Older packages showed "Ver. 1.0.0"; re-export them to fix it.
  • An exported game's title bar shows FPS, frame time, "Building N shaders" and the CPU backend, so you can tell when a stutter is a shader being built. While it prepares its shader cache at start-up it shows "Building shaders N/M" instead of looking frozen.
  • The F12 controls panel has a Resolution Scale choice. It is saved in the package and takes effect the next time the game starts.
  • A shader cache whose last entry is damaged (for example after a crash) now keeps every good entry instead of being thrown away.
  • Exporting the same game again is reliable. Old generated code is reused only when the game code matches, leftover modules are removed, and an old version number no longer carries over.
  • Mismatched game data (base game data paired with update code, and similar mixes) is refused up front instead of producing a broken build.
  • A Source export on Windows is labelled as C source code, and a Build export only reports success when the launcher actually compiled.

Keys and firmware in exported games

  • Exports never contain keys or firmware. An exported game reads them from your installed suyu: %APPDATA%\suyu\keys and %APPDATA%\suyu\nand on Windows, or the user\ folder of a portable suyu. A custom NAND folder set in suyu is honoured.
  • If prod.keys is missing, the game shows a dialog with the exact folder and the buttons Install keys in suyu (when the suyu that made the export is found), Open folder and Quit. Copying prod.keys into that folder works just as well.
  • If firmware is missing, the game warns and offers Continue anyway. Mii screens and some menus may fail without firmware.
  • Exported games now read the installed firmware. This fixes an empty "Select a Mii" screen in Mario Kart 8 Deluxe.

Controllers in exported games

  • The first gamepad becomes Player 1. More pads become Players 2–8 in the order they connect, as Pro Controllers.
  • Unplugging a pad disconnects that player. Plugging it back in returns it to the same slot (Players 2–8; an empty Player 1 goes to the next pad that connects).
  • Player 1 is never left empty: if all pads leave, the keyboard goes back to Player 1.
  • Devices that aren't gamepads are ignored. Left and right Joy-Con halves combine into one player automatically. The F12 controls panel has Combine Joy-Cons into one player and Split Joy-Cons into two players buttons.
  • The Home button works on gamepads.
  • Choosing Use keyboard or Rebind in the F12 controls panel turns automatic assignment off for that package.
  • Tested live with two pads (PowerA and Xbox Series X) at 60 fps in the JIT export.

Steam

  • Add to Steam library when the export finishes adds a non-Steam shortcut that runs the export's own program. It is named after the game and backend, for example "Mario Kart 8 Deluxe (suyu Dynarmic JIT)", "(suyu Hybrid JIT + AOT)" or "(suyu static AOT)".
  • …and replace an existing shortcut removes only suyu's own shortcut for that game.
  • Steam artwork (cover, banner, hero, logo, icon) is made from the game's icon. The optional …and fetch cover art from Wikipedia uses Wikipedia cover art instead; it sends the game's title to Wikipedia.
  • Your other Steam shortcuts are kept byte for byte. If suyu can't fully read Steam's shortcut file, it leaves the file untouched.
  • Restart Steam to see changes. Steam shows no description for non-Steam games; that is a Steam limitation.

Discord

  • While a game runs, Discord shows you as playing suyu. The activity names the game and uses its cover as the image. This works from the suyu launcher and from exported Windows games.
  • The cover comes from Wikipedia, which receives the game's title. In the launcher, the suyu logo shows until the cover is found. Turning off Show Current Game in your Discord Status (Configure → Web) stops both the status and the lookup.
  • Exported games have a new option, Show this game in Discord (cover art from Wikipedia), on by default. The cover is looked up once, while exporting; the game itself doesn't go online for it. To turn Discord off for an exported game later, set enabled=0 in the discord.ini next to its program.
  • In a NetPlay room, the room's name replaces the game's name. No address, password or join link is shared.

macOS (Apple Silicon)

  • Apple Silicon Macs work again (community PR #1 by @mmkzer0, thank you). Rendering goes through MoltenVK and Metal.
  • Fixed long stalls (0 fps for 10–40 seconds). The Mac JIT went from a 1.2 fps average to 56.6 fps, and RetroArch on Mac now reaches the race at about 59 fps.
  • Fixed a crash when the game ends (0 crashes in 9 runs, against 1 in 3 before).
  • Fixed a crash at renderer startup, a Metal window-surface lookup failure, and GPU cleanup after an error.
  • Fixed several MoltenVK visual problems: copies between images with mismatched block formats, and output fixups that touched unbound attachments.
  • Emulator threads use macOS priority classes (QoS), and the game window comes to the front when a game starts.

RetroArch / libretro core (new)

  • A Vulkan libretro core for Windows, Linux and macOS. It supports base game plus update loading with identity checks, TAS playback and core options.
  • A "Game X.X FPS · N ms" readout is on by default (core option Show Game Performance). While shaders are being built, the shader count replaces the frame time. Trust it over RetroArch's own counter, which also counts repeated frames.
  • Starting from a fresh RetroArch profile works: keys are picked up before boot, and bad or incomplete game files are rejected with a clear error instead of crashing.

Android

  • The Android app (APK) and an ARM64 libretro core build, with the stall fix above compiled in. The libretro core is an opt-in development build, separate from the app.
  • It has only been run in the Android emulator, which has no GPU and shows about 4–8 game fps. That is not a real-device result.

Everyday usability

  • View → Resolution Scale changes the internal resolution without opening Settings. A game's own resolution setting still wins, and the app tells you when that happens.
  • The status bar shows which CPU backend is actually running and counts JIT fallbacks, or shows "NO JIT".
  • The branch name no longer appears in the window title.

Graphics fixes (all platforms)

  • A shader pipeline that fails to build now skips that one draw instead of freezing the game.
  • Shader resources are declared the way stricter drivers expect, which fixes shader validation failures.
  • Texture storage support is checked against the format actually chosen. An empty texture slot is treated as "nothing bound" instead of causing a format error.

Other fixes

  • Shutdown: the GPU thread now finishes and stops before GPU services close. This fixed the Mac crash above.
  • Windows: fixed memory corruption when a game closes. The renderer was destroyed after the GPU channels it still pointed at, and its last flush wrote into freed memory; it surfaced as an intermittent crash on exit. Caught with page heap at the exact write, then verified: 0 corruptions in 7 runs with the fix, while the old build still stops.
  • Fixed a rare crash when a game thread ends, mostly seen while closing a game: the emulated kernel released a finished thread while holding the scheduler lock, so a lock it then needed could be "taken" without being owned. The thread is now released after that lock is dropped. 0 crashes in 8 debug-heap runs, against about 2 in 11.
  • Adding, repairing and removing Steam shortcuts uses safe all-or-nothing writes.
  • An exported game that fails to load its game now exits after showing the error, instead of carrying on with nothing loaded.

Performance

Mario Kart 8 Deluxe v4.0.0, game fps during a race (the game runs at up to 60), 3 runs per figure unless noted. Measured 2026-09-22 to 2026-09-24.

Dynarmic JIT Strict static, v0.0.10 code Strict static, v0.0.11
Windows x64 (Ryzen 9 9950X3D, Radeon RX 9070 XT) 60 23.7 56.1 (median 59) ¹
macOS Apple Silicon 59–60 53.7 59.9 ²
Linux x64 (RTX 3090) ³ 60 39.4 59.8

¹ A real Build export with default settings and Clang installed. Hybrid with Clang reached 51 fps in one run, before the other speed-ups. ² Reaches the 60 fps cap; the CPU it uses dropped 44%. ³ Static starts several seconds later than the JIT, so its race was measured with the TAS input delayed to match. CPU use dropped 50%.

Where the speed comes from (Windows, each step added to the previous one):

Step Race fps
Microsoft's compiler (v0.0.10 code) 23.7
Clang instead 34.6
Fast guest-memory access 36.0
Code checked once per change instead of on every block 40.2
Exact native floating point 56.3 (all three together; 53.1 without the code check)

Every step keeps results bit-identical, and each can be switched off (see For developers). Exports also now compile with full optimisation (/O2, -O2).

Shader cache on the Windows JIT package: 55.7 → 58.8 average fps, the 5-second freeze at race start removed, and the worst frames down from 66 ms to 26 ms.


Known limitations

  • Portable settings, the keys/firmware startup check and automatic controller assignment apply to Windows packages. Linux and macOS exports are Source only and have no bundled runtime.
  • Steam shortcuts go to the first Steam account found on the computer.
  • Re-exporting renames an existing Steam shortcut in place (for example to the new "(suyu Dynarmic JIT)" style names). Steam keys artwork and play time by a shortcut's name, so those start fresh for the renamed entry.
  • A game that isn't in your suyu library is named in Steam after its file name.
  • Joy-Con auto-assign and Combine/Split are covered by automated tests but haven't been tried with real Joy-Cons yet.
  • Very occasionally a game crashes during start-up (about 1 in 30 test launches, while system services start); launching it again works. Closing suyu while the Export Game dialog is open can also crash it. Both predate this release.
  • Static exports are tested on Mario Kart 8 Deluxe only. They start several seconds later than the JIT (about 8 s on Linux).
  • On Windows the full static speed needs Clang (LLVM). With only Microsoft's compiler the export still works but runs slower; that combination wasn't benchmarked with this release's speed-ups.
  • Android has not been tested on a real device, so its real performance is unknown.
  • The Android APK is signed with the public test key that ships in the source code, not a private release key. Only install it from this release page: anyone can sign an APK with that key. If a later release moves to a private key, updating will need an uninstall first, which removes the app's data, so back up saves before then.
  • In the Android emulator, black margins around the picture come from the emulator's camera-cutout setting, not from suyu.
  • On macOS RetroArch and Android, RetroArch's FPS counter reads higher than the game's real frame rate; use the core's own readout.
  • Static mode is per-title. Smash Ultimate in strict-static mode still hits uncovered code.
  • Discord: Join buttons and achievements are not supported. The NetPlay room name in Discord has not been tried in a live room yet. Exported games appear under their package name, for example "Mario Kart 8 Deluxe - Dynarmic JIT".
  • Downloads never include games, generated game code, keys, firmware or saves. The downloads include the Dynarmic JIT. A build without it is made from source with -DSUYU_NO_JIT=ON; separate no-JIT downloads are no longer published.

For developers

Static recompiler: ABI 6 (new default)

  • Exports now produce ABI 6: ABI 5 plus three negotiated features. An image reports its feature bits, and a host refuses any bit it doesn't implement, so an older suyu can't load a newer export by accident.

    • FM1, fast guest memory (bit 0): loads and stores read suyu's page table directly. Unmapped, special, GPU-tracked and page-crossing accesses fall back to the unchanged ABI 5 helper.
    • GG1, generation code guard (bit 1): a block's instruction words are checked once per code change instead of on every entry. The host moves the generation on every path that can change code: instruction-cache invalidation, map/unmap/protect, page table swaps, stores and host writes to watched code pages. Modules it can't prove stable (writable, aliased, device-mapped, raw pointers handed out) keep per-entry checking.
    • FPX1, exact native FP (bit 2): native host floating point is kept only when it provably matches the AArch64 result and flags; everything else uses the exact soft-float. The host enforces the FP mode it needs.
  • Switches: SUYU_AOT_FASTMEM=0, SUYU_AOT_GUARD_GEN=0 or SUYU_AOT_FPX=0 leave a feature out of an export (SUYU_AOT_FASTMEM=0 gives ABI 5, byte-identical to before). SUYU_RECOMP_FASTMEM=0, SUYU_RECOMP_GUARD_GEN=0 and SUYU_RECOMP_FPX=0 turn one off at run time.

  • Validation: byte-identity golden hashes with each feature off, new smoke-test modes for every fallback and hook, a differential memory fuzz, and an FP harness matched against Apple M-series hardware, including all 2^32 inputs of the unary operations.

  • Exactness fixes (all ABI versions): vector FRECPS/FRSQRTS were computed unfused; scalar FMAX/FMIN/FMAXNM/FMINNM returned the wrong signed zero; scalar FSQRT of a negative gave the wrong NaN sign on x86; FCMP/FCCMP, the FCMxx compares, integer↔FP conversions and scalar FRINT didn't set FPSR; vector FCVTMS/FCVTMU were decoded as FCVTZS/FCVTZU.

  • Windows exports and clang-cl: the generated modules build as their own project with clang-cl (found via SUYU_CLANG_CL, LLVM's default path, Visual Studio's bundled Clang, then PATH), and are linked into suyu-cmd-static with MSVC's linker. SUYU_RECOMP_COMPILER=msvc forces the old path; any clang-cl failure falls back to it. The compiler used is recorded in the log, README and aot_manifest.json.

  • Coverage gaps: Hybrid and strict runs merge user\log\recomp_gaps.json (lookup misses as module build ID + offset, modules that ran without an image, unimplemented opcodes, run counts; schema-versioned) and the installed suyu's recomp\gaps\<TITLEID>.json. The exporter passes recorded offsets as extra discovery roots only for an exact build-ID match, logs mismatches, and records the count in aot_manifest.json; with no gaps the output is byte-identical. In practice the emitter already indexes nearly all of .text, so most recorded gaps will be late-loaded modules and unimplemented opcodes, which re-export cannot fix.

Static recompiler: ABI 5

  • ABI 5 validates generated image revision, instruction coverage and memory guards more strictly. ABI 4 images are rejected at build time, by the CMake header check, and again at runtime, by recomp_image_abi().
  • Correctness over speed: ABI 5 deliberately reverts two experimental speed-ups from the iOS branch.
    • Per-entry code verification is restored. The shared generation-counter guard (check-once code guard) has been removed, and the guard_generation slot is kept for layout only. Hosts must set it to null.
    • The native AArch64 host-FP fast path is removed. It didn't raise guest FPSR flags, and its results depended on the ambient host FPCR.
    • FP conversions now use integer significands with exact FZ, rounding and sticky FPSR behaviour. Recovering that speed without losing correctness is future work.
  • Relocation policy rtld-only: relocations are pre-applied to rtld only, which matches Dynarmic's behaviour. Fixes deferred nn::ro NRO imports never binding (0/909 fighter slots bound in Smash). The rejected alternatives remain available behind SUYU_RECOMP_RELA_POLICY.
  • Correctness fixes:
    • Guarded guest reads are safe across page boundaries.
    • Page-crossing accesses no longer take the host-pointer fast path.
    • The exclusive reservation is cleared when a core switches guest threads.
    • Undefined weak symbols resolve to zero.
    • Exclusive state is cleared per thread.
    • Shifted-register encodings that are undefined for sf==0 && imm6>=32 are refused and counted as uncovered.
    • Word-level normalisation and sticky flag scans.
    • Fixed-point conversion and FPSR behaviour have synthetic regression coverage.
  • Stricter loading: known base RomFS / updated ExeFS and standalone NCA code/data mismatches are rejected. Two non-base views from an installed update still need title-specific verification before a package can be called playable.
  • Page-crossing code guard: a block that crosses a page boundary is now checked with one memcmp per page instead of a host callback per instruction word, and the host's instruction-read callback checks page validity once per word, not per byte. The same words are still compared, and any mismatch or unmapped page still stops at the exact word. On Linux x64 this cut MK8D's boot pause from 7.7 to 5.8 seconds. Re-export to get it.
  • Hybrid's frequent JIT fallbacks for one vector instruction are deliberate and help its performance.
  • Generated C no longer inherits the host's -Werror.
  • Keys and window naming for static exports now work on non-Windows hosts.
  • Env-gated diagnostics (inert unless set): targeted PC sampling, handover cutoffs, a guest-thread dump with arbiter keys, and a kernel lock trace.

Core

  • arm64 exclusive monitor: guest spin loops could saturate Dynarmic's global exclusive-monitor lock and starve other cores (0 fps for 10–40 s). The fix is enabled for arm64 GCC/Clang builds only (macOS, Linux arm64, Android); x64 is unchanged.
  • Shutdown order: the GPU thread is drained and joined before GPU services close. macOS: 0/9 exit crashes, against 1/3 before.
  • Renderer destruction order: GPU::Impl::~Impl now destroys the renderer before channels/scheduler. Otherwise ~RasterizerVulkan → Scheduler::Finish → InvalidateState ORs into a freed channel's Maxwell3D dirty flags (Windows 0xC0000374 at a later free). Found with per-process full page heap.
  • Kernel thread release: KThread::FinishTermination now calls Close() after its KScopedSchedulerLock scope. A KLightLock wait nested inside the scheduler lock never blocks for host threads, so Finalize() (TLS unmap under the page-table lock, UnregisterThread under m_list_lock) could "acquire" a contended lock without owning it, corrupting the lock tag and ending in KThread::EndWait on a stale wait queue.
  • Exported games read the installed firmware. Without it, the MK8D Mii thread was killed in strict static mode.
  • The application process is released before the kernel is torn down, which fixes a shutdown-ordering crash.
  • The fastmem arena is placed elsewhere when the low address window can't hold it. This is needed on 16 KiB-page hosts such as Apple Silicon.
  • The frontend can override the reported application version, and the override is saved in settings.
  • Explicit base/update content selection goes through a read-only provider and rejects a second, unrelated launch path.
  • Logging is rate-bounded per call site, so one repeating message can't flood the log.

Frontend and packages

  • suyu -install-keys and suyu -install-firmware open Tools → Install Decryption Keys / Install Firmware once the window is up. An exported game that is missing either starts suyu this way.
  • Exported packages look for prod.keys or prod.keys_autogenerated in the installed suyu's keys folder, and for firmware in the package's own NAND first, then the installed one (including a custom nand_directory).
  • Controller auto-assignment is stored per package under [Controls] as auto_assign_controllers and auto_assigned_pads.
  • Steam shortcut edits preserve other entries byte for byte and leave shortcuts.vdf untouched when it can't be fully read.
  • Discord Rich Presence (USE_DISCORD_PRESENCE) is now on by default for desktop builds, and suyu-cmd publishes it too. suyu-cmd reads discord.ini (enabled, cover_url) from its own folder and makes no network requests; without the file it publishes the title with the suyu logo. The Wikipedia lookup is shared with the Steam artwork step. See docs/discord-integration.md.

libretro

  • Repeated frames are marked as repeats.
  • The Show Game Performance core option (on by default) applies immediately.
  • The core reports suyu's real version and commit to RetroArch instead of "0.04".
  • The black margins seen in the Android emulator came from an emulator camera-cutout setting, not the core.

Tooling and measurement

  • suyu-cmd -t / --tas replays a TAS script headlessly.
  • SUYU_CMD_PERF_SAMPLE=1 logs game_fps, system_fps, frametime_ms and speed once per second, as a series. Stalls show up as gaps instead of disappearing into an average.
  • SUYU_CMD_CAPTURE_DIR, ..._FIRST_SEC and ..._INTERVAL_SEC take periodic screenshots. Bounded capture runs stay off the active desktop.
  • The video core attributes frame stalls to where the wall-clock time actually goes.
  • scripts/mk8d-tas/ adds a source-only 6,001-frame TAS for MK8D v4.0.0. It covers menus, then Mushroom Cup, then the opening of Mario Kart Stadium.
  • iOS research: a separate static-AOT diagnostic host lives under research/ios. It ran signed no-JIT gameplay frames on an iPhone 16 Pro Max. It's research only and isn't a release target. The ~2× iPhone speed-ups tried there are not in ABI 5; see above.

Build and release

  • The build no longer requires the Qt Svg component. The macOS MoltenVK configuration is fixed.
  • CoreTiming tests no longer depend on the order they run in.
  • suyu-v0.0.11-source.tar.gz includes all 37 submodules. The source archives exclude opaque prebuilt binaries, developer caches and generated proof outputs.
  • The source archive carries GIT-COMMIT, GIT-TAG, GIT-REFSPEC and GIT-RELEASE, so a build from it reports v0.0.11 instead of "1.0.0".

What was tested

  • Windows: GUI, CLI and libretro builds. Export regression fixtures. Nine libretro lifecycle modes, plus TAS and core-options smoke tests. MK8D v4.0.0 JIT, Hybrid and static exports measured in a race (table above), and two-pad controller assignment in the JIT export. A default-settings Build export through the GUI (clang-cl, ABI 6) raced 3 times at 55.6–56.6 fps.
  • Linux: GUI, CLI and libretro builds. CTest 71/71, focused recompiler tests 24/24. MK8D JIT at 60 fps; strict static ABI 5 at 39.4 and ABI 6 at 59.8 fps, 3 runs each on the same delayed TAS input.
  • ABI 6, all three platforms: every race run finished the TAS with zero critical errors and zero JIT fallbacks, the generation guard kept all modules on its fast path, and FPX1 was negotiated.
  • macOS ARM64: GUI, CLI and libretro builds. CTest 55 passed, with 16 expected fastmem skips on 16 KiB pages. Focused tests 24/24. JIT, static and RetroArch reached the race.
  • Android: the ARM64 debug APK built and its debug signature verified; release signing was not tested. The opt-in libretro core built and showed on-track movement in a software emulator (low fps, not a real-device benchmark).
  • Strict-static MK8D v4.0.0: the TAS replay reached a rendered, controllable race segment with zero JIT fallbacks and zero critical errors. This is a per-title test, not a compatibility guarantee.
  • Discord (Windows): Discord showed suyu as the current game from the launcher and from an exported game's program, and cleared it when the game closed. The cover lookup found MK8D's cover, and the switch in discord.ini turned Discord off.
  • Not established: firmware applet CPU-backend attribution.

Full changelog: v0.0.10...v0.0.11

suyu v0.0.10

v0.0.10 release
Release notes

suyu v0.0.10

This checkpoint improves static execution through title screens, controller prompts and menus, with attract sequences rendering in the tested configuration. Static execution remains experimental and slower during some loading and transition sequences. Use Hybrid for the best performance.

The CPU choices are:

  • Suyu Static (Experimental): ahead-of-time compiled code with JIT fallback refused. The separate no-jit downloads are built with SUYU_NO_JIT=ON; Dynarmic is absent from those binaries. Missing static coverage stops execution.
  • Dynarmic JIT (Baseline): dynamic compilation for comparison and compatibility.
  • Hybrid: static code with Dynarmic fallback, recommended for normal use and performance.

ABI 4 uses a bounded, nonrecursive loop within each compiled module to reduce returns to the host dispatcher while retaining execution limits. Automatic per-title bundles are guarded against incompatible or stale generated code. This release uses ABI 4: regenerate older static bundles before using them with these binaries.

TAS recording and replay can start from boot, keeping the input origin consistent. Keep separate baseline and static replay fixtures when loading progress differs, and allow idle time after the final command when observing a transition. Input exhaustion alone does not prove that the expected screen or gameplay state was reached.

Validation covers title/menu progress, attract rendering and the race starting grid in the tested configuration. The grid was reached during bounded idle observation after the input replay ended, with no JIT available and zero fallback attempts. Race entry is delayed relative to the JIT baseline; exact EOF image parity and full-race replay validation remain open. This release does not establish universal title compatibility or frame-exact parity. No new performance benchmark is claimed.

The standard Linux and Windows downloads support Dynarmic, Hybrid and static execution. Selecting the static export mode in a standard download disables fallback in its compiled launcher but does not remove Dynarmic from an ordinary host build. Only the separate no-jit downloads guarantee its absence; they require a locally exported ABI 4 static bundle. Each no-JIT package includes an audit record from checks of build inputs and executable symbols or link maps.

suyu-v0.0.10-source.tar.gz contains the tracked source used for these builds, including submodules. GitHub's automatic source archives omit submodule contents. No games, generated game code, keys or firmware are included or acquired by the release workflow.

Linux and macOS hosted loading accepts both CMake-prefixed library names and existing bare library names. The v0.0.9 candidate was stopped before publication after this portability issue was found; its tag remains unchanged.

suyu v0.0.8

v0.0.8 release
Release notes

Built from v0.0.8 (3428fded44a67dfdd4b63cc50b82da22ff018ed6) by GitHub Actions.

suyu-v0.0.8-source.tar.gz is the complete source this was built from, submodules included. GitHub's own source links above exclude submodules.

No game data, keys or firmware are included here or fetched by anything in it. You supply your own.

suyu v0.0.7

v0.0.7 release
Release notes

Built from v0.0.7 (642cd72ff64dd7bcc69a5741da7be4ea925b4820) by GitHub Actions.

Fixes corrupted 3D geometry in statically recompiled code caused by FRECPE/FRSQRTE being decoded as integer conversions. Reciprocal estimates now execute natively with architectural FPCR/FPSR handling. Also corrects FABS/FNEG versus FCMLT decoding and upper-bit clearing for S-register pair loads.

Existing static modules must be regenerated. Install this emulator, re-export every module, rebuild the generated code, and replace the old loaded modules. Updating the emulator alone does not fix arithmetic already compiled into a module. Source builders must rebuild the emulator before exporting because the emitter is compiled into it. GNU module builds should use -O1 -foptimize-sibling-calls; reset an old CMake cache or set the flags explicitly.

Controlled visual A/B checks reproduced corruption with the old arithmetic, clean geometry with native estimates, and corruption again when reverting. GCC/UBSan and MSVC synthetic checks cover estimates, normalization, neighboring conversions, unary operations and pair loads. Manual menu transitions were not comprehensively tested.

The Windows and Linux archives contain the emulator and static-code exporter. They do not contain precompiled game modules. suyu-v0.0.7-source.tar.gz is the complete corresponding source, including submodules; GitHub's automatic source archives omit submodules.

No game data, generated game code, captures, keys or firmware are included.

suyu v0.0.5

v0.0.5 release
Release notes

Static recompilation users: this release predates a critical FRECPE/FRSQRTE decoding fix for corrupted 3D geometry. The fix is available on mk8-recomp; v0.0.7 binaries are available. Update to the fixed emulator, then re-export and rebuild all static modules. Updating only the emulator does not repair existing generated code.

Built from v0.0.5 (2eed0a7b29f365585cd341ad02578cc0455f592c) by GitHub Actions.

suyu-v0.0.5-source.tar.gz is the complete source this was built from, submodules included. GitHub's own source links above exclude submodules.

No game data, keys or firmware are included here or fetched by anything in it. You supply your own.