I was checking out gameboy emulators recently written in C++, Rust, and Zig that have seen recent development.
I couldn't get the Zig projects to build, which is understandable since the language seems to have had a lot of breaking changes in its build system and the language in general (which is why it comes with a caveat not to use it in production yet).
But given the sheer number of comments I've read over the years recommending Rust as a replacement for C++, I was more surprised to see that I couldn't really find a single Rust project that had correct timings (the frame rates were incorrect and audio distorted) and worked with most ROMs I tried.
Meanwhile, the C++ projects I found built without issue or hassle, had correct timings, ran all the games I've tried, had lots of features, etc.
What gives? Is emulation just not a good use case for Rust?
Timings and audio handling sound like logic things and not something Rust's advantages over C++ would inherently support better.
If I had to hazard a guess, it's that the C++ projects are just more mature and have seen more "in-the-wild combat" to get to the state they're in now. Other factors that may contribute (in no particular order):
- existing emulators are Good Enough and polishing a new one is less "urgent" than other problems in the current zeitgeist;
- hobby project vs. no-longer-a-hobby project status; and
- emulator projects in C++ are of similar quality are just as prevalent, but are significantly older and instead of being discoverable on GitHub are abandoned on Sourceforge or personal websites.
That would be my guess as well. Emulation, interpretation of decades-old binary formats, and similar problems are hard to get right on the first try, they often need years of testing and breaking until they are right. A project that has been around for 20 years will always have an advantage in this space over a newcomer project.
Most people developing GameBoy emulators these days do it as a toy project, not a serious effort to create an ultra-accurate general purpose emulator. It's like writing a raytracer or a Pong clone.
The best GameBoy emulators like Gambatte predate Rust by almost a decade and are often written in C++. Since GameBoy emulation is pretty much a solved problem there's no strong motivation to innovate in the scene.
I've written several emulators in Rust, I'd argue that it's very well suited to this exercise, but it's also not an area where Rust shines particularly. There's no complex memory management in emulators usually since most memory buffers are just statically allocated constant-size arrays that mimic the physical RAM on the console.
I'm currently writing an emulator in Rust (for Apple 2). For some aspects I'm better than my C++ counterparts.... BUT those counterparts have years and years of little refinements that make a huge difference on the fact that some games run or don't. There's no question about that.
Besides Rust is just fine. I use a bit of threads, a bit of OpenGl and none of these is a problem. I'd venture to say that the rust compiler is real good and that allows me to code things without worrying about optimisation. Older emulator have started years ago and they often had to optimize by hand which leads to not so readable code; just your usual code legacy story.
Finally, the cargo tool is a modern way to build the code and I've been able to port my code to Linux, windows, MacOs and raspberry pi without a single code modification (besides expected stuff like audio frequency, file system, etc being different).
The Rust crowds are just too new to have produced good emulators. Give us time :-)
I'm also noodling on an emulator, shooting for cycle accuracy (to the point where you'd be able to run vapor lock demos). Mine passes the 6502 functional tests but not yet the Tom Harte suite. To add to the difficulty, I want to run it on an RP2040 and bit-bang the video. Is yours up in an open source repo? Mine isn't (yet) but I'm open to joining forces.
I started mine about 6 years ago, put it on the shelf, and am getting back to it. Overall I think Rust is working well for it (and for pico DVI out), but I can also see the advantages of Zig for something this low-level and specialized.
133Mhz, that's 133 cycles per instruction for a 6502 (assuming you emulate only the 6502). Seems doable but then there's the BCD stuff and that's rather tough emulate (you'll definitely need a lot of cycles to reproduce that)...
But that sounds real cool. Would you share you project ?
It's a bit tricky, but my decimal adc is 19 lines of code, all of which is fairly straightforward, mostly bit fiddling stuff that I expect will compile to a few instructions each.
I need to go through an open source releasing process but I'll get that going. Thanks for your interest!
Emulation seems fine for Rust. I don't think there's a good answer to this anecdote. One option is that when I learned C++ I built an emulator to learn how to write an emulator, whereas people writing emulators in Rust are perhaps often trying to learn Rust. Maybe that leads to a difference.
It's basically impossible to say though, we'd need to do a full survey of the C++ and Rust emulator ecosystem and look at which bugs are most prevalent, control for authors experience and skill, etc...
edit: I'll also note that when I built an emulator there were guides for it in C++. I don't know what materials exist in Rust but building an emulator is actually a project that I would consider C++ for (if for learning purposes) just because it's so well documented how to build one using the language.
So the question is why a project created in a language that has been around since before the devices being emulated are more mature than ones in a language that is 8 years old?
Sorry to be so snarky. It probably depends on people's time working on it and their familiarity with async processes. This is an inherent problem with any media like that. With sound, it's primarily that it can't come before the image but can lag behind the image. Stuff like this.
I don't think any programming language is going help with that because it's how we perceive things, not just how correct your code is.
I don’t think Rust is inherently bad to write emulators. For example one of the best cycle-accurate IBM XT emulators is written in Rust: https://github.com/dbalsom/martypc
it's not at all about "quality of programmer" and you should never reduce extremely vague arguments to that. it's literally about time for quality program to be made an completed.
those C++ emulators have often been in development for 3-5x as long as a similar Rust one.
I think its a perfectly fine use case but we're not yet at the point where that community has reached a high enough level of rust adoption that there are a significant number of mature projects, even for something as relatively simple to emulate as a gameboy.
As somebody who has written the same gameboy emulator in C++, Rust, and Zig (as well as C, Go, Nim, PHP, and Python) - I have yet to find a place where language affected emulation correctness.
Gameboy audio is kind of a pain in the ass (at least compared to CPU, which is fairly easy, and GPU, which is easy to get "good enough” if you don’t care about things like palette colours being swapped mid-scanline) - and some languages take more or less code to do the same thing (eg languages which allow one block of memory to be interpreted in several different ways concurrently will make the “interpret audio RAM as a bunch of registers” code much shorter with less copying) - but in my case at least, each one of my implementations actually has the same audio distortions, presumably because I’m misreading some part of the hardware spec :P
Oh that's so cool! Thanks for sharing. I skimmed a few of these and I'll definitely take a closer look later. I enjoyed your observations on each language as well.
From a reader standpoint, I enjoy reading emulators written in C the most. It sucks when C is missing features for complex tasks, but emulation seems to fit nicely into what it can do without black magic fuckery. Of course as someone not super experienced with C, writing it feels like an endless sea of "oh god why is this segfaulting".
Rust OTOH bothers me to look at because there are so many sigils and language doodads for functionality that seems like it should be really straightforward but whatever, I'm sure that's just because I've barely used it and can't understand it. I've been learning C++ recently because learning materials are usually tailored to it for stuff I'm interested in (vulkan currently), while ignoring a constant nagging feeling that I should stpo being curmudgeon and take a closer look at Rust.
I've tried Zig but am not looking too hard atm for many of the reasons you mentioned in your repository, I'm hoping the language will stabilize at some point and that LLMs will help with some of the repetition/verbosity.
Odin would very likely be a great choice for the stuff that you're trying to do. It's a very straight forward language and has a "vendor" (https://pkg.odin-lang.org/vendor/) part of the libraries shipped with the compiler that has lots of bindings for gamedev-related things, i.e. OpenGL, Vulkan, DirectX, glfw, stb libraries, some audio libraries, etc.
On top of that it has swizzling in the language, some automatic array programming features and a standard set of modern niceties like good tagged union support, custom allocators that are standardized, and so on. It's a nice language for pretty much any use case when you want good fundamentals and straight forward solutions without much magic but for gamedev I don't think there's a C/C++ alternative that is as well suited as Odin overall.
If you want a brief (but enough to get me excited about trying Odin out) overview of the language you can find one here: https://odin-lang.org/docs/overview/
P.S. I've written a few posts on HN about what I like about Odin in more detail so I won't reiterate those things exactly here, but one of them is recent so you can find it in my comment history.
Argh, Odin looks so close to perfect for me. The vendor libraries look fantastic. But I'm working up to deploying on Android and using OpenXR. There's support and/or examples of that for C++ and Rust, and I could probably get Zig working either by cross compilation or by exporting to C. But I don't see any documentation or examples for cross compilation for Odin, and that use case seems to fall just outside the range of its built-in batteries.
I wouldn't know about Android, really, but I do know that there is some discussion right now about how some consoles are off limits due to not being allowed to ship non-C/C++ code to them and this will be addressed by compiling Odin to a subset of C in the future if I remember correctly.
I would probably inquire on the Odin discord server about this particular thing as it's very likely someone has already stumbled upon it: https://discord.com/invite/sVBPHEv
ryujinx doesn't use the GC for their sub-systems, they use a mix of manual memory allocations throught their allocators or RC
For such project to succeed, you have to be able to optimize your memory allocation strategy
If you just rely on the GC, you'll have bad surprises (why does my game stutter during gameplay?!), they already have multiple due to their double dip on the JIT, nothing prevents the JIT compiler to compile during your gameplay, causing massive stutters, just like how shaders compilation is an issue at runtime
Personally I think C# is a great language to get stuff done. The big problem it has is the portability (especially when it comes to using it with a GUI).
Ryujinx uses Microsoft's JIT .NET technology [1] which is best available for C# codebases. Nothing new here since it needs to translate Switch's ARM instructions to x86 machine code somehow.
C++ has an ages old tried and tested ecosystem for game stuff which probably makes a lot of rendering and audio tasks easier. It could take Rust years to catch up even supposing the language is inherently better.
> But given the sheer number of comments I've read over the years recommending Rust as a replacement for C++,
> What gives?
This is The Hype Wave, where something is new and exciting so it's recommended over the things of the past without regard to whether it's as stable/full featured/correct. People are excited about Rust so Rust has been jammed into just about everywhere to varying degrees of success/correctness. This is the same as Javascript/Node and Go over the last decade-ish. Zig and Mojo are probably next.
My guess based on no evidence whatsoever: The Rust community seems to have lots of low effort enthusiasts - people whose primary motivation is "use Rust" rather than "solve a problem". People whose motivation is to solve a problem (in this case build an emulator so they can play a game or whatever) tend to be a lot more oriented towards the end-result, rather than what tool they're using. I'm pretty confident this isn't an issue with the language.
I think it's more "learn rust" than "use rust", and nowadays a lot of partly written learning projects are published on GitHub. A lot of the similar "learn C++" projects happened earlier and either stayed private or are quite good by now.
Most of the developers I know that are learning Rust do not come from a systems language background. They have to learn the domain as well as the language, which is a high bar. Learning to write systems-like code well takes a lot of time and experience.
You should add Nim to the list! There's a few implementations, though I'm not sure how complete they are. As others point out they're often used as ways to learn a language.
An alternative opinion (that I happen to hold) is that the unbelievable pain and frustration of dealing with CMake has been at least one of the forces that have led to the Cambrian explosion of new programming languages and build systems in recent-ish years.
While CMake is pretty annoying the problem from my experience are people using it incorrectly and doing stupid stuff that you can't change if you're using the project as a library.
If every library would be properly "packaged" using CMake would be far less annoying or frustrating. Just recently we had the case where a 3rd party library author of a sizeable c++ library set -Werror in his library which wreaked havoc.
It's mainly CMake's opaque and magical way of working that frustrates me so.
I feel you've outlined yet another failure mode on top of the frustrating and undiscoverable magic incantations that seem to change with every undocumented revision.
> What gives? Is emulation just not a good use case for Rust?
You fell for Rust PR (which is understandable, considering that both this site and Reddit are full of loud Rust fans/influencers). The language provides memory safety and some fancy ML-like syntax and that is it. It won't implement good emulator for you or make you a better SW developer.
Are you serious, a single specific software you like doesnt exist in a specific language and you blame the language for that? You see how that makes no sense right?
It's not a reasonable question at all. My first reaction was: "maybe the authors did it as a toy project". If nobody is paying them to do a professional and bit-perfect GameBoy emulator then there should be no expectations that the software is going to be good.
That's an insightful reply, and an important point to consider. There's been a number of great answers in this thread, and the general consensus seems to be "the language is a great fit, but the projects haven't had time to mature."
These seem (to me) like reasonable responses to a reasonable question.
>Meanwhile, the C++ projects I found built without issue or hassle, had correct timings, ran all the games I've tried, had lots of features, etc. What gives?
Aside the fact of the C++ projects being from seasoned C++ developers (which exist 3 decades now in huge quantities), not people starting with Rust as their first "native" language and using an emulators as a toy project? C++ exists for 3 decades more on top of Rust's entire life of 8 years since v1.0. And given the over time adoption distribution, most Rust devs are like 1-4 years old Rust users max.
Or the fact that those C++ are probably much more mature, and with more manyears in them compared to the Rust ones, some even being major community projects?
I couldn't get the Zig projects to build, which is understandable since the language seems to have had a lot of breaking changes in its build system and the language in general (which is why it comes with a caveat not to use it in production yet).
But given the sheer number of comments I've read over the years recommending Rust as a replacement for C++, I was more surprised to see that I couldn't really find a single Rust project that had correct timings (the frame rates were incorrect and audio distorted) and worked with most ROMs I tried.
Meanwhile, the C++ projects I found built without issue or hassle, had correct timings, ran all the games I've tried, had lots of features, etc.
What gives? Is emulation just not a good use case for Rust?