From 1732c2253bbefd55cc414dcc1de9ffafd1bf91f5 Mon Sep 17 00:00:00 2001 From: atlv Date: Mon, 26 May 2025 23:43:49 -0400 Subject: [PATCH] refactor(render): move WgpuWrapper into bevy_utils (#19303) # Objective - A step towards splitting out bevy_camera from bevy_render ## Solution - Move a shim type into bevy_utils to avoid a dependency cycle - Manually expand Deref/DerefMut to avoid having a bevy_derive dependency so early in the dep tree ## Testing - It compiles --- crates/bevy_render/Cargo.toml | 7 ++- crates/bevy_render/src/diagnostic/internal.rs | 3 +- crates/bevy_render/src/lib.rs | 3 +- .../src/render_resource/bind_group.rs | 2 +- .../src/render_resource/bind_group_layout.rs | 2 +- .../bevy_render/src/render_resource/buffer.rs | 2 +- .../src/render_resource/pipeline.rs | 2 +- .../src/render_resource/pipeline_cache.rs | 2 +- .../src/render_resource/texture.rs | 2 +- crates/bevy_render/src/renderer/mod.rs | 41 +-------------- .../bevy_render/src/renderer/render_device.rs | 2 +- crates/bevy_render/src/view/window/mod.rs | 3 +- crates/bevy_utils/Cargo.toml | 5 ++ crates/bevy_utils/src/lib.rs | 5 ++ crates/bevy_utils/src/wgpu_wrapper.rs | 50 +++++++++++++++++++ 15 files changed, 77 insertions(+), 54 deletions(-) create mode 100644 crates/bevy_utils/src/wgpu_wrapper.rs diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index aa6b6e239c..01f1e59861 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -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" } @@ -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 diff --git a/crates/bevy_render/src/diagnostic/internal.rs b/crates/bevy_render/src/diagnostic/internal.rs index ec226c760b..e7005f70f3 100644 --- a/crates/bevy_render/src/diagnostic/internal.rs +++ b/crates/bevy_render/src/diagnostic/internal.rs @@ -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; diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 3f1acf5b39..d520990f93 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -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}, @@ -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; diff --git a/crates/bevy_render/src/render_resource/bind_group.rs b/crates/bevy_render/src/render_resource/bind_group.rs index 2c8e984bfd..cff88bd355 100644 --- a/crates/bevy_render/src/render_resource/bind_group.rs +++ b/crates/bevy_render/src/render_resource/bind_group.rs @@ -1,4 +1,3 @@ -use crate::renderer::WgpuWrapper; use crate::{ define_atomic_id, render_asset::RenderAssets, @@ -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; diff --git a/crates/bevy_render/src/render_resource/bind_group_layout.rs b/crates/bevy_render/src/render_resource/bind_group_layout.rs index e19f5b969f..2d674f46d1 100644 --- a/crates/bevy_render/src/render_resource/bind_group_layout.rs +++ b/crates/bevy_render/src/render_resource/bind_group_layout.rs @@ -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); diff --git a/crates/bevy_render/src/render_resource/buffer.rs b/crates/bevy_render/src/render_resource/buffer.rs index 9b7bb2c41f..b779417bf5 100644 --- a/crates/bevy_render/src/render_resource/buffer.rs +++ b/crates/bevy_render/src/render_resource/buffer.rs @@ -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); diff --git a/crates/bevy_render/src/render_resource/pipeline.rs b/crates/bevy_render/src/render_resource/pipeline.rs index 30f9a974b8..b76174cac3 100644 --- a/crates/bevy_render/src/render_resource/pipeline.rs +++ b/crates/bevy_render/src/render_resource/pipeline.rs @@ -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, diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index 7c54b0f406..416a83cd65 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -1,4 +1,3 @@ -use crate::renderer::WgpuWrapper; use crate::{ render_resource::*, renderer::{RenderAdapter, RenderDevice}, @@ -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}; diff --git a/crates/bevy_render/src/render_resource/texture.rs b/crates/bevy_render/src/render_resource/texture.rs index f975fc18f3..c96da8a1be 100644 --- a/crates/bevy_render/src/render_resource/texture.rs +++ b/crates/bevy_render/src/render_resource/texture.rs @@ -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); diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 1691911c2c..81c693b01f 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -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}; @@ -120,46 +121,6 @@ pub fn render_system(world: &mut World, state: &mut SystemState(T); -#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] -#[derive(Debug, Clone, Deref, DerefMut)] -pub struct WgpuWrapper(send_wrapper::SendWrapper); - -// SAFETY: SendWrapper is always Send + Sync. -#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] -unsafe impl Send for WgpuWrapper {} -#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] -unsafe impl Sync for WgpuWrapper {} - -#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))] -impl WgpuWrapper { - 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 WgpuWrapper { - 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>); diff --git a/crates/bevy_render/src/renderer/render_device.rs b/crates/bevy_render/src/renderer/render_device.rs index d33139745b..31f47e5740 100644 --- a/crates/bevy_render/src/renderer/render_device.rs +++ b/crates/bevy_render/src/renderer/render_device.rs @@ -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, diff --git a/crates/bevy_render/src/view/window/mod.rs b/crates/bevy_render/src/view/window/mod.rs index 4c8d86d040..657106d5a0 100644 --- a/crates/bevy_render/src/view/window/mod.rs +++ b/crates/bevy_render/src/view/window/mod.rs @@ -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, }; diff --git a/crates/bevy_utils/Cargo.toml b/crates/bevy_utils/Cargo.toml index 5995f58bc4..ad3e1ae9c7 100644 --- a/crates/bevy_utils/Cargo.toml +++ b/crates/bevy_utils/Cargo.toml @@ -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"] @@ -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" diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index ea50306391..88c4b4e157 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -48,6 +48,8 @@ pub mod prelude { pub mod synccell; pub mod syncunsafecell; +#[cfg(feature = "wgpu_wrapper")] +mod wgpu_wrapper; mod default; mod once; @@ -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. diff --git a/crates/bevy_utils/src/wgpu_wrapper.rs b/crates/bevy_utils/src/wgpu_wrapper.rs new file mode 100644 index 0000000000..272d0dd4c0 --- /dev/null +++ b/crates/bevy_utils/src/wgpu_wrapper.rs @@ -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( + #[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))] T, + #[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] send_wrapper::SendWrapper, +); + +// 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 Send for WgpuWrapper {} +#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] +#[expect(unsafe_code, reason = "Blanket-impl Sync requires unsafe.")] +unsafe impl Sync for WgpuWrapper {} + +impl WgpuWrapper { + /// 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 core::ops::Deref for WgpuWrapper { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl core::ops::DerefMut for WgpuWrapper { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +}