Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get Bevy building for WebAssembly with multithreading #12205

Merged
merged 18 commits into from Mar 25, 2024

Conversation

kettle11
Copy link
Contributor

@kettle11 kettle11 commented Feb 29, 2024

Objective

This gets Bevy building on Wasm when the atomics flag is enabled. This does not yet multithread Bevy itself, but it allows Bevy users to use a crate like wasm_thread to spawn their own threads and manually parallelize work. This is a first step towards resolving #4078 . Also fixes #9304.

This provides a foothold so that Bevy contributors can begin to think about multithreaded Wasm's constraints and Bevy can work towards changes to get the engine itself multithreaded.

Some flags need to be set on the Rust compiler when compiling for Wasm multithreading. Here's what my build script looks like, with the correct flags set, to test out Bevy examples on web:

set -e
RUSTFLAGS='-C target-feature=+atomics,+bulk-memory,+mutable-globals' \
     cargo build --example breakout --target wasm32-unknown-unknown -Z build-std=std,panic_abort --release
 wasm-bindgen --out-name wasm_example \
   --out-dir examples/wasm/target \
   --target web target/wasm32-unknown-unknown/release/examples/breakout.wasm
 devserver --header Cross-Origin-Opener-Policy='same-origin' --header Cross-Origin-Embedder-Policy='require-corp' --path examples/wasm

A few notes:

  1. cpal crashes immediately when the atomics flag is set. That is patched in Fix crash on Web / Wasm when 'atomics' flag is enabled RustAudio/cpal#837, but not yet in the latest crates.io release.

That can be temporarily worked around by patching Cpal like so:

[patch.crates-io]
cpal = { git = "https://github.com/RustAudio/cpal" }
  1. When testing out wasm_thread you need to enable the es_modules feature.

Solution

The largest obstacle to compiling Bevy with atomics on web is that wgpu types are not Send and Sync. Longer term Bevy will need an approach to handle that, but in the near term Bevy is already configured to be single-threaded on web.

Therefor it is enough to wrap wgpu types in a send_wrapper::SendWrapper that is Send / Sync, but panics if accessed off the wgpu thread.


Changelog

  • wgpu types that are not Send are wrapped in send_wrapper::SendWrapper on Wasm + 'atomics'
  • CommandBuffers are not generated in parallel on Wasm + 'atomics'

Questions

  • Bevy should probably add CI checks to make sure this doesn't regress. Should that go in this PR or a separate PR? Edit: Added checks to build Wasm with atomics

@alice-i-cecile alice-i-cecile added C-Performance A change motivated by improving speed, memory usage or compile times O-Web Specific to web (WASM) builds A-Tasks Tools for parallel and async work C-Needs-Release-Note Work that should be called out in the blog due to impact labels Feb 29, 2024
@alice-i-cecile
Copy link
Member

alice-i-cecile commented Feb 29, 2024

Bevy should probably add CI checks to make sure this doesn't regress. Should that go in this PR or a separate PR?

Same PR please :) It would be nice to be able to use that check to ensure that this PR is green.

@mockersf
Copy link
Member

would you need to disable this feature to check that wgpu works correctly there?

"fragile-send-sync-non-atomic-wasm",

.github/workflows/ci.yml Outdated Show resolved Hide resolved
@kettle11
Copy link
Contributor Author

would you need to disable this feature to check that wgpu works correctly there?

"fragile-send-sync-non-atomic-wasm",

wgpu ignores that feature internally if the atomics flag is set, so it's not strictly necessary to disable it.

Co-authored-by: François <mockersf@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
/// On other platforms the wrapper simply contains the wrapped value.
#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
#[derive(Debug, Clone)]
pub struct WgpuWrapper<T>(T);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be ideal if this doesn't need to be public, as it's sort of an implementation detail. Perhaps we should make a wrapper for Surface since it's the only type used outside of bevy_render that needs this explicitly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@james7132 I'd also prefer it to be non-public, but the RenderQueue, RenderAdapter, RenderInstance, and RenderAdapterInfo structs all publicly expose their inner wgpu struct which needs to be wrapped in WgpuWrapper.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK sounds good to me. I believe @robtfm recently found that we may not need these wrappers anymore, but we can tackle that in a separate PR.

Copy link
Member

@james7132 james7132 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall I'm OK with this change. I'm glad we're starting to peak into enabling actual paralellism on the web.

