Skip to content

refactor(render): move WgpuWrapper into bevy_utils #19303

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

Merged
merged 2 commits into from
May 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions crates/bevy_render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ bevy_render_macros = { path = "macros", version = "0.16.0-dev" }
bevy_time = { path = "../bevy_time", version = "0.16.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.16.0-dev" }
bevy_window = { path = "../bevy_window", version = "0.16.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-dev", features = [
"wgpu_wrapper",
] }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-dev" }
bevy_image = { path = "../bevy_image", version = "0.16.0-dev" }
bevy_mesh = { path = "../bevy_mesh", version = "0.16.0-dev" }
Expand Down Expand Up @@ -151,9 +153,6 @@ bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", default-featu
"web",
] }

[target.'cfg(all(target_arch = "wasm32", target_feature = "atomics"))'.dependencies]
send_wrapper = "0.6.0"

[lints]
workspace = true

Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_render/src/diagnostic/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ use wgpu::{
PipelineStatisticsTypes, QuerySet, QuerySetDescriptor, QueryType, RenderPass,
};

use crate::renderer::{RenderAdapterInfo, RenderDevice, RenderQueue, WgpuWrapper};
use crate::renderer::{RenderAdapterInfo, RenderDevice, RenderQueue};
use bevy_utils::WgpuWrapper;

