diff --git a/_release-content/migration-guides/wgpu_wrapper.md b/_release-content/migration-guides/wgpu_wrapper.md new file mode 100644 index 0000000000000..063bdc65cf26e --- /dev/null +++ b/_release-content/migration-guides/wgpu_wrapper.md @@ -0,0 +1,8 @@ +--- +title: WgpuWrapper has been removed +pull_requests: [25512] +--- + +`WgpuWrapper` has been removed, and its uses have been replaced with either new types (e.g. `WgpuErrorSource`) or removed from the public API (e.g. `RenderQueue`). + +For `RenderQueue`, `RenderAdapter`, `RenderInstance` and `RenderAdapterInfo` in particular their only field holding a `WgpuWrapper` is no longer public. However they still `Deref`/`DerefMut` to their `wgpu` type, and if you were constructing/deconstructing them directly you can instead call `new` and `into_inner` to do so. diff --git a/crates/bevy_pbr/src/light_probe/mod.rs b/crates/bevy_pbr/src/light_probe/mod.rs index f764073ec035a..4ff6f1f2086c2 100644 --- a/crates/bevy_pbr/src/light_probe/mod.rs +++ b/crates/bevy_pbr/src/light_probe/mod.rs @@ -22,7 +22,7 @@ use bevy_platform::collections::HashMap; use bevy_render::{ render_asset::RenderAssets, render_resource::{DynamicUniformBuffer, Sampler, ShaderType, TextureView}, - renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue, WgpuWrapper}, + renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue}, settings::WgpuFeatures, sync_world::{MainEntity, MainEntityHashMap, RenderEntity}, texture::{FallbackImage, GpuImage}, @@ -776,7 +776,7 @@ pub(crate) fn binding_arrays_are_usable( render_device: &RenderDevice, render_adapter: &RenderAdapter, ) -> bool { - let adapter_info = RenderAdapterInfo(WgpuWrapper::new(render_adapter.get_info())); + let adapter_info = RenderAdapterInfo::new(render_adapter.get_info()); bevy_render::get_adreno_model(&adapter_info).is_none_or(|model| model > 610) && render_device diff --git a/crates/bevy_render/src/batching/gpu_preprocessing.rs b/crates/bevy_render/src/batching/gpu_preprocessing.rs index 967565312be80..8bfa1b054f9d8 100644 --- a/crates/bevy_render/src/batching/gpu_preprocessing.rs +++ b/crates/bevy_render/src/batching/gpu_preprocessing.rs @@ -45,7 +45,7 @@ use crate::{ PartialBufferVec, PipelineCache, RawBufferVec, SparseBufferUpdateBindGroups, SparseBufferUpdateJobs, SparseBufferUpdatePipelines, UninitBufferVec, }, - renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue, WgpuWrapper}, + renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue}, sync_world::{MainEntity, MainEntityHashMap}, view::{ExtractedView, NoIndirectDrawing, RetainedViewEntity}, GpuResourceAppExt, Render, RenderApp, RenderDebugFlags, RenderSystems, @@ -1578,7 +1578,7 @@ impl FromWorld for GpuPreprocessingSupport { .flags .contains(DownlevelFlags::COMPUTE_SHADERS); - let adapter_info = RenderAdapterInfo(WgpuWrapper::new(adapter.get_info())); + let adapter_info = RenderAdapterInfo::new(adapter.get_info()); let max_supported_mode = if device.limits().max_compute_workgroup_size_x == 0 || is_non_supported_android_device(&adapter_info) diff --git a/crates/bevy_render/src/diagnostic/internal.rs b/crates/bevy_render/src/diagnostic/internal.rs index a753e1aada3aa..10fbcbe12069e 100644 --- a/crates/bevy_render/src/diagnostic/internal.rs +++ b/crates/bevy_render/src/diagnostic/internal.rs @@ -16,7 +16,7 @@ use wgpu::{ RenderPass, }; -use crate::renderer::{RenderAdapterInfo, RenderDevice, RenderQueue, WgpuWrapper}; +use crate::renderer::{wgpu_wrapper, RenderAdapterInfo, RenderDevice, RenderQueue}; use super::RecordDiagnostics; @@ -37,10 +37,12 @@ struct DiagnosticsRecorderInternal { tracy_gpu_context: Option, } +wgpu_wrapper!(struct WgpuDiagnosticsRecorderInternal(DiagnosticsRecorderInternal)); + /// Records diagnostics into [`QuerySet`]'s keeping track of the mapping between /// spans and indices to the corresponding entries in the [`QuerySet`]. #[derive(Resource)] -pub struct DiagnosticsRecorder(WgpuWrapper); +pub struct DiagnosticsRecorder(WgpuDiagnosticsRecorderInternal); impl DiagnosticsRecorder { /// Creates the new `DiagnosticsRecorder`. @@ -56,20 +58,22 @@ impl DiagnosticsRecorder { super::tracy_gpu::new_tracy_gpu_context(adapter_info, device, queue); let _ = adapter_info; // Prevent unused variable warnings when tracing-tracy is not enabled - DiagnosticsRecorder(WgpuWrapper::new(DiagnosticsRecorderInternal { - timestamp_period_ns: queue.get_timestamp_period(), - features, - current_frame: Mutex::new(FrameData::new( - device, + DiagnosticsRecorder(WgpuDiagnosticsRecorderInternal::new( + DiagnosticsRecorderInternal { + timestamp_period_ns: queue.get_timestamp_period(), features, + current_frame: Mutex::new(FrameData::new( + device, + features, + #[cfg(feature = "tracing-tracy")] + tracy_gpu_context.clone(), + )), + submitted_frames: Vec::new(), + finished_frames: Vec::new(), #[cfg(feature = "tracing-tracy")] - tracy_gpu_context.clone(), - )), - submitted_frames: Vec::new(), - finished_frames: Vec::new(), - #[cfg(feature = "tracing-tracy")] - tracy_gpu_context, - })) + tracy_gpu_context, + }, + )) } fn current_frame_mut(&mut self) -> &mut FrameData { diff --git a/crates/bevy_render/src/error_handler.rs b/crates/bevy_render/src/error_handler.rs index 4a6036fdcaf62..abb10e1cab6f4 100644 --- a/crates/bevy_render/src/error_handler.rs +++ b/crates/bevy_render/src/error_handler.rs @@ -11,7 +11,7 @@ pub use wgpu_types::error::ErrorType; use crate::{ insert_future_resources, render_resource::PipelineCache, - renderer::{RenderDevice, WgpuWrapper}, + renderer::{wgpu_wrapper, RenderDevice}, settings::RenderCreation, FutureRenderResources, RenderStartup, }; @@ -83,13 +83,18 @@ impl Default for RenderErrorHandler { } } +wgpu_wrapper! { + #[derive(Debug)] + pub struct WgpuErrorSource(ErrorSource); +} + /// An error encountered during rendering. These are errors reported by wgpu validation layers, /// and typically indicate problems in the way it is being used. #[derive(Debug)] pub struct RenderError { pub ty: ErrorType, pub description: String, - pub source: Option>, + pub source: Option, } /// The current state of the renderer. @@ -105,11 +110,13 @@ pub(crate) enum RenderState { Reinitializing, } +wgpu_wrapper!(struct WgpuError(wgpu::Error)); + /// Resource to allow polling wgpu error handlers. #[derive(Resource)] pub(crate) struct DeviceErrorHandler { device_lost: Arc>>, - uncaptured: Arc>>>, + uncaptured: Arc>>, } impl DeviceErrorHandler { @@ -130,10 +137,7 @@ impl DeviceErrorHandler { }); device.on_uncaptured_error(Arc::new(move |e| { bevy_log::error!("Caught rendering error: {e}"); - uncaptured - .lock() - .unwrap() - .get_or_insert(WgpuWrapper::new(e)); + uncaptured.lock().unwrap().get_or_insert(WgpuError::new(e)); })); } Self { @@ -169,7 +173,7 @@ impl DeviceErrorHandler { return Some(RenderError { ty, description, - source: Some(WgpuWrapper::new(source)), + source: Some(WgpuErrorSource::new(source)), }); } None diff --git a/crates/bevy_render/src/render_resource/bind_group.rs b/crates/bevy_render/src/render_resource/bind_group.rs index f6c90bb7b70a6..471727a0224ef 100644 --- a/crates/bevy_render/src/render_resource/bind_group.rs +++ b/crates/bevy_render/src/render_resource/bind_group.rs @@ -2,7 +2,7 @@ use crate::{ material_bind_groups::FallbackBuffer, render_asset::RenderAssets, render_resource::{BindGroupLayout, Buffer, PipelineCache, Sampler, TextureView}, - renderer::{RenderDevice, WgpuWrapper}, + renderer::{wgpu_wrapper, RenderDevice}, storage::{GpuShaderBuffer, ShaderBuffer}, texture::GpuImage, }; @@ -25,6 +25,11 @@ use super::{BindlessDescriptor, BindlessSlabResourceLimit}; define_atomic_id!(BindGroupId); +wgpu_wrapper! { + #[derive(Clone, Debug)] + struct WgpuBindGroup(wgpu::BindGroup); +} + /// Bind groups are responsible for binding render resources (e.g. buffers, textures, samplers) /// to a [`TrackedRenderPass`](crate::render_phase::TrackedRenderPass). /// This makes them accessible in the pipeline (shaders) as uniforms. @@ -37,7 +42,7 @@ define_atomic_id!(BindGroupId); #[derive(Clone, Debug)] pub struct BindGroup { id: BindGroupId, - value: WgpuWrapper, + value: WgpuBindGroup, } impl BindGroup { @@ -66,7 +71,7 @@ impl From for BindGroup { fn from(value: wgpu::BindGroup) -> Self { BindGroup { id: BindGroupId::new(), - value: WgpuWrapper::new(value), + value: WgpuBindGroup::new(value), } } } 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 4b6c610074ce8..22644afd7be49 100644 --- a/crates/bevy_render/src/render_resource/bind_group_layout.rs +++ b/crates/bevy_render/src/render_resource/bind_group_layout.rs @@ -1,9 +1,14 @@ -use crate::renderer::WgpuWrapper; +use crate::renderer::wgpu_wrapper; use bevy_utils::define_atomic_id; use core::ops::Deref; define_atomic_id!(BindGroupLayoutId); +wgpu_wrapper! { + #[derive(Clone, Debug)] + struct WgpuBindGroupLayout(wgpu::BindGroupLayout); +} + /// Bind group layouts define the interface of resources (e.g. buffers, textures, samplers) /// for a shader. The actual resource binding is done via a [`BindGroup`](super::BindGroup). /// @@ -15,7 +20,7 @@ define_atomic_id!(BindGroupLayoutId); #[derive(Clone, Debug)] pub struct BindGroupLayout { id: BindGroupLayoutId, - value: WgpuWrapper, + value: WgpuBindGroupLayout, } impl PartialEq for BindGroupLayout { @@ -49,7 +54,7 @@ impl From for BindGroupLayout { fn from(value: wgpu::BindGroupLayout) -> Self { BindGroupLayout { id: BindGroupLayoutId::new(), - value: WgpuWrapper::new(value), + value: WgpuBindGroupLayout::new(value), } } } diff --git a/crates/bevy_render/src/render_resource/buffer.rs b/crates/bevy_render/src/render_resource/buffer.rs index 6032154d4ec42..7f96476c643ef 100644 --- a/crates/bevy_render/src/render_resource/buffer.rs +++ b/crates/bevy_render/src/render_resource/buffer.rs @@ -1,13 +1,18 @@ -use crate::renderer::WgpuWrapper; +use crate::renderer::wgpu_wrapper; use bevy_utils::define_atomic_id; use core::ops::{Deref, RangeBounds}; define_atomic_id!(BufferId); +wgpu_wrapper! { + #[derive(Clone, Debug)] + struct WgpuBuffer(wgpu::Buffer); +} + #[derive(Clone, Debug)] pub struct Buffer { id: BufferId, - value: WgpuWrapper, + value: WgpuBuffer, } impl Buffer { @@ -33,7 +38,7 @@ impl From for Buffer { fn from(value: wgpu::Buffer) -> Self { Buffer { id: BufferId::new(), - value: WgpuWrapper::new(value), + value: WgpuBuffer::new(value), } } } diff --git a/crates/bevy_render/src/render_resource/pipeline.rs b/crates/bevy_render/src/render_resource/pipeline.rs index 427cfb5d6c3a4..54fcd9107c432 100644 --- a/crates/bevy_render/src/render_resource/pipeline.rs +++ b/crates/bevy_render/src/render_resource/pipeline.rs @@ -1,9 +1,14 @@ -use crate::renderer::WgpuWrapper; +use crate::renderer::wgpu_wrapper; use bevy_utils::define_atomic_id; use core::ops::Deref; define_atomic_id!(RenderPipelineId); +wgpu_wrapper! { + #[derive(Clone, Debug)] + struct WgpuRenderPipeline(wgpu::RenderPipeline); +} + /// A [`RenderPipeline`] represents a graphics pipeline and its stages (shaders), bindings and vertex buffers. /// /// May be converted from and dereferences to a wgpu [`RenderPipeline`](wgpu::RenderPipeline). @@ -11,7 +16,7 @@ define_atomic_id!(RenderPipelineId); #[derive(Clone, Debug)] pub struct RenderPipeline { id: RenderPipelineId, - value: WgpuWrapper, + value: WgpuRenderPipeline, } impl RenderPipeline { @@ -25,7 +30,7 @@ impl From for RenderPipeline { fn from(value: wgpu::RenderPipeline) -> Self { RenderPipeline { id: RenderPipelineId::new(), - value: WgpuWrapper::new(value), + value: WgpuRenderPipeline::new(value), } } } @@ -41,6 +46,11 @@ impl Deref for RenderPipeline { define_atomic_id!(ComputePipelineId); +wgpu_wrapper! { + #[derive(Clone, Debug)] + struct WgpuComputePipeline(wgpu::ComputePipeline); +} + /// A [`ComputePipeline`] represents a compute pipeline and its single shader stage. /// /// May be converted from and dereferences to a wgpu [`ComputePipeline`](wgpu::ComputePipeline). @@ -48,7 +58,7 @@ define_atomic_id!(ComputePipelineId); #[derive(Clone, Debug)] pub struct ComputePipeline { id: ComputePipelineId, - value: WgpuWrapper, + value: WgpuComputePipeline, } impl ComputePipeline { @@ -63,7 +73,7 @@ impl From for ComputePipeline { fn from(value: wgpu::ComputePipeline) -> Self { ComputePipeline { id: ComputePipelineId::new(), - value: WgpuWrapper::new(value), + value: WgpuComputePipeline::new(value), } } } diff --git a/crates/bevy_render/src/render_resource/pipeline_cache.rs b/crates/bevy_render/src/render_resource/pipeline_cache.rs index c9366b4660c15..1f4a7e784c691 100644 --- a/crates/bevy_render/src/render_resource/pipeline_cache.rs +++ b/crates/bevy_render/src/render_resource/pipeline_cache.rs @@ -6,7 +6,7 @@ use smallvec::SmallVec; use crate::{ render_resource::*, - renderer::{RenderDevice, WgpuWrapper}, + renderer::{wgpu_wrapper, RenderDevice}, Extract, }; use alloc::{borrow::Cow, sync::Arc}; @@ -89,9 +89,16 @@ type LayoutCacheKey = ( SmallVec<[BindGroupLayoutId; BIND_GROUP_LAYOUTS_INLINE_CAPACITY]>, ImmediateSize, ); + +wgpu_wrapper! { + struct WgpuPipelineLayout(PipelineLayout); + + struct WgpuShaderModule(ShaderModule); +} + #[derive(Default)] struct LayoutCache { - layouts: HashMap>>, + layouts: HashMap>, } impl LayoutCache { @@ -100,7 +107,7 @@ impl LayoutCache { render_device: &RenderDevice, bind_group_layouts: &[BindGroupLayout], immediate_size: u32, - ) -> Arc> { + ) -> Arc { let bind_group_ids = bind_group_layouts.iter().map(BindGroupLayout::id).collect(); self.layouts .entry((bind_group_ids, immediate_size)) @@ -110,13 +117,13 @@ impl LayoutCache { .map(BindGroupLayout::value) .map(Some) .collect::>(); - Arc::new(WgpuWrapper::new(render_device.create_pipeline_layout( - &PipelineLayoutDescriptor { + Arc::new(WgpuPipelineLayout::new( + render_device.create_pipeline_layout(&PipelineLayoutDescriptor { bind_group_layouts: &bind_group_layouts, immediate_size: *immediate_size, ..default() - }, - ))) + }), + )) }) .clone() } @@ -126,7 +133,7 @@ fn load_module( render_device: &RenderDevice, shader_source: ShaderCacheSource, validate_shader: &ValidateShader, -) -> Result, ShaderCacheError> { +) -> Result { let shader_source = match shader_source { #[cfg(feature = "shader_format_spirv")] ShaderCacheSource::SpirV(data) => wgpu::util::make_spirv(data), @@ -145,7 +152,7 @@ fn load_module( .wgpu_device() .push_error_scope(wgpu::ErrorFilter::Validation); - let shader_module = WgpuWrapper::new(match validate_shader { + let shader_module = WgpuShaderModule::new(match validate_shader { ValidateShader::Enabled => { render_device.create_and_validate_shader_module(module_descriptor) } @@ -211,7 +218,7 @@ impl BindGroupLayoutCache { pub struct PipelineCache { layout_cache: Arc>, bindgroup_layout_cache: Arc>, - shader_cache: Arc, RenderDevice>>>, + shader_cache: Arc>>, device: RenderDevice, pipelines: Vec, waiting_pipelines: HashSet, diff --git a/crates/bevy_render/src/render_resource/texture.rs b/crates/bevy_render/src/render_resource/texture.rs index bec6b9bcba8df..55755bcd3cac3 100644 --- a/crates/bevy_render/src/render_resource/texture.rs +++ b/crates/bevy_render/src/render_resource/texture.rs @@ -1,4 +1,4 @@ -use crate::renderer::{RenderDevice, RenderQueue, WgpuWrapper}; +use crate::renderer::{wgpu_wrapper, RenderDevice, RenderQueue}; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ resource::Resource, @@ -10,6 +10,11 @@ use core::ops::Deref; define_atomic_id!(TextureId); +wgpu_wrapper! { + #[derive(Clone, Debug)] + struct WgpuTexture(wgpu::Texture); +} + /// A GPU-accessible texture. /// /// May be converted from and dereferences to a wgpu [`Texture`](wgpu::Texture). @@ -26,7 +31,7 @@ define_atomic_id!(TextureId); #[derive(Clone, Debug)] pub struct Texture { id: TextureId, - value: WgpuWrapper, + value: WgpuTexture, } impl Texture { @@ -46,7 +51,7 @@ impl From for Texture { fn from(value: wgpu::Texture) -> Self { Texture { id: TextureId::new(), - value: WgpuWrapper::new(value), + value: WgpuTexture::new(value), } } } @@ -62,15 +67,22 @@ impl Deref for Texture { define_atomic_id!(TextureViewId); +wgpu_wrapper! { + #[derive(Clone, Debug)] + struct WgpuTextureView(wgpu::TextureView); + + struct WgpuSurfaceTexture(wgpu::SurfaceTexture); +} + /// Describes a [`Texture`] with its associated metadata required by a pipeline or [`BindGroup`](super::BindGroup). #[derive(Clone, Debug)] pub struct TextureView { id: TextureViewId, - value: WgpuWrapper, + value: WgpuTextureView, } pub struct SurfaceTexture { - value: WgpuWrapper, + value: WgpuSurfaceTexture, } impl SurfaceTexture { @@ -91,7 +103,7 @@ impl From for TextureView { fn from(value: wgpu::TextureView) -> Self { TextureView { id: TextureViewId::new(), - value: WgpuWrapper::new(value), + value: WgpuTextureView::new(value), } } } @@ -99,7 +111,7 @@ impl From for TextureView { impl From for SurfaceTexture { fn from(value: wgpu::SurfaceTexture) -> Self { SurfaceTexture { - value: WgpuWrapper::new(value), + value: WgpuSurfaceTexture::new(value), } } } @@ -124,6 +136,11 @@ impl Deref for SurfaceTexture { define_atomic_id!(SamplerId); +wgpu_wrapper! { + #[derive(Clone, Debug)] + struct WgpuSampler(wgpu::Sampler); +} + /// A Sampler defines how a pipeline will sample from a [`TextureView`]. /// They define image filters (including anisotropy) and address (wrapping) modes, among other things. /// @@ -132,7 +149,7 @@ define_atomic_id!(SamplerId); #[derive(Clone, Debug)] pub struct Sampler { id: SamplerId, - value: WgpuWrapper, + value: WgpuSampler, } impl Sampler { @@ -147,7 +164,7 @@ impl From for Sampler { fn from(value: wgpu::Sampler) -> Self { Sampler { id: SamplerId::new(), - value: WgpuWrapper::new(value), + value: WgpuSampler::new(value), } } } diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index b525431e0e1b2..1173986ad9405 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -8,16 +8,15 @@ pub use render_context::{ CurrentView, FlushCommands, PendingCommandBuffers, RenderContext, RenderContextState, ViewQuery, }; pub use render_device::*; -pub use wgpu_wrapper::WgpuWrapper; + +pub(crate) use wgpu_wrapper::wgpu_wrapper; use crate::{ settings::{RenderResources, WgpuSettings, WgpuSettingsPriority}, sync_world::MainEntity, view::{screenshot::SubmitScreenshotCommandsState, ExtractedWindow, ViewTarget}, }; -use alloc::sync::Arc; use bevy_camera::NormalizedRenderTarget; -use bevy_derive::{Deref, DerefMut}; use bevy_ecs::schedule::ScheduleLabel; use bevy_ecs::{prelude::*, system::SystemState}; #[cfg(feature = "trace")] @@ -138,22 +137,24 @@ pub fn render_system( crate::view::screenshot::collect_screenshots(world); } -/// This queue is used to enqueue tasks for the GPU to execute asynchronously. -#[derive(Resource, Clone, Deref, DerefMut)] -pub struct RenderQueue(pub Arc>); +wgpu_wrapper! { + /// This queue is used to enqueue tasks for the GPU to execute asynchronously. + #[derive(Resource, Clone)] + pub struct RenderQueue(Queue); -/// The handle to the physical device being used for rendering. -/// See [`Adapter`] for more info. -#[derive(Resource, Clone, Debug, Deref, DerefMut)] -pub struct RenderAdapter(pub Arc>); + /// The handle to the physical device being used for rendering. + /// See [`Adapter`] for more info. + #[derive(Resource, Clone, Debug)] + pub struct RenderAdapter(Adapter); -/// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`], -#[derive(Resource, Clone, Deref, DerefMut)] -pub struct RenderInstance(pub Arc>); + /// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`]. + #[derive(Resource, Clone)] + pub struct RenderInstance(Instance); -/// The [`AdapterInfo`] of the adapter in use by the renderer. -#[derive(Resource, Clone, Deref, DerefMut)] -pub struct RenderAdapterInfo(pub WgpuWrapper); + /// The [`AdapterInfo`] of the adapter in use by the renderer. + #[derive(Resource, Clone)] + pub struct RenderAdapterInfo(AdapterInfo); +} const GPU_NOT_FOUND_ERROR_MESSAGE: &str = if cfg!(target_os = "linux") { "Unable to find a GPU! Make sure you have installed required drivers! For extra information, see: https://github.com/bevyengine/bevy/blob/latest/docs/linux_dependencies.md" @@ -377,10 +378,10 @@ pub async fn initialize_renderer( RenderResources( RenderDevice::from(device), - RenderQueue(Arc::new(WgpuWrapper::new(queue))), - RenderAdapterInfo(WgpuWrapper::new(adapter_info)), - RenderAdapter(Arc::new(WgpuWrapper::new(adapter))), - RenderInstance(Arc::new(WgpuWrapper::new(instance))), + RenderQueue::new(queue), + RenderAdapterInfo::new(adapter_info), + RenderAdapter::new(adapter), + RenderInstance::new(instance), #[cfg(feature = "raw_vulkan_init")] additional_vulkan_features, ) diff --git a/crates/bevy_render/src/renderer/render_context.rs b/crates/bevy_render/src/renderer/render_context.rs index 463dde7526027..8b59519b69567 100644 --- a/crates/bevy_render/src/renderer/render_context.rs +++ b/crates/bevy_render/src/renderer/render_context.rs @@ -1,8 +1,7 @@ -use super::WgpuWrapper; use crate::diagnostic::internal::DiagnosticsRecorder; use crate::render_phase::TrackedRenderPass; use crate::render_resource::{CommandEncoder, RenderPassDescriptor}; -use crate::renderer::RenderDevice; +use crate::renderer::{wgpu_wrapper, RenderDevice}; use alloc::borrow::Cow; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::change_detection::Tick; @@ -35,13 +34,17 @@ enum PendingCommandBuffer { }, } +wgpu_wrapper!(struct WgpuPendingCommandBuffersInner(PendingCommandBuffersInner)); + /// A resource that holds command buffers and encoders that are pending submission to the render queue. #[derive(Resource)] -pub struct PendingCommandBuffers(WgpuWrapper); +pub struct PendingCommandBuffers(WgpuPendingCommandBuffersInner); impl Default for PendingCommandBuffers { fn default() -> Self { - Self(WgpuWrapper::new(PendingCommandBuffersInner::default())) + Self(WgpuPendingCommandBuffersInner::new( + PendingCommandBuffersInner::default(), + )) } } @@ -162,15 +165,19 @@ impl RenderContextStateInner { } } +wgpu_wrapper!(struct WgpuRenderContextStateInner(RenderContextStateInner)); + /// A resource that holds the current render context state, including command encoder and command buffers. /// This is used internally by the [`RenderContext`] system parameter. Implements [`SystemBuffer`] to /// append command buffers and unfinished encoders in topological system order. Pending encoders are /// finished in parallel immediately before submission. -pub struct RenderContextState(WgpuWrapper); +pub struct RenderContextState(WgpuRenderContextStateInner); impl Default for RenderContextState { fn default() -> Self { - Self(WgpuWrapper::new(RenderContextStateInner::default())) + Self(WgpuRenderContextStateInner::new( + RenderContextStateInner::default(), + )) } } diff --git a/crates/bevy_render/src/renderer/render_device.rs b/crates/bevy_render/src/renderer/render_device.rs index aab42297e6cf4..30285d068cb9c 100644 --- a/crates/bevy_render/src/renderer/render_device.rs +++ b/crates/bevy_render/src/renderer/render_device.rs @@ -3,28 +3,35 @@ use crate::render_resource::{ BindGroup, BindGroupLayout, Buffer, ComputePipeline, RawRenderPipelineDescriptor, RenderPipeline, Sampler, Texture, }; -use crate::renderer::WgpuWrapper; +use crate::renderer::wgpu_wrapper; use bevy_ecs::resource::Resource; use wgpu::{ util::DeviceExt, BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BufferAsyncError, BufferBindingType, PollError, PollStatus, }; +wgpu_wrapper! { + #[derive(Clone)] + struct WgpuDevice(wgpu::Device); +} + /// This GPU device is responsible for the creation of most rendering and compute resources. #[derive(Resource, Clone)] pub struct RenderDevice { - device: WgpuWrapper, + device: WgpuDevice, } impl From for RenderDevice { fn from(device: wgpu::Device) -> Self { - Self::new(WgpuWrapper::new(device)) + Self::new(device) } } impl RenderDevice { - pub fn new(device: WgpuWrapper) -> Self { - Self { device } + pub fn new(device: wgpu::Device) -> Self { + Self { + device: WgpuDevice::new(device), + } } /// List all [`Features`](wgpu::Features) that may be used with this device. @@ -223,9 +230,9 @@ impl RenderDevice { order: wgpu::util::TextureDataOrder, data: &[u8], ) -> Texture { - let wgpu_texture = - self.device - .create_texture_with_data(render_queue.as_ref(), desc, order, data); + let wgpu_texture = self + .device + .create_texture_with_data(render_queue, desc, order, data); Texture::from(wgpu_texture) } diff --git a/crates/bevy_render/src/renderer/wgpu_wrapper.rs b/crates/bevy_render/src/renderer/wgpu_wrapper.rs index 272d0dd4c07ce..345af18620028 100644 --- a/crates/bevy_render/src/renderer/wgpu_wrapper.rs +++ b/crates/bevy_render/src/renderer/wgpu_wrapper.rs @@ -1,50 +1,96 @@ -/// A wrapper to safely make `wgpu` types Send / Sync on web with atomics enabled. +/// A macro producing wrappers 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, -); +#[cfg(not(all(target_arch = "wasm32", target_feature = "atomics")))] +macro_rules! wgpu_wrapper { + ($( $(#[$($attrs:tt)*])* $vis:vis struct $name:ident ($wgputy:ty) );+ $(;)?) => { + $( + $( #[$($attrs)*] )* + $vis struct $name ($wgputy); -// 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 $name { + /// Constructs a new instance of `WgpuWrapper` which will wrap the specified value. + pub fn new(t: $wgputy) -> Self { + Self(t) + } + + #[allow(clippy::allow_attributes, unused, reason = "This is not used on all wrappers.")] + /// Unwraps the value. + pub fn into_inner(self) -> $wgputy { + self.0 + } + } -impl core::ops::Deref for WgpuWrapper { - type Target = T; + impl ::core::ops::Deref for $name { + type Target = $wgputy; - fn deref(&self) -> &Self::Target { - &self.0 - } + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl ::core::ops::DerefMut for $name { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + // Short-circuit the `Send + Sync` implementation. + // At the type-level this does effectively nothing, however in the compiler + // this creates a short-circuit for the trait solver that reduces recursion depth + // and substantially improves compile times. + const _: () = { + const fn assert_sync_send() {} + assert_sync_send::<$wgputy>() + }; + // SAFETY: We just asserted that $wgputy is Send and Sync + #[expect(unsafe_code, reason = "Blanket-impl Send requires unsafe.")] + unsafe impl Send for $name {} + // SAFETY: We just asserted that $wgputy is Send and Sync + #[expect(unsafe_code, reason = "Blanket-impl Send requires unsafe.")] + unsafe impl Sync for $name {} + )+ + }; } -impl core::ops::DerefMut for WgpuWrapper { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } +#[cfg(all(target_arch = "wasm32", target_feature = "atomics"))] +macro_rules! wgpu_wrapper { + ($( $(#[$($attrs:tt)*])* $vis:vis struct $name:ident ($wgputy:ty) );+ $(;)?) => { + $( + // On web + atomics we use SendWrapper to make the type unconditionally Send + Sync, + // but the value can only be accessed on the `wgpu` thread or it will panic. + // We don't need short circuits here since `SendWrapper` does it for us. + $( #[$($attrs)*] )* + $vis struct $name (send_wrapper::SendWrapper<$wgputy>); + + impl $name { + /// Constructs a new instance of `WgpuWrapper` which will wrap the specified value. + pub fn new(t: $wgputy) -> Self { + Self(send_wrapper::SendWrapper::new(t)) + } + + pub fn into_inner(self) -> $wgputy { + self.0.take() + } + } + + impl ::core::ops::Deref for $name { + type Target = $wgputy; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl ::core::ops::DerefMut for $name { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + )+ + }; } + +pub(crate) use wgpu_wrapper; diff --git a/crates/bevy_render/src/test_utils.rs b/crates/bevy_render/src/test_utils.rs index a002ca61ed71e..9733bce82500f 100644 --- a/crates/bevy_render/src/test_utils.rs +++ b/crates/bevy_render/src/test_utils.rs @@ -1,13 +1,12 @@ //! Helpers for this crate's unit tests. -use alloc::sync::Arc; use bevy_platform::future::block_on; use wgpu::{ BackendOptions, Backends, DeviceDescriptor, Instance, InstanceDescriptor, InstanceFlags, NoopBackendOptions, RequestAdapterOptions, }; -use crate::renderer::{RenderDevice, RenderQueue, WgpuWrapper}; +use crate::renderer::{RenderDevice, RenderQueue}; /// Creates a dummy [`RenderDevice`] and [`RenderQueue`] on `wgpu`'s noop backend. /// @@ -36,8 +35,5 @@ pub fn create_dummy_device() -> (RenderDevice, RenderQueue) { })) .expect("the noop backend should always produce a device"); - ( - RenderDevice::from(device), - RenderQueue(Arc::new(WgpuWrapper::new(queue))), - ) + (RenderDevice::from(device), RenderQueue::new(queue)) } diff --git a/crates/bevy_render/src/view/window/mod.rs b/crates/bevy_render/src/view/window/mod.rs index a8efdc74741cf..653cea31bffae 100644 --- a/crates/bevy_render/src/view/window/mod.rs +++ b/crates/bevy_render/src/view/window/mod.rs @@ -1,4 +1,4 @@ -use crate::renderer::WgpuWrapper; +use crate::renderer::wgpu_wrapper; use crate::sync_world::{MainEntity, RenderEntity, SyncToRenderWorld}; use crate::{camera::extract_cameras, renderer::RenderQueue}; use crate::{ @@ -209,10 +209,12 @@ fn extract_windows( } } +// TODO: what lifetime should this be? +wgpu_wrapper!(struct WgpuSurface(wgpu::Surface<'static>)); + #[derive(Component)] pub struct SurfaceData { - // TODO: what lifetime should this be? - surface: WgpuWrapper>, + surface: WgpuSurface, configuration: SurfaceConfiguration, texture_view_format: Option, } @@ -426,7 +428,7 @@ pub fn create_surfaces( render_device.configure_surface(&surface, &configuration); commands.entity(entity).insert(SurfaceData { - surface: WgpuWrapper::new(surface), + surface: WgpuSurface::new(surface), configuration, texture_view_format, });