Skip to content

Commit

Permalink
linting and wording improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
PgBiel committed Feb 3, 2025
1 parent 3c27966 commit 02401f0
Showing 1 changed file with 71 additions and 28 deletions.
99 changes: 71 additions & 28 deletions src/toolchain/export-web.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ branch = "master"
features = ["experimental-wasm", "lazy-function-tables"]
```

Next, begin configuring the `emcc` flags and export targets as below. These initial settings will assume that your extension needs thread support; check the "Thread support" section below if your extension can run single-threaded.
Next, begin configuring the `emcc` flags and export targets as below. These initial settings will assume that your extension needs multi-threading
support, but that's usually not the case, so make sure to check the ["Thread support" section](#thread-support-godot-43-or-later) below if you're
exporting to Godot 4.3 or later.

If you do not already have a `.cargo/config.toml` file, do the following:

Expand Down Expand Up @@ -93,6 +95,7 @@ web.debug.wasm32 = "res://../rust/target/wasm32-unknown-emscripten/debug/{YourCr
web.release.wasm32 = "res://../rust/target/wasm32-unknown-emscripten/release/{YourCrate}.wasm"
```


## Compile the Project

Verify `emcc` is in the `PATH`. This can be as simple as doing the following:
Expand All @@ -108,17 +111,26 @@ It is necessary to both use the nightly compiler and specify to build std[^3], a
cargo +nightly build -Zbuild-std --target wasm32-unknown-emscripten
```


## Thread support (Godot 4.3 or later)

The above settings assume that multi-threading support is always needed for your extension. However, starting with Godot 4.3, when the end user exports a game to the web, Godot includes an option to disable Thread support in the export menu (see the image in the ["Godot editor setup" section](#godot-editor-setup)), with the goal of having the exported game run in more environments, including older browsers and webservers without Cross-Origin Isolation support.
The above settings assume that multi-threading support is always needed for your extension. However, starting with Godot 4.3, when the end user
exports a game to the web, Godot includes an option to disable Thread support in the web export menu
(see the image in the ["Godot editor setup" section](#godot-editor-setup)), with the goal of having the exported game run in more environments,
including older browsers, as well as webservers without Cross-Origin Isolation support.

If the user disables Thread support using that new option, however, your extension, as is, would break. If you'd like to support builds without thread support as well, you will need to update your setup as follows:
If the end user disables Thread support using that new option, however, your extension, without any changes, would break.
If you'd like to support builds without thread support as well, you will need to update your build setup as follows:

1. To build **without any multi-threading support** (that is, to have your extension only work when "Thread support" is disabled), you must remove the line with the `-pthread` flag from `.cargo/config.toml`, as well as enable the [`experimental-wasm-nothreads`][api-cargo-features] gdext feature in `Cargo.toml`.
1. To build **without any multi-threading support** (that is, to have your extension only work when "Thread support" is disabled),
you must remove the line with the `-pthread` flag from `.cargo/config.toml`,
as well as enable the [`experimental-wasm-nothreads`][api-cargo-features] gdext feature in `Cargo.toml`.

2. To support **both with and without thread support**, you will need to have two separate builds, one for each mode. Here's how this can be done:
2. To support exports **both with and without thread support (recommended)**, you will need to have two separate builds, one for each mode.
Here's how this can be done:

1. Remove `"-C", "link-args=-pthread"` from `.cargo/config.toml` so that you may conditionally enable it later, resulting in the following updated `.cargo/config.toml` file:
1. Remove `"-C", "link-args=-pthread"` from `.cargo/config.toml` so that you may conditionally enable it afterwards,
resulting in the following updated `.cargo/config.toml` file:

```toml
[target.wasm32-unknown-emscripten]
Expand All @@ -129,22 +141,29 @@ If the user disables Thread support using that new option, however, your extensi
"-Cllvm-args=-enable-emscripten-cxx-exceptions=0",
]
```
2. Create a feature for your main crate which enables [`experimental-wasm-nothreads`][api-cargo-features] when used. You can do this by creating a `[features]` section in your `Cargo.toml` as follows:


2. Create a feature for your main crate which enables [`experimental-wasm-nothreads`][api-cargo-features] when used.
You can do this by creating a `[features]` section in your crate's `Cargo.toml` as follows:

```toml
[features]
nothreads = ["gdext/experimental-wasm-nothreads"]
```

Note that this feature should be enabled on any crates depending on gdext, so if you have more than one crate in your workspace, you should add the same `[features]` section above to each other crate using gdext, and then enable each crate's `nothreads` feature from the main crate (providing the extension entrypoint).
Note that this feature should be enabled on any crates depending on gdext, so if you have more than one crate in your workspace,
you should add the same `[features]` section above to each other crate using gdext, and then enable each crate's `nothreads` feature
from the main crate (which provides the extension's entrypoint).

For example, if you have a workspace with one main crate called `extension` and two other crates called `lib1` and `lib2`, each depending on gdext, then you may add the `[features]` section above to `crates/lib1/Cargo.toml` and `crates/lib2/Cargo.toml`, and then add the following to `crates/extension/Cargo.toml`:
For example, if you have a workspace with one main crate called `extension` and two other crates called `lib1` and `lib2`,
each depending on gdext, then you may add the `[features]` section above to `crates/lib1/Cargo.toml` and `crates/lib2/Cargo.toml`,
and then add the following to `crates/extension/Cargo.toml`:

```toml
[features]
# Ensure enabling 'nothreads' for the main crate also enables it for other crates.
nothreads = ["lib1/nothreads", "lib2/nothreads", "gdext/experimental-wasm-nothreads"]
```
3. Edit your `.gdextension` file with two separate binary paths - one for the threaded build and one for the `nothreads` build:
3. Edit your `.gdextension` file to list two separate Wasm binary paths - one for the threaded build and one for the `nothreads` build:

```ini
[libraries]
Expand All @@ -156,26 +175,32 @@ If the user disables Thread support using that new option, however, your extensi
```

4. Have two separate build commands, executed in the following order:
1. **Building with thread support:** , you must add the `-pthread` flag back manually through the `RUSTFLAGS` environment variable, but NOT enable the `nothreads` feature. Afterwards, you should rename the generated wasm binary, such that it can be picked up by the modified `.gdextension` file as a threaded build:
1. **Building with thread support:** you must add the `-pthread` flag back manually through the `RUSTFLAGS` environment variable,
but NOT enable the `nothreads` feature. Afterwards, you should rename the generated wasm binary, such that it can be picked up
by the modified `.gdextension` file as a threaded build:

```sh
RUSTFLAGS="-C link-args=-pthread" cargo +nightly build -Zbuild-std --target wasm32-unknown-emscripten
mv target/debug/{YourCrate}.wasm target/debug/{YourCrate}.threads.wasm
# On Batch (Windows), use instead: REN target\debug\{YourCrate}.wasm {YourCrate}.threads.wasm
```
```

