What brings together retro gaming, modding tools and .NET UI creation frameworks? If you answered “the Ryujinx May Progress Report” then give yourself a medal; if not then you owe us a box of cookies. Some nice ones!
Before we get down to business and into the meat of the changes that were implemented in May, give our patreon goals a look:
Vulkan GPU Backend - still in progress.
A public test build is delivered and is available here!
ARB Shaders - Goal reached in April 2021.
Work is ongoing alongside Vulkan, please wait a little while longer until we are able to deliver this update into a state we are happy with.
ARB shaders will further reduce stuttering on the first run by improving the shader compilation speed on NVIDIA GPUs using the OpenGL API.
$2000/month - Texture Packs / Replacement Capabilities - almost there!
This will facilitate the replacement of in-game graphics textures which enables custom texture enhancements, alternate controller button graphics, and more.
ETA once the goal is sustained: ~3-4 weeks
$2500/month - One full-time developer - Not yet met.
This amount of monthly donations will allow the project's founder, gdkchan, to work full-time on developing Ryujinx. All our contributors currently only work on the project in their spare time!
$5000/month - Additional full-time developer - Not yet met.
This amount of monthly donations will allow an additional Ryujinx team developer to work full-time on the project.
Everyone ready? Those cookies in the mail yet?
The GPU section starts this month, alas, with regression fixes. Luckily, they were spotted in the rather popular titles of Pokémon Legends Arceus and Xenoblade Chronicles 2, and as we should all know by now, those people are vocal. The first regression was causing some considerable vertex explosions and other lighting bugs in Legends Arceus, courtesy of an oversight in texture sync where two or more threads encountered and tried to read the same memory region. Forcing both threads to wait on the data present resolves this, and with that we hope ‘texture sync’ is not mentioned in one of these reports for at least a few months!
The second issue originated in the vertex buffer calculation fix mentioned last month, which was crucial to stop certain titles using massive amounts of memory on boot and just crashing. Unfortunately, while the solution worked great for those games such as for Super Mario 64 and Perky Little Things, some other games also make use of these buffers, and calculating them in the same way can and will cause graphical issues. If the draw is not indexed, then we cannot calculate the vertex buffer size in the same way, as the result is ultimately meaningless. By returning to the previous calculation for these specific unindexed draws, cutscenes in Xenoblade Chronicles 2 return to their prior form.
A bug in the shader recompilation stages (triggered with a driver change or cache version bump) was also squashed this month by forcing Ryujinx to prefetch the GPU capabilities before it started to translate your shader cache. The backend threader only expects one thread to actually submit commands at a single time, and when lots of them asked for the capabilities at once, it really was luck of the draw whether a crash would occur.
Alright let’s move away from stuff breaking and onto our tame GPU developer riperiperi. Some say that only snaking in Mario Kart DS can make him smile… Or that every Tuesday, at exactly 12pm, he sees visions of a blue shy-guy performing a satanic ritual. All we know is that he managed to get two new Mario Kart titles working this month and it isn’t a coincidence.
Let’s start at the beginning with Mario Kart 64. A game that, no offense to any boomers, genuinely sucks. It had to be said. Either way, the recent Nintendo 64 NSO service includes this game as part of its collection, and it didn’t take long to spot some problems with rendering in certain areas, like the restore point previews, the blur effect on the game-select screen and also the Mario Kart 64 jumbotron!
The official Nintendo Switch Vulkan drivers used in the N64 NSO rendering, and the Nouveau OpenGL drivers used in plenty of homebrew applications, make use of disabling viewport transformations which your system APIs do not support. As such, this needs to be implemented manually by transforming the vertex shader to match a provided viewport. If all of that was complete jargon then just know that this fixes a lot of games that used to have missing HUD or UI elements.
Moving forward in time we eventually reach Mario Kart DS, which can be emulated on the Switch using a homebrew emulator like MelonDS. This will crop up later, but for now we’ll have to ignore it and move straight to Mario Kart 7, for which the second of riperiperi’s fixes come into play.
Citra does indeed run on the Switch via homebrew, but there were some serious graphical issues when it was rendered in Ryujinx. The first of which was seen above with the viewport transforms, but the second provides alternative StencilOP enum values that the Nouveau OpenGL driver can make use of. It’s hard to spot but this does fix missing shadows in some 3DS games and potentially other homebrew emulators.
If you’ve followed along so far, you may have got the impression that there has been a fair bit of focus on homebrew and some rather unconventional things this month, and you’d be absolutely correct. The graphical stuff is just one side of a massive coin involving everything from official and unofficial emulation, retro games galore and… Super Smash Bros. Ultimate modding?
The road to these changes have been months in the making and as such we have one hell of a tale to tell.
The story actually starts in late 2020 with the release of Super Mario 3D All-Stars; an interesting release to say the least, as it contained 3 games that were all, at least partially, emulated! While both Galaxy and Sunshine were quickly bootable and in-game, Super Mario 64 remained a problem child for over a year due to its dependence on Firmware 10.0.0 and, specifically, the JIT (Just-In-Time) services it brought.
At the same time as Nintendo’s experiments with emulation there were already plenty of fan-favorite emulators being ported to the system, such as MelonDS, PPSSPP, mGBA and projects like Skyline and ARCropolis gaining steam in the modding community. What ties all of these together is actually how they utilize code memory syscalls and can actively generate or self-modify code on the fly.
So let’s work backwards from the game that started all of this. Super Mario 3D All-Stars. Specifically Super Mario 64.
- This game needs the JIT services implemented.
- For these to function they depend on the code memory syscalls to also be implemented and these services will also get a bunch of other stuff working.
Sounds great right? One issue.
Without getting extremely technical, to accurately implement these calls Ryujinx needs to support what’s called ‘memory aliasing’, and it needs to support it on the fast memory mapping modes (previously only the slowest Software mode was functional).
gdkchan started the journey as such by rewriting a large section of the memory management system to support memory aliasing on the fast memory manager modes. Unfortunately, or fortunately, the new host APIs required for these changes were only implemented on Windows 10 and beyond. This marked the first death knell for Windows 7/8.
With this implemented, the code syscalls themselves could finally be implemented and some real results were starting to take shape.
Only the JIT services remained and here gdkchan chose a unique route. Normally we use a ‘HLE approach’ (High Level Emulation) for system services, where the service is reverse engineered and re-implemented directly in software. However, this particular service is quite different from anything else and is only initialized when a dedicated ‘PrepareForJIT’ function is called. In a really cool milestone, and to minimize the impact of running such a service when it isn’t actually needed, Ryujinx is capable of running the service directly off the firmware files in an ‘LLE’ (Low Level Emulation) fashion. Ignoring how awesome it is that our kernel and filesystem accuracy is capable of running real system services directly at playable speeds, this was the final puzzle piece in the 3D All-Stars issue. The N64 NSO emulator had also been released at this point so two birds with one stone and all that.
The modding plugin system Skyline and its most popular plugin ARCropolis also heavily abuse these services to elevate modding beyond simple replacements and into some truly wild creations. ARCroplois needed one further change to boot, which was the partial implementation of the GetProcessInfo atmosphere extension, but now functions at a core level.
As is the nature of homebrew, we cannot guarantee future changes to any of the programs or plugins listed above will work forever, and as such we recommend that, at least for the time being, all crashes or unexpected behavior should always be cross-referenced with hardware before raising issues with either our own issue tracker or the tool itself. We’d also like to extend thanks to the teams behind the modding plugins and for any mod creator who have remained patient with our lack of Web Applet functionality, and hope that in the future such specific support won’t be required. So thank you!
Hope you aren’t burnt out because gdkchan isn’t finished with the CPU changes just yet! Following the implementation of memory aliasing he quickly resolved a Windows-exclusive memory leak, and then further refactored (basically re-organised) the CPU interface to completely decouple it from the core emulator.
Emulators are made of multiple parts, and ideally each of these parts should be completely removed from each other and accessed through ‘interfaces’ to allow a very modular design. Most of Ryujinx is designed like this as anyone who has looked at our source code repository would know:
To conclude the month, gdk went on to rewrite the SVC handlers to use the new source generators that .NET 6 provides, instead of Reflection.Emit. Genkai Tokki Moero Crystal H now goes in-game but the main benefits come from the shifted compute cost from run-time to compile time.
A more abstract future benefit is that this was one of the last hurdles to make Ryujinx ready for .NET 7’s native Ahead-Of-Time compilation; a feature that should reduce startup times and improve program responsiveness/latency all-round.
It’s been a while since there’s been enough UI work to make a whole section for it, but these are the times we live in! May certainly has enough to warrant a dedicated section however with the merge of the first part of the hotly anticipated move to Avalonia.
As mentioned above this is just part 1, and further work needs to be undertaken before the UI becomes the default and GTK is banished forever. The current roadmap for Avalonia:
Part 1: UI GPU backend, Main window and App host (merged 15/05/22).
Part 2: Settings window and all its child windows and controls (currently open).
Part 3: Every other non-settings related window and controls.
Part 4: (if required) General cleanup and fixes. GTK begone.
Nevertheless, the Avalonia project was added to the build scripts for anyone who tests our pull requests, the build project itself was cleaned up and standardized with the rest of the program and a GTK specific DPI-aware workaround was removed to provide a slightly sharper image on systems with two monitors, as Avalonia handles this natively.
Shortly after, a system scaling bug was noticed where, if the user had set a scale factor above 150% in their OS, the framebuffer would only present 1/4 of the final image. By scaling this end framebuffer to match the OS scale factor too this issue was resolved.
As always the backend infrastructure that Ryujinx makes use of for everything from input, rendering and filesystem are constantly on development paths of their own. This month saw the following infrastructural changes:
- SDL2 was updated to 2.0.22 which brings support for some more controller types (G-Shark pads) and some further motion fixes on Linux.
- The artifacts generated from pull requests now reflect the 1.1.0 naming scheme instead of the old 1.0.0 AppVeyor scheme.
- Removal of an assert in debug builds. No impact on user-end release builds, just to make the command line happy!
- The Amiibo png image path was fixed so that it actually shows up in the window.
- Audio renderer error message was made clearer as to meaning when logged.
- Homebrew that are packaged inside an NSP are now bootable. Previously the emulator would error out and inform that the NSP had no main NCA.
Some HID services were also cleaned up and improved this month which resulted in RetroArch and potentially other homebrew being bootable.
Trendsetters as ever, this month also seems to have been the rallying cry to the death of Windows 7/8 in the emulation scene, and with good reason. Lack of driver updates, modern memory mapping APIs and lack of .NET 6/7 support are all problems that cannot be ignored forever, and so, on April 24th we made the announcement that support for those older systems would be dropped from June 1st 2022.
This started a bit of a debate online particularly on Reddit, but unfortunately we can either continue to advance Switch emulation or we can support legacy operating systems. We can't do both. All Ryujinx builds now directly target Windows 10 1803 and above in their build scripts, and we are no longer accepting issues from or offering support to users who are on older versions even if by some miracle the software still boots.
If February was the month of the CPU and April the month of the GPU, then May surely is the month of ‘Cool-Stuff’. Emulators within emulators, homebrew titles running left, right and center and modding support being improved made it a wild ride. We always say this but anyone who’s joined us on this journey via patreon donations, code contribution or being active around the community truly cannot be thanked enough!
If you're reading this then you’re an emulation fanatic like us! If you know some C# and .NET, our door is always open to see new code contributors who can take on anything from single-line typos to whole service implementations. Above your paygrade? Simply giving us feedback, opening issues on GitHub or just reporting compatibility helps us out enormously too!
See you next time!