use super::RecordDiagnostics;

Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ use crate::{
mesh::{MeshPlugin, MorphPlugin, RenderMesh},
render_asset::prepare_assets,
render_resource::{PipelineCache, Shader, ShaderLoader},
renderer::{render_system, RenderInstance, WgpuWrapper},
renderer::{render_system, RenderInstance},
settings::RenderCreation,
storage::StoragePlugin,
view::{ViewPlugin, WindowRenderPlugin},
Expand All @@ -112,6 +112,7 @@ use alloc::sync::Arc;
use bevy_app::{App, AppLabel, Plugin, SubApp};
use bevy_asset::{AssetApp, AssetServer};
use bevy_ecs::{prelude::*, schedule::ScheduleLabel};
use bevy_utils::WgpuWrapper;
use bitflags::bitflags;
use core::ops::{Deref, DerefMut};
use std::sync::Mutex;
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/src/render_resource/bind_group.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::renderer::WgpuWrapper;
use crate::{
define_atomic_id,
render_asset::RenderAssets,
Expand All @@ -9,6 +8,7 @@ use crate::{
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::system::{SystemParam, SystemParamItem};
pub use bevy_render_macros::AsBindGroup;
use bevy_utils::WgpuWrapper;
use core::ops::Deref;
use encase::ShaderType;
use thiserror::Error;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::define_atomic_id;
use crate::renderer::WgpuWrapper;
use bevy_utils::WgpuWrapper;
use core::ops::Deref;

define_atomic_id!(BindGroupLayoutId);
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/src/render_resource/buffer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::define_atomic_id;
use crate::renderer::WgpuWrapper;
use bevy_utils::WgpuWrapper;
use core::ops::{Bound, Deref, RangeBounds};

define_atomic_id!(BufferId);
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/src/render_resource/pipeline.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use super::ShaderDefVal;
use crate::mesh::VertexBufferLayout;
use crate::renderer::WgpuWrapper;
use crate::{
define_atomic_id,
render_resource::{BindGroupLayout, Shader},
};
use alloc::borrow::Cow;
use bevy_asset::Handle;
use bevy_utils::WgpuWrapper;
use core::ops::Deref;
use wgpu::{
ColorTargetState, DepthStencilState, MultisampleState, PrimitiveState, PushConstantRange,
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/src/render_resource/pipeline_cache.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::renderer::WgpuWrapper;
use crate::{
render_resource::*,
renderer::{RenderAdapter, RenderDevice},
Expand All @@ -14,6 +13,7 @@ use bevy_ecs::{
use bevy_platform::collections::{hash_map::EntryRef, HashMap, HashSet};
use bevy_tasks::Task;
use bevy_utils::default;
use bevy_utils::WgpuWrapper;
use core::{future::Future, hash::Hash, mem, ops::Deref};
use naga::valid::Capabilities;
use std::sync::{Mutex, PoisonError};
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/src/render_resource/texture.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::define_atomic_id;
use crate::renderer::WgpuWrapper;
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::resource::Resource;
use bevy_utils::WgpuWrapper;
use core::ops::Deref;

define_atomic_id!(TextureId);
Expand Down
41 changes: 1 addition & 40 deletions crates/bevy_render/src/renderer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod render_device;
use bevy_derive::{Deref, DerefMut};
#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
use bevy_tasks::ComputeTaskPool;
use bevy_utils::WgpuWrapper;
pub use graph_runner::*;
pub use render_device::*;
use tracing::{error, info, info_span, warn};
Expand Down Expand Up @@ -120,46 +121,6 @@ pub fn render_system(world: &mut World, state: &mut SystemState<Query<Entity, Wi
}
}

/// A wrapper to safely make `wgpu` types Send / Sync on web with atomics enabled.
///
/// On web with `atomics` enabled the inner value can only be accessed
/// or dropped on the `wgpu` thread or else a panic will occur.
/// On other platforms the wrapper simply contains the wrapped value.
#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
#[derive(Debug, Clone, Deref, DerefMut)]
pub struct WgpuWrapper<T>(T);
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
#[derive(Debug, Clone, Deref, DerefMut)]
pub struct WgpuWrapper<T>(send_wrapper::SendWrapper<T>);

// SAFETY: SendWrapper is always Send + Sync.
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
unsafe impl<T> Send for WgpuWrapper<T> {}
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
unsafe impl<T> Sync for WgpuWrapper<T> {}

#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
impl<T> WgpuWrapper<T> {
pub fn new(t: T) -> Self {
Self(t)
}

pub fn into_inner(self) -> T {
self.0
}
}

#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
impl<T> WgpuWrapper<T> {
pub fn new(t: T) -> Self {
Self(send_wrapper::SendWrapper::new(t))
}

pub fn into_inner(self) -> T {
self.0.take()
}
}

/// This queue is used to enqueue tasks for the GPU to execute asynchronously.
#[derive(Resource, Clone, Deref, DerefMut)]
pub struct RenderQueue(pub Arc<WgpuWrapper<Queue>>);
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/src/renderer/render_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::render_resource::{
BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor,
RenderPipeline, Sampler, Texture,
};
use crate::WgpuWrapper;
use bevy_ecs::resource::Resource;
use bevy_utils::WgpuWrapper;
use wgpu::{
util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor,
BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, MaintainResult,
Expand Down
3 changes: 2 additions & 1 deletion crates/bevy_render/src/view/window/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use crate::{
render_resource::{SurfaceTexture, TextureView},
renderer::{RenderAdapter, RenderDevice, RenderInstance},
Extract, ExtractSchedule, Render, RenderApp, RenderSystems, WgpuWrapper,
Extract, ExtractSchedule, Render, RenderApp, RenderSystems,
};
use bevy_app::{App, Plugin};
use bevy_ecs::{entity::EntityHashMap, prelude::*};
use bevy_platform::collections::HashSet;
use bevy_utils::default;
use bevy_utils::WgpuWrapper;
use bevy_window::{
CompositeAlphaMode, PresentMode, PrimaryWindow, RawHandleWrapper, Window, WindowClosing,
};
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ keywords = ["bevy"]
[features]
default = ["parallel"]

wgpu_wrapper = ["dep:send_wrapper"]

# Provides access to the `Parallel` type.
parallel = ["bevy_platform/std", "dep:thread_local"]

Expand All @@ -19,6 +21,9 @@ bevy_platform = { path = "../bevy_platform", version = "0.16.0-dev", default-fea

thread_local = { version = "1.0", optional = true }

[target.'cfg(all(target_arch = "wasm32", target_feature = "atomics"))'.dependencies]
send_wrapper = { version = "0.6.0", optional = true }

[dev-dependencies]
static_assertions = "1.1.0"

Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ pub mod prelude {

pub mod synccell;
pub mod syncunsafecell;
#[cfg(feature = "wgpu_wrapper")]
mod wgpu_wrapper;

mod default;
mod once;
Expand All @@ -57,6 +59,9 @@ pub use once::OnceFlag;

pub use default::default;

#[cfg(feature = "wgpu_wrapper")]
pub use wgpu_wrapper::WgpuWrapper;

use core::mem::ManuallyDrop;

/// A type which calls a function when dropped.
Expand Down
50 changes: 50 additions & 0 deletions crates/bevy_utils/src/wgpu_wrapper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/// A wrapper to safely make `wgpu` types Send / Sync on web with atomics enabled.
///
/// On web with `atomics` enabled the inner value can only be accessed
/// or dropped on the `wgpu` thread or else a panic will occur.
/// On other platforms the wrapper simply contains the wrapped value.
#[derive(Debug, Clone)]
pub struct WgpuWrapper<T>(
#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))] T,
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] send_wrapper::SendWrapper<T>,
);

// SAFETY: SendWrapper is always Send + Sync.
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
#[expect(unsafe_code, reason = "Blanket-impl Send requires unsafe.")]
unsafe impl<T> Send for WgpuWrapper<T> {}
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
#[expect(unsafe_code, reason = "Blanket-impl Sync requires unsafe.")]
unsafe impl<T> Sync for WgpuWrapper<T> {}

impl<T> WgpuWrapper<T> {
/// Constructs a new instance of `WgpuWrapper` which will wrap the specified value.
pub fn new(t: T) -> Self {
#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
return Self(t);
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
return Self(send_wrapper::SendWrapper::new(t));
}

/// Unwraps the value.
pub fn into_inner(self) -> T {
#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))]
return self.0;
#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))]
return self.0.take();
}
}

impl<T> core::ops::Deref for WgpuWrapper<T> {
type Target = T;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl<T> core::ops::DerefMut for WgpuWrapper<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}