@@ -7,7 +7,10 @@ use wgt::{
77use alloc:: { borrow:: Cow , boxed:: Box , sync:: Arc , vec:: Vec } ;
88use core:: { convert:: Infallible , fmt, str} ;
99
10- use crate :: { api_log, binding_model:: BindError , resource:: RawResourceAccess } ;
10+ use crate :: {
11+ api_log, binding_model:: BindError , command:: pass:: flush_bindings_helper,
12+ resource:: RawResourceAccess ,
13+ } ;
1114use crate :: {
1215 binding_model:: { LateMinBufferBindingSizeMismatch , PushConstantUploadError } ,
1316 command:: {
@@ -280,27 +283,68 @@ impl<'scope, 'snatch_guard, 'cmd_enc> State<'scope, 'snatch_guard, 'cmd_enc> {
280283 }
281284 }
282285
283- // `extra_buffer` is there to represent the indirect buffer that is also
284- // part of the usage scope.
285- fn flush_states (
286+ /// Flush binding state in preparation for a dispatch.
287+ ///
288+ /// # Differences between render and compute passes
289+ ///
290+ /// There are differences between the `flush_bindings` implementations for
291+ /// render and compute passes, because render passes have a single usage
292+ /// scope for the entire pass, and compute passes have a separate usage
293+ /// scope for each dispatch.
294+ ///
295+ /// For compute passes, bind groups are merged into a fresh usage scope
296+ /// here, not into the pass usage scope within calls to `set_bind_group`. As
297+ /// specified by WebGPU, for compute passes, we merge only the bind groups
298+ /// that are actually used by the pipeline, unlike render passes, which
299+ /// merge every bind group that is ever set, even if it is not ultimately
300+ /// used by the pipeline.
301+ ///
302+ /// For compute passes, we call `drain_barriers` here, because barriers may
303+ /// be needed before each dispatch if a previous dispatch had a conflicting
304+ /// usage. For render passes, barriers are emitted once at the start of the
305+ /// render pass.
306+ ///
307+ /// # Indirect buffer handling
308+ ///
309+ /// For indirect dispatches without validation, pass both `indirect_buffer`
310+ /// and `indirect_buffer_index_if_not_validating`. The indirect buffer will
311+ /// be added to the usage scope and the tracker.
312+ ///
313+ /// For indirect dispatches with validation, pass only `indirect_buffer`.
314+ /// The indirect buffer will be added to the usage scope to detect usage
315+ /// conflicts. The indirect buffer does not need to be added to the tracker;
316+ /// the indirect validation code handles transitions manually.
317+ fn flush_bindings (
286318 & mut self ,
287- indirect_buffer : Option < TrackerIndex > ,
288- ) -> Result < ( ) , ResourceUsageCompatibilityError > {
319+ indirect_buffer : Option < & Arc < Buffer > > ,
320+ indirect_buffer_index_if_not_validating : Option < TrackerIndex > ,
321+ ) -> Result < ( ) , ComputePassErrorInner > {
322+ let mut scope = self . pass . base . device . new_usage_scope ( ) ;
323+
289324 for bind_group in self . pass . binder . list_active ( ) {
290- unsafe { self . pass . scope . merge_bind_group ( & bind_group. used ) ? } ;
291- // Note: stateless trackers are not merged: the lifetime reference
292- // is held to the bind group itself.
325+ unsafe { scope. merge_bind_group ( & bind_group. used ) ? } ;
293326 }
294327
295- for bind_group in self . pass . binder . list_active ( ) {
296- self . intermediate_trackers
297- . set_from_bind_group ( & mut self . pass . scope , & bind_group. used ) ;
328+ // When indirect validation is turned on, our actual use of the buffer
329+ // is `STORAGE_READ_ONLY`, but for usage scope validation, we still want
330+ // to treat it as indirect so we can detect the conflicts prescribed by
331+ // WebGPU. The usage scope we construct here never leaves this function
332+ // (and is not used to populate a tracker), so it's fine to do this.
333+ if let Some ( buffer) = indirect_buffer {
334+ scope
335+ . buffers
336+ . merge_single ( buffer, wgt:: BufferUses :: INDIRECT ) ?;
298337 }
299338
300- // Add the state of the indirect buffer if it hasn't been hit before .
339+ // Add the state of the indirect buffer, if needed (see above) .
301340 self . intermediate_trackers
302341 . buffers
303- . set_multiple ( & mut self . pass . scope . buffers , indirect_buffer) ;
342+ . set_multiple ( & mut scope. buffers , indirect_buffer_index_if_not_validating) ;
343+
344+ flush_bindings_helper ( & mut self . pass , |bind_group| {
345+ self . intermediate_trackers
346+ . set_from_bind_group ( & mut scope, & bind_group. used )
347+ } ) ?;
304348
305349 CommandEncoder :: drain_barriers (
306350 self . pass . base . raw_encoder ,
@@ -821,7 +865,7 @@ fn set_pipeline(
821865 }
822866
823867 // Rebind resources
824- pass:: rebind_resources :: < ComputePassErrorInner , _ > (
868+ pass:: change_pipeline_layout :: < ComputePassErrorInner , _ > (
825869 & mut state. pass ,
826870 & pipeline. layout ,
827871 & pipeline. late_sized_buffer_groups ,
@@ -850,7 +894,7 @@ fn dispatch(state: &mut State, groups: [u32; 3]) -> Result<(), ComputePassErrorI
850894
851895 state. is_ready ( ) ?;
852896
853- state. flush_states ( None ) ?;
897+ state. flush_bindings ( None , None ) ?;
854898
855899 let groups_size_limit = state
856900 . pass
@@ -1051,7 +1095,7 @@ fn dispatch_indirect(
10511095 } ] ) ;
10521096 }
10531097
1054- state. flush_states ( None ) ?;
1098+ state. flush_bindings ( Some ( & buffer ) , None ) ?;
10551099 unsafe {
10561100 state
10571101 . pass
@@ -1060,14 +1104,8 @@ fn dispatch_indirect(
10601104 . dispatch_indirect ( params. dst_buffer , 0 ) ;
10611105 }
10621106 } else {
1063- state
1064- . pass
1065- . scope
1066- . buffers
1067- . merge_single ( & buffer, wgt:: BufferUses :: INDIRECT ) ?;
1068-
10691107 use crate :: resource:: Trackable ;
1070- state. flush_states ( Some ( buffer. tracker_index ( ) ) ) ?;
1108+ state. flush_bindings ( Some ( & buffer ) , Some ( buffer. tracker_index ( ) ) ) ?;
10711109
10721110 let buf_raw = buffer. try_raw ( state. pass . base . snatch_guard ) ?;
10731111 unsafe {
0 commit comments