Louder and faster: A belated Year in Review!

Wonder what we've been up to since our reboot in 2022? Here's a wild mix of improvements, from compatibility fixes to major performance uplifts and more!

Louder and faster: A belated Year in Review!

How time flies! It's been over a year since our project reboot in 2022. We've been sending around occasional updates on Twitter/YouTube/Mastodon to log our progress, but after such a while we wanted to also write a more bigger-picture overview of what we've been up to.

Strap in, because 2022 has seen a wild mix of improvements, from compatibility fixes to major performance uplifts and more: Perhaps most important on the list are the PC port of Mikage and the beginnings of audio emulation. Without further ado, let's get into it!

It's time you listened...

... is what we'd say if there had been anything to listen to. Alas, Mikage has notoriously been silent due to not emulating the 3DS audio hardware. For good reason: It would have to be done eventually, but we knew it was going to take at least a few months of dedicated work.

In July 2022 we finally decided to tackle the issue with a first prototype: An audio backend based on Teakra, a low-level and accuracy-focused approach by former Citra developer wwylele. Lo and behold, in just a few days we turned complete silence to close-to-perfect audio output! The only issue? At 5 frames per second, this accuracy-focused library wasn't practical for real-time gameplay. Nevertheless, it provided us with a crucial reference point for DSP emulation.

The real challenge was to make it fast now, which led us to writing our own LLE DSP emulator. After more than 3 months of dedicated work, we finally got to a state fast enough for fullspeed emulation, all while matching the original Teakra prototype in compatibility. Along the journey, we found a lot of neat performance tricks and developed really cool DSP debugging tools to ensure great emulation accuracy. A whole blog post could be dedicated to this topic alone - which in fact we're planning to do! For now we'll just let the results speak for themselves. We're really glad the hard work paid off!

A first preview of fullspeed audio support, illustrated with the 3DS port of VVVVVV

A sense of feeling at HOME

Mikage has always been about creating an authentic experience that fully preserves the 3DS: Not just the games, but the platform as a whole. Admittedly this vague idea was easy to shrug off as hot air, but in 2022 we could finally show what this authentic experience looks like:

HOME Menu: The hub greeting you after turning on the console

That's right! The 3DS system interface ("HOME Menu") emulated in its full glory. And not just that, you can even run the initial system setup! How cool is it that your first run of Mikage is just like the first boot of a new console?

Initial system setup, just like the first boot of a new console

From Mikage's perspective, there is a lot of little elements that each required individual work. Whether it was launching games, entering the settings app, the software keyboard, mii selector applets, or one of so many other things: All these aspects added up to months of work, which we've collected in an exhaustive overview video. Almost every second, something really cool is happening under the hood. Perhaps most impressively, this shows off Mikage's capability for multi-process emulation: Each of Mii Maker, HOME Menu, Software Keyboard, and the Mii Selector are dedicated programs that run in parallel to each other!

Multi-process emulation and applet support: A showcase of emulated system software!

