diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 50c3038615..079ac59038 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -138,6 +138,7 @@ jobs: set -e # build for WebGPU + cargo clippy --target ${{ matrix.target }} --tests --features glsl,spirv,fragile-send-sync-non-atomic-wasm cargo clippy --target ${{ matrix.target }} --tests --features glsl,spirv cargo doc --target ${{ matrix.target }} --no-deps --features glsl,spirv diff --git a/CHANGELOG.md b/CHANGELOG.md index cf48c37ecf..6bceb0e7ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,7 @@ Bottom level categories: - Document feature requirements for `DEPTH32FLOAT_STENCIL8` by @ErichDonGubler in [#3734](https://github.com/gfx-rs/wgpu/pull/3734). - Flesh out docs. for `AdapterInfo::{device,vendor}` by @ErichDonGubler in [#3763](https://github.com/gfx-rs/wgpu/pull/3763). - Spell out which sizes are in bytes. By @jimblandy in [#3773](https://github.com/gfx-rs/wgpu/pull/3773). +- On Web, types don't implement `Send` or `Sync` anymore. By @daxpedda in [#3691](https://github.com/gfx-rs/wgpu/pull/3691) ### Bug Fixes diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 2cfbe2d426..7c8213e8b0 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -51,6 +51,8 @@ serial-pass = ["serde", "wgt/serde", "arrayvec/serde"] id32 = [] # Enable `ShaderModuleSource::Wgsl` wgsl = ["naga/wgsl-in"] +# Implement `Send` and `Sync` on Wasm. +fragile-send-sync-non-atomic-wasm = ["hal/fragile-send-sync-non-atomic-wasm", "wgt/fragile-send-sync-non-atomic-wasm"] [dependencies] arrayvec = "0.7" diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index 7a99c90bd6..5c4ca122a8 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -746,7 +746,21 @@ pub struct RenderBundle { pub(crate) life_guard: LifeGuard, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] unsafe impl Send for RenderBundle {} +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] unsafe impl Sync for RenderBundle {} impl RenderBundle { diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index 98095f4490..6e0be3b297 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -37,6 +37,13 @@ pub struct SubmittedWorkDoneClosureC { pub user_data: *mut u8, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] unsafe impl Send for SubmittedWorkDoneClosureC {} pub struct SubmittedWorkDoneClosure { @@ -45,17 +52,30 @@ pub struct SubmittedWorkDoneClosure { inner: SubmittedWorkDoneClosureInner, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] +type SubmittedWorkDoneCallback = Box; +#[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +)))] +type SubmittedWorkDoneCallback = Box; + enum SubmittedWorkDoneClosureInner { - Rust { - callback: Box, - }, - C { - inner: SubmittedWorkDoneClosureC, - }, + Rust { callback: SubmittedWorkDoneCallback }, + C { inner: SubmittedWorkDoneClosureC }, } impl SubmittedWorkDoneClosure { - pub fn from_rust(callback: Box) -> Self { + pub fn from_rust(callback: SubmittedWorkDoneCallback) -> Self { Self { inner: SubmittedWorkDoneClosureInner::Rust { callback }, } diff --git a/wgpu-core/src/error.rs b/wgpu-core/src/error.rs index 3cdecc5369..62ecebe9b9 100644 --- a/wgpu-core/src/error.rs +++ b/wgpu-core/src/error.rs @@ -162,7 +162,22 @@ pub fn format_pretty_any( #[derive(Debug)] pub struct ContextError { pub string: &'static str, + #[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + ))] pub cause: Box, + #[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + )))] + pub cause: Box, pub label_key: &'static str, pub label: String, } diff --git a/wgpu-core/src/global.rs b/wgpu-core/src/global.rs index 5331265962..e3ed2be761 100644 --- a/wgpu-core/src/global.rs +++ b/wgpu-core/src/global.rs @@ -155,7 +155,16 @@ impl Drop for Global { } } -#[cfg(test)] +#[cfg(all( + test, + any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + ) +))] fn _test_send_sync(global: &Global) { fn test_internal(_: T) {} test_internal(global) diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 4aca271e9e..fe881c2d06 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -79,7 +79,21 @@ pub(crate) enum BufferMapState { Idle, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] unsafe impl Send for BufferMapState {} +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] unsafe impl Sync for BufferMapState {} #[repr(C)] @@ -88,6 +102,13 @@ pub struct BufferMapCallbackC { pub user_data: *mut u8, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] unsafe impl Send for BufferMapCallbackC {} pub struct BufferMapCallback { @@ -96,17 +117,30 @@ pub struct BufferMapCallback { inner: Option, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] +type BufferMapCallbackCallback = Box; +#[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +)))] +type BufferMapCallbackCallback = Box; + enum BufferMapCallbackInner { - Rust { - callback: Box, - }, - C { - inner: BufferMapCallbackC, - }, + Rust { callback: BufferMapCallbackCallback }, + C { inner: BufferMapCallbackC }, } impl BufferMapCallback { - pub fn from_rust(callback: Box) -> Self { + pub fn from_rust(callback: BufferMapCallbackCallback) -> Self { Self { inner: Some(BufferMapCallbackInner::Rust { callback }), } diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 79eb40bffb..46ca5de24b 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -43,6 +43,7 @@ dx12 = ["naga/hlsl-out", "d3d12", "bit-set", "range-alloc", "winapi/std", "winap windows_rs = ["gpu-allocator"] dxc_shader_compiler = ["hassle-rs"] renderdoc = ["libloading", "renderdoc-sys"] +fragile-send-sync-non-atomic-wasm = ["wgt/fragile-send-sync-non-atomic-wasm"] link = ["metal/link"] [[example]] diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index fc68b437e8..d7e72619c5 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -936,10 +936,17 @@ impl super::AdapterShared { } } -// SAFE: Wasm doesn't have threads -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for super::Adapter {} -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Send for super::Adapter {} #[cfg(test)] diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs index 14bbb36601..ecd68b4b8d 100644 --- a/wgpu-hal/src/gles/device.rs +++ b/wgpu-hal/src/gles/device.rs @@ -1321,8 +1321,15 @@ impl crate::Device for super::Device { } } -// SAFE: Wasm doesn't have threads -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for super::Device {} -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Send for super::Device {} diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index 1b5e3f1422..5c53b3a281 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -246,10 +246,17 @@ pub struct Buffer { data: Option>>>, } -// Safe: Wasm doesn't have threads -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for Buffer {} -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Send for Buffer {} #[derive(Clone, Debug)] @@ -268,11 +275,18 @@ pub enum TextureInner { }, } -// SAFE: Wasm doesn't have threads -#[cfg(target_arch = "wasm32")] -unsafe impl Send for TextureInner {} -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for TextureInner {} +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] +unsafe impl Send for TextureInner {} impl TextureInner { fn as_native(&self) -> (glow::Texture, BindTarget) { @@ -462,10 +476,17 @@ struct UniformDesc { utype: u32, } -// Safe: Wasm doesn't have threads -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for UniformDesc {} -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Send for UniformDesc {} /// For each texture in the pipeline layout, store the index of the only @@ -530,21 +551,35 @@ pub struct RenderPipeline { alpha_to_coverage_enabled: bool, } -// SAFE: Wasm doesn't have threads -#[cfg(target_arch = "wasm32")] -unsafe impl Send for RenderPipeline {} -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for RenderPipeline {} +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] +unsafe impl Send for RenderPipeline {} pub struct ComputePipeline { inner: Arc, } -// SAFE: Wasm doesn't have threads -#[cfg(target_arch = "wasm32")] -unsafe impl Send for ComputePipeline {} -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for ComputePipeline {} +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] +unsafe impl Send for ComputePipeline {} #[derive(Debug)] pub struct QuerySet { @@ -558,7 +593,21 @@ pub struct Fence { pending: Vec<(crate::FenceValue, glow::Fence)>, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] unsafe impl Send for Fence {} +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] unsafe impl Sync for Fence {} impl Fence { diff --git a/wgpu-hal/src/gles/queue.rs b/wgpu-hal/src/gles/queue.rs index ec2a18d422..6eacfdd88a 100644 --- a/wgpu-hal/src/gles/queue.rs +++ b/wgpu-hal/src/gles/queue.rs @@ -1525,8 +1525,15 @@ impl crate::Queue for super::Queue { } } -// SAFE: Wasm doesn't have threads -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for super::Queue {} -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Send for super::Queue {} diff --git a/wgpu-hal/src/gles/web.rs b/wgpu-hal/src/gles/web.rs index a6f807857b..49bc5656de 100644 --- a/wgpu-hal/src/gles/web.rs +++ b/wgpu-hal/src/gles/web.rs @@ -106,8 +106,15 @@ impl Instance { } } -// SAFE: Wasm doesn't have threads +#[cfg(all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for Instance {} +#[cfg(all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Send for Instance {} impl crate::Instance for Instance { @@ -171,16 +178,23 @@ pub struct Surface { srgb_present_program: Option, } +#[cfg(all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] +unsafe impl Sync for Surface {} +#[cfg(all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] +unsafe impl Send for Surface {} + #[derive(Clone, Debug)] enum Canvas { Canvas(web_sys::HtmlCanvasElement), Offscreen(web_sys::OffscreenCanvas), } -// SAFE: Because web doesn't have threads ( yet ) -unsafe impl Sync for Surface {} -unsafe impl Send for Surface {} - #[derive(Clone, Debug)] pub struct Swapchain { pub(crate) extent: wgt::Extent3d, diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index 1758149380..910e46ca25 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -95,6 +95,7 @@ use std::{ use bitflags::bitflags; use thiserror::Error; +use wgt::{WasmNotSend, WasmNotSync}; pub const MAX_ANISOTROPY: u8 = 16; pub const MAX_BIND_GROUPS: usize = 8; @@ -161,25 +162,25 @@ pub trait Api: Clone + Sized { type Queue: Queue; type CommandEncoder: CommandEncoder; - type CommandBuffer: Send + Sync + fmt::Debug; + type CommandBuffer: WasmNotSend + WasmNotSync + fmt::Debug; - type Buffer: fmt::Debug + Send + Sync + 'static; - type Texture: fmt::Debug + Send + Sync + 'static; - type SurfaceTexture: fmt::Debug + Send + Sync + Borrow; - type TextureView: fmt::Debug + Send + Sync; - type Sampler: fmt::Debug + Send + Sync; - type QuerySet: fmt::Debug + Send + Sync; - type Fence: fmt::Debug + Send + Sync; + type Buffer: fmt::Debug + WasmNotSend + WasmNotSync + 'static; + type Texture: fmt::Debug + WasmNotSend + WasmNotSync + 'static; + type SurfaceTexture: fmt::Debug + WasmNotSend + WasmNotSync + Borrow; + type TextureView: fmt::Debug + WasmNotSend + WasmNotSync; + type Sampler: fmt::Debug + WasmNotSend + WasmNotSync; + type QuerySet: fmt::Debug + WasmNotSend + WasmNotSync; + type Fence: fmt::Debug + WasmNotSend + WasmNotSync; - type BindGroupLayout: Send + Sync; - type BindGroup: fmt::Debug + Send + Sync; - type PipelineLayout: Send + Sync; - type ShaderModule: fmt::Debug + Send + Sync; - type RenderPipeline: Send + Sync; - type ComputePipeline: Send + Sync; + type BindGroupLayout: WasmNotSend + WasmNotSync; + type BindGroup: fmt::Debug + WasmNotSend + WasmNotSync; + type PipelineLayout: WasmNotSend + WasmNotSync; + type ShaderModule: fmt::Debug + WasmNotSend + WasmNotSync; + type RenderPipeline: WasmNotSend + WasmNotSync; + type ComputePipeline: WasmNotSend + WasmNotSync; } -pub trait Instance: Sized + Send + Sync { +pub trait Instance: Sized + WasmNotSend + WasmNotSync { unsafe fn init(desc: &InstanceDescriptor) -> Result; unsafe fn create_surface( &self, @@ -190,7 +191,7 @@ pub trait Instance: Sized + Send + Sync { unsafe fn enumerate_adapters(&self) -> Vec>; } -pub trait Surface: Send + Sync { +pub trait Surface: WasmNotSend + WasmNotSync { unsafe fn configure( &mut self, device: &A::Device, @@ -216,7 +217,7 @@ pub trait Surface: Send + Sync { unsafe fn discard_texture(&mut self, texture: A::SurfaceTexture); } -pub trait Adapter: Send + Sync { +pub trait Adapter: WasmNotSend + WasmNotSync { unsafe fn open( &self, features: wgt::Features, @@ -240,7 +241,7 @@ pub trait Adapter: Send + Sync { unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp; } -pub trait Device: Send + Sync { +pub trait Device: WasmNotSend + WasmNotSync { /// Exit connection to this logical device. unsafe fn exit(self, queue: A::Queue); /// Creates a new buffer. @@ -336,7 +337,7 @@ pub trait Device: Send + Sync { unsafe fn stop_capture(&self); } -pub trait Queue: Send + Sync { +pub trait Queue: WasmNotSend + WasmNotSync { /// Submits the command buffers for execution on GPU. /// /// Valid usage: @@ -360,7 +361,7 @@ pub trait Queue: Send + Sync { /// Serves as a parent for all the encoded command buffers. /// Works in bursts of action: one or more command buffers are recorded, /// then submitted to a queue, and then it needs to be `reset_all()`. -pub trait CommandEncoder: Send + Sync + fmt::Debug { +pub trait CommandEncoder: WasmNotSend + WasmNotSync + fmt::Debug { /// Begin encoding a new command buffer. unsafe fn begin_encoding(&mut self, label: Label) -> Result<(), DeviceError>; /// Discard currently recorded list, if any. diff --git a/wgpu-types/Cargo.toml b/wgpu-types/Cargo.toml index 8a4e7e0075..6b30c02ae5 100644 --- a/wgpu-types/Cargo.toml +++ b/wgpu-types/Cargo.toml @@ -25,6 +25,7 @@ targets = [ trace = ["serde"] replay = ["serde"] strict_asserts = [] +fragile-send-sync-non-atomic-wasm = [] [dependencies] bitflags = "2" diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index ebfc9f65aa..a5e838653b 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -5917,9 +5917,17 @@ impl std::ops::Deref for ExternalImageSource { } } -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Send for ExternalImageSource {} -#[cfg(target_arch = "wasm32")] +#[cfg(all( + target_arch = "wasm32", + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for ExternalImageSource {} /// Color spaces supported on the web. @@ -6317,3 +6325,74 @@ impl Default for InstanceDescriptor { } } } + +pub use send_sync::*; + +#[doc(hidden)] +mod send_sync { + #[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + ))] + pub trait WasmNotSend: Send {} + #[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + ))] + impl WasmNotSend for T {} + #[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + )))] + pub trait WasmNotSend {} + #[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + )))] + impl WasmNotSend for T {} + + #[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + ))] + pub trait WasmNotSync: Sync {} + #[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + ))] + impl WasmNotSync for T {} + #[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + )))] + pub trait WasmNotSync {} + #[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + )))] + impl WasmNotSync for T {} +} diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 1047e7bf47..2842281a19 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -38,6 +38,8 @@ angle = ["wgc/angle"] webgl = ["hal", "wgc"] vulkan-portability = ["wgc/vulkan"] expose-ids = [] +# Implement `Send` and `Sync` on Wasm. +fragile-send-sync-non-atomic-wasm = ["hal/fragile-send-sync-non-atomic-wasm", "wgc/fragile-send-sync-non-atomic-wasm", "wgt/fragile-send-sync-non-atomic-wasm"] # wgpu-core is always available as an optional dependency, "wgc". # Whenever wgpu-core is selected, we want the GLES backend and raw diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index d9e1085935..a5c9f30511 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -23,6 +23,7 @@ use std::{ }; use wgc::command::{bundle_ffi::*, compute_ffi::*, render_ffi::*}; use wgc::id::TypedId; +use wgt::{WasmNotSend, WasmNotSync}; const LABEL: &str = "label"; @@ -263,7 +264,7 @@ impl Context { fn handle_error( &self, sink_mutex: &Mutex, - cause: impl Error + Send + Sync + 'static, + cause: impl Error + WasmNotSend + WasmNotSync + 'static, label_key: &'static str, label: Label, string: &'static str, @@ -297,7 +298,7 @@ impl Context { fn handle_error_nolabel( &self, sink_mutex: &Mutex, - cause: impl Error + Send + Sync + 'static, + cause: impl Error + WasmNotSend + WasmNotSync + 'static, string: &'static str, ) { self.handle_error(sink_mutex, cause, "", None, string) @@ -306,7 +307,7 @@ impl Context { #[track_caller] fn handle_error_fatal( &self, - cause: impl Error + Send + Sync + 'static, + cause: impl Error + WasmNotSend + WasmNotSync + 'static, operation: &'static str, ) -> ! { panic!("Error in {operation}: {f}", f = self.format_error(&cause)); @@ -1467,7 +1468,7 @@ impl crate::Context for Context { buffer_data: &Self::BufferData, mode: MapMode, range: Range, - callback: Box) + Send + 'static>, + callback: crate::context::BufferMapCallback, ) { let operation = wgc::resource::BufferMapOperation { host: match mode { @@ -2279,7 +2280,7 @@ impl crate::Context for Context { &self, queue: &Self::QueueId, _queue_data: &Self::QueueData, - callback: Box, + callback: crate::context::SubmittedWorkDoneCallback, ) { let closure = wgc::device::queue::SubmittedWorkDoneClosure::from_rust(callback); @@ -3051,7 +3052,21 @@ pub struct BufferMappedRange { size: usize, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] unsafe impl Send for BufferMappedRange {} +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] unsafe impl Sync for BufferMappedRange {} impl crate::context::BufferMappedRange for BufferMappedRange { diff --git a/wgpu/src/backend/web.rs b/wgpu/src/backend/web.rs index 239c663630..1dc5c712ab 100644 --- a/wgpu/src/backend/web.rs +++ b/wgpu/src/backend/web.rs @@ -65,7 +65,15 @@ impl From> for ObjectId { #[derive(Clone, Debug)] pub(crate) struct Sendable(T); +#[cfg(all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Send for Sendable {} +#[cfg(all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for Sendable {} #[derive(Clone, Debug)] @@ -73,11 +81,27 @@ pub(crate) struct Identified( #[cfg(feature = "expose-ids")] std::num::NonZeroU64, PhantomData, ); +#[cfg(all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Send for Identified {} +#[cfg(all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for Identified {} pub(crate) struct Context(web_sys::Gpu); +#[cfg(all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Send for Context {} +#[cfg(all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Sync for Context {} impl fmt::Debug for Context { @@ -134,6 +158,10 @@ impl MakeSendFuture { } } +#[cfg(all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") +))] unsafe impl Send for MakeSendFuture {} fn map_texture_format(texture_format: wgt::TextureFormat) -> web_sys::GpuTextureFormat { @@ -1838,7 +1866,7 @@ impl crate::context::Context for Context { buffer_data: &Self::BufferData, mode: crate::MapMode, range: Range, - callback: Box) + Send + 'static>, + callback: crate::context::BufferMapCallback, ) { let map_promise = buffer_data.0.map_async_with_f64_and_f64( map_map_mode(mode), @@ -2531,7 +2559,7 @@ impl crate::context::Context for Context { &self, _queue: &Self::QueueId, _queue_data: &Self::QueueData, - _callback: Box, + _callback: crate::context::SubmittedWorkDoneCallback, ) { unimplemented!() } diff --git a/wgpu/src/context.rs b/wgpu/src/context.rs index 6633e61b61..33e8b5a5e4 100644 --- a/wgpu/src/context.rs +++ b/wgpu/src/context.rs @@ -4,13 +4,13 @@ use wgt::{ strict_assert, strict_assert_eq, AdapterInfo, BufferAddress, BufferSize, Color, DownlevelCapabilities, DynamicOffset, Extent3d, Features, ImageDataLayout, ImageSubresourceRange, IndexFormat, Limits, ShaderStages, SurfaceStatus, TextureFormat, - TextureFormatFeatures, + TextureFormatFeatures, WasmNotSend, WasmNotSync, }; use crate::{ - BindGroupDescriptor, BindGroupLayoutDescriptor, Buffer, BufferAsyncError, BufferDescriptor, - CommandEncoderDescriptor, ComputePassDescriptor, ComputePipelineDescriptor, DeviceDescriptor, - Error, ErrorFilter, ImageCopyBuffer, ImageCopyTexture, Maintain, MapMode, + AnyWasmNotSendSync, BindGroupDescriptor, BindGroupLayoutDescriptor, Buffer, BufferAsyncError, + BufferDescriptor, CommandEncoderDescriptor, ComputePassDescriptor, ComputePipelineDescriptor, + DeviceDescriptor, Error, ErrorFilter, ImageCopyBuffer, ImageCopyTexture, Maintain, MapMode, PipelineLayoutDescriptor, QuerySetDescriptor, RenderBundleDescriptor, RenderBundleEncoderDescriptor, RenderPassDescriptor, RenderPipelineDescriptor, RequestAdapterOptions, RequestDeviceError, SamplerDescriptor, ShaderModuleDescriptor, @@ -27,59 +27,59 @@ impl + From + Debug + 'static> ContextId for T {} /// Meta trait for an data associated with an id tracked by a context. /// /// There is no need to manually implement this trait since there is a blanket implementation for this trait. -pub trait ContextData: Debug + Send + Sync + 'static {} -impl ContextData for T {} +pub trait ContextData: Debug + WasmNotSend + WasmNotSync + 'static {} +impl ContextData for T {} -pub trait Context: Debug + Send + Sized + Sync { - type AdapterId: ContextId + Send + Sync; +pub trait Context: Debug + WasmNotSend + WasmNotSync + Sized { + type AdapterId: ContextId + WasmNotSend + WasmNotSync; type AdapterData: ContextData; - type DeviceId: ContextId + Send + Sync; + type DeviceId: ContextId + WasmNotSend + WasmNotSync; type DeviceData: ContextData; - type QueueId: ContextId + Send + Sync; + type QueueId: ContextId + WasmNotSend + WasmNotSync; type QueueData: ContextData; - type ShaderModuleId: ContextId + Send + Sync; + type ShaderModuleId: ContextId + WasmNotSend + WasmNotSync; type ShaderModuleData: ContextData; - type BindGroupLayoutId: ContextId + Send + Sync; + type BindGroupLayoutId: ContextId + WasmNotSend + WasmNotSync; type BindGroupLayoutData: ContextData; - type BindGroupId: ContextId + Send + Sync; + type BindGroupId: ContextId + WasmNotSend + WasmNotSync; type BindGroupData: ContextData; - type TextureViewId: ContextId + Send + Sync; + type TextureViewId: ContextId + WasmNotSend + WasmNotSync; type TextureViewData: ContextData; - type SamplerId: ContextId + Send + Sync; + type SamplerId: ContextId + WasmNotSend + WasmNotSync; type SamplerData: ContextData; - type BufferId: ContextId + Send + Sync; + type BufferId: ContextId + WasmNotSend + WasmNotSync; type BufferData: ContextData; - type TextureId: ContextId + Send + Sync; + type TextureId: ContextId + WasmNotSend + WasmNotSync; type TextureData: ContextData; - type QuerySetId: ContextId + Send + Sync; + type QuerySetId: ContextId + WasmNotSend + WasmNotSync; type QuerySetData: ContextData; - type PipelineLayoutId: ContextId + Send + Sync; + type PipelineLayoutId: ContextId + WasmNotSend + WasmNotSync; type PipelineLayoutData: ContextData; - type RenderPipelineId: ContextId + Send + Sync; + type RenderPipelineId: ContextId + WasmNotSend + WasmNotSync; type RenderPipelineData: ContextData; - type ComputePipelineId: ContextId + Send + Sync; + type ComputePipelineId: ContextId + WasmNotSend + WasmNotSync; type ComputePipelineData: ContextData; - type CommandEncoderId: ContextId + Send + Sync; + type CommandEncoderId: ContextId + WasmNotSend + WasmNotSync; type CommandEncoderData: ContextData; type ComputePassId: ContextId; type ComputePassData: ContextData; type RenderPassId: ContextId; type RenderPassData: ContextData; - type CommandBufferId: ContextId + Send + Sync; + type CommandBufferId: ContextId + WasmNotSend + WasmNotSync; type CommandBufferData: ContextData; type RenderBundleEncoderId: ContextId; type RenderBundleEncoderData: ContextData; - type RenderBundleId: ContextId + Send + Sync; + type RenderBundleId: ContextId + WasmNotSend + WasmNotSync; type RenderBundleData: ContextData; - type SurfaceId: ContextId + Send + Sync; + type SurfaceId: ContextId + WasmNotSend + WasmNotSync; type SurfaceData: ContextData; - type SurfaceOutputDetail: Send + Sync + 'static; - type SubmissionIndex: ContextId + Clone + Copy + Send + Sync; + type SurfaceOutputDetail: WasmNotSend + WasmNotSync + 'static; + type SubmissionIndex: ContextId + Clone + Copy + WasmNotSend + WasmNotSync; type SubmissionIndexData: ContextData + Copy; type RequestAdapterFuture: Future> - + Send + + WasmNotSend + 'static; type RequestDeviceFuture: Future< Output = Result< @@ -91,9 +91,9 @@ pub trait Context: Debug + Send + Sized + Sync { ), RequestDeviceError, >, - > + Send + > + WasmNotSend + 'static; - type PopErrorScopeFuture: Future> + Send + 'static; + type PopErrorScopeFuture: Future> + WasmNotSend + 'static; fn init(instance_desc: wgt::InstanceDescriptor) -> Self; fn instance_create_surface( @@ -299,7 +299,7 @@ pub trait Context: Debug + Send + Sized + Sync { buffer_data: &Self::BufferData, mode: MapMode, range: Range, - callback: Box) + Send + 'static>, + callback: BufferMapCallback, ); fn buffer_get_mapped_range( &self, @@ -585,7 +585,7 @@ pub trait Context: Debug + Send + Sized + Sync { &self, queue: &Self::QueueId, queue_data: &Self::QueueData, - callback: Box, + callback: SubmittedWorkDoneCallback, ); fn device_start_capture(&self, device: &Self::DeviceId, device_data: &Self::DeviceData); @@ -1039,15 +1039,24 @@ impl ObjectId { } } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(ObjectId: Send, Sync); -pub(crate) fn downcast_ref(data: &crate::Data) -> &T { +pub(crate) fn downcast_ref( + data: &crate::Data, +) -> &T { strict_assert!(data.is::()); // Copied from std. unsafe { &*(data as *const dyn Any as *const T) } } -fn downcast_mut(data: &mut crate::Data) -> &mut T { +fn downcast_mut(data: &mut crate::Data) -> &mut T { strict_assert!(data.is::()); // Copied from std. unsafe { &mut *(data as *mut dyn Any as *mut T) } @@ -1079,8 +1088,97 @@ pub(crate) struct DeviceRequest { pub queue_data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] +pub type BufferMapCallback = Box) + Send + 'static>; +#[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +)))] +pub type BufferMapCallback = Box) + 'static>; + +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] +pub(crate) type AdapterRequestDeviceFuture = + Box> + Send>; +#[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +)))] +pub(crate) type AdapterRequestDeviceFuture = + Box>>; + +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] +pub type InstanceRequestAdapterFuture = + Box)>> + Send>; +#[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +)))] +pub type InstanceRequestAdapterFuture = + Box)>>>; + +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] +pub type DevicePopErrorFuture = Box> + Send>; +#[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +)))] +pub type DevicePopErrorFuture = Box>>; + +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] +pub type SubmittedWorkDoneCallback = Box; +#[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +)))] +pub type SubmittedWorkDoneCallback = Box; + /// An object safe variant of [`Context`] implemented by all types that implement [`Context`]. -pub(crate) trait DynContext: Debug + Send + Sync { +pub(crate) trait DynContext: Debug + WasmNotSend + WasmNotSync { fn as_any(&self) -> &dyn Any; fn instance_create_surface( @@ -1092,14 +1190,14 @@ pub(crate) trait DynContext: Debug + Send + Sync { fn instance_request_adapter( &self, options: &RequestAdapterOptions<'_>, - ) -> Pin)>> + Send>>; + ) -> Pin; fn adapter_request_device( &self, adapter: &ObjectId, adapter_data: &crate::Data, desc: &DeviceDescriptor, trace_dir: Option<&std::path::Path>, - ) -> Pin> + Send>>; + ) -> Pin; fn instance_poll_all_devices(&self, force_wait: bool) -> bool; fn adapter_is_surface_supported( @@ -1152,10 +1250,10 @@ pub(crate) trait DynContext: Debug + Send + Sync { Option, Option>, SurfaceStatus, - Box, + Box, ); - fn surface_present(&self, texture: &ObjectId, detail: &(dyn Any + Send + Sync)); - fn surface_texture_discard(&self, texture: &ObjectId, detail: &(dyn Any + Send + Sync)); + fn surface_present(&self, texture: &ObjectId, detail: &dyn AnyWasmNotSendSync); + fn surface_texture_discard(&self, texture: &ObjectId, detail: &dyn AnyWasmNotSendSync); fn device_features(&self, device: &ObjectId, device_data: &crate::Data) -> Features; fn device_limits(&self, device: &ObjectId, device_data: &crate::Data) -> Limits; @@ -1262,14 +1360,14 @@ pub(crate) trait DynContext: Debug + Send + Sync { &self, device: &ObjectId, device_data: &crate::Data, - ) -> Pin> + Send + 'static>>; + ) -> Pin; fn buffer_map_async( &self, buffer: &ObjectId, buffer_data: &crate::Data, mode: MapMode, range: Range, - callback: Box) + Send + 'static>, + callback: BufferMapCallback, ); fn buffer_get_mapped_range( &self, @@ -1511,7 +1609,7 @@ pub(crate) trait DynContext: Debug + Send + Sync { &self, queue: &ObjectId, queue_data: &crate::Data, - callback: Box, + callback: SubmittedWorkDoneCallback, ); fn device_start_capture(&self, device: &ObjectId, data: &crate::Data); @@ -1931,7 +2029,7 @@ where fn instance_request_adapter( &self, options: &RequestAdapterOptions<'_>, - ) -> Pin)>> + Send>> { + ) -> Pin { let future: T::RequestAdapterFuture = Context::instance_request_adapter(self, options); Box::pin(async move { let result: Option<(T::AdapterId, T::AdapterData)> = future.await; @@ -1945,7 +2043,7 @@ where adapter_data: &crate::Data, desc: &DeviceDescriptor, trace_dir: Option<&std::path::Path>, - ) -> Pin> + Send>> { + ) -> Pin { let adapter = ::from(*adapter); let adapter_data = downcast_ref(adapter_data); let future = Context::adapter_request_device(self, &adapter, adapter_data, desc, trace_dir); @@ -2064,13 +2162,13 @@ where Option, Option>, SurfaceStatus, - Box, + Box, ) { let surface = ::from(*surface); let surface_data = downcast_ref(surface_data); let (texture, texture_data, status, detail) = Context::surface_get_current_texture(self, &surface, surface_data); - let detail = Box::new(detail) as Box; + let detail = Box::new(detail) as Box; ( texture.map(Into::into), texture_data.map(|b| Box::new(b) as _), @@ -2079,12 +2177,12 @@ where ) } - fn surface_present(&self, texture: &ObjectId, detail: &(dyn Any + Send + Sync)) { + fn surface_present(&self, texture: &ObjectId, detail: &dyn AnyWasmNotSendSync) { let texture = ::from(*texture); Context::surface_present(self, &texture, detail.downcast_ref().unwrap()) } - fn surface_texture_discard(&self, texture: &ObjectId, detail: &(dyn Any + Send + Sync)) { + fn surface_texture_discard(&self, texture: &ObjectId, detail: &dyn AnyWasmNotSendSync) { let texture = ::from(*texture); Context::surface_texture_discard(self, &texture, detail.downcast_ref().unwrap()) } @@ -2325,7 +2423,7 @@ where &self, device: &ObjectId, device_data: &crate::Data, - ) -> Pin> + Send + 'static>> { + ) -> Pin { let device = ::from(*device); let device_data = downcast_ref(device_data); Box::pin(Context::device_pop_error_scope(self, &device, device_data)) @@ -2337,7 +2435,7 @@ where buffer_data: &crate::Data, mode: MapMode, range: Range, - callback: Box) + Send + 'static>, + callback: BufferMapCallback, ) { let buffer = ::from(*buffer); let buffer_data = downcast_ref(buffer_data); @@ -2928,7 +3026,7 @@ where &self, queue: &ObjectId, queue_data: &crate::Data, - callback: Box, + callback: SubmittedWorkDoneCallback, ) { let queue = ::from(*queue); let queue_data = downcast_ref(queue_data); @@ -3861,7 +3959,7 @@ where } } -pub trait QueueWriteBuffer: Send + Sync { +pub trait QueueWriteBuffer: WasmNotSend + WasmNotSync { fn slice(&self) -> &[u8]; fn slice_mut(&mut self) -> &mut [u8]; diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index a9a45ffaa1..2ab86419a1 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -43,8 +43,9 @@ pub use wgt::{ StorageTextureAccess, SurfaceCapabilities, SurfaceStatus, TextureAspect, TextureDimension, TextureFormat, TextureFormatFeatureFlags, TextureFormatFeatures, TextureSampleType, TextureUsages, TextureViewDimension, VertexAttribute, VertexFormat, VertexStepMode, - COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT, PUSH_CONSTANT_ALIGNMENT, - QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES, QUERY_SIZE, VERTEX_STRIDE_ALIGNMENT, + WasmNotSend, WasmNotSync, COPY_BUFFER_ALIGNMENT, COPY_BYTES_PER_ROW_ALIGNMENT, MAP_ALIGNMENT, + PUSH_CONSTANT_ALIGNMENT, QUERY_RESOLVE_BUFFER_ALIGNMENT, QUERY_SET_MAX_QUERIES, QUERY_SIZE, + VERTEX_STRIDE_ALIGNMENT, }; #[cfg(any( @@ -66,8 +67,6 @@ pub use ::wgc as core; // specific, but these need to depend on web-sys. #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] pub use wgt::{ExternalImageSource, ImageCopyExternalImage}; -#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] -static_assertions::assert_impl_all!(ExternalImageSource: Send, Sync); /// Filter for error scopes. #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)] @@ -80,7 +79,22 @@ pub enum ErrorFilter { static_assertions::assert_impl_all!(ErrorFilter: Send, Sync); type C = dyn DynContext; +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] type Data = dyn Any + Send + Sync; +#[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +)))] +type Data = dyn Any; /// Context for all other wgpu objects. Instance of wgpu. /// @@ -94,6 +108,13 @@ type Data = dyn Any + Send + Sync; pub struct Instance { context: Arc, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(Instance: Send, Sync); /// Handle to a physical graphics and/or compute device. @@ -110,6 +131,13 @@ pub struct Adapter { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(Adapter: Send, Sync); impl Drop for Adapter { @@ -134,6 +162,13 @@ pub struct Device { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(Device: Send, Sync); /// Identifier for a particular call to [`Queue::submit`]. Can be used @@ -144,6 +179,13 @@ static_assertions::assert_impl_all!(Device: Send, Sync); /// There is no analogue in the WebGPU specification. #[derive(Debug, Clone)] pub struct SubmissionIndex(ObjectId, Arc); +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(SubmissionIndex: Send, Sync); /// The main purpose of this struct is to resolve mapped ranges (convert sizes @@ -220,6 +262,13 @@ pub struct Buffer { usage: BufferUsages, // Todo: missing map_state https://www.w3.org/TR/webgpu/#dom-gpubuffer-mapstate } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(Buffer: Send, Sync); /// Slice into a [`Buffer`]. @@ -236,6 +285,13 @@ pub struct BufferSlice<'a> { offset: BufferAddress, size: Option, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(BufferSlice: Send, Sync); /// Handle to a texture on the GPU. @@ -251,6 +307,13 @@ pub struct Texture { owned: bool, descriptor: TextureDescriptor<'static>, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(Texture: Send, Sync); /// Handle to a texture view. @@ -265,6 +328,13 @@ pub struct TextureView { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(TextureView: Send, Sync); /// Handle to a sampler. @@ -282,6 +352,13 @@ pub struct Sampler { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(Sampler: Send, Sync); impl Drop for Sampler { @@ -322,6 +399,13 @@ pub struct Surface { // been created is is additionally wrapped in an option. config: Mutex>, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(Surface: Send, Sync); impl Drop for Surface { @@ -349,6 +433,13 @@ pub struct BindGroupLayout { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(BindGroupLayout: Send, Sync); impl Drop for BindGroupLayout { @@ -374,6 +465,13 @@ pub struct BindGroup { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(BindGroup: Send, Sync); impl Drop for BindGroup { @@ -398,6 +496,13 @@ pub struct ShaderModule { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(ShaderModule: Send, Sync); impl Drop for ShaderModule { @@ -490,6 +595,13 @@ pub struct PipelineLayout { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(PipelineLayout: Send, Sync); impl Drop for PipelineLayout { @@ -513,6 +625,13 @@ pub struct RenderPipeline { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(RenderPipeline: Send, Sync); impl Drop for RenderPipeline { @@ -547,6 +666,13 @@ pub struct ComputePipeline { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(ComputePipeline: Send, Sync); impl Drop for ComputePipeline { @@ -584,6 +710,13 @@ pub struct CommandBuffer { id: Option, data: Option>, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(CommandBuffer: Send, Sync); impl Drop for CommandBuffer { @@ -612,6 +745,13 @@ pub struct CommandEncoder { id: Option, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(CommandEncoder: Send, Sync); impl Drop for CommandEncoder { @@ -688,6 +828,13 @@ pub struct RenderBundle { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(RenderBundle: Send, Sync); impl Drop for RenderBundle { @@ -709,6 +856,13 @@ pub struct QuerySet { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(QuerySet: Send, Sync); impl Drop for QuerySet { @@ -732,6 +886,13 @@ pub struct Queue { id: ObjectId, data: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(Queue: Send, Sync); /// Resource that can be bound to a pipeline. @@ -777,6 +938,13 @@ pub enum BindingResource<'a> { /// [`BindGroupLayoutEntry::count`] set to Some. TextureViewArray(&'a [&'a TextureView]), } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(BindingResource: Send, Sync); /// Describes the segment of a buffer to bind. @@ -808,6 +976,13 @@ pub struct BufferBinding<'a> { /// Size of the binding in bytes, or `None` for using the rest of the buffer. pub size: Option, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(BufferBinding: Send, Sync); /// Operation to perform to the output attachment at the start of a render pass. @@ -869,6 +1044,13 @@ pub struct RenderPassColorAttachment<'tex> { /// What operations will be performed on this color attachment. pub ops: Operations, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(RenderPassColorAttachment: Send, Sync); /// Describes a depth/stencil attachment to a [`RenderPass`]. @@ -886,6 +1068,13 @@ pub struct RenderPassDepthStencilAttachment<'tex> { /// What operations will be performed on the stencil part of the attachment. pub stencil_ops: Option>, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(RenderPassDepthStencilAttachment: Send, Sync); // The underlying types are also exported so that documentation shows up for them @@ -900,6 +1089,13 @@ pub use wgt::RequestAdapterOptions as RequestAdapterOptionsBase; /// Corresponds to [WebGPU `GPURequestAdapterOptions`]( /// https://gpuweb.github.io/gpuweb/#dictdef-gpurequestadapteroptions). pub type RequestAdapterOptions<'a> = RequestAdapterOptionsBase<&'a Surface>; +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(RequestAdapterOptions: Send, Sync); /// Describes a [`Device`]. /// @@ -952,6 +1148,13 @@ static_assertions::assert_impl_all!(QuerySetDescriptor: Send, Sync); pub use wgt::Maintain as MaintainBase; /// Passed to [`Device::poll`] to control how and if it should block. pub type Maintain = wgt::Maintain; +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(Maintain: Send, Sync); /// Describes a [`TextureView`]. @@ -1007,6 +1210,13 @@ pub struct PipelineLayoutDescriptor<'a> { /// If this array is non-empty, the [`Features::PUSH_CONSTANTS`] must be enabled. pub push_constant_ranges: &'a [PushConstantRange], } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(PipelineLayoutDescriptor: Send, Sync); /// Describes a [`Sampler`]. @@ -1076,6 +1286,13 @@ pub struct BindGroupEntry<'a> { /// Resource to attach to the binding pub resource: BindingResource<'a>, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(BindGroupEntry: Send, Sync); /// Describes a group of bindings and the resources to be bound. @@ -1093,6 +1310,13 @@ pub struct BindGroupDescriptor<'a> { /// The resources to bind to this bind group. pub entries: &'a [BindGroupEntry<'a>], } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(BindGroupDescriptor: Send, Sync); /// Describes the attachments of a render pass. @@ -1113,6 +1337,13 @@ pub struct RenderPassDescriptor<'tex, 'desc> { /// The depth and stencil attachment of the render pass, if any. pub depth_stencil_attachment: Option>, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(RenderPassDescriptor: Send, Sync); /// Describes how the vertex buffer is interpreted. @@ -1148,6 +1379,13 @@ pub struct VertexState<'a> { /// The format of any vertex buffers used with this pipeline. pub buffers: &'a [VertexBufferLayout<'a>], } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(VertexState: Send, Sync); /// Describes the fragment processing in a render pipeline. @@ -1166,6 +1404,13 @@ pub struct FragmentState<'a> { /// The color state of the render targets. pub targets: &'a [Option], } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(FragmentState: Send, Sync); /// Describes a render (graphics) pipeline. @@ -1194,6 +1439,13 @@ pub struct RenderPipelineDescriptor<'a> { /// layers the attachments will have. pub multiview: Option, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(RenderPipelineDescriptor: Send, Sync); /// Describes the attachments of a compute pass. @@ -1227,6 +1479,13 @@ pub struct ComputePipelineDescriptor<'a> { /// and no return value in the shader. pub entry_point: &'a str, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(ComputePipelineDescriptor: Send, Sync); pub use wgt::ImageCopyBuffer as ImageCopyBufferBase; @@ -1235,6 +1494,13 @@ pub use wgt::ImageCopyBuffer as ImageCopyBufferBase; /// Corresponds to [WebGPU `GPUImageCopyBuffer`]( /// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopybuffer). pub type ImageCopyBuffer<'a> = ImageCopyBufferBase<&'a Buffer>; +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(ImageCopyBuffer: Send, Sync); pub use wgt::ImageCopyTexture as ImageCopyTextureBase; @@ -1243,6 +1509,13 @@ pub use wgt::ImageCopyTexture as ImageCopyTextureBase; /// Corresponds to [WebGPU `GPUImageCopyTexture`]( /// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopytexture). pub type ImageCopyTexture<'a> = ImageCopyTextureBase<&'a Texture>; +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(ImageCopyTexture: Send, Sync); pub use wgt::ImageCopyTextureTagged as ImageCopyTextureTaggedBase; @@ -1252,6 +1525,13 @@ pub use wgt::ImageCopyTextureTagged as ImageCopyTextureTaggedBase; /// Corresponds to [WebGPU `GPUImageCopyTextureTagged`]( /// https://gpuweb.github.io/gpuweb/#dictdef-gpuimagecopytexturetagged). pub type ImageCopyTextureTagged<'a> = ImageCopyTextureTaggedBase<&'a Texture>; +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(ImageCopyTexture: Send, Sync); /// Describes a [`BindGroupLayout`]. @@ -1308,8 +1588,15 @@ pub struct SurfaceTexture { /// but should be recreated for maximum performance. pub suboptimal: bool, presented: bool, - detail: Box, + detail: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(SurfaceTexture: Send, Sync); /// Result of an unsuccessful call to [`Surface::get_current_texture`]. @@ -1463,7 +1750,7 @@ impl Instance { pub fn request_adapter( &self, options: &RequestAdapterOptions, - ) -> impl Future> + Send { + ) -> impl Future> + WasmNotSend { let context = Arc::clone(&self.context); let adapter = self.context.instance_request_adapter(options); async move { @@ -1743,7 +2030,7 @@ impl Adapter { &self, desc: &DeviceDescriptor, trace_path: Option<&std::path::Path>, - ) -> impl Future> + Send { + ) -> impl Future> + WasmNotSend { let context = Arc::clone(&self.context); let device = DynContext::adapter_request_device( &*self.context, @@ -2257,7 +2544,7 @@ impl Device { } /// Pop an error scope. - pub fn pop_error_scope(&self) -> impl Future> + Send { + pub fn pop_error_scope(&self) -> impl Future> + WasmNotSend { self.context .device_pop_error_scope(&self.id, self.data.as_ref()) } @@ -2562,7 +2849,7 @@ impl<'a> BufferSlice<'a> { pub fn map_async( &self, mode: MapMode, - callback: impl FnOnce(Result<(), BufferAsyncError>) + Send + 'static, + callback: impl FnOnce(Result<(), BufferAsyncError>) + WasmNotSend + 'static, ) { let mut mc = self.buffer.map_context.lock(); assert_eq!( @@ -3970,6 +4257,13 @@ pub struct QueueWriteBufferView<'a> { offset: BufferAddress, inner: Box, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(QueueWriteBufferView: Send, Sync); impl Deref for QueueWriteBufferView<'_> { @@ -4609,16 +4903,55 @@ pub enum Error { /// Out of memory error OutOfMemory { /// Lower level source of the error. + #[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + ))] source: Box, + /// Lower level source of the error. + #[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + )))] + source: Box, }, /// Validation error, signifying a bug in code or data Validation { /// Lower level source of the error. + #[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + ))] source: Box, + /// Lower level source of the error. + #[cfg(not(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) + )))] + source: Box, /// Description of the validation error. description: String, }, } +#[cfg(any( + not(target_arch = "wasm32"), + all( + feature = "fragile-send-sync-non-atomic-wasm", + not(target_feature = "atomics") + ) +))] static_assertions::assert_impl_all!(Error: Send); impl error::Error for Error { @@ -4638,3 +4971,35 @@ impl Display for Error { } } } + +use send_sync::*; + +mod send_sync { + use std::any::Any; + use std::fmt; + + use wgt::{WasmNotSend, WasmNotSync}; + + pub trait AnyWasmNotSendSync: Any + WasmNotSend + WasmNotSync { + fn upcast_any_ref(&self) -> &dyn Any; + } + impl AnyWasmNotSendSync for T { + #[inline] + fn upcast_any_ref(&self) -> &dyn Any { + self + } + } + + impl dyn AnyWasmNotSendSync + 'static { + #[inline] + pub fn downcast_ref(&self) -> Option<&T> { + self.upcast_any_ref().downcast_ref::() + } + } + + impl fmt::Debug for dyn AnyWasmNotSendSync { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Any").finish_non_exhaustive() + } + } +}