With that said, I'm getting very concerned with how much cfg(target_arch = "wasm32"...) we're seeing across the engine. Much of this is teething pains for wasm support in Rust, which is understandable, particularly as it's the job of the engine to handle platform abstraction. However, problems arise when that platform abstraction itself is leaky, and plugins in the ecosystem and end users end up needing to deal with it, as evidenced by the endless stream of issues coming in. These problems are endemic of a deeper problem that needs to be addressed in the foundational crates for wasm support (i.e. wasm-bindgen), as the problems raised are seemingly viral.

/// On other platforms the wrapper simply contains the wrapped value.
#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
#[derive(Debug, Clone)]
pub struct WgpuWrapper<T>(T);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK sounds good to me. I believe @robtfm recently found that we may not need these wrappers anymore, but we can tackle that in a separate PR.

crates/bevy_render/src/renderer/mod.rs Outdated Show resolved Hide resolved
.github/workflows/ci.yml Outdated Show resolved Hide resolved
Co-authored-by: daxpedda <daxpedda@gmail.com>
@alice-i-cecile alice-i-cecile added this to the 0.14 milestone Mar 7, 2024
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.NIGHTLY_TOOLCHAIN }}
target: wasm32-unknown-unknown
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
target: wasm32-unknown-unknown
targets: wasm32-unknown-unknown,x86_64-unknown-linux-gnu

CI is failing because it also needs this target

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That wasn't the issue, CI is still complaining:

"/home/runner/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/Cargo.lock" does not exist, unable to build with the standard library, try:
        rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu

@mockersf
Copy link
Member

mockersf commented Mar 9, 2024

PR looks OK to me, but I'm not sure why the new CI job build-wasm-atomics is failing

@kettle11
Copy link
Contributor Author

kettle11 commented Mar 9, 2024

PR looks OK to me, but I'm not sure why the new CI job build-wasm-atomics is failing

@mockersf I got it fixed after a few attempts. I needed to add components: rust-src.

@james7132
Copy link
Member

Is there another blocker to removing the fragile-send-sync-non-atomic-wasm feature on wgpu with this change?

@kettle11
Copy link
Contributor Author

Is there another blocker to removing the fragile-send-sync-non-atomic-wasm feature on wgpu with this change?

There isn't, but fragile-send-sync-non-atomic-wasm doesn't do anything in wgpu when the atomics flag is enabled, so there's no reason to bother enabling or disabling it.

Copy link
Contributor

@daxpedda daxpedda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

I believe removing fragile-send-sync-non-atomic-wasm would indeed be better. This should remove a bunch of cfgs as types would be more consistent between feature = "atomics" and not(feature = "atomics").

Comment on lines 108 to 109
/// On web with atomics enabled the inner value can only be accessed on the
/// `wgpu` thread or else a panic will occur.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should probably be noted that it will panic if dropped in another thread as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@daxpedda Good call out. I updated the comment.

.github/workflows/ci.yml Outdated Show resolved Hide resolved
Co-authored-by: daxpedda <daxpedda@gmail.com>
@james7132
Copy link
Member

I think this PR also resolves #9304. Marking up the priority on this given the potential unsoundness implications.

@james7132 james7132 added P-High This is particularly urgent, and deserves immediate attention C-Unsoundness A bug that results in undefined compiler behavior labels Mar 23, 2024
Copy link
Member

@JoJoJet JoJoJet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

fragile-send-sync-non-atomic-wasm doesn't do anything in wgpu when the atomics flag is enabled, so there's no reason to bother enabling or disabling it.

Can you at least update the comments in the Cargo.toml? Currently it says this which no longer captures the whole picture

# `fragile-send-sync-non-atomic-wasm` feature means we can't use WASM threads for rendering
# It is enabled for now to avoid having to do a significant overhaul of the renderer just for wasm

@kettle11
Copy link
Contributor Author

Can you at least update the comments in the Cargo.toml? Currently it says this which no longer captures the whole picture

Done!

@james7132 james7132 added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Mar 23, 2024
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Mar 25, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Mar 25, 2024
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Mar 25, 2024
Merged via the queue into bevyengine:main with commit b359740 Mar 25, 2024
31 of 32 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Tasks Tools for parallel and async work C-Needs-Release-Note Work that should be called out in the blog due to impact C-Performance A change motivated by improving speed, memory usage or compile times C-Unsoundness A bug that results in undefined compiler behavior O-Web Specific to web (WASM) builds P-High This is particularly urgent, and deserves immediate attention S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

WASM renderer isn't thread safe
6 participants