Support wasm32-unknown-emscripten target
With WebAssembly support being added to the engine, this should be a priority for the next release.
Unfortunately, this cannot be done right now since Rust is still using LLVM 11, which is no longer supported by the version of emscripten that Godot requires (2.0.10). Rust only updates LLVM on new releases, so we'll have to wait for a few months until LLVM is updated upstream.
Until then, it might be a good idea to clarify the state of WASM support in documentation.
Any updates on this?
@deep-gaurav Not yet, still blocked by rustc :(
With Version 1.52 we got LLVM 12 Support:
https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1520-2021-05-06

Just FYI I successfully compiled for wasm with various hacks : https://github.com/orion78fr/godot_keepass_rust_totp Example : https://orion78.fr/godot_test/keepassTotp.html (it has a simple script that change the button text according to a function call in rust, but I may update things so it may not work as of now)
@orion78fr That's wonderful! Last time I checked there was some miscompilation around i128/u128, but glad to see that the ar trick is working now! Could you add a license on the emcc-test files so we can actually put the workaround in our docs for other people?
I've put the repository under MIT License, that should be OK. The whole steps are here : https://github.com/orion78fr/godot_keepass_rust_totp/actions/runs/900998393/workflow#L255
Some things I had to do :
- Don't forget to set export type to GDNative in the export template (or it won't even try to load the wasm silently) :
- Pass
-allto wasm-opt because it's automatically called by emcc but doesn't seem to pass the correct wasm feature flags and fails - Disable stack unwinding because C exceptions is still in spec : https://github.com/WebAssembly/exception-handling (
CARGO_PROFILE_RELEASE_PANIC: abort, maybe the other flags for overflow / assertions are necessary but I did so many testing I don't remember). - Need to compile with PIC to allow the linker to relocate code :
RUSTFLAGS: -C link-args=-fPIC -C relocation-model=pic
Maybe something else, I don't remember x)
@orion78fr Thanks. The workflow file is very helpful!
@orion78fr thanks so much for looking into this. did you try to run your Web export in safari? on Firefox, chrome it seems to work fine.
I have the same issue with my demo, so I was wondering
Yeah I only tested Chrome / Firefox on Windows and Android (as I mainly focus on APK export atm, can't figure out how I could make Android java calls to open files).
Seems like it's some part of the specs that are not supported on Safari.
CanIuse list shared memory as not supported, but I don't know if it's this in particular. Note that I had emcc complaining about shared memory though so maybe it's this.

@orion78fr I can export a regular WebGL app via godot and it runs in the browser: https://jontopielski.itch.io/puzzle-sigma (just an example)
so I am still a little hopeful we can figure out how to build a gdnative lib so that safari can load it aswell - after all the above shows that godot itself manages to compile to something safari is able to take and run. it must be some crazy param that i am missing to pass to emcc
whatever I do once I run wasm-validate on my gdnative .wasm I get:
wasm-validate _deploy/web/logic.wasm
0000da7: error: invalid section code: 12
which does not happen on any of the Wasm files godot produces.
after a little digging validate works once I pass: --enable-bulk-memory
which tells me that somehow my emcc generates something we do not want
(Oh I've seen this one featured in the GMTK jam 2021 video !)
Yeah probably, but I think that I'm missing some flags to pass to rustc too, like the "pic" one or the "panic = abort" to avoid generating things that emcc / wasm don't like.
The dynamic lib ABI is still in a WIP state though : https://github.com/WebAssembly/tool-conventions/blob/master/DynamicLinking.md
I've read somewhere that iOS doesn't even support dynamic libs and that GDNative lib should be statically linked, maybe we could do something similar and produce a binary with both godot and out GDNative lib ?
Try removing -Wl,--no-check-features -all from the emcc flags, you will see the experimental part used by the binaries produced by rustc.
I really stopped debugging this when it produced a binary that loaded without error. Probably some code generated by the rust compiler isn't correctly supported by all browsers but I don't know how to avoid it, I'm really a noob at rust atm.
Hi! I just arrived after googling a few hours... Is there a "one-line" command to do the same as you have in the build? (I'm on archlinux)
I tried several things but I end with the error:
...registry/src/github.com-1ecc6299db9ec823/gdnative-sys-0.9.3/godot_headers/gdnative/string.h:39:10: fatal error: 'wchar.h' file not found
I setted the clang lib with LIBCLANG_PATH=/usr/lib/emsdk/upstream/lib, tried other ones that I installed in the OS... I have no luck with this
I tries to build for wasm32-unknown-unknown and I have the same error with the header...
Try removing
-Wl,--no-check-features -allfrom theemccflags, you will see the experimental part used by the binaries produced byrustc. I really stopped debugging this when it produced a binary that loaded without error. Probably some code generated by the rust compiler isn't correctly supported by all browsers but I don't know how to avoid it, I'm really a noob at rust atm.
yeah that gives us:
wasm-ld: error: mutable global imported but 'mutable-globals' feature not present in inputs: `__stack_pointer`. Use --no-check-features to suppress.
so the question is: can we make rust not emit mutable-globals (maybe via https://releases.llvm.org/10.0.0/tools/clang/docs/ClangCommandLineReference.html#cmdoption-clang-mmutable-globals)?
@orion78fr oooor reading this maybe we can advance here not exporting all functions --export-all since the exported functions are well defined by gdnative anyway
@cristomc I think you are missing the include path from emscripten (C_INCLUDE_PATH: ${{env.EMSDK}}/upstream/emscripten/cache/sysroot/include/)
A little followup:
I figured out all the emscripten specific rust flags using:
rustc --target wasm32-unknown-emscripten --print target-features
Features supported by rustc for this target:
simd128 - Enable 128-bit SIMD.
atomics - Enable Atomics.
nontrapping-fptoint - Enable non-trapping float-to-int conversion operators.
crt-static - Enables C Run-time Libraries to be statically linked.
Code-generation features supported by LLVM for this target:
bulk-memory - Enable bulk memory operations.
exception-handling - Enable Wasm exception handling.
multivalue - Enable multivalue blocks, instructions, and functions.
mutable-globals - Enable mutable globals.
reference-types - Enable reference types.
sign-ext - Enable sign extension operators.
tail-call - Enable tail call instructions.
Use +feature to enable a feature, or -feature to disable it.
For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2
Code-generation features cannot be used in cfg or #[target_feature],
and may be renamed or removed in a future version of LLVM or rustc.
so by passing -C target-feature=+mutable-globals the emscripten error actually goes away. unfortunately the resulting library still contains bulk-memory operations
Can you try adding -C target-feature=-bulk-memory to RUSTFLAGS ? Maybe it will work
Can you try adding
-C target-feature=-bulk-memoryto RUSTFLAGS ? Maybe it will work
I tried all that :D, seems all features are off by default.
but I got it to run. let me cleanup and see which of the thousand changes actually were needed, then I will post that here🥳
but I got it to run.
Example code of this would really help
but I got it to run.
Example code of this would really help
like I said: I am cleaning it up and posting back here...
Sorry wasn't sure if you meant you were just going to share the flags
Sorry wasn't sure if you meant you were just going to share the flags
It sure is just about the flags - the rest is unchanged to the repo @orion78fr shared
Now I can confirm it works on safari aswell, you only need to reduce the optimisation: EMMAKEN_CFLAGS="-O1 -s SIDE_MODULE=1"
But this is not where the story ends, I wanted to port two open source godot projects: godot-vs-rapier and rapier-determinism (you can find the exact build calls as makefiles in there aswell)
Both make use of rapier with different feature flags and uncovered an issue in rapier itself using instant which was not emscripten ready: https://github.com/dimforge/rapier/issues/207
When using serde support the 128bit error @toasteater mentioned happens which boils down to serde deliberately disabling 128bit support in emscripten builds: https://github.com/serde-rs/serde/pull/2049
After figuring all those out I now have web builds of the above projects!! 🥳
see: https://extrawurst.github.io/godot-vs-rapier/ https://extrawurst.github.io/rapier-determinism/
Thanks again @orion78fr for kicking this off.
A few things that I would like to investigate from here on:
- update godot 3.3 to 3.3.2
- updating emscripten (I ran into issue if the emsdk was not exactly
2.0.17 - ~~the file api in gdnative does not work in wasm builds~~ (I just forgot to make my custom export rules apply to the web export)
- figuring out why optimisation breaks safari builds (-O0 builds bigger wasm files)
Is this stable enough to be added to the Makefile in godot-rust-template?
Honestly, I'm unsure. It seems very brittle. Changing opt flag level or the emscripten version seems to break the build or make it incompatible with some browser vendors.
being a emscripten noob I am wondering: is there a way to make godo-web call our gdnative lib compiled to regular wasm? I mean the wasm64-unknown-unknown seems more stable and we do not have to worry at all about the PITA emscripten toolchain.
That would have been cool but it seems like godot-rust needs a libc (wchar.h & co), which is what is provided by emscripten IIRC.