We implemented support for somewhat rarely used 3DS controls as well: The 3D slider, the HOME button, and the Power button. (Yes, that's right: You can turn off your virtual 3DS.)

At the speed of light

As far as an authentic experience goes, one aspect that's so obvious it's rarely spoken out loud is performance. Emulating the HOME Menu with audio is cool and all, but it's nothing like the real console if you can count the frames as they show up on screen. Mikage's GPU emulation code hadn't received much optimization work to this point, being focused much more on getting things rendered correctly than fast. In fact the vast majority of the graphics pipeline was still implemented on the CPU-side, so it was time to move more parts of it to the host GPU.

Our first attack point was the 3DS vertex shader engine: It was an obvious waste of precious CPU time on a task that GPUs were practically made to process faster. But the latter can't easily be used without translating the low-level shader bytecode of the 3DS GPU to common shader languages like GLSL or SPIR-V. The big difficulty here is the somewhat esoteric control flow instructions used by the 3DS, which must be remapped to a convential control flow graph. If you're interested in the technical details, Mees Delzenne has actually written an entire Bachelor thesis on this topic!

For Mikage, we've independently built our own control flow analyzer, from which it's straightforward to build a GLSL/SPIR-V equivalent to the original 3DS shader. Since getting this right is critical, we also created a mighty debugging interface that allows showing the 3DS shader bytecode side-by-side to our reconstructed control-flow-graph and other representations. This has proven very useful, since these shaders can be mind-bogglingly complex!

One further nifty design trick is that we've strongly decoupled the control flow analysis from the actual shader generation. This helps us handle some corner cases of 3DS shaders that can't be emulated on the host GPU: Instead of falling back to an excruciatingly slow CPU interpreter path, we recompile these shaders to an internal bytecode representation that's designed to be fast to process on the CPU. Accurate control flow analysis was crucial for this to work, and thanks to it the worst 3DS shaders now won't have any noticeable impact on emulator performance. All this works without any platform-specific JIT code generation, so it equally benefits all devices (PC and Android)!

One other key optimization is flushless rendering, wherein Mikage avoids writing back rendered images to emulated memory and instead keeps them on the host GPU as long as possible. This saves unnecessary work caused by copying around data, enables the host CPU to process more non-graphics emulation work in parallel to the GPU rendering, and provides an efficient mechanism for handling changes in texture formats (format reinterpretation).

A similar approach is found in what Citra calls texture forwarding, though Mikage features a somewhat more sophisticated system that tracks ownership of each part of emulated memory. This ownership information allows us to omit any unnecessary data copying until the point where the owner changes (e.g. because the emulated game reads directly from a framebuffer). At the same time, it's a useful safety net that double-checks each GPU resource is in the exact state we expect it to be. This paid off quickly by catching important bugs just by investigating each time the tracking system was yelling at us. Going the extra mile to add validation code is an often underestimated yet highly effective approach to emulation!

These two major optimizations combined yielded impressive results: We've been able to run our evergreen testing game The Legend of Zelda: Ocarina of Time 3D consistently at full native frame rate! Further optimization work is ongoing for more complex games, but it's reassuring that we've now reached this milestone for such a popular title.

No more slideshows! Ocarina of Time 3D finally runs at the full 30 native frames per second (FPS shown in the video are internal)

No one is left in the dark

Fragment lighting was a major blocker for most games to display anything. Some games don't care if you just don't emulate lights, but for most games this will leave all the objects fully black! The reason we didn't implement fragment lighting initially was that it's a highly complex hardware feature with tons of configurable knobs. Our plan is to get this right by writing hardware tests that verify all the different corner cases. But for the time being, just implementing a very minimal subset is enough to at least render some graphics:

Let there be light: There's kinks to work out, but it's better than a blank screen!

Do you guys not have phones?

Anyone who's tried Android development before knows: It's kind of a pain. Iteration times are very slow, debugging is an awful experience, and mobile graphics drivers are royal bugfests. Hence despite Mikage being announced as an Android app, our primary development environment has always been on an internal PC version.

We never planned on releasing this internal PC build: There's hundreds of tasks we have to juggle simultaneously during development, so adding 3 more GPU drivers (AMD/Nvidia/Intel) and a new CPU architecture (x86) into the mix wouldn't have helped us get anything done. Making Mikage ready for PC wouldn't take egregious amounts of time, but other fires were simply more important.

Until one day, on a whimp we gave it a shot after all:

Most of the work that went into this was on the Vulkan renderer by going through all validation layer problems that cropped up on AMD/Intel GPUs. We also found a fun memory leak in the Steam Overlay that we added a workaround for. On the CPU side, we've added the excellent dynarmic recompiler as a backend for faster CPU emulation on x86.

We also added controller support - which even works on Android for both wired and bluetooth devices!

When using the keyboard just doesn't feel right: Controller support!

Finally, all this is bundled in a new GUI that we're going to show off in its full glory another day. We've chosen a powerful tech stack that is rather unique among emulators, so we'll have some very Mikage-exclusive features to offer. We'll have to ask for your imagination to guess the full potential from the small glimpses we've given so far, but we'll have more to show later :)

Layout editing has never been more convenient. Wait until you see this in motion!

The time it takes to build Rome

With the bigger-picture issues like performance and audio support out of the way, we went through a couple of games in our library and made sure they don't just somewhat boot but actually run great in Mikage. Emulating some new minor configuration here, fixing a small but game-breaking bug there. Other than the already mentioned changes to the GPU core and the new audio support, practically all of Mikage's subsystems have received changes: From the ARM CPU emulation core, our kernel system call handling, the service HLE code, YUV video decoding, GPU DMAs, and the filesystem emulation.

Individually each of these changes had little effect overall. It's like the heads of a Hydra, where fixing one emulator bug makes you notice two others. But improving each game bit by bit, letting the work bake and add up over months, we could finally reap the fruit that made games suitable for presentation. We couldn't individually list all the changes we made (there's too many), but we're really proud that we can feature so many well-working games thanks to them!

A look ahead

That's all up to the beginning of 2023. We're really proud of the progress we've made: Countless hours, 400 git commits, and more than 25000 new lines of code. Of course these metrics must be taken with a grain of salt, but they do illustrate the scope of our work really well.

This new phase came with a shift in development philosophy: Our original plan when announcing the project in 2019 was to create a product that can sustain full-time development of a high-quality 3DS emulator. As history showed, that didn't work out. Ever since the reboot, development is driven by what sparks our passion instead: Working on advanced tech and cool emulator features while pursuing technical challenges that others shied away from. All bundled under the umbrella of a great emulator project.

Today Mikage is all about pushing the envelope, and it's really been a breath of fresh air. We can focus on the stuff that excites us without feeling the pressure to please everyone's expectations. Yet, our Patreon page has seen more support than ever. Seeing other people share the same enthusiasm as us is probably the biggest source of motivation there could be.

Thanks so much for your support. We can't wait to share what we've been up to since the start of 2023!