From add7a201b5eadce57fecd89fc4d04813cce57097 Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Wed, 12 Aug 2026 16:14:52 +0200 Subject: [PATCH 1/5] Move Ids, Regestries and Global into wgpu-core-remote Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- Cargo.lock | 12 + Cargo.toml | 1 + wgpu-core-remote/Cargo.toml | 37 + wgpu-core-remote/src/global/as_hal.rs | 161 ++++ wgpu-core-remote/src/global/bundle.rs | 332 ++++++++ .../src/global/command_encoder.rs | 331 ++++++++ wgpu-core-remote/src/global/compute_pass.rs | 350 ++++++++ .../src/global/device.rs | 34 +- wgpu-core-remote/src/global/instance.rs | 206 +++++ .../src/global/mod.rs | 52 +- wgpu-core-remote/src/global/present.rs | 48 ++ wgpu-core-remote/src/global/queue.rs | 133 +++ wgpu-core-remote/src/global/ray_tracing.rs | 73 ++ wgpu-core-remote/src/global/render_pass.rs | 775 ++++++++++++++++++ {wgpu-core => wgpu-core-remote}/src/hub.rs | 17 +- wgpu-core-remote/src/id.rs | 278 +++++++ .../src/identity.rs | 22 +- wgpu-core-remote/src/lib.rs | 52 ++ .../src/registry.rs | 14 +- wgpu-core-remote/src/storage.rs | 201 +++++ wgpu-core/src/as_hal.rs | 172 +--- wgpu-core/src/command/bundle.rs | 332 +------- wgpu-core/src/command/clear.rs | 33 +- wgpu-core/src/command/compute.rs | 351 -------- wgpu-core/src/command/encoder_command.rs | 19 - wgpu-core/src/command/ffi.rs | 9 - wgpu-core/src/command/mod.rs | 78 +- wgpu-core/src/command/query.rs | 38 - wgpu-core/src/command/ray_tracing.rs | 89 +- wgpu-core/src/command/render.rs | 774 ----------------- wgpu-core/src/command/transfer.rs | 89 -- wgpu-core/src/command/transition_resources.rs | 38 - wgpu-core/src/device/mod.rs | 1 - wgpu-core/src/device/queue.rs | 152 +--- wgpu-core/src/device/ray_tracing.rs | 76 +- wgpu-core/src/id.rs | 275 +------ wgpu-core/src/instance.rs | 304 +------ wgpu-core/src/lib.rs | 9 +- wgpu-core/src/lock/rank.rs | 1 - wgpu-core/src/present.rs | 43 +- wgpu-core/src/storage.rs | 193 +---- 41 files changed, 3091 insertions(+), 3114 deletions(-) create mode 100644 wgpu-core-remote/Cargo.toml create mode 100644 wgpu-core-remote/src/global/as_hal.rs create mode 100644 wgpu-core-remote/src/global/bundle.rs create mode 100644 wgpu-core-remote/src/global/command_encoder.rs create mode 100644 wgpu-core-remote/src/global/compute_pass.rs rename wgpu-core/src/device/global.rs => wgpu-core-remote/src/global/device.rs (98%) create mode 100644 wgpu-core-remote/src/global/instance.rs rename wgpu-core/src/global.rs => wgpu-core-remote/src/global/mod.rs (91%) create mode 100644 wgpu-core-remote/src/global/present.rs create mode 100644 wgpu-core-remote/src/global/queue.rs create mode 100644 wgpu-core-remote/src/global/ray_tracing.rs create mode 100644 wgpu-core-remote/src/global/render_pass.rs rename {wgpu-core => wgpu-core-remote}/src/hub.rs (96%) create mode 100644 wgpu-core-remote/src/id.rs rename {wgpu-core => wgpu-core-remote}/src/identity.rs (92%) create mode 100644 wgpu-core-remote/src/lib.rs rename {wgpu-core => wgpu-core-remote}/src/registry.rs (93%) create mode 100644 wgpu-core-remote/src/storage.rs delete mode 100644 wgpu-core/src/command/ffi.rs diff --git a/Cargo.lock b/Cargo.lock index f32a01f6695..f195bd36805 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4859,6 +4859,18 @@ dependencies = [ "wgpu-hal", ] +[[package]] +name = "wgpu-core-remote" +version = "30.0.0" +dependencies = [ + "parking_lot", + "raw-window-handle", + "serde", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + [[package]] name = "wgpu-example-01-hello-compute" version = "0.0.0" diff --git a/Cargo.toml b/Cargo.toml index 67d9b7cb465..a6953ffc91e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ members = [ "tests", "wgpu-core", "wgpu-core/platform-deps/*", + "wgpu-core-remote", "wgpu-hal", "wgpu-info", "wgpu-naga-bridge", diff --git a/wgpu-core-remote/Cargo.toml b/wgpu-core-remote/Cargo.toml new file mode 100644 index 00000000000..8583796b1d2 --- /dev/null +++ b/wgpu-core-remote/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "wgpu-core-remote" +version.workspace = true +edition.workspace = true +description = "Remoting version of wgpu-core" +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +license.workspace = true + +# Override the workspace's `rust-version` key. `wgpu-core-remote` and its dependencies +# have a less strict MSRV, to allow firefox more leeway in updating their Rust toolchain. +# +# See the repo README for more information on MSRV policy. +rust-version = "1.87" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] +targets = [ + "x86_64-unknown-linux-gnu", + "x86_64-apple-darwin", + "x86_64-pc-windows-msvc", + "wasm32-unknown-unknown", +] + +[features] +## Enables serialization via `serde` on common wgpu types. +serde = ["dep:serde", "wgpu-types/serde", "wgpu-core/serde"] + +[dependencies] +wgpu-core = { workspace = true, default-features = false } +wgpu-hal = { workspace = true, default-features = false } +wgpu-types.workspace = true +parking_lot.workspace = true +raw-window-handle.workspace = true +serde = { workspace = true, optional = true } diff --git a/wgpu-core-remote/src/global/as_hal.rs b/wgpu-core-remote/src/global/as_hal.rs new file mode 100644 index 00000000000..2014ab65c8e --- /dev/null +++ b/wgpu-core-remote/src/global/as_hal.rs @@ -0,0 +1,161 @@ +use core::ops::Deref; + +use crate::global::Global; +use crate::id::*; + +impl Global { + /// # Safety + /// + /// - The raw buffer handle must not be manually destroyed + pub unsafe fn buffer_as_hal( + &self, + id: BufferId, + ) -> Option> { + let hub = &self.hub; + + let buffer = hub.buffers.get(id); + + unsafe { buffer.as_hal::() } + } + + /// # Safety + /// + /// - The raw texture handle must not be manually destroyed + pub unsafe fn texture_as_hal( + &self, + id: TextureId, + ) -> Option> { + let hub = &self.hub; + + let texture = hub.textures.get(id); + + unsafe { texture.as_hal::() } + } + + /// # Safety + /// + /// - The raw texture view handle must not be manually destroyed + pub unsafe fn texture_view_as_hal( + &self, + id: TextureViewId, + ) -> Option> { + let hub = &self.hub; + + let view = hub.texture_views.get(id); + + unsafe { view.as_hal::() } + } + + /// # Safety + /// + /// - The raw adapter handle must not be manually destroyed + pub unsafe fn adapter_as_hal( + &self, + id: AdapterId, + ) -> Option> { + let hub = &self.hub; + let adapter = hub.adapters.get(id); + + unsafe { adapter.as_hal::() } + } + + /// # Safety + /// + /// - The raw device handle must not be manually destroyed + pub unsafe fn device_as_hal( + &self, + id: DeviceId, + ) -> Option> { + let device = self.hub.devices.get(id); + + unsafe { device.as_hal::() } + } + + /// # Safety + /// + /// - The raw fence handle must not be manually destroyed + pub unsafe fn device_fence_as_hal( + &self, + id: DeviceId, + ) -> Option> { + let device = self.hub.devices.get(id); + + unsafe { device.fence_as_hal::() } + } + + /// # Safety + /// + /// - The raw surface handle must not be manually destroyed + pub unsafe fn surface_as_hal( + &self, + id: SurfaceId, + ) -> Option> { + let surface = self.surfaces.get(id); + + unsafe { surface.as_hal::() } + } + + /// Encode commands using the raw HAL command encoder. + /// + /// # Panics + /// + /// If the command encoder has already been used with the wgpu encoding API. + /// + /// # Safety + /// + /// - The raw command encoder handle must not be manually destroyed + pub unsafe fn command_encoder_as_hal_mut< + A: hal::Api, + F: FnOnce(Option<&mut A::CommandEncoder>) -> R, + R, + >( + &self, + id: CommandEncoderId, + hal_command_encoder_callback: F, + ) -> R { + let hub = &self.hub; + + let cmd_enc = hub.command_encoders.get(id); + unsafe { cmd_enc.as_hal_mut::(hal_command_encoder_callback) } + } + + /// # Safety + /// + /// - The raw queue handle must not be manually destroyed + pub unsafe fn queue_as_hal( + &self, + id: QueueId, + ) -> Option> { + let queue = self.hub.queues.get(id); + + unsafe { queue.as_hal::() } + } + + /// # Safety + /// + /// - The raw blas handle must not be manually destroyed + pub unsafe fn blas_as_hal( + &self, + id: BlasId, + ) -> Option> { + let hub = &self.hub; + + let blas = hub.blas_s.get(id); + + unsafe { blas.as_hal::() } + } + + /// # Safety + /// + /// - The raw tlas handle must not be manually destroyed + pub unsafe fn tlas_as_hal( + &self, + id: TlasId, + ) -> Option> { + let hub = &self.hub; + + let tlas = hub.tlas_s.get(id); + + unsafe { tlas.as_hal::() } + } +} diff --git a/wgpu-core-remote/src/global/bundle.rs b/wgpu-core-remote/src/global/bundle.rs new file mode 100644 index 00000000000..70c1fe6b432 --- /dev/null +++ b/wgpu-core-remote/src/global/bundle.rs @@ -0,0 +1,332 @@ +use crate::id; +use wgpu_core::command::{PassStateError, RenderBundleEncoder}; + +impl crate::global::Global { + pub fn render_bundle_encoder_set_bind_group( + &self, + bundle: &mut RenderBundleEncoder, + index: u32, + bind_group_id: Option, + offsets: &[wgt::DynamicOffset], + ) -> Result<(), PassStateError> { + 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( + &self, + bundle_encoder: id::RenderBundleEncoderId, + index: u32, + bind_group_id: Option, + offsets: &[wgt::DynamicOffset], + ) -> Result<(), PassStateError> { + let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); + + let mut bundle_encoder = bundle_encoder + .try_lock() + .expect("RenderBundleEncoders should not be accessed concurrently"); + + 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( + &self, + bundle: &mut RenderBundleEncoder, + pipeline_id: id::RenderPipelineId, + ) -> Result<(), PassStateError> { + bundle.set_pipeline(self.hub.render_pipelines.get(pipeline_id)) + } + + pub fn render_bundle_encoder_set_pipeline_with_id( + &self, + bundle_encoder: id::RenderBundleEncoderId, + pipeline_id: id::RenderPipelineId, + ) -> Result<(), PassStateError> { + let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); + + let mut bundle_encoder = bundle_encoder + .try_lock() + .expect("RenderBundleEncoders should not be accessed concurrently"); + + bundle_encoder.set_pipeline(self.hub.render_pipelines.get(pipeline_id)) + } + + pub fn render_bundle_encoder_set_vertex_buffer( + &self, + bundle: &mut RenderBundleEncoder, + slot: u32, + buffer_id: Option, + offset: wgt::BufferAddress, + size: Option, + ) -> Result<(), PassStateError> { + 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( + &self, + bundle_encoder: id::RenderBundleEncoderId, + slot: u32, + buffer_id: Option, + offset: wgt::BufferAddress, + size: Option, + ) -> Result<(), PassStateError> { + let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); + + let mut bundle_encoder = bundle_encoder + .try_lock() + .expect("RenderBundleEncoders should not be accessed concurrently"); + + 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( + &self, + encoder: &mut RenderBundleEncoder, + buffer: id::BufferId, + index_format: wgt::IndexFormat, + offset: wgt::BufferAddress, + size: Option, + ) -> Result<(), PassStateError> { + encoder.set_index_buffer(self.hub.buffers.get(buffer), index_format, offset, size) + } + + pub fn render_bundle_encoder_set_index_buffer_with_id( + &self, + bundle_encoder: id::RenderBundleEncoderId, + buffer: id::BufferId, + index_format: wgt::IndexFormat, + offset: wgt::BufferAddress, + size: Option, + ) -> Result<(), PassStateError> { + let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); + + let mut bundle_encoder = bundle_encoder + .try_lock() + .expect("RenderBundleEncoders should not be accessed concurrently"); + + bundle_encoder.set_index_buffer(self.hub.buffers.get(buffer), index_format, offset, size) + } + + pub fn render_bundle_encoder_set_immediates( + &self, + pass: &mut RenderBundleEncoder, + offset: u32, + data: &[u8], + ) -> Result<(), PassStateError> { + pass.set_immediates(offset, data) + } + + pub fn render_bundle_encoder_set_immediates_with_id( + &self, + bundle_encoder: id::RenderBundleEncoderId, + offset: u32, + data: &[u8], + ) -> Result<(), PassStateError> { + let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); + + let mut bundle_encoder = bundle_encoder + .try_lock() + .expect("RenderBundleEncoders should not be accessed concurrently"); + + bundle_encoder.set_immediates(offset, data) + } + + pub fn render_bundle_encoder_draw( + &self, + bundle: &mut RenderBundleEncoder, + vertex_count: u32, + instance_count: u32, + first_vertex: u32, + first_instance: u32, + ) -> Result<(), PassStateError> { + bundle.draw(vertex_count, instance_count, first_vertex, first_instance) + } + + pub fn render_bundle_encoder_draw_with_id( + &self, + bundle_encoder: id::RenderBundleEncoderId, + vertex_count: u32, + instance_count: u32, + first_vertex: u32, + first_instance: u32, + ) -> Result<(), PassStateError> { + let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); + + let mut bundle_encoder = bundle_encoder + .try_lock() + .expect("RenderBundleEncoders should not be accessed concurrently"); + + bundle_encoder.draw(vertex_count, instance_count, first_vertex, first_instance) + } + + pub fn render_bundle_encoder_draw_indexed( + &self, + bundle: &mut RenderBundleEncoder, + index_count: u32, + instance_count: u32, + first_index: u32, + base_vertex: i32, + first_instance: u32, + ) -> Result<(), PassStateError> { + bundle.draw_indexed( + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + ) + } + + pub fn render_bundle_encoder_draw_indexed_with_id( + &self, + bundle_encoder: id::RenderBundleEncoderId, + index_count: u32, + instance_count: u32, + first_index: u32, + base_vertex: i32, + first_instance: u32, + ) -> Result<(), PassStateError> { + let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); + + let mut bundle_encoder = bundle_encoder + .try_lock() + .expect("RenderBundleEncoders should not be accessed concurrently"); + + bundle_encoder.draw_indexed( + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + ) + } + + pub fn render_bundle_encoder_draw_indirect( + &self, + bundle: &mut RenderBundleEncoder, + buffer_id: id::BufferId, + offset: wgt::BufferAddress, + ) -> Result<(), PassStateError> { + bundle.draw_indirect(self.hub.buffers.get(buffer_id), offset) + } + + pub fn render_bundle_encoder_draw_indirect_with_id( + &self, + bundle_encoder: id::RenderBundleEncoderId, + buffer_id: id::BufferId, + offset: wgt::BufferAddress, + ) -> Result<(), PassStateError> { + let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); + + let mut bundle_encoder = bundle_encoder + .try_lock() + .expect("RenderBundleEncoders should not be accessed concurrently"); + + bundle_encoder.draw_indirect(self.hub.buffers.get(buffer_id), offset) + } + + pub fn render_bundle_encoder_draw_indexed_indirect( + &self, + bundle: &mut RenderBundleEncoder, + buffer_id: id::BufferId, + offset: wgt::BufferAddress, + ) -> Result<(), PassStateError> { + bundle.draw_indexed_indirect(self.hub.buffers.get(buffer_id), offset) + } + + pub fn render_bundle_encoder_draw_indexed_indirect_with_id( + &self, + bundle_encoder: id::RenderBundleEncoderId, + buffer_id: id::BufferId, + offset: wgt::BufferAddress, + ) -> Result<(), PassStateError> { + let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); + + let mut bundle_encoder = bundle_encoder + .try_lock() + .expect("RenderBundleEncoders should not be accessed concurrently"); + + bundle_encoder.draw_indexed_indirect(self.hub.buffers.get(buffer_id), offset) + } + + pub fn render_bundle_encoder_push_debug_group( + &self, + bundle: &mut RenderBundleEncoder, + label: &str, + ) -> Result<(), PassStateError> { + bundle.push_debug_group(label) + } + + pub fn render_bundle_encoder_push_debug_group_with_id( + &self, + bundle_encoder: id::RenderBundleEncoderId, + label: &str, + ) -> Result<(), PassStateError> { + let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); + + let mut bundle_encoder = bundle_encoder + .try_lock() + .expect("RenderBundleEncoders should not be accessed concurrently"); + + bundle_encoder.push_debug_group(label) + } + + pub fn render_bundle_encoder_pop_debug_group( + &self, + bundle: &mut RenderBundleEncoder, + ) -> Result<(), PassStateError> { + bundle.pop_debug_group() + } + + pub fn render_bundle_encoder_pop_debug_group_with_id( + &self, + bundle_encoder: id::RenderBundleEncoderId, + ) -> Result<(), PassStateError> { + let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); + + let mut bundle_encoder = bundle_encoder + .try_lock() + .expect("RenderBundleEncoders should not be accessed concurrently"); + + bundle_encoder.pop_debug_group() + } + + pub fn render_bundle_encoder_insert_debug_marker( + &self, + bundle: &mut RenderBundleEncoder, + label: &str, + ) -> Result<(), PassStateError> { + bundle.insert_debug_marker(label) + } + + pub fn render_bundle_encoder_insert_debug_marker_with_id( + &self, + bundle_encoder: id::RenderBundleEncoderId, + label: &str, + ) -> Result<(), PassStateError> { + let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); + + let mut bundle_encoder = bundle_encoder + .try_lock() + .expect("RenderBundleEncoders should not be accessed concurrently"); + + bundle_encoder.insert_debug_marker(label) + } +} diff --git a/wgpu-core-remote/src/global/command_encoder.rs b/wgpu-core-remote/src/global/command_encoder.rs new file mode 100644 index 00000000000..bcf10cbd98f --- /dev/null +++ b/wgpu-core-remote/src/global/command_encoder.rs @@ -0,0 +1,331 @@ +use wgpu_core::command::{CommandEncoderError, EncoderStateError}; +use wgpu_core::ray_tracing::{ + BlasAabbGeometry, BlasBuildEntry, BlasGeometries, BlasTriangleGeometry, TlasInstance, + TlasPackage, +}; +use wgpu_core::Label; +use wgt::{BufferAddress, Extent3d, ImageSubresourceRange}; + +use crate::global::Global; +use crate::id::{BlasId, BufferId, CommandEncoderId, TextureId, TlasId}; +use crate::{id, TexelCopyBufferInfo}; + +impl Global { + /// Finishes a command encoder, creating a command buffer and returning errors that were + /// deferred until now. + /// + /// The returned `String` is the label of the command encoder, supplied so that `wgpu` can + /// include the label when printing deferred errors without having its own copy of the label. + /// This is a kludge and should be replaced if we think of a better solution to propagating + /// labels. + pub fn command_encoder_finish( + &self, + encoder_id: CommandEncoderId, + desc: &wgt::CommandBufferDescriptor(name.to_owned(), hal_instance), surfaces: Registry::new(), @@ -78,7 +83,6 @@ impl Global { /// /// - The raw handles obtained from the Instance must not be manually destroyed pub unsafe fn from_instance(instance: Arc) -> Self { - profiling::scope!("Global::new"); Self { instance, surfaces: Registry::new(), @@ -293,14 +297,6 @@ impl fmt::Debug for Global { } } -impl Drop for Global { - fn drop(&mut self) { - profiling::scope!("Global::drop"); - resource_log!("Global::drop"); - } -} - -#[cfg(send_sync)] fn _test_send_sync(global: &Global) { fn test_internal(_: T) {} test_internal(global) diff --git a/wgpu-core-remote/src/global/present.rs b/wgpu-core-remote/src/global/present.rs new file mode 100644 index 00000000000..fa52b606747 --- /dev/null +++ b/wgpu-core-remote/src/global/present.rs @@ -0,0 +1,48 @@ +use wgpu_core::present::{SurfaceError, SurfaceOutput}; +use wgt::SurfaceStatus; + +use crate::global::Global; +use crate::id; + +impl Global { + pub fn surface_get_current_texture( + &self, + surface_id: id::SurfaceId, + texture_id_in: Option, + ) -> Result, SurfaceError> { + let surface = self.surfaces.get(surface_id); + + let fid = self.hub.textures.prepare(texture_id_in); + + let output = surface.get_current_texture()?; + + let status = output.status; + let texture_id = output.texture.map(|texture| fid.assign(texture)); + + Ok(SurfaceOutput { + status, + texture: texture_id, + }) + } + + pub fn surface_present( + &self, + surface_id: id::SurfaceId, + ) -> Result { + let surface = self.surfaces.get(surface_id); + + surface.present() + } + + pub fn surface_texture_discard(&self, surface_id: id::SurfaceId) -> Result<(), SurfaceError> { + let surface = self.surfaces.get(surface_id); + + surface.discard() + } + + pub fn surface_texture_release(&self, surface_id: id::SurfaceId) -> Result<(), SurfaceError> { + let surface = self.surfaces.get(surface_id); + + surface.release() + } +} diff --git a/wgpu-core-remote/src/global/queue.rs b/wgpu-core-remote/src/global/queue.rs new file mode 100644 index 00000000000..d282c9f8919 --- /dev/null +++ b/wgpu-core-remote/src/global/queue.rs @@ -0,0 +1,133 @@ +use core::ptr::NonNull; + +use wgpu_core::device::queue::{QueueSubmitError, QueueWriteError, SubmittedWorkDoneClosure}; +use wgpu_core::ray_tracing::CompactBlasError; +use wgpu_core::SubmissionIndex; + +use crate::global::Global; +use crate::id::{BlasId, BufferId, CommandBufferId, QueueId, StagingBufferId, TextureId}; + +impl Global { + pub fn queue_write_buffer( + &self, + queue_id: QueueId, + buffer_id: BufferId, + buffer_offset: wgt::BufferAddress, + data: &[u8], + ) -> Result<(), QueueWriteError> { + let queue = self.hub.queues.get(queue_id); + let buffer = self.hub.buffers.get(buffer_id); + + queue.write_buffer(buffer, buffer_offset, data) + } + + pub fn queue_create_staging_buffer( + &self, + queue_id: QueueId, + buffer_size: wgt::BufferSize, + id_in: Option, + ) -> Result<(StagingBufferId, NonNull), QueueWriteError> { + let queue = self.hub.queues.get(queue_id); + let (staging_buffer, ptr) = queue.create_staging_buffer(buffer_size)?; + + let fid = self.hub.staging_buffers.prepare(id_in); + let id = fid.assign(staging_buffer); + + Ok((id, ptr)) + } + + pub fn queue_write_staging_buffer( + &self, + queue_id: QueueId, + buffer_id: BufferId, + buffer_offset: wgt::BufferAddress, + staging_buffer_id: StagingBufferId, + ) -> Result<(), QueueWriteError> { + let queue = self.hub.queues.get(queue_id); + let buffer = self.hub.buffers.get(buffer_id); + let staging_buffer = self.hub.staging_buffers.remove(staging_buffer_id); + queue.write_staging_buffer(buffer, buffer_offset, staging_buffer) + } + + pub fn queue_validate_write_buffer( + &self, + queue_id: QueueId, + buffer_id: BufferId, + buffer_offset: u64, + buffer_size: wgt::BufferSize, + ) -> Result<(), QueueWriteError> { + let queue = self.hub.queues.get(queue_id); + let buffer = self.hub.buffers.get(buffer_id); + queue.validate_write_buffer(buffer, buffer_offset, buffer_size) + } + + pub fn queue_write_texture( + &self, + queue_id: QueueId, + destination: &wgt::TexelCopyTextureInfo, + data: &[u8], + data_layout: &wgt::TexelCopyBufferLayout, + size: &wgt::Extent3d, + ) -> Result<(), QueueWriteError> { + let queue = self.hub.queues.get(queue_id); + let texture = self.hub.textures.get(destination.texture); + let destination = wgt::TexelCopyTextureInfo { + texture, + mip_level: destination.mip_level, + origin: destination.origin, + aspect: destination.aspect, + }; + + queue.write_texture(destination, data, data_layout, size) + } + + pub fn queue_submit( + &self, + queue_id: QueueId, + command_buffer_ids: &[CommandBufferId], + ) -> Result { + let queue = self.hub.queues.get(queue_id); + let command_buffer_guard = self.hub.command_buffers.read(); + let command_buffers = command_buffer_ids + .iter() + .map(|id| command_buffer_guard.get(*id)) + .collect::>(); + drop(command_buffer_guard); + queue.submit(&command_buffers) + } + + pub fn queue_get_timestamp_period(&self, queue_id: QueueId) -> f32 { + let queue = self.hub.queues.get(queue_id); + + queue.get_timestamp_period() + } + + pub fn queue_on_submitted_work_done( + &self, + queue_id: QueueId, + closure: SubmittedWorkDoneClosure, + ) -> SubmissionIndex { + let queue = self.hub.queues.get(queue_id); + let result = queue.on_submitted_work_done(closure); + result.unwrap_or(0) // '0' means no wait is necessary + } + + pub fn queue_compact_blas( + &self, + queue_id: QueueId, + blas_id: BlasId, + id_in: Option, + ) -> (BlasId, Option, Option) { + let fid = self.hub.blas_s.prepare(id_in); + + let queue = self.hub.queues.get(queue_id); + let blas = self.hub.blas_s.get(blas_id); + + let (blas, error) = queue.compact_blas(&blas); + + let handle = blas.handle(); + let id = fid.assign(blas); + + (id, handle, error) + } +} diff --git a/wgpu-core-remote/src/global/ray_tracing.rs b/wgpu-core-remote/src/global/ray_tracing.rs new file mode 100644 index 00000000000..ef253c6c894 --- /dev/null +++ b/wgpu-core-remote/src/global/ray_tracing.rs @@ -0,0 +1,73 @@ +use wgpu_core::ray_tracing::{BlasPrepareCompactError, CreateBlasError, CreateTlasError}; +use wgpu_core::resource::{self, BlasCompactCallback, InvalidResourceError}; +use wgpu_core::SubmissionIndex; + +use crate::global::Global; +use crate::id::{self, BlasId, TlasId}; + +impl Global { + pub fn device_create_blas( + &self, + device_id: id::DeviceId, + desc: &resource::BlasDescriptor, + sizes: wgt::BlasGeometrySizeDescriptors, + id_in: Option, + ) -> (BlasId, Option, Option) { + let fid = self.hub.blas_s.prepare(id_in); + + let device = self.hub.devices.get(device_id); + + let (blas, error) = device.create_blas(desc, sizes); + + let handle = blas.handle(); + + let id = fid.assign(blas); + + (id, handle, error) + } + + pub fn device_create_tlas( + &self, + device_id: id::DeviceId, + desc: &resource::TlasDescriptor, + id_in: Option, + ) -> (TlasId, Option) { + let fid = self.hub.tlas_s.prepare(id_in); + + let device = self.hub.devices.get(device_id); + + let (tlas, error) = device.create_tlas(desc); + + let id = fid.assign(tlas); + + (id, error) + } + + pub fn blas_drop(&self, blas_id: BlasId) { + let _blas = self.hub.blas_s.remove(blas_id); + } + + pub fn tlas_drop(&self, tlas_id: TlasId) { + let _tlas = self.hub.tlas_s.remove(tlas_id); + } + + pub fn blas_prepare_compact_async( + &self, + blas_id: BlasId, + callback: Option, + ) -> Result { + let hub = &self.hub; + + let blas = hub.blas_s.get(blas_id); + + blas.prepare_compact_async(callback) + } + + pub fn ready_for_compaction(&self, blas_id: BlasId) -> Result { + let hub = &self.hub; + + let blas = hub.blas_s.get(blas_id); + + blas.ready_for_compaction() + } +} diff --git a/wgpu-core-remote/src/global/render_pass.rs b/wgpu-core-remote/src/global/render_pass.rs new file mode 100644 index 00000000000..a3d39de079d --- /dev/null +++ b/wgpu-core-remote/src/global/render_pass.rs @@ -0,0 +1,775 @@ +use alloc::borrow::Cow; +use alloc::sync::Arc; +use core::num::NonZeroU32; + +use parking_lot::Mutex; +use wgpu_core::command::{ + CommandEncoderError, EncoderStateError, PassStateError, PassTimestampWrites, RenderPass, + RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassError, + ResolvedRenderPassDescriptor, +}; +use wgpu_core::Label; +use wgt::{BufferAddress, BufferSize, Color, DynamicOffset, IndexFormat}; + +use crate::global::Global; +use crate::id; + +/// Describes the attachments of a render pass. +#[derive(Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct RenderPassDescriptor<'a> { + pub label: Label<'a>, + /// The color attachments of the render pass. + pub color_attachments: Cow<'a, [Option>]>, + /// The depth and stencil attachment of the render pass, if any. + pub depth_stencil_attachment: Option>, + /// Defines where and when timestamp values will be written for this pass. + pub timestamp_writes: Option>, + /// Defines where the occlusion query results will be stored for this pass. + pub occlusion_query_set: Option, + /// The multiview array layers that will be used + pub multiview_mask: Option, +} + +impl Global { + /// Creates a render pass. + /// + /// If creation fails, an invalid pass is returned. Attempting to record + /// commands into an invalid pass is permitted, but a validation error will + /// ultimately be generated when the parent encoder is finished, and it is + /// not possible to run any commands from the invalid pass. + /// + /// If successful, puts the encoder into the [`Locked`] state. + /// + /// [`Locked`]: crate::command::CommandEncoderStatus::Locked + pub fn command_encoder_begin_render_pass( + &self, + encoder_id: id::CommandEncoderId, + desc: &RenderPassDescriptor<'_>, + ) -> (RenderPass, Option) { + let hub = &self.hub; + + let cmd_enc = hub.command_encoders.get(encoder_id); + + let texture_views = hub.texture_views.read(); + let query_sets = hub.query_sets.read(); + + let desc = ResolvedRenderPassDescriptor { + label: desc.label.as_deref().map(Cow::Borrowed), + color_attachments: Cow::Owned( + desc.color_attachments + .iter() + .map(|at| { + at.as_ref().map(|at| RenderPassColorAttachment { + view: texture_views.get(at.view), + depth_slice: at.depth_slice, + resolve_target: at + .resolve_target + .as_ref() + .map(|rt| texture_views.get(*rt)), + load_op: at.load_op, + store_op: at.store_op, + }) + }) + .collect(), + ), + depth_stencil_attachment: desc.depth_stencil_attachment.as_ref().map(|at| { + RenderPassDepthStencilAttachment { + view: texture_views.get(at.view), + depth: at.depth.clone(), + stencil: at.stencil.clone(), + } + }), + timestamp_writes: desc + .timestamp_writes + .as_ref() + .map(|tw| PassTimestampWrites { + query_set: query_sets.get(tw.query_set), + beginning_of_pass_write_index: tw.beginning_of_pass_write_index, + end_of_pass_write_index: tw.end_of_pass_write_index, + }), + occlusion_query_set: desc + .occlusion_query_set + .as_ref() + .map(|query_set| query_sets.get(*query_set)), + multiview_mask: desc.multiview_mask, + }; + + drop(texture_views); + drop(query_sets); + + cmd_enc.begin_render_pass(desc) + } + + pub fn command_encoder_begin_render_pass_with_id( + &self, + encoder_id: id::CommandEncoderId, + desc: &RenderPassDescriptor<'_>, + id_in: Option, + ) -> (id::RenderPassEncoderId, Option) { + let hub = &self.hub; + let fid = hub.render_passes.prepare(id_in); + let (render_pass, error) = self.command_encoder_begin_render_pass(encoder_id, desc); + // no lock rank here because only one thread should be using renderpass + // and it's only used by id variants of render pass methods on global + // so no deadlock (or concurrent lock) should happen in practise + let id = fid.assign(Arc::new(Mutex::new(render_pass))); + (id, error) + } + + pub fn render_pass_end(&self, pass: &mut RenderPass) -> Result<(), EncoderStateError> { + pass.end() + } + + pub fn render_pass_end_with_id( + &self, + pass: id::RenderPassEncoderId, + ) -> Result<(), EncoderStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be accessed concurrently"); + self.render_pass_end(&mut pass) + } + + pub fn render_pass_drop(&self, pass: id::RenderPassEncoderId) { + self.hub.render_passes.remove(pass); + } +} + +impl Global { + pub fn render_pass_set_bind_group( + &self, + pass: &mut RenderPass, + index: u32, + bind_group_id: Option, + offsets: &[DynamicOffset], + ) -> Result<(), PassStateError> { + pass.set_bind_group( + index, + bind_group_id.map(|id| self.hub.bind_groups.get(id)), + offsets, + ) + } + + pub fn render_pass_set_bind_group_with_id( + &self, + pass: id::RenderPassEncoderId, + index: u32, + bind_group_id: Option, + offsets: &[DynamicOffset], + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_set_bind_group(&mut pass, index, bind_group_id, offsets) + } + + pub fn render_pass_set_pipeline( + &self, + pass: &mut RenderPass, + pipeline_id: id::RenderPipelineId, + ) -> Result<(), PassStateError> { + let pipeline = self.resolve_render_pipeline_id(pipeline_id); + pass.set_pipeline(pipeline) + } + + pub fn render_pass_set_pipeline_with_id( + &self, + pass: id::RenderPassEncoderId, + pipeline_id: id::RenderPipelineId, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_set_pipeline(&mut pass, pipeline_id) + } + + pub fn render_pass_set_index_buffer( + &self, + pass: &mut RenderPass, + buffer_id: id::BufferId, + index_format: IndexFormat, + offset: BufferAddress, + size: Option, + ) -> Result<(), PassStateError> { + pass.set_index_buffer( + self.resolve_buffer_id(buffer_id), + index_format, + offset, + size, + ) + } + + pub fn render_pass_set_index_buffer_with_id( + &self, + pass: id::RenderPassEncoderId, + buffer_id: id::BufferId, + index_format: IndexFormat, + offset: BufferAddress, + size: Option, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_set_index_buffer(&mut pass, buffer_id, index_format, offset, size) + } + + pub fn render_pass_set_vertex_buffer( + &self, + pass: &mut RenderPass, + slot: u32, + buffer_id: Option, + offset: BufferAddress, + size: Option, + ) -> Result<(), PassStateError> { + pass.set_vertex_buffer( + slot, + buffer_id.map(|id| self.resolve_buffer_id(id)), + offset, + size, + ) + } + + pub fn render_pass_set_vertex_buffer_with_id( + &self, + pass: id::RenderPassEncoderId, + slot: u32, + buffer_id: Option, + offset: BufferAddress, + size: Option, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_set_vertex_buffer(&mut pass, slot, buffer_id, offset, size) + } + + pub fn render_pass_set_blend_constant( + &self, + pass: &mut RenderPass, + color: Color, + ) -> Result<(), PassStateError> { + pass.set_blend_constant(color) + } + + pub fn render_pass_set_blend_constant_with_id( + &self, + pass: id::RenderPassEncoderId, + color: Color, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_set_blend_constant(&mut pass, color) + } + + pub fn render_pass_set_stencil_reference( + &self, + pass: &mut RenderPass, + value: u32, + ) -> Result<(), PassStateError> { + pass.set_stencil_reference(value) + } + + pub fn render_pass_set_stencil_reference_with_id( + &self, + pass: id::RenderPassEncoderId, + value: u32, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_set_stencil_reference(&mut pass, value) + } + + pub fn render_pass_set_viewport( + &self, + pass: &mut RenderPass, + x: f32, + y: f32, + w: f32, + h: f32, + depth_min: f32, + depth_max: f32, + ) -> Result<(), PassStateError> { + pass.set_viewport(x, y, w, h, depth_min, depth_max) + } + + pub fn render_pass_set_viewport_with_id( + &self, + pass: id::RenderPassEncoderId, + x: f32, + y: f32, + w: f32, + h: f32, + depth_min: f32, + depth_max: f32, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_set_viewport(&mut pass, x, y, w, h, depth_min, depth_max) + } + + pub fn render_pass_set_scissor_rect( + &self, + pass: &mut RenderPass, + x: u32, + y: u32, + w: u32, + h: u32, + ) -> Result<(), PassStateError> { + pass.set_scissor_rect(x, y, w, h) + } + + pub fn render_pass_set_scissor_rect_with_id( + &self, + pass: id::RenderPassEncoderId, + x: u32, + y: u32, + w: u32, + h: u32, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_set_scissor_rect(&mut pass, x, y, w, h) + } + + pub fn render_pass_set_immediates( + &self, + pass: &mut RenderPass, + offset: u32, + data: &[u8], + ) -> Result<(), PassStateError> { + pass.set_immediates(offset, data) + } + + pub fn render_pass_set_immediates_with_id( + &self, + pass: id::RenderPassEncoderId, + offset: u32, + data: &[u8], + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_set_immediates(&mut pass, offset, data) + } + + pub fn render_pass_draw( + &self, + pass: &mut RenderPass, + vertex_count: u32, + instance_count: u32, + first_vertex: u32, + first_instance: u32, + ) -> Result<(), PassStateError> { + pass.draw(vertex_count, instance_count, first_vertex, first_instance) + } + + pub fn render_pass_draw_with_id( + &self, + pass: id::RenderPassEncoderId, + vertex_count: u32, + instance_count: u32, + first_vertex: u32, + first_instance: u32, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_draw( + &mut pass, + vertex_count, + instance_count, + first_vertex, + first_instance, + ) + } + + pub fn render_pass_draw_indexed( + &self, + pass: &mut RenderPass, + index_count: u32, + instance_count: u32, + first_index: u32, + base_vertex: i32, + first_instance: u32, + ) -> Result<(), PassStateError> { + pass.draw_indexed( + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + ) + } + + pub fn render_pass_draw_indexed_with_id( + &self, + pass: id::RenderPassEncoderId, + index_count: u32, + instance_count: u32, + first_index: u32, + base_vertex: i32, + first_instance: u32, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_draw_indexed( + &mut pass, + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + ) + } + + pub fn render_pass_draw_mesh_tasks( + &self, + pass: &mut RenderPass, + group_count_x: u32, + group_count_y: u32, + group_count_z: u32, + ) -> Result<(), RenderPassError> { + pass.draw_mesh_tasks(group_count_x, group_count_y, group_count_z) + } + + pub fn render_pass_draw_indirect( + &self, + pass: &mut RenderPass, + buffer_id: id::BufferId, + offset: BufferAddress, + ) -> Result<(), PassStateError> { + pass.draw_indirect(self.resolve_buffer_id(buffer_id), offset) + } + + pub fn render_pass_draw_indirect_with_id( + &self, + pass: id::RenderPassEncoderId, + buffer_id: id::BufferId, + offset: BufferAddress, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_draw_indirect(&mut pass, buffer_id, offset) + } + + pub fn render_pass_draw_indexed_indirect( + &self, + pass: &mut RenderPass, + buffer_id: id::BufferId, + offset: BufferAddress, + ) -> Result<(), PassStateError> { + pass.draw_indexed_indirect(self.resolve_buffer_id(buffer_id), offset) + } + + pub fn render_pass_draw_indexed_indirect_with_id( + &self, + pass: id::RenderPassEncoderId, + buffer_id: id::BufferId, + offset: BufferAddress, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_draw_indexed_indirect(&mut pass, buffer_id, offset) + } + + pub fn render_pass_draw_mesh_tasks_indirect( + &self, + pass: &mut RenderPass, + buffer_id: id::BufferId, + offset: BufferAddress, + ) -> Result<(), RenderPassError> { + pass.draw_mesh_tasks_indirect(self.resolve_buffer_id(buffer_id), offset) + } + + pub fn render_pass_multi_draw_indirect( + &self, + pass: &mut RenderPass, + buffer_id: id::BufferId, + offset: BufferAddress, + count: u32, + ) -> Result<(), PassStateError> { + pass.multi_draw_indirect(self.resolve_buffer_id(buffer_id), offset, count) + } + + pub fn render_pass_multi_draw_indexed_indirect( + &self, + pass: &mut RenderPass, + buffer_id: id::BufferId, + offset: BufferAddress, + count: u32, + ) -> Result<(), PassStateError> { + pass.multi_draw_indexed_indirect(self.resolve_buffer_id(buffer_id), offset, count) + } + + pub fn render_pass_multi_draw_mesh_tasks_indirect( + &self, + pass: &mut RenderPass, + buffer_id: id::BufferId, + offset: BufferAddress, + count: u32, + ) -> Result<(), RenderPassError> { + pass.multi_draw_mesh_tasks_indirect(self.resolve_buffer_id(buffer_id), offset, count) + } + + pub fn render_pass_multi_draw_indirect_count( + &self, + pass: &mut RenderPass, + buffer_id: id::BufferId, + offset: BufferAddress, + count_buffer_id: id::BufferId, + count_buffer_offset: BufferAddress, + max_count: u32, + ) -> Result<(), PassStateError> { + pass.multi_draw_indirect_count( + self.resolve_buffer_id(buffer_id), + offset, + self.resolve_buffer_id(count_buffer_id), + count_buffer_offset, + max_count, + ) + } + + pub fn render_pass_multi_draw_indexed_indirect_count( + &self, + pass: &mut RenderPass, + buffer_id: id::BufferId, + offset: BufferAddress, + count_buffer_id: id::BufferId, + count_buffer_offset: BufferAddress, + max_count: u32, + ) -> Result<(), PassStateError> { + pass.multi_draw_indexed_indirect_count( + self.resolve_buffer_id(buffer_id), + offset, + self.resolve_buffer_id(count_buffer_id), + count_buffer_offset, + max_count, + ) + } + + pub fn render_pass_multi_draw_mesh_tasks_indirect_count( + &self, + pass: &mut RenderPass, + buffer_id: id::BufferId, + offset: BufferAddress, + count_buffer_id: id::BufferId, + count_buffer_offset: BufferAddress, + max_count: u32, + ) -> Result<(), RenderPassError> { + pass.multi_draw_mesh_tasks_indirect_count( + self.resolve_buffer_id(buffer_id), + offset, + self.resolve_buffer_id(count_buffer_id), + count_buffer_offset, + max_count, + ) + } + + pub fn render_pass_push_debug_group( + &self, + pass: &mut RenderPass, + label: &str, + color: u32, + ) -> Result<(), PassStateError> { + pass.push_debug_group(label, color) + } + + pub fn render_pass_push_debug_group_with_id( + &self, + pass: id::RenderPassEncoderId, + label: &str, + color: u32, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_push_debug_group(&mut pass, label, color) + } + + pub fn render_pass_pop_debug_group(&self, pass: &mut RenderPass) -> Result<(), PassStateError> { + pass.pop_debug_group() + } + + pub fn render_pass_pop_debug_group_with_id( + &self, + pass: id::RenderPassEncoderId, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_pop_debug_group(&mut pass) + } + + pub fn render_pass_insert_debug_marker( + &self, + pass: &mut RenderPass, + label: &str, + color: u32, + ) -> Result<(), PassStateError> { + pass.insert_debug_marker(label, color) + } + + pub fn render_pass_insert_debug_marker_with_id( + &self, + pass: id::RenderPassEncoderId, + label: &str, + color: u32, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_insert_debug_marker(&mut pass, label, color) + } + + pub fn render_pass_write_timestamp( + &self, + pass: &mut RenderPass, + query_set_id: id::QuerySetId, + query_index: u32, + ) -> Result<(), PassStateError> { + pass.write_timestamp(self.resolve_query_set_id(query_set_id), query_index) + } + + pub fn render_pass_write_timestamp_with_id( + &self, + pass: id::RenderPassEncoderId, + query_set_id: id::QuerySetId, + query_index: u32, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_write_timestamp(&mut pass, query_set_id, query_index) + } + + pub fn render_pass_begin_occlusion_query( + &self, + pass: &mut RenderPass, + query_index: u32, + ) -> Result<(), PassStateError> { + pass.begin_occlusion_query(query_index) + } + + pub fn render_pass_begin_occlusion_query_with_id( + &self, + pass: id::RenderPassEncoderId, + query_index: u32, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_begin_occlusion_query(&mut pass, query_index) + } + + pub fn render_pass_end_occlusion_query( + &self, + pass: &mut RenderPass, + ) -> Result<(), PassStateError> { + pass.end_occlusion_query() + } + + pub fn render_pass_end_occlusion_query_with_id( + &self, + pass: id::RenderPassEncoderId, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_end_occlusion_query(&mut pass) + } + + pub fn render_pass_begin_pipeline_statistics_query( + &self, + pass: &mut RenderPass, + query_set_id: id::QuerySetId, + query_index: u32, + ) -> Result<(), PassStateError> { + pass.begin_pipeline_statistics_query(self.resolve_query_set_id(query_set_id), query_index) + } + + pub fn render_pass_begin_pipeline_statistics_query_with_id( + &self, + pass: id::RenderPassEncoderId, + query_set_id: id::QuerySetId, + query_index: u32, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_begin_pipeline_statistics_query(&mut pass, query_set_id, query_index) + } + + pub fn render_pass_end_pipeline_statistics_query( + &self, + pass: &mut RenderPass, + ) -> Result<(), PassStateError> { + pass.end_pipeline_statistics_query() + } + + pub fn render_pass_end_pipeline_statistics_query_with_id( + &self, + pass: id::RenderPassEncoderId, + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_end_pipeline_statistics_query(&mut pass) + } + + pub fn render_pass_execute_bundles( + &self, + pass: &mut RenderPass, + render_bundle_ids: &[id::RenderBundleId], + ) -> Result<(), PassStateError> { + let hub = &self.hub; + let bundles = hub.render_bundles.read(); + let render_bundles = render_bundle_ids + .iter() + .map(|&id| bundles.get(id)) + .collect::>(); + + pass.execute_bundles(&render_bundles) + } + + pub fn render_pass_execute_bundles_with_id( + &self, + pass: id::RenderPassEncoderId, + render_bundle_ids: &[id::RenderBundleId], + ) -> Result<(), PassStateError> { + let pass = self.hub.render_passes.get(pass); + let mut pass = pass + .try_lock() + .expect("RenderPasses should not be used concurrently"); + self.render_pass_execute_bundles(&mut pass, render_bundle_ids) + } +} diff --git a/wgpu-core/src/hub.rs b/wgpu-core-remote/src/hub.rs similarity index 96% rename from wgpu-core/src/hub.rs rename to wgpu-core-remote/src/hub.rs index ce61538d02b..3f235e1fc9b 100644 --- a/wgpu-core/src/hub.rs +++ b/wgpu-core-remote/src/hub.rs @@ -119,16 +119,15 @@ when replaying a trace. use alloc::sync::Arc; use core::fmt::Debug; -use crate::{ +use crate::registry::{Registry, RegistryReport}; +use wgpu_core::{ binding_model::{BindGroup, BindGroupLayout, PipelineLayout}, command::{ CommandBuffer, CommandEncoder, ComputePass, RenderBundle, RenderBundleEncoder, RenderPass, }, device::{queue::Queue, Device}, instance::Adapter, - lock::rank, pipeline::{ComputePipeline, PipelineCache, RenderPipeline, ShaderModule}, - registry::{Registry, RegistryReport}, resource::{ Blas, Buffer, ExternalTexture, QuerySet, Sampler, StagingBuffer, Texture, TextureView, Tlas, }, @@ -233,22 +232,22 @@ impl Hub { pipeline_layouts: Registry::new(), shader_modules: Registry::new(), bind_group_layouts: Registry::new(), - bind_groups: Registry::with_rank(rank::HUB_BIND_GROUPS), + bind_groups: Registry::new(), command_encoders: Registry::new(), command_buffers: Registry::new(), render_bundles: Registry::new(), - render_pipelines: Registry::with_rank(rank::HUB_RENDER_PIPELINES), + render_pipelines: Registry::new(), compute_pipelines: Registry::new(), pipeline_caches: Registry::new(), query_sets: Registry::new(), buffers: Registry::new(), staging_buffers: Registry::new(), textures: Registry::new(), - texture_views: Registry::with_rank(rank::HUB_TEXTURE_VIEWS), - external_textures: Registry::with_rank(rank::HUB_EXTERNAL_TEXTURES), - samplers: Registry::with_rank(rank::HUB_SAMPLERS), + texture_views: Registry::new(), + external_textures: Registry::new(), + samplers: Registry::new(), blas_s: Registry::new(), - tlas_s: Registry::with_rank(rank::HUB_TLAS), + tlas_s: Registry::new(), render_passes: Registry::new(), compute_passes: Registry::new(), render_bundle_encoders: Registry::new(), diff --git a/wgpu-core-remote/src/id.rs b/wgpu-core-remote/src/id.rs new file mode 100644 index 00000000000..a375bb60de3 --- /dev/null +++ b/wgpu-core-remote/src/id.rs @@ -0,0 +1,278 @@ +use crate::{Epoch, Index}; +use core::{ + cmp::Ordering, + fmt::{self, Debug}, + hash::Hash, + marker::PhantomData, + num::NonZeroU64, +}; +pub use wgpu_core::id::markers; +pub use wgpu_core::id::Marker; + +const _: () = { + if size_of::() != 4 { + panic!() + } +}; +const _: () = { + if size_of::() != 4 { + panic!() + } +}; +const _: () = { + if size_of::() != 8 { + panic!() + } +}; + +/// The raw underlying representation of an identifier. +#[repr(transparent)] +#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(into = "SerialId"))] +#[cfg_attr( + feature = "serde", + derive(serde::Deserialize), + serde(try_from = "SerialId") +)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RawId(NonZeroU64); + +impl RawId { + /// Zip together an identifier and return its raw underlying representation. + /// + /// # Panics + /// + /// If both ID components are zero. + pub fn zip(index: Index, epoch: Epoch) -> RawId { + let v = (index as u64) | ((epoch as u64) << 32); + Self(NonZeroU64::new(v).expect("IDs may not be zero")) + } + + /// Unzip a raw identifier into its components. + pub fn unzip(self) -> (Index, Epoch) { + (self.0.get() as Index, (self.0.get() >> 32) as Epoch) + } +} + +/// An identifier for a wgpu object. +/// +/// An `Id` value identifies a value stored in a [`Global`]'s [`Hub`]. +/// +/// [`Global`]: crate::global::Global +/// [`Hub`]: crate::hub::Hub +#[repr(transparent)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(transparent))] +pub struct Id(RawId, PhantomData); + +// This type represents Id in a more readable (and editable) way. +#[cfg(feature = "serde")] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug)] +pub enum SerialId { + // The only variant forces RON to not ignore "Id" + Id(Index, Epoch), +} + +#[cfg(feature = "serde")] +impl From for SerialId { + fn from(id: RawId) -> Self { + let (index, epoch) = id.unzip(); + Self::Id(index, epoch) + } +} + +#[cfg(feature = "serde")] +pub struct ZeroIdError; + +#[cfg(feature = "serde")] +impl fmt::Display for ZeroIdError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "IDs may not be zero") + } +} + +#[cfg(feature = "serde")] +impl TryFrom for RawId { + type Error = ZeroIdError; + fn try_from(id: SerialId) -> Result { + let SerialId::Id(index, epoch) = id; + if index == 0 && epoch == 0 { + Err(ZeroIdError) + } else { + Ok(RawId::zip(index, epoch)) + } + } +} + +impl Id +where + T: Marker, +{ + /// # Safety + /// + /// The raw id must be valid for the type. + pub unsafe fn from_raw(raw: RawId) -> Self { + Self(raw, PhantomData) + } + + /// Coerce the identifiers into its raw underlying representation. + pub fn into_raw(self) -> RawId { + self.0 + } + + #[inline] + pub fn zip(index: Index, epoch: Epoch) -> Self { + Id(RawId::zip(index, epoch), PhantomData) + } + + #[inline] + pub fn unzip(self) -> (Index, Epoch) { + self.0.unzip() + } +} + +impl Copy for Id where T: Marker {} + +impl Clone for Id +where + T: Marker, +{ + #[inline] + fn clone(&self) -> Self { + *self + } +} + +impl Debug for Id +where + T: Marker, +{ + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + let (index, epoch) = self.unzip(); + write!(formatter, "{}Id({index},{epoch})", T::TYPE)?; + Ok(()) + } +} + +impl Hash for Id +where + T: Marker, +{ + #[inline] + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +impl PartialEq for Id +where + T: Marker, +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for Id where T: Marker {} + +impl PartialOrd for Id +where + T: Marker, +{ + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Id +where + T: Marker, +{ + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.0.cmp(&other.0) + } +} + +/// Define identifiers for each resource. +macro_rules! ids { + ($( + $(#[$($meta:meta)*])* + pub type $name:ident $marker:ident; + )*) => { + $( + $(#[$($meta)*])* + pub type $name = Id; + )* + } +} + +ids! { + pub type AdapterId Adapter; + pub type SurfaceId Surface; + pub type DeviceId Device; + pub type QueueId Queue; + pub type BufferId Buffer; + pub type StagingBufferId StagingBuffer; + pub type TextureViewId TextureView; + pub type TextureId Texture; + pub type ExternalTextureId ExternalTexture; + pub type SamplerId Sampler; + pub type BindGroupLayoutId BindGroupLayout; + pub type PipelineLayoutId PipelineLayout; + pub type BindGroupId BindGroup; + pub type ShaderModuleId ShaderModule; + pub type RenderPipelineId RenderPipeline; + pub type ComputePipelineId ComputePipeline; + pub type PipelineCacheId PipelineCache; + pub type CommandEncoderId CommandEncoder; + pub type CommandBufferId CommandBuffer; + pub type RenderPassEncoderId RenderPassEncoder; + pub type ComputePassEncoderId ComputePassEncoder; + pub type RenderBundleEncoderId RenderBundleEncoder; + pub type RenderBundleId RenderBundle; + pub type QuerySetId QuerySet; + pub type BlasId Blas; + pub type TlasId Tlas; +} + +#[test] +fn test_id() { + struct TestMarker; + + impl Marker for TestMarker { + const TYPE: &'static str = "TestMarker"; + } + + let indexes = [0, Index::MAX / 2 - 1, Index::MAX / 2 + 1, Index::MAX]; + let epochs = [1, Epoch::MAX / 2 - 1, Epoch::MAX / 2 + 1, Epoch::MAX]; + for &i in &indexes { + for &e in &epochs { + let id = Id::::zip(i, e); + let (index, epoch) = id.unzip(); + assert_eq!(index, i); + assert_eq!(epoch, e); + } + } +} + +/// Reference wgpu objects via numeric IDs assigned by [`crate::identity::IdentityManager`]. +#[derive(Clone, Debug)] +pub struct IdReferences; + +impl wgpu_core::command::ReferenceType for IdReferences { + type Buffer = BufferId; + type Surface = SurfaceId; + type Texture = TextureId; + type TextureView = TextureViewId; + type ExternalTexture = ExternalTextureId; + type QuerySet = QuerySetId; + type BindGroup = BindGroupId; + type RenderPipeline = RenderPipelineId; + type RenderBundle = RenderBundleId; + type ComputePipeline = ComputePipelineId; + type Blas = BlasId; + type Tlas = TlasId; +} diff --git a/wgpu-core/src/identity.rs b/wgpu-core-remote/src/identity.rs similarity index 92% rename from wgpu-core/src/identity.rs rename to wgpu-core-remote/src/identity.rs index ea2f6326e56..524a06568f6 100644 --- a/wgpu-core/src/identity.rs +++ b/wgpu-core-remote/src/identity.rs @@ -1,11 +1,8 @@ use alloc::vec::Vec; use core::{fmt::Debug, marker::PhantomData}; +use parking_lot::Mutex; -use crate::{ - id::{Id, Marker}, - lock::{rank, Mutex}, - Epoch, Index, -}; +use crate::{id::Id, id::Marker, Epoch, Index}; #[derive(Copy, Clone, Debug, PartialEq)] enum IdSource { @@ -135,15 +132,12 @@ impl IdentityManager { impl IdentityManager { pub fn new() -> Self { Self { - values: Mutex::new( - rank::IDENTITY_MANAGER_VALUES, - IdentityValues { - free: Vec::new(), - next_index: 0, - count: 0, - id_source: IdSource::None, - }, - ), + values: Mutex::new(IdentityValues { + free: Vec::new(), + next_index: 0, + count: 0, + id_source: IdSource::None, + }), _phantom: PhantomData, } } diff --git a/wgpu-core-remote/src/lib.rs b/wgpu-core-remote/src/lib.rs new file mode 100644 index 00000000000..104b002d3bb --- /dev/null +++ b/wgpu-core-remote/src/lib.rs @@ -0,0 +1,52 @@ +#![allow( + // It is much clearer to assert negative conditions with eq! false + clippy::bool_assert_comparison, + // We don't use syntax sugar where it's not necessary. + clippy::match_like_matches_macro, + // Redundant matching is more explicit. + clippy::redundant_pattern_matching, + // Explicit lifetimes are often easier to reason about. + clippy::needless_lifetimes, + // No need for defaults in the internal types. + clippy::new_without_default, + // Needless updates are more scalable, easier to play with features. + clippy::needless_update, + // Need many arguments for some core functions to be able to re-use code in many situations. + clippy::too_many_arguments, + // It gets in the way a lot and does not prevent bugs in practice. + clippy::pattern_type_mismatch, + // `wgpu-core` isn't entirely user-facing, so it's useful to document internal items. + rustdoc::private_intra_doc_links, +)] +#![expect(missing_debug_implementations, reason = "TODO")] +#![warn( + clippy::alloc_instead_of_core, + clippy::ptr_as_ptr, + clippy::std_instead_of_alloc, + clippy::std_instead_of_core, + trivial_casts, + trivial_numeric_casts, + unsafe_op_in_unsafe_fn, + unused_extern_crates, + unused_qualifications +)] + +extern crate alloc; +extern crate wgpu_hal as hal; +extern crate wgpu_types as wgt; + +pub type TexelCopyBufferInfo = wgt::TexelCopyBufferInfo; +pub type TexelCopyTextureInfo = wgt::TexelCopyTextureInfo; +pub type CopyExternalImageDestInfo = wgt::CopyExternalImageDestInfo; + +pub type Command = wgpu_core::command::Command; + +pub mod global; +pub mod hub; +pub mod id; +pub mod identity; +pub mod registry; +pub mod storage; + +type Index = u32; +type Epoch = u32; diff --git a/wgpu-core/src/registry.rs b/wgpu-core-remote/src/registry.rs similarity index 93% rename from wgpu-core/src/registry.rs rename to wgpu-core-remote/src/registry.rs index 6bc954f6b09..fbb12061173 100644 --- a/wgpu-core/src/registry.rs +++ b/wgpu-core-remote/src/registry.rs @@ -1,12 +1,9 @@ use alloc::sync::Arc; +use parking_lot::{RwLock, RwLockReadGuard}; use crate::{ id::Id, identity::IdentityManager, - lock::{ - rank::{self, LockRank}, - RwLock, RwLockReadGuard, - }, storage::{Element, Storage, StorageItem}, }; @@ -49,13 +46,9 @@ pub(crate) struct Registry { impl Registry { pub(crate) fn new() -> Self { - Self::with_rank(rank::HUB_OTHER) - } - - pub(crate) fn with_rank(rank: LockRank) -> Self { Self { identity: Arc::new(IdentityManager::new()), - storage: RwLock::new(rank, Storage::new()), + storage: RwLock::new(Storage::new()), } } } @@ -137,8 +130,9 @@ impl Registry { #[cfg(test)] mod tests { use super::Registry; - use crate::{id::Marker, resource::ResourceType, storage::StorageItem}; + use crate::{id::Marker, storage::StorageItem}; use alloc::sync::Arc; + use wgpu_core::resource::ResourceType; struct TestData; struct TestDataId; diff --git a/wgpu-core-remote/src/storage.rs b/wgpu-core-remote/src/storage.rs new file mode 100644 index 00000000000..423edc45f84 --- /dev/null +++ b/wgpu-core-remote/src/storage.rs @@ -0,0 +1,201 @@ +use alloc::vec::Vec; +use core::mem; + +use crate::id::Id; +use crate::{Epoch, Index}; +pub use wgpu_core::storage::StorageItem; + +/// An entry in a `Storage::map` table. +#[derive(Debug)] +pub(crate) enum Element +where + T: StorageItem, +{ + /// There are no live ids with this index. + Vacant, + + /// There is one live id with this index, allocated at the given + /// epoch. + Occupied(T, Epoch), +} + +/// A table of `T` values indexed by the id type `I`. +/// +/// `Storage` implements [`core::ops::Index`], accepting `Id` values as +/// indices. +/// +/// The table is represented as a vector indexed by the ids' index +/// values, so you should use an id allocator like `IdentityManager` +/// that keeps the index values dense and close to zero. +#[derive(Debug)] +pub(crate) struct Storage +where + T: StorageItem, +{ + pub(crate) map: Vec>, +} + +impl Storage +where + T: StorageItem, +{ + pub(crate) fn new() -> Self { + Self { map: Vec::new() } + } +} + +impl Storage +where + T: StorageItem, +{ + pub(crate) fn insert(&mut self, id: Id, value: T) { + let (index, epoch) = id.unzip(); + let index = index as usize; + if index >= self.map.len() { + self.map.resize_with(index + 1, || Element::Vacant); + } + match mem::replace(&mut self.map[index], Element::Occupied(value, epoch)) { + Element::Vacant => {} + Element::Occupied(_, storage_epoch) => { + panic!( + "Cannot insert {id:?}, found existing resource {other:?}", + other = Id::::zip(index as Index, storage_epoch), + ); + } + } + } + + pub(crate) fn remove(&mut self, id: Id) -> T { + let (index, epoch) = id.unzip(); + let stored = self.map.get_mut(index as usize); + match stored.map(|stored| mem::replace(stored, Element::Vacant)) { + Some(Element::Occupied(value, storage_epoch)) => { + assert_eq!( + epoch, + storage_epoch, + "Cannot remove {id:?}, found other resource {other:?}", + other = Id::::zip(index, storage_epoch), + ); + value + } + None | Some(Element::Vacant) => { + panic!("Cannot remove non-existent resource {id:?}"); + } + } + } + + #[allow(dead_code)] + pub(crate) fn iter(&self) -> impl Iterator, &T)> { + self.map + .iter() + .enumerate() + .filter_map(move |(index, x)| match *x { + Element::Occupied(ref value, storage_epoch) => { + Some((Id::zip(index as Index, storage_epoch), value)) + } + _ => None, + }) + } +} + +impl Storage +where + T: StorageItem + Clone, +{ + /// Get an owned reference to an item. + /// Panics if there is an epoch mismatch, the entry is empty or in error. + pub(crate) fn get(&self, id: Id) -> T { + let (index, epoch) = id.unzip(); + let (result, storage_epoch) = match self.map.get(index as usize) { + Some(&Element::Occupied(ref v, epoch)) => (v.clone(), epoch), + None | Some(&Element::Vacant) => { + panic!("Cannot get non-existent resource {id:?}"); + } + }; + assert_eq!( + epoch, + storage_epoch, + "Cannot get {id:?}, found other resource {other:?}", + other = Id::::zip(index, storage_epoch), + ); + result + } +} + +#[cfg(test)] +mod tests { + use crate::id::Marker; + + use super::*; + + #[derive(Clone, Debug)] + struct TestItem; + + impl wgpu_core::resource::ResourceType for TestItem { + const TYPE: &'static str = "TestItem"; + } + + struct TestMarker; + + impl Marker for TestMarker { + const TYPE: &'static str = "TestMarker"; + } + + impl StorageItem for TestItem { + type Marker = TestMarker; + } + + fn id(index: Index, epoch: Epoch) -> Id { + Id::zip(index, epoch) + } + + #[test] + #[should_panic( + expected = "Cannot insert UntypedId(0,1), found existing resource UntypedId(0,1)" + )] + fn insert_occupied_same_epoch() { + let mut storage = Storage::new(); + storage.insert(id(0, 1), TestItem); + storage.insert(id(0, 1), TestItem); + } + + #[test] + #[should_panic( + expected = "Cannot insert UntypedId(0,2), found existing resource UntypedId(0,1)" + )] + fn insert_occupied_different_epoch() { + let mut storage = Storage::new(); + storage.insert(id(0, 1), TestItem); + storage.insert(id(0, 2), TestItem); + } + + #[test] + #[should_panic(expected = "Cannot remove UntypedId(0,2), found other resource UntypedId(0,1)")] + fn remove_epoch_mismatch() { + let mut storage = Storage::new(); + storage.insert(id(0, 1), TestItem); + storage.remove(id(0, 2)); + } + + #[test] + #[should_panic(expected = "Cannot remove non-existent resource UntypedId(0,1)")] + fn remove_vacant() { + let mut storage = Storage::::new(); + storage.remove(id(0, 1)); + } + + #[test] + #[should_panic(expected = "Cannot get non-existent resource UntypedId(0,1)")] + fn get_vacant() { + let storage = Storage::::new(); + storage.get(id(0, 1)); + } + + #[test] + #[should_panic(expected = "Cannot get UntypedId(0,2), found other resource UntypedId(0,1)")] + fn get_epoch_mismatch() { + let mut storage = Storage::new(); + storage.insert(id(0, 1), TestItem); + storage.get(id(0, 2)); + } +} diff --git a/wgpu-core/src/as_hal.rs b/wgpu-core/src/as_hal.rs index f71a21e5d9f..978115ffa7e 100644 --- a/wgpu-core/src/as_hal.rs +++ b/wgpu-core/src/as_hal.rs @@ -3,16 +3,7 @@ use core::{mem::ManuallyDrop, ops::Deref}; use alloc::sync::Arc; use hal::DynResource; -use crate::{ - global::Global, - id::{ - AdapterId, BlasId, BufferId, CommandEncoderId, DeviceId, QueueId, SurfaceId, TextureId, - TextureViewId, TlasId, - }, - lock::RankData, - resource::RawResourceAccess, - snatch::SnatchGuard, -}; +use crate::{lock::RankData, resource::RawResourceAccess, snatch::SnatchGuard}; /// A guard which holds alive a wgpu-core resource and dereferences to the Hal type. struct SimpleResourceGuard { @@ -301,164 +292,3 @@ impl crate::resource::Tlas { SnatchableResourceGuard::new(self) } } - -impl Global { - /// # Safety - /// - /// - The raw buffer handle must not be manually destroyed - pub unsafe fn buffer_as_hal( - &self, - id: BufferId, - ) -> Option> { - let hub = &self.hub; - - let buffer = hub.buffers.get(id); - - unsafe { buffer.as_hal::() } - } - - /// # Safety - /// - /// - The raw texture handle must not be manually destroyed - pub unsafe fn texture_as_hal( - &self, - id: TextureId, - ) -> Option> { - let hub = &self.hub; - - let texture = hub.textures.get(id); - - unsafe { texture.as_hal::() } - } - - /// # Safety - /// - /// - The raw texture view handle must not be manually destroyed - pub unsafe fn texture_view_as_hal( - &self, - id: TextureViewId, - ) -> Option> { - let hub = &self.hub; - - let view = hub.texture_views.get(id); - - unsafe { view.as_hal::() } - } - - /// # Safety - /// - /// - The raw adapter handle must not be manually destroyed - pub unsafe fn adapter_as_hal( - &self, - id: AdapterId, - ) -> Option> { - let hub = &self.hub; - let adapter = hub.adapters.get(id); - - unsafe { adapter.as_hal::() } - } - - /// # Safety - /// - /// - The raw device handle must not be manually destroyed - pub unsafe fn device_as_hal( - &self, - id: DeviceId, - ) -> Option> { - let device = self.hub.devices.get(id); - - unsafe { device.as_hal::() } - } - - /// # Safety - /// - /// - The raw fence handle must not be manually destroyed - pub unsafe fn device_fence_as_hal( - &self, - id: DeviceId, - ) -> Option> { - let device = self.hub.devices.get(id); - - unsafe { device.fence_as_hal::() } - } - - /// # Safety - /// - /// - The raw surface handle must not be manually destroyed - pub unsafe fn surface_as_hal( - &self, - id: SurfaceId, - ) -> Option> { - let surface = self.surfaces.get(id); - - unsafe { surface.as_hal::() } - } - - /// Encode commands using the raw HAL command encoder. - /// - /// # Panics - /// - /// If the command encoder has already been used with the wgpu encoding API. - /// - /// # Safety - /// - /// - The raw command encoder handle must not be manually destroyed - pub unsafe fn command_encoder_as_hal_mut< - A: hal::Api, - F: FnOnce(Option<&mut A::CommandEncoder>) -> R, - R, - >( - &self, - id: CommandEncoderId, - hal_command_encoder_callback: F, - ) -> R { - let hub = &self.hub; - - let cmd_enc = hub.command_encoders.get(id); - unsafe { cmd_enc.as_hal_mut::(hal_command_encoder_callback) } - } - - /// # Safety - /// - /// - The raw queue handle must not be manually destroyed - pub unsafe fn queue_as_hal( - &self, - id: QueueId, - ) -> Option> { - let queue = self.hub.queues.get(id); - - unsafe { queue.as_hal::() } - } - - /// # Safety - /// - /// - The raw blas handle must not be manually destroyed - pub unsafe fn blas_as_hal( - &self, - id: BlasId, - ) -> Option> { - profiling::scope!("Blas::as_hal"); - - let hub = &self.hub; - - let blas = hub.blas_s.get(id); - - unsafe { blas.as_hal::() } - } - - /// # Safety - /// - /// - The raw tlas handle must not be manually destroyed - pub unsafe fn tlas_as_hal( - &self, - id: TlasId, - ) -> Option> { - profiling::scope!("Tlas::as_hal"); - - let hub = &self.hub; - - let tlas = hub.tlas_s.get(id); - - unsafe { tlas.as_hal::() } - } -} diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index a361b08e6e3..3e41301d77d 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -111,7 +111,7 @@ use crate::{ AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures, RenderPassContext, }, - id, impl_resource_type, impl_storage_item, + impl_resource_type, impl_storage_item, init_tracker::{BufferInitTrackerAction, MemoryInitKind, TextureInitTrackerAction}, pipeline::{PipelineFlags, RenderPipeline}, resource::{ @@ -1833,333 +1833,3 @@ where } } } - -impl crate::global::Global { - pub fn render_bundle_encoder_set_bind_group( - &self, - bundle: &mut RenderBundleEncoder, - index: u32, - bind_group_id: Option, - offsets: &[wgt::DynamicOffset], - ) -> Result<(), PassStateError> { - 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( - &self, - bundle_encoder: id::RenderBundleEncoderId, - index: u32, - bind_group_id: Option, - offsets: &[wgt::DynamicOffset], - ) -> Result<(), PassStateError> { - let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); - - let mut bundle_encoder = bundle_encoder - .try_lock() - .expect("RenderBundleEncoders should not be accessed concurrently"); - - 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( - &self, - bundle: &mut RenderBundleEncoder, - pipeline_id: id::RenderPipelineId, - ) -> Result<(), PassStateError> { - bundle.set_pipeline(self.hub.render_pipelines.get(pipeline_id)) - } - - pub fn render_bundle_encoder_set_pipeline_with_id( - &self, - bundle_encoder: id::RenderBundleEncoderId, - pipeline_id: id::RenderPipelineId, - ) -> Result<(), PassStateError> { - let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); - - let mut bundle_encoder = bundle_encoder - .try_lock() - .expect("RenderBundleEncoders should not be accessed concurrently"); - - bundle_encoder.set_pipeline(self.hub.render_pipelines.get(pipeline_id)) - } - - pub fn render_bundle_encoder_set_vertex_buffer( - &self, - bundle: &mut RenderBundleEncoder, - slot: u32, - buffer_id: Option, - offset: wgt::BufferAddress, - size: Option, - ) -> Result<(), PassStateError> { - 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( - &self, - bundle_encoder: id::RenderBundleEncoderId, - slot: u32, - buffer_id: Option, - offset: wgt::BufferAddress, - size: Option, - ) -> Result<(), PassStateError> { - let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); - - let mut bundle_encoder = bundle_encoder - .try_lock() - .expect("RenderBundleEncoders should not be accessed concurrently"); - - 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( - &self, - encoder: &mut RenderBundleEncoder, - buffer: id::BufferId, - index_format: wgt::IndexFormat, - offset: wgt::BufferAddress, - size: Option, - ) -> Result<(), PassStateError> { - encoder.set_index_buffer(self.hub.buffers.get(buffer), index_format, offset, size) - } - - pub fn render_bundle_encoder_set_index_buffer_with_id( - &self, - bundle_encoder: id::RenderBundleEncoderId, - buffer: id::BufferId, - index_format: wgt::IndexFormat, - offset: wgt::BufferAddress, - size: Option, - ) -> Result<(), PassStateError> { - let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); - - let mut bundle_encoder = bundle_encoder - .try_lock() - .expect("RenderBundleEncoders should not be accessed concurrently"); - - bundle_encoder.set_index_buffer(self.hub.buffers.get(buffer), index_format, offset, size) - } - - pub fn render_bundle_encoder_set_immediates( - &self, - pass: &mut RenderBundleEncoder, - offset: u32, - data: &[u8], - ) -> Result<(), PassStateError> { - pass.set_immediates(offset, data) - } - - pub fn render_bundle_encoder_set_immediates_with_id( - &self, - bundle_encoder: id::RenderBundleEncoderId, - offset: u32, - data: &[u8], - ) -> Result<(), PassStateError> { - let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); - - let mut bundle_encoder = bundle_encoder - .try_lock() - .expect("RenderBundleEncoders should not be accessed concurrently"); - - bundle_encoder.set_immediates(offset, data) - } - - pub fn render_bundle_encoder_draw( - &self, - bundle: &mut RenderBundleEncoder, - vertex_count: u32, - instance_count: u32, - first_vertex: u32, - first_instance: u32, - ) -> Result<(), PassStateError> { - bundle.draw(vertex_count, instance_count, first_vertex, first_instance) - } - - pub fn render_bundle_encoder_draw_with_id( - &self, - bundle_encoder: id::RenderBundleEncoderId, - vertex_count: u32, - instance_count: u32, - first_vertex: u32, - first_instance: u32, - ) -> Result<(), PassStateError> { - let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); - - let mut bundle_encoder = bundle_encoder - .try_lock() - .expect("RenderBundleEncoders should not be accessed concurrently"); - - bundle_encoder.draw(vertex_count, instance_count, first_vertex, first_instance) - } - - pub fn render_bundle_encoder_draw_indexed( - &self, - bundle: &mut RenderBundleEncoder, - index_count: u32, - instance_count: u32, - first_index: u32, - base_vertex: i32, - first_instance: u32, - ) -> Result<(), PassStateError> { - bundle.draw_indexed( - index_count, - instance_count, - first_index, - base_vertex, - first_instance, - ) - } - - pub fn render_bundle_encoder_draw_indexed_with_id( - &self, - bundle_encoder: id::RenderBundleEncoderId, - index_count: u32, - instance_count: u32, - first_index: u32, - base_vertex: i32, - first_instance: u32, - ) -> Result<(), PassStateError> { - let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); - - let mut bundle_encoder = bundle_encoder - .try_lock() - .expect("RenderBundleEncoders should not be accessed concurrently"); - - bundle_encoder.draw_indexed( - index_count, - instance_count, - first_index, - base_vertex, - first_instance, - ) - } - - pub fn render_bundle_encoder_draw_indirect( - &self, - bundle: &mut RenderBundleEncoder, - buffer_id: id::BufferId, - offset: wgt::BufferAddress, - ) -> Result<(), PassStateError> { - bundle.draw_indirect(self.hub.buffers.get(buffer_id), offset) - } - - pub fn render_bundle_encoder_draw_indirect_with_id( - &self, - bundle_encoder: id::RenderBundleEncoderId, - buffer_id: id::BufferId, - offset: wgt::BufferAddress, - ) -> Result<(), PassStateError> { - let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); - - let mut bundle_encoder = bundle_encoder - .try_lock() - .expect("RenderBundleEncoders should not be accessed concurrently"); - - bundle_encoder.draw_indirect(self.hub.buffers.get(buffer_id), offset) - } - - pub fn render_bundle_encoder_draw_indexed_indirect( - &self, - bundle: &mut RenderBundleEncoder, - buffer_id: id::BufferId, - offset: wgt::BufferAddress, - ) -> Result<(), PassStateError> { - bundle.draw_indexed_indirect(self.hub.buffers.get(buffer_id), offset) - } - - pub fn render_bundle_encoder_draw_indexed_indirect_with_id( - &self, - bundle_encoder: id::RenderBundleEncoderId, - buffer_id: id::BufferId, - offset: wgt::BufferAddress, - ) -> Result<(), PassStateError> { - let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); - - let mut bundle_encoder = bundle_encoder - .try_lock() - .expect("RenderBundleEncoders should not be accessed concurrently"); - - bundle_encoder.draw_indexed_indirect(self.hub.buffers.get(buffer_id), offset) - } - - pub fn render_bundle_encoder_push_debug_group( - &self, - bundle: &mut RenderBundleEncoder, - label: &str, - ) -> Result<(), PassStateError> { - bundle.push_debug_group(label) - } - - pub fn render_bundle_encoder_push_debug_group_with_id( - &self, - bundle_encoder: id::RenderBundleEncoderId, - label: &str, - ) -> Result<(), PassStateError> { - let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); - - let mut bundle_encoder = bundle_encoder - .try_lock() - .expect("RenderBundleEncoders should not be accessed concurrently"); - - bundle_encoder.push_debug_group(label) - } - - pub fn render_bundle_encoder_pop_debug_group( - &self, - bundle: &mut RenderBundleEncoder, - ) -> Result<(), PassStateError> { - bundle.pop_debug_group() - } - - pub fn render_bundle_encoder_pop_debug_group_with_id( - &self, - bundle_encoder: id::RenderBundleEncoderId, - ) -> Result<(), PassStateError> { - let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); - - let mut bundle_encoder = bundle_encoder - .try_lock() - .expect("RenderBundleEncoders should not be accessed concurrently"); - - bundle_encoder.pop_debug_group() - } - - pub fn render_bundle_encoder_insert_debug_marker( - &self, - bundle: &mut RenderBundleEncoder, - label: &str, - ) -> Result<(), PassStateError> { - bundle.insert_debug_marker(label) - } - - pub fn render_bundle_encoder_insert_debug_marker_with_id( - &self, - bundle_encoder: id::RenderBundleEncoderId, - label: &str, - ) -> Result<(), PassStateError> { - let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder); - - let mut bundle_encoder = bundle_encoder - .try_lock() - .expect("RenderBundleEncoders should not be accessed concurrently"); - - bundle_encoder.insert_debug_marker(label) - } -} diff --git a/wgpu-core/src/command/clear.rs b/wgpu-core/src/command/clear.rs index 3f4109575b9..164d4e88ff7 100644 --- a/wgpu-core/src/command/clear.rs +++ b/wgpu-core/src/command/clear.rs @@ -5,10 +5,7 @@ use crate::{ api_log, command::{encoder::EncodingState, ArcCommand, EncoderStateError}, device::{DeviceError, MissingFeatures}, - get_lowest_common_denom, - global::Global, - hal_label, - id::{BufferId, CommandEncoderId, TextureId}, + get_lowest_common_denom, hal_label, init_tracker::{MemoryInitKind, TextureInitRange}, resource::{ Buffer, DestroyedResourceError, InvalidOrDestroyedResourceError, InvalidResourceError, @@ -148,34 +145,6 @@ impl super::CommandEncoder { } } -impl Global { - pub fn command_encoder_clear_buffer( - &self, - command_encoder_id: CommandEncoderId, - dst: BufferId, - offset: BufferAddress, - size: Option, - ) -> Result<(), EncoderStateError> { - let hub = &self.hub; - - let cmd_enc = hub.command_encoders.get(command_encoder_id); - cmd_enc.clear_buffer(hub.buffers.get(dst), offset, size) - } - - pub fn command_encoder_clear_texture( - &self, - command_encoder_id: CommandEncoderId, - dst: TextureId, - subresource_range: &ImageSubresourceRange, - ) -> Result<(), EncoderStateError> { - let hub = &self.hub; - - let cmd_enc = hub.command_encoders.get(command_encoder_id); - - cmd_enc.clear_texture(hub.textures.get(dst), subresource_range) - } -} - pub(super) fn clear_buffer( state: &mut EncodingState, dst_buffer: Arc, diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index 9b2ead9c9a6..09d77df89e0 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -1,4 +1,3 @@ -use parking_lot::Mutex; use thiserror::Error; use wgt::{ error::{ErrorType, WebGpuError}, @@ -28,7 +27,6 @@ use crate::{ TransitionResourcesError, }, device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures}, - global::Global, hal_label, id, impl_resource_type, init_tracker::MemoryInitKind, pipeline::ComputePipeline, @@ -594,79 +592,6 @@ impl ComputePass { } } -impl Global { - /// Creates a compute pass. - /// - /// If creation fails, an invalid pass is returned. Attempting to record - /// commands into an invalid pass is permitted, but a validation error will - /// ultimately be generated when the parent encoder is finished, and it is - /// not possible to run any commands from the invalid pass. - /// - /// If successful, puts the encoder into the [`Locked`] state. - /// - /// [`Locked`]: crate::command::CommandEncoderStatus::Locked - pub fn command_encoder_begin_compute_pass( - &self, - encoder_id: id::CommandEncoderId, - desc: &ComputePassDescriptor<'_, PassTimestampWrites>, - ) -> (ComputePass, Option) { - let hub = &self.hub; - - let cmd_enc = hub.command_encoders.get(encoder_id); - - let desc = ComputePassDescriptor { - label: desc.label.as_deref().map(Cow::Borrowed), - timestamp_writes: desc - .timestamp_writes - .as_ref() - .map(|tw| PassTimestampWrites { - query_set: hub.query_sets.get(tw.query_set), - beginning_of_pass_write_index: tw.beginning_of_pass_write_index, - end_of_pass_write_index: tw.end_of_pass_write_index, - }), - }; - - cmd_enc.begin_compute_pass(&desc) - } - - pub fn command_encoder_begin_compute_pass_with_id( - &self, - encoder_id: id::CommandEncoderId, - desc: &ComputePassDescriptor<'_, PassTimestampWrites>, - id_in: Option, - ) -> (id::ComputePassEncoderId, Option) { - let fid = self.hub.compute_passes.prepare(id_in); - - let (pass, err) = self.command_encoder_begin_compute_pass(encoder_id, desc); - - // no lock rank here because only one thread should be using compute pass - // and it's only used by id variants of compute pass methods on global - // so no deadlock (or concurrent lock) should happen in practise - let id = fid.assign(Arc::new(Mutex::new(pass))); - - (id, err) - } - - pub fn compute_pass_end(&self, pass: &mut ComputePass) -> Result<(), EncoderStateError> { - pass.end() - } - - pub fn compute_pass_end_with_id( - &self, - pass_id: id::ComputePassEncoderId, - ) -> Result<(), EncoderStateError> { - let pass = self.hub.compute_passes.get(pass_id); - let mut pass = pass - .try_lock() - .expect("ComputePasses should not be accessed concurrently"); - self.compute_pass_end(&mut pass) - } - - pub fn compute_pass_drop(&self, pass_id: id::ComputePassEncoderId) { - self.hub.compute_passes.remove(pass_id); - } -} - pub(super) fn encode_compute_pass( parent_state: &mut EncodingState, mut base: BasePass, @@ -1466,279 +1391,3 @@ impl ComputePass { Ok(()) } } - -// Recording a compute pass. -// -// The only error that should be returned from these methods is -// `EncoderStateError::Ended`, when the pass has already ended and an immediate -// validation error is raised. -// -// All other errors should be stored in the pass for later reporting when -// `CommandEncoder.finish()` is called. -// -// The `pass_try!` macro should be used to handle errors appropriately. Note -// that the `pass_try!` and `pass_base!` macros may return early from the -// function that invokes them, like the `?` operator. -impl Global { - pub fn compute_pass_set_bind_group( - &self, - pass: &mut ComputePass, - index: u32, - bind_group_id: Option, - offsets: &[DynamicOffset], - ) -> Result<(), PassStateError> { - pass.set_bind_group( - index, - bind_group_id.map(|bind_group_id| self.hub.bind_groups.get(bind_group_id)), - offsets, - ) - } - - pub fn compute_pass_set_bind_group_with_id( - &self, - pass_id: id::ComputePassEncoderId, - index: u32, - bind_group_id: Option, - offsets: &[DynamicOffset], - ) -> Result<(), PassStateError> { - let pass = self.hub.compute_passes.get(pass_id); - let mut pass = pass - .try_lock() - .expect("ComputePasses should not be accessed concurrently"); - self.compute_pass_set_bind_group(&mut pass, index, bind_group_id, offsets) - } - - pub fn compute_pass_set_pipeline( - &self, - pass: &mut ComputePass, - pipeline_id: id::ComputePipelineId, - ) -> Result<(), PassStateError> { - let pipeline = self.hub.compute_pipelines.get(pipeline_id); - pass.set_pipeline(pipeline) - } - - pub fn compute_pass_set_pipeline_with_id( - &self, - pass_id: id::ComputePassEncoderId, - pipeline_id: id::ComputePipelineId, - ) -> Result<(), PassStateError> { - let pass = self.hub.compute_passes.get(pass_id); - let mut pass = pass - .try_lock() - .expect("ComputePasses should not be accessed concurrently"); - self.compute_pass_set_pipeline(&mut pass, pipeline_id) - } - - pub fn compute_pass_set_immediates( - &self, - pass: &mut ComputePass, - offset: u32, - data: &[u8], - ) -> Result<(), PassStateError> { - pass.set_immediates(offset, data) - } - - pub fn compute_pass_set_immediates_with_id( - &self, - pass_id: id::ComputePassEncoderId, - offset: u32, - data: &[u8], - ) -> Result<(), PassStateError> { - let pass = self.hub.compute_passes.get(pass_id); - let mut pass = pass - .try_lock() - .expect("ComputePasses should not be accessed concurrently"); - self.compute_pass_set_immediates(&mut pass, offset, data) - } - - pub fn compute_pass_dispatch_workgroups( - &self, - pass: &mut ComputePass, - groups_x: u32, - groups_y: u32, - groups_z: u32, - ) -> Result<(), PassStateError> { - pass.dispatch_workgroups(groups_x, groups_y, groups_z) - } - - pub fn compute_pass_dispatch_workgroups_with_id( - &self, - pass_id: id::ComputePassEncoderId, - groups_x: u32, - groups_y: u32, - groups_z: u32, - ) -> Result<(), PassStateError> { - let pass = self.hub.compute_passes.get(pass_id); - let mut pass = pass - .try_lock() - .expect("ComputePasses should not be accessed concurrently"); - self.compute_pass_dispatch_workgroups(&mut pass, groups_x, groups_y, groups_z) - } - - pub fn compute_pass_dispatch_workgroups_indirect( - &self, - pass: &mut ComputePass, - buffer_id: id::BufferId, - offset: BufferAddress, - ) -> Result<(), PassStateError> { - pass.dispatch_workgroups_indirect(self.hub.buffers.get(buffer_id), offset) - } - - pub fn compute_pass_dispatch_workgroups_indirect_with_id( - &self, - pass_id: id::ComputePassEncoderId, - buffer_id: id::BufferId, - offset: BufferAddress, - ) -> Result<(), PassStateError> { - let pass = self.hub.compute_passes.get(pass_id); - let mut pass = pass - .try_lock() - .expect("ComputePasses should not be accessed concurrently"); - self.compute_pass_dispatch_workgroups_indirect(&mut pass, buffer_id, offset) - } - - pub fn compute_pass_push_debug_group( - &self, - pass: &mut ComputePass, - label: &str, - color: u32, - ) -> Result<(), PassStateError> { - pass.push_debug_group(label, color) - } - - pub fn compute_pass_push_debug_group_with_id( - &self, - pass_id: id::ComputePassEncoderId, - label: &str, - color: u32, - ) -> Result<(), PassStateError> { - let pass = self.hub.compute_passes.get(pass_id); - let mut pass = pass - .try_lock() - .expect("ComputePasses should not be accessed concurrently"); - self.compute_pass_push_debug_group(&mut pass, label, color) - } - - pub fn compute_pass_pop_debug_group( - &self, - pass: &mut ComputePass, - ) -> Result<(), PassStateError> { - pass.pop_debug_group() - } - - pub fn compute_pass_pop_debug_group_with_id( - &self, - pass_id: id::ComputePassEncoderId, - ) -> Result<(), PassStateError> { - let pass = self.hub.compute_passes.get(pass_id); - let mut pass = pass - .try_lock() - .expect("ComputePasses should not be accessed concurrently"); - self.compute_pass_pop_debug_group(&mut pass) - } - - pub fn compute_pass_insert_debug_marker( - &self, - pass: &mut ComputePass, - label: &str, - color: u32, - ) -> Result<(), PassStateError> { - pass.insert_debug_marker(label, color) - } - - pub fn compute_pass_insert_debug_marker_with_id( - &self, - pass_id: id::ComputePassEncoderId, - label: &str, - color: u32, - ) -> Result<(), PassStateError> { - let pass = self.hub.compute_passes.get(pass_id); - let mut pass = pass - .try_lock() - .expect("ComputePasses should not be accessed concurrently"); - self.compute_pass_insert_debug_marker(&mut pass, label, color) - } - - pub fn compute_pass_write_timestamp( - &self, - pass: &mut ComputePass, - query_set_id: id::QuerySetId, - query_index: u32, - ) -> Result<(), PassStateError> { - let query_set = self.hub.query_sets.get(query_set_id); - pass.write_timestamp(query_set, query_index) - } - - pub fn compute_pass_write_timestamp_with_id( - &self, - pass_id: id::ComputePassEncoderId, - query_set_id: id::QuerySetId, - query_index: u32, - ) -> Result<(), PassStateError> { - let pass = self.hub.compute_passes.get(pass_id); - let mut pass = pass - .try_lock() - .expect("ComputePasses should not be accessed concurrently"); - self.compute_pass_write_timestamp(&mut pass, query_set_id, query_index) - } - - pub fn compute_pass_begin_pipeline_statistics_query( - &self, - pass: &mut ComputePass, - query_set_id: id::QuerySetId, - query_index: u32, - ) -> Result<(), PassStateError> { - let query_set = self.hub.query_sets.get(query_set_id); - pass.begin_pipeline_statistics_query(query_set, query_index) - } - - pub fn compute_pass_begin_pipeline_statistics_query_with_id( - &self, - pass_id: id::ComputePassEncoderId, - query_set_id: id::QuerySetId, - query_index: u32, - ) -> Result<(), PassStateError> { - let pass = self.hub.compute_passes.get(pass_id); - let mut pass = pass - .try_lock() - .expect("ComputePasses should not be accessed concurrently"); - self.compute_pass_begin_pipeline_statistics_query(&mut pass, query_set_id, query_index) - } - - pub fn compute_pass_end_pipeline_statistics_query( - &self, - pass: &mut ComputePass, - ) -> Result<(), PassStateError> { - pass.end_pipeline_statistics_query() - } - - pub fn compute_pass_end_pipeline_statistics_query_with_id( - &self, - pass_id: id::ComputePassEncoderId, - ) -> Result<(), PassStateError> { - let pass = self.hub.compute_passes.get(pass_id); - let mut pass = pass - .try_lock() - .expect("ComputePasses should not be accessed concurrently"); - self.compute_pass_end_pipeline_statistics_query(&mut pass) - } - - pub fn compute_pass_transition_resources( - &self, - pass: &mut ComputePass, - buffer_transitions: impl Iterator>, - texture_transitions: impl Iterator>, - ) -> Result<(), PassStateError> { - pass.transition_resources( - buffer_transitions.map(|bt| wgt::BufferTransition { - buffer: self.hub.buffers.get(bt.buffer), - state: bt.state, - }), - texture_transitions.map(|tt| wgt::TextureTransition { - texture: self.hub.texture_views.get(tt.texture), - selector: tt.selector, - state: tt.state, - }), - ) - } -} diff --git a/wgpu-core/src/command/encoder_command.rs b/wgpu-core/src/command/encoder_command.rs index 2dfe24590c8..43837a0c8e7 100644 --- a/wgpu-core/src/command/encoder_command.rs +++ b/wgpu-core/src/command/encoder_command.rs @@ -26,10 +26,6 @@ pub trait ReferenceType { type Tlas: Clone + core::fmt::Debug; } -/// Reference wgpu objects via numeric IDs assigned by [`crate::identity::IdentityManager`]. -#[derive(Clone, Debug)] -pub struct IdReferences; - /// Reference wgpu objects via the integer value of pointers. /// /// This is used for trace recording and playback. Recording stores the pointer @@ -44,21 +40,6 @@ pub struct PointerReferences; #[derive(Clone, Debug)] pub struct ArcReferences; -impl ReferenceType for IdReferences { - type Buffer = id::BufferId; - type Surface = id::SurfaceId; - type Texture = id::TextureId; - type TextureView = id::TextureViewId; - type ExternalTexture = id::ExternalTextureId; - type QuerySet = id::QuerySetId; - type BindGroup = id::BindGroupId; - type RenderPipeline = id::RenderPipelineId; - type RenderBundle = id::RenderBundleId; - type ComputePipeline = id::ComputePipelineId; - type Blas = id::BlasId; - type Tlas = id::TlasId; -} - #[cfg(any(feature = "trace", feature = "replay"))] impl ReferenceType for PointerReferences { type Buffer = id::PointerId; diff --git a/wgpu-core/src/command/ffi.rs b/wgpu-core/src/command/ffi.rs deleted file mode 100644 index 4b9a5effbc6..00000000000 --- a/wgpu-core/src/command/ffi.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! Types that are useful for FFI bindings to `wgpu`. - -use crate::{command::IdReferences, id}; - -pub type TexelCopyBufferInfo = wgt::TexelCopyBufferInfo; -pub type TexelCopyTextureInfo = wgt::TexelCopyTextureInfo; -pub type CopyExternalImageDestInfo = wgt::CopyExternalImageDestInfo; - -pub type Command = super::Command; diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index df6d3b162d7..5d5d7456730 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -17,7 +17,6 @@ mod compute_command; mod draw; mod encoder; mod encoder_command; -pub mod ffi; mod memory_init; mod pass; mod query; @@ -54,13 +53,13 @@ pub use self::{ }, compute_command::ArcComputeCommand, draw::{DrawError, Rect, RenderCommandError}, - encoder_command::{ArcCommand, ArcReferences, Command, IdReferences, ReferenceType}, + encoder_command::{ArcCommand, ArcReferences, Command, ReferenceType}, query::{QueryError, QueryUseError, ResolveError, SimplifiedQueryType}, render::{ AttachmentError, AttachmentErrorLocation, ColorAttachmentError, ColorAttachments, LoadOp, PassChannel, RenderBasePass, RenderPass, RenderPassColorAttachment, - RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPassError, - RenderPassErrorInner, ResolvedPassChannel, ResolvedRenderPassDepthStencilAttachment, + RenderPassDepthStencilAttachment, RenderPassError, RenderPassErrorInner, + ResolvedPassChannel, ResolvedRenderPassDepthStencilAttachment, ResolvedRenderPassDescriptor, StoreOp, }, render_command::ArcRenderCommand, @@ -98,20 +97,13 @@ use crate::resource::{ ParentDevice as _, QuerySet, }; use crate::track::{DeviceTracker, ResourceUsageCompatibilityError, Tracker, UsageScope}; -use crate::{api_log, global::Global, id, resource_log, Label}; +use crate::{api_log, resource_log, Label}; use crate::{hal_label, LabelHelpers}; use wgt::error::{ErrorType, WebGpuError}; use thiserror::Error; -/// cbindgen:ignore -pub type TexelCopyBufferInfo = ffi::TexelCopyBufferInfo; -/// cbindgen:ignore -pub type TexelCopyTextureInfo = ffi::TexelCopyTextureInfo; -/// cbindgen:ignore -pub type CopyExternalImageDestInfo = ffi::CopyExternalImageDestInfo; - pub(crate) struct EncoderErrorState { error: CommandEncoderError, @@ -1794,62 +1786,6 @@ impl CommandEncoder { } } -impl Global { - /// Finishes a command encoder, creating a command buffer and returning errors that were - /// deferred until now. - /// - /// The returned `String` is the label of the command encoder, supplied so that `wgpu` can - /// include the label when printing deferred errors without having its own copy of the label. - /// This is a kludge and should be replaced if we think of a better solution to propagating - /// labels. - pub fn command_encoder_finish( - &self, - encoder_id: id::CommandEncoderId, - desc: &wgt::CommandBufferDescriptor` will have the same type of identifier as @@ -274,11 +76,11 @@ impl Marker for () { const TYPE: &'static str = "Untyped"; } -/// Define identifiers for each resource. +/// Define markers for each resource. macro_rules! ids { ($( $(#[$($meta:meta)*])* - pub type $name:ident $marker:ident; + pub type $marker:ident; )*) => { /// Marker types for each resource. pub mod markers { @@ -290,53 +92,34 @@ macro_rules! ids { } )* } - - $( - $(#[$($meta)*])* - pub type $name = Id; - )* } } ids! { - pub type AdapterId Adapter; - pub type SurfaceId Surface; - pub type DeviceId Device; - pub type QueueId Queue; - pub type BufferId Buffer; - pub type StagingBufferId StagingBuffer; - pub type TextureViewId TextureView; - pub type TextureId Texture; - pub type ExternalTextureId ExternalTexture; - pub type SamplerId Sampler; - pub type BindGroupLayoutId BindGroupLayout; - pub type PipelineLayoutId PipelineLayout; - pub type BindGroupId BindGroup; - pub type ShaderModuleId ShaderModule; - pub type RenderPipelineId RenderPipeline; - pub type ComputePipelineId ComputePipeline; - pub type PipelineCacheId PipelineCache; - pub type CommandEncoderId CommandEncoder; - pub type CommandBufferId CommandBuffer; - pub type RenderPassEncoderId RenderPassEncoder; - pub type ComputePassEncoderId ComputePassEncoder; - pub type RenderBundleEncoderId RenderBundleEncoder; - pub type RenderBundleId RenderBundle; - pub type QuerySetId QuerySet; - pub type BlasId Blas; - pub type TlasId Tlas; -} - -#[test] -fn test_id() { - let indexes = [0, Index::MAX / 2 - 1, Index::MAX / 2 + 1, Index::MAX]; - let epochs = [1, Epoch::MAX / 2 - 1, Epoch::MAX / 2 + 1, Epoch::MAX]; - for &i in &indexes { - for &e in &epochs { - let id = Id::<()>::zip(i, e); - let (index, epoch) = id.unzip(); - assert_eq!(index, i); - assert_eq!(epoch, e); - } - } + pub type Adapter; + pub type Surface; + pub type Device; + pub type Queue; + pub type Buffer; + pub type StagingBuffer; + pub type TextureView; + pub type Texture; + pub type ExternalTexture; + pub type Sampler; + pub type BindGroupLayout; + pub type PipelineLayout; + pub type BindGroup; + pub type ShaderModule; + pub type RenderPipeline; + pub type ComputePipeline; + pub type PipelineCache; + pub type CommandEncoder; + pub type CommandBuffer; + pub type RenderPassEncoder; + pub type ComputePassEncoder; + pub type RenderBundleEncoder; + pub type RenderBundle; + pub type QuerySet; + pub type Blas; + pub type Tlas; } diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index a078307c859..0fb8083426d 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -9,8 +9,7 @@ use crate::{ device::{ queue::Queue, resource::Device, DeviceDescriptor, DeviceError, UserClosures, WaitIdleError, }, - global::Global, - id::{markers, AdapterId, DeviceId, QueueId, SurfaceId}, + id::markers, limits::{self, check_limits, FailedLimit}, lock::{rank, Mutex}, present::{ConfigureSurfaceError, Presentation}, @@ -23,8 +22,6 @@ use crate::{ use wgt::{Backend, Backends, InstanceFlags, PowerPreference}; -pub type RequestAdapterOptions = wgt::RequestAdapterOptions; - #[test] fn downlevel_default_limits_less_than_default_limits() { let res = check_limits(&wgt::Limits::downlevel_defaults(), &wgt::Limits::default()); @@ -1352,305 +1349,6 @@ pub enum CreateSurfaceError { MissingDisplayHandle, } -impl Global { - /// Creates a new surface targeting the given display/window handles. - /// - /// Internally attempts to create hal surfaces for all enabled backends. - /// - /// Fails only if creation for surfaces for all enabled backends fails in which case - /// the error for each enabled backend is listed. - /// Vice versa, if creation for any backend succeeds, success is returned. - /// Surface creation errors are logged to the debug log in any case. - /// - /// id_in: - /// - If `Some`, the id to assign to the surface. A new one will be generated otherwise. - /// - /// # Safety - /// - /// - `display_handle` must be a valid object to create a surface upon, - /// falls back to the instance display handle otherwise. - /// - `window_handle` must remain valid as long as the returned - /// [`SurfaceId`] is being used. - pub unsafe fn instance_create_surface( - &self, - display_handle: Option, - window_handle: raw_window_handle::RawWindowHandle, - id_in: Option, - ) -> Result { - let surface = unsafe { self.instance.create_surface(display_handle, window_handle) }?; - let id = self.surfaces.prepare(id_in).assign(surface); - Ok(id) - } - - /// Creates a new surface from the given drm configuration. - /// - /// # Safety - /// - /// - All parameters must point to valid DRM values. - /// - /// # Platform Support - /// - /// This function requires the `"drm"` feature, and is only available on - /// non-apple Unix-like platforms (Linux, FreeBSD) and currently only works - /// with the Vulkan backend. - #[cfg(drm)] - pub unsafe fn instance_create_surface_from_drm( - &self, - fd: i32, - plane: u32, - connector_id: u32, - width: u32, - height: u32, - refresh_rate: u32, - id_in: Option, - ) -> Result { - let surface = unsafe { - self.instance.create_surface_from_drm( - fd, - plane, - connector_id, - width, - height, - refresh_rate, - ) - }?; - let id = self.surfaces.prepare(id_in).assign(surface); - - Ok(id) - } - - /// # Safety - /// - /// `layer` must be a valid pointer. - #[cfg(metal)] - pub unsafe fn instance_create_surface_metal( - &self, - layer: *mut core::ffi::c_void, - id_in: Option, - ) -> Result { - let surface = unsafe { self.instance.create_surface_metal(layer) }?; - let id = self.surfaces.prepare(id_in).assign(surface); - Ok(id) - } - - #[cfg(dx12)] - /// # Safety - /// - /// The visual must be valid and able to be used to make a swapchain with. - pub unsafe fn instance_create_surface_from_visual( - &self, - visual: *mut core::ffi::c_void, - id_in: Option, - ) -> Result { - let surface = unsafe { self.instance.create_surface_from_visual(visual) }?; - let id = self.surfaces.prepare(id_in).assign(surface); - Ok(id) - } - - #[cfg(dx12)] - /// # Safety - /// - /// The surface_handle must be valid and able to be used to make a swapchain with. - pub unsafe fn instance_create_surface_from_surface_handle( - &self, - surface_handle: *mut core::ffi::c_void, - id_in: Option, - ) -> Result { - let surface = unsafe { - self.instance - .create_surface_from_surface_handle(surface_handle) - }?; - let id = self.surfaces.prepare(id_in).assign(surface); - Ok(id) - } - - #[cfg(dx12)] - /// # Safety - /// - /// The swap_chain_panel must be valid and able to be used to make a swapchain with. - pub unsafe fn instance_create_surface_from_swap_chain_panel( - &self, - swap_chain_panel: *mut core::ffi::c_void, - id_in: Option, - ) -> Result { - let surface = unsafe { - self.instance - .create_surface_from_swap_chain_panel(swap_chain_panel) - }?; - let id = self.surfaces.prepare(id_in).assign(surface); - Ok(id) - } - - pub fn surface_drop(&self, id: SurfaceId) { - self.surfaces.remove(id); - } - - pub fn enumerate_adapters( - &self, - backends: Backends, - apply_limit_buckets: bool, - ) -> Vec { - let adapters = self - .instance - .enumerate_adapters(backends, apply_limit_buckets); - adapters - .into_iter() - .map(|adapter| self.hub.adapters.prepare(None).assign(adapter)) - .collect() - } - - pub fn request_adapter( - &self, - desc: &RequestAdapterOptions, - backends: Backends, - id_in: Option, - ) -> Result { - let compatible_surface = desc.compatible_surface.map(|id| self.surfaces.get(id)); - let desc = wgt::RequestAdapterOptions { - power_preference: desc.power_preference, - force_fallback_adapter: desc.force_fallback_adapter, - compatible_surface: compatible_surface.as_deref(), - apply_limit_buckets: desc.apply_limit_buckets, - }; - let adapter = self.instance.request_adapter(&desc, backends)?; - let id = self.hub.adapters.prepare(id_in).assign(adapter); - Ok(id) - } - - /// Create an adapter from a HAL adapter. - /// - /// The HAL adapter may be obtained e.g. by calling `enumerate_adapters` on - /// the HAL directly. - /// - /// If [limit bucketing][lt] is desired, [`crate::limits::apply_limit_buckets`] - /// should be called with the HAL adapter before calling this function. - /// - /// # Safety - /// - /// `hal_adapter` must be created from this global internal instance handle. - /// - /// [lt]: crate::limits#Limit-bucketing - pub unsafe fn create_adapter_from_hal( - &self, - hal_adapter: hal::DynExposedAdapter, - input: Option, - ) -> AdapterId { - let fid = self.hub.adapters.prepare(input); - fid.assign(unsafe { self.instance.create_adapter_from_hal(hal_adapter) }) - } - - pub fn adapter_get_info(&self, adapter_id: AdapterId) -> wgt::AdapterInfo { - let adapter = self.hub.adapters.get(adapter_id); - adapter.get_info() - } - - pub fn adapter_get_texture_format_features( - &self, - adapter_id: AdapterId, - format: wgt::TextureFormat, - ) -> wgt::TextureFormatFeatures { - let adapter = self.hub.adapters.get(adapter_id); - adapter.get_texture_format_features(format) - } - - pub fn adapter_features(&self, adapter_id: AdapterId) -> wgt::Features { - let adapter = self.hub.adapters.get(adapter_id); - adapter.features() - } - - pub fn adapter_limits(&self, adapter_id: AdapterId) -> wgt::Limits { - let adapter = self.hub.adapters.get(adapter_id); - adapter.limits() - } - - pub fn adapter_downlevel_capabilities( - &self, - adapter_id: AdapterId, - ) -> wgt::DownlevelCapabilities { - let adapter = self.hub.adapters.get(adapter_id); - adapter.downlevel_capabilities() - } - - pub fn adapter_get_presentation_timestamp( - &self, - adapter_id: AdapterId, - ) -> wgt::PresentationTimestamp { - let adapter = self.hub.adapters.get(adapter_id); - adapter.get_presentation_timestamp() - } - - pub fn adapter_cooperative_matrix_properties( - &self, - adapter_id: AdapterId, - ) -> Vec { - let adapter = self.hub.adapters.get(adapter_id); - adapter.cooperative_matrix_properties() - } - - pub fn adapter_drop(&self, adapter_id: AdapterId) { - self.hub.adapters.remove(adapter_id); - } -} - -impl Global { - pub fn adapter_request_device( - &self, - adapter_id: AdapterId, - desc: &DeviceDescriptor, - device_id_in: Option, - queue_id_in: Option, - ) -> Result<(DeviceId, QueueId), RequestDeviceError> { - let device_fid = self.hub.devices.prepare(device_id_in); - let queue_fid = self.hub.queues.prepare(queue_id_in); - - let adapter = self.hub.adapters.get(adapter_id); - let (device, queue) = adapter.request_device(desc)?; - - let device_id = device_fid.assign(device); - resource_log!("Created Device {:?}", device_id); - - let queue_id = queue_fid.assign(queue); - resource_log!("Created Queue {:?}", queue_id); - - Ok((device_id, queue_id)) - } - - pub fn adapter_validate_device_descriptor( - &self, - adapter_id: AdapterId, - desc: &mut DeviceDescriptor, - ) -> Result<(), RequestDeviceError> { - let adapter = self.hub.adapters.get(adapter_id); - adapter.validate_device_descriptor(desc) - } - - /// # Safety - /// - /// - `hal_device` must be created from `adapter_id` or its internal handle. - /// - `desc` must be a subset of `hal_device` features and limits. - pub unsafe fn create_device_from_hal( - &self, - adapter_id: AdapterId, - hal_device: hal::DynOpenDevice, - desc: &DeviceDescriptor, - device_id_in: Option, - queue_id_in: Option, - ) -> Result<(DeviceId, QueueId), RequestDeviceError> { - let devices_fid = self.hub.devices.prepare(device_id_in); - let queues_fid = self.hub.queues.prepare(queue_id_in); - - let adapter = self.hub.adapters.get(adapter_id); - let (device, queue) = - unsafe { adapter.create_device_and_queue_from_hal(hal_device, desc) }?; - - let device_id = devices_fid.assign(device); - - let queue_id = queues_fid.assign(queue); - - Ok((device_id, queue_id)) - } -} - /// This function checks that the adapter obeys WebGPU's adapter capability /// guarantees. Most of the limits are adjusted in wgpu-hal's /// `adjust_raw_limits` fn. So we only check the remaining properties here. diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs index 0043fa3f163..cca44880d66 100644 --- a/wgpu-core/src/lib.rs +++ b/wgpu-core/src/lib.rs @@ -75,10 +75,7 @@ pub mod command; mod conv; pub mod device; pub mod error; -pub mod global; -pub mod hub; pub mod id; -pub mod identity; mod indirect_validation; mod init_tracker; pub mod instance; @@ -89,7 +86,6 @@ mod pipeline_cache; mod pool; pub mod present; pub mod ray_tracing; -pub mod registry; pub mod resource; mod snatch; pub mod storage; @@ -120,13 +116,10 @@ pub(crate) use nt::{FastHashMap, FastHashSet, FastIndexMap}; /// These are the values stored in `Device::fence`. pub type SubmissionIndex = hal::FenceValue; -type Index = u32; -type Epoch = u32; - pub type RawString = *const core::ffi::c_char; pub type Label<'a> = Option>; -trait LabelHelpers<'a> { +pub trait LabelHelpers<'a> { fn to_hal(&'a self, flags: wgt::InstanceFlags) -> Option<&'a str>; fn to_string(&self) -> String; } diff --git a/wgpu-core/src/lock/rank.rs b/wgpu-core/src/lock/rank.rs index f177dc6c914..78662fc9d6d 100644 --- a/wgpu-core/src/lock/rank.rs +++ b/wgpu-core/src/lock/rank.rs @@ -213,7 +213,6 @@ define_lock_ranks! { // Ranks not connected to the graph, alphabetical. rank BLAS_BUILT_INDEX "Blas::built_index" followed by { } rank DEVICE_LOST_CLOSURE "Device::device_lost_closure" followed by { } - rank IDENTITY_MANAGER_VALUES "IdentityManager::values" followed by { } rank RESOURCE_POOL_INNER "ResourcePool::inner" followed by { } rank SURFACE_PRESENTATION "Surface::presentation" followed by { } rank TLAS_BUILT_INDEX "Tlas::built_index" followed by { } diff --git a/wgpu-core/src/present.rs b/wgpu-core/src/present.rs index 54418882e47..1607687be46 100644 --- a/wgpu-core/src/present.rs +++ b/wgpu-core/src/present.rs @@ -17,8 +17,7 @@ use crate::device::trace::{Action, IntoTrace}; use crate::{ conv, device::{queue::Queue, Device, DeviceError, MissingDownlevelFlags, WaitIdleError}, - global::Global, - hal_label, id, + hal_label, instance::Surface, resource::{self, Labeled}, }; @@ -481,43 +480,3 @@ impl Surface { Ok(()) } } - -impl Global { - pub fn surface_get_current_texture( - &self, - surface_id: id::SurfaceId, - texture_id_in: Option, - ) -> Result, SurfaceError> { - let surface = self.surfaces.get(surface_id); - - let fid = self.hub.textures.prepare(texture_id_in); - - let output = surface.get_current_texture()?; - - let status = output.status; - let texture_id = output.texture.map(|texture| fid.assign(texture)); - - Ok(SurfaceOutput { - status, - texture: texture_id, - }) - } - - pub fn surface_present(&self, surface_id: id::SurfaceId) -> Result { - let surface = self.surfaces.get(surface_id); - - surface.present() - } - - pub fn surface_texture_discard(&self, surface_id: id::SurfaceId) -> Result<(), SurfaceError> { - let surface = self.surfaces.get(surface_id); - - surface.discard() - } - - pub fn surface_texture_release(&self, surface_id: id::SurfaceId) -> Result<(), SurfaceError> { - let surface = self.surfaces.get(surface_id); - - surface.release() - } -} diff --git a/wgpu-core/src/storage.rs b/wgpu-core/src/storage.rs index 684bf7c3458..29cb4d2ded8 100644 --- a/wgpu-core/src/storage.rs +++ b/wgpu-core/src/storage.rs @@ -1,25 +1,9 @@ -use alloc::{sync::Arc, vec::Vec}; -use core::mem; +use alloc::sync::Arc; -use crate::id::{Id, Marker}; +use crate::id::Marker; use crate::resource::ResourceType; -use crate::{Epoch, Index}; use parking_lot::Mutex; -/// An entry in a `Storage::map` table. -#[derive(Debug)] -pub(crate) enum Element -where - T: StorageItem, -{ - /// There are no live ids with this index. - Vacant, - - /// There is one live id with this index, allocated at the given - /// epoch. - Occupied(T, Epoch), -} - /// Not a public API. For use only by `player`. #[doc(hidden)] pub trait StorageItem: ResourceType { @@ -50,176 +34,3 @@ macro_rules! impl_storage_item { } }; } - -/// A table of `T` values indexed by the id type `I`. -/// -/// `Storage` implements [`core::ops::Index`], accepting `Id` values as -/// indices. -/// -/// The table is represented as a vector indexed by the ids' index -/// values, so you should use an id allocator like `IdentityManager` -/// that keeps the index values dense and close to zero. -#[derive(Debug)] -pub(crate) struct Storage -where - T: StorageItem, -{ - pub(crate) map: Vec>, -} - -impl Storage -where - T: StorageItem, -{ - pub(crate) fn new() -> Self { - Self { map: Vec::new() } - } -} - -impl Storage -where - T: StorageItem, -{ - pub(crate) fn insert(&mut self, id: Id, value: T) { - let (index, epoch) = id.unzip(); - let index = index as usize; - if index >= self.map.len() { - self.map.resize_with(index + 1, || Element::Vacant); - } - match mem::replace(&mut self.map[index], Element::Occupied(value, epoch)) { - Element::Vacant => {} - Element::Occupied(_, storage_epoch) => { - panic!( - "Cannot insert {id:?}, found existing resource {other:?}", - other = Id::::zip(index as Index, storage_epoch), - ); - } - } - } - - pub(crate) fn remove(&mut self, id: Id) -> T { - let (index, epoch) = id.unzip(); - let stored = self.map.get_mut(index as usize); - match stored.map(|stored| mem::replace(stored, Element::Vacant)) { - Some(Element::Occupied(value, storage_epoch)) => { - assert_eq!( - epoch, - storage_epoch, - "Cannot remove {id:?}, found other resource {other:?}", - other = Id::::zip(index, storage_epoch), - ); - value - } - None | Some(Element::Vacant) => { - panic!("Cannot remove non-existent resource {id:?}"); - } - } - } - - #[allow(dead_code)] - pub(crate) fn iter(&self) -> impl Iterator, &T)> { - self.map - .iter() - .enumerate() - .filter_map(move |(index, x)| match *x { - Element::Occupied(ref value, storage_epoch) => { - Some((Id::zip(index as Index, storage_epoch), value)) - } - _ => None, - }) - } -} - -impl Storage -where - T: StorageItem + Clone, -{ - /// Get an owned reference to an item. - /// Panics if there is an epoch mismatch, the entry is empty or in error. - pub(crate) fn get(&self, id: Id) -> T { - let (index, epoch) = id.unzip(); - let (result, storage_epoch) = match self.map.get(index as usize) { - Some(&Element::Occupied(ref v, epoch)) => (v.clone(), epoch), - None | Some(&Element::Vacant) => { - panic!("Cannot get non-existent resource {id:?}"); - } - }; - assert_eq!( - epoch, - storage_epoch, - "Cannot get {id:?}, found other resource {other:?}", - other = Id::::zip(index, storage_epoch), - ); - result - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[derive(Clone, Debug)] - struct TestItem; - - impl ResourceType for TestItem { - const TYPE: &'static str = "TestItem"; - } - - impl StorageItem for TestItem { - type Marker = (); - } - - fn id(index: Index, epoch: Epoch) -> Id<()> { - Id::zip(index, epoch) - } - - #[test] - #[should_panic( - expected = "Cannot insert UntypedId(0,1), found existing resource UntypedId(0,1)" - )] - fn insert_occupied_same_epoch() { - let mut storage = Storage::new(); - storage.insert(id(0, 1), TestItem); - storage.insert(id(0, 1), TestItem); - } - - #[test] - #[should_panic( - expected = "Cannot insert UntypedId(0,2), found existing resource UntypedId(0,1)" - )] - fn insert_occupied_different_epoch() { - let mut storage = Storage::new(); - storage.insert(id(0, 1), TestItem); - storage.insert(id(0, 2), TestItem); - } - - #[test] - #[should_panic(expected = "Cannot remove UntypedId(0,2), found other resource UntypedId(0,1)")] - fn remove_epoch_mismatch() { - let mut storage = Storage::new(); - storage.insert(id(0, 1), TestItem); - storage.remove(id(0, 2)); - } - - #[test] - #[should_panic(expected = "Cannot remove non-existent resource UntypedId(0,1)")] - fn remove_vacant() { - let mut storage = Storage::::new(); - storage.remove(id(0, 1)); - } - - #[test] - #[should_panic(expected = "Cannot get non-existent resource UntypedId(0,1)")] - fn get_vacant() { - let storage = Storage::::new(); - storage.get(id(0, 1)); - } - - #[test] - #[should_panic(expected = "Cannot get UntypedId(0,2), found other resource UntypedId(0,1)")] - fn get_epoch_mismatch() { - let mut storage = Storage::new(); - storage.insert(id(0, 1), TestItem); - storage.get(id(0, 2)); - } -} From 91e489bedc96d73279b8e8fc0d06f7b437079bcd Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Wed, 12 Aug 2026 19:12:01 +0200 Subject: [PATCH 2/5] some doc fixes Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- wgpu-core-remote/src/hub.rs | 13 ------------ wgpu-core/src/command/encoder_command.rs | 25 ++++++++++++------------ wgpu-core/src/id.rs | 21 +++++++++++++++----- wgpu-core/src/instance.rs | 2 +- 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/wgpu-core-remote/src/hub.rs b/wgpu-core-remote/src/hub.rs index 3f235e1fc9b..66e670bb37b 100644 --- a/wgpu-core-remote/src/hub.rs +++ b/wgpu-core-remote/src/hub.rs @@ -101,19 +101,6 @@ flagged as errors as well. [wrapped in a mutex]: trait.IdentityHandler.html#impl-IdentityHandler%3CI%3E-for-Mutex%3CIdentityManager%3E [WebGPU]: https://www.w3.org/TR/webgpu/ -## IDs and tracing - -As of `wgpu` v27, commands are encoded all at once when -`CommandEncoder::finish` is called, not when the encoding methods are -called for each command. This implies storing a representation of the -commands in memory until `finish` is called. `Arc`s are more suitable -for this purpose than numeric ids. Rather than redundantly store both -`Id`s and `Arc`s, tracing has been changed to work with `Arc`s. The -serialized trace identifies resources by the integer value of -`Arc::as_ptr`. These IDs have the type [`crate::id::PointerId`]. The -trace player uses hash maps to go from `PointerId`s to `Arc`s -when replaying a trace. - */ use alloc::sync::Arc; diff --git a/wgpu-core/src/command/encoder_command.rs b/wgpu-core/src/command/encoder_command.rs index 43837a0c8e7..092843e5944 100644 --- a/wgpu-core/src/command/encoder_command.rs +++ b/wgpu-core/src/command/encoder_command.rs @@ -6,7 +6,6 @@ use macro_rules_attribute::{apply, attribute_alias}; use crate::{ command::ColorAttachments, - id, instance::Surface, resource::{Buffer, QuerySet, Texture}, }; @@ -42,18 +41,18 @@ pub struct ArcReferences; #[cfg(any(feature = "trace", feature = "replay"))] impl ReferenceType for PointerReferences { - type Buffer = id::PointerId; - type Surface = id::PointerId; - type Texture = id::PointerId; - type TextureView = id::PointerId; - type ExternalTexture = id::PointerId; - type QuerySet = id::PointerId; - type BindGroup = id::PointerId; - type RenderPipeline = id::PointerId; - type RenderBundle = id::PointerId; - type ComputePipeline = id::PointerId; - type Blas = id::PointerId; - type Tlas = id::PointerId; + type Buffer = crate::id::PointerId; + type Surface = crate::id::PointerId; + type Texture = crate::id::PointerId; + type TextureView = crate::id::PointerId; + type ExternalTexture = crate::id::PointerId; + type QuerySet = crate::id::PointerId; + type BindGroup = crate::id::PointerId; + type RenderPipeline = crate::id::PointerId; + type RenderBundle = crate::id::PointerId; + type ComputePipeline = crate::id::PointerId; + type Blas = crate::id::PointerId; + type Tlas = crate::id::PointerId; } impl ReferenceType for ArcReferences { diff --git a/wgpu-core/src/id.rs b/wgpu-core/src/id.rs index 202e20982d5..b9876781b14 100644 --- a/wgpu-core/src/id.rs +++ b/wgpu-core/src/id.rs @@ -1,15 +1,26 @@ -use core::{fmt::Debug, hash::Hash, marker::PhantomData}; use wgt::WasmNotSendSync; /// Identify an object by the pointer returned by `Arc::as_ptr`. /// -/// This is used for tracing. See [IDs and tracing](crate::hub#ids-and-tracing). +/// This is used for tracing. +/// +/// As of `wgpu` v27, commands are encoded all at once when +/// `CommandEncoder::finish` is called, not when the encoding methods are +/// called for each command. This implies storing a representation of the +/// commands in memory until `finish` is called. The +/// serialized trace identifies resources by the integer value of +/// `Arc::as_ptr`. These IDs have the type [`crate::id::PointerId`]. The +/// trace player uses hash maps to go from `PointerId`s to `Arc`s +/// when replaying a trace. #[allow(dead_code)] #[cfg(feature = "serde")] #[derive(Debug, serde::Serialize, serde::Deserialize)] pub enum PointerId { // The only variant forces RON to not ignore "Id" - PointerId(core::num::NonZeroUsize, #[serde(skip)] PhantomData), + PointerId( + core::num::NonZeroUsize, + #[serde(skip)] core::marker::PhantomData, + ), } #[cfg(feature = "serde")] @@ -35,7 +46,7 @@ impl PartialEq for PointerId { impl Eq for PointerId {} #[cfg(feature = "serde")] -impl Hash for PointerId { +impl core::hash::Hash for PointerId { fn hash(&self, state: &mut H) { let PointerId::PointerId(this, _) = self; this.hash(state); @@ -54,7 +65,7 @@ impl From<&alloc::sync::Arc> for PointerId, From 796b3ce5130d0f875e0d246bba53412d17fe3f80 Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Thu, 13 Aug 2026 06:43:50 +0200 Subject: [PATCH 3/5] less lints Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- wgpu-core-remote/src/identity.rs | 6 ++++++ wgpu-core-remote/src/lib.rs | 17 ----------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/wgpu-core-remote/src/identity.rs b/wgpu-core-remote/src/identity.rs index 524a06568f6..8f4317631a1 100644 --- a/wgpu-core-remote/src/identity.rs +++ b/wgpu-core-remote/src/identity.rs @@ -143,6 +143,12 @@ impl IdentityManager { } } +impl Default for IdentityManager { + fn default() -> Self { + Self::new() + } +} + #[test] fn test_epoch_end_of_life() { use crate::id; diff --git a/wgpu-core-remote/src/lib.rs b/wgpu-core-remote/src/lib.rs index 104b002d3bb..12a3b59eae6 100644 --- a/wgpu-core-remote/src/lib.rs +++ b/wgpu-core-remote/src/lib.rs @@ -1,24 +1,7 @@ #![allow( - // It is much clearer to assert negative conditions with eq! false - clippy::bool_assert_comparison, - // We don't use syntax sugar where it's not necessary. - clippy::match_like_matches_macro, - // Redundant matching is more explicit. - clippy::redundant_pattern_matching, - // Explicit lifetimes are often easier to reason about. - clippy::needless_lifetimes, - // No need for defaults in the internal types. - clippy::new_without_default, - // Needless updates are more scalable, easier to play with features. - clippy::needless_update, // Need many arguments for some core functions to be able to re-use code in many situations. clippy::too_many_arguments, - // It gets in the way a lot and does not prevent bugs in practice. - clippy::pattern_type_mismatch, - // `wgpu-core` isn't entirely user-facing, so it's useful to document internal items. - rustdoc::private_intra_doc_links, )] -#![expect(missing_debug_implementations, reason = "TODO")] #![warn( clippy::alloc_instead_of_core, clippy::ptr_as_ptr, From 925c4b6651605cb621d5283e996657285eee9d13 Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Thu, 13 Aug 2026 18:32:30 +0200 Subject: [PATCH 4/5] remove stale ranks Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- wgpu-core/src/lock/rank.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/wgpu-core/src/lock/rank.rs b/wgpu-core/src/lock/rank.rs index 78662fc9d6d..30094f9d420 100644 --- a/wgpu-core/src/lock/rank.rs +++ b/wgpu-core/src/lock/rank.rs @@ -176,23 +176,6 @@ define_lock_ranks! { SHARED_TRACKER_INDEX_ALLOCATOR_INNER, } rank HUB_OTHER "Hub (general)" followed by { - HUB_TEXTURE_VIEWS, - HUB_BIND_GROUPS, - } - rank HUB_BIND_GROUPS "Hub::bind_groups" followed by { - HUB_RENDER_PIPELINES, - } - rank HUB_RENDER_PIPELINES "Hub::render_pipelines" followed by { - SHARED_TRACKER_INDEX_ALLOCATOR_INNER, - } - rank HUB_TEXTURE_VIEWS "Hub::texture_views" followed by { - HUB_SAMPLERS, - } - rank HUB_SAMPLERS "Hub::samplers" followed by { - HUB_TLAS, - } - rank HUB_TLAS "Hub::tlas" followed by { - HUB_EXTERNAL_TEXTURES, } // Leaf ranks reachable from the graph above, alphabetical. @@ -203,7 +186,6 @@ define_lock_ranks! { rank DEVICE_DEFERRED_DESTROY "Device::deferred_destroy" followed by { } rank DEVICE_TRACE "Device::trace" followed by { } rank DEVICE_USAGE_SCOPES "Device::usage_scopes" followed by { } - rank HUB_EXTERNAL_TEXTURES "Hub::external_textures" followed by { } rank SHARED_TRACKER_INDEX_ALLOCATOR_INNER "SharedTrackerIndexAllocator::inner" followed by { } rank QUERY_SET_INITIALIZED_SLOTS "QuerySet::initialized_slots" followed by { } rank TEXTURE_BIND_GROUPS "Texture::bind_groups" followed by { } From b2272e8afa4f2f699f7c0cf26a8e5772787d0c50 Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Thu, 13 Aug 2026 18:34:47 +0200 Subject: [PATCH 5/5] bring back docs Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- wgpu-core-remote/src/global/compute_pass.rs | 8 ++++++++ wgpu-core-remote/src/global/render_pass.rs | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/wgpu-core-remote/src/global/compute_pass.rs b/wgpu-core-remote/src/global/compute_pass.rs index 9a05ed2f44e..7fe1ec98d1e 100644 --- a/wgpu-core-remote/src/global/compute_pass.rs +++ b/wgpu-core-remote/src/global/compute_pass.rs @@ -85,6 +85,14 @@ impl Global { } } +// Recording a compute pass. +// +// The only error that should be returned from these methods is +// `EncoderStateError::Ended`, when the pass has already ended and an immediate +// validation error is raised. +// +// All other errors should be stored in the pass for later reporting when +// `CommandEncoder.finish()` is called. impl Global { pub fn compute_pass_set_bind_group( &self, diff --git a/wgpu-core-remote/src/global/render_pass.rs b/wgpu-core-remote/src/global/render_pass.rs index a3d39de079d..94734488e15 100644 --- a/wgpu-core-remote/src/global/render_pass.rs +++ b/wgpu-core-remote/src/global/render_pass.rs @@ -137,6 +137,14 @@ impl Global { } } +// Recording a render pass. +// +// The only error that should be returned from these methods is +// `EncoderStateError::Ended`, when the pass has already ended and an immediate +// validation error is raised. +// +// All other errors should be stored in the pass for later reporting when +// `CommandEncoder.finish()` is called. impl Global { pub fn render_pass_set_bind_group( &self,