We hope you liked our previous blog post, covering how Ryujinx started.
Now let's talk about some of the improvements we have made during the last three months.
We know most users don't like command-line programs, so we needed to add a GUI to Ryujinx. Due to the lack of GUI frameworks for .NET Core, this proved to be a real challenge.
Emmaus was the first to try their hand at creating a GUI for the emulator. His first attempt (Ryujinx#101) over one year ago used GTK with a C# wrapper. Our main issue was the additional dependencies, which we had to include with the emulator binaries, and he also had some difficulties rendering the game screen on the GUI window.
We considered writing our own GUI, but Xpl0itR came up with another attempt (Ryujinx#695) using GTK, which was perfect for our case. He made a package to avoid the issue with dependencies, and we left the rendering and log windows separate from the GUI for now. He did an amazing job, and the emulator is much more user friendly now. Users can also change settings directly from the GUI, and more features will be implemented later.
Since RyuJIT isn't flexible enough, gdkchan managed to write our own JIT.
On the old CPU emulator (ChocolArm64), the ARM code was directly translated to CIL, which was in turn compiled by RyuJIT into platform specific code. This has some advantages, and also some disadvantages. The main disadvantage is the lack of control. Here is a brief list of issues with the approach used on the old CPU emulator:
- RyuJIT is a bit slow compiling the code. This is undesirable as it may lead to long boot times and lags on the game.
- Sometimes we are not able to emit the optimal code sequence for a given ARM instruction, as we don't control what the JIT is going to produce.
- We can't do AOT builds as
System.Reflection.Emitis not supported there.
With our own JIT, we don't have any of those limitations, but the cost is increased complexity as we generate machine code directly. That said, we believe this is the way forward and we can improve it over time to get better performance and throughput.
The new JIT also allows us to save the compiled code on disk (so that recompiling it on the next run is not necessary), or do full AOT compilation of the game code.
ARMeilleure is an ARM CPU emulator.
Meilleure means better in French, as it is supposed to be a improvement compared to the old CPU. Currently it can be thought as 2 pieces, the translator and the compiler. The current gdkchan plan is moving the compiler part into a separate project, to allow it to be reused for other things later. It is already fairly independent, so the work required to move those bits into a new project is minimal.
For more technical information, you can read more up on it, in gdkchan's pull request on our GitHub (Ryujinx#693).
When games and system updates come out, more services and functions are added. To keep new games working, the implementation of those services on the emulator also needs to be updated. For this reason, Thog decided to reverse engineer the newer version of the
friend (Ryujinx#721) service, and implement it more accurately. While it doesn't change much for the titles that are already working, it is required by games like
Super Smash Bros. Ultimate.
He also reverse engineered the
time service, up to Switch firmware version 9.0.0, and implemented it (Ryujinx#722, Ryujinx#735 & Ryujinx#736).
Super Smash Bros. Ultimate,
Megaman 11 and a bunch of other games to boot further aswell.
Ac_K did some refactoring on the HLE services (Ryujinx#526 and Ryujinx#728). With those changes, it's now a lot easier to implement new functions on the services, and overall, the code is more organized, and matches the structure used by the Switch OS more closely. Those changes were inspired by another C# Switch emulator SupercellNx written by Daeken.
Although, everything isn't perfect and we have to improve the way we handle the result codes of those commands. That's why he refactored them (Ryujinx#731). Now we can store all result codes by the service instead of using generic classes or a dedicated class in some cases.
Finally, even with time and contributions, the services folder wasn't really structured and everything was mixed in the wrong way. That's why he did some cleanup of it (Ryujinx#771) and added a placeholder for every service's interface.
Moreover, Ac_K implemented a bunch of needed calls (Ryujinx#714, Ryujinx#747, Ryujinx#749, Ryujinx#761 & Ryujinx#768) which also helped some games boot further than before. Although, nothing too amazing but if you needs more explaination you can take a look at the pull requests for more detail.
Also, Moosehunter has worked pretty hard on the filesystem implementation in LibHac. Some bugs have been fixed and the result codes are more accurate to the real service. Then he updated Ryujinx up to the version 0.5.1 of LibHac (Ryujinx#698, Ryujinx#725 & Ryujinx#770).
Then he implemented a command which is needed by games like
Dragon Quest Builder 2 and
Megaman 11 (Ryujinx#748) and some interesting feature
FsAccessLog which is stored in the configuration in order to enable it, which lets the emulator log filesystem accesses (Ryujinx#748).
HID isn't currently well implemented, but that doesn't prevent emmaus from refactoring in some areas. He used some structures to handle it in a better way than how the shared memory is used (Ryujinx#723).
Then he changed the game controller input from using the
OpenTK.Input.Gamepad class to
OpenTK.Input.Joystick. This made every DInput and XInput device recognized as a game controller by the OS to work with Ryujinx. Instead of limiting it to recognized and automapped gamepads (Ryujinx#737).
On of the main concerns of emulation is optimization. For this reason, we needed a way to measure the impact of various methods. Thus the profiler (Ryujinx#567 was created.
The profiler, coded by Andy, provides a real time graph showing the time it takes to run various methods. It has been designed in such a way to be very simple to use as a developer and so that it can be reused for other projects. The interface uses OpenGL rendering, and it was implemented in the GPU implementation as well (Ryujinx#570).
One drawback as of now, is that a large amount of the code base is still unprofiled. This should improve over time as more profiling events get added. Developers can contact Andy for details on how to add profiling to their code if need be.
As the emulation matures, the profiler should make it easier to find bottlenecks and then further improve performance for all users.
With the profiler comes the new profiled builds. This means any overhead caused by profiling can be removed during normal usage. Because of this, if you wish to try out the profiler, you will need to download the special builds. They are available on the download page of our website and are released in parallel with every release; including any automated PR builds. So you can profile and release them if you wish to do so.
As you can see, we implemented a lot of features to Ryujinx this past summer, and we already have a lot of others interesting ideas for the future. You can suggest some of yours on our discord server.
If our work inspires you or if you want to support us; feel free to donate to our Patreon.
That's a wrap, feel free to share this blog post with your friends.