For a release mode build, you'd replace `debug` with `release` in the last command.


2. **Building without thread support:** build without the `-pthread` flag, but enabling your `nothreads` feature created in the second step. No further renaming is needed (but make sure the previous build's resulting binary was renamed to avoid overwriting it):
2. **Building without thread support:** build without the `-pthread` flag, but enabling your `nothreads` feature created in the second step.
No further renaming is needed (but make sure the previous build's resulting binary was renamed to avoid overwriting it):

```sh
cargo +nightly build --features nothreads -Zbuild-std --target wasm32-unknown-emscripten
```

3. If your extension is meant to be distributed to other users beside you, the developer, make sure to ship both binaries (with and without thread support) to end users.
3. If your extension is meant to be distributed to other users beside you, the developer, don't forget to ship both binaries
(with and without thread support) to end users.

5. Optionally, if you'd like to disable certain functionality in your extension when building for an environment without multi-threading (e.g. disable a certain multi-threaded function call), you can use `#[cfg(nothreads)]` to not compile certain code to the nothreads binary, thanks to the feature created in step 2:
5. Optionally, if you'd like to disable certain functionality in your extension for `nothreads` builds
(e.g. disable a certain multi-threaded function call), you can use `#[cfg(nothreads)]` and its variants to conditionally compile certain code
under non-threaded builds, thanks to the `nothreads` feature created in step 2. For example:

```rs
fn maybe_threaded_function() {
Expand All @@ -186,12 +211,15 @@ If the user disables Thread support using that new option, however, your extensi

#[cfg(not(nothreads))]
{
std::thread::spawn(|| { /* multi-threaded code */ }).join()
std::thread::spawn(|| { /* multi-threaded code */ }).join().unwrap();
}
}
```

With those steps, you may successfully compile your extension with and without thread support, and let you and your end users choose either when exporting games to the web. To not have to remember the multiple build commands, it is advised to add them to a single shell script file called `build.sh` which invokes both builds in order (including the binary file renaming and any other steps), or store them in a Justfile (useful if you need Windows support), Makefile or similar.
With those steps, you may successfully compile your extension with and without thread support, and let you and your end users choose either when
exporting games to the web. To not have to remember the multiple build commands, it is advised to add them to a single shell script file
called `build.sh` which invokes both builds in order (including the binary file renaming before the second build and any other steps), or store them
in a [Justfile](https://github.com/casey/just) (useful if you need to build from Windows), Makefile or similar.

[api-cargo-features]: https://godot-rust.github.io/docs/gdext/master/godot/#cargo-features

Expand All @@ -201,11 +229,12 @@ With those steps, you may successfully compile your extension with and without t
To export your game using gdext to the web, add a web export in the Godot Editor. It can be configured at `Project > Export...` in the top menu bar.
Make sure to turn on the `Extensions Support` checkbox.

In Godot 4.3 or above, you should also make sure to turn on the `Thread Support` checkbox, unless your extension has a nothreads build, which can be made by following the steps in the ["Thread Support" section](#thread-support-(godot-4.3-or-later)).
In Godot 4.3 or above, you should also make sure to turn on the `Thread Support` checkbox, unless your extension has a nothreads build,
which can be made by following the steps in the ["Thread Support" section](#thread-support-godot-43-or-later).

![Example of export screen](images/web-export.png)

If the error below appears at the bottom on the export popup instead:
If the error below appears in red at the bottom of the export popup instead:

> No export template found at expected path:
Expand All @@ -227,21 +256,32 @@ At the top right, choose `Remote Debug > Run in Browser` and it will automatical
- GDExtension support for Firefox requires Godot 4.3+, and can be more limited compared to Chromium-based browsers (Google Chrome or Microsoft Edge).
```

If you face problems with Firefox, you may need to copy the URL of the server created by the editor, which is usually `http://localhost:8060/tmp_js_export.html`, and open it in a Chromium-based browser such as Google Chrome or Microsoft Edge to verify whether it's a problem with your game or with Firefox.
If you face problems when testing with Firefox, you may need to copy the URL of the server created by the editor, which is usually
`http://localhost:8060/tmp_js_export.html`, and open it in a Chromium-based browser such as Google Chrome or Microsoft Edge to verify
whether it's a problem with your game or with Firefox.

[godot-export-templates]: https://docs.godotengine.org/en/stable/tutorials/export/exporting_projects.html#export-menu


## Troubleshooting

1. Make sure `Extensions Support` is turned on when exporting.
2. When using Godot 4.3+, `Thread Support` has to be turned on during export unless your extension supports a nothreads build, as described in the ["Thread Support" section](#thread-support-(godot-4.3-or-later)).
3. If "Thread support" is enabled (or the game is exported with Godot 4.1 or 4.2), make sure the webserver you use to host your game supports Cross-Origin Isolation. Games uploaded to `itch.io` should work fine here. To test locally, you can either use the Godot editor's built-in web game runner, or a third-party HTTP server host such as `sfz --coi` (the flag ensures Cross-Origin Isolation is supported).

- Note that Godot 4.3 games exported to the web without "Thread support" are not subject to this restriction, making them compatible with more environments, which is the main advantage of disabling that option. You may even have success in running those games by simply double-clicking the generated HTML file. The main caveat is that they may only run single-threaded.

4. Make sure your Rust library and Godot project are named differently, as otherwise your extension's `.wasm` file may be overwritten.
2. When using Godot 4.3+, `Thread Support` has to be turned on during export unless your extension supports a nothreads build,
as described in the ["Thread Support" section](#thread-support-godot-43-or-later).
3. If the game was exported with `Thread support` enabled (or targeting Godot 4.1 or 4.2), make sure the webserver you use to host your game supports
Cross-Origin Isolation. Web games hosted on `itch.io`, for example, should already support this out of the box.
To test it locally, you can either use the Godot editor's built-in web game runner (shown in ["Running the webserver"](#running-the-webserver)),
or a third-party HTTP server program such as `sfz --coi` (the flag ensures Cross-Origin Isolation is supported).

- Note that Godot 4.3 games exported to the web without `Thread Support` are not subject to this restriction, making them compatible with
more environments, which is the main advantage of disabling that option. You may even have success in running those games by simply
double-clicking the generated HTML file. The main caveat is that they may only run single-threaded.

4. Make sure your Rust library and Godot project are named differently (for example, `cool-game-extension` and `cool-game`),
as otherwise your extension's `.wasm` file may be overwritten.
5. Make sure you're using at least the minimum recommended `emcc` version in the guide.


## Debugging

Currently, the only option for WASM debugging is
Expand All @@ -253,7 +293,10 @@ for Chrome. It adds support for breakpoints and a memory viewer into the F12 men

---

[^1]: Note: Due to a bug with `emscripten`, web export templates for Godot 4.2 and earlier versions could only be compiled with `emcc`[^2] versions up to `3.1.39`. If you're targeting those older Godot versions, it could be safer to use `emcc` version `3.1.39` to compile your extension as well, but newer `emcc` versions might still work regardless (just make sure to test your extension in all targeted Godot versions).
[^1]: Note: Due to a bug with `emscripten`, web export templates for Godot 4.2 and earlier versions could only be compiled with
`emcc`[^2] versions up to `3.1.39`. If you're targeting those older Godot versions, it could be safer to use `emcc` version `3.1.39`
to compile your extension as well, but newer `emcc` versions might still work regardless
(just make sure to test your extension in all targeted Godot versions).

[^2]: `emcc` is the name of Emscripten's compiler.

Expand Down

0 comments on commit 02401f0

Please sign in to comment.