From 29c57f999681383f9c1bc713486e8986d05cd71f Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Tue, 7 Jul 2026 18:57:04 +0200 Subject: [PATCH 1/5] [core] Remove `bundle_ffi` Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- wgpu-core/src/command/bundle.rs | 148 +------------------------------- wgpu-core/src/command/mod.rs | 2 +- 2 files changed, 2 insertions(+), 148 deletions(-) diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index 408140f7e3e..b0ab45ccc31 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -42,8 +42,7 @@ To create a render bundle: 1) Create a [`RenderBundleEncoder`] by calling [`Global::device_create_render_bundle_encoder`][Gdcrbe]. -2) Record commands in the `RenderBundleEncoder` using functions from the - [`bundle_ffi`] module. +2) Record commands in the `RenderBundleEncoder` using methods on [`RenderBundleEncoder`]. 3) Call [`Global::render_bundle_encoder_finish`][Grbef], which analyzes and cleans up the command stream and returns a `RenderBundleId`. @@ -2220,148 +2219,3 @@ impl crate::global::Global { bundle_encoder.insert_debug_marker(label) } } - -pub mod bundle_ffi { - use super::RenderBundleEncoder; - use crate::{id, RawString}; - use core::slice; - use wgt::{BufferAddress, BufferSize, DynamicOffset, IndexFormat}; - - #[deprecated(note = "Use `Global::render_bundle_encoder_set_bind_group` instead.")] - /// # Safety - /// - /// This function is unsafe as there is no guarantee that the given pointer is - /// valid for `offset_length` elements. - pub unsafe fn wgpu_render_bundle_set_bind_group( - bundle: &mut RenderBundleEncoder, - index: u32, - bind_group_id: Option, - offsets: *const DynamicOffset, - offset_length: usize, - ) { - let offsets = unsafe { slice::from_raw_parts(offsets, offset_length) }; - - let _ = bundle.set_bind_group(index, bind_group_id, offsets); - } - - #[deprecated(note = "Use `Global::render_bundle_encoder_set_pipeline` instead.")] - pub fn wgpu_render_bundle_set_pipeline( - bundle: &mut RenderBundleEncoder, - pipeline_id: id::RenderPipelineId, - ) { - let _ = bundle.set_pipeline(pipeline_id); - } - - #[deprecated(note = "Use `Global::render_bundle_encoder_set_vertex_buffer` instead.")] - pub fn wgpu_render_bundle_set_vertex_buffer( - bundle: &mut RenderBundleEncoder, - slot: u32, - buffer_id: Option, - offset: BufferAddress, - size: Option, - ) { - let _ = bundle.set_vertex_buffer(slot, buffer_id, offset, size); - } - - #[deprecated(note = "Use `Global::render_bundle_encoder_set_index_buffer` instead.")] - pub fn wgpu_render_bundle_set_index_buffer( - encoder: &mut RenderBundleEncoder, - buffer: id::BufferId, - index_format: IndexFormat, - offset: BufferAddress, - size: Option, - ) { - let _ = encoder.set_index_buffer(buffer, index_format, offset, size); - } - - #[deprecated(note = "Use `Global::render_bundle_encoder_set_immediates` instead.")] - /// # Safety - /// - /// This function is unsafe as there is no guarantee that the given pointer is - /// valid for `data` elements. - pub unsafe fn wgpu_render_bundle_set_immediates( - pass: &mut RenderBundleEncoder, - offset: u32, - size_bytes: u32, - data: *const u8, - ) { - let data_slice = unsafe { slice::from_raw_parts(data, size_bytes as usize) }; - let _ = pass.set_immediates(offset, data_slice); - } - - #[deprecated(note = "Use `Global::render_bundle_encoder_draw` instead.")] - pub fn wgpu_render_bundle_draw( - bundle: &mut RenderBundleEncoder, - vertex_count: u32, - instance_count: u32, - first_vertex: u32, - first_instance: u32, - ) { - let _ = bundle.draw(vertex_count, instance_count, first_vertex, first_instance); - } - - #[deprecated(note = "Use `Global::render_bundle_encoder_draw_indexed` instead.")] - pub fn wgpu_render_bundle_draw_indexed( - bundle: &mut RenderBundleEncoder, - index_count: u32, - instance_count: u32, - first_index: u32, - base_vertex: i32, - first_instance: u32, - ) { - let _ = bundle.draw_indexed( - index_count, - instance_count, - first_index, - base_vertex, - first_instance, - ); - } - - #[deprecated(note = "Use `Global::render_bundle_encoder_draw_indirect` instead.")] - pub fn wgpu_render_bundle_draw_indirect( - bundle: &mut RenderBundleEncoder, - buffer_id: id::BufferId, - offset: BufferAddress, - ) { - let _ = bundle.draw_indirect(buffer_id, offset); - } - - #[deprecated(note = "Use `Global::render_bundle_encoder_draw_indexed_indirect` instead.")] - pub fn wgpu_render_bundle_draw_indexed_indirect( - bundle: &mut RenderBundleEncoder, - buffer_id: id::BufferId, - offset: BufferAddress, - ) { - let _ = bundle.draw_indexed_indirect(buffer_id, offset); - } - - #[deprecated(note = "Use `Global::render_bundle_encoder_push_debug_group` instead.")] - /// # Safety - /// - /// This function is unsafe as there is no guarantee that the given `label` - /// is a valid null-terminated string. - pub unsafe fn wgpu_render_bundle_push_debug_group( - _bundle: &mut RenderBundleEncoder, - _label: RawString, - ) { - //TODO - } - - #[deprecated(note = "Use `Global::render_bundle_encoder_pop_debug_group` instead.")] - pub fn wgpu_render_bundle_pop_debug_group(_bundle: &mut RenderBundleEncoder) { - //TODO - } - - #[deprecated(note = "Use `Global::render_bundle_encoder_insert_debug_marker` instead.")] - /// # Safety - /// - /// This function is unsafe as there is no guarantee that the given `label` - /// is a valid null-terminated string. - pub unsafe fn wgpu_render_bundle_insert_debug_marker( - _bundle: &mut RenderBundleEncoder, - _label: RawString, - ) { - //TODO - } -} diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index 5ec7046279c..6421ad9db44 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -43,7 +43,7 @@ pub use self::encoder_command::PointerReferences; // here. Some types (in particular `CopySide`) may be exported unnecessarily. pub use self::{ bundle::{ - bundle_ffi, CreateRenderBundleError, ExecutionError, RenderBundle, RenderBundleDescriptor, + CreateRenderBundleError, ExecutionError, RenderBundle, RenderBundleDescriptor, RenderBundleEncoder, RenderBundleEncoderDescriptor, RenderBundleError, RenderBundleErrorInner, }, From 6dfb4cb8af25754ac4e1453c25211763d512d11a Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Tue, 7 Jul 2026 19:08:37 +0200 Subject: [PATCH 2/5] pass arced device to RenderBundleEncoder Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- wgpu-core/src/command/bundle.rs | 93 +++++++++------------------------ wgpu-core/src/device/global.rs | 13 ++--- 2 files changed, 31 insertions(+), 75 deletions(-) diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index b0ab45ccc31..097ede22f6d 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -164,10 +164,9 @@ pub struct RenderBundleEncoderDescriptor<'a> { } #[derive(Debug)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct RenderBundleEncoder { pub(crate) base: BasePass, Infallible>, - parent_id: id::DeviceId, + device: Arc, /// State of the render bundle encoder. Encoded to be compatible with pass macros. /// /// If this is `Some`, then the pass is in WebGPU's "open" state. If it is @@ -179,9 +178,7 @@ pub struct RenderBundleEncoder { pub(crate) is_stencil_read_only: bool, // Resource binding dedupe state. - #[cfg_attr(feature = "serde", serde(skip))] current_bind_groups: BindGroupStateChange, - #[cfg_attr(feature = "serde", serde(skip))] current_pipeline: StateChange, } @@ -190,20 +187,15 @@ impl_storage_item!(RenderBundleEncoder); /// Validate a render bundle descriptor. /// -/// The underlying `device` is required to fully validate the descriptor. -/// If omitted, some validation will be skipped. -/// /// Returns a tuple (is_depth_read_only, is_stencil_read_only). fn validate_render_bundle_encoder_descriptor( desc: &RenderBundleEncoderDescriptor, - device: Option<&Arc>, + device: &Arc, ) -> Result<(bool, bool), CreateRenderBundleError> { let mut have_attachment = false; - let max_color_attachments = device.map_or(hal::MAX_COLOR_ATTACHMENTS as u32, |device| { - assert!(device.limits.max_color_attachments <= hal::MAX_COLOR_ATTACHMENTS as u32); - device.limits.max_color_attachments - }); + let max_color_attachments = device.limits.max_color_attachments; + assert!(max_color_attachments <= hal::MAX_COLOR_ATTACHMENTS as u32); check_color_attachment_count(desc.color_formats.len(), max_color_attachments)?; for &format in desc.color_formats.iter().flatten() { @@ -211,23 +203,19 @@ fn validate_render_bundle_encoder_descriptor( if !format.has_color_aspect() { return Err(CreateRenderBundleError::FormatNotColor(format)); } - if let Some(device) = device { - let format_features = device.describe_format_features(format)?; - if !format_features - .allowed_usages - .contains(wgt::TextureUsages::RENDER_ATTACHMENT) - { - return Err(CreateRenderBundleError::FormatNotRenderable(format)); - } + let format_features = device.describe_format_features(format)?; + if !format_features + .allowed_usages + .contains(wgt::TextureUsages::RENDER_ATTACHMENT) + { + return Err(CreateRenderBundleError::FormatNotRenderable(format)); } } - if let Some(device) = device { - validate_color_attachment_bytes_per_sample( - desc.color_formats.iter().flatten().copied(), - device.limits.max_color_attachment_bytes_per_sample, - )?; - } + validate_color_attachment_bytes_per_sample( + desc.color_formats.iter().flatten().copied(), + device.limits.max_color_attachment_bytes_per_sample, + )?; let (is_depth_read_only, is_stencil_read_only) = match desc.depth_stencil { Some(ds) => { @@ -258,22 +246,17 @@ fn validate_render_bundle_encoder_descriptor( impl RenderBundleEncoder { /// Create a new `RenderBundleEncoder`. - /// - /// The underlying `device` is required to fully validate the descriptor. - /// If the device is not available, some validation will be deferred - /// until `finish()`. pub fn new( + device: &Arc, desc: &RenderBundleEncoderDescriptor, - device: Option<&Arc>, - parent_id: id::DeviceId, ) -> Result { let (is_depth_read_only, is_stencil_read_only) = validate_render_bundle_encoder_descriptor(desc, device)?; Ok(Self { base: BasePass::new(&desc.label), + device: Arc::clone(device), parent: Some(()), - parent_id, context: RenderPassContext { attachments: AttachmentData { colors: desc.color_formats.iter().cloned().collect(), @@ -291,11 +274,11 @@ impl RenderBundleEncoder { }) } - pub fn dummy(parent_id: id::DeviceId) -> Self { + pub fn dummy(device: &Arc) -> Self { Self { base: BasePass::new(&None), parent: None, - parent_id, + device: Arc::clone(device), context: RenderPassContext::default(), is_depth_read_only: false, is_stencil_read_only: false, @@ -305,10 +288,6 @@ impl RenderBundleEncoder { } } - pub fn parent(&self) -> id::DeviceId { - self.parent_id - } - pub fn label(&self) -> Option<&str> { self.base.label.as_deref() } @@ -326,7 +305,6 @@ impl RenderBundleEncoder { pub fn finish( &mut self, desc: &RenderBundleDescriptor, - device: &Arc, hub: &Hub, ) -> (Arc, Option) { #[cfg(feature = "trace")] @@ -337,13 +315,16 @@ impl RenderBundleEncoder { self.is_stencil_read_only, ); - let (render_bundle, error) = match self.finish_inner(desc, device, hub) { + let (render_bundle, error) = match self.finish_inner(desc, hub) { Ok(render_bundle) => (render_bundle, None), - Err(e) => (RenderBundle::invalid(Arc::clone(device), desc), Some(e)), + Err(e) => ( + RenderBundle::invalid(Arc::clone(&self.device), desc), + Some(e), + ), }; #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { + if let Some(ref mut trace) = *self.device.trace.lock() { use crate::device::trace::{Action, IntoTrace}; trace.add(Action::CreateRenderBundle { id: render_bundle.to_trace(), @@ -373,7 +354,6 @@ impl RenderBundleEncoder { pub(crate) fn finish_inner( &mut self, desc: &RenderBundleDescriptor, - device: &Arc, hub: &Hub, ) -> Result, RenderBundleError> { let scope = PassErrorScope::Bundle; @@ -383,28 +363,7 @@ impl RenderBundleEncoder { .ok_or(RenderBundleErrorInner::Ended) .map_pass_err(scope)?; - device.check_is_valid().map_pass_err(scope)?; - - { - // Reconstruct and revalidate the encoder descriptor, because - // `RenderBundleEncoder` is serializable and could have been tampered. - let encoder_desc = RenderBundleEncoderDescriptor { - label: self.base.label.as_ref().map(Cow::from), - color_formats: Cow::Borrowed(&self.context.attachments.colors), - depth_stencil: self.context.attachments.depth_stencil.map(|format| { - wgt::RenderBundleDepthStencil { - format, - depth_read_only: self.is_depth_read_only, - stencil_read_only: self.is_stencil_read_only, - } - }), - sample_count: self.context.sample_count, - multiview: self.context.multiview_mask, - }; - - validate_render_bundle_encoder_descriptor(&encoder_desc, Some(device)) - .map_pass_err(scope)?; - }; + self.device.check_is_valid().map_pass_err(scope)?; let buffer_guard = hub.buffers.read(); let bind_group_guard = hub.bind_groups.read(); @@ -416,7 +375,7 @@ impl RenderBundleEncoder { vertex: Default::default(), index: None, flat_dynamic_offsets: Vec::new(), - device: device.clone(), + device: Arc::clone(&self.device), commands: Vec::new(), buffer_memory_init_actions: Vec::new(), texture_memory_init_actions: Vec::new(), diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index 4fe986f52dc..1dd1f0e1027 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -702,11 +702,10 @@ impl Global { profiling::scope!("Device::create_render_bundle_encoder"); api_log!("Device::device_create_render_bundle_encoder"); let device = self.hub.devices.get(device_id); - let (encoder, error) = - match command::RenderBundleEncoder::new(desc, Some(&device), device_id) { - Ok(encoder) => (encoder, None), - Err(e) => (command::RenderBundleEncoder::dummy(device_id), Some(e)), - }; + let (encoder, error) = match command::RenderBundleEncoder::new(&device, desc) { + Ok(encoder) => (encoder, None), + Err(e) => (command::RenderBundleEncoder::dummy(&device), Some(e)), + }; (Box::new(encoder), error) } @@ -744,9 +743,7 @@ impl Global { let fid = hub.render_bundles.prepare(id_in); - let device = self.hub.devices.get(bundle_encoder.parent()); - - let (render_bundle, error) = bundle_encoder.finish(desc, &device, hub); + let (render_bundle, error) = bundle_encoder.finish(desc, hub); let id = fid.assign(render_bundle); From 1474649e734752dcb4e7c51fdcf60d1d9f1eb96c Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Tue, 7 Jul 2026 19:11:34 +0200 Subject: [PATCH 3/5] check device validity at creation Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- wgpu-core/src/command/bundle.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index 097ede22f6d..1f447dc9c93 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -250,6 +250,7 @@ impl RenderBundleEncoder { device: &Arc, desc: &RenderBundleEncoderDescriptor, ) -> Result { + device.check_is_valid()?; let (is_depth_read_only, is_stencil_read_only) = validate_render_bundle_encoder_descriptor(desc, device)?; @@ -1230,6 +1231,8 @@ pub enum CreateRenderBundleError { InvalidSampleCount(u32), #[error(transparent)] MissingFeatures(#[from] MissingFeatures), + #[error(transparent)] + Device(#[from] DeviceError), } impl WebGpuError for CreateRenderBundleError { @@ -1242,6 +1245,7 @@ impl WebGpuError for CreateRenderBundleError { | Self::NoAttachment | Self::InvalidSampleCount(_) => ErrorType::Validation, Self::MissingFeatures(e) => e.webgpu_error_type(), + Self::Device(e) => e.webgpu_error_type(), } } } From 0beb122b97075f4fbcd37b000cb7ca509f3114c6 Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Wed, 8 Jul 2026 05:41:24 +0200 Subject: [PATCH 4/5] Arced resources in RenderBundleEncoder Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- wgpu-core/src/command/bundle.rs | 194 ++++++++++++++------------------ wgpu-core/src/device/global.rs | 2 +- 2 files changed, 88 insertions(+), 108 deletions(-) diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index 1f447dc9c93..cee070146d1 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -44,8 +44,8 @@ To create a render bundle: 2) Record commands in the `RenderBundleEncoder` using methods on [`RenderBundleEncoder`]. -3) Call [`Global::render_bundle_encoder_finish`][Grbef], which analyzes and cleans up - the command stream and returns a `RenderBundleId`. +3) Call [`RenderBundleEncoder::finish`], which analyzes and cleans up + the command stream and returns a [`RenderBundle`]. 4) Then, any number of times, call [`render_pass_execute_bundles`][wrpeb] to execute the bundle as part of some render pass. @@ -71,7 +71,6 @@ buffer. Thanks to isolation, it doesn't track any bind group invalidations or index format changes. [Gdcrbe]: crate::global::Global::device_create_render_bundle_encoder -[Grbef]: crate::global::Global::render_bundle_encoder_finish [wrpeb]: crate::global::Global::render_pass_execute_bundles !*/ @@ -98,23 +97,20 @@ use thiserror::Error; use wgpu_hal::ShouldBeNonZeroExt; use wgt::error::{ErrorType, WebGpuError}; -#[cfg(feature = "trace")] -use crate::command::ArcReferences; use crate::{ api_log, binding_model::{BindError, BindGroup, ImmediateUploadError, PipelineLayout}, command::{ bind::Binder, pass::{validate_immediates_alignment, ImmediateState}, - pass_base, BasePass, BindGroupStateChange, ColorAttachmentError, DrawError, - EncoderStateError, IdReferences, MapPassErr, PassErrorScope, PassStateError, RenderCommand, + pass_base, ArcReferences, BasePass, BindGroupStateChange, ColorAttachmentError, DrawError, + EncoderStateError, MapPassErr, PassErrorScope, PassStateError, RenderCommand, RenderCommandError, StateChange, }, device::{ AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures, RenderPassContext, }, - hub::Hub, id, impl_resource_type, impl_storage_item, init_tracker::{BufferInitTrackerAction, MemoryInitKind, TextureInitTrackerAction}, pipeline::{PipelineFlags, RenderPipeline}, @@ -165,7 +161,7 @@ pub struct RenderBundleEncoderDescriptor<'a> { #[derive(Debug)] pub struct RenderBundleEncoder { - pub(crate) base: BasePass, Infallible>, + pub(crate) base: BasePass, Infallible>, device: Arc, /// State of the render bundle encoder. Encoded to be compatible with pass macros. /// @@ -178,8 +174,8 @@ pub struct RenderBundleEncoder { pub(crate) is_stencil_read_only: bool, // Resource binding dedupe state. - current_bind_groups: BindGroupStateChange, - current_pipeline: StateChange, + current_bind_groups: BindGroupStateChange>, + current_pipeline: StateChange>, } impl_resource_type!(RenderBundleEncoder); @@ -306,7 +302,6 @@ impl RenderBundleEncoder { pub fn finish( &mut self, desc: &RenderBundleDescriptor, - hub: &Hub, ) -> (Arc, Option) { #[cfg(feature = "trace")] let trace_desc = crate::device::trace::new_render_bundle_encoder_descriptor( @@ -316,7 +311,7 @@ impl RenderBundleEncoder { self.is_stencil_read_only, ); - let (render_bundle, error) = match self.finish_inner(desc, hub) { + let (render_bundle, error) = match self.finish_inner(desc) { Ok(render_bundle) => (render_bundle, None), Err(e) => ( RenderBundle::invalid(Arc::clone(&self.device), desc), @@ -355,7 +350,6 @@ impl RenderBundleEncoder { pub(crate) fn finish_inner( &mut self, desc: &RenderBundleDescriptor, - hub: &Hub, ) -> Result, RenderBundleError> { let scope = PassErrorScope::Bundle; @@ -366,10 +360,6 @@ impl RenderBundleEncoder { self.device.check_is_valid().map_pass_err(scope)?; - let buffer_guard = hub.buffers.read(); - let bind_group_guard = hub.bind_groups.read(); - let pipeline_guard = hub.render_pipelines.read(); - let mut state = State { trackers: RenderBundleScope::new(), pipeline: None, @@ -389,11 +379,9 @@ impl RenderBundleEncoder { state.trackers.buffers.set_size(indices.buffers.size()); state.trackers.textures.set_size(indices.textures.size()); - let base = &self.base; - - for command in &base.commands { + for command in self.base.commands.drain(..) { match command { - &RenderCommand::SetBindGroup { + RenderCommand::SetBindGroup { index, num_dynamic_offsets, bind_group, @@ -401,19 +389,17 @@ impl RenderBundleEncoder { let scope = PassErrorScope::SetBindGroup; set_bind_group( &mut state, - &bind_group_guard, - &base.dynamic_offsets, + &self.base.dynamic_offsets, index, num_dynamic_offsets, bind_group, ) .map_pass_err(scope)?; } - &RenderCommand::SetPipeline(pipeline) => { + RenderCommand::SetPipeline(pipeline) => { let scope = PassErrorScope::SetPipelineRender; set_pipeline( &mut state, - &pipeline_guard, &self.context, self.is_depth_read_only, self.is_stencil_read_only, @@ -421,38 +407,31 @@ impl RenderBundleEncoder { ) .map_pass_err(scope)?; } - &RenderCommand::SetIndexBuffer { + RenderCommand::SetIndexBuffer { buffer, index_format, offset, size, } => { let scope = PassErrorScope::SetIndexBuffer; - set_index_buffer( - &mut state, - &buffer_guard, - buffer, - index_format, - offset, - size, - ) - .map_pass_err(scope)?; + set_index_buffer(&mut state, buffer, index_format, offset, size) + .map_pass_err(scope)?; } - &RenderCommand::SetVertexBuffer { + RenderCommand::SetVertexBuffer { slot, buffer, offset, size, } => { let scope = PassErrorScope::SetVertexBuffer; - set_vertex_buffer(&mut state, &buffer_guard, slot, buffer, offset, size) + set_vertex_buffer(&mut state, slot, buffer, offset, size) .map_pass_err(scope)?; } - &RenderCommand::SetImmediate { offset, ref data } => { + RenderCommand::SetImmediate { offset, ref data } => { let scope = PassErrorScope::SetImmediate; set_immediates(&mut state, offset, data).map_pass_err(scope)?; } - &RenderCommand::Draw { + RenderCommand::Draw { vertex_count, instance_count, first_vertex, @@ -471,7 +450,7 @@ impl RenderBundleEncoder { ) .map_pass_err(scope)?; } - &RenderCommand::DrawIndexed { + RenderCommand::DrawIndexed { index_count, instance_count, first_index, @@ -492,7 +471,7 @@ impl RenderBundleEncoder { ) .map_pass_err(scope)?; } - &RenderCommand::DrawMeshTasks { + RenderCommand::DrawMeshTasks { group_count_x, group_count_y, group_count_z, @@ -504,7 +483,7 @@ impl RenderBundleEncoder { draw_mesh_tasks(&mut state, group_count_x, group_count_y, group_count_z) .map_pass_err(scope)?; } - &RenderCommand::DrawIndirect { + RenderCommand::DrawIndirect { buffer, offset, count: 1, @@ -516,10 +495,9 @@ impl RenderBundleEncoder { kind: DrawKind::DrawIndirect, family, }; - multi_draw_indirect(&mut state, &buffer_guard, buffer, offset, family) - .map_pass_err(scope)?; + multi_draw_indirect(&mut state, buffer, offset, family).map_pass_err(scope)?; } - &RenderCommand::DrawIndirect { + RenderCommand::DrawIndirect { count, vertex_or_index_limit, instance_limit, @@ -527,25 +505,25 @@ impl RenderBundleEncoder { } => { unreachable!("unexpected (multi-)draw indirect with count {count}, vertex_or_index_limits {vertex_or_index_limit:?}, instance_limit {instance_limit:?} found in a render bundle"); } - &RenderCommand::MultiDrawIndirectCount { .. } - | &RenderCommand::PushDebugGroup { color: _, len: _ } - | &RenderCommand::InsertDebugMarker { color: _, len: _ } - | &RenderCommand::PopDebugGroup => { + RenderCommand::MultiDrawIndirectCount { .. } + | RenderCommand::PushDebugGroup { color: _, len: _ } + | RenderCommand::InsertDebugMarker { color: _, len: _ } + | RenderCommand::PopDebugGroup => { unimplemented!("not supported by a render bundle") } // Must check the TIMESTAMP_QUERY_INSIDE_PASSES feature - &RenderCommand::WriteTimestamp { .. } - | &RenderCommand::BeginOcclusionQuery { .. } - | &RenderCommand::EndOcclusionQuery - | &RenderCommand::BeginPipelineStatisticsQuery { .. } - | &RenderCommand::EndPipelineStatisticsQuery => { + RenderCommand::WriteTimestamp { .. } + | RenderCommand::BeginOcclusionQuery { .. } + | RenderCommand::EndOcclusionQuery + | RenderCommand::BeginPipelineStatisticsQuery { .. } + | RenderCommand::EndPipelineStatisticsQuery => { unimplemented!("not supported by a render bundle") } - &RenderCommand::ExecuteBundle(_) - | &RenderCommand::SetBlendConstant(_) - | &RenderCommand::SetStencilReference(_) - | &RenderCommand::SetViewport { .. } - | &RenderCommand::SetScissor(_) => unreachable!("not supported by a render bundle"), + RenderCommand::ExecuteBundle(_) + | RenderCommand::SetBlendConstant(_) + | RenderCommand::SetStencilReference(_) + | RenderCommand::SetViewport { .. } + | RenderCommand::SetScissor(_) => unreachable!("not supported by a render bundle"), } } @@ -595,7 +573,7 @@ impl RenderBundleEncoder { pub fn set_index_buffer( &mut self, - buffer: id::BufferId, + buffer: Arc, index_format: wgt::IndexFormat, offset: wgt::BufferAddress, size: Option, @@ -613,12 +591,12 @@ impl RenderBundleEncoder { pub fn set_bind_group( &mut self, index: u32, - bind_group_id: Option, + bind_group: Option>, offsets: &[wgt::DynamicOffset], ) -> Result<(), PassStateError> { pass_base!(self, PassErrorScope::SetBindGroup); let redundant = self.current_bind_groups.set_and_check_redundant( - &bind_group_id, + &bind_group, index, &mut self.base.dynamic_offsets, offsets, @@ -631,37 +609,34 @@ impl RenderBundleEncoder { self.base.commands.push(RenderCommand::SetBindGroup { index, num_dynamic_offsets: offsets.len(), - bind_group: bind_group_id, + bind_group, }); Ok(()) } - pub fn set_pipeline( - &mut self, - pipeline_id: id::RenderPipelineId, - ) -> Result<(), PassStateError> { + pub fn set_pipeline(&mut self, pipeline: Arc) -> Result<(), PassStateError> { pass_base!(self, PassErrorScope::SetPipelineRender); - if self.current_pipeline.set_and_check_redundant(&pipeline_id) { + if self.current_pipeline.set_and_check_redundant(&pipeline) { return Ok(()); } self.base .commands - .push(RenderCommand::SetPipeline(pipeline_id)); + .push(RenderCommand::SetPipeline(pipeline)); Ok(()) } pub fn set_vertex_buffer( &mut self, slot: u32, - buffer_id: Option, + buffer: Option>, offset: wgt::BufferAddress, size: Option, ) -> Result<(), PassStateError> { pass_base!(self, PassErrorScope::SetVertexBuffer); self.base.commands.push(RenderCommand::SetVertexBuffer { slot, - buffer: buffer_id, + buffer, offset, size, }); @@ -734,7 +709,7 @@ impl RenderBundleEncoder { pub fn draw_indirect( &mut self, - buffer_id: id::BufferId, + buffer: Arc, offset: wgt::BufferAddress, ) -> Result<(), PassStateError> { pass_base!( @@ -745,7 +720,7 @@ impl RenderBundleEncoder { } ); self.base.commands.push(RenderCommand::DrawIndirect { - buffer: buffer_id, + buffer, offset, count: 1, family: DrawCommandFamily::Draw, @@ -757,7 +732,7 @@ impl RenderBundleEncoder { pub fn draw_indexed_indirect( &mut self, - buffer_id: id::BufferId, + buffer: Arc, offset: wgt::BufferAddress, ) -> Result<(), PassStateError> { pass_base!( @@ -768,7 +743,7 @@ impl RenderBundleEncoder { } ); self.base.commands.push(RenderCommand::DrawIndirect { - buffer: buffer_id, + buffer, offset, count: 1, family: DrawCommandFamily::DrawIndexed, @@ -799,11 +774,10 @@ impl RenderBundleEncoder { fn set_bind_group( state: &mut State, - bind_group_guard: &crate::storage::Storage>, dynamic_offsets: &[u32], index: u32, num_dynamic_offsets: usize, - bind_group_id: Option>, + bind_group: Option>, ) -> Result<(), RenderBundleErrorInner> { let max_bind_groups = state.device.limits.max_bind_groups; if index >= max_bind_groups { @@ -821,8 +795,6 @@ fn set_bind_group( state.next_dynamic_offset = offsets_range.end; let offsets = &dynamic_offsets[offsets_range.clone()]; - let bind_group = bind_group_id.map(|id| bind_group_guard.get(id)); - if let Some(bind_group) = bind_group { bind_group.check_is_valid()?; bind_group.same_device(&state.device)?; @@ -852,14 +824,11 @@ fn set_bind_group( fn set_pipeline( state: &mut State, - pipeline_guard: &crate::storage::Storage>, context: &RenderPassContext, is_depth_read_only: bool, is_stencil_read_only: bool, - pipeline_id: id::Id, + pipeline: Arc, ) -> Result<(), RenderBundleErrorInner> { - let pipeline = pipeline_guard.get(pipeline_id); - pipeline.same_device(&state.device)?; context @@ -892,13 +861,11 @@ fn set_pipeline( // This function is duplicative of `render::set_index_buffer`. fn set_index_buffer( state: &mut State, - buffer_guard: &crate::storage::Storage>, - buffer_id: id::Id, + buffer: Arc, index_format: wgt::IndexFormat, offset: u64, size: Option, ) -> Result<(), RenderBundleErrorInner> { - let buffer = buffer_guard.get(buffer_id); buffer.check_is_valid()?; state @@ -932,9 +899,8 @@ fn set_index_buffer( // This function is duplicative of `render::set_vertex_buffer`. fn set_vertex_buffer( state: &mut State, - buffer_guard: &crate::storage::Storage>, slot: u32, - buffer_id: Option>, + buffer: Option>, offset: u64, size: Option, ) -> Result<(), RenderBundleErrorInner> { @@ -947,8 +913,7 @@ fn set_vertex_buffer( .into()); } - if let Some(buffer_id) = buffer_id { - let buffer = buffer_guard.get(buffer_id); + if let Some(buffer) = buffer { buffer.check_is_valid()?; state @@ -1138,8 +1103,7 @@ fn draw_mesh_tasks( fn multi_draw_indirect( state: &mut State, - buffer_guard: &crate::storage::Storage>, - buffer_id: id::Id, + buffer: Arc, offset: u64, family: DrawCommandFamily, ) -> Result<(), RenderBundleErrorInner> { @@ -1148,8 +1112,6 @@ fn multi_draw_indirect( .device .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?; - let buffer = buffer_guard.get(buffer_id); - buffer.check_is_valid()?; buffer.same_device(&state.device)?; buffer.check_usage(wgt::BufferUsages::INDIRECT)?; @@ -1879,7 +1841,11 @@ impl crate::global::Global { bind_group_id: Option, offsets: &[wgt::DynamicOffset], ) -> Result<(), PassStateError> { - bundle.set_bind_group(index, bind_group_id, offsets) + bundle.set_bind_group( + index, + bind_group_id.map(|id| self.hub.bind_groups.get(id)), + offsets, + ) } pub fn render_bundle_encoder_set_bind_group_with_id( @@ -1895,7 +1861,11 @@ impl crate::global::Global { .try_lock() .expect("RenderBundleEncoders should not be accessed concurrently"); - bundle_encoder.set_bind_group(index, bind_group_id, offsets) + bundle_encoder.set_bind_group( + index, + bind_group_id.map(|id| self.hub.bind_groups.get(id)), + offsets, + ) } pub fn render_bundle_encoder_set_pipeline( @@ -1903,7 +1873,7 @@ impl crate::global::Global { bundle: &mut RenderBundleEncoder, pipeline_id: id::RenderPipelineId, ) -> Result<(), PassStateError> { - bundle.set_pipeline(pipeline_id) + bundle.set_pipeline(self.hub.render_pipelines.get(pipeline_id)) } pub fn render_bundle_encoder_set_pipeline_with_id( @@ -1917,7 +1887,7 @@ impl crate::global::Global { .try_lock() .expect("RenderBundleEncoders should not be accessed concurrently"); - bundle_encoder.set_pipeline(pipeline_id) + bundle_encoder.set_pipeline(self.hub.render_pipelines.get(pipeline_id)) } pub fn render_bundle_encoder_set_vertex_buffer( @@ -1928,7 +1898,12 @@ impl crate::global::Global { offset: wgt::BufferAddress, size: Option, ) -> Result<(), PassStateError> { - bundle.set_vertex_buffer(slot, buffer_id, offset, size) + bundle.set_vertex_buffer( + slot, + buffer_id.map(|id| self.hub.buffers.get(id)), + offset, + size, + ) } pub fn render_bundle_encoder_set_vertex_buffer_with_id( @@ -1945,7 +1920,12 @@ impl crate::global::Global { .try_lock() .expect("RenderBundleEncoders should not be accessed concurrently"); - bundle_encoder.set_vertex_buffer(slot, buffer_id, offset, size) + bundle_encoder.set_vertex_buffer( + slot, + buffer_id.map(|id| self.hub.buffers.get(id)), + offset, + size, + ) } pub fn render_bundle_encoder_set_index_buffer( @@ -1956,7 +1936,7 @@ impl crate::global::Global { offset: wgt::BufferAddress, size: Option, ) -> Result<(), PassStateError> { - encoder.set_index_buffer(buffer, index_format, offset, size) + encoder.set_index_buffer(self.hub.buffers.get(buffer), index_format, offset, size) } pub fn render_bundle_encoder_set_index_buffer_with_id( @@ -1973,7 +1953,7 @@ impl crate::global::Global { .try_lock() .expect("RenderBundleEncoders should not be accessed concurrently"); - bundle_encoder.set_index_buffer(buffer, index_format, offset, size) + bundle_encoder.set_index_buffer(self.hub.buffers.get(buffer), index_format, offset, size) } pub fn render_bundle_encoder_set_immediates( @@ -2076,7 +2056,7 @@ impl crate::global::Global { buffer_id: id::BufferId, offset: wgt::BufferAddress, ) -> Result<(), PassStateError> { - bundle.draw_indirect(buffer_id, offset) + bundle.draw_indirect(self.hub.buffers.get(buffer_id), offset) } pub fn render_bundle_encoder_draw_indirect_with_id( @@ -2091,7 +2071,7 @@ impl crate::global::Global { .try_lock() .expect("RenderBundleEncoders should not be accessed concurrently"); - bundle_encoder.draw_indirect(buffer_id, offset) + bundle_encoder.draw_indirect(self.hub.buffers.get(buffer_id), offset) } pub fn render_bundle_encoder_draw_indexed_indirect( @@ -2100,7 +2080,7 @@ impl crate::global::Global { buffer_id: id::BufferId, offset: wgt::BufferAddress, ) -> Result<(), PassStateError> { - bundle.draw_indexed_indirect(buffer_id, offset) + bundle.draw_indexed_indirect(self.hub.buffers.get(buffer_id), offset) } pub fn render_bundle_encoder_draw_indexed_indirect_with_id( @@ -2115,7 +2095,7 @@ impl crate::global::Global { .try_lock() .expect("RenderBundleEncoders should not be accessed concurrently"); - bundle_encoder.draw_indexed_indirect(buffer_id, offset) + bundle_encoder.draw_indexed_indirect(self.hub.buffers.get(buffer_id), offset) } pub fn render_bundle_encoder_push_debug_group( diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index 1dd1f0e1027..8b157b2c82d 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -743,7 +743,7 @@ impl Global { let fid = hub.render_bundles.prepare(id_in); - let (render_bundle, error) = bundle_encoder.finish(desc, hub); + let (render_bundle, error) = bundle_encoder.finish(desc); let id = fid.assign(render_bundle); From d0297f7dbda8b46eb4f2080728f74ae28c16aaea Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Wed, 8 Jul 2026 05:48:58 +0200 Subject: [PATCH 5/5] [core] Make `Global` methods of RenderBundleEncoder thin Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- wgpu-core/src/command/bundle.rs | 1 + wgpu-core/src/device/global.rs | 11 +---------- wgpu-core/src/device/resource.rs | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index cee070146d1..09ba742c2b7 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -303,6 +303,7 @@ impl RenderBundleEncoder { &mut self, desc: &RenderBundleDescriptor, ) -> (Arc, Option) { + profiling::scope!("RenderBundleEncoder::finish"); #[cfg(feature = "trace")] let trace_desc = crate::device::trace::new_render_bundle_encoder_descriptor( desc.label.clone(), diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index 8b157b2c82d..400c79ea72e 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -4,7 +4,6 @@ use core::ptr::NonNull; #[cfg(feature = "trace")] use crate::device::trace; use crate::{ - api_log, binding_model::{ self, BindGroupEntry, BindingResource, BufferBinding, ResolvedBindGroupDescriptor, ResolvedBindGroupEntry, ResolvedBindingResource, ResolvedBufferBinding, @@ -699,14 +698,8 @@ impl Global { Box, Option, ) { - profiling::scope!("Device::create_render_bundle_encoder"); - api_log!("Device::device_create_render_bundle_encoder"); let device = self.hub.devices.get(device_id); - let (encoder, error) = match command::RenderBundleEncoder::new(&device, desc) { - Ok(encoder) => (encoder, None), - Err(e) => (command::RenderBundleEncoder::dummy(&device), Some(e)), - }; - (Box::new(encoder), error) + device.create_render_bundle_encoder(desc) } pub fn device_create_render_bundle_encoder_with_id( @@ -737,8 +730,6 @@ impl Global { desc: &command::RenderBundleDescriptor, id_in: Option, ) -> (id::RenderBundleId, Option) { - profiling::scope!("RenderBundleEncoder::finish"); - let hub = &self.hub; let fid = hub.render_bundles.prepare(id_in); diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index 88998f0ec6a..24010375099 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -3071,6 +3071,22 @@ impl Device { Ok(cmd_enc) } + pub fn create_render_bundle_encoder( + self: &Arc, + desc: &command::RenderBundleEncoderDescriptor, + ) -> ( + Box, + Option, + ) { + profiling::scope!("Device::create_render_bundle_encoder"); + api_log!("Device::create_render_bundle_encoder"); + let (encoder, error) = match command::RenderBundleEncoder::new(self, desc) { + Ok(encoder) => (encoder, None), + Err(e) => (command::RenderBundleEncoder::dummy(self), Some(e)), + }; + (Box::new(encoder), error) + } + /// Generate information about late-validated buffer bindings for pipelines. //TODO: should this be combined with `get_introspection_bind_group_layouts` in some way? fn make_late_sized_buffer_groups(