https://github.com/cedrickchee/doom-wasm

Porting DOOM to WebAssembly from scratch, without much magic tooling or frameworks (i.e, Emscripten) on our way.

https://github.com/cedrickchee/doom-wasm

Science Score: 13.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
  • DOI references
  • Academic publication links
  • Academic email domains
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (11.5%) to scientific vocabulary

Keywords

doom from-scratch javascript rust wasm webassembly
Last synced: 5 months ago · JSON representation

Repository

Porting DOOM to WebAssembly from scratch, without much magic tooling or frameworks (i.e, Emscripten) on our way.

Basic Info
  • Host: GitHub
  • Owner: cedrickchee
  • Language: C++
  • Default Branch: main
  • Homepage:
  • Size: 1.66 MB
Statistics
  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • Open Issues: 0
  • Releases: 0
Topics
doom from-scratch javascript rust wasm webassembly
Created over 3 years ago · Last pushed over 3 years ago
Metadata Files
Readme

README.md

Porting Linux DOOM to WebAssembly

We got vanilla Linux DOOM starting from Rust.

DOOM starting via Cargo


libc

Getting musl

AR=llvm-ar-10 CC=clang CFLAGS="-m32 --target=wasm32" ./configure --target=wasm32

no wasm32 arch support

Getting the arch from https://github.com/emscripten-core/emscripten/tree/efede793113ce1aa4d38d4f2df08e6b251cc53c6/system/lib/libc/musl/arch/emscripten

Throwing out everything which is complicated.

Crossover of musl 1.2.2 and arch from emscripten of musl 1.1.15.

Only need the string formatting functions anyway. Kicking out everything else.

Only making make lib/libc.a.

compiler rt for builtins

From: https://compiler-rt.llvm.org/

For example, when compiling for a 32-bit target, converting a double to a 64-bit unsigned integer is compiling into a runtime call to the "__fixunsdfdi" function.

https://00f.net/2019/04/07/compiling-to-webassembly-with-llvm-and-clang/ provides a precompiled libclang_rt.builtins-wasm32.a, which brings down the missing imports to 51. The result looks very promising. But I want to build a minimal version myself.

Get https://github.com/llvm/llvm-project/ llvm-project/compiler-rt/lib/builtins sources and compile myself. No need for arch I hope, no assembly to be emitted. But using git tag llvmorg-11.1.0.


Dat feel! After so much theory and no way to test. Finally seeing the first screen of Doom rendered. Awesome!

Doom rendering the first screen to an HTML5 canvas

Doom rendering broken colors, but can read text:

Doom rendering broken colors, but can read text

Start screen rendering correctly on an HTML5 canvas:

We mapped Doom's X11 ColorMap to canvas's RGBA color:

Doom's title screen


If I don't make I_FinishUpdate panic!(), then Doom runs in its infinite game loop. Unfortunately, this runs at 100% CPU, Firefox complains that a website is misbehaving, and nothing is rendered, since the browser has no chance of drawing the animation.

Probably, I want to change Doom such that doom itself is not looping, but I can call the loop via window.requestAnimationFrame().

This somehow inverses control and gives the browser a chance to render the frames.

Project Structure

A summary of the directory structure: - build.rs: Rust build script. Tells the rust compiler to build and link to our small libc, compiler runtime, and doom library. - clang_compiler_rt: C compiler runtime, to compile as static archive. - musl-1.2.2: libc for C string functions, to compile as static archive. - linuxdoom-1.10: original doom sources, to compile as static archive. - doom1.wad: Doom game file. - src: Rust sources. - index.html: HTML and Javascript to load the compiled WebAssembly and provide keyboard input and HTML5 canvas rendering output.

Optimizations

There is more to optimize for web-native performance.

The firefox performance profiler says we spend most of our time in gettimeofday. In b1eab74, we remove this implementation completely, avoiding the need to construct a Date object in javascript and avoiding sedond and microsecond translation, since Doom just cares about the milliseconds since the start of the game, which happens to be what javascript's performance.now() provides.

The game runs at ~35 FPS on my machine, but Chrome performance debugging tools still show many dropped frames, since the browser wants to animate at 60 FPS. In addition, since Doom is still polling the time to know when it can proceed, this is super energy inefficient and gives the browser no room for background tasks, such as garbage collection. In a048af0, we make Doom to return immediately when running one step of its game loop if there is nothing to do, giving control back to the browser. Now, Doom still runs at ~35 FPS (this is what Doom was designed for), but the browser gets a chance to render its 60 animation frames per second and the system is mostly idle otherwise. I can clearly hear the difference, since my CPU fan is no longer spinning up when starting Doom.

Canvas

Copy video bufer one time less.

Before:

javascript var doom_screen = new Uint8Array(memory.buffer, ptr, doom_screen_width*doom_screen_height*4); var ctx = canvas.getContext('2d'); var render_screen = ctx.createImageData(doom_screen_width, doom_screen_height);

After: javascript var doom_screen = new Uint8ClampedArray(memory.buffer, ptr, doom_screen_width*doom_screen_height*4); var render_screen = new ImageData(doom_screen, doom_screen_width, doom_screen_height); var ctx = canvas.getContext('2d');

Chrome and Firefox Dev tools confirm that this makes the game run at least twice as efficient.

WebAssembly Specific Optimizations

I want to enable full optimization of the whole wasm binary.

I would love to use LLVM-supported cross-language C/Rust LTO here, but it looks like wasm-ld-10 does not support this.

The -plugin-opt option is not supported by my wasm-ld-10 (and it looks like neither v11 or v12 support it).

As reference, ld.lld would support this option. IIUC, LLVM currently does not support LTO for wasm?

Makefile wasm-opt -O3 -o doom.wasm ${BUILDDIR}/xdoom.wasm


Now go to https://cedrickchee.github.io/wasm-doom and start shooting monsters!

Owner

  • Name: Cedric Chee
  • Login: cedrickchee
  • Kind: user
  • Location: PID 1
  • Company: InvictusByte

Lead Software Engineer | LLMs | full stack Go/JS dev, backend | product dev @ startups | 🧑‍🎓 CompSci | alumni: fast.ai, Antler.co

GitHub Events

Total
Last Year

Issues and Pull Requests

Last synced: 12 months ago

All Time
  • Total issues: 0
  • Total pull requests: 3
  • Average time to close issues: N/A
  • Average time to close pull requests: 7 minutes
  • Total issue authors: 0
  • Total pull request authors: 1
  • Average comments per issue: 0
  • Average comments per pull request: 0.0
  • Merged pull requests: 3
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 0
  • Pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Issue authors: 0
  • Pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
Pull Request Authors
  • cedrickchee (3)
Top Labels
Issue Labels
Pull Request Labels

Dependencies

Cargo.lock cargo
  • lazy_static 1.4.0
Cargo.toml cargo