From c6bc37dbdee65195cc26bcca410db0f95466df27 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 21 Nov 2020 00:05:56 -0500 Subject: [PATCH] [error] render bundles --- player/src/bin/play.rs | 4 +- player/src/lib.rs | 71 +++- player/tests/test.rs | 4 +- wgpu-core/src/binding_model.rs | 12 +- wgpu-core/src/command/bundle.rs | 688 ++++++++++++++++---------------- wgpu-core/src/command/mod.rs | 33 +- wgpu-core/src/device/mod.rs | 183 +++++---- wgpu-core/src/instance.rs | 2 +- wgpu-core/src/pipeline.rs | 8 +- wgpu-core/src/resource.rs | 14 +- wgpu-core/src/validation.rs | 10 +- 11 files changed, 542 insertions(+), 487 deletions(-) diff --git a/player/src/bin/play.rs b/player/src/bin/play.rs index b2ae956bb3..0cc4e05f4b 100644 --- a/player/src/bin/play.rs +++ b/player/src/bin/play.rs @@ -91,7 +91,9 @@ fn main() { None, id )); - assert_eq!(error, None); + if let Some(e) = error { + panic!("{:?}", e); + } id } _ => panic!("Expected Action::Init"), diff --git a/player/src/lib.rs b/player/src/lib.rs index 2553142427..d40926a5fd 100644 --- a/player/src/lib.rs +++ b/player/src/lib.rs @@ -117,8 +117,12 @@ impl GlobalPlay for wgc::hub::Global { } } } - self.command_encoder_finish::(encoder, &wgt::CommandBufferDescriptor { label: None }) - .unwrap() + let (cmd_buf, error) = self + .command_encoder_finish::(encoder, &wgt::CommandBufferDescriptor { label: None }); + if let Some(e) = error { + panic!("{:?}", e); + } + cmd_buf } fn process( @@ -138,7 +142,9 @@ impl GlobalPlay for wgc::hub::Global { A::CreateBuffer(id, desc) => { self.device_maintain_ids::(device).unwrap(); let (_, error) = self.device_create_buffer::(device, &desc, id); - assert_eq!(error, None); + if let Some(e) = error { + panic!("{:?}", e); + } } A::FreeBuffer(id) => { self.buffer_destroy::(id).unwrap(); @@ -149,7 +155,9 @@ impl GlobalPlay for wgc::hub::Global { A::CreateTexture(id, desc) => { self.device_maintain_ids::(device).unwrap(); let (_, error) = self.device_create_texture::(device, &desc, id); - assert_eq!(error, None); + if let Some(e) = error { + panic!("{:?}", e); + } } A::FreeTexture(id) => { self.texture_destroy::(id).unwrap(); @@ -164,7 +172,9 @@ impl GlobalPlay for wgc::hub::Global { } => { self.device_maintain_ids::(device).unwrap(); let (_, error) = self.texture_create_view::(parent_id, &desc, id); - assert_eq!(error, None); + if let Some(e) = error { + panic!("{:?}", e); + } } A::DestroyTextureView(id) => { self.texture_view_drop::(id).unwrap(); @@ -172,7 +182,9 @@ impl GlobalPlay for wgc::hub::Global { A::CreateSampler(id, desc) => { self.device_maintain_ids::(device).unwrap(); let (_, error) = self.device_create_sampler::(device, &desc, id); - assert_eq!(error, None); + if let Some(e) = error { + panic!("{:?}", e); + } } A::DestroySampler(id) => { self.sampler_drop::(id); @@ -187,7 +199,9 @@ impl GlobalPlay for wgc::hub::Global { } A::CreateBindGroupLayout(id, desc) => { let (_, error) = self.device_create_bind_group_layout::(device, &desc, id); - assert_eq!(error, None); + if let Some(e) = error { + panic!("{:?}", e); + } } A::DestroyBindGroupLayout(id) => { self.bind_group_layout_drop::(id); @@ -195,7 +209,9 @@ impl GlobalPlay for wgc::hub::Global { A::CreatePipelineLayout(id, desc) => { self.device_maintain_ids::(device).unwrap(); let (_, error) = self.device_create_pipeline_layout::(device, &desc, id); - assert_eq!(error, None); + if let Some(e) = error { + panic!("{:?}", e); + } } A::DestroyPipelineLayout(id) => { self.pipeline_layout_drop::(id); @@ -203,7 +219,9 @@ impl GlobalPlay for wgc::hub::Global { A::CreateBindGroup(id, desc) => { self.device_maintain_ids::(device).unwrap(); let (_, error) = self.device_create_bind_group::(device, &desc, id); - assert_eq!(error, None); + if let Some(e) = error { + panic!("{:?}", e); + } } A::DestroyBindGroup(id) => { self.bind_group_drop::(id); @@ -224,7 +242,9 @@ impl GlobalPlay for wgc::hub::Global { label, }; let (_, error) = self.device_create_shader_module::(device, &desc, id); - assert_eq!(error, None); + if let Some(e) = error { + panic!("{:?}", e); + } } A::DestroyShaderModule(id) => { self.shader_module_drop::(id); @@ -233,7 +253,9 @@ impl GlobalPlay for wgc::hub::Global { self.device_maintain_ids::(device).unwrap(); let (_, _, error) = self.device_create_compute_pipeline::(device, &desc, id, None); - assert_eq!(error, None); + if let Some(e) = error { + panic!("{:?}", e); + } } A::DestroyComputePipeline(id) => { self.compute_pipeline_drop::(id); @@ -242,7 +264,9 @@ impl GlobalPlay for wgc::hub::Global { self.device_maintain_ids::(device).unwrap(); let (_, _, error) = self.device_create_render_pipeline::(device, &desc, id, None); - assert_eq!(error, None); + if let Some(e) = error { + panic!("{:?}", e); + } } A::DestroyRenderPipeline(id) => { self.render_pipeline_drop::(id); @@ -250,12 +274,14 @@ impl GlobalPlay for wgc::hub::Global { A::CreateRenderBundle { id, desc, base } => { let bundle = wgc::command::RenderBundleEncoder::new(&desc, device, Some(base)).unwrap(); - self.render_bundle_encoder_finish::( + let (_, error) = self.render_bundle_encoder_finish::( bundle, &wgt::RenderBundleDescriptor { label: desc.label }, id, - ) - .unwrap(); + ); + if let Some(e) = error { + panic!("{:?}", e); + } } A::DestroyRenderBundle(id) => { self.render_bundle_drop::(id); @@ -288,13 +314,14 @@ impl GlobalPlay for wgc::hub::Global { .unwrap(); } A::Submit(_index, commands) => { - let encoder = self - .device_create_command_encoder::( - device, - &wgt::CommandEncoderDescriptor { label: None }, - comb_manager.alloc(device.backend()), - ) - .unwrap(); + let (encoder, error) = self.device_create_command_encoder::( + device, + &wgt::CommandEncoderDescriptor { label: None }, + comb_manager.alloc(device.backend()), + ); + if let Some(e) = error { + panic!("{:?}", e); + } let cmdbuf = self.encode_commands::(encoder, commands); self.queue_submit::(device, &[cmdbuf]).unwrap(); } diff --git a/player/tests/test.rs b/player/tests/test.rs index 43397c3883..c96cecf5e7 100644 --- a/player/tests/test.rs +++ b/player/tests/test.rs @@ -98,7 +98,9 @@ impl Test<'_> { None, device )); - assert_eq!(error, None); + if let Some(e) = error { + panic!("{:?}", e); + } let mut command_buffer_id_manager = wgc::hub::IdentityManager::default(); println!("\t\t\tRunning..."); diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index c90a65db48..cc5d270a5e 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -26,7 +26,7 @@ use std::{ use thiserror::Error; -#[derive(Clone, Debug, Error, PartialEq)] +#[derive(Clone, Debug, Error)] pub enum CreateBindGroupLayoutError { #[error(transparent)] Device(#[from] DeviceError), @@ -40,7 +40,7 @@ pub enum CreateBindGroupLayoutError { TooManyBindings(BindingTypeMaxCountError), } -#[derive(Clone, Debug, Error, PartialEq)] +#[derive(Clone, Debug, Error)] pub enum CreateBindGroupError { #[error(transparent)] Device(#[from] DeviceError), @@ -96,7 +96,7 @@ pub enum CreateBindGroupError { DepthStencilAspect, } -#[derive(Clone, Debug, Error, PartialEq)] +#[derive(Clone, Debug, Error)] pub enum BindingZone { #[error("stage {0:?}")] Stage(wgt::ShaderStage), @@ -104,7 +104,7 @@ pub enum BindingZone { Pipeline, } -#[derive(Clone, Debug, Error, PartialEq)] +#[derive(Clone, Debug, Error)] #[error("too many bindings of type {kind:?} in {zone}, limit is {count}")] pub struct BindingTypeMaxCountError { pub kind: BindingTypeMaxCountErrorKind, @@ -112,7 +112,7 @@ pub struct BindingTypeMaxCountError { pub count: u32, } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub enum BindingTypeMaxCountErrorKind { DynamicUniformBuffers, DynamicStorageBuffers, @@ -336,7 +336,7 @@ impl Resource for BindGroupLayout { } } -#[derive(Clone, Debug, Error, PartialEq)] +#[derive(Clone, Debug, Error)] pub enum CreatePipelineLayoutError { #[error(transparent)] Device(#[from] DeviceError), diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index 7e22267eaa..591eb574f0 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -44,9 +44,10 @@ use crate::{ }, conv, device::{ - AttachmentData, DeviceError, RenderPassContext, MAX_VERTEX_BUFFERS, SHADER_STAGE_COUNT, + AttachmentData, Device, DeviceError, RenderPassContext, MAX_VERTEX_BUFFERS, + SHADER_STAGE_COUNT, }, - hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Resource, Storage, Token}, + hub::{GfxBackend, GlobalIdentityHandlerFactory, Hub, Resource, Storage, Token}, id, resource::BufferUse, span, @@ -55,7 +56,7 @@ use crate::{ Label, LabelHelpers, LifeGuard, Stored, MAX_BIND_GROUPS, }; use arrayvec::ArrayVec; -use std::{borrow::Cow, iter, marker::PhantomData, ops::Range}; +use std::{borrow::Cow, iter, ops::Range}; use thiserror::Error; /// Describes a [`RenderBundleEncoder`]. @@ -111,9 +112,328 @@ impl RenderBundleEncoder { }) } + pub fn dummy(parent_id: id::DeviceId) -> Self { + Self { + base: BasePass::new(), + parent_id, + context: RenderPassContext { + attachments: AttachmentData { + colors: ArrayVec::new(), + resolves: ArrayVec::new(), + depth_stencil: None, + }, + sample_count: 0, + }, + } + } + pub fn parent(&self) -> id::DeviceId { self.parent_id } + + pub(crate) fn finish( + self, + desc: &RenderBundleDescriptor, + device: &Device, + hub: &Hub, + token: &mut Token>, + ) -> Result { + let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(token); + let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); + let (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token); + let (buffer_guard, _) = hub.buffers.read(&mut token); + + let mut state = State { + trackers: TrackerSet::new(self.parent_id.backend()), + index: IndexState::new(), + vertex: (0..MAX_VERTEX_BUFFERS) + .map(|_| VertexState::new()) + .collect(), + bind: (0..MAX_BIND_GROUPS).map(|_| BindState::new()).collect(), + push_constant_ranges: PushConstantState::new(), + raw_dynamic_offsets: Vec::new(), + flat_dynamic_offsets: Vec::new(), + used_bind_groups: 0, + pipeline: StateChange::new(), + }; + let mut commands = Vec::new(); + let mut base = self.base.as_ref(); + let mut pipeline_layout_id = None::>; + + for &command in base.commands { + match command { + RenderCommand::SetBindGroup { + index, + num_dynamic_offsets, + bind_group_id, + } => { + let scope = PassErrorScope::SetBindGroup(bind_group_id); + + let max_bind_groups = device.limits.max_bind_groups; + if (index as u32) >= max_bind_groups { + return Err(RenderCommandError::BindGroupIndexOutOfRange { + index, + max: max_bind_groups, + }) + .map_pass_err(scope); + } + + let offsets = &base.dynamic_offsets[..num_dynamic_offsets as usize]; + base.dynamic_offsets = &base.dynamic_offsets[num_dynamic_offsets as usize..]; + // Check for misaligned offsets. + if let Some(offset) = offsets + .iter() + .map(|offset| *offset as wgt::BufferAddress) + .find(|offset| offset % wgt::BIND_BUFFER_ALIGNMENT != 0) + { + return Err(RenderCommandError::UnalignedBufferOffset(offset)) + .map_pass_err(scope); + } + + let bind_group = state + .trackers + .bind_groups + .use_extend(&*bind_group_guard, bind_group_id, (), ()) + .map_err(|_| RenderCommandError::InvalidBindGroup(bind_group_id)) + .map_pass_err(scope)?; + if bind_group.dynamic_binding_info.len() != offsets.len() { + return Err(RenderCommandError::InvalidDynamicOffsetCount { + actual: offsets.len(), + expected: bind_group.dynamic_binding_info.len(), + }) + .map_pass_err(scope); + } + + state.set_bind_group(index, bind_group_id, bind_group.layout_id, offsets); + state + .trackers + .merge_extend(&bind_group.used) + .map_pass_err(scope)?; + } + RenderCommand::SetPipeline(pipeline_id) => { + let scope = PassErrorScope::SetPipelineRender(pipeline_id); + if state.pipeline.set_and_check_redundant(pipeline_id) { + continue; + } + + let pipeline = state + .trackers + .render_pipes + .use_extend(&*pipeline_guard, pipeline_id, (), ()) + .unwrap(); + + self.context + .check_compatible(&pipeline.pass_context) + .map_err(RenderCommandError::IncompatiblePipeline) + .map_pass_err(scope)?; + + //TODO: check read-only depth + + let layout = &pipeline_layout_guard[pipeline.layout_id.value]; + pipeline_layout_id = Some(pipeline.layout_id.value); + + state.set_pipeline( + pipeline.index_format, + &pipeline.vertex_strides, + &layout.bind_group_layout_ids, + &layout.push_constant_ranges, + ); + commands.push(command); + if let Some(iter) = state.flush_push_constants() { + commands.extend(iter) + } + } + RenderCommand::SetIndexBuffer { + buffer_id, + offset, + size, + } => { + let scope = PassErrorScope::SetIndexBuffer(buffer_id); + let buffer = state + .trackers + .buffers + .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDEX) + .unwrap(); + check_buffer_usage(buffer.usage, wgt::BufferUsage::INDEX) + .map_pass_err(scope)?; + + let end = match size { + Some(s) => offset + s.get(), + None => buffer.size, + }; + state.index.set_buffer(buffer_id, offset..end); + } + RenderCommand::SetVertexBuffer { + slot, + buffer_id, + offset, + size, + } => { + let scope = PassErrorScope::SetVertexBuffer(buffer_id); + let buffer = state + .trackers + .buffers + .use_extend(&*buffer_guard, buffer_id, (), BufferUse::VERTEX) + .unwrap(); + check_buffer_usage(buffer.usage, wgt::BufferUsage::VERTEX) + .map_pass_err(scope)?; + + let end = match size { + Some(s) => offset + s.get(), + None => buffer.size, + }; + state.vertex[slot as usize].set_buffer(buffer_id, offset..end); + } + RenderCommand::SetPushConstant { + stages, + offset, + size_bytes, + values_offset: _, + } => { + let scope = PassErrorScope::SetPushConstant; + let end_offset = offset + size_bytes; + + let pipeline_layout_id = pipeline_layout_id + .ok_or(DrawError::MissingPipeline) + .map_pass_err(scope)?; + let pipeline_layout = &pipeline_layout_guard[pipeline_layout_id]; + + pipeline_layout + .validate_push_constant_ranges(stages, offset, end_offset) + .map_pass_err(scope)?; + + commands.push(command); + } + RenderCommand::Draw { + vertex_count, + instance_count, + first_vertex, + first_instance, + } => { + let scope = PassErrorScope::Draw; + let (vertex_limit, instance_limit) = state.vertex_limits(); + let last_vertex = first_vertex + vertex_count; + if last_vertex > vertex_limit { + return Err(DrawError::VertexBeyondLimit { + last_vertex, + vertex_limit, + }) + .map_pass_err(scope); + } + let last_instance = first_instance + instance_count; + if last_instance > instance_limit { + return Err(DrawError::InstanceBeyondLimit { + last_instance, + instance_limit, + }) + .map_pass_err(scope); + } + commands.extend(state.flush_vertices()); + commands.extend(state.flush_binds()); + commands.push(command); + } + RenderCommand::DrawIndexed { + index_count, + instance_count, + first_index, + base_vertex: _, + first_instance, + } => { + let scope = PassErrorScope::DrawIndexed; + //TODO: validate that base_vertex + max_index() is within the provided range + let (_, instance_limit) = state.vertex_limits(); + let index_limit = state.index.limit(); + let last_index = first_index + index_count; + if last_index > index_limit { + return Err(DrawError::IndexBeyondLimit { + last_index, + index_limit, + }) + .map_pass_err(scope); + } + let last_instance = first_instance + instance_count; + if last_instance > instance_limit { + return Err(DrawError::InstanceBeyondLimit { + last_instance, + instance_limit, + }) + .map_pass_err(scope); + } + commands.extend(state.index.flush()); + commands.extend(state.flush_vertices()); + commands.extend(state.flush_binds()); + commands.push(command); + } + RenderCommand::MultiDrawIndirect { + buffer_id, + offset: _, + count: None, + indexed: false, + } => { + let scope = PassErrorScope::DrawIndirect; + let buffer = state + .trackers + .buffers + .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT) + .unwrap(); + check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT) + .map_pass_err(scope)?; + + commands.extend(state.flush_vertices()); + commands.extend(state.flush_binds()); + commands.push(command); + } + RenderCommand::MultiDrawIndirect { + buffer_id, + offset: _, + count: None, + indexed: true, + } => { + let scope = PassErrorScope::DrawIndexedIndirect; + let buffer = state + .trackers + .buffers + .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT) + .map_err(|err| RenderCommandError::Buffer(buffer_id, err)) + .map_pass_err(scope)?; + check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT) + .map_pass_err(scope)?; + + commands.extend(state.index.flush()); + commands.extend(state.flush_vertices()); + commands.extend(state.flush_binds()); + commands.push(command); + } + RenderCommand::MultiDrawIndirect { .. } + | RenderCommand::MultiDrawIndirectCount { .. } => unimplemented!(), + RenderCommand::PushDebugGroup { color: _, len: _ } => unimplemented!(), + RenderCommand::InsertDebugMarker { color: _, len: _ } => unimplemented!(), + RenderCommand::PopDebugGroup => unimplemented!(), + RenderCommand::ExecuteBundle(_) + | RenderCommand::SetBlendColor(_) + | RenderCommand::SetStencilReference(_) + | RenderCommand::SetViewport { .. } + | RenderCommand::SetScissor(_) => unreachable!("not supported by a render bundle"), + } + } + + let _ = desc.label; //TODO: actually use + Ok(RenderBundle { + base: BasePass { + commands, + dynamic_offsets: state.flat_dynamic_offsets, + string_data: Vec::new(), + push_constant_data: Vec::new(), + }, + device_id: Stored { + value: id::Valid(self.parent_id), + ref_count: device.life_guard.add_ref(), + }, + used: state.trackers, + context: self.context, + life_guard: LifeGuard::new(desc.label.borrow_or_default()), + }) + } } /// Error type returned from `RenderBundleEncoder::new` if the sample count is invalid. @@ -150,6 +470,11 @@ unsafe impl Send for RenderBundle {} unsafe impl Sync for RenderBundle {} impl RenderBundle { + #[cfg(feature = "trace")] + pub(crate) fn to_base_pass(&self) -> BasePass { + BasePass::from_ref(self.base.as_ref()) + } + /// Actually encode the contents into a native command buffer. /// /// This is partially duplicating the logic of `command_encoder_run_render_pass`. @@ -685,6 +1010,13 @@ pub struct RenderBundleError { inner: RenderBundleErrorInner, } +impl RenderBundleError { + pub(crate) const INVALID_DEVICE: Self = RenderBundleError { + scope: PassErrorScope::Bundle, + inner: RenderBundleErrorInner::Device(DeviceError::Invalid), + }; +} + impl MapPassErr for Result where E: Into, @@ -697,356 +1029,6 @@ where } } -impl Global { - pub fn render_bundle_encoder_finish( - &self, - bundle_encoder: RenderBundleEncoder, - desc: &RenderBundleDescriptor, - id_in: Input, - ) -> Result { - span!(_guard, INFO, "RenderBundleEncoder::finish"); - let scope = PassErrorScope::Bundle; - - let hub = B::hub(self); - let mut token = Token::root(); - let (device_guard, mut token) = hub.devices.read(&mut token); - - let device = device_guard - .get(bundle_encoder.parent_id) - .map_err(|_| DeviceError::Invalid) - .map_pass_err(scope)?; - let render_bundle = { - let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); - let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); - let (pipeline_guard, mut token) = hub.render_pipelines.read(&mut token); - let (buffer_guard, _) = hub.buffers.read(&mut token); - - let mut state = State { - trackers: TrackerSet::new(bundle_encoder.parent_id.backend()), - index: IndexState::new(), - vertex: (0..MAX_VERTEX_BUFFERS) - .map(|_| VertexState::new()) - .collect(), - bind: (0..MAX_BIND_GROUPS).map(|_| BindState::new()).collect(), - push_constant_ranges: PushConstantState::new(), - raw_dynamic_offsets: Vec::new(), - flat_dynamic_offsets: Vec::new(), - used_bind_groups: 0, - pipeline: StateChange::new(), - }; - let mut commands = Vec::new(); - let mut base = bundle_encoder.base.as_ref(); - let mut pipeline_layout_id = None::>; - - for &command in base.commands { - match command { - RenderCommand::SetBindGroup { - index, - num_dynamic_offsets, - bind_group_id, - } => { - let scope = PassErrorScope::SetBindGroup(bind_group_id); - - let max_bind_groups = device.limits.max_bind_groups; - if (index as u32) >= max_bind_groups { - return Err(RenderCommandError::BindGroupIndexOutOfRange { - index, - max: max_bind_groups, - }) - .map_pass_err(scope); - } - - let offsets = &base.dynamic_offsets[..num_dynamic_offsets as usize]; - base.dynamic_offsets = - &base.dynamic_offsets[num_dynamic_offsets as usize..]; - // Check for misaligned offsets. - if let Some(offset) = offsets - .iter() - .map(|offset| *offset as wgt::BufferAddress) - .find(|offset| offset % wgt::BIND_BUFFER_ALIGNMENT != 0) - { - return Err(RenderCommandError::UnalignedBufferOffset(offset)) - .map_pass_err(scope); - } - - let bind_group = state - .trackers - .bind_groups - .use_extend(&*bind_group_guard, bind_group_id, (), ()) - .map_err(|_| RenderCommandError::InvalidBindGroup(bind_group_id)) - .map_pass_err(scope)?; - if bind_group.dynamic_binding_info.len() != offsets.len() { - return Err(RenderCommandError::InvalidDynamicOffsetCount { - actual: offsets.len(), - expected: bind_group.dynamic_binding_info.len(), - }) - .map_pass_err(scope); - } - - state.set_bind_group(index, bind_group_id, bind_group.layout_id, offsets); - state - .trackers - .merge_extend(&bind_group.used) - .map_pass_err(scope)?; - } - RenderCommand::SetPipeline(pipeline_id) => { - let scope = PassErrorScope::SetPipelineRender(pipeline_id); - if state.pipeline.set_and_check_redundant(pipeline_id) { - continue; - } - - let pipeline = state - .trackers - .render_pipes - .use_extend(&*pipeline_guard, pipeline_id, (), ()) - .unwrap(); - - bundle_encoder - .context - .check_compatible(&pipeline.pass_context) - .map_err(RenderCommandError::IncompatiblePipeline) - .map_pass_err(scope)?; - - //TODO: check read-only depth - - let layout = &pipeline_layout_guard[pipeline.layout_id.value]; - pipeline_layout_id = Some(pipeline.layout_id.value); - - state.set_pipeline( - pipeline.index_format, - &pipeline.vertex_strides, - &layout.bind_group_layout_ids, - &layout.push_constant_ranges, - ); - commands.push(command); - if let Some(iter) = state.flush_push_constants() { - commands.extend(iter) - } - } - RenderCommand::SetIndexBuffer { - buffer_id, - offset, - size, - } => { - let scope = PassErrorScope::SetIndexBuffer(buffer_id); - let buffer = state - .trackers - .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDEX) - .unwrap(); - check_buffer_usage(buffer.usage, wgt::BufferUsage::INDEX) - .map_pass_err(scope)?; - - let end = match size { - Some(s) => offset + s.get(), - None => buffer.size, - }; - state.index.set_buffer(buffer_id, offset..end); - } - RenderCommand::SetVertexBuffer { - slot, - buffer_id, - offset, - size, - } => { - let scope = PassErrorScope::SetVertexBuffer(buffer_id); - let buffer = state - .trackers - .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::VERTEX) - .unwrap(); - check_buffer_usage(buffer.usage, wgt::BufferUsage::VERTEX) - .map_pass_err(scope)?; - - let end = match size { - Some(s) => offset + s.get(), - None => buffer.size, - }; - state.vertex[slot as usize].set_buffer(buffer_id, offset..end); - } - RenderCommand::SetPushConstant { - stages, - offset, - size_bytes, - values_offset: _, - } => { - let scope = PassErrorScope::SetPushConstant; - let end_offset = offset + size_bytes; - - let pipeline_layout_id = pipeline_layout_id - .ok_or(DrawError::MissingPipeline) - .map_pass_err(scope)?; - let pipeline_layout = &pipeline_layout_guard[pipeline_layout_id]; - - pipeline_layout - .validate_push_constant_ranges(stages, offset, end_offset) - .map_pass_err(scope)?; - - commands.push(command); - } - RenderCommand::Draw { - vertex_count, - instance_count, - first_vertex, - first_instance, - } => { - let scope = PassErrorScope::Draw; - let (vertex_limit, instance_limit) = state.vertex_limits(); - let last_vertex = first_vertex + vertex_count; - if last_vertex > vertex_limit { - return Err(DrawError::VertexBeyondLimit { - last_vertex, - vertex_limit, - }) - .map_pass_err(scope); - } - let last_instance = first_instance + instance_count; - if last_instance > instance_limit { - return Err(DrawError::InstanceBeyondLimit { - last_instance, - instance_limit, - }) - .map_pass_err(scope); - } - commands.extend(state.flush_vertices()); - commands.extend(state.flush_binds()); - commands.push(command); - } - RenderCommand::DrawIndexed { - index_count, - instance_count, - first_index, - base_vertex: _, - first_instance, - } => { - let scope = PassErrorScope::DrawIndexed; - //TODO: validate that base_vertex + max_index() is within the provided range - let (_, instance_limit) = state.vertex_limits(); - let index_limit = state.index.limit(); - let last_index = first_index + index_count; - if last_index > index_limit { - return Err(DrawError::IndexBeyondLimit { - last_index, - index_limit, - }) - .map_pass_err(scope); - } - let last_instance = first_instance + instance_count; - if last_instance > instance_limit { - return Err(DrawError::InstanceBeyondLimit { - last_instance, - instance_limit, - }) - .map_pass_err(scope); - } - commands.extend(state.index.flush()); - commands.extend(state.flush_vertices()); - commands.extend(state.flush_binds()); - commands.push(command); - } - RenderCommand::MultiDrawIndirect { - buffer_id, - offset: _, - count: None, - indexed: false, - } => { - let scope = PassErrorScope::DrawIndirect; - let buffer = state - .trackers - .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT) - .unwrap(); - check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT) - .map_pass_err(scope)?; - - commands.extend(state.flush_vertices()); - commands.extend(state.flush_binds()); - commands.push(command); - } - RenderCommand::MultiDrawIndirect { - buffer_id, - offset: _, - count: None, - indexed: true, - } => { - let scope = PassErrorScope::DrawIndexedIndirect; - let buffer = state - .trackers - .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT) - .map_err(|err| RenderCommandError::Buffer(buffer_id, err)) - .map_pass_err(scope)?; - check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT) - .map_pass_err(scope)?; - - commands.extend(state.index.flush()); - commands.extend(state.flush_vertices()); - commands.extend(state.flush_binds()); - commands.push(command); - } - RenderCommand::MultiDrawIndirect { .. } - | RenderCommand::MultiDrawIndirectCount { .. } => unimplemented!(), - RenderCommand::PushDebugGroup { color: _, len: _ } => unimplemented!(), - RenderCommand::InsertDebugMarker { color: _, len: _ } => unimplemented!(), - RenderCommand::PopDebugGroup => unimplemented!(), - RenderCommand::ExecuteBundle(_) - | RenderCommand::SetBlendColor(_) - | RenderCommand::SetStencilReference(_) - | RenderCommand::SetViewport { .. } - | RenderCommand::SetScissor(_) => { - unreachable!("not supported by a render bundle") - } - } - } - - tracing::debug!("Render bundle {:?} = {:#?}", id_in, state.trackers); - let _ = desc.label; //TODO: actually use - //TODO: check if the device is still alive - RenderBundle { - base: BasePass { - commands, - dynamic_offsets: state.flat_dynamic_offsets, - string_data: Vec::new(), - push_constant_data: Vec::new(), - }, - device_id: Stored { - value: id::Valid(bundle_encoder.parent_id), - ref_count: device.life_guard.add_ref(), - }, - used: state.trackers, - context: bundle_encoder.context, - life_guard: LifeGuard::new(desc.label.borrow_or_default()), - } - }; - - let ref_count = render_bundle.life_guard.add_ref(); - let id = hub - .render_bundles - .register_identity(id_in, render_bundle, &mut token); - - #[cfg(feature = "trace")] - if let Some(ref trace) = device.trace { - use crate::device::trace; - let (bundle_guard, _) = hub.render_bundles.read(&mut token); - let bundle = &bundle_guard[id]; - let label = desc.label.as_ref().map(|l| l.as_ref()); - trace.lock().add(trace::Action::CreateRenderBundle { - id: id.0, - desc: trace::new_render_bundle_encoder_descriptor(label, &bundle.context), - base: BasePass::from_ref(bundle.base.as_ref()), - }); - } - - device - .trackers - .lock() - .bundles - .init(id, ref_count, PhantomData) - .unwrap(); - Ok(id.0) - } -} - pub mod bundle_ffi { use super::{RenderBundleEncoder, RenderCommand}; use crate::{id, span, RawString}; diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index 523b0ebd2c..1093bde155 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -186,7 +186,7 @@ impl Global { &self, encoder_id: id::CommandEncoderId, _desc: &wgt::CommandBufferDescriptor