zig-gamedev
zig-gamedev copied to clipboard
Emscripten support: wasm & webgpu in browser
Feel free to reject this if you think this is out of scope or if some of the required workarounds are too ugly etc. But I thought to share this in case this interesting/useful and hear what you think.
This adds support and a sample for web compilation with emscripten
toolchain. In general it just adds additional linking step with emsdk on top of regular wasm32-freestanding
build which generates all the nececary js browser binding glue code. But also needs some platform specific modifications.
Main notes with required changes are in triangle_wgpu_emscripten
sample readme.
Live demo (needs nightly browser with webgpu support): https://deins.lv/triangle_wgpu_emscripten/.
Its quite raw and its possible that some of the emscripten specific stuff could be better abstracted and included in zgpu for example. But currently I tried to avoid modifying api in any way requiring user/sample_code to do it instead.
Known issues
- doesn't display any user friendly message if browser doesn't support webgpu
- [fixed] ~~not all keyboard input works correctly in imgui, such as arrow keys / backspace. Might be glfw version incompatibility, has to be debugged.~~
- mapAsync is much slower and there is larger chance of running out of uniform buffers. Blocking is not viable and requires added
canRender
fn to be called before each frame to prevent crashes in such scenarios. Or in most extreme situations skipping frame. Also requires emscripten from upstream as I had to make pull request in regards to mapAsync alignment.
Thanks for the PR and nice work!
I will be looking at this in coming days but please note that I have zero experience with the web stuff. That said, I would like to have this functionality in the project.
@hazeycode Can you please help with the review?
Also, how does this compare to https://github.com/michal-z/zig-gamedev/pull/224?
Very nice work @Deins !
@michal-z This is focused on linking glfw-webgpu using emscipten where in #224 I tried linking sdl-webgl. Also the way the program entry point is resolved is different. I need to revisit some of the details. But I will try to review before the end of the week.
Interesting that Firefox is not playing along. I think that's what I was testing with last time I looked at this - I'll use Chromium as a default from now on, it seems to generally work more often than Firefox.
Sorry, missed #224. It is similar, but yes main difference is glfw vs sdl and webgl vs webgpu.
I don't see a reason why they both could not work. As in this project user can choose which libs to use and which backend. Main requirement would be to create a nice way from build system to be able to choose correct emscripten link flags, etc.
Another difference is that this uses wasm32-freestanding
target instead of wasm32-emscripten
. Reason is: I ran into issues with zig std
that had almost no support for emscripten os tag even for most basic things. And it might be that it is the reason why the c entry point was needed for #224. Overall there is no need to use wasm32-emscripten
its only needed for C/C++ libraries that use emscripten macro magic to inline JS in C source files. Otherwise freestanding
for zig code is much nicer and I don't know any downsides other than not being able to use builtin os.tag
for platform specific checks. So most ergonomic build flow from my experience is to:
- use
freestanding
for zig code - use
freestanding
for C libraries that have no emscripten specific macros - use
emscripten
target tag for C libraries that use emscripten specific macros
In regards to firefox, I got webgpu running on different pc. But it still didn't run: webgpu function onSubmittedWorkDone, is not implemented in it. I am not sure how critical it is in zgpu, probably can be worked around. But it might get implemented in future so might not need to worry too much about it until any browser actually enables webgpu by default in stable releases and doesn't have it (as from my understanding it is in webgpu spec).
I don't see a reason why they both could not work. As in this project user can choose which libs to use and which backend. Main requirement would be to create a nice way from build system to be able to choose correct emscripten link flags, etc.
Agreed
Overall there is no need to use wasm32-emscripten its only needed for C/C++ libraries that use emscripten macro magic to inline JS in C source files. Otherwise freestanding for zig code is much nicer and I don't know any downsides other than not being able to use builtin os.tag for platform specific checks. So most ergonomic build flow from my experience is to: use freestanding for zig code use freestanding for C libraries that have no emscripten specific macros use emscripten target tag for C libraries that use emscripten specific macros
Also agreed. IIRC SDL makes use of these macros and there is some complication with how the main shim works. New SDL 3 simplifies this FWIW. Regardless, we can focus on GLFW+WebGPU here.
Fixed some of the requested changes, still might need some cleanup.
- removed/ switched to target
wasm32-emscripten
. - separated / moved emscripten build and utility stuff in separate lib
zems
. I tried to avoid adding it as dependency to zglfw, zgpu etc. So that mostly stays the same. - ported existing
gui_test_wgpu
to see how easy it is to port existing sample with refactored build stuff demo. Includes usage ofemscripten
asset file preloading that worked well. But file ops are mostly called from c, haven't tested how / if std.fs would work from zig code. Most likely would require some hacks to make std.fs to use posix filesystem calls in either .emscripten or .freestanding target (has to be investigated). - ported instanced_pills_wgpu for quick benchmarks. (mostly probably stresses browser wgpu overhead than cpu/wasm). But gives some feeling of overhead: 1M pills with 7 segments - browser 15FPS vs 20FPS native.
Needs a rebase
Hello everyone.
I'm not sure exactly how to do this, but I rebased deins:emscripten onto michal-z:main on my branch ckrowland:em. Hopefully it can save someone some time. If my attempt is acceptable, Should I create a PR on deins:emscripten branch or create a new pull request here on michal-z:main?
I got all the emscripten examples running on the web with chrome, zig 0.11.0-dev.4403+e84cda0eb, macos 12.6.7 and the current zig-gamedev main.
Firefox nightly is throwing a
queue.onSubmittedWorkDone is not a function
error
What else needs to be done? I'm still getting up to speed on emscripten/wasm.
@Deins Could you please rebase this? We can take it from there if you don't have time to work on it anymore. I think we are very close to finally merge it. Thanks for your work!
sorry, have been too busy to work much on this.
Overall I think its in ok state. Since wgpu.zig was updated to newer version, as well wgpuCreateInstance
implementation in emscripten, some of the ugly hacks are gone now.
Just have to be careful with emscripten versions as wgpu still slightly changes.
Tested now with zig 11 and emscripten sdk from latest master commit ef2a8e929d5337755e9b1d1e1d4ad859dc694eee
seems to be working.
The last thing that still might be nice to get rid of is zgpu canRender
check that I added to safeguard against running out of buffers.
Otherwise just testing it in more complex use cases. I think there might be more of minor usize/u64 mistakes in some wgpu functions but those should be easy to identify and fix.
Some useful developments/info here: https://github.com/ziglang/zig/issues/10836#issuecomment-1703611299
Zig now has emscripten support proper https://github.com/ziglang/zig/issues/10836
Also this is pretty cool, emsdk can be consumed via package manager: https://github.com/floooh/sokol-zig/pull/50#issuecomment-1885387170
I was able to get my demo running live on chromium browsers with this. So much thanks Deins!
Only a few minor things I found:
- Needed to pass -sUSE_OFFSET_CONVERTER to emsdk
- Needed to copy shell.html file emscripten relies on into zems
- Maybe we should include a basic template to start?
Having another go at emscripten support here -> https://github.com/zig-gamedev/zig-gamedev/pull/480, based largely on this PR