So we have Anis Astier is going to tell us about the Wazem 101 which is very nice to
put in. Thank you very much.
Thank you. Thank you. A quick presentation. My name is Anis. This is not my first talk.
This is my first time here in the Rust Dev Room. You can find my social media here.
Follow me if you want. I've been learning Rust for five years on and off. I wanted a
bigger project to learn a bit more about Rust. I said why not write an emulator. I started
this project. This is a Game Gear emulator. The Game Gear is this small device. I don't
know if you've ever heard of the Game Gear. So yeah, it's a Sega handheld from the 1990s.
So this is the name of my emulator. Gears you can see. It's written in Rust. It depends
only on the standard library. It has a native UI. This is how it looks like. It works. After
I developed this native UI, I thought maybe I should port it to the web. To do that, I
would need to use WebAssembly. So quick show of hands. Who here has never heard of WebAssembly?
It's interesting. Who here has heard of WebAssembly but never used it? Who here has heard of WebAssembly
but never used it and developed things with it? Oh, many people. Okay, quite interesting.
So WebAssembly is a kind of a new platform. You can think of it as a new platform, a new
to port code. It defines the text by code format. It's a take on the Java Compile 1, so
whatever your system. It works in the browser where it's as secure as JavaScript, it's sandbox.
It also has many other use cases. You can work on servers. You can use it in fast. It
has many use cases. So I want to port my emulator. So there's this first level which is how do
I build my code? How do I compile it? So let's go through this journey. How do you compile
WebAssembly? I assume you know about Rust, but if you don't, usually you install Rust with
this tool called RustUp. You need to add a new target with RustUp. Then you also need
this tool called WasmbineGen which will bridge your WebAssembly code with the JavaScript world
and generate some things. Use RustUp. You use WasmbineGen to build your code with the
new target and then you use WasmbineGen to generate a directory with JavaScript. You
serve that with an HTTP browser and that's how it works. You don't have to use WasmbineGen
directly. You can use tools that integrate WasmbineGen and call it. There are many such
tools that have selected a few. Wasm Server Runner. It comes from the baby community. You
have Cargo Run Rasm. You have Trunk which is even higher level and Wasm Pack which is
from the Rust Wasm project. I won't go into the details. You can find the comments on
how to run them here. I did a quick comparison of those tools from let's say the lowest level
tools to the highest levels. WasmineGen, everyone uses it. It's like the reference tool. Then
you have a bit higher level tools and then more open-united tools like Wasm Pack and
Trunk. Wasm Pack will generally be used to generate libraries that you can use from the
JavaScript world whether with NPM for example. Trunk will integrate even more things like
compress your HTML assets and things like that. You know how to build. How do you run
the code? You usually write a binary. You have a main function and the entry point of
your main is how it works. Or you can build a library and usually you annotate your entry
point with WasmineGen macro start and you say, okay, this function is my entry point.
You start executing from here. We know how to compile. Let's continue porting our application
and go to the second level of porting the emulator. This emulator I've written called Gears
for the desktop UI. I only selected dependencies that work with WebAssembly. So the whole wasmineGen
wasmineGen was capable. They work with the web platform. Have pixels, WinIt, simple,
Giller-S which is for gamepads. We'll go deeper into that. They all support WebAssembly. How
hard can it be? It should be very simple. Well, it depends. For pixels and WinIt, pixels is a library
to make a front buffer, basically a front buffer library that's GPU accelerated. So you can write pixels
to coordinate and then it will run that with WGPU. Pixels use WGPU. It's another great to do the rendering.
In order to work on the web, you need to enable the WebCR feature of WGPU. In the future, it will also
use WebGPU, but that's another subject. The initialization of pixels is also different because it
uses WinIt and WinIt needs to be initialized differently if you want to render your UI in canvas
in the browser. Last but not least, the initialization of WGPU is Async. So in my emulator, I never used
Rust Async. I needed to add that. So I used WebAssembly. Gen features to bridge the Async world from
Rust to JavaScript promises. To part the audio part, I'm going to use the WGPU. I'm going to
use the CIPL create, which also works on the web. This is a reference create to play audio. It needs to create
feature as well. There were also some challenges because maybe nature started directly and if you use a browser,
you can't start playing audio directly. That's actually a good thing because it means you can't play audio
on anyone's browser without interaction. So you need to have interaction. The user wanted to do this action.
Another issue I had with the standard library is I used NPSC channels and they don't work on the web platform.
So I wrote a quick channel myself because it was in the core. There are other channels that work on the web platform.
But I prefer to implement something with no other dependencies. For time, usually for synchronization in an emulator,
you need to know about the current time. Just like for the channels, in the standard libraries, the time API are not
available on the web platform. So there are crates that do the bridge. I used the instant crates. You can
also use web time, which also works. This is the code, the use code if it's for the was 32 target using
instant, if not, use the standard import. For Gil arrest, which was very nice, there was no action needed in order to support
working the browser. Everything worked out of the box except the gamepad API, I would say, on browser is not as much much
mature as on native. So there is some rebinding to do. There are good reasons for that. For example, browser don't want you to be able to
fingerprint someone with the gamepad API, but then it means the bindings are not mature enough. Not the bindings, but the key bindings,
which is something else. And then during porting, I also had bugs that were entirely my fault. I used a bit to turn it into a
huge bit, but I didn't use it too much. I used to make it too much new size, mostly because I like to index slices.
That's what you need to do in the slices. Wasp 32, as it says in the name, is a 32 bit platform. So I had overflows when I had
multiplication, additions, it grew bigger than 32 bits. All these were codes because in my project, in my cargo
project, I had a lot of defaults in Rust. And yeah, it worked well. I just replaced new size with 64 when it did. And that's it.
So let's take a quick break and let's go through a demo of what it looks like. So just for first then, brought to you again,
which is this one, I will lend it to you for a few minutes. It's for them exclusive. I recommend you play this demo on, not
necessarily on mobile, it will work, but you won't be able to control it. So maybe more on desktop browser or anything that has a
keyboard or gamepad controller. So I'll let you a bit more time to load it. It might not work for you if you don't have WebGL
enabled on your browser, but otherwise it should. If you have Firefox or Chrome, here's how it looks like.
So I've loaded the page, it's play, and basically the emulator starts. If you have audio, it will play audio. And yeah, this is what you should see.
Okay, yeah, it works. I can play it. Who here successfully runs the demo? Just a quick show of hands who managed to run it. Okay, thanks.
Okay, let's continue. So we have this porting. Okay, mostly worked. I showed you. It worked. There were a few tricks, I picked along the way.
There's not mandatory, but let's see what we have here. First thing, if you're used to debugging like me with println, you print code on
terminal, it probably won't work as is on the browser, so you want to use the Web console. There's this console log crate which does the
binding of the console. If you use the log crate, it's really well integrated with the log levels and things like that. I also recommend that you use the
console error panic hook crate. This one helps show when your program crashes, for example, I showed you the overflow checks it can panic. It will show you basically the panic in the
console. That's how you register a console panic hook. Another trick I picked along the way is the cargo config. For this demo, I showed you, there's a bit of a
problem with some interactions. Some API I use, which I use directly from Rust, and I use the Web Cyscrate, which allows accessing those APIs for this demo.
In order to be able to access those APIs, which they are considered unstable, and you need to add an environment variable when you build, which is a bit annoying to add every time. You can add this Rust flags directly in your cargo config.tamo.
This way you can build with cargo builds. It will work. Another trick if you use to having VS code or integrated development environments, you probably are using Rust analyzer. If you have code that works on multiple platforms like me for the native, there's WebAssembly, you probably want to tell Rust analyzer to build as a tool.
You can do two different architecture. This way you have completion on the WebAssembly part. This is done as well in the cargo config.tamo by specifying multiple build targets. When you build it, you will have multiple build targets.
There are some drawbacks for that. It won't work with the workspace member. It must be at the root of your workspace. It also means that when you use cargo run, since you have multiple targets, cargo run will say, oh, no, you have to pick one target in order to run, which makes sense. It can be a bit annoying.
So let's go with what did I think of this experience of putting this emulation. What's my feedback? I would say in general it's very easy to port standalone code to WebAssembly if you're using Rust. I did not change anything in my app's architecture.
The total port took a few hours over a few days. As I told you, I did custom code for initialization, which is I think, and for DOM interaction, which is the demo you've seen.
To go a bit further, what I won't talk about in this talk is how to build a web UI, for example. You probably want to use U or Laptos because I don't recommend accessing DOM APIs directly. This is very ugly, not really ergonomic.
I did it so you don't have to try. Those library developers do a great job to do that. I didn't try building a complete UI. As you saw, nothing is configurable, etc.
I'm thinking of building a UI with slints or a GUI, but I'm not really satisfied with the current status of font rendering. I know it's something that's being worked on.
Just like as well, minification in web size is not web-specific. There are many Rust tutorials you can find on minification, and I didn't do any performance measurement. I can tell you that it works. It also works on native. But I don't have any special feedback for that.
That's it for my presentation.
Thank you.
We have a question.
Yes, I have a question. When you build websites today, they have to be responsive. You use media queries in CSS style sheets to adopt to different kinds of resolution so that on the mobile tablet or desktop, it still looks nice.
Can you also do this in web assembly that you would say if I run the game in portrait or landscape mode, or if I do it on a bigger screen, that it takes care of the resolution? Will it also scale the graphics accordingly?
There are multiple aspects to that. If you're building a web UI, you probably do that with CSS. If you use leptos or you, you will be able to generate HTML whether on the server or on the client.
Then it's basically the same thing as web development. You have CSS, you scan this HTML directly. For this demo, this is an emulator. It's a bit specific, especially because it's a full-screen application.
So basically it takes the whole width of your screen, and that's it. That's how it works on mobile and tablets and desktops.
But it's not that you can combine those and that you can also do something in JavaScript or CSS.
You can do that. You can find tutorials on the Rust-Waston book. You can look at the Rust-Waston guide and on the Rust-Waston project, which is this URL.
You can find information on how to bridge the two worlds. If you decide to use a crate, as I recommend, like you or leptos, they also have a lot of documentation on how to do that.
I understand. Maybe a general question. Why did you choose Rust? Did you also consider programming in C++? Or are there any advantages of using Rust compared to C++?
That's a great question. It was actually covered in other talks, but usually I like using Rust because it's a very nice language. It has nice ergonomics. It's fast and native.
It has more safety guarantees than C++. A great ecosystem.
Thank you.
You're welcome.
Any other questions?
I'm curious what your main loop looks like. Do you spend all the time polling for events? Do you get called back from the browser? Does the browser hang if you never sleep?
That's a good question. I did not modify my main loop, but mostly because I used Winit. I used a Winit event loop. This is specific to the Winit crate.
Nothing was modified in the main loop. It spins. I don't remember how many times, but basically the length of a frame every time, and then it gets refreshed.
Yeah, that's it.
And that's all the time we have.
Thank you.