diff --git a/Cargo.lock b/Cargo.lock index 361c31d38b5..66761f7d540 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2360,6 +2360,22 @@ version = "0.1.4+2024.11.22-df583a3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e3cd67e8ea2ba061339150970542cf1c60ba44c6d17e31279cbc133a4b018f8" +[[package]] +name = "macro_rules_attribute" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520" +dependencies = [ + "macro_rules_attribute-proc_macro", + "paste", +] + +[[package]] +name = "macro_rules_attribute-proc_macro" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30" + [[package]] name = "malloc_buf" version = "0.0.6" @@ -3162,6 +3178,7 @@ version = "27.0.0" dependencies = [ "bytemuck", "env_logger", + "hashbrown 0.16.0", "log", "raw-window-handle 0.6.2", "ron", @@ -3776,6 +3793,9 @@ name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] [[package]] name = "smithay-client-toolkit" @@ -4817,6 +4837,7 @@ dependencies = [ "hashbrown 0.16.0", "indexmap", "log", + "macro_rules_attribute", "naga", "once_cell", "parking_lot", diff --git a/Cargo.toml b/Cargo.toml index 12f94bfed07..335dddaa601 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -142,6 +142,7 @@ libloading = "0.8" libm = { version = "0.2.6", default-features = false } libtest-mimic = "0.8" log = "0.4.21" +macro_rules_attribute = "0.2" nanoserde = "0.2" nanorand = { version = "0.8", default-features = false, features = ["wyrand"] } noise = "0.9" diff --git a/deno_webgpu/device.rs b/deno_webgpu/device.rs index b59fc633cd4..f6c3110d48c 100644 --- a/deno_webgpu/device.rs +++ b/deno_webgpu/device.rs @@ -577,11 +577,8 @@ impl GPUDevice { multiview: None, }; - let res = wgpu_core::command::RenderBundleEncoder::new( - &wgpu_descriptor, - self.id, - None, - ); + let res = + wgpu_core::command::RenderBundleEncoder::new(&wgpu_descriptor, self.id); let (encoder, err) = match res { Ok(encoder) => (encoder, None), Err(e) => ( diff --git a/player/Cargo.toml b/player/Cargo.toml index f29aacf0e94..f09231830c8 100644 --- a/player/Cargo.toml +++ b/player/Cargo.toml @@ -22,6 +22,7 @@ test = false wgpu-types = { workspace = true, features = ["serde", "std"] } env_logger.workspace = true +hashbrown.workspace = true log.workspace = true raw-window-handle.workspace = true ron.workspace = true diff --git a/player/src/bin/play.rs b/player/src/bin/play.rs index dfce80b8ad4..1497b96c7fd 100644 --- a/player/src/bin/play.rs +++ b/player/src/bin/play.rs @@ -5,14 +5,15 @@ fn main() { extern crate wgpu_core as wgc; extern crate wgpu_types as wgt; - use player::GlobalPlay as _; + use player::Player; use wgc::device::trace; - use wgpu_core::identity::IdentityManager; + use wgpu_core::command::PointerReferences; use std::{ fs, path::{Path, PathBuf}, process::exit, + sync::Arc, }; #[cfg(feature = "winit")] @@ -52,7 +53,7 @@ fn main() { log::info!("Loading trace '{trace:?}'"); let file = fs::File::open(trace).unwrap(); - let mut actions: Vec = ron::de::from_reader(file).unwrap(); + let mut actions: Vec> = ron::de::from_reader(file).unwrap(); actions.reverse(); // allows us to pop from the top log::info!("Found {} actions", actions.len()); @@ -68,17 +69,14 @@ fn main() { .build(&event_loop) .unwrap(); - let global = - wgc::global::Global::new("player", &wgt::InstanceDescriptor::from_env_or_default()); - let mut command_encoder_id_manager = IdentityManager::new(); - let mut command_buffer_id_manager = IdentityManager::new(); + let instance_desc = wgt::InstanceDescriptor::from_env_or_default(); + let instance = wgc::instance::Instance::new("player", &instance_desc); #[cfg(feature = "winit")] let surface = unsafe { - global.instance_create_surface( + instance.create_surface( window.display_handle().unwrap().into(), window.window_handle().unwrap().into(), - Some(wgc::id::Id::zip(0, 1)), ) } .unwrap(); @@ -93,50 +91,41 @@ fn main() { None => (wgt::Backends::all(), wgt::DeviceDescriptor::default()), }; - let adapter = global - .request_adapter( - &wgc::instance::RequestAdapterOptions { - #[cfg(feature = "winit")] - compatible_surface: Some(surface), - #[cfg(not(feature = "winit"))] - compatible_surface: None, - ..Default::default() - }, - backends, - Some(wgc::id::AdapterId::zip(0, 1)), - ) - .expect("Unable to obtain an adapter"); - - let info = global.adapter_get_info(adapter); + let adapter = Arc::new( + instance + .request_adapter( + &wgt::RequestAdapterOptions { + #[cfg(feature = "winit")] + compatible_surface: Some(&surface), + #[cfg(not(feature = "winit"))] + compatible_surface: None, + ..Default::default() + }, + backends, + ) + .expect("Unable to obtain an adapter"), + ); + + let info = adapter.get_info(); log::info!("Using '{}'", info.name); - let device = wgc::id::Id::zip(0, 1); - let queue = wgc::id::Id::zip(0, 1); - let res = global.adapter_request_device(adapter, &device_desc, Some(device), Some(queue)); - if let Err(e) = res { - panic!("{e:?}"); - } + let (device, queue) = adapter + .create_device_and_queue(&device_desc, instance_desc.flags) + .unwrap(); + + let mut player = Player::default(); log::info!("Executing actions"); #[cfg(not(feature = "winit"))] { - unsafe { global.device_start_graphics_debugger_capture(device) }; + unsafe { device.start_graphics_debugger_capture() }; while let Some(action) = actions.pop() { - global.process( - device, - queue, - action, - &dir, - &mut command_encoder_id_manager, - &mut command_buffer_id_manager, - ); + player.process(&device, &queue, action, &dir); } - unsafe { global.device_stop_graphics_debugger_capture(device) }; - global - .device_poll(device, wgt::PollType::wait_indefinitely()) - .unwrap(); + unsafe { device.stop_graphics_debugger_capture() }; + device.poll(wgt::PollType::wait_indefinitely()).unwrap(); } #[cfg(feature = "winit")] { @@ -170,33 +159,25 @@ fn main() { resize_config = Some(config); target.exit(); } else { - let error = - global.surface_configure(surface, device, &config); + let error = device.configure_surface(&surface, &config); if let Some(e) = error { panic!("{e:?}"); } } } - Some(trace::Action::Present(id)) => { + Some(trace::Action::Present(_id)) => { frame_count += 1; log::debug!("Presenting frame {frame_count}"); - global.surface_present(id).unwrap(); + surface.present().unwrap(); target.exit(); } - Some(trace::Action::DiscardSurfaceTexture(id)) => { + Some(trace::Action::DiscardSurfaceTexture(_id)) => { log::debug!("Discarding frame {frame_count}"); - global.surface_texture_discard(id).unwrap(); + surface.discard().unwrap(); target.exit(); } Some(action) => { - global.process( - device, - queue, - action, - &dir, - &mut command_encoder_id_manager, - &mut command_buffer_id_manager, - ); + player.process(&device, &queue, action, &dir); } None => { if !done { @@ -209,7 +190,7 @@ fn main() { } WindowEvent::Resized(_) => { if let Some(config) = resize_config.take() { - let error = global.surface_configure(surface, device, &config); + let error = device.configure_surface(&surface, &config); if let Some(e) = error { panic!("{e:?}"); } @@ -229,9 +210,7 @@ fn main() { }, Event::LoopExiting => { log::info!("Closing"); - global - .device_poll(device, wgt::PollType::wait_indefinitely()) - .unwrap(); + device.poll(wgt::PollType::wait_indefinitely()).unwrap(); } _ => {} } diff --git a/player/src/lib.rs b/player/src/lib.rs index 437f51b14bb..ececc1da0f8 100644 --- a/player/src/lib.rs +++ b/player/src/lib.rs @@ -6,196 +6,98 @@ extern crate wgpu_core as wgc; extern crate wgpu_types as wgt; -use wgc::{command::Command, device::trace, identity::IdentityManager}; +use std::{borrow::Cow, convert::Infallible, fs, path::Path, sync::Arc}; -use std::{borrow::Cow, fs, path::Path}; +use hashbrown::HashMap; -pub trait GlobalPlay { - fn encode_commands( - &self, - encoder: wgc::id::CommandEncoderId, - commands: Vec, - command_buffer_id_manager: &mut IdentityManager, - ) -> wgc::id::CommandBufferId; - fn process( - &self, - device: wgc::id::DeviceId, - queue: wgc::id::QueueId, - action: trace::Action, - dir: &Path, - command_encoder_id_manager: &mut IdentityManager, - command_buffer_id_manager: &mut IdentityManager, - ); +use wgc::{ + binding_model::BindingResource, + command::{ArcCommand, ArcReferences, BasePass, Command, PointerReferences}, + device::trace, + id::PointerId, +}; + +pub struct Player { + pipeline_layouts: HashMap< + wgc::id::PointerId, + Arc, + >, + shader_modules: HashMap< + wgc::id::PointerId, + Arc, + >, + bind_group_layouts: HashMap< + wgc::id::PointerId, + Arc, + >, + bind_groups: HashMap< + wgc::id::PointerId, + Arc, + >, + render_bundles: HashMap< + wgc::id::PointerId, + Arc, + >, + render_pipelines: HashMap< + wgc::id::PointerId, + Arc, + >, + compute_pipelines: HashMap< + wgc::id::PointerId, + Arc, + >, + pipeline_caches: HashMap< + wgc::id::PointerId, + Arc, + >, + query_sets: + HashMap, Arc>, + buffers: HashMap, Arc>, + textures: HashMap, Arc>, + texture_views: + HashMap, Arc>, + external_textures: HashMap< + wgc::id::PointerId, + Arc, + >, + samplers: HashMap, Arc>, + blas_s: HashMap, Arc>, + tlas_s: HashMap, Arc>, } -impl GlobalPlay for wgc::global::Global { - fn encode_commands( - &self, - encoder: wgc::id::CommandEncoderId, - commands: Vec, - command_buffer_id_manager: &mut IdentityManager, - ) -> wgc::id::CommandBufferId { - for command in commands { - match command { - Command::CopyBufferToBuffer { - src, - src_offset, - dst, - dst_offset, - size, - } => self - .command_encoder_copy_buffer_to_buffer( - encoder, src, src_offset, dst, dst_offset, size, - ) - .unwrap(), - Command::CopyBufferToTexture { src, dst, size } => self - .command_encoder_copy_buffer_to_texture(encoder, &src, &dst, &size) - .unwrap(), - Command::CopyTextureToBuffer { src, dst, size } => self - .command_encoder_copy_texture_to_buffer(encoder, &src, &dst, &size) - .unwrap(), - Command::CopyTextureToTexture { src, dst, size } => self - .command_encoder_copy_texture_to_texture(encoder, &src, &dst, &size) - .unwrap(), - Command::ClearBuffer { dst, offset, size } => self - .command_encoder_clear_buffer(encoder, dst, offset, size) - .unwrap(), - Command::ClearTexture { - dst, - subresource_range, - } => self - .command_encoder_clear_texture(encoder, dst, &subresource_range) - .unwrap(), - Command::WriteTimestamp { - query_set_id, - query_index, - } => self - .command_encoder_write_timestamp(encoder, query_set_id, query_index) - .unwrap(), - Command::ResolveQuerySet { - query_set_id, - start_query, - query_count, - destination, - destination_offset, - } => self - .command_encoder_resolve_query_set( - encoder, - query_set_id, - start_query, - query_count, - destination, - destination_offset, - ) - .unwrap(), - Command::PushDebugGroup(marker) => self - .command_encoder_push_debug_group(encoder, &marker) - .unwrap(), - Command::PopDebugGroup => self.command_encoder_pop_debug_group(encoder).unwrap(), - Command::InsertDebugMarker(marker) => self - .command_encoder_insert_debug_marker(encoder, &marker) - .unwrap(), - Command::RunComputePass { - base, - timestamp_writes, - } => { - self.compute_pass_end_with_unresolved_commands( - encoder, - base, - timestamp_writes.as_ref(), - ); - } - Command::RunRenderPass { - base, - target_colors, - target_depth_stencil, - timestamp_writes, - occlusion_query_set_id, - } => { - self.render_pass_end_with_unresolved_commands( - encoder, - base, - &target_colors, - target_depth_stencil.as_ref(), - timestamp_writes.as_ref(), - occlusion_query_set_id, - ); - } - Command::BuildAccelerationStructures { blas, tlas } => { - let blas_iter = blas.iter().map(|x| { - let geometries = match &x.geometries { - wgc::ray_tracing::TraceBlasGeometries::TriangleGeometries( - triangle_geometries, - ) => { - let iter = triangle_geometries.iter().map(|tg| { - wgc::ray_tracing::BlasTriangleGeometry { - size: &tg.size, - vertex_buffer: tg.vertex_buffer, - index_buffer: tg.index_buffer, - transform_buffer: tg.transform_buffer, - first_vertex: tg.first_vertex, - vertex_stride: tg.vertex_stride, - first_index: tg.first_index, - transform_buffer_offset: tg.transform_buffer_offset, - } - }); - wgc::ray_tracing::BlasGeometries::TriangleGeometries(Box::new(iter)) - } - }; - wgc::ray_tracing::BlasBuildEntry { - blas_id: x.blas_id, - geometries, - } - }); - - let tlas_iter = tlas.iter().map(|x| { - let instances = x.instances.iter().map(|instance| { - instance - .as_ref() - .map(|instance| wgc::ray_tracing::TlasInstance { - blas_id: instance.blas_id, - transform: &instance.transform, - custom_data: instance.custom_data, - mask: instance.mask, - }) - }); - wgc::ray_tracing::TlasPackage { - tlas_id: x.tlas_id, - instances: Box::new(instances), - lowest_unmodified: x.lowest_unmodified, - } - }); - - self.command_encoder_build_acceleration_structures( - encoder, blas_iter, tlas_iter, - ) - .unwrap(); - } - } - } - let (cmd_buf, error) = self.command_encoder_finish( - encoder, - &wgt::CommandBufferDescriptor { label: None }, - Some(command_buffer_id_manager.process()), - ); - if let Some(e) = error { - panic!("{e}"); +impl Default for Player { + fn default() -> Self { + Self { + pipeline_layouts: HashMap::new(), + shader_modules: HashMap::new(), + bind_group_layouts: HashMap::new(), + bind_groups: HashMap::new(), + render_bundles: HashMap::new(), + render_pipelines: HashMap::new(), + compute_pipelines: HashMap::new(), + pipeline_caches: HashMap::new(), + query_sets: HashMap::new(), + buffers: HashMap::new(), + textures: HashMap::new(), + texture_views: HashMap::new(), + external_textures: HashMap::new(), + samplers: HashMap::new(), + blas_s: HashMap::new(), + tlas_s: HashMap::new(), } - cmd_buf } +} - fn process( - &self, - device: wgc::id::DeviceId, - queue: wgc::id::QueueId, - action: trace::Action, +impl Player { + pub fn process( + &mut self, + device: &Arc, + queue: &Arc, + action: trace::Action, dir: &Path, - command_encoder_id_manager: &mut IdentityManager, - command_buffer_id_manager: &mut IdentityManager, ) { use wgc::device::trace::Action; log::debug!("action {action:?}"); - //TODO: find a way to force ID perishing without excessive `maintain()` calls. match action { Action::Init { .. } => { panic!("Unexpected Action::Init: has to be the first action only") @@ -206,96 +108,122 @@ impl GlobalPlay for wgc::global::Global { panic!("Unexpected Surface action: winit feature is not enabled") } Action::CreateBuffer(id, desc) => { - let (_, error) = self.device_create_buffer(device, &desc, Some(id)); - if let Some(e) = error { - panic!("{e}"); - } + let buffer = device.create_buffer(&desc).expect("create_buffer error"); + self.buffers.insert(id, buffer); } Action::FreeBuffer(id) => { - self.buffer_destroy(id); + // Note: buffer remains in the HashMap. "Free" and "Destroy" + // mean the opposite from WebGPU. + let buffer = self.buffers.get(&id).expect("invalid buffer"); + buffer.destroy(); } Action::DestroyBuffer(id) => { - self.buffer_drop(id); + let buffer = self.buffers.remove(&id).expect("invalid buffer"); + let _ = buffer.unmap(); } Action::CreateTexture(id, desc) => { - let (_, error) = self.device_create_texture(device, &desc, Some(id)); - if let Some(e) = error { - panic!("{e}"); - } + let texture = device.create_texture(&desc).expect("create_texture error"); + self.textures.insert(id, texture); } Action::FreeTexture(id) => { - self.texture_destroy(id); + // Note: texture remains in the HashMap. "Free" and "Destroy" + // mean the opposite from WebGPU. + let texture = self.textures.get(&id).expect("invalid texture"); + texture.destroy(); } Action::DestroyTexture(id) => { - self.texture_drop(id); + self.textures.remove(&id).expect("invalid texture"); } - Action::CreateTextureView { - id, - parent_id, - desc, - } => { - let (_, error) = self.texture_create_view(parent_id, &desc, Some(id)); - if let Some(e) = error { - panic!("{e}"); - } + Action::CreateTextureView { id, parent, desc } => { + let parent_texture = self.resolve_texture_id(parent); + let texture_view = device + .create_texture_view(&parent_texture, &desc) + .expect("create_texture_view error"); + self.texture_views.insert(id, texture_view); } Action::DestroyTextureView(id) => { - self.texture_view_drop(id).unwrap(); + self.texture_views + .remove(&id) + .expect("invalid texture view"); } Action::CreateExternalTexture { id, desc, planes } => { - let (_, error) = - self.device_create_external_texture(device, &desc, &planes, Some(id)); - if let Some(e) = error { - panic!("{e}"); - } + let planes = planes + .iter() + .map(|&id| self.resolve_texture_view_id(id)) + .collect::>(); + let external_texture = device + .create_external_texture(&desc, &planes) + .expect("create_external_texture error"); + self.external_textures.insert(id, external_texture); } Action::FreeExternalTexture(id) => { - self.external_texture_destroy(id); + // Note: external texture remains in the HashMap. "Free" and "Destroy" + // mean the opposite from WebGPU. + let external_texture = self + .external_textures + .get(&id) + .expect("invalid external texture"); + external_texture.destroy(); } Action::DestroyExternalTexture(id) => { - self.external_texture_drop(id); + self.external_textures + .remove(&id) + .expect("invalid external texture"); } Action::CreateSampler(id, desc) => { - let (_, error) = self.device_create_sampler(device, &desc, Some(id)); - if let Some(e) = error { - panic!("{e}"); - } + let sampler = device.create_sampler(&desc).expect("create_sampler error"); + self.samplers.insert(id, sampler); } Action::DestroySampler(id) => { - self.sampler_drop(id); + self.samplers.remove(&id).expect("invalid sampler"); } - Action::GetSurfaceTexture { id, parent_id } => { - self.surface_get_current_texture(parent_id, Some(id)) - .unwrap() - .texture - .unwrap(); + Action::GetSurfaceTexture { .. } => { + unimplemented!() } Action::CreateBindGroupLayout(id, desc) => { - let (_, error) = self.device_create_bind_group_layout(device, &desc, Some(id)); - if let Some(e) = error { - panic!("{e}"); - } + let bind_group_layout = device + .create_bind_group_layout(&desc) + .expect("create_bind_group_layout error"); + self.bind_group_layouts.insert(id, bind_group_layout); } Action::DestroyBindGroupLayout(id) => { - self.bind_group_layout_drop(id); + self.bind_group_layouts + .remove(&id) + .expect("invalid bind group layout"); } Action::CreatePipelineLayout(id, desc) => { - let (_, error) = self.device_create_pipeline_layout(device, &desc, Some(id)); - if let Some(e) = error { - panic!("{e}"); - } + let bind_group_layouts: Vec> = desc + .bind_group_layouts + .to_vec() + .into_iter() + .map(|bgl_id| self.resolve_bind_group_layout_id(bgl_id)) + .collect(); + + let resolved_desc = wgc::binding_model::ResolvedPipelineLayoutDescriptor { + label: desc.label.clone(), + bind_group_layouts: Cow::from(&bind_group_layouts), + push_constant_ranges: Cow::Borrowed(&*desc.push_constant_ranges), + }; + + let pipeline_layout = device + .create_pipeline_layout(&resolved_desc) + .expect("create_pipeline_layout error"); + self.pipeline_layouts.insert(id, pipeline_layout); } Action::DestroyPipelineLayout(id) => { - self.pipeline_layout_drop(id); + self.pipeline_layouts + .remove(&id) + .expect("invalid pipeline layout"); } Action::CreateBindGroup(id, desc) => { - let (_, error) = self.device_create_bind_group(device, &desc, Some(id)); - if let Some(e) = error { - panic!("{e}"); - } + let resolved_desc = self.resolve_bind_group_descriptor(desc); + let bind_group = device + .create_bind_group(resolved_desc) + .expect("create_bind_group error"); + self.bind_groups.insert(id, bind_group); } Action::DestroyBindGroup(id) => { - self.bind_group_drop(id); + let _bind_group = self.bind_groups.remove(&id).expect("invalid bind group"); } Action::CreateShaderModule { id, desc, data } => { log::debug!("Creating shader from {data}"); @@ -308,10 +236,10 @@ impl GlobalPlay for wgc::global::Global { } else { panic!("Unknown shader {data}"); }; - let (_, error) = self.device_create_shader_module(device, &desc, source, Some(id)); - if let Some(e) = error { - println!("shader compilation error:\n---{code}\n---\n{e}"); - } + match device.create_shader_module(&desc, source) { + Ok(module) => self.shader_modules.insert(id, module), + Err(e) => panic!("shader compilation error:\n---{code}\n---\n{e}"), + }; } Action::CreateShaderModulePassthrough { id, @@ -384,69 +312,68 @@ impl GlobalPlay for wgc::global::Global { glsl, wgsl, }; - let (_, error) = unsafe { - self.device_create_shader_module_passthrough(device, &desc, Some(id)) + match unsafe { device.create_shader_module_passthrough(&desc) } { + Ok(module) => self.shader_modules.insert(id, module), + Err(e) => panic!("shader compilation error:\n{e}"), }; - if let Some(e) = error { - println!("shader compilation error: {e}"); - } } Action::DestroyShaderModule(id) => { - self.shader_module_drop(id); + self.shader_modules + .remove(&id) + .expect("invalid shader module"); } Action::CreateComputePipeline { id, desc } => { - let (_, error) = self.device_create_compute_pipeline(device, &desc, Some(id)); - if let Some(e) = error { - panic!("{e}"); - } + let resolved_desc = self.resolve_compute_pipeline_descriptor(desc); + let pipeline = device + .create_compute_pipeline(resolved_desc) + .expect("create_compute_pipeline error"); + self.compute_pipelines.insert(id, pipeline); } Action::DestroyComputePipeline(id) => { - self.compute_pipeline_drop(id); + self.compute_pipelines + .remove(&id) + .expect("invalid compute pipeline"); } - Action::CreateRenderPipeline { id, desc } => { - let (_, error) = self.device_create_render_pipeline(device, &desc, Some(id)); - if let Some(e) = error { - panic!("{e}"); - } - } - Action::CreateMeshPipeline { id, desc } => { - let (_, error) = self.device_create_mesh_pipeline(device, &desc, Some(id)); - if let Some(e) = error { - panic!("{e}"); - } + Action::CreateGeneralRenderPipeline { id, desc } => { + // Note that this is the `General` version of the render + // pipeline descriptor that can represent either a conventional + // pipeline or a mesh shading pipeline. + let resolved_desc = self.resolve_render_pipeline_descriptor(desc); + let pipeline = device + .create_render_pipeline(resolved_desc) + .expect("create_render_pipeline error"); + self.render_pipelines.insert(id, pipeline); } Action::DestroyRenderPipeline(id) => { - self.render_pipeline_drop(id); + self.render_pipelines + .remove(&id) + .expect("invalid render pipeline"); } Action::CreatePipelineCache { id, desc } => { - let _ = unsafe { self.device_create_pipeline_cache(device, &desc, Some(id)) }; + let cache = unsafe { device.create_pipeline_cache(&desc) }.unwrap(); + self.pipeline_caches.insert(id, cache); } Action::DestroyPipelineCache(id) => { - self.pipeline_cache_drop(id); - } - Action::CreateRenderBundle { id, desc, base } => { - let bundle = - wgc::command::RenderBundleEncoder::new(&desc, device, Some(base)).unwrap(); - let (_, error) = self.render_bundle_encoder_finish( - bundle, - &wgt::RenderBundleDescriptor { label: desc.label }, - Some(id), - ); - if let Some(e) = error { - panic!("{e}"); - } + self.pipeline_caches + .remove(&id) + .expect("invalid pipeline cache"); + } + Action::CreateRenderBundle { .. } => { + unimplemented!("traced render bundles are not supported"); } Action::DestroyRenderBundle(id) => { - self.render_bundle_drop(id); + self.render_bundles + .remove(&id) + .expect("invalid render bundle"); } Action::CreateQuerySet { id, desc } => { - let (_, error) = self.device_create_query_set(device, &desc, Some(id)); - if let Some(e) = error { - panic!("{e}"); - } + let query_set = device + .create_query_set(&desc) + .expect("create_query_set error"); + self.query_sets.insert(id, query_set); } Action::DestroyQuerySet(id) => { - self.query_set_drop(id); + self.query_sets.remove(&id).expect("invalid query set"); } Action::WriteBuffer { id, @@ -454,14 +381,17 @@ impl GlobalPlay for wgc::global::Global { range, queued, } => { + let buffer = self.resolve_buffer_id(id); let bin = std::fs::read(dir.join(data)).unwrap(); let size = (range.end - range.start) as usize; if queued { - self.queue_write_buffer(queue, id, range.start, &bin) - .unwrap(); + queue + .write_buffer(buffer, range.start, &bin) + .expect("Queue::write_buffer error"); } else { - self.device_set_buffer_data(id, range.start, &bin[..size]) - .unwrap(); + device + .set_buffer_data(&buffer, range.start, &bin[..size]) + .expect("Device::set_buffer_data error"); } } Action::WriteTexture { @@ -470,37 +400,901 @@ impl GlobalPlay for wgc::global::Global { layout, size, } => { + let to = self.resolve_texel_copy_texture_info(to); let bin = std::fs::read(dir.join(data)).unwrap(); - self.queue_write_texture(queue, &to, &bin, &layout, &size) - .unwrap(); + queue + .write_texture(to, &bin, &layout, &size) + .expect("Queue::write_texture error"); } Action::Submit(_index, ref commands) if commands.is_empty() => { - self.queue_submit(queue, &[]).unwrap(); + queue.submit(&[]).unwrap(); } Action::Submit(_index, commands) => { - let (encoder, error) = self.device_create_command_encoder( - device, - &wgt::CommandEncoderDescriptor { label: None }, - Some(command_encoder_id_manager.process()), - ); - if let Some(e) = error { - panic!("{e}"); - } - let cmdbuf = self.encode_commands(encoder, commands, command_buffer_id_manager); - self.queue_submit(queue, &[cmdbuf]).unwrap(); + let resolved_commands: Vec<_> = commands + .into_iter() + .map(|cmd| self.resolve_command(cmd)) + .collect(); + let buffer = wgc::command::CommandBuffer::from_trace(device, resolved_commands); + queue.submit(&[buffer]).unwrap(); } Action::CreateBlas { id, desc, sizes } => { - self.device_create_blas(device, &desc, sizes, Some(id)); + let blas = device.create_blas(&desc, sizes).expect("create_blas error"); + self.blas_s.insert(id, blas); } Action::DestroyBlas(id) => { - self.blas_drop(id); + self.blas_s.remove(&id).expect("invalid blas"); } Action::CreateTlas { id, desc } => { - self.device_create_tlas(device, &desc, Some(id)); + let tlas = device.create_tlas(&desc).expect("create_tlas error"); + self.tlas_s.insert(id, tlas); } Action::DestroyTlas(id) => { - self.tlas_drop(id); + self.tlas_s.remove(&id).expect("invalid tlas"); + } + } + } + + pub fn resolve_buffer_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.buffers.get(&id).expect("invalid buffer").clone() + } + + fn resolve_texture_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.textures.get(&id).expect("invalid texture").clone() + } + + fn resolve_texture_view_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.texture_views + .get(&id) + .expect("invalid texture view") + .clone() + } + + fn resolve_external_texture_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.external_textures + .get(&id) + .expect("invalid external texture") + .clone() + } + + fn resolve_sampler_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.samplers.get(&id).expect("invalid sampler").clone() + } + + fn resolve_bind_group_layout_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.bind_group_layouts + .get(&id) + .expect("invalid bind group layout") + .clone() + } + + fn resolve_bind_group_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.bind_groups + .get(&id) + .expect("invalid bind group") + .clone() + } + + fn resolve_pipeline_layout_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.pipeline_layouts + .get(&id) + .expect("invalid pipeline layout") + .clone() + } + + fn resolve_shader_module_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.shader_modules + .get(&id) + .expect("invalid shader module") + .clone() + } + + fn resolve_render_pipeline_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.render_pipelines + .get(&id) + .expect("invalid render pipeline") + .clone() + } + + fn resolve_compute_pipeline_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.compute_pipelines + .get(&id) + .expect("invalid compute pipeline") + .clone() + } + + fn resolve_pipeline_cache_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.pipeline_caches + .get(&id) + .expect("invalid pipeline cache") + .clone() + } + + fn resolve_render_bundle_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.render_bundles + .get(&id) + .expect("invalid render bundle") + .clone() + } + + fn resolve_query_set_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.query_sets.get(&id).expect("invalid query set").clone() + } + + fn resolve_blas_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.blas_s.get(&id).expect("invalid blas").clone() + } + + fn resolve_tlas_id( + &self, + id: wgc::id::PointerId, + ) -> Arc { + self.tlas_s.get(&id).expect("invalid tlas").clone() + } + + fn resolve_texel_copy_texture_info( + &self, + info: wgt::TexelCopyTextureInfo>, + ) -> wgt::TexelCopyTextureInfo> { + wgt::TexelCopyTextureInfo { + texture: self.resolve_texture_id(info.texture), + mip_level: info.mip_level, + origin: info.origin, + aspect: info.aspect, + } + } + + fn resolve_compute_pipeline_descriptor<'a>( + &self, + desc: wgc::device::trace::TraceComputePipelineDescriptor<'a>, + ) -> wgc::pipeline::ResolvedComputePipelineDescriptor<'a> { + wgc::pipeline::ResolvedComputePipelineDescriptor { + label: desc.label, + layout: desc.layout.map(|id| self.resolve_pipeline_layout_id(id)), + stage: wgc::pipeline::ResolvedProgrammableStageDescriptor { + module: self.resolve_shader_module_id(desc.stage.module), + entry_point: desc.stage.entry_point, + constants: desc.stage.constants, + zero_initialize_workgroup_memory: desc.stage.zero_initialize_workgroup_memory, + }, + cache: desc.cache.map(|id| self.resolve_pipeline_cache_id(id)), + } + } + + fn resolve_render_pipeline_descriptor<'a>( + &self, + desc: wgc::device::trace::TraceGeneralRenderPipelineDescriptor<'a>, + ) -> wgc::pipeline::ResolvedGeneralRenderPipelineDescriptor<'a> { + let layout = desc.layout.map(|id| self.resolve_pipeline_layout_id(id)); + + let vertex = match desc.vertex { + wgc::pipeline::RenderPipelineVertexProcessor::Vertex(vertex_state) => { + wgc::pipeline::RenderPipelineVertexProcessor::Vertex( + wgc::pipeline::ResolvedVertexState { + stage: wgc::pipeline::ResolvedProgrammableStageDescriptor { + module: self.resolve_shader_module_id(vertex_state.stage.module), + entry_point: vertex_state.stage.entry_point, + constants: vertex_state.stage.constants, + zero_initialize_workgroup_memory: vertex_state + .stage + .zero_initialize_workgroup_memory, + }, + buffers: vertex_state.buffers, + }, + ) + } + wgc::pipeline::RenderPipelineVertexProcessor::Mesh(task_state, mesh_state) => { + let resolved_task = task_state.map(|task| wgc::pipeline::ResolvedTaskState { + stage: wgc::pipeline::ResolvedProgrammableStageDescriptor { + module: self.resolve_shader_module_id(task.stage.module), + entry_point: task.stage.entry_point, + constants: task.stage.constants, + zero_initialize_workgroup_memory: task + .stage + .zero_initialize_workgroup_memory, + }, + }); + let resolved_mesh = wgc::pipeline::ResolvedMeshState { + stage: wgc::pipeline::ResolvedProgrammableStageDescriptor { + module: self.resolve_shader_module_id(mesh_state.stage.module), + entry_point: mesh_state.stage.entry_point, + constants: mesh_state.stage.constants, + zero_initialize_workgroup_memory: mesh_state + .stage + .zero_initialize_workgroup_memory, + }, + }; + wgc::pipeline::RenderPipelineVertexProcessor::Mesh(resolved_task, resolved_mesh) + } + }; + + let fragment = desc + .fragment + .map(|fragment_state| wgc::pipeline::ResolvedFragmentState { + stage: wgc::pipeline::ResolvedProgrammableStageDescriptor { + module: self.resolve_shader_module_id(fragment_state.stage.module), + entry_point: fragment_state.stage.entry_point, + constants: fragment_state.stage.constants, + zero_initialize_workgroup_memory: fragment_state + .stage + .zero_initialize_workgroup_memory, + }, + targets: fragment_state.targets, + }); + + wgc::pipeline::ResolvedGeneralRenderPipelineDescriptor { + label: desc.label, + layout, + vertex, + primitive: desc.primitive, + depth_stencil: desc.depth_stencil, + multisample: desc.multisample, + fragment, + multiview: desc.multiview, + cache: desc.cache.map(|id| self.resolve_pipeline_cache_id(id)), + } + } + + fn resolve_bind_group_descriptor<'a>( + &self, + desc: wgc::device::trace::TraceBindGroupDescriptor<'a>, + ) -> wgc::binding_model::ResolvedBindGroupDescriptor<'a> { + let layout = self.resolve_bind_group_layout_id(desc.layout); + + let entries: Vec = desc + .entries + .to_vec() + .into_iter() + .map(|entry| { + let resource = match entry.resource { + BindingResource::Buffer(buffer_binding) => { + let buffer = self.resolve_buffer_id(buffer_binding.buffer); + wgc::binding_model::ResolvedBindingResource::Buffer( + wgc::binding_model::ResolvedBufferBinding { + buffer, + offset: buffer_binding.offset, + size: buffer_binding.size, + }, + ) + } + BindingResource::BufferArray(buffer_bindings) => { + let resolved_buffers: Vec<_> = buffer_bindings + .to_vec() + .into_iter() + .map(|bb| { + let buffer = self.resolve_buffer_id(bb.buffer); + wgc::binding_model::ResolvedBufferBinding { + buffer, + offset: bb.offset, + size: bb.size, + } + }) + .collect(); + wgc::binding_model::ResolvedBindingResource::BufferArray(Cow::Owned( + resolved_buffers, + )) + } + BindingResource::Sampler(sampler_id) => { + let sampler = self.resolve_sampler_id(sampler_id); + wgc::binding_model::ResolvedBindingResource::Sampler(sampler) + } + BindingResource::SamplerArray(sampler_ids) => { + let resolved_samplers: Vec<_> = sampler_ids + .to_vec() + .into_iter() + .map(|id| self.resolve_sampler_id(id)) + .collect(); + wgc::binding_model::ResolvedBindingResource::SamplerArray(Cow::Owned( + resolved_samplers, + )) + } + BindingResource::TextureView(texture_view_id) => { + let texture_view = self.resolve_texture_view_id(texture_view_id); + wgc::binding_model::ResolvedBindingResource::TextureView(texture_view) + } + BindingResource::TextureViewArray(texture_view_ids) => { + let resolved_views: Vec<_> = texture_view_ids + .to_vec() + .into_iter() + .map(|id| self.resolve_texture_view_id(id)) + .collect(); + wgc::binding_model::ResolvedBindingResource::TextureViewArray(Cow::Owned( + resolved_views, + )) + } + BindingResource::AccelerationStructure(tlas_id) => { + let tlas = self.resolve_tlas_id(tlas_id); + wgc::binding_model::ResolvedBindingResource::AccelerationStructure(tlas) + } + BindingResource::ExternalTexture(external_texture_id) => { + let external_texture = + self.resolve_external_texture_id(external_texture_id); + wgc::binding_model::ResolvedBindingResource::ExternalTexture( + external_texture, + ) + } + }; + + wgc::binding_model::ResolvedBindGroupEntry { + binding: entry.binding, + resource, + } + }) + .collect(); + + wgc::binding_model::ResolvedBindGroupDescriptor { + label: desc.label.clone(), + layout, + entries: entries.into(), + } + } + + fn resolve_command(&self, command: Command) -> ArcCommand { + match command { + Command::CopyBufferToBuffer { + src, + src_offset, + dst, + dst_offset, + size, + } => Command::CopyBufferToBuffer { + src: self.resolve_buffer_id(src), + src_offset, + dst: self.resolve_buffer_id(dst), + dst_offset, + size, + }, + Command::CopyBufferToTexture { src, dst, size } => Command::CopyBufferToTexture { + src: self.resolve_texel_copy_buffer_info(src), + dst: self.resolve_texel_copy_texture_info(dst), + size, + }, + Command::CopyTextureToBuffer { src, dst, size } => Command::CopyTextureToBuffer { + src: self.resolve_texel_copy_texture_info(src), + dst: self.resolve_texel_copy_buffer_info(dst), + size, + }, + Command::CopyTextureToTexture { src, dst, size } => Command::CopyTextureToTexture { + src: self.resolve_texel_copy_texture_info(src), + dst: self.resolve_texel_copy_texture_info(dst), + size, + }, + Command::ClearBuffer { dst, offset, size } => Command::ClearBuffer { + dst: self.resolve_buffer_id(dst), + offset, + size, + }, + Command::ClearTexture { + dst, + subresource_range, + } => Command::ClearTexture { + dst: self.resolve_texture_id(dst), + subresource_range, + }, + Command::WriteTimestamp { + query_set, + query_index, + } => Command::WriteTimestamp { + query_set: self.resolve_query_set_id(query_set), + query_index, + }, + Command::ResolveQuerySet { + query_set, + start_query, + query_count, + destination, + destination_offset, + } => Command::ResolveQuerySet { + query_set: self.resolve_query_set_id(query_set), + start_query, + query_count, + destination: self.resolve_buffer_id(destination), + destination_offset, + }, + Command::PushDebugGroup(label) => Command::PushDebugGroup(label.clone()), + Command::PopDebugGroup => Command::PopDebugGroup, + Command::InsertDebugMarker(label) => Command::InsertDebugMarker(label.clone()), + Command::RunComputePass { + pass, + timestamp_writes, + } => Command::RunComputePass { + pass: self.resolve_compute_pass(pass), + timestamp_writes: timestamp_writes.map(|tw| self.resolve_pass_timestamp_writes(tw)), + }, + Command::RunRenderPass { + pass, + color_attachments, + depth_stencil_attachment, + timestamp_writes, + occlusion_query_set, + } => Command::RunRenderPass { + pass: self.resolve_render_pass(pass), + color_attachments: self.resolve_color_attachments(color_attachments), + depth_stencil_attachment: depth_stencil_attachment + .map(|att| self.resolve_depth_stencil_attachment(att)), + timestamp_writes: timestamp_writes.map(|tw| self.resolve_pass_timestamp_writes(tw)), + occlusion_query_set: occlusion_query_set.map(|qs| self.resolve_query_set_id(qs)), + }, + Command::BuildAccelerationStructures { blas, tlas } => { + Command::BuildAccelerationStructures { + blas: blas + .into_iter() + .map(|entry| self.resolve_blas_build_entry(entry)) + .collect(), + tlas: tlas + .into_iter() + .map(|package| self.resolve_tlas_package(package)) + .collect(), + } + } + Command::TransitionResources { + buffer_transitions, + texture_transitions, + } => Command::TransitionResources { + buffer_transitions: buffer_transitions + .into_iter() + .map(|trans| self.resolve_buffer_transition(trans)) + .collect(), + texture_transitions: texture_transitions + .into_iter() + .map(|trans| self.resolve_texture_transition(trans)) + .collect(), + }, + } + } + + // Helper methods for command resolution + fn resolve_texel_copy_buffer_info( + &self, + info: wgt::TexelCopyBufferInfo>, + ) -> wgt::TexelCopyBufferInfo> { + wgt::TexelCopyBufferInfo { + buffer: self + .buffers + .get(&info.buffer) + .cloned() + .expect("invalid buffer"), + layout: info.layout, + } + } + + fn resolve_compute_pass( + &self, + pass: BasePass, Infallible>, + ) -> BasePass, Infallible> { + let BasePass { + label, + error, + commands, + dynamic_offsets, + push_constant_data, + string_data, + } = pass; + + BasePass { + label, + error, + commands: commands + .into_iter() + .map(|cmd| self.resolve_compute_command(cmd)) + .collect(), + dynamic_offsets, + push_constant_data, + string_data, + } + } + + fn resolve_render_pass( + &self, + pass: BasePass, Infallible>, + ) -> BasePass, Infallible> { + let BasePass { + label, + error, + commands, + dynamic_offsets, + push_constant_data, + string_data, + } = pass; + + BasePass { + label, + error, + commands: commands + .into_iter() + .map(|cmd| self.resolve_render_command(cmd)) + .collect(), + dynamic_offsets, + push_constant_data, + string_data, + } + } + + fn resolve_compute_command( + &self, + command: wgc::command::ComputeCommand, + ) -> wgc::command::ComputeCommand { + use wgc::command::ComputeCommand as C; + match command { + C::SetBindGroup { + index, + num_dynamic_offsets, + bind_group, + } => C::SetBindGroup { + index, + num_dynamic_offsets, + bind_group: bind_group.map(|bg| self.resolve_bind_group_id(bg)), + }, + C::SetPipeline(id) => C::SetPipeline(self.resolve_compute_pipeline_id(id)), + C::SetPushConstant { + offset, + size_bytes, + values_offset, + } => C::SetPushConstant { + offset, + size_bytes, + values_offset, + }, + C::Dispatch(groups) => C::Dispatch(groups), + C::DispatchIndirect { buffer, offset } => C::DispatchIndirect { + buffer: self.resolve_buffer_id(buffer), + offset, + }, + C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len }, + C::PopDebugGroup => C::PopDebugGroup, + C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len }, + C::WriteTimestamp { + query_set, + query_index, + } => C::WriteTimestamp { + query_set: self.resolve_query_set_id(query_set), + query_index, + }, + C::BeginPipelineStatisticsQuery { + query_set, + query_index, + } => C::BeginPipelineStatisticsQuery { + query_set: self.resolve_query_set_id(query_set), + query_index, + }, + C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery, + } + } + + fn resolve_render_command( + &self, + command: wgc::command::RenderCommand, + ) -> wgc::command::RenderCommand { + use wgc::command::RenderCommand as C; + match command { + C::SetBindGroup { + index, + num_dynamic_offsets, + bind_group, + } => C::SetBindGroup { + index, + num_dynamic_offsets, + bind_group: bind_group.map(|bg| self.resolve_bind_group_id(bg)), + }, + C::SetPipeline(id) => C::SetPipeline(self.resolve_render_pipeline_id(id)), + C::SetIndexBuffer { + buffer, + index_format, + offset, + size, + } => C::SetIndexBuffer { + buffer: self.resolve_buffer_id(buffer), + index_format, + offset, + size, + }, + C::SetVertexBuffer { + slot, + buffer, + offset, + size, + } => C::SetVertexBuffer { + slot, + buffer: self.resolve_buffer_id(buffer), + offset, + size, + }, + C::SetBlendConstant(color) => C::SetBlendConstant(color), + C::SetStencilReference(val) => C::SetStencilReference(val), + C::SetViewport { + rect, + depth_min, + depth_max, + } => C::SetViewport { + rect, + depth_min, + depth_max, + }, + C::SetScissor(rect) => C::SetScissor(rect), + C::SetPushConstant { + stages, + offset, + size_bytes, + values_offset, + } => C::SetPushConstant { + stages, + offset, + size_bytes, + values_offset, + }, + C::Draw { + vertex_count, + instance_count, + first_vertex, + first_instance, + } => C::Draw { + vertex_count, + instance_count, + first_vertex, + first_instance, + }, + C::DrawIndexed { + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + } => C::DrawIndexed { + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + }, + C::DrawMeshTasks { + group_count_x, + group_count_y, + group_count_z, + } => C::DrawMeshTasks { + group_count_x, + group_count_y, + group_count_z, + }, + C::DrawIndirect { + buffer, + offset, + count, + family, + vertex_or_index_limit, + instance_limit, + } => C::DrawIndirect { + buffer: self.resolve_buffer_id(buffer), + offset, + count, + family, + vertex_or_index_limit, + instance_limit, + }, + C::MultiDrawIndirectCount { + buffer, + offset, + count_buffer, + count_buffer_offset, + max_count, + family, + } => C::MultiDrawIndirectCount { + buffer: self.resolve_buffer_id(buffer), + offset, + count_buffer: self.resolve_buffer_id(count_buffer), + count_buffer_offset, + max_count, + family, + }, + C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len }, + C::PopDebugGroup => C::PopDebugGroup, + C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len }, + C::WriteTimestamp { + query_set, + query_index, + } => C::WriteTimestamp { + query_set: self.resolve_query_set_id(query_set), + query_index, + }, + C::BeginOcclusionQuery { query_index } => C::BeginOcclusionQuery { query_index }, + C::EndOcclusionQuery => C::EndOcclusionQuery, + C::BeginPipelineStatisticsQuery { + query_set, + query_index, + } => C::BeginPipelineStatisticsQuery { + query_set: self.resolve_query_set_id(query_set), + query_index, + }, + C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery, + C::ExecuteBundle(bundle) => C::ExecuteBundle(self.resolve_render_bundle_id(bundle)), + } + } + + fn resolve_pass_timestamp_writes( + &self, + writes: wgc::command::PassTimestampWrites>, + ) -> wgc::command::PassTimestampWrites> { + wgc::command::PassTimestampWrites { + query_set: self.resolve_query_set_id(writes.query_set), + beginning_of_pass_write_index: writes.beginning_of_pass_write_index, + end_of_pass_write_index: writes.end_of_pass_write_index, + } + } + + fn resolve_color_attachments( + &self, + attachments: wgc::command::ColorAttachments>, + ) -> wgc::command::ColorAttachments> { + attachments + .into_iter() + .map(|opt| { + opt.map(|att| wgc::command::RenderPassColorAttachment { + view: self.resolve_texture_view_id(att.view), + depth_slice: att.depth_slice, + resolve_target: att + .resolve_target + .map(|rt| self.resolve_texture_view_id(rt)), + load_op: att.load_op, + store_op: att.store_op, + }) + }) + .collect() + } + + fn resolve_depth_stencil_attachment( + &self, + attachment: wgc::command::ResolvedRenderPassDepthStencilAttachment< + PointerId, + >, + ) -> wgc::command::ResolvedRenderPassDepthStencilAttachment> + { + wgc::command::ResolvedRenderPassDepthStencilAttachment { + view: self.resolve_texture_view_id(attachment.view), + depth: attachment.depth, + stencil: attachment.stencil, + } + } + + fn resolve_blas_build_entry( + &self, + entry: wgc::ray_tracing::OwnedBlasBuildEntry, + ) -> wgc::ray_tracing::OwnedBlasBuildEntry { + wgc::ray_tracing::OwnedBlasBuildEntry { + blas: self.resolve_blas_id(entry.blas), + geometries: self.resolve_blas_geometries(entry.geometries), + } + } + + fn resolve_tlas_package( + &self, + package: wgc::ray_tracing::OwnedTlasPackage, + ) -> wgc::ray_tracing::OwnedTlasPackage { + wgc::ray_tracing::OwnedTlasPackage { + tlas: self.resolve_tlas_id(package.tlas), + instances: package + .instances + .into_iter() + .map(|opt| opt.map(|inst| self.resolve_tlas_instance(inst))) + .collect(), + lowest_unmodified: package.lowest_unmodified, + } + } + + // Helper functions for ray tracing structures + fn resolve_blas_geometries( + &self, + geometries: wgc::ray_tracing::OwnedBlasGeometries, + ) -> wgc::ray_tracing::OwnedBlasGeometries { + match geometries { + wgc::ray_tracing::OwnedBlasGeometries::TriangleGeometries(geos) => { + wgc::ray_tracing::OwnedBlasGeometries::TriangleGeometries( + geos.into_iter() + .map(|geo| self.resolve_blas_triangle_geometry(geo)) + .collect(), + ) } } } + + fn resolve_blas_triangle_geometry( + &self, + geometry: wgc::ray_tracing::OwnedBlasTriangleGeometry, + ) -> wgc::ray_tracing::OwnedBlasTriangleGeometry { + wgc::ray_tracing::OwnedBlasTriangleGeometry { + size: geometry.size, + vertex_buffer: self.resolve_buffer_id(geometry.vertex_buffer), + index_buffer: geometry.index_buffer.map(|buf| self.resolve_buffer_id(buf)), + transform_buffer: geometry + .transform_buffer + .map(|buf| self.resolve_buffer_id(buf)), + first_vertex: geometry.first_vertex, + vertex_stride: geometry.vertex_stride, + first_index: geometry.first_index, + transform_buffer_offset: geometry.transform_buffer_offset, + } + } + + fn resolve_tlas_instance( + &self, + instance: wgc::ray_tracing::OwnedTlasInstance, + ) -> wgc::ray_tracing::OwnedTlasInstance { + wgc::ray_tracing::OwnedTlasInstance { + blas: self.resolve_blas_id(instance.blas), + transform: instance.transform, + custom_data: instance.custom_data, + mask: instance.mask, + } + } + + fn resolve_buffer_transition( + &self, + trans: wgt::BufferTransition>, + ) -> wgt::BufferTransition> { + wgt::BufferTransition { + buffer: self + .buffers + .get(&trans.buffer) + .cloned() + .expect("invalid buffer"), + state: trans.state, + } + } + + fn resolve_texture_transition( + &self, + trans: wgt::TextureTransition>, + ) -> wgt::TextureTransition> { + wgt::TextureTransition { + texture: self + .textures + .get(&trans.texture) + .cloned() + .expect("invalid texture"), + selector: trans.selector.clone(), + state: trans.state, + } + } } diff --git a/player/tests/player/data/bind-group.ron b/player/tests/player/data/bind-group.ron index 5b3a256d37a..5a20aabfce8 100644 --- a/player/tests/player/data/bind-group.ron +++ b/player/tests/player/data/bind-group.ron @@ -2,13 +2,13 @@ features: "", expectations: [], //not crash! actions: [ - CreateBuffer(Id(0, 1), ( + CreateBuffer(PointerId(0x10), ( label: None, size: 16, usage: "UNIFORM", mapped_at_creation: false, )), - CreateBindGroupLayout(Id(0, 1), ( + CreateBindGroupLayout(PointerId(0x10), ( label: None, entries: [ ( @@ -20,29 +20,29 @@ ), ], )), - CreateBindGroup(Id(0, 1), ( + CreateBindGroup(PointerId(0x10), ( label: None, - layout: Id(0, 1), + layout: PointerId(0x10), entries: [ ( binding: 0, resource: Buffer(( - buffer: Id(0, 1), + buffer: PointerId(0x10), offset: 0, size: None, )), ) ], )), - CreatePipelineLayout(Id(0, 1), ( + CreatePipelineLayout(PointerId(0x10), ( label: Some("empty"), bind_group_layouts: [ - Id(0, 1), + PointerId(0x10), ], push_constant_ranges: [], )), CreateShaderModule( - id: Id(0, 1), + id: PointerId(0x10), desc: ( label: None, flags: (bits: 3), @@ -50,12 +50,12 @@ data: "empty.wgsl", ), CreateComputePipeline( - id: Id(0, 1), + id: PointerId(0x10), desc: ( label: None, - layout: Some(Id(0, 1)), + layout: Some(PointerId(0x10)), stage: ( - module: Id(0, 1), + module: PointerId(0x10), entry_point: None, constants: {}, zero_initialize_workgroup_memory: true, @@ -65,14 +65,14 @@ ), Submit(1, [ RunComputePass( - base: ( + pass: ( commands: [ SetBindGroup( index: 0, num_dynamic_offsets: 0, - bind_group_id: Some(Id(0, 1)), + bind_group: Some(PointerId(0x10)), ), - SetPipeline(Id(0, 1)), + SetPipeline(PointerId(0x10)), ], dynamic_offsets: [], string_data: [], diff --git a/player/tests/player/data/buffer-copy.ron b/player/tests/player/data/buffer-copy.ron index 10a1822bd7a..93b868c90e8 100644 --- a/player/tests/player/data/buffer-copy.ron +++ b/player/tests/player/data/buffer-copy.ron @@ -3,14 +3,14 @@ expectations: [ ( name: "basic", - buffer: (index: 0, epoch: 1), + buffer: PointerId(0x10), offset: 0, data: Raw([0x00, 0x00, 0x80, 0xBF]), ) ], actions: [ CreateBuffer( - Id(0, 1), + PointerId(0x10), ( label: Some("dummy"), size: 16, @@ -19,7 +19,7 @@ ), ), WriteBuffer( - id: Id(0, 1), + id: PointerId(0x10), data: "data1.bin", range: ( start: 0, diff --git a/player/tests/player/data/clear-buffer-texture.ron b/player/tests/player/data/clear-buffer-texture.ron index f6c929252c9..489e1d6639e 100644 --- a/player/tests/player/data/clear-buffer-texture.ron +++ b/player/tests/player/data/clear-buffer-texture.ron @@ -3,13 +3,13 @@ expectations: [ ( name: "Quad", - buffer: (index: 0, epoch: 1), + buffer: PointerId(0x10), offset: 0, data: File("clear-texture.bin", 16384), ), ( name: "buffer clear", - buffer: (index: 1, epoch: 1), + buffer: PointerId(0x110), offset: 0, data: Raw([ 0x00, 0x00, 0x80, 0xBF, @@ -20,7 +20,7 @@ ) ], actions: [ - CreateTexture(Id(0, 1), ( + CreateTexture(PointerId(0x10), ( label: Some("Output Texture"), size: ( width: 64, @@ -36,7 +36,7 @@ // First fill the texture to ensure it wasn't just zero initialized or "happened" to be zero. WriteTexture( to: ( - texture: Id(0, 1), + texture: PointerId(0x10), mip_level: 0, array_layer: 0, ), @@ -52,7 +52,7 @@ ), ), CreateBuffer( - Id(0, 1), + PointerId(0x10), ( label: Some("Output Buffer"), size: 16384, @@ -62,7 +62,7 @@ ), CreateBuffer( - Id(1, 1), + PointerId(0x110), ( label: Some("Buffer to be cleared"), size: 16, @@ -72,7 +72,7 @@ ), // Make sure there is something in the buffer, otherwise it might be just zero init! WriteBuffer( - id: Id(1, 1), + id: PointerId(0x110), data: "data1.bin", range: ( start: 0, @@ -82,7 +82,7 @@ ), Submit(1, [ ClearTexture( - dst: Id(0, 1), + dst: PointerId(0x10), subresource_range: ImageSubresourceRange( aspect: all, baseMipLevel: 0, @@ -93,12 +93,12 @@ ), CopyTextureToBuffer( src: ( - texture: Id(0, 1), + texture: PointerId(0x10), mip_level: 0, array_layer: 0, ), dst: ( - buffer: Id(0, 1), + buffer: PointerId(0x10), layout: ( offset: 0, bytes_per_row: Some(256), @@ -112,7 +112,7 @@ ), // Partial clear to prove ClearBuffer( - dst: Id(1, 1), + dst: PointerId(0x110), offset: 4, size: Some(8), ) diff --git a/player/tests/player/data/pipeline-statistics-query.ron b/player/tests/player/data/pipeline-statistics-query.ron index 95acc414c87..2d1c208845e 100644 --- a/player/tests/player/data/pipeline-statistics-query.ron +++ b/player/tests/player/data/pipeline-statistics-query.ron @@ -3,19 +3,19 @@ expectations: [ ( name: "Queried number of compute invocations is correct", - buffer: (index: 0, epoch: 1), + buffer: PointerId(0x10), offset: 0, data: U64([0x0, 0x2A]), ), ], actions: [ - CreatePipelineLayout(Id(0, 1), ( + CreatePipelineLayout(PointerId(0x10), ( label: Some("empty"), bind_group_layouts: [], push_constant_ranges: [], )), CreateShaderModule( - id: Id(0, 1), + id: PointerId(0x10), desc: ( label: None, flags: (bits: 3), @@ -23,12 +23,12 @@ data: "empty.wgsl", ), CreateComputePipeline( - id: Id(0, 1), + id: PointerId(0x10), desc: ( label: None, - layout: Some(Id(0, 1)), + layout: Some(PointerId(0x10)), stage: ( - module: Id(0, 1), + module: PointerId(0x10), entry_point: None, constants: {}, zero_initialize_workgroup_memory: true, @@ -37,7 +37,7 @@ ), ), CreateQuerySet( - id: Id(0, 1), + id: PointerId(0x10), desc: ( label: Some("Compute Invocation QuerySet"), count: 2, @@ -45,7 +45,7 @@ ), ), CreateBuffer( - Id(0, 1), + PointerId(0x10), ( label: Some("Compute Invocation Result Buffer"), size: 16, @@ -55,11 +55,11 @@ ), Submit(1, [ RunComputePass( - base: ( + pass: ( commands: [ - SetPipeline(Id(0, 1)), + SetPipeline(PointerId(0x10)), BeginPipelineStatisticsQuery( - query_set_id: Id(0, 1), + query_set: PointerId(0x10), query_index: 0, ), Dispatch((2, 3, 7,)), @@ -71,10 +71,10 @@ ), ), ResolveQuerySet( - query_set_id: Id(0, 1), + query_set: PointerId(0x10), start_query: 0, query_count: 1, - destination: Id(0, 1), + destination: PointerId(0x10), destination_offset: 0, ) ]), diff --git a/player/tests/player/data/quad.ron b/player/tests/player/data/quad.ron index 7bb56a6be0f..7ef16eead4f 100644 --- a/player/tests/player/data/quad.ron +++ b/player/tests/player/data/quad.ron @@ -3,21 +3,21 @@ expectations: [ ( name: "Quad", - buffer: (index: 0, epoch: 1), + buffer: PointerId(0x10), offset: 0, data: File("quad.bin", 16384), ) ], actions: [ CreateShaderModule( - id: Id(0, 1), + id: PointerId(0x10), desc: ( label: None, flags: (bits: 3), ), data: "quad.wgsl", ), - CreateTexture(Id(0, 1), ( + CreateTexture(PointerId(0x10), ( label: Some("Output Texture"), size: ( width: 64, @@ -31,12 +31,12 @@ view_formats: [], )), CreateTextureView( - id: Id(0, 1), - parent_id: Id(0, 1), + id: PointerId(0x10), + parent: PointerId(0x10), desc: (), ), CreateBuffer( - Id(0, 1), + PointerId(0x10), ( label: Some("Output Buffer"), size: 16384, @@ -44,29 +44,31 @@ mapped_at_creation: false, ), ), - CreatePipelineLayout(Id(0, 1), ( + CreatePipelineLayout(PointerId(0x10), ( label: None, bind_group_layouts: [], push_constant_ranges: [], )), - CreateRenderPipeline( - id: Id(0, 1), + CreateGeneralRenderPipeline( + id: PointerId(0x10), desc: ( label: None, - layout: Some(Id(0, 1)), - vertex: ( - stage: ( - module: Id(0, 1), - entry_point: None, - constants: {}, - zero_initialize_workgroup_memory: true, - vertex_pulling_transform: false, + layout: Some(PointerId(0x10)), + vertex: Vertex( + VertexState( + stage: ( + module: PointerId(0x10), + entry_point: None, + constants: {}, + zero_initialize_workgroup_memory: true, + vertex_pulling_transform: false, + ), + buffers: [], ), - buffers: [], ), fragment: Some(( stage: ( - module: Id(0, 1), + module: PointerId(0x10), entry_point: None, constants: {}, zero_initialize_workgroup_memory: true, @@ -82,9 +84,9 @@ ), Submit(1, [ RunRenderPass( - base: ( + pass: ( commands: [ - SetPipeline(Id(0, 1)), + SetPipeline(PointerId(0x10)), Draw( vertex_count: 3, instance_count: 1, @@ -96,9 +98,9 @@ string_data: [], push_constant_data: [], ), - target_colors: [ + color_attachments: [ Some(( - view: Id(0, 1), + view: PointerId(0x10), resolve_target: None, load_op: clear(Color( r: 0, @@ -109,16 +111,16 @@ store_op: store, )), ], - target_depth_stencil: None, + depth_stencil_attachment: None, ), CopyTextureToBuffer( src: ( - texture: Id(0, 1), + texture: PointerId(0x10), mip_level: 0, array_layer: 0, ), dst: ( - buffer: Id(0, 1), + buffer: PointerId(0x10), layout: ( offset: 0, bytes_per_row: Some(256), diff --git a/player/tests/player/data/zero-init-buffer.ron b/player/tests/player/data/zero-init-buffer.ron index 143f91f51f8..6e153aa616b 100644 --- a/player/tests/player/data/zero-init-buffer.ron +++ b/player/tests/player/data/zero-init-buffer.ron @@ -4,19 +4,19 @@ // Ensuring that mapping zero-inits buffers. ( name: "mapped_at_creation: false, with MAP_WRITE", - buffer: (index: 0, epoch: 1), + buffer: PointerId(0x10), offset: 0, data: Raw([0x00, 0x00, 0x00, 0x00]), ), ( name: "mapped_at_creation: false, without MAP_WRITE", - buffer: (index: 1, epoch: 1), + buffer: PointerId(0x110), offset: 0, data: Raw([0x00, 0x00, 0x00, 0x00]), ), ( name: "partially written buffer", - buffer: (index: 2, epoch: 1), + buffer: PointerId(0x120), offset: 0, data: Raw([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBF, @@ -29,7 +29,7 @@ // (by observing correct side effects of compute shader reading & writing values) ( name: "buffer has correct values", - buffer: (index: 3, epoch: 1), + buffer: PointerId(0x130), offset: 0, data: Raw([0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, @@ -39,7 +39,7 @@ ], actions: [ CreateBuffer( - Id(0, 1), + PointerId(0x10), ( label: Some("mapped_at_creation: false, with MAP_WRITE"), size: 16, @@ -48,7 +48,7 @@ ), ), CreateBuffer( - Id(1, 1), + PointerId(0x110), ( label: Some("mapped_at_creation: false, without MAP_WRITE"), size: 16, @@ -57,7 +57,7 @@ ), ), CreateBuffer( - Id(2, 1), + PointerId(0x120), ( label: Some("partially written"), size: 24, @@ -66,7 +66,7 @@ ), ), WriteBuffer( - id: Id(2, 1), + id: PointerId(0x120), data: "data1.bin", range: ( start: 4, @@ -75,20 +75,20 @@ queued: true, ), CreateShaderModule( - id: Id(0, 1), + id: PointerId(0x10), desc: ( label: None, flags: (bits: 3), ), data: "zero-init-buffer-for-binding.wgsl", ), - CreateBuffer(Id(3, 1), ( + CreateBuffer(PointerId(0x130), ( label: Some("used in binding"), size: 16, usage: "STORAGE | MAP_READ", mapped_at_creation: false, )), - CreateBindGroupLayout(Id(0, 1), ( + CreateBindGroupLayout(PointerId(0x10), ( label: None, entries: [ ( @@ -105,34 +105,34 @@ ), ], )), - CreateBindGroup(Id(0, 1), ( + CreateBindGroup(PointerId(0x10), ( label: None, - layout: Id(0, 1), + layout: PointerId(0x10), entries: [ ( binding: 0, resource: Buffer(( - buffer: Id(3, 1), + buffer: PointerId(0x130), offset: 0, size: Some(16), )), ), ], )), - CreatePipelineLayout(Id(0, 1), ( + CreatePipelineLayout(PointerId(0x10), ( label: None, bind_group_layouts: [ - Id(0, 1), + PointerId(0x10), ], push_constant_ranges: [], )), CreateComputePipeline( - id: Id(0, 1), + id: PointerId(0x10), desc: ( label: None, - layout: Some(Id(0, 1)), + layout: Some(PointerId(0x10)), stage: ( - module: Id(0, 1), + module: PointerId(0x10), entry_point: None, constants: {}, zero_initialize_workgroup_memory: true, @@ -142,14 +142,14 @@ ), Submit(1, [ RunComputePass( - base: ( + pass: ( label: None, commands: [ - SetPipeline(Id(0, 1)), + SetPipeline(PointerId(0x10)), SetBindGroup( index: 0, num_dynamic_offsets: 0, - bind_group_id: Some(Id(0, 1)), + bind_group: Some(PointerId(0x10)), ), Dispatch((4, 1, 1)), ], diff --git a/player/tests/player/data/zero-init-texture-binding.ron b/player/tests/player/data/zero-init-texture-binding.ron index 8920c41b7a5..a8d5bbc18fe 100644 --- a/player/tests/player/data/zero-init-texture-binding.ron +++ b/player/tests/player/data/zero-init-texture-binding.ron @@ -3,13 +3,13 @@ expectations: [ ( name: "Sampled Texture", - buffer: (index: 0, epoch: 1), + buffer: PointerId(0x10), offset: 0, data: File("zero-16k.bin", 16384), ), ( name: "Storage Texture", - buffer: (index: 1, epoch: 1), + buffer: PointerId(0x110), offset: 0, data: File("zero-16k.bin", 16384), ), @@ -17,7 +17,7 @@ // MISSING: Partial views ], actions: [ - CreateTexture(Id(0, 1), ( + CreateTexture(PointerId(0x10), ( label: Some("Sampled Texture"), size: ( width: 64, @@ -31,12 +31,12 @@ view_formats: [], )), CreateTextureView( - id: Id(0, 1), - parent_id: Id(0, 1), + id: PointerId(0x10), + parent: PointerId(0x10), desc: (), ), CreateBuffer( - Id(0, 1), + PointerId(0x10), ( label: Some("Sampled Texture Buffer"), size: 16384, @@ -44,7 +44,7 @@ mapped_at_creation: false, ), ), - CreateTexture(Id(1, 1), ( + CreateTexture(PointerId(0x110), ( label: Some("Storage Texture"), size: ( width: 64, @@ -58,12 +58,12 @@ view_formats: [], )), CreateTextureView( - id: Id(1, 1), - parent_id: Id(1, 1), + id: PointerId(0x110), + parent: PointerId(0x110), desc: (), ), CreateBuffer( - Id(1, 1), + PointerId(0x110), ( label: Some("Storage Texture Buffer"), size: 16384, @@ -73,7 +73,7 @@ ), - CreateBindGroupLayout(Id(0, 1), ( + CreateBindGroupLayout(PointerId(0x10), ( label: None, entries: [ ( @@ -98,29 +98,29 @@ ), ], )), - CreateBindGroup(Id(0, 1), ( + CreateBindGroup(PointerId(0x10), ( label: None, - layout: Id(0, 1), + layout: PointerId(0x10), entries: [ ( binding: 0, - resource: TextureView(Id(0, 1)), + resource: TextureView(PointerId(0x10)), ), ( binding: 1, - resource: TextureView(Id(1, 1)), + resource: TextureView(PointerId(0x110)), ), ], )), - CreatePipelineLayout(Id(0, 1), ( + CreatePipelineLayout(PointerId(0x10), ( label: None, bind_group_layouts: [ - Id(0, 1), + PointerId(0x10), ], push_constant_ranges: [], )), CreateShaderModule( - id: Id(0, 1), + id: PointerId(0x10), desc: ( label: None, flags: (bits: 3), @@ -128,12 +128,12 @@ data: "zero-init-texture-binding.wgsl", ), CreateComputePipeline( - id: Id(0, 1), + id: PointerId(0x10), desc: ( label: None, - layout: Some(Id(0, 1)), + layout: Some(PointerId(0x10)), stage: ( - module: Id(0, 1), + module: PointerId(0x10), entry_point: None, constants: {}, zero_initialize_workgroup_memory: true, @@ -144,13 +144,13 @@ Submit(1, [ RunComputePass( - base: ( + pass: ( commands: [ - SetPipeline(Id(0, 1)), + SetPipeline(PointerId(0x10)), SetBindGroup( index: 0, num_dynamic_offsets: 0, - bind_group_id: Some(Id(0, 1)), + bind_group: Some(PointerId(0x10)), ), Dispatch((4, 1, 1)), ], @@ -161,12 +161,12 @@ ), CopyTextureToBuffer( src: ( - texture: Id(0, 1), + texture: PointerId(0x10), mip_level: 0, array_layer: 0, ), dst: ( - buffer: Id(0, 1), + buffer: PointerId(0x10), layout: ( offset: 0, bytes_per_row: Some(256), @@ -180,12 +180,12 @@ ), CopyTextureToBuffer( src: ( - texture: Id(1, 1), + texture: PointerId(0x110), mip_level: 0, array_layer: 0, ), dst: ( - buffer: Id(1, 1), + buffer: PointerId(0x110), layout: ( offset: 0, bytes_per_row: Some(256), diff --git a/player/tests/player/data/zero-init-texture-copytobuffer.ron b/player/tests/player/data/zero-init-texture-copytobuffer.ron index 2346e8b4099..c547af92f65 100644 --- a/player/tests/player/data/zero-init-texture-copytobuffer.ron +++ b/player/tests/player/data/zero-init-texture-copytobuffer.ron @@ -3,14 +3,14 @@ expectations: [ ( name: "Copy to Buffer", - buffer: (index: 0, epoch: 1), + buffer: PointerId(0x10), offset: 0, data: File("zero-16k.bin", 16384), ), // MISSING: Partial copies ], actions: [ - CreateTexture(Id(0, 1), ( + CreateTexture(PointerId(0x10), ( label: Some("Copy To Buffer Texture"), size: ( width: 64, @@ -24,7 +24,7 @@ view_formats: [], )), CreateBuffer( - Id(0, 1), + PointerId(0x10), ( label: Some("Copy to Buffer Buffer"), size: 16384, @@ -35,12 +35,12 @@ Submit(1, [ CopyTextureToBuffer( src: ( - texture: Id(0, 1), + texture: PointerId(0x10), mip_level: 0, array_layer: 0, ), dst: ( - buffer: Id(0, 1), + buffer: PointerId(0x10), layout: ( offset: 0, bytes_per_row: Some(256), diff --git a/player/tests/player/data/zero-init-texture-rendertarget.ron b/player/tests/player/data/zero-init-texture-rendertarget.ron index 7f06de9b4e8..49923f64ac5 100644 --- a/player/tests/player/data/zero-init-texture-rendertarget.ron +++ b/player/tests/player/data/zero-init-texture-rendertarget.ron @@ -3,14 +3,14 @@ expectations: [ ( name: "Render Target", - buffer: (index: 0, epoch: 1), + buffer: PointerId(0x10), offset: 0, data: File("zero-16k.bin", 16384), ), // MISSING: Partial view. ], actions: [ - CreateTexture(Id(0, 1), ( + CreateTexture(PointerId(0x10), ( label: Some("Render Target Texture"), size: ( width: 64, @@ -24,12 +24,12 @@ view_formats: [], )), CreateTextureView( - id: Id(0, 1), - parent_id: Id(0, 1), + id: PointerId(0x10), + parent: PointerId(0x10), desc: (), ), CreateBuffer( - Id(0, 1), + PointerId(0x10), ( label: Some("Render Target Buffer"), size: 16384, @@ -40,15 +40,15 @@ Submit(1, [ RunRenderPass( - base: ( + pass: ( commands: [], dynamic_offsets: [], string_data: [], push_constant_data: [], ), - target_colors: [ + color_attachments: [ Some(( - view: Id(0, 1), + view: PointerId(0x10), resolve_target: None, load_op: load, store_op: store, @@ -57,16 +57,16 @@ ), )), ], - target_depth_stencil: None, + depth_stencil_attachment: None, ), CopyTextureToBuffer( src: ( - texture: Id(0, 1), + texture: PointerId(0x10), mip_level: 0, array_layer: 0, ), dst: ( - buffer: Id(0, 1), + buffer: PointerId(0x10), layout: ( offset: 0, bytes_per_row: Some(256), diff --git a/player/tests/player/main.rs b/player/tests/player/main.rs index 700a6739048..11a5a3cbd88 100644 --- a/player/tests/player/main.rs +++ b/player/tests/player/main.rs @@ -13,20 +13,15 @@ extern crate wgpu_core as wgc; extern crate wgpu_types as wgt; -use player::GlobalPlay; +use player::Player; use std::{ fs::{read_to_string, File}, io::{Read, Seek, SeekFrom}, path::{Path, PathBuf}, slice, + sync::Arc, }; -use wgc::identity::IdentityManager; - -#[derive(serde::Deserialize)] -struct RawId { - index: u32, - epoch: u32, -} +use wgc::command::PointerReferences; #[derive(serde::Deserialize)] enum ExpectedData { @@ -48,7 +43,7 @@ impl ExpectedData { #[derive(serde::Deserialize)] struct Expectation { name: String, - buffer: RawId, + buffer: wgc::id::PointerId, offset: wgt::BufferAddress, data: ExpectedData, } @@ -57,7 +52,7 @@ struct Expectation { struct Test<'a> { features: wgt::Features, expectations: Vec, - actions: Vec>, + actions: Vec>, } fn map_callback(status: Result<(), wgc::resource::BufferAccessError>) { @@ -82,48 +77,34 @@ impl Test<'_> { fn run( self, dir: &Path, - global: &wgc::global::Global, - adapter: wgc::id::AdapterId, - test_num: u32, + instance_desc: &wgt::InstanceDescriptor, + adapter: Arc, ) { - let device_id = wgc::id::Id::zip(test_num, 1); - let queue_id = wgc::id::Id::zip(test_num, 1); - let res = global.adapter_request_device( - adapter, - &wgt::DeviceDescriptor { - label: None, - required_features: self.features, - required_limits: wgt::Limits::default(), - experimental_features: unsafe { wgt::ExperimentalFeatures::enabled() }, - memory_hints: wgt::MemoryHints::default(), - trace: wgt::Trace::Off, - }, - Some(device_id), - Some(queue_id), - ); - if let Err(e) = res { - panic!("{e:?}"); - } + let (device, queue) = adapter + .create_device_and_queue( + &wgt::DeviceDescriptor { + label: None, + required_features: self.features, + required_limits: wgt::Limits::default(), + experimental_features: unsafe { wgt::ExperimentalFeatures::enabled() }, + memory_hints: wgt::MemoryHints::default(), + trace: wgt::Trace::Off, + }, + instance_desc.flags, + ) + .unwrap(); + + let mut player = Player::default(); - let mut command_encoder_id_manager = IdentityManager::new(); - let mut command_buffer_id_manager = IdentityManager::new(); println!("\t\t\tRunning..."); for action in self.actions { - global.process( - device_id, - queue_id, - action, - dir, - &mut command_encoder_id_manager, - &mut command_buffer_id_manager, - ); + player.process(&device, &queue, action, dir); } println!("\t\t\tMapping..."); for expect in &self.expectations { - let buffer = wgc::id::Id::zip(expect.buffer.index, expect.buffer.epoch); - global - .buffer_map_async( - buffer, + player + .resolve_buffer_id(expect.buffer) + .map_async( expect.offset, Some(expect.data.len() as u64), wgc::resource::BufferMapOperation { @@ -135,25 +116,18 @@ impl Test<'_> { } println!("\t\t\tWaiting..."); - global - .device_poll( - device_id, - wgt::PollType::Wait { - submission_index: None, - timeout: Some(std::time::Duration::from_secs(1)), // Tests really shouldn't need longer than that! - }, - ) + device + .poll(wgt::PollType::Wait { + submission_index: None, + timeout: Some(std::time::Duration::from_secs(1)), // Tests really shouldn't need longer than that! + }) .unwrap(); for expect in self.expectations { println!("\t\t\tChecking {}", expect.name); - let buffer = wgc::id::Id::zip(expect.buffer.index, expect.buffer.epoch); - let (ptr, size) = global - .buffer_get_mapped_range( - buffer, - expect.offset, - Some(expect.data.len() as wgt::BufferAddress), - ) + let (ptr, size) = player + .resolve_buffer_id(expect.buffer) + .get_mapped_range(expect.offset, Some(expect.data.len() as wgt::BufferAddress)) .unwrap(); let contents = unsafe { slice::from_raw_parts(ptr.as_ptr(), size as usize) }; let expected_data = match expect.data { @@ -204,30 +178,26 @@ impl Corpus { if !corpus.backends.contains(backend.into()) { continue; } - let mut test_num = 0; for test_path in &corpus.tests { println!("\t\tTest '{test_path:?}'"); - let global = wgc::global::Global::new( - "test", - &wgt::InstanceDescriptor::from_env_or_default(), - ); - let adapter = match global.request_adapter( - &wgc::instance::RequestAdapterOptions { + let instance_desc = wgt::InstanceDescriptor::from_env_or_default(); + let instance = wgc::instance::Instance::new("test", &instance_desc); + let adapter = match instance.request_adapter( + &wgt::RequestAdapterOptions { power_preference: wgt::PowerPreference::None, force_fallback_adapter: false, compatible_surface: None, }, wgt::Backends::from(backend), - Some(wgc::id::Id::zip(0, 1)), ) { - Ok(adapter) => adapter, + Ok(adapter) => Arc::new(adapter), Err(_) => continue, }; println!("\tBackend {backend:?}"); - let supported_features = global.adapter_features(adapter); - let downlevel_caps = global.adapter_downlevel_capabilities(adapter); + let supported_features = adapter.features(); + let downlevel_caps = adapter.downlevel_capabilities(); let test = Test::load(dir.join(test_path), backend); if !supported_features.contains(test.features) { @@ -244,8 +214,7 @@ impl Corpus { println!("\t\tSkipped due to missing compute shader capability"); continue; } - test.run(dir, &global, adapter, test_num); - test_num += 1; + test.run(dir, &instance_desc, adapter); } } } diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 170d0f74928..c63756f89bf 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -69,7 +69,14 @@ observe_locks = ["std", "dep:ron", "serde/serde_derive"] # -------------------------------------------------------------------- ## Enables serialization via `serde` on common wgpu types. -serde = ["dep:serde", "wgpu-types/serde", "arrayvec/serde", "hashbrown/serde"] +serde = [ + "dep:serde", + "wgpu-types/serde", + "arrayvec/serde", + "hashbrown/serde", + "smallvec/serde", + "macro_rules_attribute", +] ## Enable API tracing. trace = ["serde", "std", "dep:ron", "naga/serialize", "wgpu-types/trace"] @@ -178,6 +185,7 @@ document-features.workspace = true hashbrown.workspace = true indexmap.workspace = true log.workspace = true +macro_rules_attribute = { workspace = true, optional = true } once_cell = { workspace = true, features = ["std"] } parking_lot.workspace = true profiling = { workspace = true, default-features = false } diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index 2cbac7bfa6b..3869d23776c 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -889,7 +889,8 @@ where } /// cbindgen:ignore -pub type ResolvedPipelineLayoutDescriptor<'a> = PipelineLayoutDescriptor<'a, Arc>; +pub type ResolvedPipelineLayoutDescriptor<'a, BGL = Arc> = + PipelineLayoutDescriptor<'a, BGL>; #[derive(Debug)] pub struct PipelineLayout { diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index 8e8bf90b146..37e63e23cef 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -96,11 +96,13 @@ use thiserror::Error; use wgpu_hal::ShouldBeNonZeroExt; use wgt::error::{ErrorType, WebGpuError}; +#[cfg(feature = "trace")] +use crate::command::ArcReferences; use crate::{ binding_model::{BindError, BindGroup, PipelineLayout}, command::{ - BasePass, BindGroupStateChange, ColorAttachmentError, DrawError, MapPassErr, - PassErrorScope, RenderCommandError, StateChange, + BasePass, BindGroupStateChange, ColorAttachmentError, DrawError, IdReferences, MapPassErr, + PassErrorScope, RenderCommand, RenderCommandError, StateChange, }, device::{ AttachmentData, Device, DeviceError, MissingDownlevelFlags, RenderPassContext, @@ -120,11 +122,7 @@ use crate::{ Label, LabelHelpers, }; -use super::{ - pass, - render_command::{ArcRenderCommand, RenderCommand}, - DrawCommandFamily, DrawKind, -}; +use super::{pass, render_command::ArcRenderCommand, DrawCommandFamily, DrawKind}; /// Describes a [`RenderBundleEncoder`]. #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] @@ -158,7 +156,7 @@ pub struct RenderBundleEncoderDescriptor<'a> { #[derive(Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct RenderBundleEncoder { - base: BasePass, + base: BasePass, Infallible>, parent_id: id::DeviceId, pub(crate) context: RenderPassContext, pub(crate) is_depth_read_only: bool, @@ -175,7 +173,6 @@ impl RenderBundleEncoder { pub fn new( desc: &RenderBundleEncoderDescriptor, parent_id: id::DeviceId, - base: Option>, ) -> Result { let (is_depth_read_only, is_stencil_read_only) = match desc.depth_stencil { Some(ds) => { @@ -197,7 +194,7 @@ impl RenderBundleEncoder { //TODO: validate that attachment formats are renderable, // have expected aspects, support multisampling. Ok(Self { - base: base.unwrap_or_else(|| BasePass::new(&desc.label)), + base: BasePass::new(&desc.label), parent_id, context: RenderPassContext { attachments: AttachmentData { @@ -252,11 +249,6 @@ impl RenderBundleEncoder { } } - #[cfg(feature = "trace")] - pub(crate) fn to_base_pass(&self) -> BasePass { - self.base.clone() - } - pub fn parent(&self) -> id::DeviceId { self.parent_id } @@ -305,12 +297,12 @@ impl RenderBundleEncoder { let base = &self.base; - for &command in &base.commands { + for command in &base.commands { match command { - RenderCommand::SetBindGroup { + &RenderCommand::SetBindGroup { index, num_dynamic_offsets, - bind_group_id, + bind_group, } => { let scope = PassErrorScope::SetBindGroup; set_bind_group( @@ -319,11 +311,11 @@ impl RenderBundleEncoder { &base.dynamic_offsets, index, num_dynamic_offsets, - bind_group_id, + bind_group, ) .map_pass_err(scope)?; } - RenderCommand::SetPipeline(pipeline_id) => { + &RenderCommand::SetPipeline(pipeline) => { let scope = PassErrorScope::SetPipelineRender; set_pipeline( &mut state, @@ -331,12 +323,12 @@ impl RenderBundleEncoder { &self.context, self.is_depth_read_only, self.is_stencil_read_only, - pipeline_id, + pipeline, ) .map_pass_err(scope)?; } - RenderCommand::SetIndexBuffer { - buffer_id, + &RenderCommand::SetIndexBuffer { + buffer, index_format, offset, size, @@ -345,24 +337,24 @@ impl RenderBundleEncoder { set_index_buffer( &mut state, &buffer_guard, - buffer_id, + buffer, index_format, offset, size, ) .map_pass_err(scope)?; } - RenderCommand::SetVertexBuffer { + &RenderCommand::SetVertexBuffer { slot, - buffer_id, + buffer, offset, size, } => { let scope = PassErrorScope::SetVertexBuffer; - set_vertex_buffer(&mut state, &buffer_guard, slot, buffer_id, offset, size) + set_vertex_buffer(&mut state, &buffer_guard, slot, buffer, offset, size) .map_pass_err(scope)?; } - RenderCommand::SetPushConstant { + &RenderCommand::SetPushConstant { stages, offset, size_bytes, @@ -372,7 +364,7 @@ impl RenderBundleEncoder { set_push_constant(&mut state, stages, offset, size_bytes, values_offset) .map_pass_err(scope)?; } - RenderCommand::Draw { + &RenderCommand::Draw { vertex_count, instance_count, first_vertex, @@ -392,7 +384,7 @@ impl RenderBundleEncoder { ) .map_pass_err(scope)?; } - RenderCommand::DrawIndexed { + &RenderCommand::DrawIndexed { index_count, instance_count, first_index, @@ -414,7 +406,7 @@ impl RenderBundleEncoder { ) .map_pass_err(scope)?; } - RenderCommand::DrawMeshTasks { + &RenderCommand::DrawMeshTasks { group_count_x, group_count_y, group_count_z, @@ -432,11 +424,13 @@ impl RenderBundleEncoder { ) .map_pass_err(scope)?; } - RenderCommand::DrawIndirect { - buffer_id, + &RenderCommand::DrawIndirect { + buffer, offset, count: 1, family, + vertex_or_index_limit: None, + instance_limit: None, } => { let scope = PassErrorScope::Draw { kind: DrawKind::DrawIndirect, @@ -446,32 +440,39 @@ impl RenderBundleEncoder { &mut state, &base.dynamic_offsets, &buffer_guard, - buffer_id, + buffer, offset, family, ) .map_pass_err(scope)?; } - RenderCommand::DrawIndirect { .. } - | RenderCommand::MultiDrawIndirectCount { .. } - | RenderCommand::PushDebugGroup { color: _, len: _ } - | RenderCommand::InsertDebugMarker { color: _, len: _ } - | RenderCommand::PopDebugGroup => { + &RenderCommand::DrawIndirect { + count, + vertex_or_index_limit, + instance_limit, + .. + } => { + unreachable!("unexpected (multi-)draw indirect with count {count}, vertex_or_index_limits {vertex_or_index_limit:?}, instance_limit {instance_limit:?} found in a render bundle"); + } + &RenderCommand::MultiDrawIndirectCount { .. } + | &RenderCommand::PushDebugGroup { color: _, len: _ } + | &RenderCommand::InsertDebugMarker { color: _, len: _ } + | &RenderCommand::PopDebugGroup => { unimplemented!("not supported by a render bundle") } // Must check the TIMESTAMP_QUERY_INSIDE_PASSES feature - RenderCommand::WriteTimestamp { .. } - | RenderCommand::BeginOcclusionQuery { .. } - | RenderCommand::EndOcclusionQuery - | RenderCommand::BeginPipelineStatisticsQuery { .. } - | RenderCommand::EndPipelineStatisticsQuery => { + &RenderCommand::WriteTimestamp { .. } + | &RenderCommand::BeginOcclusionQuery { .. } + | &RenderCommand::EndOcclusionQuery + | &RenderCommand::BeginPipelineStatisticsQuery { .. } + | &RenderCommand::EndPipelineStatisticsQuery => { unimplemented!("not supported by a render bundle") } - RenderCommand::ExecuteBundle(_) - | RenderCommand::SetBlendConstant(_) - | RenderCommand::SetStencilReference(_) - | RenderCommand::SetViewport { .. } - | RenderCommand::SetScissor(_) => unreachable!("not supported by a render bundle"), + &RenderCommand::ExecuteBundle(_) + | &RenderCommand::SetBlendConstant(_) + | &RenderCommand::SetStencilReference(_) + | &RenderCommand::SetViewport { .. } + | &RenderCommand::SetScissor(_) => unreachable!("not supported by a render bundle"), } } @@ -518,13 +519,13 @@ impl RenderBundleEncoder { pub fn set_index_buffer( &mut self, - buffer_id: id::BufferId, + buffer: id::BufferId, index_format: wgt::IndexFormat, offset: wgt::BufferAddress, size: Option, ) { self.base.commands.push(RenderCommand::SetIndexBuffer { - buffer_id, + buffer, index_format, offset, size, @@ -899,8 +900,8 @@ fn multi_draw_indirect( count: 1, family, - vertex_or_index_limit, - instance_limit, + vertex_or_index_limit: Some(vertex_or_index_limit), + instance_limit: Some(instance_limit), }); Ok(()) } @@ -941,6 +942,7 @@ pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor>; //Note: here, `RenderBundle` is just wrapping a raw stream of render commands. // The plan is to back it by an actual Vulkan secondary buffer, D3D12 Bundle, // or Metal indirect command buffer. +/// cbindgen:ignore #[derive(Debug)] pub struct RenderBundle { // Normalized command stream. It can be executed verbatim, @@ -971,6 +973,11 @@ unsafe impl Send for RenderBundle {} unsafe impl Sync for RenderBundle {} impl RenderBundle { + #[cfg(feature = "trace")] + pub(crate) fn to_base_pass(&self) -> BasePass, Infallible> { + self.base.clone() + } + /// Actually encode the contents into a native command buffer. /// /// This is partially duplicating the logic of `render_pass_end`. @@ -1142,8 +1149,9 @@ impl RenderBundle { buffer, *offset, *family, - *vertex_or_index_limit, - *instance_limit, + vertex_or_index_limit + .expect("finalized render bundle missing vertex_or_index_limit"), + instance_limit.expect("finalized render bundle missing instance_limit"), )?; let dst_buffer = @@ -1590,7 +1598,7 @@ impl State { /// Error encountered when finishing recording a render bundle. #[derive(Clone, Debug, Error)] -pub(super) enum RenderBundleErrorInner { +pub enum RenderBundleErrorInner { #[error(transparent)] Device(#[from] DeviceError), #[error(transparent)] @@ -1692,7 +1700,7 @@ pub mod bundle_ffi { bundle.base.commands.push(RenderCommand::SetBindGroup { index, num_dynamic_offsets: offset_length, - bind_group_id, + bind_group: bind_group_id, }); } @@ -1719,7 +1727,7 @@ pub mod bundle_ffi { ) { bundle.base.commands.push(RenderCommand::SetVertexBuffer { slot, - buffer_id, + buffer: buffer_id, offset, size, }); @@ -1813,10 +1821,12 @@ pub mod bundle_ffi { offset: BufferAddress, ) { bundle.base.commands.push(RenderCommand::DrawIndirect { - buffer_id, + buffer: buffer_id, offset, count: 1, family: DrawCommandFamily::Draw, + vertex_or_index_limit: None, + instance_limit: None, }); } @@ -1826,10 +1836,12 @@ pub mod bundle_ffi { offset: BufferAddress, ) { bundle.base.commands.push(RenderCommand::DrawIndirect { - buffer_id, + buffer: buffer_id, offset, count: 1, family: DrawCommandFamily::DrawIndexed, + vertex_or_index_limit: None, + instance_limit: None, }); } diff --git a/wgpu-core/src/command/clear.rs b/wgpu-core/src/command/clear.rs index 5c8838f69e5..82bf960e78a 100644 --- a/wgpu-core/src/command/clear.rs +++ b/wgpu-core/src/command/clear.rs @@ -1,8 +1,6 @@ use alloc::{sync::Arc, vec::Vec}; use core::ops::Range; -#[cfg(feature = "trace")] -use crate::command::Command as TraceCommand; use crate::{ api_log, command::{encoder::EncodingState, ArcCommand, EncoderStateError}, @@ -119,11 +117,6 @@ impl Global { let cmd_enc = hub.command_encoders.get(command_encoder_id); let mut cmd_buf_data = cmd_enc.data.lock(); - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.trace() { - list.push(TraceCommand::ClearBuffer { dst, offset, size }); - } - cmd_buf_data.push_with(|| -> Result<_, ClearError> { Ok(ArcCommand::ClearBuffer { dst: self.resolve_buffer_id(dst)?, @@ -147,14 +140,6 @@ impl Global { let cmd_enc = hub.command_encoders.get(command_encoder_id); let mut cmd_buf_data = cmd_enc.data.lock(); - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.trace() { - list.push(TraceCommand::ClearTexture { - dst, - subresource_range: *subresource_range, - }); - } - cmd_buf_data.push_with(|| -> Result<_, ClearError> { Ok(ArcCommand::ClearTexture { dst: self.resolve_texture_id(dst)?, diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index c5d321ad2dd..1cac5f705c7 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -419,73 +419,6 @@ impl Global { } } - /// Note that this differs from [`Self::compute_pass_end`], it will - /// create a new pass, replay the commands and end the pass. - /// - /// # Panics - /// On any error. - #[doc(hidden)] - #[cfg(any(feature = "serde", feature = "replay"))] - pub fn compute_pass_end_with_unresolved_commands( - &self, - encoder_id: id::CommandEncoderId, - base: BasePass, - timestamp_writes: Option<&PassTimestampWrites>, - ) { - #[cfg(feature = "trace")] - { - let cmd_enc = self.hub.command_encoders.get(encoder_id); - let mut cmd_buf_data = cmd_enc.data.lock(); - let cmd_buf_data = cmd_buf_data.get_inner(); - - if let Some(ref mut list) = cmd_buf_data.trace_commands { - list.push(crate::command::Command::RunComputePass { - base: BasePass { - label: base.label.clone(), - error: None, - commands: base.commands.clone(), - dynamic_offsets: base.dynamic_offsets.clone(), - string_data: base.string_data.clone(), - push_constant_data: base.push_constant_data.clone(), - }, - timestamp_writes: timestamp_writes.cloned(), - }); - } - } - - let BasePass { - label, - error: _, - commands, - dynamic_offsets, - string_data, - push_constant_data, - } = base; - - let (mut compute_pass, encoder_error) = self.command_encoder_begin_compute_pass( - encoder_id, - &ComputePassDescriptor { - label: label.as_deref().map(Cow::Borrowed), - timestamp_writes: timestamp_writes.cloned(), - }, - ); - if let Some(err) = encoder_error { - panic!("{:?}", err); - }; - - compute_pass.base = BasePass { - label, - error: None, - commands: super::ComputeCommand::resolve_compute_command_ids(&self.hub, &commands) - .unwrap(), - dynamic_offsets, - string_data, - push_constant_data, - }; - - self.compute_pass_end(&mut compute_pass).unwrap(); - } - pub fn compute_pass_end(&self, pass: &mut ComputePass) -> Result<(), EncoderStateError> { profiling::scope!( "CommandEncoder::run_compute_pass {}", diff --git a/wgpu-core/src/command/compute_command.rs b/wgpu-core/src/command/compute_command.rs index 249713db6d4..412fdd1205e 100644 --- a/wgpu-core/src/command/compute_command.rs +++ b/wgpu-core/src/command/compute_command.rs @@ -1,208 +1,21 @@ -use alloc::sync::Arc; +#[cfg(feature = "serde")] +use crate::command::serde_object_reference_struct; +use crate::command::{ArcReferences, ReferenceType}; -use crate::{ - binding_model::BindGroup, - id, - pipeline::ComputePipeline, - resource::{Buffer, QuerySet}, -}; +#[cfg(feature = "serde")] +use macro_rules_attribute::apply; -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum ComputeCommand { - SetBindGroup { - index: u32, - num_dynamic_offsets: usize, - bind_group_id: Option, - }, - - SetPipeline(id::ComputePipelineId), - - /// Set a range of push constants to values stored in `push_constant_data`. - SetPushConstant { - /// The byte offset within the push constant storage to write to. This - /// must be a multiple of four. - offset: u32, - - /// The number of bytes to write. This must be a multiple of four. - size_bytes: u32, - - /// Index in `push_constant_data` of the start of the data - /// to be written. - /// - /// Note: this is not a byte offset like `offset`. Rather, it is the - /// index of the first `u32` element in `push_constant_data` to read. - values_offset: u32, - }, - - Dispatch([u32; 3]), - - DispatchIndirect { - buffer_id: id::BufferId, - offset: wgt::BufferAddress, - }, - - PushDebugGroup { - color: u32, - len: usize, - }, - - PopDebugGroup, - - InsertDebugMarker { - color: u32, - len: usize, - }, - - WriteTimestamp { - query_set_id: id::QuerySetId, - query_index: u32, - }, - - BeginPipelineStatisticsQuery { - query_set_id: id::QuerySetId, - query_index: u32, - }, - - EndPipelineStatisticsQuery, -} - -impl ComputeCommand { - /// Resolves all ids in a list of commands into the corresponding resource Arc. - #[cfg(any(feature = "serde", feature = "replay"))] - pub fn resolve_compute_command_ids( - hub: &crate::hub::Hub, - commands: &[ComputeCommand], - ) -> Result, super::ComputePassError> { - use super::{ComputePassError, PassErrorScope}; - use alloc::vec::Vec; - - let buffers_guard = hub.buffers.read(); - let bind_group_guard = hub.bind_groups.read(); - let query_set_guard = hub.query_sets.read(); - let pipelines_guard = hub.compute_pipelines.read(); - - let resolved_commands: Vec = commands - .iter() - .map(|c| -> Result { - Ok(match *c { - ComputeCommand::SetBindGroup { - index, - num_dynamic_offsets, - bind_group_id, - } => { - if bind_group_id.is_none() { - return Ok(ArcComputeCommand::SetBindGroup { - index, - num_dynamic_offsets, - bind_group: None, - }); - } - - let bind_group_id = bind_group_id.unwrap(); - let bg = bind_group_guard.get(bind_group_id).get().map_err(|e| { - ComputePassError { - scope: PassErrorScope::SetBindGroup, - inner: e.into(), - } - })?; - - ArcComputeCommand::SetBindGroup { - index, - num_dynamic_offsets, - bind_group: Some(bg), - } - } - ComputeCommand::SetPipeline(pipeline_id) => ArcComputeCommand::SetPipeline( - pipelines_guard - .get(pipeline_id) - .get() - .map_err(|e| ComputePassError { - scope: PassErrorScope::SetPipelineCompute, - inner: e.into(), - })?, - ), - - ComputeCommand::SetPushConstant { - offset, - size_bytes, - values_offset, - } => ArcComputeCommand::SetPushConstant { - offset, - size_bytes, - values_offset, - }, - - ComputeCommand::Dispatch(dim) => ArcComputeCommand::Dispatch(dim), - - ComputeCommand::DispatchIndirect { buffer_id, offset } => { - ArcComputeCommand::DispatchIndirect { - buffer: buffers_guard.get(buffer_id).get().map_err(|e| { - ComputePassError { - scope: PassErrorScope::Dispatch { indirect: true }, - inner: e.into(), - } - })?, - offset, - } - } - - ComputeCommand::PushDebugGroup { color, len } => { - ArcComputeCommand::PushDebugGroup { color, len } - } - - ComputeCommand::PopDebugGroup => ArcComputeCommand::PopDebugGroup, - - ComputeCommand::InsertDebugMarker { color, len } => { - ArcComputeCommand::InsertDebugMarker { color, len } - } - - ComputeCommand::WriteTimestamp { - query_set_id, - query_index, - } => ArcComputeCommand::WriteTimestamp { - query_set: query_set_guard.get(query_set_id).get().map_err(|e| { - ComputePassError { - scope: PassErrorScope::WriteTimestamp, - inner: e.into(), - } - })?, - query_index, - }, - - ComputeCommand::BeginPipelineStatisticsQuery { - query_set_id, - query_index, - } => ArcComputeCommand::BeginPipelineStatisticsQuery { - query_set: query_set_guard.get(query_set_id).get().map_err(|e| { - ComputePassError { - scope: PassErrorScope::BeginPipelineStatisticsQuery, - inner: e.into(), - } - })?, - query_index, - }, - - ComputeCommand::EndPipelineStatisticsQuery => { - ArcComputeCommand::EndPipelineStatisticsQuery - } - }) - }) - .collect::, ComputePassError>>()?; - Ok(resolved_commands) - } -} - -/// Equivalent to `ComputeCommand` but the Ids resolved into resource Arcs. +/// cbindgen:ignore #[derive(Clone, Debug)] -pub enum ArcComputeCommand { +#[cfg_attr(feature = "serde", apply(serde_object_reference_struct))] +pub enum ComputeCommand { SetBindGroup { index: u32, num_dynamic_offsets: usize, - bind_group: Option>, + bind_group: Option, }, - SetPipeline(Arc), + SetPipeline(R::ComputePipeline), /// Set a range of push constants to values stored in `push_constant_data`. SetPushConstant { @@ -224,12 +37,11 @@ pub enum ArcComputeCommand { Dispatch([u32; 3]), DispatchIndirect { - buffer: Arc, + buffer: R::Buffer, offset: wgt::BufferAddress, }, PushDebugGroup { - #[cfg_attr(not(any(feature = "serde", feature = "replay")), allow(dead_code))] color: u32, len: usize, }, @@ -237,20 +49,22 @@ pub enum ArcComputeCommand { PopDebugGroup, InsertDebugMarker { - #[cfg_attr(not(any(feature = "serde", feature = "replay")), allow(dead_code))] color: u32, len: usize, }, WriteTimestamp { - query_set: Arc, + query_set: R::QuerySet, query_index: u32, }, BeginPipelineStatisticsQuery { - query_set: Arc, + query_set: R::QuerySet, query_index: u32, }, EndPipelineStatisticsQuery, } + +/// cbindgen:ignore +pub type ArcComputeCommand = ComputeCommand; diff --git a/wgpu-core/src/command/encoder_command.rs b/wgpu-core/src/command/encoder_command.rs index 092228099bc..74e3c4c39dd 100644 --- a/wgpu-core/src/command/encoder_command.rs +++ b/wgpu-core/src/command/encoder_command.rs @@ -1,141 +1,185 @@ use core::convert::Infallible; use alloc::{string::String, sync::Arc, vec::Vec}; +#[cfg(feature = "serde")] +use macro_rules_attribute::{apply, attribute_alias}; use crate::{ + command::ColorAttachments, id, + instance::Surface, resource::{Buffer, QuerySet, Texture}, }; +pub trait ReferenceType { + type Buffer: Clone + core::fmt::Debug; + type Surface: Clone; // Surface does not implement Debug, although it probably could. + type Texture: Clone + core::fmt::Debug; + type TextureView: Clone + core::fmt::Debug; + type ExternalTexture: Clone + core::fmt::Debug; + type QuerySet: Clone + core::fmt::Debug; + type BindGroup: Clone + core::fmt::Debug; + type RenderPipeline: Clone + core::fmt::Debug; + type RenderBundle: Clone + core::fmt::Debug; + type ComputePipeline: Clone + core::fmt::Debug; + type Blas: Clone + core::fmt::Debug; + type Tlas: Clone + core::fmt::Debug; +} + +/// Reference wgpu objects via numeric IDs assigned by [`crate::identity::IdentityManager`]. #[derive(Clone, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Command { - CopyBufferToBuffer { - src: id::BufferId, - src_offset: wgt::BufferAddress, - dst: id::BufferId, - dst_offset: wgt::BufferAddress, - size: Option, - }, - CopyBufferToTexture { - src: wgt::TexelCopyBufferInfo, - dst: wgt::TexelCopyTextureInfo, - size: wgt::Extent3d, - }, - CopyTextureToBuffer { - src: wgt::TexelCopyTextureInfo, - dst: wgt::TexelCopyBufferInfo, - size: wgt::Extent3d, - }, - CopyTextureToTexture { - src: wgt::TexelCopyTextureInfo, - dst: wgt::TexelCopyTextureInfo, - size: wgt::Extent3d, - }, - ClearBuffer { - dst: id::BufferId, - offset: wgt::BufferAddress, - size: Option, - }, - ClearTexture { - dst: id::TextureId, - subresource_range: wgt::ImageSubresourceRange, - }, - WriteTimestamp { - query_set_id: id::QuerySetId, - query_index: u32, - }, - ResolveQuerySet { - query_set_id: id::QuerySetId, - start_query: u32, - query_count: u32, - destination: id::BufferId, - destination_offset: wgt::BufferAddress, - }, - PushDebugGroup(String), - PopDebugGroup, - InsertDebugMarker(String), - RunComputePass { - base: crate::command::BasePass, - timestamp_writes: Option, - }, - RunRenderPass { - base: crate::command::BasePass, - target_colors: Vec>, - target_depth_stencil: Option, - timestamp_writes: Option, - occlusion_query_set_id: Option, - }, - BuildAccelerationStructures { - blas: Vec, - tlas: Vec, - }, +pub struct IdReferences; + +/// Reference wgpu objects via the integer value of pointers. +/// +/// This is used for trace recording and playback. Recording stores the pointer +/// value of `Arc` references in the trace. Playback uses the integer values +/// as keys to a `HashMap`. +#[cfg(feature = "serde")] +#[doc(hidden)] +#[derive(Clone, Debug)] +pub struct PointerReferences; + +/// Reference wgpu objects via `Arc`s. +#[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(feature = "serde")] +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; +} + +impl ReferenceType for ArcReferences { + type Buffer = Arc; + type Surface = Arc; + type Texture = Arc; + type TextureView = Arc; + type ExternalTexture = Arc; + type QuerySet = Arc; + type BindGroup = Arc; + type RenderPipeline = Arc; + type RenderBundle = Arc; + type ComputePipeline = Arc; + type Blas = Arc; + type Tlas = Arc; +} + +#[cfg(feature = "serde")] +attribute_alias! { + #[apply(serde_object_reference_struct)] = + #[derive(serde::Serialize, serde::Deserialize)] + #[serde(bound = + "R::Buffer: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + R::Surface: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + R::Texture: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + R::TextureView: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + R::ExternalTexture: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + R::QuerySet: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + R::BindGroup: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + R::RenderPipeline: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + R::RenderBundle: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + R::ComputePipeline: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + R::Blas: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + R::Tlas: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + wgt::BufferTransition: serde::Serialize + for<'d> serde::Deserialize<'d>,\ + wgt::TextureTransition: serde::Serialize + for<'d> serde::Deserialize<'d>" + )]; } #[derive(Clone, Debug)] -pub enum ArcCommand { +#[cfg_attr(feature = "serde", apply(serde_object_reference_struct))] +pub enum Command { CopyBufferToBuffer { - src: Arc, + src: R::Buffer, src_offset: wgt::BufferAddress, - dst: Arc, + dst: R::Buffer, dst_offset: wgt::BufferAddress, size: Option, }, CopyBufferToTexture { - src: wgt::TexelCopyBufferInfo>, - dst: wgt::TexelCopyTextureInfo>, + src: wgt::TexelCopyBufferInfo, + dst: wgt::TexelCopyTextureInfo, size: wgt::Extent3d, }, CopyTextureToBuffer { - src: wgt::TexelCopyTextureInfo>, - dst: wgt::TexelCopyBufferInfo>, + src: wgt::TexelCopyTextureInfo, + dst: wgt::TexelCopyBufferInfo, size: wgt::Extent3d, }, CopyTextureToTexture { - src: wgt::TexelCopyTextureInfo>, - dst: wgt::TexelCopyTextureInfo>, + src: wgt::TexelCopyTextureInfo, + dst: wgt::TexelCopyTextureInfo, size: wgt::Extent3d, }, ClearBuffer { - dst: Arc, + dst: R::Buffer, offset: wgt::BufferAddress, size: Option, }, ClearTexture { - dst: Arc, + dst: R::Texture, subresource_range: wgt::ImageSubresourceRange, }, WriteTimestamp { - query_set: Arc, + query_set: R::QuerySet, query_index: u32, }, ResolveQuerySet { - query_set: Arc, + query_set: R::QuerySet, start_query: u32, query_count: u32, - destination: Arc, + destination: R::Buffer, destination_offset: wgt::BufferAddress, }, PushDebugGroup(String), PopDebugGroup, InsertDebugMarker(String), RunComputePass { - pass: super::BasePass, - timestamp_writes: Option, + pass: crate::command::BasePass, Infallible>, + timestamp_writes: Option>, }, RunRenderPass { - pass: super::BasePass, - color_attachments: super::ArcRenderPassColorAttachmentArray, - depth_stencil_attachment: Option, - timestamp_writes: Option, - occlusion_query_set: Option>, + pass: crate::command::BasePass, Infallible>, + color_attachments: ColorAttachments, + depth_stencil_attachment: + Option>, + timestamp_writes: Option>, + occlusion_query_set: Option, }, BuildAccelerationStructures { - blas: Vec, - tlas: Vec, + blas: Vec>, + tlas: Vec>, }, TransitionResources { - buffer_transitions: Vec>>, - texture_transitions: Vec>>, + buffer_transitions: Vec>, + texture_transitions: Vec>, }, } + +pub type ArcCommand = Command; diff --git a/wgpu-core/src/command/ffi.rs b/wgpu-core/src/command/ffi.rs index 9f53d7b5634..4b9a5effbc6 100644 --- a/wgpu-core/src/command/ffi.rs +++ b/wgpu-core/src/command/ffi.rs @@ -1,7 +1,9 @@ //! Types that are useful for FFI bindings to `wgpu`. -use crate::id; +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 77848a312a0..d09c4c7599f 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -31,23 +31,31 @@ mod transition_resources; use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec}; use core::convert::Infallible; use core::mem::{self, ManuallyDrop}; -use core::ops; +use core::{ops, panic}; pub(crate) use self::clear::clear_texture; +#[cfg(feature = "serde")] +pub(crate) use self::encoder_command::serde_object_reference_struct; +#[cfg(any(feature = "trace", feature = "replay"))] +#[doc(hidden)] +pub use self::encoder_command::PointerReferences; pub use self::{ bundle::*, clear::ClearError, compute::*, - compute_command::{ArcComputeCommand, ComputeCommand}, + compute_command::ArcComputeCommand, draw::*, - encoder_command::{ArcCommand, Command}, + encoder_command::{ArcCommand, ArcReferences, Command, IdReferences, ReferenceType}, query::*, render::*, - render_command::{ArcRenderCommand, RenderCommand}, + render_command::ArcRenderCommand, transfer::*, }; pub(crate) use allocator::CommandAllocator; +/// cbindgen:ignore +pub use self::{compute_command::ComputeCommand, render_command::RenderCommand}; + pub(crate) use timestamp_writes::ArcPassTimestampWrites; pub use timestamp_writes::PassTimestampWrites; @@ -81,9 +89,6 @@ use wgt::error::{ErrorType, WebGpuError}; use thiserror::Error; -#[cfg(feature = "trace")] -type TraceCommand = Command; - /// cbindgen:ignore pub type TexelCopyBufferInfo = ffi::TexelCopyBufferInfo; /// cbindgen:ignore @@ -148,12 +153,12 @@ pub(crate) enum CommandEncoderStatus { } impl CommandEncoderStatus { - #[cfg(feature = "trace")] - fn trace(&mut self) -> Option<&mut Vec> { - match self { - Self::Recording(cmd_buf_data) => cmd_buf_data.trace_commands.as_mut(), - _ => None, - } + #[doc(hidden)] + fn replay(&mut self, commands: Vec>) { + let Self::Recording(cmd_buf_data) = self else { + panic!("encoder should be in the recording state"); + }; + cmd_buf_data.commands.extend(commands); } /// Push a command provided by a closure onto the encoder. @@ -277,20 +282,6 @@ impl CommandEncoderStatus { } } - #[cfg(all(feature = "trace", any(feature = "serde", feature = "replay")))] - fn get_inner(&mut self) -> &mut CommandBufferMutable { - match self { - Self::Locked(inner) | Self::Finished(inner) | Self::Recording(inner) => inner, - // This is unreachable because this function is only used when - // playing back a recorded trace. If only to avoid having to - // implement serialization for all the error types, we don't support - // storing the errors in a trace. - Self::Consumed => unreachable!("command encoder is consumed"), - Self::Error(_) => unreachable!("passes in a trace do not store errors"), - Self::Transitioning => unreachable!(), - } - } - /// Locks the encoder by putting it in the [`Self::Locked`] state. /// /// Render or compute passes call this on start. At the end of the pass, @@ -782,10 +773,12 @@ pub struct CommandBufferMutable { indirect_draw_validation_resources: crate::indirect_validation::DrawResources, - pub(crate) commands: Vec, + pub(crate) commands: Vec>, + /// If tracing, `command_encoder_finish` replaces the `Arc`s in `commands` + /// with integer pointers, and moves them into `trace_commands`. #[cfg(feature = "trace")] - pub(crate) trace_commands: Option>, + pub(crate) trace_commands: Option>>, } impl CommandBufferMutable { @@ -972,7 +965,31 @@ impl CommandEncoder { } let mut commands = mem::take(&mut cmd_buf_data.commands); - for command in commands.drain(..) { + #[cfg(not(feature = "trace"))] + let command_iter = commands.drain(..); + #[cfg(feature = "trace")] + let mut trace_commands = None; + + #[cfg(feature = "trace")] + let command_iter = { + if self.device.trace.lock().is_some() { + trace_commands = Some( + cmd_buf_data + .trace_commands + .insert(Vec::with_capacity(commands.len())), + ); + } + + commands.drain(..).inspect(|cmd| { + use crate::device::trace::IntoTrace; + + if let Some(ref mut trace) = trace_commands { + trace.push(cmd.clone().to_trace()); + } + }) + }; + + for command in command_iter { if matches!( command, ArcCommand::RunRenderPass { .. } | ArcCommand::RunComputePass { .. } @@ -1179,6 +1196,26 @@ impl CommandEncoder { } impl CommandBuffer { + /// Replay commands from a trace. + /// + /// This is exposed for the `player` crate only. It is not a public API. + /// It is not guaranteed to apply all of the validation that the original + /// entrypoints provide. + #[doc(hidden)] + pub fn from_trace(device: &Arc, commands: Vec>) -> Arc { + let encoder = device.create_command_encoder(&None).unwrap(); + let mut cmd_enc_status = encoder.data.lock(); + cmd_enc_status.replay(commands); + drop(cmd_enc_status); + + let (cmd_buf, error) = encoder.finish(&wgt::CommandBufferDescriptor { label: None }); + if let Some(err) = error { + panic!("CommandEncoder::finish failed: {err}"); + } + + cmd_buf + } + pub fn take_finished(&self) -> Result { use CommandEncoderStatus as St; match mem::replace( @@ -1561,7 +1598,6 @@ impl Global { profiling::scope!("CommandEncoder::finish"); let hub = &self.hub; - let cmd_enc = hub.command_encoders.get(encoder_id); let (cmd_buf, error) = cmd_enc.finish(desc); @@ -1583,11 +1619,6 @@ impl Global { let cmd_enc = hub.command_encoders.get(encoder_id); let mut cmd_buf_data = cmd_enc.data.lock(); - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.trace() { - list.push(TraceCommand::PushDebugGroup(label.to_owned())); - } - cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PushDebugGroup(label.to_owned())) }) @@ -1606,11 +1637,6 @@ impl Global { let cmd_enc = hub.command_encoders.get(encoder_id); let mut cmd_buf_data = cmd_enc.data.lock(); - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.trace() { - list.push(TraceCommand::InsertDebugMarker(label.to_owned())); - } - cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::InsertDebugMarker(label.to_owned())) }) @@ -1628,11 +1654,6 @@ impl Global { let cmd_enc = hub.command_encoders.get(encoder_id); let mut cmd_buf_data = cmd_enc.data.lock(); - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.trace() { - list.push(TraceCommand::PopDebugGroup); - } - cmd_buf_data .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) }) } diff --git a/wgpu-core/src/command/query.rs b/wgpu-core/src/command/query.rs index 3e4ed7889f1..79d3bf9dd2a 100644 --- a/wgpu-core/src/command/query.rs +++ b/wgpu-core/src/command/query.rs @@ -1,8 +1,6 @@ use alloc::{sync::Arc, vec, vec::Vec}; use core::{iter, mem}; -#[cfg(feature = "trace")] -use crate::command::Command as TraceCommand; use crate::{ command::{encoder::EncodingState, ArcCommand, EncoderStateError}, device::{Device, DeviceError, MissingFeatures}, @@ -366,14 +364,6 @@ impl Global { let cmd_enc = hub.command_encoders.get(command_encoder_id); let mut cmd_buf_data = cmd_enc.data.lock(); - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.trace() { - list.push(TraceCommand::WriteTimestamp { - query_set_id, - query_index, - }); - } - cmd_buf_data.push_with(|| -> Result<_, QueryError> { Ok(ArcCommand::WriteTimestamp { query_set: self.resolve_query_set(query_set_id)?, @@ -396,17 +386,6 @@ impl Global { let cmd_enc = hub.command_encoders.get(command_encoder_id); let mut cmd_buf_data = cmd_enc.data.lock(); - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.trace() { - list.push(TraceCommand::ResolveQuerySet { - query_set_id, - start_query, - query_count, - destination, - destination_offset, - }); - } - cmd_buf_data.push_with(|| -> Result<_, QueryError> { Ok(ArcCommand::ResolveQuerySet { query_set: self.resolve_query_set(query_set_id)?, diff --git a/wgpu-core/src/command/ray_tracing.rs b/wgpu-core/src/command/ray_tracing.rs index ac4547ff232..e1daf726686 100644 --- a/wgpu-core/src/command/ray_tracing.rs +++ b/wgpu-core/src/command/ray_tracing.rs @@ -8,32 +8,28 @@ use core::{ use wgt::{math::align_to, BufferUsages, BufferUses, Features}; use crate::{ - command::CommandBufferMutable, + command::encoder::EncodingState, + ray_tracing::{AsAction, AsBuild, BlasTriangleGeometryInfo, TlasBuild, ValidateAsActionsError}, + resource::InvalidResourceError, + track::Tracker, +}; +use crate::{command::EncoderStateError, device::resource::CommandIndices}; +use crate::{ + command::{ArcCommand, ArcReferences, CommandBufferMutable}, device::queue::TempResource, global::Global, id::CommandEncoderId, init_tracker::MemoryInitKind, ray_tracing::{ - BlasBuildEntry, BlasGeometries, BuildAccelerationStructureError, TlasPackage, - TraceBlasBuildEntry, TraceBlasGeometries, TraceBlasTriangleGeometry, TraceTlasInstance, - TraceTlasPackage, + ArcBlasBuildEntry, ArcBlasGeometries, ArcBlasTriangleGeometry, ArcTlasInstance, + ArcTlasPackage, BlasBuildEntry, BlasGeometries, BuildAccelerationStructureError, + OwnedBlasBuildEntry, OwnedTlasPackage, TlasPackage, }, resource::{Blas, BlasCompactState, Buffer, Labeled, StagingBuffer, Tlas}, scratch::ScratchBuffer, snatch::SnatchGuard, track::PendingTransition, }; -use crate::{command::EncoderStateError, device::resource::CommandIndices}; -use crate::{ - command::{encoder::EncodingState, ArcCommand}, - ray_tracing::{ - ArcBlasBuildEntry, ArcBlasGeometries, ArcBlasTriangleGeometry, ArcTlasInstance, - ArcTlasPackage, AsAction, AsBuild, BlasTriangleGeometryInfo, TlasBuild, - ValidateAsActionsError, - }, - resource::InvalidResourceError, - track::Tracker, -}; use crate::{lock::RwLockWriteGuard, resource::RawResourceAccess}; use crate::id::{BlasId, TlasId}; @@ -125,73 +121,14 @@ impl Global { let hub = &self.hub; let cmd_enc = hub.command_encoders.get(command_encoder_id); - - let trace_blas: Vec = blas_iter - .map(|blas_entry| { - let geometries = match blas_entry.geometries { - BlasGeometries::TriangleGeometries(triangle_geometries) => { - TraceBlasGeometries::TriangleGeometries( - triangle_geometries - .map(|tg| TraceBlasTriangleGeometry { - size: tg.size.clone(), - vertex_buffer: tg.vertex_buffer, - index_buffer: tg.index_buffer, - transform_buffer: tg.transform_buffer, - first_vertex: tg.first_vertex, - vertex_stride: tg.vertex_stride, - first_index: tg.first_index, - transform_buffer_offset: tg.transform_buffer_offset, - }) - .collect(), - ) - } - }; - TraceBlasBuildEntry { - blas_id: blas_entry.blas_id, - geometries, - } - }) - .collect(); - - let trace_tlas: Vec = tlas_iter - .map(|package: TlasPackage| { - let instances = package - .instances - .map(|instance| { - instance.map(|instance| TraceTlasInstance { - blas_id: instance.blas_id, - transform: *instance.transform, - custom_data: instance.custom_data, - mask: instance.mask, - }) - }) - .collect(); - TraceTlasPackage { - tlas_id: package.tlas_id, - instances, - lowest_unmodified: package.lowest_unmodified, - } - }) - .collect(); - let mut cmd_buf_data = cmd_enc.data.lock(); - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.trace() { - list.push(crate::command::Command::BuildAccelerationStructures { - blas: trace_blas.clone(), - tlas: trace_tlas.clone(), - }); - } - cmd_buf_data.push_with(|| -> Result<_, BuildAccelerationStructureError> { - let blas = trace_blas - .iter() + let blas = blas_iter .map(|blas_entry| { - let geometries = match &blas_entry.geometries { - TraceBlasGeometries::TriangleGeometries(triangle_geometries) => { + let geometries = match blas_entry.geometries { + BlasGeometries::TriangleGeometries(triangle_geometries) => { let tri_geo = triangle_geometries - .iter() .map(|tg| { Ok(ArcBlasTriangleGeometry { size: tg.size.clone(), @@ -221,19 +158,17 @@ impl Global { }) .collect::>()?; - let tlas = trace_tlas - .iter() + let tlas = tlas_iter .map(|tlas_package| { let instances = tlas_package .instances - .iter() .map(|instance| { instance .as_ref() .map(|instance| { Ok(ArcTlasInstance { blas: self.resolve_blas_id(instance.blas_id)?, - transform: instance.transform, + transform: *instance.transform, custom_data: instance.custom_data, mask: instance.mask, }) @@ -256,8 +191,8 @@ impl Global { pub(crate) fn build_acceleration_structures( state: &mut EncodingState, - blas: Vec, - tlas: Vec, + blas: Vec>, + tlas: Vec>, ) -> Result<(), BuildAccelerationStructureError> { state .device @@ -282,7 +217,7 @@ pub(crate) fn build_acceleration_structures( &mut scratch_buffer_blas_size, &mut blas_storage, )?; - let mut tlas_lock_store = Vec::<(Option, Arc)>::new(); + let mut tlas_lock_store = Vec::<(Option>, Arc)>::new(); for package in tlas.into_iter() { let tlas = package.tlas.clone(); @@ -615,7 +550,7 @@ impl CommandBufferMutable { ///iterates over the blas iterator, and it's geometry, pushing the buffers into a storage vector (and also some validation). fn iter_blas( - blas_iter: impl Iterator, + blas_iter: impl Iterator>, tracker: &mut Tracker, build_command: &mut AsBuild, buf_storage: &mut Vec, diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 80dcb049970..5fff2ce2ace 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -75,6 +75,9 @@ fn store_hal_ops(store: StoreOp) -> hal::AttachmentOps { } /// Describes an individual channel within a render pass, such as color, depth, or stencil. +/// +/// A channel must either be read-only, or it must specify both load and store +/// operations. See [`ResolvedPassChannel`] for a validated version. #[repr(C)] #[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -118,7 +121,12 @@ impl PassChannel> { } } +/// Describes an individual channel within a render pass, such as color, depth, or stencil. +/// +/// Unlike [`PassChannel`], this version uses the Rust type system to guarantee +/// a valid specification. #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum ResolvedPassChannel { ReadOnly, Operational(wgt::Operations), @@ -183,8 +191,8 @@ pub type ArcRenderPassColorAttachment = RenderPassColorAttachment>>; 1]>; +pub type ColorAttachments> = + SmallVec<[Option>; 1]>; impl ArcRenderPassColorAttachment { fn hal_ops(&self) -> hal::AttachmentOps { @@ -200,12 +208,14 @@ impl ArcRenderPassColorAttachment { } /// Describes a depth/stencil attachment to a render pass. +/// +/// This version uses the unvalidated [`PassChannel`]. #[repr(C)] #[derive(Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct RenderPassDepthStencilAttachment { +pub struct RenderPassDepthStencilAttachment { /// The view to use as an attachment. - pub view: id::TextureViewId, + pub view: TV, /// What operations will be performed on the depth part of the attachment. pub depth: PassChannel>, /// What operations will be performed on the stencil part of the attachment. @@ -213,10 +223,13 @@ pub struct RenderPassDepthStencilAttachment { } /// Describes a depth/stencil attachment to a render pass. +/// +/// This version uses the validated [`ResolvedPassChannel`]. #[derive(Clone, Debug)] -pub struct ArcRenderPassDepthStencilAttachment { +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct ResolvedRenderPassDepthStencilAttachment { /// The view to use as an attachment. - pub view: Arc, + pub view: TV, /// What operations will be performed on the depth part of the attachment. pub depth: ResolvedPassChannel, /// What operations will be performed on the stencil part of the attachment. @@ -230,7 +243,7 @@ pub struct RenderPassDescriptor<'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<&'a RenderPassDepthStencilAttachment>, + pub depth_stencil_attachment: Option<&'a RenderPassDepthStencilAttachment>, /// Defines where and when timestamp values will be written for this pass. pub timestamp_writes: Option<&'a PassTimestampWrites>, /// Defines where the occlusion query results will be stored for this pass. @@ -244,7 +257,8 @@ struct ArcRenderPassDescriptor<'a> { pub color_attachments: ArrayVec, { hal::MAX_COLOR_ATTACHMENTS }>, /// The depth and stencil attachment of the render pass, if any. - pub depth_stencil_attachment: Option, + 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. @@ -273,7 +287,7 @@ pub struct RenderPass { color_attachments: ArrayVec, { hal::MAX_COLOR_ATTACHMENTS }>, - depth_stencil_attachment: Option, + depth_stencil_attachment: Option>>, timestamp_writes: Option, occlusion_query_set: Option>, @@ -960,7 +974,9 @@ impl RenderPassInfo { device: &Arc, hal_label: Option<&str>, color_attachments: &[Option], - mut depth_stencil_attachment: Option, + mut depth_stencil_attachment: Option< + ResolvedRenderPassDepthStencilAttachment>, + >, mut timestamp_writes: Option, mut occlusion_query_set: Option>, encoder: &mut dyn hal::DynCommandEncoder, @@ -1638,7 +1654,7 @@ impl Global { ))); } - Some(ArcRenderPassDepthStencilAttachment { + Some(ResolvedRenderPassDepthStencilAttachment { view, depth: if format.has_depth_aspect() { depth_stencil_attachment.depth.resolve(|clear| if let Some(clear) = clear { @@ -1750,79 +1766,6 @@ impl Global { } } - /// Note that this differs from [`Self::render_pass_end`], it will - /// create a new pass, replay the commands and end the pass. - #[doc(hidden)] - #[cfg(any(feature = "serde", feature = "replay"))] - pub fn render_pass_end_with_unresolved_commands( - &self, - encoder_id: id::CommandEncoderId, - base: BasePass, - color_attachments: &[Option], - depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>, - timestamp_writes: Option<&PassTimestampWrites>, - occlusion_query_set: Option, - ) { - #[cfg(feature = "trace")] - { - let cmd_enc = self.hub.command_encoders.get(encoder_id); - let mut cmd_buf_data = cmd_enc.data.lock(); - let cmd_buf_data = cmd_buf_data.get_inner(); - - if let Some(ref mut list) = cmd_buf_data.trace_commands { - list.push(crate::command::Command::RunRenderPass { - base: BasePass { - label: base.label.clone(), - error: None, - commands: base.commands.clone(), - dynamic_offsets: base.dynamic_offsets.clone(), - string_data: base.string_data.clone(), - push_constant_data: base.push_constant_data.clone(), - }, - target_colors: color_attachments.to_vec(), - target_depth_stencil: depth_stencil_attachment.cloned(), - timestamp_writes: timestamp_writes.cloned(), - occlusion_query_set_id: occlusion_query_set, - }); - } - } - - let BasePass { - label, - error: _, - commands, - dynamic_offsets, - string_data, - push_constant_data, - } = base; - - let (mut render_pass, encoder_error) = self.command_encoder_begin_render_pass( - encoder_id, - &RenderPassDescriptor { - label: label.as_deref().map(Cow::Borrowed), - color_attachments: Cow::Borrowed(color_attachments), - depth_stencil_attachment, - timestamp_writes, - occlusion_query_set, - }, - ); - if let Some(err) = encoder_error { - panic!("{:?}", err); - }; - - render_pass.base = BasePass { - label, - error: None, - commands: super::RenderCommand::resolve_render_command_ids(&self.hub, &commands) - .unwrap(), - dynamic_offsets, - string_data, - push_constant_data, - }; - - self.render_pass_end(&mut render_pass).unwrap(); - } - pub fn render_pass_end(&self, pass: &mut RenderPass) -> Result<(), EncoderStateError> { profiling::scope!( "CommandEncoder::run_render_pass {}", @@ -1869,8 +1812,10 @@ impl Global { pub(super) fn encode_render_pass( parent_state: &mut EncodingState, mut base: BasePass, - color_attachments: ArcRenderPassColorAttachmentArray, - mut depth_stencil_attachment: Option, + color_attachments: ColorAttachments>, + mut depth_stencil_attachment: Option< + ResolvedRenderPassDepthStencilAttachment>, + >, mut timestamp_writes: Option, occlusion_query_set: Option>, ) -> Result<(), RenderPassError> { @@ -3425,8 +3370,8 @@ impl Global { count: 1, family: DrawCommandFamily::Draw, - vertex_or_index_limit: 0, - instance_limit: 0, + vertex_or_index_limit: None, + instance_limit: None, }); Ok(()) @@ -3450,8 +3395,8 @@ impl Global { count: 1, family: DrawCommandFamily::DrawIndexed, - vertex_or_index_limit: 0, - instance_limit: 0, + vertex_or_index_limit: None, + instance_limit: None, }); Ok(()) @@ -3475,8 +3420,8 @@ impl Global { count: 1, family: DrawCommandFamily::DrawMeshTasks, - vertex_or_index_limit: 0, - instance_limit: 0, + vertex_or_index_limit: None, + instance_limit: None, }); Ok(()) @@ -3501,8 +3446,8 @@ impl Global { count, family: DrawCommandFamily::Draw, - vertex_or_index_limit: 0, - instance_limit: 0, + vertex_or_index_limit: None, + instance_limit: None, }); Ok(()) @@ -3527,8 +3472,8 @@ impl Global { count, family: DrawCommandFamily::DrawIndexed, - vertex_or_index_limit: 0, - instance_limit: 0, + vertex_or_index_limit: None, + instance_limit: None, }); Ok(()) @@ -3553,8 +3498,8 @@ impl Global { count, family: DrawCommandFamily::DrawMeshTasks, - vertex_or_index_limit: 0, - instance_limit: 0, + vertex_or_index_limit: None, + instance_limit: None, }); Ok(()) diff --git a/wgpu-core/src/command/render_command.rs b/wgpu-core/src/command/render_command.rs index f2972f9949d..b7965960932 100644 --- a/wgpu-core/src/command/render_command.rs +++ b/wgpu-core/src/command/render_command.rs @@ -1,34 +1,33 @@ -use alloc::sync::Arc; - use wgt::{BufferAddress, BufferSize, Color}; -use super::{DrawCommandFamily, Rect, RenderBundle}; -use crate::{ - binding_model::BindGroup, - id, - pipeline::RenderPipeline, - resource::{Buffer, QuerySet}, -}; +use super::{DrawCommandFamily, Rect}; +#[cfg(feature = "serde")] +use crate::command::serde_object_reference_struct; +use crate::command::{ArcReferences, ReferenceType}; + +#[cfg(feature = "serde")] +use macro_rules_attribute::apply; +/// cbindgen:ignore #[doc(hidden)] -#[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum RenderCommand { +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", apply(serde_object_reference_struct))] +pub enum RenderCommand { SetBindGroup { index: u32, num_dynamic_offsets: usize, - bind_group_id: Option, + bind_group: Option, }, - SetPipeline(id::RenderPipelineId), + SetPipeline(R::RenderPipeline), SetIndexBuffer { - buffer_id: id::BufferId, + buffer: R::Buffer, index_format: wgt::IndexFormat, offset: BufferAddress, size: Option, }, SetVertexBuffer { slot: u32, - buffer_id: id::BufferId, + buffer: R::Buffer, offset: BufferAddress, size: Option, }, @@ -88,15 +87,19 @@ pub enum RenderCommand { group_count_z: u32, }, DrawIndirect { - buffer_id: id::BufferId, + buffer: R::Buffer, offset: BufferAddress, count: u32, family: DrawCommandFamily, + /// This limit is only populated for commands in a finished [`RenderBundle`]. + vertex_or_index_limit: Option, + /// This limit is only populated for commands in a finished [`RenderBundle`]. + instance_limit: Option, }, MultiDrawIndirectCount { - buffer_id: id::BufferId, + buffer: R::Buffer, offset: BufferAddress, - count_buffer_id: id::BufferId, + count_buffer: R::Buffer, count_buffer_offset: BufferAddress, max_count: u32, family: DrawCommandFamily, @@ -111,7 +114,7 @@ pub enum RenderCommand { len: usize, }, WriteTimestamp { - query_set_id: id::QuerySetId, + query_set: R::QuerySet, query_index: u32, }, BeginOcclusionQuery { @@ -119,290 +122,11 @@ pub enum RenderCommand { }, EndOcclusionQuery, BeginPipelineStatisticsQuery { - query_set_id: id::QuerySetId, + query_set: R::QuerySet, query_index: u32, }, EndPipelineStatisticsQuery, - ExecuteBundle(id::RenderBundleId), -} - -impl RenderCommand { - /// Resolves all ids in a list of commands into the corresponding resource Arc. - #[cfg(any(feature = "serde", feature = "replay"))] - pub fn resolve_render_command_ids( - hub: &crate::hub::Hub, - commands: &[RenderCommand], - ) -> Result, super::RenderPassError> { - use super::{DrawKind, PassErrorScope, RenderPassError}; - use alloc::vec::Vec; - - let buffers_guard = hub.buffers.read(); - let bind_group_guard = hub.bind_groups.read(); - let query_set_guard = hub.query_sets.read(); - let pipelines_guard = hub.render_pipelines.read(); - let render_bundles_guard = hub.render_bundles.read(); - - let resolved_commands: Vec = - commands - .iter() - .map(|c| -> Result { - Ok(match *c { - RenderCommand::SetBindGroup { - index, - num_dynamic_offsets, - bind_group_id, - } => { - if bind_group_id.is_none() { - return Ok(ArcRenderCommand::SetBindGroup { - index, - num_dynamic_offsets, - bind_group: None, - }); - } - - let bind_group_id = bind_group_id.unwrap(); - let bg = bind_group_guard.get(bind_group_id).get().map_err(|e| { - RenderPassError { - scope: PassErrorScope::SetBindGroup, - inner: e.into(), - } - })?; - - ArcRenderCommand::SetBindGroup { - index, - num_dynamic_offsets, - bind_group: Some(bg), - } - } - - RenderCommand::SetPipeline(pipeline_id) => ArcRenderCommand::SetPipeline( - pipelines_guard.get(pipeline_id).get().map_err(|e| { - RenderPassError { - scope: PassErrorScope::SetPipelineRender, - inner: e.into(), - } - })?, - ), - - RenderCommand::SetPushConstant { - offset, - size_bytes, - values_offset, - stages, - } => ArcRenderCommand::SetPushConstant { - offset, - size_bytes, - values_offset, - stages, - }, - - RenderCommand::PushDebugGroup { color, len } => { - ArcRenderCommand::PushDebugGroup { color, len } - } - - RenderCommand::PopDebugGroup => ArcRenderCommand::PopDebugGroup, - - RenderCommand::InsertDebugMarker { color, len } => { - ArcRenderCommand::InsertDebugMarker { color, len } - } - - RenderCommand::WriteTimestamp { - query_set_id, - query_index, - } => ArcRenderCommand::WriteTimestamp { - query_set: query_set_guard.get(query_set_id).get().map_err(|e| { - RenderPassError { - scope: PassErrorScope::WriteTimestamp, - inner: e.into(), - } - })?, - query_index, - }, - - RenderCommand::BeginPipelineStatisticsQuery { - query_set_id, - query_index, - } => ArcRenderCommand::BeginPipelineStatisticsQuery { - query_set: query_set_guard.get(query_set_id).get().map_err(|e| { - RenderPassError { - scope: PassErrorScope::BeginPipelineStatisticsQuery, - inner: e.into(), - } - })?, - query_index, - }, - - RenderCommand::EndPipelineStatisticsQuery => { - ArcRenderCommand::EndPipelineStatisticsQuery - } - - RenderCommand::SetIndexBuffer { - buffer_id, - index_format, - offset, - size, - } => ArcRenderCommand::SetIndexBuffer { - buffer: buffers_guard.get(buffer_id).get().map_err(|e| { - RenderPassError { - scope: PassErrorScope::SetIndexBuffer, - inner: e.into(), - } - })?, - index_format, - offset, - size, - }, - - RenderCommand::SetVertexBuffer { - slot, - buffer_id, - offset, - size, - } => ArcRenderCommand::SetVertexBuffer { - slot, - buffer: buffers_guard.get(buffer_id).get().map_err(|e| { - RenderPassError { - scope: PassErrorScope::SetVertexBuffer, - inner: e.into(), - } - })?, - offset, - size, - }, - - RenderCommand::SetBlendConstant(color) => { - ArcRenderCommand::SetBlendConstant(color) - } - - RenderCommand::SetStencilReference(reference) => { - ArcRenderCommand::SetStencilReference(reference) - } - - RenderCommand::SetViewport { - rect, - depth_min, - depth_max, - } => ArcRenderCommand::SetViewport { - rect, - depth_min, - depth_max, - }, - - RenderCommand::SetScissor(scissor) => ArcRenderCommand::SetScissor(scissor), - - RenderCommand::Draw { - vertex_count, - instance_count, - first_vertex, - first_instance, - } => ArcRenderCommand::Draw { - vertex_count, - instance_count, - first_vertex, - first_instance, - }, - - RenderCommand::DrawIndexed { - index_count, - instance_count, - first_index, - base_vertex, - first_instance, - } => ArcRenderCommand::DrawIndexed { - index_count, - instance_count, - first_index, - base_vertex, - first_instance, - }, - RenderCommand::DrawMeshTasks { - group_count_x, - group_count_y, - group_count_z, - } => ArcRenderCommand::DrawMeshTasks { - group_count_x, - group_count_y, - group_count_z, - }, - - RenderCommand::DrawIndirect { - buffer_id, - offset, - count, - family, - } => ArcRenderCommand::DrawIndirect { - buffer: buffers_guard.get(buffer_id).get().map_err(|e| { - RenderPassError { - scope: PassErrorScope::Draw { - kind: if count != 1 { - DrawKind::MultiDrawIndirect - } else { - DrawKind::DrawIndirect - }, - family, - }, - inner: e.into(), - } - })?, - offset, - count, - family, - - vertex_or_index_limit: 0, - instance_limit: 0, - }, - - RenderCommand::MultiDrawIndirectCount { - buffer_id, - offset, - count_buffer_id, - count_buffer_offset, - max_count, - family, - } => { - let scope = PassErrorScope::Draw { - kind: DrawKind::MultiDrawIndirectCount, - family, - }; - ArcRenderCommand::MultiDrawIndirectCount { - buffer: buffers_guard.get(buffer_id).get().map_err(|e| { - RenderPassError { - scope, - inner: e.into(), - } - })?, - offset, - count_buffer: buffers_guard.get(count_buffer_id).get().map_err( - |e| RenderPassError { - scope, - inner: e.into(), - }, - )?, - count_buffer_offset, - max_count, - family, - } - } - - RenderCommand::BeginOcclusionQuery { query_index } => { - ArcRenderCommand::BeginOcclusionQuery { query_index } - } - - RenderCommand::EndOcclusionQuery => ArcRenderCommand::EndOcclusionQuery, - - RenderCommand::ExecuteBundle(bundle) => ArcRenderCommand::ExecuteBundle( - render_bundles_guard.get(bundle).get().map_err(|e| { - RenderPassError { - scope: PassErrorScope::ExecuteBundle, - inner: e.into(), - } - })?, - ), - }) - }) - .collect::, RenderPassError>>()?; - Ok(resolved_commands) - } + ExecuteBundle(R::RenderBundle), } /// Equivalent to `RenderCommand` with the Ids resolved into resource Arcs. @@ -417,123 +141,6 @@ impl RenderCommand { /// is `finish()`ed and when the bundle is executed. Validation occurs when the /// bundle is finished, which means that parameters stored in an `ArcRenderCommand` /// for a render bundle operation must have been validated. -#[doc(hidden)] -#[derive(Clone, Debug)] -pub enum ArcRenderCommand { - SetBindGroup { - index: u32, - num_dynamic_offsets: usize, - bind_group: Option>, - }, - SetPipeline(Arc), - SetIndexBuffer { - buffer: Arc, - index_format: wgt::IndexFormat, - offset: BufferAddress, - size: Option, - }, - SetVertexBuffer { - slot: u32, - buffer: Arc, - offset: BufferAddress, - size: Option, - }, - SetBlendConstant(Color), - SetStencilReference(u32), - SetViewport { - rect: Rect, - depth_min: f32, - depth_max: f32, - }, - SetScissor(Rect), - - /// Set a range of push constants to values stored in [`BasePass::push_constant_data`]. - /// - /// See [`wgpu::RenderPass::set_push_constants`] for a detailed explanation - /// of the restrictions these commands must satisfy. - SetPushConstant { - /// Which stages we are setting push constant values for. - stages: wgt::ShaderStages, - - /// The byte offset within the push constant storage to write to. This - /// must be a multiple of four. - offset: u32, - - /// The number of bytes to write. This must be a multiple of four. - size_bytes: u32, - - /// Index in [`BasePass::push_constant_data`] of the start of the data - /// to be written. - /// - /// Note: this is not a byte offset like `offset`. Rather, it is the - /// index of the first `u32` element in `push_constant_data` to read. - /// - /// `None` means zeros should be written to the destination range, and - /// there is no corresponding data in `push_constant_data`. This is used - /// by render bundles, which explicitly clear out any state that - /// post-bundle code might see. - values_offset: Option, - }, - Draw { - vertex_count: u32, - instance_count: u32, - first_vertex: u32, - first_instance: u32, - }, - DrawIndexed { - index_count: u32, - instance_count: u32, - first_index: u32, - base_vertex: i32, - first_instance: u32, - }, - DrawMeshTasks { - group_count_x: u32, - group_count_y: u32, - group_count_z: u32, - }, - DrawIndirect { - buffer: Arc, - offset: BufferAddress, - count: u32, - family: DrawCommandFamily, - - /// This limit is only populated for commands in a [`RenderBundle`]. - vertex_or_index_limit: u64, - /// This limit is only populated for commands in a [`RenderBundle`]. - instance_limit: u64, - }, - MultiDrawIndirectCount { - buffer: Arc, - offset: BufferAddress, - count_buffer: Arc, - count_buffer_offset: BufferAddress, - max_count: u32, - family: DrawCommandFamily, - }, - PushDebugGroup { - #[cfg_attr(not(any(feature = "serde", feature = "replay")), allow(dead_code))] - color: u32, - len: usize, - }, - PopDebugGroup, - InsertDebugMarker { - #[cfg_attr(not(any(feature = "serde", feature = "replay")), allow(dead_code))] - color: u32, - len: usize, - }, - WriteTimestamp { - query_set: Arc, - query_index: u32, - }, - BeginOcclusionQuery { - query_index: u32, - }, - EndOcclusionQuery, - BeginPipelineStatisticsQuery { - query_set: Arc, - query_index: u32, - }, - EndPipelineStatisticsQuery, - ExecuteBundle(Arc), -} +/// +/// cbindgen:ignore +pub type ArcRenderCommand = RenderCommand; diff --git a/wgpu-core/src/command/transfer.rs b/wgpu-core/src/command/transfer.rs index 7c631d3a462..daaadf1aa09 100644 --- a/wgpu-core/src/command/transfer.rs +++ b/wgpu-core/src/command/transfer.rs @@ -8,8 +8,6 @@ use wgt::{ TextureUsages, }; -#[cfg(feature = "trace")] -use crate::command::Command as TraceCommand; use crate::{ api_log, command::{ @@ -825,17 +823,6 @@ impl Global { let cmd_enc = hub.command_encoders.get(command_encoder_id); let mut cmd_buf_data = cmd_enc.data.lock(); - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.trace() { - list.push(TraceCommand::CopyBufferToBuffer { - src: source, - src_offset: source_offset, - dst: destination, - dst_offset: destination_offset, - size, - }); - } - cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::CopyBufferToBuffer { src: self.resolve_buffer_id(source)?, @@ -864,15 +851,6 @@ impl Global { let cmd_enc = self.hub.command_encoders.get(command_encoder_id); let mut cmd_buf_data = cmd_enc.data.lock(); - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.trace() { - list.push(TraceCommand::CopyBufferToTexture { - src: *source, - dst: *destination, - size: *copy_size, - }); - } - cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::CopyBufferToTexture { src: wgt::TexelCopyBufferInfo::> { @@ -907,15 +885,6 @@ impl Global { let cmd_enc = self.hub.command_encoders.get(command_encoder_id); let mut cmd_buf_data = cmd_enc.data.lock(); - #[cfg(feature = "trace")] - if let Some(list) = cmd_buf_data.trace() { - list.push(TraceCommand::CopyTextureToBuffer { - src: *source, - dst: *destination, - size: *copy_size, - }); - } - cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::CopyTextureToBuffer { src: wgt::TexelCopyTextureInfo::> { @@ -950,15 +919,6 @@ impl Global { let cmd_enc = self.hub.command_encoders.get(command_encoder_id); let mut cmd_buf_data = cmd_enc.data.lock(); - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.trace() { - list.push(TraceCommand::CopyTextureToTexture { - src: *source, - dst: *destination, - size: *copy_size, - }); - } - cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::CopyTextureToTexture { src: wgt::TexelCopyTextureInfo { diff --git a/wgpu-core/src/device/global.rs b/wgpu-core/src/device/global.rs index 0542c6ebd51..d03ea2ef493 100644 --- a/wgpu-core/src/device/global.rs +++ b/wgpu-core/src/device/global.rs @@ -2,7 +2,7 @@ use alloc::{borrow::Cow, boxed::Box, string::String, sync::Arc, vec::Vec}; use core::{ptr::NonNull, sync::atomic::Ordering}; #[cfg(feature = "trace")] -use crate::device::trace; +use crate::device::trace::{self, IntoTrace}; use crate::{ api_log, binding_model::{ @@ -11,7 +11,7 @@ use crate::{ }, command::{self, CommandEncoder}, conv, - device::{bgl, life::WaitIdleError, DeviceError, DeviceLostClosure}, + device::{life::WaitIdleError, DeviceError, DeviceLostClosure}, global::Global, id::{self, AdapterId, DeviceId, QueueId, SurfaceId}, instance::{self, Adapter, Surface}, @@ -106,6 +106,13 @@ impl Global { let error = 'error: { let device = self.hub.devices.get(device_id); + let buffer = match device.create_buffer(desc) { + Ok(buffer) => buffer, + Err(e) => { + break 'error e; + } + }; + #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { let mut desc = desc.clone(); @@ -113,16 +120,9 @@ impl Global { if mapped_at_creation && !desc.usage.contains(wgt::BufferUsages::MAP_WRITE) { desc.usage |= wgt::BufferUsages::COPY_DST; } - trace.add(trace::Action::CreateBuffer(fid.id(), desc)); + trace.add(trace::Action::CreateBuffer(buffer.to_trace(), desc)); } - let buffer = match device.create_buffer(desc) { - Ok(buffer) => buffer, - Err(e) => { - break 'error e; - } - }; - let id = fid.assign(Fallible::Valid(buffer)); api_log!( @@ -215,60 +215,6 @@ impl Global { fid.assign(Fallible::Invalid(Arc::new(desc.label.to_string()))); } - #[cfg(feature = "replay")] - pub fn device_set_buffer_data( - &self, - buffer_id: id::BufferId, - offset: BufferAddress, - data: &[u8], - ) -> BufferAccessResult { - use crate::resource::RawResourceAccess; - - let hub = &self.hub; - - let buffer = hub.buffers.get(buffer_id).get()?; - - let device = &buffer.device; - - device.check_is_valid()?; - buffer.check_usage(wgt::BufferUsages::MAP_WRITE)?; - - let last_submission = device.get_queue().and_then(|queue| { - queue - .lock_life() - .get_buffer_latest_submission_index(&buffer) - }); - - if let Some(last_submission) = last_submission { - device.wait_for_submit(last_submission)?; - } - - let snatch_guard = device.snatchable_lock.read(); - let raw_buf = buffer.try_raw(&snatch_guard)?; - - let mapping = unsafe { - device - .raw() - .map_buffer(raw_buf, offset..offset + data.len() as u64) - } - .map_err(|e| device.handle_hal_error(e))?; - - unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len()) }; - - if !mapping.is_coherent { - #[allow(clippy::single_range_in_vec_init)] - unsafe { - device - .raw() - .flush_mapped_ranges(raw_buf, &[offset..offset + data.len() as u64]) - }; - } - - unsafe { device.raw().unmap_buffer(raw_buf) }; - - Ok(()) - } - pub fn buffer_destroy(&self, buffer_id: id::BufferId) { profiling::scope!("Buffer::destroy"); api_log!("Buffer::destroy {buffer_id:?}"); @@ -282,13 +228,10 @@ impl Global { #[cfg(feature = "trace")] if let Some(trace) = buffer.device.trace.lock().as_mut() { - trace.add(trace::Action::FreeBuffer(buffer_id)); + trace.add(trace::Action::FreeBuffer(buffer.to_trace())); } - let _ = buffer.unmap( - #[cfg(feature = "trace")] - buffer_id, - ); + let _ = buffer.unmap(); buffer.destroy(); } @@ -308,13 +251,10 @@ impl Global { #[cfg(feature = "trace")] if let Some(t) = buffer.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyBuffer(buffer_id)); + t.add(trace::Action::DestroyBuffer(buffer.to_trace())); } - let _ = buffer.unmap( - #[cfg(feature = "trace")] - buffer_id, - ); + let _ = buffer.unmap(); } pub fn device_create_texture( @@ -332,16 +272,19 @@ impl Global { let error = 'error: { let device = self.hub.devices.get(device_id); - #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - trace.add(trace::Action::CreateTexture(fid.id(), desc.clone())); - } - let texture = match device.create_texture(desc) { Ok(texture) => texture, Err(error) => break 'error error, }; + #[cfg(feature = "trace")] + if let Some(ref mut trace) = *device.trace.lock() { + trace.add(trace::Action::CreateTexture( + texture.to_trace(), + desc.clone(), + )); + } + let id = fid.assign(Fallible::Valid(texture)); api_log!("Device::create_texture({desc:?}) -> {id:?}"); @@ -373,18 +316,21 @@ impl Global { let error = 'error: { let device = self.hub.devices.get(device_id); + let texture = match device.create_texture_from_hal(hal_texture, desc) { + Ok(texture) => texture, + Err(error) => break 'error error, + }; + // NB: Any change done through the raw texture handle will not be // recorded in the replay #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { - trace.add(trace::Action::CreateTexture(fid.id(), desc.clone())); + trace.add(trace::Action::CreateTexture( + texture.to_trace(), + desc.clone(), + )); } - let texture = match device.create_texture_from_hal(hal_texture, desc) { - Ok(texture) => texture, - Err(error) => break 'error error, - }; - let id = fid.assign(Fallible::Valid(texture)); api_log!("Device::create_texture({desc:?}) -> {id:?}"); @@ -415,15 +361,20 @@ impl Global { let device = self.hub.devices.get(device_id); + let (buffer, err) = unsafe { device.create_buffer_from_hal(Box::new(hal_buffer), desc) }; + // NB: Any change done through the raw buffer handle will not be // recorded in the replay #[cfg(feature = "trace")] if let Some(trace) = device.trace.lock().as_mut() { - trace.add(trace::Action::CreateBuffer(fid.id(), desc.clone())); + match &buffer { + Fallible::Valid(arc) => { + trace.add(trace::Action::CreateBuffer(arc.to_trace(), desc.clone())) + } + Fallible::Invalid(_) => {} + } } - let (buffer, err) = unsafe { device.create_buffer_from_hal(Box::new(hal_buffer), desc) }; - let id = fid.assign(buffer); api_log!("Device::create_buffer -> {id:?}"); @@ -443,7 +394,7 @@ impl Global { #[cfg(feature = "trace")] if let Some(trace) = texture.device.trace.lock().as_mut() { - trace.add(trace::Action::FreeTexture(texture_id)); + trace.add(trace::Action::FreeTexture(texture.to_trace())); } texture.destroy(); @@ -459,7 +410,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(texture) = _texture.get() { if let Some(t) = texture.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyTexture(texture_id)); + t.add(trace::Action::DestroyTexture(texture.to_trace())); } } } @@ -483,20 +434,20 @@ impl Global { }; let device = &texture.device; + let view = match device.create_texture_view(&texture, desc) { + Ok(view) => view, + Err(e) => break 'error e, + }; + #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { trace.add(trace::Action::CreateTextureView { - id: fid.id(), - parent_id: texture_id, + id: view.to_trace(), + parent: texture.to_trace(), desc: desc.clone(), }); } - let view = match device.create_texture_view(&texture, desc) { - Ok(view) => view, - Err(e) => break 'error e, - }; - let id = fid.assign(Fallible::Valid(view)); api_log!("Texture::create_view({texture_id:?}) -> {id:?}"); @@ -522,7 +473,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(view) = _view.get() { if let Some(t) = view.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyTextureView(texture_view_id)); + t.add(trace::Action::DestroyTextureView(view.to_trace())); } } Ok(()) @@ -547,16 +498,6 @@ impl Global { let error = 'error: { let device = self.hub.devices.get(device_id); - #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - let planes = Box::from(planes); - trace.add(trace::Action::CreateExternalTexture { - id: fid.id(), - desc: desc.clone(), - planes, - }); - } - let planes = planes .iter() .map(|plane_id| self.hub.texture_views.get(*plane_id).get()) @@ -571,6 +512,21 @@ impl Global { Err(error) => break 'error error, }; + #[cfg(feature = "trace")] + if let Some(ref mut trace) = *device.trace.lock() { + let planes = Box::from( + planes + .into_iter() + .map(|plane| plane.to_trace()) + .collect::>(), + ); + trace.add(trace::Action::CreateExternalTexture { + id: external_texture.to_trace(), + desc: desc.clone(), + planes, + }); + } + let id = fid.assign(Fallible::Valid(external_texture)); api_log!("Device::create_external_texture({desc:?}) -> {id:?}"); @@ -594,7 +550,9 @@ impl Global { #[cfg(feature = "trace")] if let Some(trace) = external_texture.device.trace.lock().as_mut() { - trace.add(trace::Action::FreeExternalTexture(external_texture_id)); + trace.add(trace::Action::FreeExternalTexture( + external_texture.to_trace(), + )); } external_texture.destroy(); @@ -611,7 +569,9 @@ impl Global { #[cfg(feature = "trace")] if let Ok(external_texture) = _external_texture.get() { if let Some(t) = external_texture.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyExternalTexture(external_texture_id)); + t.add(trace::Action::DestroyExternalTexture( + external_texture.to_trace(), + )); } } } @@ -630,16 +590,19 @@ impl Global { let error = 'error: { let device = self.hub.devices.get(device_id); - #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - trace.add(trace::Action::CreateSampler(fid.id(), desc.clone())); - } - let sampler = match device.create_sampler(desc) { Ok(sampler) => sampler, Err(e) => break 'error e, }; + #[cfg(feature = "trace")] + if let Some(ref mut trace) = *device.trace.lock() { + trace.add(trace::Action::CreateSampler( + sampler.to_trace(), + desc.clone(), + )); + } + let id = fid.assign(Fallible::Valid(sampler)); api_log!("Device::create_sampler -> {id:?}"); @@ -661,7 +624,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(sampler) = _sampler.get() { if let Some(t) = sampler.device.trace.lock().as_mut() { - t.add(trace::Action::DestroySampler(sampler_id)); + t.add(trace::Action::DestroySampler(sampler.to_trace())); } } } @@ -683,35 +646,19 @@ impl Global { let error = 'error: { let device = self.hub.devices.get(device_id); - #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - trace.add(trace::Action::CreateBindGroupLayout(fid.id(), desc.clone())); - } - - // this check can't go in the body of `create_bind_group_layout` since the closure might not get called - if let Err(e) = device.check_is_valid() { - break 'error e.into(); - } - - let entry_map = match bgl::EntryMap::from_entries(&desc.entries) { - Ok(map) => map, - Err(e) => break 'error e, - }; - - let bgl_result = device.bgl_pool.get_or_init(entry_map, |entry_map| { - let bgl = - device.create_bind_group_layout(&desc.label, entry_map, bgl::Origin::Pool)?; - bgl.exclusive_pipeline - .set(binding_model::ExclusivePipeline::None) - .unwrap(); - Ok(bgl) - }); - - let layout = match bgl_result { + let layout = match device.create_bind_group_layout(desc) { Ok(layout) => layout, Err(e) => break 'error e, }; + #[cfg(feature = "trace")] + if let Some(ref mut trace) = *device.trace.lock() { + trace.add(trace::Action::CreateBindGroupLayout( + layout.to_trace(), + desc.clone(), + )); + } + let id = fid.assign(Fallible::Valid(layout.clone())); api_log!("Device::create_bind_group_layout -> {id:?}"); @@ -733,7 +680,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(layout) = _layout.get() { if let Some(t) = layout.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyBindGroupLayout(bind_group_layout_id)); + t.add(trace::Action::DestroyBindGroupLayout(layout.to_trace())); } } } @@ -755,11 +702,6 @@ impl Global { let error = 'error: { let device = self.hub.devices.get(device_id); - #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - trace.add(trace::Action::CreatePipelineLayout(fid.id(), desc.clone())); - } - if let Err(e) = device.check_is_valid() { break 'error e.into(); } @@ -788,6 +730,14 @@ impl Global { Err(e) => break 'error e, }; + #[cfg(feature = "trace")] + if let Some(ref mut trace) = *device.trace.lock() { + trace.add(trace::Action::CreatePipelineLayout( + layout.to_trace(), + desc.to_trace(), + )); + } + let id = fid.assign(Fallible::Valid(layout)); api_log!("Device::create_pipeline_layout -> {id:?}"); return (id, None); @@ -808,7 +758,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(layout) = _layout.get() { if let Some(t) = layout.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyPipelineLayout(pipeline_layout_id)); + t.add(trace::Action::DestroyPipelineLayout(layout.to_trace())); } } } @@ -827,11 +777,6 @@ impl Global { let error = 'error: { let device = self.hub.devices.get(device_id); - #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - trace.add(trace::Action::CreateBindGroup(fid.id(), desc.clone())); - } - if let Err(e) = device.check_is_valid() { break 'error e.into(); } @@ -959,12 +904,22 @@ impl Global { layout, entries, }; + #[cfg(feature = "trace")] + let trace_desc = (&desc).to_trace(); let bind_group = match device.create_bind_group(desc) { Ok(bind_group) => bind_group, Err(e) => break 'error e, }; + #[cfg(feature = "trace")] + if let Some(ref mut trace) = *device.trace.lock() { + trace.add(trace::Action::CreateBindGroup( + bind_group.to_trace(), + trace_desc, + )); + } + let id = fid.assign(Fallible::Valid(bind_group)); api_log!("Device::create_bind_group -> {id:?}"); @@ -985,9 +940,9 @@ impl Global { let _bind_group = hub.bind_groups.remove(bind_group_id); #[cfg(feature = "trace")] - if let Ok(_bind_group) = _bind_group.get() { - if let Some(t) = _bind_group.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyBindGroup(bind_group_id)); + if let Ok(bind_group) = _bind_group.get() { + if let Some(t) = bind_group.device.trace.lock().as_mut() { + t.add(trace::Action::DestroyBindGroup(bind_group.to_trace())); } } } @@ -1025,42 +980,50 @@ impl Global { let device = self.hub.devices.get(device_id); #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - let data = match source { - #[cfg(feature = "wgsl")] - pipeline::ShaderModuleSource::Wgsl(ref code) => { - trace.make_binary("wgsl", code.as_bytes()) - } - #[cfg(feature = "glsl")] - pipeline::ShaderModuleSource::Glsl(ref code, _) => { - trace.make_binary("glsl", code.as_bytes()) - } - #[cfg(feature = "spirv")] - pipeline::ShaderModuleSource::SpirV(ref code, _) => { - trace.make_binary("spirv", bytemuck::cast_slice::(code)) - } - pipeline::ShaderModuleSource::Naga(ref module) => { - let string = - ron::ser::to_string_pretty(module, ron::ser::PrettyConfig::default()) - .unwrap(); - trace.make_binary("ron", string.as_bytes()) - } - pipeline::ShaderModuleSource::Dummy(_) => { - panic!("found `ShaderModuleSource::Dummy`") - } - }; - trace.add(trace::Action::CreateShaderModule { - id: fid.id(), - desc: desc.clone(), - data, - }); - }; + let data = device.trace.lock().as_mut().map(|trace| match source { + #[cfg(feature = "wgsl")] + pipeline::ShaderModuleSource::Wgsl(ref code) => { + trace.make_binary("wgsl", code.as_bytes()) + } + #[cfg(feature = "glsl")] + pipeline::ShaderModuleSource::Glsl(ref code, _) => { + trace.make_binary("glsl", code.as_bytes()) + } + #[cfg(feature = "spirv")] + pipeline::ShaderModuleSource::SpirV(ref code, _) => { + trace.make_binary("spirv", bytemuck::cast_slice::(code)) + } + pipeline::ShaderModuleSource::Naga(ref module) => { + let string = + ron::ser::to_string_pretty(module, ron::ser::PrettyConfig::default()) + .unwrap(); + trace.make_binary("ron", string.as_bytes()) + } + pipeline::ShaderModuleSource::Dummy(_) => { + panic!("found `ShaderModuleSource::Dummy`") + } + }); let shader = match device.create_shader_module(desc, source) { Ok(shader) => shader, Err(e) => break 'error e, }; + #[cfg(feature = "trace")] + if let Some(data) = data { + // We don't need these two operations with the trace to be atomic. + device + .trace + .lock() + .as_mut() + .expect("trace went away during create_shader_module?") + .add(trace::Action::CreateShaderModule { + id: shader.to_trace(), + desc: desc.clone(), + data, + }); + }; + let id = fid.assign(Fallible::Valid(shader)); api_log!("Device::create_shader_module -> {id:?}"); return (id, None); @@ -1092,6 +1055,13 @@ impl Global { let error = 'error: { let device = self.hub.devices.get(device_id); + let result = unsafe { device.create_shader_module_passthrough(desc) }; + + let shader = match result { + Ok(shader) => shader, + Err(e) => break 'error e, + }; + #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { let mut file_names = Vec::new(); @@ -1108,7 +1078,7 @@ impl Global { } } trace.add(trace::Action::CreateShaderModulePassthrough { - id: fid.id(), + id: shader.to_trace(), data: file_names, entry_point: desc.entry_point.clone(), @@ -1118,12 +1088,6 @@ impl Global { }); }; - let result = unsafe { device.create_shader_module_passthrough(desc) }; - - let shader = match result { - Ok(shader) => shader, - Err(e) => break 'error e, - }; let id = fid.assign(Fallible::Valid(shader)); api_log!("Device::create_shader_module_spirv -> {id:?}"); return (id, None); @@ -1144,7 +1108,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(shader_module) = _shader_module.get() { if let Some(t) = shader_module.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyShaderModule(shader_module_id)); + t.add(trace::Action::DestroyShaderModule(shader_module.to_trace())); } } } @@ -1203,7 +1167,7 @@ impl Global { ) { profiling::scope!("Device::create_render_bundle_encoder"); api_log!("Device::device_create_render_bundle_encoder"); - let (encoder, error) = match command::RenderBundleEncoder::new(desc, device_id, None) { + let (encoder, error) = match command::RenderBundleEncoder::new(desc, device_id) { Ok(encoder) => (encoder, None), Err(e) => (command::RenderBundleEncoder::dummy(device_id), Some(e)), }; @@ -1226,24 +1190,27 @@ impl Global { let device = self.hub.devices.get(bundle_encoder.parent()); #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - trace.add(trace::Action::CreateRenderBundle { - id: fid.id(), - desc: trace::new_render_bundle_encoder_descriptor( - desc.label.clone(), - &bundle_encoder.context, - bundle_encoder.is_depth_read_only, - bundle_encoder.is_stencil_read_only, - ), - base: bundle_encoder.to_base_pass(), - }); - } + let trace_desc = trace::new_render_bundle_encoder_descriptor( + desc.label.clone(), + &bundle_encoder.context, + bundle_encoder.is_depth_read_only, + bundle_encoder.is_stencil_read_only, + ); let render_bundle = match bundle_encoder.finish(desc, &device, hub) { Ok(bundle) => bundle, Err(e) => break 'error e, }; + #[cfg(feature = "trace")] + if let Some(ref mut trace) = *device.trace.lock() { + trace.add(trace::Action::CreateRenderBundle { + id: render_bundle.to_trace(), + desc: trace_desc, + base: render_bundle.to_base_pass().to_trace(), + }); + } + let id = fid.assign(Fallible::Valid(render_bundle)); api_log!("RenderBundleEncoder::finish -> {id:?}"); @@ -1265,7 +1232,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(bundle) = _bundle.get() { if let Some(t) = bundle.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyRenderBundle(render_bundle_id)); + t.add(trace::Action::DestroyRenderBundle(bundle.to_trace())); } } } @@ -1284,19 +1251,19 @@ impl Global { let error = 'error: { let device = self.hub.devices.get(device_id); + let query_set = match device.create_query_set(desc) { + Ok(query_set) => query_set, + Err(err) => break 'error err, + }; + #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { trace.add(trace::Action::CreateQuerySet { - id: fid.id(), + id: query_set.to_trace(), desc: desc.clone(), }); } - let query_set = match device.create_query_set(desc) { - Ok(query_set) => query_set, - Err(err) => break 'error err, - }; - let id = fid.assign(Fallible::Valid(query_set)); api_log!("Device::create_query_set -> {id:?}"); @@ -1318,7 +1285,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(query_set) = _query_set.get() { if let Some(trace) = query_set.device.trace.lock().as_mut() { - trace.add(trace::Action::DestroyQuerySet(query_set_id)); + trace.add(trace::Action::DestroyQuerySet(query_set.to_trace())); } } } @@ -1339,13 +1306,7 @@ impl Global { let fid = hub.render_pipelines.prepare(id_in); let device = self.hub.devices.get(device_id); - #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - trace.add(trace::Action::CreateRenderPipeline { - id: fid.id(), - desc: desc.clone(), - }); - } + self.device_create_general_render_pipeline(desc.clone().into(), device, fid) } @@ -1363,13 +1324,6 @@ impl Global { let fid = hub.render_pipelines.prepare(id_in); let device = self.hub.devices.get(device_id); - #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - trace.add(trace::Action::CreateMeshPipeline { - id: fid.id(), - desc: desc.clone(), - }); - } self.device_create_general_render_pipeline(desc.clone().into(), device, fid) } @@ -1528,11 +1482,22 @@ impl Global { cache, }; + #[cfg(feature = "trace")] + let trace_desc = desc.clone().into_trace(); + let pipeline = match device.create_render_pipeline(desc) { Ok(pair) => pair, Err(e) => break 'error e, }; + #[cfg(feature = "trace")] + if let Some(ref mut trace) = *device.trace.lock() { + trace.add(trace::Action::CreateGeneralRenderPipeline { + id: pipeline.to_trace(), + desc: trace_desc, + }); + } + let id = fid.assign(Fallible::Valid(pipeline)); api_log!("Device::create_render_pipeline -> {id:?}"); @@ -1588,7 +1553,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(pipeline) = _pipeline.get() { if let Some(t) = pipeline.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyRenderPipeline(render_pipeline_id)); + t.add(trace::Action::DestroyRenderPipeline(pipeline.to_trace())); } } } @@ -1611,14 +1576,6 @@ impl Global { let error = 'error: { let device = self.hub.devices.get(device_id); - #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - trace.add(trace::Action::CreateComputePipeline { - id: fid.id(), - desc: desc.clone(), - }); - } - if let Err(e) = device.check_is_valid() { break 'error e.into(); } @@ -1660,11 +1617,22 @@ impl Global { cache, }; + #[cfg(feature = "trace")] + let trace_desc = desc.clone().into_trace(); + let pipeline = match device.create_compute_pipeline(desc) { Ok(pair) => pair, Err(e) => break 'error e, }; + #[cfg(feature = "trace")] + if let Some(ref mut trace) = *device.trace.lock() { + trace.add(trace::Action::CreateComputePipeline { + id: pipeline.to_trace(), + desc: trace_desc, + }); + } + let id = fid.assign(Fallible::Valid(pipeline)); api_log!("Device::create_compute_pipeline -> {id:?}"); @@ -1722,7 +1690,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(pipeline) = _pipeline.get() { if let Some(t) = pipeline.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyComputePipeline(compute_pipeline_id)); + t.add(trace::Action::DestroyComputePipeline(pipeline.to_trace())); } } } @@ -1747,17 +1715,17 @@ impl Global { let error: pipeline::CreatePipelineCacheError = 'error: { let device = self.hub.devices.get(device_id); - #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - trace.add(trace::Action::CreatePipelineCache { - id: fid.id(), - desc: desc.clone(), - }); - } - let cache = unsafe { device.create_pipeline_cache(desc) }; match cache { Ok(cache) => { + #[cfg(feature = "trace")] + if let Some(ref mut trace) = *device.trace.lock() { + trace.add(trace::Action::CreatePipelineCache { + id: cache.to_trace(), + desc: desc.clone(), + }); + } + let id = fid.assign(Fallible::Valid(cache)); api_log!("Device::create_pipeline_cache -> {id:?}"); return (id, None); @@ -1782,7 +1750,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(cache) = _cache.get() { if let Some(t) = cache.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyPipelineCache(pipeline_cache_id)); + t.add(trace::Action::DestroyPipelineCache(cache.to_trace())); } } } @@ -1793,276 +1761,18 @@ impl Global { device_id: DeviceId, config: &wgt::SurfaceConfiguration>, ) -> Option { - use present::ConfigureSurfaceError as E; - profiling::scope!("surface_configure"); - - fn validate_surface_configuration( - config: &mut hal::SurfaceConfiguration, - caps: &hal::SurfaceCapabilities, - max_texture_dimension_2d: u32, - ) -> Result<(), E> { - let width = config.extent.width; - let height = config.extent.height; - - if width > max_texture_dimension_2d || height > max_texture_dimension_2d { - return Err(E::TooLarge { - width, - height, - max_texture_dimension_2d, - }); - } - - if !caps.present_modes.contains(&config.present_mode) { - // Automatic present mode checks. - // - // The "Automatic" modes are never supported by the backends. - let fallbacks = match config.present_mode { - wgt::PresentMode::AutoVsync => { - &[wgt::PresentMode::FifoRelaxed, wgt::PresentMode::Fifo][..] - } - // Always end in FIFO to make sure it's always supported - wgt::PresentMode::AutoNoVsync => &[ - wgt::PresentMode::Immediate, - wgt::PresentMode::Mailbox, - wgt::PresentMode::Fifo, - ][..], - _ => { - return Err(E::UnsupportedPresentMode { - requested: config.present_mode, - available: caps.present_modes.clone(), - }); - } - }; - - let new_mode = fallbacks - .iter() - .copied() - .find(|fallback| caps.present_modes.contains(fallback)) - .unwrap_or_else(|| { - unreachable!( - "Fallback system failed to choose present mode. \ - This is a bug. Mode: {:?}, Options: {:?}", - config.present_mode, &caps.present_modes - ); - }); - - api_log!( - "Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}", - config.present_mode - ); - config.present_mode = new_mode; - } - if !caps.formats.contains(&config.format) { - return Err(E::UnsupportedFormat { - requested: config.format, - available: caps.formats.clone(), - }); - } - if !caps - .composite_alpha_modes - .contains(&config.composite_alpha_mode) - { - let new_alpha_mode = 'alpha: { - // Automatic alpha mode checks. - let fallbacks = match config.composite_alpha_mode { - wgt::CompositeAlphaMode::Auto => &[ - wgt::CompositeAlphaMode::Opaque, - wgt::CompositeAlphaMode::Inherit, - ][..], - _ => { - return Err(E::UnsupportedAlphaMode { - requested: config.composite_alpha_mode, - available: caps.composite_alpha_modes.clone(), - }); - } - }; - - for &fallback in fallbacks { - if caps.composite_alpha_modes.contains(&fallback) { - break 'alpha fallback; - } - } - - unreachable!( - "Fallback system failed to choose alpha mode. This is a bug. \ - AlphaMode: {:?}, Options: {:?}", - config.composite_alpha_mode, &caps.composite_alpha_modes - ); - }; + let device = self.hub.devices.get(device_id); + let surface = self.surfaces.get(surface_id); - api_log!( - "Automatically choosing alpha mode by rule {:?}. Chose {new_alpha_mode:?}", - config.composite_alpha_mode - ); - config.composite_alpha_mode = new_alpha_mode; - } - if !caps.usage.contains(config.usage) { - return Err(E::UnsupportedUsage { - requested: config.usage, - available: caps.usage, - }); - } - if width == 0 || height == 0 { - return Err(E::ZeroArea); - } - Ok(()) + #[cfg(feature = "trace")] + if let Some(ref mut trace) = *device.trace.lock() { + trace.add(trace::Action::ConfigureSurface( + surface.to_trace(), + config.clone(), + )); } - log::debug!("configuring surface with {config:?}"); - - let error = 'error: { - // User callbacks must not be called while we are holding locks. - let user_callbacks; - { - let device = self.hub.devices.get(device_id); - - #[cfg(feature = "trace")] - if let Some(ref mut trace) = *device.trace.lock() { - trace.add(trace::Action::ConfigureSurface(surface_id, config.clone())); - } - - if let Err(e) = device.check_is_valid() { - break 'error e.into(); - } - - let surface = self.surfaces.get(surface_id); - - let caps = match surface.get_capabilities(&device.adapter) { - Ok(caps) => caps, - Err(_) => break 'error E::UnsupportedQueueFamily, - }; - - let mut hal_view_formats = Vec::new(); - for format in config.view_formats.iter() { - if *format == config.format { - continue; - } - if !caps.formats.contains(&config.format) { - break 'error E::UnsupportedFormat { - requested: config.format, - available: caps.formats, - }; - } - if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() { - break 'error E::InvalidViewFormat(*format, config.format); - } - hal_view_formats.push(*format); - } - - if !hal_view_formats.is_empty() { - if let Err(missing_flag) = - device.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS) - { - break 'error E::MissingDownlevelFlags(missing_flag); - } - } - - let maximum_frame_latency = config.desired_maximum_frame_latency.clamp( - *caps.maximum_frame_latency.start(), - *caps.maximum_frame_latency.end(), - ); - let mut hal_config = hal::SurfaceConfiguration { - maximum_frame_latency, - present_mode: config.present_mode, - composite_alpha_mode: config.alpha_mode, - format: config.format, - extent: wgt::Extent3d { - width: config.width, - height: config.height, - depth_or_array_layers: 1, - }, - usage: conv::map_texture_usage( - config.usage, - hal::FormatAspects::COLOR, - wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY - | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY - | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE, - ), - view_formats: hal_view_formats, - }; - - if let Err(error) = validate_surface_configuration( - &mut hal_config, - &caps, - device.limits.max_texture_dimension_2d, - ) { - break 'error error; - } - - // Wait for all work to finish before configuring the surface. - let snatch_guard = device.snatchable_lock.read(); - let fence = device.fence.read(); - - let maintain_result; - (user_callbacks, maintain_result) = - device.maintain(fence, wgt::PollType::wait_indefinitely(), snatch_guard); - - match maintain_result { - // We're happy - Ok(wgt::PollStatus::QueueEmpty) => {} - Ok(wgt::PollStatus::WaitSucceeded) => { - // After the wait, the queue should be empty. It can only be non-empty - // if another thread is submitting at the same time. - break 'error E::GpuWaitTimeout; - } - Ok(wgt::PollStatus::Poll) => { - unreachable!("Cannot get a Poll result from a Wait action.") - } - Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => { - // On wasm, you cannot actually successfully wait for the surface. - // However WebGL does not actually require you do this, so ignoring - // the failure is totally fine. See https://github.com/gfx-rs/wgpu/issues/7363 - } - Err(e) => { - break 'error e.into(); - } - } - - // All textures must be destroyed before the surface can be re-configured. - if let Some(present) = surface.presentation.lock().take() { - if present.acquired_texture.is_some() { - break 'error E::PreviousOutputExists; - } - } - - // TODO: Texture views may still be alive that point to the texture. - // this will allow the user to render to the surface texture, long after - // it has been removed. - // - // https://github.com/gfx-rs/wgpu/issues/4105 - - let surface_raw = surface.raw(device.backend()).unwrap(); - match unsafe { surface_raw.configure(device.raw(), &hal_config) } { - Ok(()) => (), - Err(error) => { - break 'error match error { - hal::SurfaceError::Outdated | hal::SurfaceError::Lost => { - E::InvalidSurface - } - hal::SurfaceError::Device(error) => { - E::Device(device.handle_hal_error(error)) - } - hal::SurfaceError::Other(message) => { - log::error!("surface configuration failed: {message}"); - E::InvalidSurface - } - } - } - } - - let mut presentation = surface.presentation.lock(); - *presentation = Some(present::Presentation { - device, - config: config.clone(), - acquired_texture: None, - }); - } - - user_callbacks.fire(); - return None; - }; - - Some(error) + device.configure_surface(&surface, config) } /// Check `device_id` for freeable resources and completed buffer mappings. @@ -2077,30 +1787,13 @@ impl Global { let device = self.hub.devices.get(device_id); - let (closures, result) = Self::poll_single_device(&device, poll_type); + let (closures, result) = device.poll_and_return_closures(poll_type); closures.fire(); result } - fn poll_single_device( - device: &crate::device::Device, - poll_type: wgt::PollType, - ) -> (UserClosures, Result) { - let snatch_guard = device.snatchable_lock.read(); - let fence = device.fence.read(); - let maintain_result = device.maintain(fence, poll_type, snatch_guard); - - device.lose_if_oom(); - - // Some deferred destroys are scheduled in maintain so run this right after - // to avoid holding on to them until the next device poll. - device.deferred_resource_destruction(); - - maintain_result - } - /// Poll all devices belonging to the specified backend. /// /// If `force_wait` is true, block until all buffer mappings are done. @@ -2127,7 +1820,7 @@ impl Global { wgt::PollType::Poll }; - let (closures, result) = Self::poll_single_device(device, poll_type); + let (closures, result) = device.poll_and_return_closures(poll_type); let is_queue_empty = matches!(result, Ok(wgt::PollStatus::QueueEmpty)); @@ -2162,14 +1855,12 @@ impl Global { /// /// [api]: ../../wgpu/struct.Device.html#method.start_graphics_debugger_capture pub unsafe fn device_start_graphics_debugger_capture(&self, device_id: DeviceId) { - api_log!("Device::start_graphics_debugger_capture"); - - let device = self.hub.devices.get(device_id); - - if !device.is_valid() { - return; + unsafe { + self.hub + .devices + .get(device_id) + .start_graphics_debugger_capture(); } - unsafe { device.raw().start_graphics_debugger_capture() }; } /// # Safety @@ -2178,14 +1869,12 @@ impl Global { /// /// [api]: ../../wgpu/struct.Device.html#method.stop_graphics_debugger_capture pub unsafe fn device_stop_graphics_debugger_capture(&self, device_id: DeviceId) { - api_log!("Device::stop_graphics_debugger_capture"); - - let device = self.hub.devices.get(device_id); - - if !device.is_valid() { - return; + unsafe { + self.hub + .devices + .get(device_id) + .stop_graphics_debugger_capture(); } - unsafe { device.raw().stop_graphics_debugger_capture() }; } pub fn pipeline_cache_get_data(&self, id: id::PipelineCacheId) -> Option> { @@ -2327,69 +2016,9 @@ impl Global { let buffer = hub.buffers.get(buffer_id).get()?; - { - let snatch_guard = buffer.device.snatchable_lock.read(); - buffer.check_destroyed(&snatch_guard)?; - } - - let range_size = if let Some(size) = size { - size - } else { - buffer.size.saturating_sub(offset) - }; - - if offset % wgt::MAP_ALIGNMENT != 0 { - return Err(BufferAccessError::UnalignedOffset { offset }); - } - if range_size % wgt::COPY_BUFFER_ALIGNMENT != 0 { - return Err(BufferAccessError::UnalignedRangeSize { range_size }); - } - let map_state = &*buffer.map_state.lock(); - match *map_state { - resource::BufferMapState::Init { ref staging_buffer } => { - // offset (u64) can not be < 0, so no need to validate the lower bound - if offset + range_size > buffer.size { - return Err(BufferAccessError::OutOfBoundsOverrun { - index: offset + range_size - 1, - max: buffer.size, - }); - } - let ptr = unsafe { staging_buffer.ptr() }; - let ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().offset(offset as isize)) }; - Ok((ptr, range_size)) - } - resource::BufferMapState::Active { - ref mapping, - ref range, - .. - } => { - if offset < range.start { - return Err(BufferAccessError::OutOfBoundsUnderrun { - index: offset, - min: range.start, - }); - } - if offset + range_size > range.end { - return Err(BufferAccessError::OutOfBoundsOverrun { - index: offset + range_size - 1, - max: range.end, - }); - } - // ptr points to the beginning of the range we mapped in map_async - // rather than the beginning of the buffer. - let relative_offset = (offset - range.start) as isize; - unsafe { - Ok(( - NonNull::new_unchecked(mapping.ptr.as_ptr().offset(relative_offset)), - range_size, - )) - } - } - resource::BufferMapState::Idle | resource::BufferMapState::Waiting(_) => { - Err(BufferAccessError::NotMapped) - } - } + buffer.get_mapped_range(offset, size) } + pub fn buffer_unmap(&self, buffer_id: id::BufferId) -> BufferAccessResult { profiling::scope!("unmap", "Buffer"); api_log!("Buffer::unmap {buffer_id:?}"); @@ -2403,9 +2032,6 @@ impl Global { drop(snatch_guard); buffer.device.check_is_valid()?; - buffer.unmap( - #[cfg(feature = "trace")] - buffer_id, - ) + buffer.unmap() } } diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index 931effcd31e..7752cfeb99f 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -15,7 +15,7 @@ use wgt::{ use super::{life::LifetimeTracker, Device}; #[cfg(feature = "trace")] -use crate::device::trace::Action; +use crate::device::trace::{Action, IntoTrace}; use crate::{ api_log, command::{ @@ -521,7 +521,7 @@ impl WebGpuError for QueueSubmitError { impl Queue { pub fn write_buffer( &self, - buffer: Fallible, + buffer: Arc, buffer_offset: wgt::BufferAddress, data: &[u8], ) -> Result<(), QueueWriteError> { @@ -530,8 +530,6 @@ impl Queue { self.device.check_is_valid()?; - let buffer = buffer.get()?; - let data_size = data.len() as wgt::BufferAddress; self.same_device_as(buffer.as_ref())?; @@ -728,7 +726,7 @@ impl Queue { pub fn write_texture( &self, - destination: wgt::TexelCopyTextureInfo>, + destination: wgt::TexelCopyTextureInfo>, data: &[u8], data_layout: &wgt::TexelCopyBufferLayout, size: &wgt::Extent3d, @@ -738,7 +736,7 @@ impl Queue { self.device.check_is_valid()?; - let dst = destination.texture.get()?; + let dst = destination.texture; let destination = wgt::TexelCopyTextureInfo { texture: (), mip_level: destination.mip_level, @@ -1559,19 +1557,19 @@ impl Global { data: &[u8], ) -> Result<(), QueueWriteError> { let queue = self.hub.queues.get(queue_id); + let buffer = self.hub.buffers.get(buffer_id).get()?; #[cfg(feature = "trace")] if let Some(ref mut trace) = *queue.device.trace.lock() { let data_path = trace.make_binary("bin", data); trace.add(Action::WriteBuffer { - id: buffer_id, + id: buffer.to_trace(), data: data_path, range: buffer_offset..buffer_offset + data.len() as u64, queued: true, }); } - let buffer = self.hub.buffers.get(buffer_id); queue.write_buffer(buffer, buffer_offset, data) } @@ -1624,24 +1622,25 @@ impl Global { size: &wgt::Extent3d, ) -> Result<(), QueueWriteError> { let queue = self.hub.queues.get(queue_id); + let texture = self.hub.textures.get(destination.texture).get()?; + let destination = wgt::TexelCopyTextureInfo { + texture, + mip_level: destination.mip_level, + origin: destination.origin, + aspect: destination.aspect, + }; #[cfg(feature = "trace")] if let Some(ref mut trace) = *queue.device.trace.lock() { let data_path = trace.make_binary("bin", data); trace.add(Action::WriteTexture { - to: *destination, + to: destination.to_trace(), data: data_path, layout: *data_layout, size: *size, }); } - let destination = wgt::TexelCopyTextureInfo { - texture: self.hub.textures.get(destination.texture), - mip_level: destination.mip_level, - origin: destination.origin, - aspect: destination.aspect, - }; queue.write_texture(destination, data, data_layout, size) } diff --git a/wgpu-core/src/device/ray_tracing.rs b/wgpu-core/src/device/ray_tracing.rs index b44a8719d3f..370be44af27 100644 --- a/wgpu-core/src/device/ray_tracing.rs +++ b/wgpu-core/src/device/ray_tracing.rs @@ -2,7 +2,7 @@ use alloc::{string::ToString as _, sync::Arc, vec::Vec}; use core::mem::{size_of, ManuallyDrop}; #[cfg(feature = "trace")] -use crate::device::trace; +use crate::device::trace::{Action, IntoTrace}; use crate::device::DeviceError; use crate::{ api_log, @@ -25,7 +25,7 @@ use hal::AccelerationStructureTriangleIndices; use wgt::Features; impl Device { - fn create_blas( + pub fn create_blas( self: &Arc, blas_desc: &resource::BlasDescriptor, sizes: wgt::BlasGeometrySizeDescriptors, @@ -172,7 +172,7 @@ impl Device { })) } - fn create_tlas( + pub fn create_tlas( self: &Arc, desc: &resource::TlasDescriptor, ) -> Result, CreateTlasError> { @@ -273,13 +273,7 @@ impl Global { let device = self.hub.devices.get(device_id); #[cfg(feature = "trace")] - if let Some(trace) = device.trace.lock().as_mut() { - trace.add(trace::Action::CreateBlas { - id: fid.id(), - desc: desc.clone(), - sizes: sizes.clone(), - }); - } + let trace_sizes = sizes.clone(); let blas = match device.create_blas(desc, sizes) { Ok(blas) => blas, @@ -287,6 +281,15 @@ impl Global { }; let handle = blas.handle; + #[cfg(feature = "trace")] + if let Some(trace) = device.trace.lock().as_mut() { + trace.add(Action::CreateBlas { + id: blas.to_trace(), + desc: desc.clone(), + sizes: trace_sizes, + }); + } + let id = fid.assign(Fallible::Valid(blas)); api_log!("Device::create_blas -> {id:?}"); @@ -310,19 +313,19 @@ impl Global { let error = 'error: { let device = self.hub.devices.get(device_id); + let tlas = match device.create_tlas(desc) { + Ok(tlas) => tlas, + Err(e) => break 'error e, + }; + #[cfg(feature = "trace")] if let Some(trace) = device.trace.lock().as_mut() { - trace.add(trace::Action::CreateTlas { - id: fid.id(), + trace.add(Action::CreateTlas { + id: tlas.to_trace(), desc: desc.clone(), }); } - let tlas = match device.create_tlas(desc) { - Ok(tlas) => tlas, - Err(e) => break 'error e, - }; - let id = fid.assign(Fallible::Valid(tlas)); api_log!("Device::create_tlas -> {id:?}"); @@ -342,7 +345,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(blas) = _blas.get() { if let Some(t) = blas.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyBlas(blas_id)); + t.add(Action::DestroyBlas(blas.to_trace())); } } } @@ -356,7 +359,7 @@ impl Global { #[cfg(feature = "trace")] if let Ok(tlas) = _tlas.get() { if let Some(t) = tlas.device.trace.lock().as_mut() { - t.add(trace::Action::DestroyTlas(tlas_id)); + t.add(Action::DestroyTlas(tlas.to_trace())); } } } diff --git a/wgpu-core/src/device/resource.rs b/wgpu-core/src/device/resource.rs index ec8f94d3d53..463e0af2b89 100644 --- a/wgpu-core/src/device/resource.rs +++ b/wgpu-core/src/device/resource.rs @@ -24,6 +24,7 @@ use wgt::{ #[cfg(feature = "trace")] use crate::device::trace; use crate::{ + api_log, binding_model::{self, BindGroup, BindGroupLayout, BindGroupLayoutEntryError}, command, conv, device::{ @@ -39,6 +40,7 @@ use crate::{ lock::{rank, Mutex, RwLock}, pipeline, pool::ResourcePool, + present, resource::{ self, Buffer, ExternalTexture, Fallible, Labeled, ParentDevice, QuerySet, RawResourceAccess, Sampler, StagingBuffer, Texture, TextureView, @@ -336,6 +338,34 @@ impl Device { Err(MissingDownlevelFlags(flags)) } } + + /// # Safety + /// + /// - See [wgpu::Device::start_graphics_debugger_capture][api] for details the safety. + /// + /// [api]: ../../wgpu/struct.Device.html#method.start_graphics_debugger_capture + pub unsafe fn start_graphics_debugger_capture(&self) { + api_log!("Device::start_graphics_debugger_capture"); + + if !self.is_valid() { + return; + } + unsafe { self.raw().start_graphics_debugger_capture() }; + } + + /// # Safety + /// + /// - See [wgpu::Device::stop_graphics_debugger_capture][api] for details the safety. + /// + /// [api]: ../../wgpu/struct.Device.html#method.stop_graphics_debugger_capture + pub unsafe fn stop_graphics_debugger_capture(&self) { + api_log!("Device::stop_graphics_debugger_capture"); + + if !self.is_valid() { + return; + } + unsafe { self.raw().stop_graphics_debugger_capture() }; + } } impl Device { @@ -682,6 +712,38 @@ impl Device { assert!(self.queue.set(Arc::downgrade(queue)).is_ok()); } + pub fn poll( + &self, + poll_type: wgt::PollType, + ) -> Result { + let (user_closures, result) = self.poll_and_return_closures(poll_type); + user_closures.fire(); + result + } + + /// Poll the device, returning any `UserClosures` that need to be executed. + /// + /// The caller must invoke the `UserClosures` even if this function returns + /// an error. This is an internal helper, used by `Device::poll` and + /// `Global::poll_all_devices`, so that `poll_all_devices` can invoke + /// closures once after all devices have been polled. + pub(crate) fn poll_and_return_closures( + &self, + poll_type: wgt::PollType, + ) -> (UserClosures, Result) { + let snatch_guard = self.snatchable_lock.read(); + let fence = self.fence.read(); + let maintain_result = self.maintain(fence, poll_type, snatch_guard); + + self.lose_if_oom(); + + // Some deferred destroys are scheduled in maintain so run this right after + // to avoid holding on to them until the next device poll. + self.deferred_resource_destruction(); + + maintain_result + } + /// Check the current status of the GPU and process any submissions that have /// finished. /// @@ -851,7 +913,7 @@ impl Device { (user_closures, result) } - pub(crate) fn create_buffer( + pub fn create_buffer( self: &Arc, desc: &resource::BufferDescriptor, ) -> Result, resource::CreateBufferError> { @@ -1029,6 +1091,54 @@ impl Device { Ok(buffer) } + #[cfg(feature = "replay")] + pub fn set_buffer_data( + self: &Arc, + buffer: &Arc, + offset: wgt::BufferAddress, + data: &[u8], + ) -> resource::BufferAccessResult { + use crate::resource::RawResourceAccess; + + let device = &buffer.device; + + device.check_is_valid()?; + buffer.check_usage(wgt::BufferUsages::MAP_WRITE)?; + + let last_submission = device + .get_queue() + .and_then(|queue| queue.lock_life().get_buffer_latest_submission_index(buffer)); + + if let Some(last_submission) = last_submission { + device.wait_for_submit(last_submission)?; + } + + let snatch_guard = device.snatchable_lock.read(); + let raw_buf = buffer.try_raw(&snatch_guard)?; + + let mapping = unsafe { + device + .raw() + .map_buffer(raw_buf, offset..offset + data.len() as u64) + } + .map_err(|e| device.handle_hal_error(e))?; + + unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len()) }; + + if !mapping.is_coherent { + #[allow(clippy::single_range_in_vec_init)] + unsafe { + device + .raw() + .flush_mapped_ranges(raw_buf, &[offset..offset + data.len() as u64]) + }; + } + + unsafe { device.raw().unmap_buffer(raw_buf) }; + + Ok(()) + } + pub(crate) fn create_texture_from_hal( self: &Arc, hal_texture: Box, @@ -1161,7 +1271,7 @@ impl Device { } } - pub(crate) fn create_texture( + pub fn create_texture( self: &Arc, desc: &resource::TextureDescriptor, ) -> Result, resource::CreateTextureError> { @@ -1482,7 +1592,7 @@ impl Device { Ok(texture) } - pub(crate) fn create_texture_view( + pub fn create_texture_view( self: &Arc, texture: &Arc, desc: &resource::TextureViewDescriptor, @@ -1818,7 +1928,7 @@ impl Device { Ok(view) } - pub(crate) fn create_external_texture( + pub fn create_external_texture( self: &Arc, desc: &resource::ExternalTextureDescriptor, planes: &[Arc], @@ -1896,7 +2006,7 @@ impl Device { }; let params = self.create_buffer(¶ms_desc)?; self.get_queue().unwrap().write_buffer( - Fallible::Valid(params.clone()), + params.clone(), 0, bytemuck::bytes_of(¶ms_data), )?; @@ -1913,7 +2023,7 @@ impl Device { Ok(external_texture) } - pub(crate) fn create_sampler( + pub fn create_sampler( self: &Arc, desc: &resource::SamplerDescriptor, ) -> Result, resource::CreateSamplerError> { @@ -2024,7 +2134,7 @@ impl Device { Ok(sampler) } - pub(crate) fn create_shader_module<'a>( + pub fn create_shader_module<'a>( self: &Arc, desc: &pipeline::ShaderModuleDescriptor<'a>, source: pipeline::ShaderModuleSource<'a>, @@ -2152,8 +2262,10 @@ impl Device { Ok(module) } + /// Not a public API. For use by `player` only. #[allow(unused_unsafe)] - pub(crate) unsafe fn create_shader_module_passthrough<'a>( + #[doc(hidden)] + pub unsafe fn create_shader_module_passthrough<'a>( self: &Arc, descriptor: &pipeline::ShaderModuleDescriptorPassthrough<'a>, ) -> Result, pipeline::CreateShaderModuleError> { @@ -2296,7 +2408,32 @@ impl Device { .collect() } - pub(crate) fn create_bind_group_layout( + pub fn create_bind_group_layout( + self: &Arc, + desc: &binding_model::BindGroupLayoutDescriptor, + ) -> Result, binding_model::CreateBindGroupLayoutError> { + self.check_is_valid()?; + + let entry_map = bgl::EntryMap::from_entries(&desc.entries)?; + + let bgl_result = self.bgl_pool.get_or_init(entry_map, |entry_map| { + let bgl = + self.create_bind_group_layout_internal(&desc.label, entry_map, bgl::Origin::Pool)?; + bgl.exclusive_pipeline + .set(binding_model::ExclusivePipeline::None) + .unwrap(); + Ok(bgl) + }); + + match bgl_result { + Ok(layout) => Ok(layout), + Err(e) => Err(e), + } + } + + /// Internal function exposed for use by `player` crate only. + #[doc(hidden)] + pub fn create_bind_group_layout_internal( self: &Arc, label: &crate::Label, entry_map: bgl::EntryMap, @@ -2913,7 +3050,7 @@ impl Device { // This function expects the provided bind group layout to be resolved // (not passing a duplicate) beforehand. - pub(crate) fn create_bind_group( + pub fn create_bind_group( self: &Arc, desc: binding_model::ResolvedBindGroupDescriptor, ) -> Result, binding_model::CreateBindGroupError> { @@ -3368,7 +3505,7 @@ impl Device { } } - pub(crate) fn create_pipeline_layout( + pub fn create_pipeline_layout( self: &Arc, desc: &binding_model::ResolvedPipelineLayoutDescriptor, ) -> Result, binding_model::CreatePipelineLayoutError> { @@ -3499,7 +3636,7 @@ impl Device { match unique_bind_group_layouts.entry(bgl_entry_map) { hashbrown::hash_map::Entry::Occupied(v) => Ok(Arc::clone(v.get())), hashbrown::hash_map::Entry::Vacant(e) => { - match self.create_bind_group_layout( + match self.create_bind_group_layout_internal( &None, e.key().clone(), bgl::Origin::Derived, @@ -3525,7 +3662,7 @@ impl Device { Ok(layout) } - pub(crate) fn create_compute_pipeline( + pub fn create_compute_pipeline( self: &Arc, desc: pipeline::ResolvedComputePipelineDescriptor, ) -> Result, pipeline::CreateComputePipelineError> { @@ -3658,7 +3795,7 @@ impl Device { Ok(pipeline) } - pub(crate) fn create_render_pipeline( + pub fn create_render_pipeline( self: &Arc, desc: pipeline::ResolvedGeneralRenderPipelineDescriptor, ) -> Result, pipeline::CreateRenderPipelineError> { @@ -4553,7 +4690,7 @@ impl Device { Ok(()) } - pub(crate) fn create_query_set( + pub fn create_query_set( self: &Arc, desc: &resource::QuerySetDescriptor, ) -> Result, resource::CreateQuerySetError> { @@ -4600,6 +4737,274 @@ impl Device { Ok(query_set) } + pub fn configure_surface( + self: &Arc, + surface: &crate::instance::Surface, + config: &wgt::SurfaceConfiguration>, + ) -> Option { + use present::ConfigureSurfaceError as E; + profiling::scope!("surface_configure"); + + fn validate_surface_configuration( + config: &mut hal::SurfaceConfiguration, + caps: &hal::SurfaceCapabilities, + max_texture_dimension_2d: u32, + ) -> Result<(), E> { + let width = config.extent.width; + let height = config.extent.height; + + if width > max_texture_dimension_2d || height > max_texture_dimension_2d { + return Err(E::TooLarge { + width, + height, + max_texture_dimension_2d, + }); + } + + if !caps.present_modes.contains(&config.present_mode) { + // Automatic present mode checks. + // + // The "Automatic" modes are never supported by the backends. + let fallbacks = match config.present_mode { + wgt::PresentMode::AutoVsync => { + &[wgt::PresentMode::FifoRelaxed, wgt::PresentMode::Fifo][..] + } + // Always end in FIFO to make sure it's always supported + wgt::PresentMode::AutoNoVsync => &[ + wgt::PresentMode::Immediate, + wgt::PresentMode::Mailbox, + wgt::PresentMode::Fifo, + ][..], + _ => { + return Err(E::UnsupportedPresentMode { + requested: config.present_mode, + available: caps.present_modes.clone(), + }); + } + }; + + let new_mode = fallbacks + .iter() + .copied() + .find(|fallback| caps.present_modes.contains(fallback)) + .unwrap_or_else(|| { + unreachable!( + "Fallback system failed to choose present mode. \ + This is a bug. Mode: {:?}, Options: {:?}", + config.present_mode, &caps.present_modes + ); + }); + + api_log!( + "Automatically choosing presentation mode by rule {:?}. Chose {new_mode:?}", + config.present_mode + ); + config.present_mode = new_mode; + } + if !caps.formats.contains(&config.format) { + return Err(E::UnsupportedFormat { + requested: config.format, + available: caps.formats.clone(), + }); + } + if !caps + .composite_alpha_modes + .contains(&config.composite_alpha_mode) + { + let new_alpha_mode = 'alpha: { + // Automatic alpha mode checks. + let fallbacks = match config.composite_alpha_mode { + wgt::CompositeAlphaMode::Auto => &[ + wgt::CompositeAlphaMode::Opaque, + wgt::CompositeAlphaMode::Inherit, + ][..], + _ => { + return Err(E::UnsupportedAlphaMode { + requested: config.composite_alpha_mode, + available: caps.composite_alpha_modes.clone(), + }); + } + }; + + for &fallback in fallbacks { + if caps.composite_alpha_modes.contains(&fallback) { + break 'alpha fallback; + } + } + + unreachable!( + "Fallback system failed to choose alpha mode. This is a bug. \ + AlphaMode: {:?}, Options: {:?}", + config.composite_alpha_mode, &caps.composite_alpha_modes + ); + }; + + api_log!( + "Automatically choosing alpha mode by rule {:?}. Chose {new_alpha_mode:?}", + config.composite_alpha_mode + ); + config.composite_alpha_mode = new_alpha_mode; + } + if !caps.usage.contains(config.usage) { + return Err(E::UnsupportedUsage { + requested: config.usage, + available: caps.usage, + }); + } + if width == 0 || height == 0 { + return Err(E::ZeroArea); + } + Ok(()) + } + + log::debug!("configuring surface with {config:?}"); + + let error = 'error: { + // User callbacks must not be called while we are holding locks. + let user_callbacks; + { + if let Err(e) = self.check_is_valid() { + break 'error e.into(); + } + + let caps = match surface.get_capabilities(&self.adapter) { + Ok(caps) => caps, + Err(_) => break 'error E::UnsupportedQueueFamily, + }; + + let mut hal_view_formats = Vec::new(); + for format in config.view_formats.iter() { + if *format == config.format { + continue; + } + if !caps.formats.contains(&config.format) { + break 'error E::UnsupportedFormat { + requested: config.format, + available: caps.formats, + }; + } + if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() { + break 'error E::InvalidViewFormat(*format, config.format); + } + hal_view_formats.push(*format); + } + + if !hal_view_formats.is_empty() { + if let Err(missing_flag) = + self.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS) + { + break 'error E::MissingDownlevelFlags(missing_flag); + } + } + + let maximum_frame_latency = config.desired_maximum_frame_latency.clamp( + *caps.maximum_frame_latency.start(), + *caps.maximum_frame_latency.end(), + ); + let mut hal_config = hal::SurfaceConfiguration { + maximum_frame_latency, + present_mode: config.present_mode, + composite_alpha_mode: config.alpha_mode, + format: config.format, + extent: wgt::Extent3d { + width: config.width, + height: config.height, + depth_or_array_layers: 1, + }, + usage: conv::map_texture_usage( + config.usage, + hal::FormatAspects::COLOR, + wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY + | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY + | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE, + ), + view_formats: hal_view_formats, + }; + + if let Err(error) = validate_surface_configuration( + &mut hal_config, + &caps, + self.limits.max_texture_dimension_2d, + ) { + break 'error error; + } + + // Wait for all work to finish before configuring the surface. + let snatch_guard = self.snatchable_lock.read(); + let fence = self.fence.read(); + + let maintain_result; + (user_callbacks, maintain_result) = + self.maintain(fence, wgt::PollType::wait_indefinitely(), snatch_guard); + + match maintain_result { + // We're happy + Ok(wgt::PollStatus::QueueEmpty) => {} + Ok(wgt::PollStatus::WaitSucceeded) => { + // After the wait, the queue should be empty. It can only be non-empty + // if another thread is submitting at the same time. + break 'error E::GpuWaitTimeout; + } + Ok(wgt::PollStatus::Poll) => { + unreachable!("Cannot get a Poll result from a Wait action.") + } + Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => { + // On wasm, you cannot actually successfully wait for the surface. + // However WebGL does not actually require you do this, so ignoring + // the failure is totally fine. See https://github.com/gfx-rs/wgpu/issues/7363 + } + Err(e) => { + break 'error e.into(); + } + } + + // All textures must be destroyed before the surface can be re-configured. + if let Some(present) = surface.presentation.lock().take() { + if present.acquired_texture.is_some() { + break 'error E::PreviousOutputExists; + } + } + + // TODO: Texture views may still be alive that point to the texture. + // this will allow the user to render to the surface texture, long after + // it has been removed. + // + // https://github.com/gfx-rs/wgpu/issues/4105 + + let surface_raw = surface.raw(self.backend()).unwrap(); + match unsafe { surface_raw.configure(self.raw(), &hal_config) } { + Ok(()) => (), + Err(error) => { + break 'error match error { + hal::SurfaceError::Outdated | hal::SurfaceError::Lost => { + E::InvalidSurface + } + hal::SurfaceError::Device(error) => { + E::Device(self.handle_hal_error(error)) + } + hal::SurfaceError::Other(message) => { + log::error!("surface configuration failed: {message}"); + E::InvalidSurface + } + } + } + } + + let mut presentation = surface.presentation.lock(); + *presentation = Some(present::Presentation { + device: Arc::clone(self), + config: config.clone(), + acquired_texture: None, + }); + } + + user_callbacks.fire(); + return None; + }; + + Some(error) + } + fn lose(&self, message: &str) { // Follow the steps at https://gpuweb.github.io/gpuweb/#lose-the-device. diff --git a/wgpu-core/src/device/trace.rs b/wgpu-core/src/device/trace.rs index fcb3f589154..50eb22ad898 100644 --- a/wgpu-core/src/device/trace.rs +++ b/wgpu-core/src/device/trace.rs @@ -1,100 +1,88 @@ -use alloc::{string::String, vec::Vec}; +#[cfg(feature = "trace")] +mod record; + use core::{convert::Infallible, ops::Range}; -#[cfg(feature = "trace")] -use {alloc::borrow::Cow, std::io::Write as _}; +use alloc::{string::String, vec::Vec}; +use macro_rules_attribute::apply; -use crate::{command::Command, id}; +use crate::{ + command::{serde_object_reference_struct, BasePass, Command, ReferenceType, RenderCommand}, + id::{markers, PointerId}, + pipeline::GeneralRenderPipelineDescriptor, +}; -//TODO: consider a readable Id that doesn't include the backend +#[cfg(feature = "trace")] +pub use record::*; type FileName = String; pub const FILE_NAME: &str = "trace.ron"; -#[cfg(feature = "trace")] -pub(crate) fn new_render_bundle_encoder_descriptor<'a>( - label: crate::Label<'a>, - context: &'a super::RenderPassContext, - depth_read_only: bool, - stencil_read_only: bool, -) -> crate::command::RenderBundleEncoderDescriptor<'a> { - crate::command::RenderBundleEncoderDescriptor { - label, - color_formats: Cow::Borrowed(&context.attachments.colors), - depth_stencil: context.attachments.depth_stencil.map(|format| { - wgt::RenderBundleDepthStencil { - format, - depth_read_only, - stencil_read_only, - } - }), - sample_count: context.sample_count, - multiview: context.multiview, - } -} - #[allow(clippy::large_enum_variant)] #[derive(Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum Action<'a> { +#[apply(serde_object_reference_struct)] +pub enum Action<'a, R: ReferenceType> { Init { desc: crate::device::DeviceDescriptor<'a>, backend: wgt::Backend, }, ConfigureSurface( - id::SurfaceId, + R::Surface, wgt::SurfaceConfiguration>, ), - CreateBuffer(id::BufferId, crate::resource::BufferDescriptor<'a>), - FreeBuffer(id::BufferId), - DestroyBuffer(id::BufferId), - CreateTexture(id::TextureId, crate::resource::TextureDescriptor<'a>), - FreeTexture(id::TextureId), - DestroyTexture(id::TextureId), + CreateBuffer(R::Buffer, crate::resource::BufferDescriptor<'a>), + FreeBuffer(R::Buffer), + DestroyBuffer(R::Buffer), + CreateTexture(R::Texture, crate::resource::TextureDescriptor<'a>), + FreeTexture(R::Texture), + DestroyTexture(R::Texture), CreateTextureView { - id: id::TextureViewId, - parent_id: id::TextureId, + id: R::TextureView, + parent: R::Texture, desc: crate::resource::TextureViewDescriptor<'a>, }, - DestroyTextureView(id::TextureViewId), + DestroyTextureView(R::TextureView), CreateExternalTexture { - id: id::ExternalTextureId, + id: R::ExternalTexture, desc: crate::resource::ExternalTextureDescriptor<'a>, - planes: alloc::boxed::Box<[id::TextureViewId]>, + planes: alloc::boxed::Box<[R::TextureView]>, }, - FreeExternalTexture(id::ExternalTextureId), - DestroyExternalTexture(id::ExternalTextureId), - CreateSampler(id::SamplerId, crate::resource::SamplerDescriptor<'a>), - DestroySampler(id::SamplerId), + FreeExternalTexture(R::ExternalTexture), + DestroyExternalTexture(R::ExternalTexture), + CreateSampler( + PointerId, + crate::resource::SamplerDescriptor<'a>, + ), + DestroySampler(PointerId), GetSurfaceTexture { - id: id::TextureId, - parent_id: id::SurfaceId, + id: R::Texture, + parent: R::Surface, }, - Present(id::SurfaceId), - DiscardSurfaceTexture(id::SurfaceId), + Present(R::Surface), + DiscardSurfaceTexture(R::Surface), CreateBindGroupLayout( - id::BindGroupLayoutId, + PointerId, crate::binding_model::BindGroupLayoutDescriptor<'a>, ), - DestroyBindGroupLayout(id::BindGroupLayoutId), + DestroyBindGroupLayout(PointerId), CreatePipelineLayout( - id::PipelineLayoutId, - crate::binding_model::PipelineLayoutDescriptor<'a>, - ), - DestroyPipelineLayout(id::PipelineLayoutId), - CreateBindGroup( - id::BindGroupId, - crate::binding_model::BindGroupDescriptor<'a>, + PointerId, + crate::binding_model::ResolvedPipelineLayoutDescriptor< + 'a, + PointerId, + >, ), - DestroyBindGroup(id::BindGroupId), + DestroyPipelineLayout(PointerId), + CreateBindGroup(PointerId, TraceBindGroupDescriptor<'a>), + DestroyBindGroup(PointerId), CreateShaderModule { - id: id::ShaderModuleId, + id: PointerId, desc: crate::pipeline::ShaderModuleDescriptor<'a>, data: FileName, }, CreateShaderModulePassthrough { - id: id::ShaderModuleId, + id: PointerId, data: Vec, entry_point: String, @@ -102,108 +90,88 @@ pub enum Action<'a> { num_workgroups: (u32, u32, u32), runtime_checks: wgt::ShaderRuntimeChecks, }, - DestroyShaderModule(id::ShaderModuleId), + DestroyShaderModule(PointerId), CreateComputePipeline { - id: id::ComputePipelineId, - desc: crate::pipeline::ComputePipelineDescriptor<'a>, + id: PointerId, + desc: TraceComputePipelineDescriptor<'a>, }, - DestroyComputePipeline(id::ComputePipelineId), - CreateRenderPipeline { - id: id::RenderPipelineId, - desc: crate::pipeline::RenderPipelineDescriptor<'a>, + DestroyComputePipeline(PointerId), + CreateGeneralRenderPipeline { + id: PointerId, + desc: TraceGeneralRenderPipelineDescriptor<'a>, }, - CreateMeshPipeline { - id: id::RenderPipelineId, - desc: crate::pipeline::MeshPipelineDescriptor<'a>, - }, - DestroyRenderPipeline(id::RenderPipelineId), + DestroyRenderPipeline(PointerId), CreatePipelineCache { - id: id::PipelineCacheId, + id: PointerId, desc: crate::pipeline::PipelineCacheDescriptor<'a>, }, - DestroyPipelineCache(id::PipelineCacheId), + DestroyPipelineCache(PointerId), CreateRenderBundle { - id: id::RenderBundleId, + id: R::RenderBundle, desc: crate::command::RenderBundleEncoderDescriptor<'a>, - base: crate::command::BasePass, + base: BasePass, Infallible>, }, - DestroyRenderBundle(id::RenderBundleId), + DestroyRenderBundle(PointerId), CreateQuerySet { - id: id::QuerySetId, + id: PointerId, desc: crate::resource::QuerySetDescriptor<'a>, }, - DestroyQuerySet(id::QuerySetId), + DestroyQuerySet(PointerId), WriteBuffer { - id: id::BufferId, + id: R::Buffer, data: FileName, range: Range, queued: bool, }, WriteTexture { - to: wgt::TexelCopyTextureInfo, + to: wgt::TexelCopyTextureInfo, data: FileName, layout: wgt::TexelCopyBufferLayout, size: wgt::Extent3d, }, - Submit(crate::SubmissionIndex, Vec), + Submit(crate::SubmissionIndex, Vec>), CreateBlas { - id: id::BlasId, + id: R::Blas, desc: crate::resource::BlasDescriptor<'a>, sizes: wgt::BlasGeometrySizeDescriptors, }, - DestroyBlas(id::BlasId), + DestroyBlas(R::Blas), CreateTlas { - id: id::TlasId, + id: R::Tlas, desc: crate::resource::TlasDescriptor<'a>, }, - DestroyTlas(id::TlasId), -} - -#[cfg(feature = "trace")] -#[derive(Debug)] -pub struct Trace { - path: std::path::PathBuf, - file: std::fs::File, - config: ron::ser::PrettyConfig, - binary_id: usize, + DestroyTlas(R::Tlas), } -#[cfg(feature = "trace")] -impl Trace { - pub fn new(path: std::path::PathBuf) -> Result { - log::info!("Tracing into '{path:?}'"); - let mut file = std::fs::File::create(path.join(FILE_NAME))?; - file.write_all(b"[\n")?; - Ok(Self { - path, - file, - config: ron::ser::PrettyConfig::default(), - binary_id: 0, - }) - } - - pub fn make_binary(&mut self, kind: &str, data: &[u8]) -> String { - self.binary_id += 1; - let name = std::format!("data{}.{}", self.binary_id, kind); - let _ = std::fs::write(self.path.join(&name), data); - name - } +/// cbindgen:ignore +pub type TraceBindGroupDescriptor<'a> = crate::binding_model::BindGroupDescriptor< + 'a, + PointerId, + PointerId, + PointerId, + PointerId, + PointerId, + PointerId, +>; - pub(crate) fn add(&mut self, action: Action) { - match ron::ser::to_string_pretty(&action, self.config.clone()) { - Ok(string) => { - let _ = writeln!(self.file, "{string},"); - } - Err(e) => { - log::warn!("RON serialization failure: {e:?}"); - } - } - } -} +/// Not a public API. For use by `player` only. +/// +/// cbindgen:ignore +#[doc(hidden)] +pub type TraceGeneralRenderPipelineDescriptor<'a> = GeneralRenderPipelineDescriptor< + 'a, + PointerId, + PointerId, + PointerId, +>; -#[cfg(feature = "trace")] -impl Drop for Trace { - fn drop(&mut self) { - let _ = self.file.write_all(b"]"); - } -} +/// Not a public API. For use by `player` only. +/// +/// cbindgen:ignore +#[doc(hidden)] +pub type TraceComputePipelineDescriptor<'a> = crate::pipeline::ComputePipelineDescriptor< + 'a, + PointerId, + PointerId, + PointerId, +>; diff --git a/wgpu-core/src/device/trace/record.rs b/wgpu-core/src/device/trace/record.rs new file mode 100644 index 00000000000..76172cece59 --- /dev/null +++ b/wgpu-core/src/device/trace/record.rs @@ -0,0 +1,758 @@ +use alloc::{ + borrow::Cow, + string::{String, ToString}, + sync::Arc, + vec::Vec, +}; +use core::convert::Infallible; +use std::io::Write as _; + +use crate::{ + command::{ + ArcCommand, ArcComputeCommand, ArcPassTimestampWrites, ArcReferences, ArcRenderCommand, + BasePass, ColorAttachments, Command, ComputeCommand, PointerReferences, RenderCommand, + RenderPassColorAttachment, ResolvedRenderPassDepthStencilAttachment, + }, + id::{markers, PointerId}, + storage::StorageItem, +}; + +use super::{ + Action, TraceBindGroupDescriptor, TraceComputePipelineDescriptor, + TraceGeneralRenderPipelineDescriptor, FILE_NAME, +}; + +pub(crate) fn new_render_bundle_encoder_descriptor( + label: crate::Label<'_>, + context: &crate::device::RenderPassContext, + depth_read_only: bool, + stencil_read_only: bool, +) -> crate::command::RenderBundleEncoderDescriptor<'static> { + crate::command::RenderBundleEncoderDescriptor { + label: label.map(|l| Cow::from(l.to_string())), + color_formats: Cow::from(context.attachments.colors.to_vec()), + depth_stencil: context.attachments.depth_stencil.map(|format| { + wgt::RenderBundleDepthStencil { + format, + depth_read_only, + stencil_read_only, + } + }), + sample_count: context.sample_count, + multiview: context.multiview, + } +} + +#[derive(Debug)] +pub struct Trace { + path: std::path::PathBuf, + file: std::fs::File, + config: ron::ser::PrettyConfig, + binary_id: usize, +} + +impl Trace { + pub fn new(path: std::path::PathBuf) -> Result { + log::info!("Tracing into '{path:?}'"); + let mut file = std::fs::File::create(path.join(FILE_NAME))?; + file.write_all(b"[\n")?; + Ok(Self { + path, + file, + config: ron::ser::PrettyConfig::default(), + binary_id: 0, + }) + } + + pub fn make_binary(&mut self, kind: &str, data: &[u8]) -> String { + self.binary_id += 1; + let name = std::format!("data{}.{}", self.binary_id, kind); + let _ = std::fs::write(self.path.join(&name), data); + name + } + + pub(crate) fn add(&mut self, action: Action<'_, PointerReferences>) + where + for<'a> Action<'a, PointerReferences>: serde::Serialize, + { + match ron::ser::to_string_pretty(&action, self.config.clone()) { + Ok(string) => { + let _ = writeln!(self.file, "{string},"); + } + Err(e) => { + log::warn!("RON serialization failure: {e:?}"); + } + } + } +} + +impl Drop for Trace { + fn drop(&mut self) { + let _ = self.file.write_all(b"]"); + } +} + +pub(crate) trait IntoTrace { + type Output; + fn into_trace(self) -> Self::Output; + + fn to_trace(&self) -> Self::Output + where + Self: Sized + Clone, + { + self.clone().into_trace() + } +} + +impl IntoTrace for Arc { + type Output = PointerId; + fn into_trace(self) -> Self::Output { + self.to_trace() + } + + fn to_trace(&self) -> Self::Output { + PointerId::from(self) + } +} + +impl IntoTrace for ArcCommand { + type Output = Command; + fn into_trace(self) -> Self::Output { + match self { + ArcCommand::CopyBufferToBuffer { + src, + src_offset, + dst, + dst_offset, + size, + } => Command::CopyBufferToBuffer { + src: src.to_trace(), + src_offset, + dst: dst.to_trace(), + dst_offset, + size, + }, + ArcCommand::CopyBufferToTexture { src, dst, size } => Command::CopyBufferToTexture { + src: src.into_trace(), + dst: dst.into_trace(), + size, + }, + ArcCommand::CopyTextureToBuffer { src, dst, size } => Command::CopyTextureToBuffer { + src: src.into_trace(), + dst: dst.into_trace(), + size, + }, + ArcCommand::CopyTextureToTexture { src, dst, size } => Command::CopyTextureToTexture { + src: src.into_trace(), + dst: dst.into_trace(), + size, + }, + ArcCommand::ClearBuffer { dst, offset, size } => Command::ClearBuffer { + dst: dst.to_trace(), + offset, + size, + }, + ArcCommand::ClearTexture { + dst, + subresource_range, + } => Command::ClearTexture { + dst: dst.to_trace(), + subresource_range, + }, + ArcCommand::WriteTimestamp { + query_set, + query_index, + } => Command::WriteTimestamp { + query_set: query_set.to_trace(), + query_index, + }, + ArcCommand::ResolveQuerySet { + query_set, + start_query, + query_count, + destination, + destination_offset, + } => Command::ResolveQuerySet { + query_set: query_set.to_trace(), + start_query, + query_count, + destination: destination.to_trace(), + destination_offset, + }, + ArcCommand::PushDebugGroup(label) => Command::PushDebugGroup(label), + ArcCommand::PopDebugGroup => Command::PopDebugGroup, + ArcCommand::InsertDebugMarker(label) => Command::InsertDebugMarker(label), + ArcCommand::RunComputePass { + pass, + timestamp_writes, + } => Command::RunComputePass { + pass: pass.into_trace(), + timestamp_writes: timestamp_writes.map(|tw| tw.into_trace()), + }, + ArcCommand::RunRenderPass { + pass, + color_attachments, + depth_stencil_attachment, + timestamp_writes, + occlusion_query_set, + } => Command::RunRenderPass { + pass: pass.into_trace(), + color_attachments: color_attachments.into_trace(), + depth_stencil_attachment: depth_stencil_attachment.map(|d| d.into_trace()), + timestamp_writes: timestamp_writes.map(|tw| tw.into_trace()), + occlusion_query_set: occlusion_query_set.map(|q| q.to_trace()), + }, + ArcCommand::BuildAccelerationStructures { blas, tlas } => { + Command::BuildAccelerationStructures { + blas: blas.into_iter().map(|b| b.into_trace()).collect(), + tlas: tlas.into_iter().map(|b| b.into_trace()).collect(), + } + } + ArcCommand::TransitionResources { + buffer_transitions: _, + texture_transitions: _, + } => { + // TransitionResources does not exist in Command, so skip or handle as needed. + // If you want to ignore, you could panic or return a default. + panic!("TransitionResources cannot be converted to Command"); + } + } + } +} + +impl IntoTrace for wgt::TexelCopyBufferInfo { + type Output = wgt::TexelCopyBufferInfo; + fn into_trace(self) -> Self::Output { + wgt::TexelCopyBufferInfo { + buffer: self.buffer.into_trace(), + layout: self.layout, + } + } +} + +impl IntoTrace for wgt::TexelCopyTextureInfo { + type Output = wgt::TexelCopyTextureInfo; + fn into_trace(self) -> Self::Output { + wgt::TexelCopyTextureInfo { + texture: self.texture.into_trace(), + mip_level: self.mip_level, + origin: self.origin, + aspect: self.aspect, + } + } +} + +impl IntoTrace for ArcPassTimestampWrites { + type Output = crate::command::PassTimestampWrites>; + fn into_trace(self) -> Self::Output { + crate::command::PassTimestampWrites { + query_set: self.query_set.into_trace(), + beginning_of_pass_write_index: self.beginning_of_pass_write_index, + end_of_pass_write_index: self.end_of_pass_write_index, + } + } +} + +impl IntoTrace for ColorAttachments { + type Output = ColorAttachments>; + fn into_trace(self) -> Self::Output { + self.into_iter() + .map(|opt| { + opt.map(|att| RenderPassColorAttachment { + view: att.view.into_trace(), + depth_slice: att.depth_slice, + resolve_target: att.resolve_target.map(|r| r.into_trace()), + load_op: att.load_op, + store_op: att.store_op, + }) + }) + .collect() + } +} + +impl IntoTrace for ResolvedRenderPassDepthStencilAttachment { + type Output = ResolvedRenderPassDepthStencilAttachment; + fn into_trace(self) -> Self::Output { + ResolvedRenderPassDepthStencilAttachment { + view: self.view.into_trace(), + depth: self.depth, + stencil: self.stencil, + } + } +} + +impl IntoTrace for crate::ray_tracing::OwnedBlasBuildEntry { + type Output = crate::ray_tracing::OwnedBlasBuildEntry; + fn into_trace(self) -> Self::Output { + crate::ray_tracing::OwnedBlasBuildEntry { + blas: self.blas.into_trace(), + geometries: self.geometries.into_trace(), + } + } +} + +impl IntoTrace for crate::ray_tracing::OwnedBlasGeometries { + type Output = crate::ray_tracing::OwnedBlasGeometries; + fn into_trace(self) -> Self::Output { + match self { + crate::ray_tracing::OwnedBlasGeometries::TriangleGeometries(geos) => { + crate::ray_tracing::OwnedBlasGeometries::TriangleGeometries( + geos.into_iter().map(|g| g.into_trace()).collect(), + ) + } + } + } +} + +impl IntoTrace for crate::ray_tracing::OwnedBlasTriangleGeometry { + type Output = crate::ray_tracing::OwnedBlasTriangleGeometry; + fn into_trace(self) -> Self::Output { + crate::ray_tracing::OwnedBlasTriangleGeometry { + size: self.size, + vertex_buffer: self.vertex_buffer.into_trace(), + index_buffer: self.index_buffer.map(|b| b.into_trace()), + transform_buffer: self.transform_buffer.map(|b| b.into_trace()), + first_vertex: self.first_vertex, + vertex_stride: self.vertex_stride, + first_index: self.first_index, + transform_buffer_offset: self.transform_buffer_offset, + } + } +} + +impl IntoTrace for crate::ray_tracing::OwnedTlasPackage { + type Output = crate::ray_tracing::OwnedTlasPackage; + fn into_trace(self) -> Self::Output { + crate::ray_tracing::OwnedTlasPackage { + tlas: self.tlas.into_trace(), + instances: self + .instances + .into_iter() + .map(|opt| opt.map(|inst| inst.into_trace())) + .collect(), + lowest_unmodified: self.lowest_unmodified, + } + } +} + +impl IntoTrace for crate::ray_tracing::OwnedTlasInstance { + type Output = crate::ray_tracing::OwnedTlasInstance; + fn into_trace(self) -> Self::Output { + crate::ray_tracing::OwnedTlasInstance { + blas: self.blas.into_trace(), + transform: self.transform, + custom_data: self.custom_data, + mask: self.mask, + } + } +} + +impl IntoTrace for BasePass { + type Output = BasePass; + + fn into_trace(self) -> Self::Output { + BasePass { + label: self.label, + error: self.error, + commands: self + .commands + .into_iter() + .map(|cmd| cmd.into_trace()) + .collect(), + dynamic_offsets: self.dynamic_offsets, + string_data: self.string_data, + push_constant_data: self.push_constant_data, + } + } +} + +impl IntoTrace for ArcComputeCommand { + type Output = ComputeCommand; + fn into_trace(self) -> Self::Output { + use ComputeCommand as C; + match self { + C::SetBindGroup { + index, + num_dynamic_offsets, + bind_group, + } => C::SetBindGroup { + index, + num_dynamic_offsets, + bind_group: bind_group.map(|bg| bg.into_trace()), + }, + C::SetPipeline(id) => C::SetPipeline(id.into_trace()), + C::SetPushConstant { + offset, + size_bytes, + values_offset, + } => C::SetPushConstant { + offset, + size_bytes, + values_offset, + }, + C::Dispatch(groups) => C::Dispatch(groups), + C::DispatchIndirect { buffer, offset } => C::DispatchIndirect { + buffer: buffer.into_trace(), + offset, + }, + C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len }, + C::PopDebugGroup => C::PopDebugGroup, + C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len }, + C::WriteTimestamp { + query_set, + query_index, + } => C::WriteTimestamp { + query_set: query_set.into_trace(), + query_index, + }, + C::BeginPipelineStatisticsQuery { + query_set, + query_index, + } => C::BeginPipelineStatisticsQuery { + query_set: query_set.into_trace(), + query_index, + }, + C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery, + } + } +} + +impl IntoTrace for ArcRenderCommand { + type Output = RenderCommand; + fn into_trace(self) -> Self::Output { + use RenderCommand as C; + match self { + C::SetBindGroup { + index, + num_dynamic_offsets, + bind_group, + } => C::SetBindGroup { + index, + num_dynamic_offsets, + bind_group: bind_group.map(|bg| bg.into_trace()), + }, + C::SetPipeline(id) => C::SetPipeline(id.into_trace()), + C::SetIndexBuffer { + buffer, + index_format, + offset, + size, + } => C::SetIndexBuffer { + buffer: buffer.into_trace(), + index_format, + offset, + size, + }, + C::SetVertexBuffer { + slot, + buffer, + offset, + size, + } => C::SetVertexBuffer { + slot, + buffer: buffer.into_trace(), + offset, + size, + }, + C::SetBlendConstant(color) => C::SetBlendConstant(color), + C::SetStencilReference(val) => C::SetStencilReference(val), + C::SetViewport { + rect, + depth_min, + depth_max, + } => C::SetViewport { + rect, + depth_min, + depth_max, + }, + C::SetScissor(rect) => C::SetScissor(rect), + C::SetPushConstant { + stages, + offset, + size_bytes, + values_offset, + } => C::SetPushConstant { + stages, + offset, + size_bytes, + values_offset, + }, + C::Draw { + vertex_count, + instance_count, + first_vertex, + first_instance, + } => C::Draw { + vertex_count, + instance_count, + first_vertex, + first_instance, + }, + C::DrawIndexed { + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + } => C::DrawIndexed { + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + }, + C::DrawMeshTasks { + group_count_x, + group_count_y, + group_count_z, + } => C::DrawMeshTasks { + group_count_x, + group_count_y, + group_count_z, + }, + C::DrawIndirect { + buffer, + offset, + count, + family, + vertex_or_index_limit, + instance_limit, + } => C::DrawIndirect { + buffer: buffer.into_trace(), + offset, + count, + family, + vertex_or_index_limit, + instance_limit, + }, + C::MultiDrawIndirectCount { + buffer, + offset, + count_buffer, + count_buffer_offset, + max_count, + family, + } => C::MultiDrawIndirectCount { + buffer: buffer.into_trace(), + offset, + count_buffer: count_buffer.into_trace(), + count_buffer_offset, + max_count, + family, + }, + C::PushDebugGroup { color, len } => C::PushDebugGroup { color, len }, + C::PopDebugGroup => C::PopDebugGroup, + C::InsertDebugMarker { color, len } => C::InsertDebugMarker { color, len }, + C::WriteTimestamp { + query_set, + query_index, + } => C::WriteTimestamp { + query_set: query_set.into_trace(), + query_index, + }, + C::BeginOcclusionQuery { query_index } => C::BeginOcclusionQuery { query_index }, + C::EndOcclusionQuery => C::EndOcclusionQuery, + C::BeginPipelineStatisticsQuery { + query_set, + query_index, + } => C::BeginPipelineStatisticsQuery { + query_set: query_set.into_trace(), + query_index, + }, + C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery, + C::ExecuteBundle(bundle) => C::ExecuteBundle(bundle.into_trace()), + } + } +} + +impl<'a> IntoTrace for crate::binding_model::ResolvedPipelineLayoutDescriptor<'a> { + type Output = + crate::binding_model::PipelineLayoutDescriptor<'a, PointerId>; + fn into_trace(self) -> Self::Output { + crate::binding_model::PipelineLayoutDescriptor { + label: self.label.map(|l| Cow::Owned(l.into_owned())), + bind_group_layouts: self + .bind_group_layouts + .iter() + .map(|bgl| bgl.to_trace()) + .collect(), + push_constant_ranges: self.push_constant_ranges.clone(), + } + } +} + +impl<'a> IntoTrace for &'_ crate::binding_model::ResolvedBindGroupDescriptor<'a> { + type Output = TraceBindGroupDescriptor<'a>; + + fn into_trace(self) -> Self::Output { + use crate::binding_model::{ + BindGroupEntry, BindingResource, BufferBinding, ResolvedBindingResource, + }; + TraceBindGroupDescriptor { + label: self.label.clone(), + layout: self.layout.to_trace(), + entries: Cow::Owned( + self.entries + .iter() + .map(|entry| { + let resource = match &entry.resource { + ResolvedBindingResource::Buffer(buffer_binding) => { + BindingResource::Buffer(BufferBinding { + buffer: buffer_binding.buffer.to_trace(), + offset: buffer_binding.offset, + size: buffer_binding.size, + }) + } + ResolvedBindingResource::BufferArray(buffer_bindings) => { + let resolved_buffers: Vec<_> = buffer_bindings + .iter() + .map(|bb| BufferBinding { + buffer: bb.buffer.to_trace(), + offset: bb.offset, + size: bb.size, + }) + .collect(); + BindingResource::BufferArray(Cow::Owned(resolved_buffers)) + } + ResolvedBindingResource::Sampler(sampler_id) => { + BindingResource::Sampler(sampler_id.to_trace()) + } + ResolvedBindingResource::SamplerArray(sampler_ids) => { + let resolved: Vec<_> = + sampler_ids.iter().map(|id| id.to_trace()).collect(); + BindingResource::SamplerArray(Cow::Owned(resolved)) + } + ResolvedBindingResource::TextureView(texture_view_id) => { + BindingResource::TextureView(texture_view_id.to_trace()) + } + ResolvedBindingResource::TextureViewArray(texture_view_ids) => { + let resolved: Vec<_> = + texture_view_ids.iter().map(|id| id.to_trace()).collect(); + BindingResource::TextureViewArray(Cow::Owned(resolved)) + } + ResolvedBindingResource::AccelerationStructure(tlas_id) => { + BindingResource::AccelerationStructure(tlas_id.to_trace()) + } + ResolvedBindingResource::ExternalTexture(external_texture_id) => { + BindingResource::ExternalTexture(external_texture_id.to_trace()) + } + }; + BindGroupEntry { + binding: entry.binding, + resource, + } + }) + .collect(), + ), + } + } +} + +impl<'a> IntoTrace for crate::pipeline::ResolvedGeneralRenderPipelineDescriptor<'a> { + type Output = TraceGeneralRenderPipelineDescriptor<'a>; + + fn into_trace(self) -> Self::Output { + TraceGeneralRenderPipelineDescriptor { + label: self.label, + layout: self.layout.into_trace(), + vertex: self.vertex.into_trace(), + primitive: self.primitive, + depth_stencil: self.depth_stencil, + multisample: self.multisample, + fragment: self.fragment.map(|f| f.into_trace()), + multiview: self.multiview, + cache: self.cache.map(|c| c.into_trace()), + } + } +} + +impl<'a> IntoTrace for crate::pipeline::ResolvedComputePipelineDescriptor<'a> { + type Output = TraceComputePipelineDescriptor<'a>; + + fn into_trace(self) -> Self::Output { + TraceComputePipelineDescriptor { + label: self.label, + layout: self.layout.into_trace(), + stage: self.stage.into_trace(), + cache: self.cache.map(|c| c.into_trace()), + } + } +} + +impl<'a> IntoTrace for crate::pipeline::ResolvedProgrammableStageDescriptor<'a> { + type Output = + crate::pipeline::ProgrammableStageDescriptor<'a, PointerId>; + fn into_trace(self) -> Self::Output { + crate::pipeline::ProgrammableStageDescriptor { + module: self.module.into_trace(), + entry_point: self.entry_point, + constants: self.constants, + zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory, + } + } +} + +impl<'a> IntoTrace + for crate::pipeline::RenderPipelineVertexProcessor<'a, Arc> +{ + type Output = + crate::pipeline::RenderPipelineVertexProcessor<'a, PointerId>; + fn into_trace(self) -> Self::Output { + match self { + crate::pipeline::RenderPipelineVertexProcessor::Vertex(vertex) => { + crate::pipeline::RenderPipelineVertexProcessor::Vertex(vertex.into_trace()) + } + crate::pipeline::RenderPipelineVertexProcessor::Mesh(task, mesh) => { + crate::pipeline::RenderPipelineVertexProcessor::Mesh( + task.map(|t| t.into_trace()), + mesh.into_trace(), + ) + } + } + } +} + +impl<'a> IntoTrace for crate::pipeline::ResolvedTaskState<'a> { + type Output = crate::pipeline::TaskState<'a, PointerId>; + fn into_trace(self) -> Self::Output { + crate::pipeline::TaskState { + stage: self.stage.into_trace(), + } + } +} + +impl<'a> IntoTrace for crate::pipeline::ResolvedMeshState<'a> { + type Output = crate::pipeline::MeshState<'a, PointerId>; + fn into_trace(self) -> Self::Output { + crate::pipeline::MeshState { + stage: self.stage.into_trace(), + } + } +} + +impl<'a> IntoTrace for crate::pipeline::ResolvedVertexState<'a> { + type Output = crate::pipeline::VertexState<'a, PointerId>; + fn into_trace(self) -> Self::Output { + crate::pipeline::VertexState { + stage: self.stage.into_trace(), + buffers: self.buffers, + } + } +} + +impl<'a> IntoTrace for crate::pipeline::ResolvedFragmentState<'a> { + type Output = crate::pipeline::FragmentState<'a, PointerId>; + fn into_trace(self) -> Self::Output { + crate::pipeline::FragmentState { + stage: self.stage.into_trace(), + targets: self.targets, + } + } +} + +impl IntoTrace for Option { + type Output = Option; + fn into_trace(self) -> Self::Output { + self.map(|v| v.into_trace()) + } +} diff --git a/wgpu-core/src/id.rs b/wgpu-core/src/id.rs index ca864f1d1a4..daf2f6c334e 100644 --- a/wgpu-core/src/id.rs +++ b/wgpu-core/src/id.rs @@ -82,22 +82,20 @@ impl RawId { /// [`Registry`]: crate::registry::Registry /// [`Noop`]: hal::api::Noop #[repr(transparent)] -#[cfg_attr(any(feature = "serde", feature = "trace"), derive(serde::Serialize))] -#[cfg_attr(any(feature = "serde", feature = "replay"), derive(serde::Deserialize))] -#[cfg_attr( - any(feature = "serde", feature = "trace", feature = "replay"), - serde(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. -#[allow(dead_code)] +#[cfg(feature = "serde")] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -enum SerialId { +#[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(); @@ -105,14 +103,17 @@ impl From for SerialId { } } +#[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 { @@ -125,6 +126,61 @@ impl TryFrom for RawId { } } +/// Identify an object by the pointer returned by `Arc::as_ptr`. +/// +/// This is used for tracing. +#[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(usize, #[serde(skip)] PhantomData), +} + +#[cfg(feature = "serde")] +impl Copy for PointerId {} + +#[cfg(feature = "serde")] +impl Clone for PointerId { + fn clone(&self) -> Self { + *self + } +} + +#[cfg(feature = "serde")] +impl PartialEq for PointerId { + fn eq(&self, other: &Self) -> bool { + let PointerId::PointerId(this, _) = self; + let PointerId::PointerId(other, _) = other; + this == other + } +} + +#[cfg(feature = "serde")] +impl Eq for PointerId {} + +#[cfg(feature = "serde")] +impl Hash for PointerId { + fn hash(&self, state: &mut H) { + let PointerId::PointerId(this, _) = self; + this.hash(state); + } +} + +#[cfg(feature = "serde")] +impl From<&alloc::sync::Arc> for PointerId { + fn from(arc: &alloc::sync::Arc) -> Self { + // Since the memory representation of `Arc` is just a pointer to + // `ArcInner`, it would be nice to use that pointer as the trace ID, + // since many `into_trace` implementations would then be no-ops at + // runtime. However, `Arc::as_ptr` returns a pointer to the contained + // data, not to the `ArcInner`. The `ArcInner` stores the reference + // counts before the data, so the machine code for this conversion has + // to add an offset to the pointer. + PointerId::PointerId(alloc::sync::Arc::as_ptr(arc) as usize, PhantomData) + } +} + impl Id where T: Marker, diff --git a/wgpu-core/src/pipeline.rs b/wgpu-core/src/pipeline.rs index 753518f67ad..1955fee2f37 100644 --- a/wgpu-core/src/pipeline.rs +++ b/wgpu-core/src/pipeline.rs @@ -425,9 +425,17 @@ pub struct MeshState<'a, SM = ShaderModuleId> { pub type ResolvedMeshState<'a> = MeshState<'a, Arc>; +/// Describes a vertex processor for either a conventional or mesh shading +/// pipeline architecture. +/// +/// This is not a public API. It is for use by `player` only. The public APIs +/// are [`VertexState`], [`TaskState`], and [`MeshState`]. +/// +/// cbindgen:ignore +#[doc(hidden)] #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub(crate) enum RenderPipelineVertexProcessor<'a, SM = ShaderModuleId> { +pub enum RenderPipelineVertexProcessor<'a, SM = ShaderModuleId> { Vertex(VertexState<'a, SM>), Mesh(Option>, MeshState<'a, SM>), } @@ -497,10 +505,17 @@ pub struct MeshPipelineDescriptor< pub cache: Option, } -/// Describes a render (graphics) pipeline. +/// Describes a render (graphics) pipeline, with either conventional or mesh +/// shading architecture. +/// +/// This is not a public API. It is for use by `player` only. The public APIs +/// are [`RenderPipelineDescriptor`] and [`MeshPipelineDescriptor`]. +/// +/// cbindgen:ignore +#[doc(hidden)] #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub(crate) struct GeneralRenderPipelineDescriptor< +pub struct GeneralRenderPipelineDescriptor< 'a, PLL = PipelineLayoutId, SM = ShaderModuleId, @@ -563,8 +578,10 @@ impl<'a, PLL, SM, PLC> From> } } +/// Not a public API. For use by `player` only. +/// /// cbindgen:ignore -pub(crate) type ResolvedGeneralRenderPipelineDescriptor<'a> = +pub type ResolvedGeneralRenderPipelineDescriptor<'a> = GeneralRenderPipelineDescriptor<'a, Arc, Arc, Arc>; #[derive(Clone, Debug)] diff --git a/wgpu-core/src/present.rs b/wgpu-core/src/present.rs index 4ad9a4c0f0b..5d4e250814a 100644 --- a/wgpu-core/src/present.rs +++ b/wgpu-core/src/present.rs @@ -13,7 +13,7 @@ use alloc::{sync::Arc, vec::Vec}; use core::mem::ManuallyDrop; #[cfg(feature = "trace")] -use crate::device::trace::Action; +use crate::device::trace::{Action, IntoTrace}; use crate::{ conv, device::{Device, DeviceError, MissingDownlevelFlags, WaitIdleError}, @@ -363,10 +363,12 @@ impl Global { #[cfg(feature = "trace")] if let Some(present) = surface.presentation.lock().as_ref() { if let Some(ref mut trace) = *present.device.trace.lock() { - trace.add(Action::GetSurfaceTexture { - id: fid.id(), - parent_id: surface_id, - }); + if let Some(texture) = present.acquired_texture.as_ref() { + trace.add(Action::GetSurfaceTexture { + id: texture.to_trace(), + parent: surface.to_trace(), + }); + } } } @@ -389,7 +391,7 @@ impl Global { #[cfg(feature = "trace")] if let Some(present) = surface.presentation.lock().as_ref() { if let Some(ref mut trace) = *present.device.trace.lock() { - trace.add(Action::Present(surface_id)); + trace.add(Action::Present(surface.to_trace())); } } @@ -402,7 +404,7 @@ impl Global { #[cfg(feature = "trace")] if let Some(present) = surface.presentation.lock().as_ref() { if let Some(ref mut trace) = *present.device.trace.lock() { - trace.add(Action::DiscardSurfaceTexture(surface_id)); + trace.add(Action::DiscardSurfaceTexture(surface.to_trace())); } } diff --git a/wgpu-core/src/ray_tracing.rs b/wgpu-core/src/ray_tracing.rs index 29165667815..f0354d197af 100644 --- a/wgpu-core/src/ray_tracing.rs +++ b/wgpu-core/src/ray_tracing.rs @@ -10,18 +10,22 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec}; +#[cfg(feature = "serde")] +use macro_rules_attribute::apply; use thiserror::Error; use wgt::{ error::{ErrorType, WebGpuError}, AccelerationStructureGeometryFlags, BufferAddress, IndexFormat, VertexFormat, }; +#[cfg(feature = "serde")] +use crate::command::serde_object_reference_struct; use crate::{ - command::EncoderStateError, + command::{ArcReferences, EncoderStateError, IdReferences, ReferenceType}, device::{DeviceError, MissingFeatures}, id::{BlasId, BufferId, TlasId}, resource::{ - Blas, BlasCompactCallback, BlasPrepareCompactResult, Buffer, DestroyedResourceError, + Blas, BlasCompactCallback, BlasPrepareCompactResult, DestroyedResourceError, InvalidResourceError, MissingBufferUsageError, ResourceErrorIdent, Tlas, }, }; @@ -306,60 +310,62 @@ pub(crate) enum AsAction { /// Like [`BlasTriangleGeometry`], but with owned data. #[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TraceBlasTriangleGeometry { +#[cfg_attr(feature = "serde", apply(serde_object_reference_struct))] +pub struct OwnedBlasTriangleGeometry { pub size: wgt::BlasTriangleGeometrySizeDescriptor, - pub vertex_buffer: BufferId, - pub index_buffer: Option, - pub transform_buffer: Option, + pub vertex_buffer: R::Buffer, + pub index_buffer: Option, + pub transform_buffer: Option, pub first_vertex: u32, pub vertex_stride: BufferAddress, pub first_index: Option, pub transform_buffer_offset: Option, } +pub type ArcBlasTriangleGeometry = OwnedBlasTriangleGeometry; +pub type TraceBlasTriangleGeometry = OwnedBlasTriangleGeometry; + #[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum TraceBlasGeometries { - TriangleGeometries(Vec), +#[cfg_attr(feature = "serde", apply(serde_object_reference_struct))] +pub enum OwnedBlasGeometries { + TriangleGeometries(Vec>), } +pub type ArcBlasGeometries = OwnedBlasGeometries; +pub type TraceBlasGeometries = OwnedBlasGeometries; + #[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TraceBlasBuildEntry { - pub blas_id: BlasId, - pub geometries: TraceBlasGeometries, +#[cfg_attr(feature = "serde", apply(serde_object_reference_struct))] +pub struct OwnedBlasBuildEntry { + pub blas: R::Blas, + pub geometries: OwnedBlasGeometries, } +pub type ArcBlasBuildEntry = OwnedBlasBuildEntry; +pub type TraceBlasBuildEntry = OwnedBlasBuildEntry; + #[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TraceTlasInstance { - pub blas_id: BlasId, +#[cfg_attr(feature = "serde", apply(serde_object_reference_struct))] +pub struct OwnedTlasInstance { + pub blas: R::Blas, pub transform: [f32; 12], pub custom_data: u32, pub mask: u8, } +pub type ArcTlasInstance = OwnedTlasInstance; +pub type TraceTlasInstance = OwnedTlasInstance; + #[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct TraceTlasPackage { - pub tlas_id: TlasId, - pub instances: Vec>, +#[cfg_attr(feature = "serde", apply(serde_object_reference_struct))] +pub struct OwnedTlasPackage { + pub tlas: R::Tlas, + pub instances: Vec>>, pub lowest_unmodified: u32, } -/// Like [`BlasTriangleGeometry`], but with `Arc`s. -#[derive(Debug, Clone)] -pub struct ArcBlasTriangleGeometry { - pub size: wgt::BlasTriangleGeometrySizeDescriptor, - pub vertex_buffer: Arc, - pub index_buffer: Option>, - pub transform_buffer: Option>, - pub first_vertex: u32, - pub vertex_stride: BufferAddress, - pub first_index: Option, - pub transform_buffer_offset: Option, -} +pub type TraceTlasPackage = OwnedTlasPackage; +pub type ArcTlasPackage = OwnedTlasPackage; /// [`BlasTriangleGeometry`], without the resources. #[derive(Debug, Clone)] @@ -371,32 +377,6 @@ pub struct BlasTriangleGeometryInfo { pub transform_buffer_offset: Option, } -#[derive(Debug, Clone)] -pub enum ArcBlasGeometries { - TriangleGeometries(Vec), -} - -#[derive(Debug, Clone)] -pub struct ArcBlasBuildEntry { - pub blas: Arc, - pub geometries: ArcBlasGeometries, -} - -#[derive(Debug, Clone)] -pub struct ArcTlasInstance { - pub blas: Arc, - pub transform: [f32; 12], - pub custom_data: u32, - pub mask: u8, -} - -#[derive(Debug, Clone)] -pub struct ArcTlasPackage { - pub tlas: Arc, - pub instances: Vec>, - pub lowest_unmodified: u32, -} - #[derive(Clone, Debug, Error)] pub enum BlasPrepareCompactError { #[error(transparent)] diff --git a/wgpu-core/src/registry.rs b/wgpu-core/src/registry.rs index 3ff81dc73e5..2cdaf3f16bc 100644 --- a/wgpu-core/src/registry.rs +++ b/wgpu-core/src/registry.rs @@ -55,11 +55,6 @@ pub(crate) struct FutureId<'a, T: StorageItem> { } impl FutureId<'_, T> { - #[cfg(feature = "trace")] - pub fn id(&self) -> Id { - self.id - } - /// Assign a new resource to this ID. /// /// Registers it with the registry. diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index e15242d3f2a..2119885e062 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -573,7 +573,7 @@ impl Buffer { /// Returns the mapping callback in case of error so that the callback can be fired outside /// of the locks that are held in this function. - pub(crate) fn map_async( + pub fn map_async( self: &Arc, offset: wgt::BufferAddress, size: Option, @@ -677,6 +677,72 @@ impl Buffer { Ok(submit_index) } + pub fn get_mapped_range( + self: &Arc, + offset: wgt::BufferAddress, + size: Option, + ) -> Result<(NonNull, u64), BufferAccessError> { + { + let snatch_guard = self.device.snatchable_lock.read(); + self.check_destroyed(&snatch_guard)?; + } + + let range_size = if let Some(size) = size { + size + } else { + self.size.saturating_sub(offset) + }; + + if offset % wgt::MAP_ALIGNMENT != 0 { + return Err(BufferAccessError::UnalignedOffset { offset }); + } + if range_size % wgt::COPY_BUFFER_ALIGNMENT != 0 { + return Err(BufferAccessError::UnalignedRangeSize { range_size }); + } + let map_state = &*self.map_state.lock(); + match *map_state { + BufferMapState::Init { ref staging_buffer } => { + // offset (u64) can not be < 0, so no need to validate the lower bound + if offset + range_size > self.size { + return Err(BufferAccessError::OutOfBoundsOverrun { + index: offset + range_size - 1, + max: self.size, + }); + } + let ptr = unsafe { staging_buffer.ptr() }; + let ptr = unsafe { NonNull::new_unchecked(ptr.as_ptr().offset(offset as isize)) }; + Ok((ptr, range_size)) + } + BufferMapState::Active { + ref mapping, + ref range, + .. + } => { + if offset < range.start { + return Err(BufferAccessError::OutOfBoundsUnderrun { + index: offset, + min: range.start, + }); + } + if offset + range_size > range.end { + return Err(BufferAccessError::OutOfBoundsOverrun { + index: offset + range_size - 1, + max: range.end, + }); + } + // ptr points to the beginning of the range we mapped in map_async + // rather than the beginning of the buffer. + let relative_offset = (offset - range.start) as isize; + unsafe { + Ok(( + NonNull::new_unchecked(mapping.ptr.as_ptr().offset(relative_offset)), + range_size, + )) + } + } + BufferMapState::Idle | BufferMapState::Waiting(_) => Err(BufferAccessError::NotMapped), + } + } /// This function returns [`None`] only if [`Self::map_state`] is not [`BufferMapState::Waiting`]. #[must_use] pub(crate) fn map(&self, snatch_guard: &SnatchGuard) -> Option { @@ -731,14 +797,8 @@ impl Buffer { } // Note: This must not be called while holding a lock. - pub(crate) fn unmap( - self: &Arc, - #[cfg(feature = "trace")] buffer_id: crate::id::BufferId, - ) -> Result<(), BufferAccessError> { - if let Some((mut operation, status)) = self.unmap_inner( - #[cfg(feature = "trace")] - buffer_id, - )? { + pub fn unmap(self: &Arc) -> Result<(), BufferAccessError> { + if let Some((mut operation, status)) = self.unmap_inner()? { if let Some(callback) = operation.callback.take() { callback(status); } @@ -747,10 +807,7 @@ impl Buffer { Ok(()) } - fn unmap_inner( - self: &Arc, - #[cfg(feature = "trace")] buffer_id: crate::id::BufferId, - ) -> Result, BufferAccessError> { + fn unmap_inner(self: &Arc) -> Result, BufferAccessError> { let device = &self.device; let snatch_guard = device.snatchable_lock.read(); let raw_buf = self.try_raw(&snatch_guard)?; @@ -758,9 +815,11 @@ impl Buffer { BufferMapState::Init { staging_buffer } => { #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { + use crate::device::trace::IntoTrace; + let data = trace.make_binary("bin", staging_buffer.get_data()); trace.add(trace::Action::WriteBuffer { - id: buffer_id, + id: self.to_trace(), data, range: 0..self.size, queued: true, @@ -820,12 +879,14 @@ impl Buffer { if host == HostMap::Write { #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { + use crate::device::trace::IntoTrace; + let size = range.end - range.start; let data = trace.make_binary("bin", unsafe { core::slice::from_raw_parts(mapping.ptr.as_ptr(), size as usize) }); trace.add(trace::Action::WriteBuffer { - id: buffer_id, + id: self.to_trace(), data, range: range.clone(), queued: false, @@ -841,7 +902,7 @@ impl Buffer { Ok(None) } - pub(crate) fn destroy(self: &Arc) { + pub fn destroy(self: &Arc) { let device = &self.device; let temp = { @@ -1343,7 +1404,7 @@ impl Texture { } } - pub(crate) fn destroy(self: &Arc) { + pub fn destroy(self: &Arc) { let device = &self.device; let temp = { @@ -1843,7 +1904,7 @@ impl Drop for ExternalTexture { } impl ExternalTexture { - pub(crate) fn destroy(self: &Arc) { + pub fn destroy(self: &Arc) { self.params.destroy(); } } diff --git a/wgpu-core/src/storage.rs b/wgpu-core/src/storage.rs index 8f04261006e..e053cb8f4db 100644 --- a/wgpu-core/src/storage.rs +++ b/wgpu-core/src/storage.rs @@ -19,7 +19,9 @@ where Occupied(T, Epoch), } -pub(crate) trait StorageItem: ResourceType { +/// Not a public API. For use only by `player`. +#[doc(hidden)] +pub trait StorageItem: ResourceType { type Marker: Marker; } diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 1f00fc9cd5c..6b847b9ae07 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -5339,6 +5339,8 @@ bitflags::bitflags! { bitflags::bitflags! { /// Similar to `BufferUsages`, but used only for `CommandEncoder::transition_resources`. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + #[cfg_attr(feature = "serde", serde(transparent))] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct BufferUses: u16 { /// The argument to a read-only mapping. @@ -5390,6 +5392,7 @@ bitflags::bitflags! { /// A buffer transition for use with `CommandEncoder::transition_resources`. #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct BufferTransition { /// The buffer to transition. pub buffer: T, @@ -5651,6 +5654,8 @@ bitflags::bitflags! { bitflags::bitflags! { /// Similar to `TextureUsages`, but used only for `CommandEncoder::transition_resources`. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] + #[cfg_attr(feature = "serde", serde(transparent))] pub struct TextureUses: u16 { /// The texture is in unknown state. const UNINITIALIZED = 1 << 0; @@ -5706,6 +5711,7 @@ bitflags::bitflags! { /// A texture transition for use with `CommandEncoder::transition_resources`. #[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct TextureTransition { /// The texture to transition. pub texture: T, @@ -5719,6 +5725,7 @@ pub struct TextureTransition { /// Specifies a particular set of subresources in a texture. #[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct TextureSelector { /// Range of mips to use. pub mips: Range, diff --git a/wgpu/src/backend/wgpu_core.rs b/wgpu/src/backend/wgpu_core.rs index 405e4779b70..7a396360b7e 100644 --- a/wgpu/src/backend/wgpu_core.rs +++ b/wgpu/src/backend/wgpu_core.rs @@ -1779,7 +1779,7 @@ impl dispatch::DeviceInterface for CoreDevice { sample_count: desc.sample_count, multiview: desc.multiview, }; - let encoder = match wgc::command::RenderBundleEncoder::new(&descriptor, self.id, None) { + let encoder = match wgc::command::RenderBundleEncoder::new(&descriptor, self.id) { Ok(encoder) => encoder, Err(e) => panic!("Error in Device::create_render_bundle_encoder: {e}"), };