Skip to content

Xevion/rust-sdl2-emscripten

Repository files navigation

rust-sdl2-emscripten

A demo project to explore Rust + SDL2 + Emscripten.

  • Cross-platform build scripts with GitHub Actions for Windows, Linux, and Web Assembly.
    • Includes special packaging for Windows for DLLs
  • All SDL2 extensions enabled (Image, Mixer, TTF, GFX) and used in the project.
  • Emscripten: Asyncify for the game loop, Javascript Interop, extern functions & hooks.

Goals

  • Emscrimpten Looping Method
    • Asyncify
    • emscripten_set_main_loop
  • Automatic Builds
  • SDL2 Extensions
    • Image
    • Mixer
    • TTF
    • GFX
  • Javascript Interop
    • LocalStorage
    • Fetch
  • Resizable Window

Considerations

This project took a lot of work to get 'right', and it's still quite hacky.

  • It's not possible to quickly get accurate non-monotonic timings with Emscripten, as Instant::now() is not broken (seconds only), and emscripten_get_now() is only accurate to milliseconds. The FPS counter shows inconsistent (but stable) readings on different browsers for this reason.
  • emscripten_set_main_loop was quite difficult for me to use due to static lifetime issues, perhaps this is just a skill issue on my part, but I found it easier to use Asyncify initially.

If you're new to Rust + SDL2 and are interested in Emscripten, I would recommend reconsidering your need for Web builds, focusing entirely on Web-only, or using a different language/framework. Native C++ with SDL2 is likely more stable and easier to work with.

While this combination and project is possible, it's not easy, documentation/examples are sparse, and the tooling is not as mature as other languages.

My worry with a bigger project is that the complexity of the system will grow exponentially, and the time spent on debugging and fixing issues will be much higher than the time spent on actual development.

Notes

  • build.sh is the main build script for the Web Assembly target. It's intended for use by both GitHub Actions and local development.
    • Flags are available to modify the behavior. Inspect the script for more information.
  • Conditional compliation is used often in this project to provide different behavior, mostly for Emscripten.
    • Sleeping and Timing is handled by Emscripten's extern functions, while native builds use std::thread::sleep.
    • Store is a simple wrapper around LocalStorage for Emscripten, and has a Dummy implementation for native builds (for now).
  • config.toml is used to provide the special flags for the Emscripten target.
    • ASYNCIFY is used to enable the Asyncify feature, which is required for the game loop. Learn more here.
    • ALLOW_MEMORY_GROWTH seems to be necessary for SDL2 mostly. It allows the memory to grow dynamically, which is required for SDL2 extensions. Perhaps this can be disabled, but I haven't tried yet.
    • USE_SDL is required for SDL2.
    • USE_SDL_IMAGE is required for using SDL2's Image extension.
    • USE_SDL_TTF is required for using SDL2's TTF extension.
    • USE_SDL_MIXER is required for using SDL2's Mixer extension.
    • USE_SDL_GFX is required for using SDL2's GFX extension.
    • USE_OGG is required for using OGG files with SDL2's Mixer extension.
    • SDL2_IMAGE_FORMATS is required for specifying the image formats to load with SDL2's Image.
    • SDL2_MIXER_FORMATS is required for specifying the audio formats to load with SDL2's Mixer.
    • Please check config.toml for an example of how to specify it, as well as the format of each flag; some take a list of values or a version number, or just 1.
  • SDL2, as well as all the extensions require .lib files for compilation, and .dll files for execution on Windows.
    • Check the workflow file for details on what this means specifically.
      • While all extensions could be compiled using vcpkg, downloading the devel libraries seems to be fastest. Although, they might be debug artifacts...
    • Please note that SDL2_gfx is different in that it is not managed by the SDL working group, and thus does not provide official releases.
      • Additionally, there are separate SDL (v1) and SDL2 versions of SDL_gfx, and of course for added confusion, SDL_gfx (for SDL v1), is currently on v2.x.x

Resources

A list of various resources I relied on and studied while building this project. Organized in descending 'usefulness'.

  • build.sh - One of the core files in this repository, it builds the project with Emscripten. Note all the flags available for modifying the behavior of the build.
  • aelred/tetris
    • Contains multiple sub-projects, including a web server. Uses SDL2 TTF & Mixer.
    • Most recent development (3 months ago).
    • Custom font loading, packed inside the binary (WASM) instead of .data file, or external file.
    • Advanced Emscripten bindings for Javascript (fetch, GET/POST)
    • No Asyncify, uses emscripten_set_main_loop callback instead.
    • See the REST functions for Emscripten.
  • gregbuchholz/RuSDLem
    • One of the few with a demo available.
  • tung/ruggrogue
    • A very large game example, great codebase, documentation, online player.
  • KyleMiles/Rust-SDL-Emscripten-Template/
    • Has some special javascript interop code
  • hello-rust-sdl2-wasm
    • A bit of a weird repository, I'm not sure that the creator knows Rust that well, but it compiles.
    • Note that the asmjs-unknown-emscripten target is deprecated, and you should use wasm32-unknown-emscripten instead. You'll need to change all the files, flags etc. to make it match.
  • arskiy/chess
    • Image usage, decent code example
    • Has more advanced javascript config and examples to look at.
  • deckarep/flappy-rust
    • Image + Mixer Usage, possibly GFX & TTF
  • coderedart/rust-sdl2-wasm
    • This is mostly interesting because it has an egui implementation; egui is very cool for demos, developer tooling, debug menus, and so on.
    • The only thing I don't understand is where SDL2 is; there is almost no real code referencing SDL2 except a SDL2Backend provided by the egui crate. Weird.
    • While devoid of anything particularly interesting for my own needs, it has a demo here