1use thiserror::Error;
2use wgt::{
3 error::{ErrorType, WebGpuError},
4 BufferAddress, DynamicOffset,
5};
6
7use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec};
8use core::{convert::Infallible, fmt, str};
9
10use crate::{
11 api_log,
12 binding_model::{BindError, BindGroup, ImmediateUploadError, LateMinBufferBindingSizeMismatch},
13 command::{
14 bind::{Binder, BinderError},
15 compute_command::ArcComputeCommand,
16 encoder::EncodingState,
17 memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState},
18 pass::{self, flush_bindings_helper, ImmediateState},
19 pass_base, pass_try,
20 query::{
21 end_pipeline_statistics_query, record_pass_timestamp_writes,
22 validate_and_begin_pipeline_statistics_query,
23 },
24 ArcCommand, BasePass, BindGroupStateChange, CommandEncoder, CommandEncoderError,
25 DebugGroupError, EncoderStateError, InnerCommandEncoder, MapPassErr, PassErrorScope,
26 PassStateError, PassTimestampWrites, QueryUseError, StateChange, TimestampWritesError,
27 TransitionResourcesError,
28 },
29 device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures},
30 hal_label, id, impl_resource_type,
31 init_tracker::MemoryInitKind,
32 pipeline::ComputePipeline,
33 resource::{
34 Buffer, DestroyedResourceError, InvalidOrDestroyedResourceError, InvalidResourceError,
35 Labeled, MissingBufferUsageError, ParentDevice, QuerySet, RawResourceAccess, TextureView,
36 Trackable,
37 },
38 track::{ResourceUsageCompatibilityError, TextureViewBindGroupState, Tracker},
39 Label,
40};
41
42pub type ComputeBasePass = BasePass<ArcComputeCommand, ComputePassError>;
43
44pub struct ComputePass {
52 base: ComputeBasePass,
54
55 parent: Option<Arc<CommandEncoder>>,
61
62 device: Arc<Device>,
63
64 timestamp_writes: Option<PassTimestampWrites>,
65
66 current_bind_groups: BindGroupStateChange,
68 current_pipeline: StateChange<Arc<ComputePipeline>>,
69}
70
71impl_resource_type!(ComputePass);
72
73impl crate::storage::StorageItem for ComputePass {
74 type Marker = id::markers::ComputePassEncoder;
75}
76
77impl ComputePass {
78 fn new(parent: Arc<CommandEncoder>, desc: ComputePassDescriptor) -> Self {
80 let ComputePassDescriptor {
81 label,
82 timestamp_writes,
83 } = desc;
84
85 Self {
86 base: BasePass::new(&label),
87 device: parent.device().clone(),
88 parent: Some(parent),
89 timestamp_writes,
90
91 current_bind_groups: BindGroupStateChange::new(),
92 current_pipeline: StateChange::new(),
93 }
94 }
95
96 fn new_invalid(parent: Arc<CommandEncoder>, label: &Label, err: ComputePassError) -> Self {
97 Self {
98 base: BasePass::new_invalid(label, err),
99 device: parent.device().clone(),
100 parent: Some(parent),
101 timestamp_writes: None,
102 current_bind_groups: BindGroupStateChange::new(),
103 current_pipeline: StateChange::new(),
104 }
105 }
106
107 #[inline]
108 pub fn label(&self) -> Option<&str> {
109 self.base.label.as_deref()
110 }
111
112 pub fn device(&self) -> &Arc<Device> {
113 &self.device
114 }
115}
116
117impl fmt::Debug for ComputePass {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 match self.parent {
120 Some(ref cmd_enc) => write!(f, "ComputePass {{ parent: {} }}", cmd_enc.error_ident()),
121 None => write!(f, "ComputePass {{ parent: None }}"),
122 }
123 }
124}
125
126#[derive(Clone, Debug, Default)]
127#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
128pub struct ComputePassDescriptor<'a, PTW = PassTimestampWrites> {
130 pub label: Label<'a>,
131 pub timestamp_writes: Option<PTW>,
133}
134
135#[derive(Clone, Debug, Error)]
136#[non_exhaustive]
137pub enum DispatchError {
138 #[error("Compute pipeline must be set")]
139 MissingPipeline(pass::MissingPipeline),
140 #[error(transparent)]
141 IncompatibleBindGroup(#[from] Box<BinderError>),
142 #[error(
143 "Each current dispatch group size dimension ({current:?}) must be less or equal to {limit}"
144 )]
145 InvalidGroupSize { current: [u32; 3], limit: u32 },
146 #[error(transparent)]
147 BindingSizeTooSmall(#[from] LateMinBufferBindingSizeMismatch),
148 #[error("Not all immediate data required by the pipeline has been set via set_immediates (missing byte ranges: {missing})")]
149 MissingImmediateData {
150 missing: naga::valid::ImmediateSlots,
151 },
152}
153
154impl WebGpuError for DispatchError {
155 fn webgpu_error_type(&self) -> ErrorType {
156 ErrorType::Validation
157 }
158}
159
160#[derive(Clone, Debug, Error)]
162pub enum ComputePassErrorInner {
163 #[error(transparent)]
164 Device(#[from] DeviceError),
165 #[error(transparent)]
166 EncoderState(#[from] EncoderStateError),
167 #[error("Parent encoder is invalid")]
168 InvalidParentEncoder,
169 #[error(transparent)]
170 DebugGroupError(#[from] DebugGroupError),
171 #[error(transparent)]
172 BindGroupIndexOutOfRange(#[from] pass::BindGroupIndexOutOfRange),
173 #[error(transparent)]
174 DestroyedResource(#[from] DestroyedResourceError),
175 #[error("Indirect buffer offset {0:?} is not a multiple of 4")]
176 UnalignedIndirectBufferOffset(BufferAddress),
177 #[error("Indirect buffer of {args_size} bytes starting at offset {offset} would overrun buffer of size {buffer_size}")]
178 IndirectBufferOverrun {
179 args_size: u64,
180 offset: u64,
181 buffer_size: u64,
182 },
183 #[error(transparent)]
184 ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),
185 #[error(transparent)]
186 MissingBufferUsage(#[from] MissingBufferUsageError),
187 #[error(transparent)]
188 Dispatch(#[from] DispatchError),
189 #[error(transparent)]
190 Bind(#[from] BindError),
191 #[error(transparent)]
192 ImmediateData(#[from] ImmediateUploadError),
193 #[error(transparent)]
194 QueryUse(#[from] QueryUseError),
195 #[error(transparent)]
196 TransitionResources(#[from] TransitionResourcesError),
197 #[error(transparent)]
198 MissingFeatures(#[from] MissingFeatures),
199 #[error(transparent)]
200 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
201 #[error("The compute pass has already been ended and no further commands can be recorded")]
202 PassEnded,
203 #[error(transparent)]
204 InvalidResource(#[from] InvalidResourceError),
205 #[error(transparent)]
206 TimestampWrites(#[from] TimestampWritesError),
207}
208
209impl From<InvalidOrDestroyedResourceError> for ComputePassErrorInner {
210 fn from(value: InvalidOrDestroyedResourceError) -> Self {
211 match value {
212 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
213 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
214 }
215 }
216}
217
218#[derive(Clone, Debug, Error)]
221#[error("{scope}")]
222pub struct ComputePassError {
223 pub scope: PassErrorScope,
224 #[source]
225 pub(super) inner: ComputePassErrorInner,
226}
227
228impl From<pass::MissingPipeline> for ComputePassErrorInner {
229 fn from(value: pass::MissingPipeline) -> Self {
230 Self::Dispatch(DispatchError::MissingPipeline(value))
231 }
232}
233
234impl<E> MapPassErr<ComputePassError> for E
235where
236 E: Into<ComputePassErrorInner>,
237{
238 fn map_pass_err(self, scope: PassErrorScope) -> ComputePassError {
239 ComputePassError {
240 scope,
241 inner: self.into(),
242 }
243 }
244}
245
246impl WebGpuError for ComputePassError {
247 fn webgpu_error_type(&self) -> ErrorType {
248 let Self { scope: _, inner } = self;
249 match inner {
250 ComputePassErrorInner::Device(e) => e.webgpu_error_type(),
251 ComputePassErrorInner::EncoderState(e) => e.webgpu_error_type(),
252 ComputePassErrorInner::DebugGroupError(e) => e.webgpu_error_type(),
253 ComputePassErrorInner::DestroyedResource(e) => e.webgpu_error_type(),
254 ComputePassErrorInner::ResourceUsageCompatibility(e) => e.webgpu_error_type(),
255 ComputePassErrorInner::MissingBufferUsage(e) => e.webgpu_error_type(),
256 ComputePassErrorInner::Dispatch(e) => e.webgpu_error_type(),
257 ComputePassErrorInner::Bind(e) => e.webgpu_error_type(),
258 ComputePassErrorInner::ImmediateData(e) => e.webgpu_error_type(),
259 ComputePassErrorInner::QueryUse(e) => e.webgpu_error_type(),
260 ComputePassErrorInner::TransitionResources(e) => e.webgpu_error_type(),
261 ComputePassErrorInner::MissingFeatures(e) => e.webgpu_error_type(),
262 ComputePassErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),
263 ComputePassErrorInner::InvalidResource(e) => e.webgpu_error_type(),
264 ComputePassErrorInner::TimestampWrites(e) => e.webgpu_error_type(),
265
266 ComputePassErrorInner::InvalidParentEncoder
267 | ComputePassErrorInner::BindGroupIndexOutOfRange { .. }
268 | ComputePassErrorInner::UnalignedIndirectBufferOffset(_)
269 | ComputePassErrorInner::IndirectBufferOverrun { .. }
270 | ComputePassErrorInner::PassEnded => ErrorType::Validation,
271 }
272 }
273}
274
275struct State<'scope, 'snatch_guard, 'cmd_enc> {
276 pipeline: Option<Arc<ComputePipeline>>,
277
278 pass: pass::PassState<'scope, 'snatch_guard, 'cmd_enc>,
279
280 active_query: Option<(Arc<QuerySet>, u32)>,
281
282 intermediate_trackers: Tracker,
283}
284
285impl<'scope, 'snatch_guard, 'cmd_enc> State<'scope, 'snatch_guard, 'cmd_enc> {
286 fn is_ready(&self) -> Result<(), DispatchError> {
287 if let Some(pipeline) = self.pipeline.as_ref() {
288 self.pass.binder.check_compatibility(pipeline.as_ref())?;
289 self.pass.binder.check_late_buffer_bindings()?;
290 if !self
291 .pass
292 .immediate_state
293 .immediate_slots_set
294 .contains(pipeline.immediate_slots_required)
295 {
296 return Err(DispatchError::MissingImmediateData {
297 missing: pipeline
298 .immediate_slots_required
299 .difference(self.pass.immediate_state.immediate_slots_set),
300 });
301 }
302 Ok(())
303 } else {
304 Err(DispatchError::MissingPipeline(pass::MissingPipeline))
305 }
306 }
307
308 fn flush_immediates(&mut self) {
309 let pipeline = self.pipeline.as_ref().unwrap();
310 let layout = pipeline.layout().unwrap();
311 self.pass
312 .immediate_state
313 .flush_immediates(layout, self.pass.base.raw_encoder);
314 }
315
316 fn flush_bindings(
355 &mut self,
356 indirect_buffer: Option<&Arc<Buffer>>,
357 track_indirect_buffer: bool,
358 ) -> Result<(), ComputePassErrorInner> {
359 for bind_group in self.pass.binder.list_active() {
360 unsafe { self.pass.scope.merge_bind_group(&bind_group.used)? };
361 }
362
363 if let Some(buffer) = indirect_buffer {
367 self.pass
368 .scope
369 .buffers
370 .merge_single(buffer, wgt::BufferUses::INDIRECT)?;
371 }
372
373 for bind_group in self.pass.binder.list_active() {
379 self.intermediate_trackers
380 .set_and_remove_from_usage_scope_sparse(&mut self.pass.scope, &bind_group.used);
381 }
382
383 if track_indirect_buffer {
384 self.intermediate_trackers
385 .buffers
386 .set_and_remove_from_usage_scope_sparse(
387 &mut self.pass.scope.buffers,
388 indirect_buffer.map(|buf| buf.tracker_index()),
389 );
390 } else if let Some(buffer) = indirect_buffer {
391 self.pass
392 .scope
393 .buffers
394 .remove_usage(buffer, wgt::BufferUses::INDIRECT);
395 }
396
397 flush_bindings_helper(&mut self.pass)?;
398
399 CommandEncoder::drain_barriers(
400 self.pass.base.raw_encoder,
401 &mut self.intermediate_trackers,
402 self.pass.base.snatch_guard,
403 );
404 Ok(())
405 }
406}
407
408fn transition_resources(
411 state: &mut State,
412 buffer_transitions: Vec<wgt::BufferTransition<Arc<Buffer>>>,
413 texture_transitions: Vec<wgt::TextureTransition<Arc<TextureView>>>,
414) -> Result<(), TransitionResourcesError> {
415 let indices = &state.pass.base.device.tracker_indices;
416 state.pass.scope.buffers.set_size(indices.buffers.size());
417 state.pass.scope.textures.set_size(indices.textures.size());
418
419 let mut buffer_ids = Vec::with_capacity(buffer_transitions.len());
420 let mut textures = TextureViewBindGroupState::new();
421
422 for buffer_transition in buffer_transitions {
424 buffer_transition
425 .buffer
426 .same_device(state.pass.base.device)?;
427
428 state
429 .pass
430 .scope
431 .buffers
432 .merge_single(&buffer_transition.buffer, buffer_transition.state)?;
433 buffer_ids.push(buffer_transition.buffer.tracker_index());
434 }
435
436 state
437 .intermediate_trackers
438 .buffers
439 .set_and_remove_from_usage_scope_sparse(&mut state.pass.scope.buffers, buffer_ids);
440
441 for texture_transition in texture_transitions {
443 texture_transition
444 .texture
445 .same_device(state.pass.base.device)?;
446
447 unsafe {
448 state.pass.scope.textures.merge_single(
449 &texture_transition.texture.parent,
450 texture_transition.selector,
451 texture_transition.state,
452 )
453 }?;
454
455 textures.insert_single(texture_transition.texture, texture_transition.state);
456 }
457
458 state
459 .intermediate_trackers
460 .textures
461 .set_and_remove_from_usage_scope_sparse(&mut state.pass.scope.textures, &textures);
462
463 CommandEncoder::drain_barriers(
465 state.pass.base.raw_encoder,
466 &mut state.intermediate_trackers,
467 state.pass.base.snatch_guard,
468 );
469 Ok(())
470}
471
472impl CommandEncoder {
473 fn begin_compute_pass_inner(
474 self: &Arc<Self>,
475 desc: &ComputePassDescriptor<'_, PassTimestampWrites<Arc<QuerySet>>>,
476 ) -> (ComputePass, Option<CommandEncoderError>) {
477 use EncoderStateError as SErr;
478
479 let scope = PassErrorScope::Pass;
480
481 let label = desc.label.as_deref().map(Cow::Borrowed);
482
483 let mut cmd_buf_data = self.data.lock();
484
485 match cmd_buf_data.lock_encoder() {
486 Ok(()) => {
487 drop(cmd_buf_data);
488 if let Err(err) = self.device.check_is_valid() {
489 return (
490 ComputePass::new_invalid(Arc::clone(self), &label, err.map_pass_err(scope)),
491 None,
492 );
493 }
494
495 match desc
496 .timestamp_writes
497 .as_ref()
498 .map(|tw| {
499 Self::validate_pass_timestamp_writes::<ComputePassErrorInner>(
500 &self.device,
501 tw,
502 )
503 })
504 .transpose()
505 {
506 Ok(timestamp_writes) => {
507 let arc_desc = ComputePassDescriptor {
508 label,
509 timestamp_writes,
510 };
511 (ComputePass::new(Arc::clone(self), arc_desc), None)
512 }
513 Err(err) => (
514 ComputePass::new_invalid(Arc::clone(self), &label, err.map_pass_err(scope)),
515 None,
516 ),
517 }
518 }
519 Err(err @ SErr::Locked) => {
520 cmd_buf_data.invalidate(err.clone());
524 drop(cmd_buf_data);
525 (
526 ComputePass::new_invalid(Arc::clone(self), &label, err.map_pass_err(scope)),
527 None,
528 )
529 }
530 Err(err @ (SErr::Ended | SErr::Submitted)) => {
531 drop(cmd_buf_data);
534 (
535 ComputePass::new_invalid(
536 Arc::clone(self),
537 &label,
538 err.clone().map_pass_err(scope),
539 ),
540 Some(err.into()),
541 )
542 }
543 Err(err @ SErr::Invalid) => {
544 drop(cmd_buf_data);
550 (
551 ComputePass::new_invalid(Arc::clone(self), &label, err.map_pass_err(scope)),
552 None,
553 )
554 }
555 Err(SErr::Unlocked) => {
556 unreachable!("lock_encoder cannot fail due to the encoder being unlocked")
557 }
558 }
559 }
560
561 pub fn begin_compute_pass(
562 self: &Arc<Self>,
563 desc: &ComputePassDescriptor<'_, PassTimestampWrites<Arc<QuerySet>>>,
564 ) -> ComputePass {
565 let (pass, err) = self.begin_compute_pass_inner(desc);
566 if let Some(err) = err {
567 self.device
568 .handle_error(err, pass.label(), "CommandEncoder::begin_compute_pass");
569 }
570 pass
571 }
572}
573
574impl ComputePass {
576 fn end_inner(&mut self) -> Result<(), EncoderStateError> {
577 profiling::scope!(
578 "CommandEncoder::run_compute_pass {}",
579 self.base.label.as_deref().unwrap_or("")
580 );
581
582 let cmd_enc = self.parent.take().ok_or(EncoderStateError::Ended)?;
583 let mut cmd_buf_data = cmd_enc.data.lock();
584
585 cmd_buf_data.unlock_encoder()?;
586
587 let base = self.base.take();
588
589 if let Err(ComputePassError {
590 inner:
591 ComputePassErrorInner::EncoderState(
592 err @ (EncoderStateError::Locked | EncoderStateError::Ended),
593 ),
594 scope: _,
595 }) = base
596 {
597 return Err(err.clone());
604 }
605
606 cmd_buf_data.push_with(|| -> Result<_, ComputePassError> {
607 Ok(ArcCommand::RunComputePass {
608 pass: base?,
609 timestamp_writes: self.timestamp_writes.take(),
610 })
611 })
612 }
613
614 pub fn end(&mut self) {
615 if let Err(err) = self.end_inner() {
616 self.device
617 .handle_error(err, self.label(), "ComputePass::end");
618 }
619 }
620}
621
622pub(super) fn encode_compute_pass(
623 parent_state: &mut EncodingState<InnerCommandEncoder>,
624 mut base: BasePass<ArcComputeCommand, Infallible>,
625 mut timestamp_writes: Option<PassTimestampWrites>,
626) -> Result<(), ComputePassError> {
627 let pass_scope = PassErrorScope::Pass;
628
629 let device = parent_state.device;
630
631 parent_state
635 .raw_encoder
636 .close_if_open()
637 .map_pass_err(pass_scope)?;
638 let raw_encoder = parent_state
639 .raw_encoder
640 .open_pass(base.label.as_deref())
641 .map_pass_err(pass_scope)?;
642
643 let mut debug_scope_depth = 0;
644
645 let mut state = State {
646 pipeline: None,
647
648 pass: pass::PassState {
649 base: EncodingState {
650 device,
651 raw_encoder,
652 tracker: parent_state.tracker,
653 buffer_memory_init_actions: parent_state.buffer_memory_init_actions,
654 texture_memory_actions: parent_state.texture_memory_actions,
655 as_actions: parent_state.as_actions,
656 temp_resources: parent_state.temp_resources,
657 indirect_draw_validation_resources: parent_state.indirect_draw_validation_resources,
658 snatch_guard: parent_state.snatch_guard,
659 debug_scope_depth: &mut debug_scope_depth,
660 query_set_writes: parent_state.query_set_writes,
661 deferred_query_set_resolves: parent_state.deferred_query_set_resolves,
662 },
663 binder: Binder::new(),
664 temp_offsets: Vec::new(),
665 dynamic_offset_count: 0,
666 pending_discard_init_fixups: SurfacesInDiscardState::new(),
667 scope: device.new_usage_scope(),
668 string_offset: 0,
669 immediate_state: ImmediateState::default(),
670 },
671 active_query: None,
672
673 intermediate_trackers: Tracker::new(
674 device.ordered_buffer_usages,
675 device.ordered_texture_usages,
676 ),
677 };
678
679 let indices = &device.tracker_indices;
680 state
681 .pass
682 .base
683 .tracker
684 .buffers
685 .set_size(indices.buffers.size());
686 state
687 .pass
688 .base
689 .tracker
690 .textures
691 .set_size(indices.textures.size());
692
693 let timestamp_writes: Option<hal::PassTimestampWrites<'_, dyn hal::DynQuerySet>> =
694 if let Some(tw) = timestamp_writes.take() {
695 tw.query_set.same_device(device).map_pass_err(pass_scope)?;
696
697 record_pass_timestamp_writes(&tw, state.pass.base.query_set_writes);
698
699 let query_set = state
700 .pass
701 .base
702 .tracker
703 .query_sets
704 .insert_single(tw.query_set);
705
706 let range = if let (Some(index_a), Some(index_b)) =
709 (tw.beginning_of_pass_write_index, tw.end_of_pass_write_index)
710 {
711 Some(index_a.min(index_b)..index_a.max(index_b) + 1)
712 } else {
713 tw.beginning_of_pass_write_index
714 .or(tw.end_of_pass_write_index)
715 .map(|i| i..i + 1)
716 };
717 let raw_query_set = query_set
718 .try_raw(parent_state.snatch_guard)
719 .map_pass_err(pass_scope)?;
720 if let Some(range) = range {
723 unsafe {
724 state
725 .pass
726 .base
727 .raw_encoder
728 .reset_queries(raw_query_set, range);
729 }
730 }
731
732 Some(hal::PassTimestampWrites {
733 query_set: raw_query_set,
734 beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
735 end_of_pass_write_index: tw.end_of_pass_write_index,
736 })
737 } else {
738 None
739 };
740
741 let hal_desc = hal::ComputePassDescriptor {
742 label: hal_label(base.label.as_deref(), device.instance_flags),
743 timestamp_writes,
744 };
745
746 unsafe {
747 state.pass.base.raw_encoder.begin_compute_pass(&hal_desc);
748 }
749
750 for command in base.commands.drain(..) {
751 match command {
752 ArcComputeCommand::SetBindGroup {
753 index,
754 num_dynamic_offsets,
755 bind_group,
756 } => {
757 let scope = PassErrorScope::SetBindGroup;
758 pass::set_bind_group::<ComputePassErrorInner>(
759 &mut state.pass,
760 device,
761 &base.dynamic_offsets,
762 index,
763 num_dynamic_offsets,
764 bind_group,
765 false,
766 )
767 .map_pass_err(scope)?;
768 }
769 ArcComputeCommand::SetPipeline(pipeline) => {
770 let scope = PassErrorScope::SetPipelineCompute;
771 set_pipeline(&mut state, device, pipeline).map_pass_err(scope)?;
772 }
773 ArcComputeCommand::SetImmediate { offset, data } => {
774 let scope = PassErrorScope::SetImmediate;
775 state
776 .pass
777 .immediate_state
778 .set_immediates::<ComputePassErrorInner>(
779 &state.pass.base.device.limits,
780 offset,
781 &data,
782 )
783 .map_pass_err(scope)?;
784 }
785 ArcComputeCommand::DispatchWorkgroups(groups) => {
786 let scope = PassErrorScope::Dispatch { indirect: false };
787 dispatch_workgroups(&mut state, groups).map_pass_err(scope)?;
788 }
789 ArcComputeCommand::DispatchWorkgroupsIndirect { buffer, offset } => {
790 let scope = PassErrorScope::Dispatch { indirect: true };
791 dispatch_workgroups_indirect(&mut state, device, buffer, offset)
792 .map_pass_err(scope)?;
793 }
794 ArcComputeCommand::PushDebugGroup { color: _, len } => {
795 pass::push_debug_group(&mut state.pass, &base.string_data, len);
796 }
797 ArcComputeCommand::PopDebugGroup => {
798 let scope = PassErrorScope::PopDebugGroup;
799 pass::pop_debug_group::<ComputePassErrorInner>(&mut state.pass)
800 .map_pass_err(scope)?;
801 }
802 ArcComputeCommand::InsertDebugMarker { color: _, len } => {
803 pass::insert_debug_marker(&mut state.pass, &base.string_data, len);
804 }
805 ArcComputeCommand::WriteTimestamp {
806 query_set,
807 query_index,
808 } => {
809 let scope = PassErrorScope::WriteTimestamp;
810 pass::write_timestamp::<ComputePassErrorInner>(
811 &mut state.pass,
812 device,
813 None, query_set,
815 query_index,
816 )
817 .map_pass_err(scope)?;
818 }
819 ArcComputeCommand::BeginPipelineStatisticsQuery {
820 query_set,
821 query_index,
822 } => {
823 let scope = PassErrorScope::BeginPipelineStatisticsQuery;
824 validate_and_begin_pipeline_statistics_query(
825 query_set,
826 state.pass.base.raw_encoder,
827 &mut state.pass.base.tracker.query_sets,
828 device,
829 query_index,
830 None,
831 &mut state.active_query,
832 state.pass.base.snatch_guard,
833 )
834 .map_pass_err(scope)?;
835 }
836 ArcComputeCommand::EndPipelineStatisticsQuery => {
837 let scope = PassErrorScope::EndPipelineStatisticsQuery;
838 end_pipeline_statistics_query(
839 state.pass.base.raw_encoder,
840 &mut state.active_query,
841 state.pass.base.snatch_guard,
842 state.pass.base.query_set_writes,
843 )
844 .map_pass_err(scope)?;
845 }
846 ArcComputeCommand::TransitionResources {
847 buffer_transitions,
848 texture_transitions,
849 } => {
850 let scope = PassErrorScope::TransitionResources;
851 transition_resources(&mut state, buffer_transitions, texture_transitions)
852 .map_pass_err(scope)?;
853 }
854 }
855 }
856
857 if *state.pass.base.debug_scope_depth > 0 {
858 Err(
859 ComputePassErrorInner::DebugGroupError(DebugGroupError::MissingPop)
860 .map_pass_err(pass_scope),
861 )?;
862 }
863
864 unsafe {
865 state.pass.base.raw_encoder.end_compute_pass();
866 }
867
868 let State {
869 pass: pass::PassState {
870 pending_discard_init_fixups,
871 ..
872 },
873 intermediate_trackers,
874 ..
875 } = state;
876
877 parent_state.raw_encoder.close().map_pass_err(pass_scope)?;
879
880 let transit = parent_state
884 .raw_encoder
885 .open_pass(hal_label(
886 Some("(wgpu internal) Pre Pass"),
887 device.instance_flags,
888 ))
889 .map_pass_err(pass_scope)?;
890 fixup_discarded_surfaces(
893 pending_discard_init_fixups.into_iter(),
894 transit,
895 &mut parent_state.tracker.textures,
896 device,
897 parent_state.snatch_guard,
898 );
899 CommandEncoder::insert_barriers_from_tracker(
900 transit,
901 parent_state.tracker,
902 &intermediate_trackers,
903 parent_state.snatch_guard,
904 );
905 parent_state
907 .raw_encoder
908 .close_and_swap()
909 .map_pass_err(pass_scope)?;
910
911 Ok(())
912}
913
914fn set_pipeline(
915 state: &mut State,
916 device: &Arc<Device>,
917 pipeline: Arc<ComputePipeline>,
918) -> Result<(), ComputePassErrorInner> {
919 pipeline.same_device(device)?;
920
921 state.pipeline = Some(pipeline.clone());
922
923 let pipeline = state
924 .pass
925 .base
926 .tracker
927 .compute_pipelines
928 .insert_single(pipeline)
929 .clone();
930
931 unsafe {
932 state
933 .pass
934 .base
935 .raw_encoder
936 .set_compute_pipeline(pipeline.raw()?);
937 }
938
939 let pipeline_layout = pipeline.layout()?;
941 pass::change_pipeline_layout::<ComputePassErrorInner>(
942 &mut state.pass,
943 pipeline_layout,
944 &pipeline.late_sized_buffer_groups,
945 )
946}
947
948fn dispatch_workgroups(state: &mut State, groups: [u32; 3]) -> Result<(), ComputePassErrorInner> {
949 api_log!("ComputePass::dispatch {groups:?}");
950
951 state.is_ready()?;
952
953 state.flush_bindings(None, false)?;
954 state.flush_immediates();
955
956 let groups_size_limit = state
957 .pass
958 .base
959 .device
960 .limits
961 .max_compute_workgroups_per_dimension;
962
963 if groups.iter().copied().any(|g| g > groups_size_limit) {
964 return Err(ComputePassErrorInner::Dispatch(
965 DispatchError::InvalidGroupSize {
966 current: groups,
967 limit: groups_size_limit,
968 },
969 ));
970 }
971
972 unsafe {
973 state.pass.base.raw_encoder.dispatch_workgroups(groups);
974 }
975 Ok(())
976}
977
978fn dispatch_workgroups_indirect(
979 state: &mut State,
980 device: &Arc<Device>,
981 buffer: Arc<Buffer>,
982 offset: u64,
983) -> Result<(), ComputePassErrorInner> {
984 api_log!("ComputePass::dispatch_indirect");
985
986 buffer.same_device(device)?;
987
988 state.is_ready()?;
989
990 state
991 .pass
992 .base
993 .device
994 .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
995
996 buffer.check_usage(wgt::BufferUsages::INDIRECT)?;
997
998 if !offset.is_multiple_of(4) {
999 return Err(ComputePassErrorInner::UnalignedIndirectBufferOffset(offset));
1000 }
1001
1002 let args_size = size_of::<wgt::DispatchIndirectArgs>() as u64;
1003 if buffer.size < args_size || buffer.size - args_size < offset {
1004 return Err(ComputePassErrorInner::IndirectBufferOverrun {
1005 args_size,
1006 offset,
1007 buffer_size: buffer.size,
1008 });
1009 }
1010
1011 buffer.check_destroyed(state.pass.base.snatch_guard)?;
1012
1013 let stride = 3 * 4; state.pass.base.buffer_memory_init_actions.extend(
1015 buffer.initialization_status.read().create_action(
1016 &buffer,
1017 offset..(offset + stride),
1018 MemoryInitKind::NeedsInitializedMemory,
1019 ),
1020 );
1021
1022 if let Some(ref indirect_validation) = state.pass.base.device.indirect_validation {
1023 let params = indirect_validation.dispatch.params(
1024 &state.pass.base.device.limits,
1025 offset,
1026 buffer.size,
1027 );
1028
1029 unsafe {
1030 state
1031 .pass
1032 .base
1033 .raw_encoder
1034 .set_compute_pipeline(params.pipeline);
1035 }
1036
1037 unsafe {
1038 state.pass.base.raw_encoder.set_immediates(
1039 params.pipeline_layout,
1040 0,
1041 &[params.offset_remainder as u32 / 4],
1042 );
1043 }
1044
1045 unsafe {
1046 state.pass.base.raw_encoder.set_bind_group(
1047 params.pipeline_layout,
1048 0,
1049 params.dst_bind_group,
1050 &[],
1051 );
1052 }
1053 unsafe {
1054 state.pass.base.raw_encoder.set_bind_group(
1055 params.pipeline_layout,
1056 1,
1057 buffer
1058 .indirect_validation_bind_groups
1059 .get(state.pass.base.snatch_guard)
1060 .unwrap()
1061 .dispatch
1062 .as_ref(),
1063 &[params.aligned_offset as u32],
1064 );
1065 }
1066
1067 let src_transition = state
1068 .intermediate_trackers
1069 .buffers
1070 .set_single(&buffer, wgt::BufferUses::STORAGE_READ_ONLY);
1071 let src_barrier = src_transition
1072 .map(|transition| transition.into_hal(&buffer, state.pass.base.snatch_guard));
1073 unsafe {
1074 state
1075 .pass
1076 .base
1077 .raw_encoder
1078 .transition_buffers(src_barrier.as_slice());
1079 }
1080
1081 unsafe {
1082 state
1083 .pass
1084 .base
1085 .raw_encoder
1086 .transition_buffers(&[hal::BufferBarrier {
1087 buffer: params.dst_buffer,
1088 usage: hal::StateTransition {
1089 from: wgt::BufferUses::INDIRECT,
1090 to: wgt::BufferUses::STORAGE_READ_WRITE,
1091 },
1092 }]);
1093 }
1094
1095 unsafe {
1096 state.pass.base.raw_encoder.dispatch_workgroups([1, 1, 1]);
1097 }
1098
1099 {
1101 unsafe {
1102 state
1103 .pass
1104 .base
1105 .raw_encoder
1106 .set_compute_pipeline(state.pipeline.as_ref().unwrap().raw()?);
1107 }
1108
1109 state.pass.immediate_state.immediates_dirty = true;
1111 state.flush_immediates();
1112
1113 for (i, group, dynamic_offsets) in state.pass.binder.list_valid() {
1114 let raw_bg = group.try_raw(state.pass.base.snatch_guard)?;
1115 unsafe {
1116 state.pass.base.raw_encoder.set_bind_group(
1117 state.pipeline.as_ref().unwrap().layout()?.raw()?,
1118 i as u32,
1119 raw_bg,
1120 dynamic_offsets,
1121 );
1122 }
1123 }
1124 }
1125
1126 unsafe {
1127 state
1128 .pass
1129 .base
1130 .raw_encoder
1131 .transition_buffers(&[hal::BufferBarrier {
1132 buffer: params.dst_buffer,
1133 usage: hal::StateTransition {
1134 from: wgt::BufferUses::STORAGE_READ_WRITE,
1135 to: wgt::BufferUses::INDIRECT,
1136 },
1137 }]);
1138 }
1139
1140 state.flush_bindings(Some(&buffer), false)?;
1141 unsafe {
1142 state
1143 .pass
1144 .base
1145 .raw_encoder
1146 .dispatch_workgroups_indirect(params.dst_buffer, 0);
1147 }
1148 } else {
1149 state.flush_bindings(Some(&buffer), true)?;
1150 state.flush_immediates();
1151 let buf_raw = buffer.try_raw(state.pass.base.snatch_guard)?;
1152 unsafe {
1153 state
1154 .pass
1155 .base
1156 .raw_encoder
1157 .dispatch_workgroups_indirect(buf_raw, offset);
1158 }
1159 }
1160
1161 Ok(())
1162}
1163
1164impl ComputePass {
1177 fn set_bind_group_inner(
1178 &mut self,
1179 index: u32,
1180 bind_group: Option<Arc<BindGroup>>,
1181 offsets: &[DynamicOffset],
1182 ) -> Result<(), PassStateError> {
1183 let scope = PassErrorScope::SetBindGroup;
1184
1185 let base = pass_base!(self, scope);
1189
1190 if self.current_bind_groups.set_and_check_redundant(
1191 &bind_group,
1192 index,
1193 &mut base.dynamic_offsets,
1194 offsets,
1195 ) {
1196 return Ok(());
1197 }
1198
1199 let bind_group = if let Some(bind_group) = bind_group {
1200 pass_try!(base, scope, bind_group.check_is_valid());
1201 Some(bind_group)
1202 } else {
1203 None
1204 };
1205
1206 base.commands.push(ArcComputeCommand::SetBindGroup {
1207 index,
1208 num_dynamic_offsets: offsets.len(),
1209 bind_group,
1210 });
1211
1212 Ok(())
1213 }
1214
1215 pub fn set_bind_group(
1216 &mut self,
1217 index: u32,
1218 bind_group: Option<Arc<BindGroup>>,
1219 offsets: &[DynamicOffset],
1220 ) {
1221 if let Err(error) = self.set_bind_group_inner(index, bind_group, offsets) {
1222 self.device
1223 .handle_error(error, self.label(), "ComputePass::set_bind_group");
1224 }
1225 }
1226
1227 fn set_pipeline_inner(
1228 &mut self,
1229 compute_pipeline: Arc<ComputePipeline>,
1230 ) -> Result<(), PassStateError> {
1231 let redundant = self
1232 .current_pipeline
1233 .set_and_check_redundant(&compute_pipeline);
1234
1235 let scope = PassErrorScope::SetPipelineCompute;
1236
1237 let base = pass_base!(self, scope);
1240
1241 if redundant {
1242 return Ok(());
1243 }
1244
1245 pass_try!(base, scope, compute_pipeline.check_valid());
1246
1247 base.commands
1248 .push(ArcComputeCommand::SetPipeline(compute_pipeline));
1249
1250 Ok(())
1251 }
1252
1253 pub fn set_pipeline(&mut self, compute_pipeline: Arc<ComputePipeline>) {
1254 if let Err(error) = self.set_pipeline_inner(compute_pipeline) {
1255 self.device
1256 .handle_error(error, self.label(), "ComputePass::set_pipeline");
1257 }
1258 }
1259
1260 fn set_immediates_inner(&mut self, offset: u32, data: &[u8]) -> Result<(), PassStateError> {
1261 let scope = PassErrorScope::SetImmediate;
1262 let base = pass_base!(self, scope);
1263
1264 pass_try!(
1265 base,
1266 scope,
1267 pass::validate_immediates_alignment(offset, data.len())
1268 );
1269
1270 base.commands.push(ArcComputeCommand::SetImmediate {
1271 offset,
1272 data: data
1273 .chunks_exact(size_of::<u32>())
1274 .map(|ck| u32::from_le_bytes(ck.try_into().unwrap()))
1275 .collect(),
1276 });
1277
1278 Ok(())
1279 }
1280
1281 pub fn set_immediates(&mut self, offset: u32, data: &[u8]) {
1282 if let Err(error) = self.set_immediates_inner(offset, data) {
1283 self.device
1284 .handle_error(error, self.label(), "ComputePass::set_immediates");
1285 }
1286 }
1287
1288 fn dispatch_workgroups_inner(
1289 &mut self,
1290 groups_x: u32,
1291 groups_y: u32,
1292 groups_z: u32,
1293 ) -> Result<(), PassStateError> {
1294 let scope = PassErrorScope::Dispatch { indirect: false };
1295
1296 pass_base!(self, scope)
1297 .commands
1298 .push(ArcComputeCommand::DispatchWorkgroups([
1299 groups_x, groups_y, groups_z,
1300 ]));
1301
1302 Ok(())
1303 }
1304
1305 pub fn dispatch_workgroups(&mut self, groups_x: u32, groups_y: u32, groups_z: u32) {
1306 if let Err(error) = self.dispatch_workgroups_inner(groups_x, groups_y, groups_z) {
1307 self.device
1308 .handle_error(error, self.label(), "ComputePass::dispatch_workgroups");
1309 }
1310 }
1311
1312 fn dispatch_workgroups_indirect_inner(
1313 &mut self,
1314 buffer: Arc<Buffer>,
1315 offset: BufferAddress,
1316 ) -> Result<(), PassStateError> {
1317 let scope = PassErrorScope::Dispatch { indirect: true };
1318 let base = pass_base!(self, scope);
1319
1320 pass_try!(base, scope, buffer.check_is_valid());
1321
1322 base.commands
1323 .push(ArcComputeCommand::DispatchWorkgroupsIndirect { buffer, offset });
1324
1325 Ok(())
1326 }
1327
1328 pub fn dispatch_workgroups_indirect(&mut self, buffer: Arc<Buffer>, offset: BufferAddress) {
1329 if let Err(error) = self.dispatch_workgroups_indirect_inner(buffer, offset) {
1330 self.device.handle_error(
1331 error,
1332 self.label(),
1333 "ComputePass::dispatch_workgroups_indirect",
1334 );
1335 }
1336 }
1337
1338 fn push_debug_group_inner(&mut self, label: &str, color: u32) -> Result<(), PassStateError> {
1339 let base = pass_base!(self, PassErrorScope::PushDebugGroup);
1340
1341 let bytes = label.as_bytes();
1342 base.string_data.extend_from_slice(bytes);
1343
1344 base.commands.push(ArcComputeCommand::PushDebugGroup {
1345 color,
1346 len: bytes.len(),
1347 });
1348
1349 Ok(())
1350 }
1351
1352 pub fn push_debug_group(&mut self, label: &str, color: u32) {
1353 if let Err(error) = self.push_debug_group_inner(label, color) {
1354 self.device
1355 .handle_error(error, self.label(), "ComputePass::push_debug_group");
1356 }
1357 }
1358
1359 fn pop_debug_group_inner(&mut self) -> Result<(), PassStateError> {
1360 let base = pass_base!(self, PassErrorScope::PopDebugGroup);
1361
1362 base.commands.push(ArcComputeCommand::PopDebugGroup);
1363
1364 Ok(())
1365 }
1366
1367 pub fn pop_debug_group(&mut self) {
1368 if let Err(error) = self.pop_debug_group_inner() {
1369 self.device
1370 .handle_error(error, self.label(), "ComputePass::pop_debug_group");
1371 }
1372 }
1373
1374 fn insert_debug_marker_inner(&mut self, label: &str, color: u32) -> Result<(), PassStateError> {
1375 let base = pass_base!(self, PassErrorScope::InsertDebugMarker);
1376
1377 let bytes = label.as_bytes();
1378 base.string_data.extend_from_slice(bytes);
1379
1380 base.commands.push(ArcComputeCommand::InsertDebugMarker {
1381 color,
1382 len: bytes.len(),
1383 });
1384
1385 Ok(())
1386 }
1387
1388 pub fn insert_debug_marker(&mut self, label: &str, color: u32) {
1389 if let Err(error) = self.insert_debug_marker_inner(label, color) {
1390 self.device
1391 .handle_error(error, self.label(), "ComputePass::insert_debug_marker");
1392 }
1393 }
1394
1395 fn write_timestamp_inner(
1396 &mut self,
1397 query_set: Arc<QuerySet>,
1398 query_index: u32,
1399 ) -> Result<(), PassStateError> {
1400 let scope = PassErrorScope::WriteTimestamp;
1401 let base = pass_base!(self, scope);
1402
1403 pass_try!(base, scope, query_set.check_is_valid());
1404
1405 base.commands.push(ArcComputeCommand::WriteTimestamp {
1406 query_set,
1407 query_index,
1408 });
1409
1410 Ok(())
1411 }
1412
1413 pub fn write_timestamp(&mut self, query_set: Arc<QuerySet>, query_index: u32) {
1414 if let Err(error) = self.write_timestamp_inner(query_set, query_index) {
1415 self.device
1416 .handle_error(error, self.label(), "ComputePass::write_timestamp");
1417 }
1418 }
1419
1420 fn begin_pipeline_statistics_query_inner(
1421 &mut self,
1422 query_set: Arc<QuerySet>,
1423 query_index: u32,
1424 ) -> Result<(), PassStateError> {
1425 let scope = PassErrorScope::BeginPipelineStatisticsQuery;
1426 let base = pass_base!(self, scope);
1427
1428 pass_try!(base, scope, query_set.check_is_valid());
1429
1430 base.commands
1431 .push(ArcComputeCommand::BeginPipelineStatisticsQuery {
1432 query_set,
1433 query_index,
1434 });
1435
1436 Ok(())
1437 }
1438
1439 pub fn begin_pipeline_statistics_query(&mut self, query_set: Arc<QuerySet>, query_index: u32) {
1440 if let Err(error) = self.begin_pipeline_statistics_query_inner(query_set, query_index) {
1441 self.device.handle_error(
1442 error,
1443 self.label(),
1444 "ComputePass::begin_pipeline_statistics_query",
1445 );
1446 }
1447 }
1448
1449 fn end_pipeline_statistics_query_inner(&mut self) -> Result<(), PassStateError> {
1450 pass_base!(self, PassErrorScope::EndPipelineStatisticsQuery)
1451 .commands
1452 .push(ArcComputeCommand::EndPipelineStatisticsQuery);
1453
1454 Ok(())
1455 }
1456
1457 pub fn end_pipeline_statistics_query(&mut self) {
1458 if let Err(error) = self.end_pipeline_statistics_query_inner() {
1459 self.device.handle_error(
1460 error,
1461 self.label(),
1462 "ComputePass::end_pipeline_statistics_query",
1463 );
1464 }
1465 }
1466
1467 fn transition_resources_inner(
1468 &mut self,
1469 buffer_transitions: impl Iterator<Item = wgt::BufferTransition<Arc<Buffer>>>,
1470 texture_transitions: impl Iterator<Item = wgt::TextureTransition<Arc<TextureView>>>,
1471 ) -> Result<(), PassStateError> {
1472 let scope = PassErrorScope::TransitionResources;
1473 let base = pass_base!(self, scope);
1474
1475 let buffer_transitions = pass_try!(
1476 base,
1477 scope,
1478 buffer_transitions
1479 .map(|buffer_transition| -> Result<_, InvalidResourceError> {
1480 let buffer = buffer_transition.buffer;
1481 buffer.check_is_valid()?;
1482 Ok(wgt::BufferTransition {
1483 buffer,
1484 state: buffer_transition.state,
1485 })
1486 })
1487 .collect::<Result<Vec<_>, _>>()
1488 );
1489
1490 let texture_transitions = pass_try!(
1491 base,
1492 scope,
1493 texture_transitions
1494 .map(|texture_transition| -> Result<_, InvalidResourceError> {
1495 let texture_view = texture_transition.texture;
1496 texture_view.check_valid()?;
1497 Ok(wgt::TextureTransition {
1498 texture: texture_view,
1499 selector: texture_transition.selector,
1500 state: texture_transition.state,
1501 })
1502 })
1503 .collect::<Result<Vec<_>, _>>()
1504 );
1505
1506 base.commands.push(ArcComputeCommand::TransitionResources {
1507 buffer_transitions,
1508 texture_transitions,
1509 });
1510
1511 Ok(())
1512 }
1513
1514 pub fn transition_resources(
1515 &mut self,
1516 buffer_transitions: impl Iterator<Item = wgt::BufferTransition<Arc<Buffer>>>,
1517 texture_transitions: impl Iterator<Item = wgt::TextureTransition<Arc<TextureView>>>,
1518 ) {
1519 if let Err(error) = self.transition_resources_inner(buffer_transitions, texture_transitions)
1520 {
1521 self.device
1522 .handle_error(error, self.label(), "ComputePass::transition_resources");
1523 }
1524 }
1525}