1 of 20

Webassembly & Fallout2

How it went

2 of 20

It begun with fallout2-ce project

  • Someone used IDA pro for reverse-engineering
  • Working C code

3 of 20

Webassembly

Webassembly.Memory ~ ArrayBuffer

JavaScript

Webassembly

js_func1()

wa_func2()

js_func3()

4 of 20

Emscripten

LLVM code

Emscripten output

JS

WASM

HTML

libc / SDL / etc

5 of 20

JS event loop

  • call init
  • call emscripten_set_main_loop

JS

Webassembly

requestAnimationFrame()

call_me_on_each_frame()

6 of 20

Real-life example

more than 2 loops = “many”

7 of 20

First try: thread

PROXY_TO_PTHREAD || --proxy-to-worker == did not work with SDL & opengl

Main thread

Game thread

<waits in Atomics>

postMessage

8 of 20

Another try: post ImageBuffer

Very slow

Main thread

Game thread

postMessage with ImageBuffer

9 of 20

OffscreenCanvas?

WebGL commits on event loop

Main thread

Game thread

Canvas

OffscreenCanvas

10 of 20

Asyncify

Unwinds / rewinds call stack

“Pause and Resume WebAssembly with Asyncify, Alon Zakai”

11 of 20

Binaryen & JS

js_run_main()

wa_main()

main_loop()

render_frame()

get_time()

SDL_sleep()

sleep()

js_sleep()

return Date.now()

GLOBAL STATE = normal | unwinding | rewinding

char asyncify_stack[1000];

move_enemies()

12 of 20

It works!

13 of 20

Asyncfetchfs

Usual way is to preload everything

With Asyncify we can have async filesystem

Emscripten

JS

Asyncfetchfs

JS

__syscall_openat

WASM

stdlib

open()

WASM

fallout2-ce

fopen()

14 of 20

Issue with random freezing

___syscall_openat creates new stream everytime it runs

Emscripten

JS

WASM

Asyncfetchfs

JS

__syscall_openat

Here is side effect

open()

15 of 20

fd_read

In theory possible too

Emscripten

JS

WASM

wasi fd_read

Asyncfetchfs

JS

read()

16 of 20

Final FS layout

/ = MEMFS with no preloaded files

/app = asyncfetchfs

/app/data/SAVEGAME = IDBFS

/app/<temp1> = MEMFS

/app/<temp2> = MEMFS

17 of 20

Mostly no changes to original code

  • Remove spin waits
  • Call stack exceeded when pathfinding - solved via “user space” stack
  • Call fsync to trigger IDBFS to save data

18 of 20

Spinwait for sound

while (! is_sound_buf_empty()) {

SDL_Sleep(1); // Add this line!

}

WebAudio scriptProcessorNode

read_data_from_SDL_buf()

19 of 20

Javascript code

  • Download/upload savegames
  • Use service worker for engine
  • Save downloaded file into `caches`

20 of 20

Thanks!

Questions?