Wow. What a month. November is not typically this eventful, but sometimes a little spice is exactly what the Doctor ordered.
LDN being introduced to the number 3, macOS meeting a graphically demanding program, and Ryujinx facing its final adversary… the cardinal direction, North!
All-in-all we’ve had a busy month, with two major feature releases and that pesky company GameFreak deciding to push out those new indie games. What were they called? Red and Blue? Oh sorry, Scarlet & Violet. It’s getting hard to tell the difference between new and old games when they look like this, huh?!
Before that rant goes any further, please check out our remaining and final set of patreon goals. Once more, all the goals are planned to be actioned eventually, although if a sufficient monetary amount is sustained then focus would be shifted to deliver the respective feature in a reasonable timeframe.
$2000/month - Texture Packs / Replacement Capabilities - getting closer!
This will facilitate the replacement of in-game graphics textures which enables custom texture enhancements, alternate controller button graphics, and more.
$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.
Done reading? Alright, let’s whirl.
How could this section start with anything other than Pokémon? Our streak of playable day 1 titles continues although not without hitches. Some issues were immediately apparent, both in the graphical and performance department. Firstly, resolution scaling on Vulkan was a tad broken as showcased below:
This was isolated to Vulkan and was caused by the SPIR-V scale helpers being unable to find Array textures. With this issue addressed, beaches and lake beds were no longer covered in perpetual grass.
The Mesa Radeon Vulkan driver, RADV, was also displaying some rather interesting albeit random behavior where occasionally, on boot the whole screen would be covered in a bright white filter. The Switch will always sample the initial dummy texture as 0 but Ryujinx was not forcing this. By clearing the texture to (0,0,0,0) when on creation, the variability is removed and so is the holy light of purgatory.
While users with newer graphics cards and Linux users were having all of the fun, owners of Nvidia’s GTX 700 (and even some mobile 900 series) cards were getting no graphical output at all. As with most bugs of this type, we can usually trace them back to an unsupported feature or extension. This time it was the VK_EXT_shader_viewport_index_layer, and its OpenGL equivalent, that needed workarounds. By moving the gl_Layer from vertex to geometry if the GPU doesn’t support the extension, these older cards no longer have any issues rendering as nicely as their younger siblings.
The final Scarlet/Violet graphical bug fix that was squeezed into November resolved an issue with the Pokémon stats graph, where parts would be completely cut off and fail to render. It turns out that GameFreak, to no one's surprise, is using extremely dated rendering methods. Polygon topology is used to draw the hexagon but most Vulkan drivers, and a fair few OpenGL drivers, do not support the extensions required anymore. Nvidia does still support GL_POLYGON in compatibility mode but no such equivalent exists in Vulkan. Luckily for us, convex polygons can be identically rendered via a triangle fan (imagine a fan of triangles around a single point) which, as has been stated in previous reports, is a breeze for modern computer graphics renderers.
To close out our Scarlet & Violet monologue, a random crash was resolved; but, we still have those pesky performance issues mentioned earlier to talk about. Thankfully, such issues affect more than just these games so we can take a breather from all this Pokémon talk.
First stop: Pokémon Sword & Shield. What? We managed a quick breath there. Didn’t you?
Jokes aside, they’re but a couple of games that have seen some rather startling performance improvements this month due to an avalanche of activity in the GPU emulation department. Given how varied and numerous the changes here are, it would be ill-advised to give them individual spotlight for the sake of all our time, but here’s a quick rundown. See the graph a bit further down for a TL;DR.
- A HLE macro for DrawElementsIndirect was implemented. This helps performance in a few titles such as: Monster Hunter Rise, NieR Automata & Pokémon Scarlet/Violet.
- Redundant buffer updates no longer trigger updates. Improves performance in Xenoblade titles when using Vulkan by around 25%.
- _volatile flag is once again allowed to be set from MultiRegionHandle. Fixes a regression to the performance of Pokémon Sword & Shield when using Vulkan. Once again places Vulkan and OpenGL on-par in performance in these titles.
- CB0 accesses have been eliminated when storage buffer accesses are resolved. Improves performance in Xenoblade titles and The Legend of Zelda Link’s Awakening by up to 30%.
- Preload command buffers are no longer created outside the render pass on Vulkan. Improves performance by around 15% in Pokémon Scarlet/Violet.
- All buffer assignments are now sent simultaneously instead of one at a time. Improves performance in GPU bottlenecked games such as Super Mario Odyssey by around 7%.
- Non-prefetch command buffers are now accessed directly. Can improve performance in Super Mario Odyssey and Pokémon Sword & Shield up to 3%.
- Locking on the buffer cache has been heavily relaxed. Further improvement to GPU-bound titles such as Super Mario Odyssey by up to 5%.
That’s a lot of bullet points containing Super Mario Odyssey and Pokémon. Don’t be disappointed though, these are just our developers’ go-to titles to test any GPU thread improvements at the moment due to their relative ease on the CPU-side. Tying all of those together; here is a quick graph of some popular titles’ performance at the start of November vs the start of December:
The big winners here are the two Pokémon entries with Scarlet/Violet receiving a staggering 81% performance uplift in under two weeks from its release. Even titles with hideous bottlenecks elsewhere, such as The Legend of Zelda: Breath of the Wild, still saw a healthy improvement of around 10%. It’s worth noting that both Xenoblade Chronicles and Sword/Shield were already hitting similar framerates on OpenGL. November has eliminated that gap and allowed both backends to reach new performance heights for any users who may still need to switch between OpenGL and Vulkan.
Moving away from all those figures and stats onto something a little more visual again, a change was mentioned a couple of months ago that allowed Fate Extella: The Umbral Star to render on OpenGL. We claimed a similar fix for Vulkan was on the way and November indeed provided.
Due to Vulkan needing a little extra work in the form of implementing support for depth-stencil resolve and some changes to texture compatibility rules, Sonic Colours Ultimate also now renders on Intel GPUs using Vulkan. This game already worked for NVIDIA but it seems Intel’s driver is a little more picky.
Nights of Azure 2: Bride of the New Moon highlighted a flaw in the way Ryujinx handles instanced draws this month. Originally, we attempted to defer the draw until we could confirm the total instance count which seemed like the safest way to ensure an accurate render. Unfortunately there are rare circumstances, such as in Nights of Azure 2, where a compute dispatch is performed while the draw is still ‘pending’. Thus by the time the draw finally triggered, the state was completely wrong and was causing hard crashes for all GPU vendors. By ensuring all pending draws are forced to complete before any compute dispatches occur, our process holds true even in these niche scenarios.
Alongside some minor improvements to the Vulkan pipeline management systems, a long-standing transform feedback bug that was affecting AMD and Intel was finally stamped out. If any users with those GPUs tried to play Xenoblade Chronicles Definitive Edition, the issue would have been immediately apparent:
Maybe the grass in the field was a metaphor about GPU drivers all along… Either way it shouldn’t look like that. Making use of the vector outputs in cases like Xenoblade can get around the root of the problem, which is: missing data or data being written to the wrong offset in Intel’s case. Anyway, grass!
Do I see some Mystery Dungeon fans out there? Just me…? Well if you’re a Mystery Dungeon fan with an Intel GPU then this month you’re eating well. To render Pokémon Mystery Dungeon: Rescue Team DX in Vulkan, we were making use of OpenGL conventions such as gl_VertexID and gl_InstanceID. While Nvidia and even AMD have sufficient conformance for this to not be an issue, Intel wasn’t so lucky. Vulkan does have equivalents of sorts but they aren’t a direct 1:1 mapping; they add a little more information. By correcting for this divergence and literally subtracting some values, the visual gore is finally a relic of the past.
Finishing up the improvements to our GPU emulation this month: not one but two crashes were resolved with the second deserving some extra air time, as it removes some extension requirements for MoltenVK and macOS. Regressions in A Hat in Time, Xenoblade Chronicles 3 and Super Smash Bros: Ultimate were quickly cleaned up and an interesting little bug in which Super Mario Odyssey could collect 10s of thousands of entries in the buffer cache was resolved!
Onwards, toward the black hole that is the Switch kernel and Horizon OS.
Service HLE maestros of various usernames graced us with a couple of service implementations and cleanups in November, including a rather curious bug in the deserialization process of a sfdnsres (catchy!) service. The old implementation was unable to deserialize AddrInfoSerialized when the addresses were empty, causing a crash. With that scenario covered, a very cool bit of homebrew finally boots!
More services of equally memorable names like ‘IFriendService: 1 (Cancel)’ and ‘GetSaveDataSizeMax’ were both stubbed, the first of which allows SnowRunner to advance a little further into gameplay before hitting another friend service crash. One step at a time.
Two of the larger service implementations of audio and filesystem were updated to their firmware 15.0.0 variants with a variety of bug fixes attached.
On the audio side, this resolves an audio renderer crash in Paper Mario: Origami King and implements the new audio renderer features such as voice parameter support which was added in 15.0.0. Older effects like Delay also had miscellaneous bugs resolved with their initial implementations in 14.0.0.
LibHac, the library used for our Switch file system emulation, was also bumped to its newest release which added support for firmware 15.0.0 decryption keys and implemented some new save data services that were introduced in firmware 14.0.0. ‘General system stability improvements to enhance the user's experience’ are also listed if that floats your boat.
The spring autumn cleaning continued with fixes to IPsmSession, eventfd logic in the bsd services and even reaching as far as the software keyboard. Seemingly no one had ever noticed that when presented with exotic characters, it simply didn’t know what to do.
Hmmmmmmm, yes. This character is made out of… LEGO? Fear not, this turned out to be a very silly bug where the text displayed was not filtering out unicode control characters before displaying. A unicode control character is basically a character that instructs your computer to do something that, as a user, you don’t need to see, such as enter a word end or new-line. By adding a method to strip the raw unicode output into something we’d expect to see, text like this becomes much more legible.
November has seen Avalonia edge closer to the spotlight with a lot of clean-up work and Linux bug fixes. We invested so much faith in it that we exclusively shipped Avalonia for the macOS releases, and other operating systems are mere inches away from following suit.
The main roadblock thus far has been a couple of Linux-exclusive rendering bugs on the GUI such as dialog boxes and pop-up windows failing to draw. Even worse was that the render window was solid black when using Vulkan, not something we could realistically push out the door when we’re sure most users, even on Linux, would prefer to use Vulkan for that butter-smooth shader compilation.
Thankfully, the issue was tracked down to the way we initialized the X11 window and, with some back and forth and breaking OpenGL for a bit along the way, everything now works correctly. Although the dialogs were still broken, clearly this wasn’t the same issue.
The real cause was tracked down to the Avalonia style we’re using called “FluentAvalonia” which is, effectively, Fluent WinUI design and controls ported into Avalonia. We had to open a pull request to their repo directly to get this issue resolved, but the maintainers over there were gracious enough to get the change merged extremely promptly. All we then had to do was update to the new FluentAvalonia version.
A final boon to any Linux users; Ryujinx can also now boot in Wayland directly, if setting two-hundred environment variables wasn’t to your liking!
On to more general changes not targeted toward a specific OS: historically we needed a RenderTimer to keep the GUI and game in-sync before the switch back to an embedded window (covered a couple of progress reports ago!). That was still kicking around and was still forcing the GUI to refresh at 60hz no matter your monitor refresh rate. Removing the timer and some other legacy bits-and-bobs now allows the GUI to animate at full refresh rate.
We took this month to address some quirks with the general user-experience and streamline a few basics of organizing your libraries. The DLC manager has been completely reworked for Avalonia and now features the ability to enable or disable all files that have been added. As anyone who has all of the DLC for Super Smash Bros: Ultimate can tell you, this is a godsend.
Just the DLC window? Of course not, it would be fairly stupid to go through all that effort and not give the same treatment to the game update window...although, we can’t let you enable all the update versions at once; we’re told that it would cause some kind of tear in the fabric of the space-time continuum…
For those that didn’t know, Ryujinx can also be launched completely GUI-lessly from a terminal or command window. This is very useful for frontend launchers or those people with a very particular workflow! Until this month it was impossible to set the preferred graphics backend, OpenGL or Vulkan, when launching a game from the terminal, which was a major annoyance to many who wanted game-specific setups. This was the main focus of a bit of refactoring of the command-line launch process which also reduced some duplicated code to boot. There’s never been a better time to be a keyboard warrior.
Finally, like exasperated parents, we’ve had to rename the “Expand DRAM size to 6GB” option to not include the words “Expand”, “DRAM” or “6GB”, as faaaaaaarrrr too many of you were enabling it. We place it under ‘HACKS (may cause instability)’, and what do you think 90% of the support requests we got when Scarlet & Violet launched were? Right-o.
The entire team now has “disable the DRAM expansion” tattooed into the backs of their eyeballs.
We’re looking at a fairly trivial miscellaneous section this time but with a single, very large, elephant.
.NET 7, the latest release of the .NET runtime, was let loose upon the world on November 8th and we, being the cutting edge software project that we are, jumped on it almost instantly. One of the many advantages to developing software in languages that are in active development is that we regularly see new features and performance gains that other people have written for us! In the case of major runtime updates this can be fairly significant. Just the update to the runtime gave us another 6% performance jump in Super Mario Odyssey (part of the jump in the graph above!) and when enabling a new .NET feature called Tiered PGO (TPGO) we saw a very healthy 13% gain over .NET 6.
TPGO, Tiered Profile Guided Optimization, was the feature that was most interesting to the development team going into .NET 7, as it effectively allows the runtime to optimize common code paths in real-time. This is a bit of an open goal as it required zero changes on Ryujinx’s end and simply provided more performance. What’s not to like?
Loads of other work in this section revolves around the new tricks we can take advantage of, or old bits we can remove, thanks to both .NET 7 and C#11. New LINQ methods, Random.Shared, ReadOnlySpan and string literals being the main quick additions.
A significant portion of this report has involved Linux, and we’re finishing the exact same way. Some fresh Fedora installs weren’t symlinking required dependencies so we now attempt to import those libraries as a fallback. Not content with stopping there, FFmpeg 5.1.x decided to break our video decoding and instead present a green screen.
Turns out the FFmpeg maintainers reverted some AVCodec changes they made in 5.0.x releases and we didn’t get the memo. Adjusting our decoder back to the older format fixes these bugs.
If that’s what she wrote, then that is all! This report alone really cannot do the month of November justice. Check out our separate blog posts for both the release of LDN3 and our world-first macOS port! It’s a phrase that’s been said a lot recently, but there truly never has been a better time to emulate the Switch.
Once again, it’s the recruitment section of the report! If you know some C#, .NET, 3D-graphics or low-level engineering, you too can help the remainder of 2022 be as smooth and bug-free as possible. If that's all alchemy & wizardry to you then donating to our patreon, or being active in testing and bug-reporting really does help out a bunch.
Until December, and the New Year!
Image credits: Key (Header image), MutantAura & Ryujinx contributors on Github (Misc images).