Skip to content

Commit 2ac1fae

Browse files
authored
feat(core): Add ComputePass::transition_resources (#9371)
1 parent 8bc9fc1 commit 2ac1fae

12 files changed

Lines changed: 312 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Bottom level categories:
5151
- Fix missing dependency feature activations when building wgpu-hal with gles/dx12 in isolation. By @wumpf in [#9325](https://github.com/gfx-rs/wgpu/pull/9325)
5252
- Make `wgpu_types::texture::format::TextureChannel` accessible as `wgpu::TextureChannel`. By @TornaxO7 in [#9394](https://github.com/gfx-rs/wgpu/pull/9349).
5353
- Add support for `per_vertex` in Metal and DX12, as well as some validation for `per_vertex`, and a new enable extension, `wgpu_per_vertex`. By @inner-daemons in [#9219](https://github.com/gfx-rs/wgpu/pull/9219).
54+
- Add `ComputePass` version of `CommandEncoder::transition_resources` that allows intra-pass transitions. By @wingertge in [#9371](https://github.com/gfx-rs/wgpu/pull/9371).
5455

5556
#### Metal
5657

player/src/lib.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,6 +1079,26 @@ impl Player {
10791079
query_index,
10801080
},
10811081
C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery,
1082+
C::TransitionResources {
1083+
buffer_transitions,
1084+
texture_transitions,
1085+
} => C::TransitionResources {
1086+
buffer_transitions: buffer_transitions
1087+
.into_iter()
1088+
.map(|buffer_transition| wgt::BufferTransition {
1089+
buffer: self.resolve_buffer_id(buffer_transition.buffer),
1090+
state: buffer_transition.state,
1091+
})
1092+
.collect(),
1093+
texture_transitions: texture_transitions
1094+
.into_iter()
1095+
.map(|texture_transition| wgt::TextureTransition {
1096+
texture: self.resolve_texture_view_id(texture_transition.texture),
1097+
selector: texture_transition.selector,
1098+
state: texture_transition.state,
1099+
})
1100+
.collect(),
1101+
},
10821102
}
10831103
}
10841104

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters};
2+
3+
pub fn all_tests(vec: &mut Vec<GpuTestInitializer>) {
4+
vec.push(COMPUTE_PASS_TRANSITION_RESOURCES);
5+
}
6+
7+
#[gpu_test]
8+
static COMPUTE_PASS_TRANSITION_RESOURCES: GpuTestConfiguration = GpuTestConfiguration::new()
9+
.parameters(TestParameters::default().enable_noop())
10+
.run_sync(|ctx| {
11+
let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor {
12+
label: None,
13+
size: 128,
14+
usage: wgpu::BufferUsages::STORAGE,
15+
mapped_at_creation: false,
16+
});
17+
18+
let mut encoder = ctx
19+
.device
20+
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
21+
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
22+
label: None,
23+
timestamp_writes: None,
24+
});
25+
26+
pass.transition_resources(
27+
[wgpu::BufferTransition {
28+
buffer: &buffer,
29+
state: wgpu::BufferUses::STORAGE_READ_WRITE,
30+
}]
31+
.into_iter(),
32+
core::iter::empty(),
33+
);
34+
35+
drop(pass);
36+
37+
ctx.queue.submit([encoder.finish()]);
38+
});

tests/tests/wgpu-gpu/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ mod clear_texture;
2323
mod clip_distances;
2424
mod cloneable_types;
2525
mod compute_pass_ownership;
26+
mod compute_pass_transition_resources;
2627
mod create_surface_error;
2728
mod device;
2829
mod dispatch_workgroups_indirect;
@@ -155,6 +156,7 @@ fn all_tests() -> Vec<wgpu_test::GpuTestInitializer> {
155156
transfer::all_tests(&mut tests);
156157
transient::all_tests(&mut tests);
157158
transition_resources::all_tests(&mut tests);
159+
compute_pass_transition_resources::all_tests(&mut tests);
158160
vertex_formats::all_tests(&mut tests);
159161
vertex_indices::all_tests(&mut tests);
160162
vertex_state::all_tests(&mut tests);

wgpu-core/src/command/compute.rs

Lines changed: 123 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use crate::{
2121
ArcCommand, ArcPassTimestampWrites, BasePass, BindGroupStateChange, CommandEncoder,
2222
CommandEncoderError, DebugGroupError, EncoderStateError, InnerCommandEncoder, MapPassErr,
2323
PassErrorScope, PassStateError, PassTimestampWrites, QueryUseError, StateChange,
24-
TimestampWritesError,
24+
TimestampWritesError, TransitionResourcesError,
2525
},
2626
device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures},
2727
global::Global,
@@ -30,9 +30,9 @@ use crate::{
3030
pipeline::ComputePipeline,
3131
resource::{
3232
self, Buffer, DestroyedResourceError, InvalidResourceError, Labeled,
33-
MissingBufferUsageError, ParentDevice, RawResourceAccess, Trackable,
33+
MissingBufferUsageError, ParentDevice, RawResourceAccess, TextureView, Trackable,
3434
},
35-
track::{ResourceUsageCompatibilityError, Tracker},
35+
track::{ResourceUsageCompatibilityError, TextureViewBindGroupState, Tracker},
3636
Label,
3737
};
3838

@@ -179,6 +179,8 @@ pub enum ComputePassErrorInner {
179179
#[error(transparent)]
180180
QueryUse(#[from] QueryUseError),
181181
#[error(transparent)]
182+
TransitionResources(#[from] TransitionResourcesError),
183+
#[error(transparent)]
182184
MissingFeatures(#[from] MissingFeatures),
183185
#[error(transparent)]
184186
MissingDownlevelFlags(#[from] MissingDownlevelFlags),
@@ -235,6 +237,7 @@ impl WebGpuError for ComputePassError {
235237
ComputePassErrorInner::Bind(e) => e.webgpu_error_type(),
236238
ComputePassErrorInner::ImmediateData(e) => e.webgpu_error_type(),
237239
ComputePassErrorInner::QueryUse(e) => e.webgpu_error_type(),
240+
ComputePassErrorInner::TransitionResources(e) => e.webgpu_error_type(),
238241
ComputePassErrorInner::MissingFeatures(e) => e.webgpu_error_type(),
239242
ComputePassErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),
240243
ComputePassErrorInner::InvalidResource(e) => e.webgpu_error_type(),
@@ -368,6 +371,70 @@ impl<'scope, 'snatch_guard, 'cmd_enc> State<'scope, 'snatch_guard, 'cmd_enc> {
368371
}
369372
}
370373

374+
/// Compute pass version of [`command::transition_resources`](crate::command::transition_resources).
375+
/// See also `State::flush_bindings` for details on the implementation.
376+
fn transition_resources(
377+
state: &mut State,
378+
buffer_transitions: Vec<wgt::BufferTransition<Arc<Buffer>>>,
379+
texture_transitions: Vec<wgt::TextureTransition<Arc<TextureView>>>,
380+
) -> Result<(), TransitionResourcesError> {
381+
let indices = &state.pass.base.device.tracker_indices;
382+
state.pass.scope.buffers.set_size(indices.buffers.size());
383+
state.pass.scope.textures.set_size(indices.textures.size());
384+
385+
let mut buffer_ids = Vec::with_capacity(buffer_transitions.len());
386+
let mut textures = TextureViewBindGroupState::new();
387+
388+
// Process buffer transitions
389+
for buffer_transition in buffer_transitions {
390+
buffer_transition
391+
.buffer
392+
.same_device(state.pass.base.device)?;
393+
394+
state
395+
.pass
396+
.scope
397+
.buffers
398+
.merge_single(&buffer_transition.buffer, buffer_transition.state)?;
399+
buffer_ids.push(buffer_transition.buffer.tracker_index());
400+
}
401+
402+
state
403+
.intermediate_trackers
404+
.buffers
405+
.set_and_remove_from_usage_scope_sparse(&mut state.pass.scope.buffers, buffer_ids);
406+
407+
// Process texture transitions
408+
for texture_transition in texture_transitions {
409+
texture_transition
410+
.texture
411+
.same_device(state.pass.base.device)?;
412+
413+
unsafe {
414+
state.pass.scope.textures.merge_single(
415+
&texture_transition.texture.parent,
416+
texture_transition.selector,
417+
texture_transition.state,
418+
)
419+
}?;
420+
421+
textures.insert_single(texture_transition.texture, texture_transition.state);
422+
}
423+
424+
state
425+
.intermediate_trackers
426+
.textures
427+
.set_and_remove_from_usage_scope_sparse(&mut state.pass.scope.textures, &textures);
428+
429+
// Record any needed barriers based on tracker data
430+
CommandEncoder::drain_barriers(
431+
state.pass.base.raw_encoder,
432+
&mut state.intermediate_trackers,
433+
state.pass.base.snatch_guard,
434+
);
435+
Ok(())
436+
}
437+
371438
// Running the compute pass.
372439

373440
impl Global {
@@ -729,6 +796,14 @@ pub(super) fn encode_compute_pass(
729796
end_pipeline_statistics_query(state.pass.base.raw_encoder, &mut state.active_query)
730797
.map_pass_err(scope)?;
731798
}
799+
ArcComputeCommand::TransitionResources {
800+
buffer_transitions,
801+
texture_transitions,
802+
} => {
803+
let scope = PassErrorScope::TransitionResources;
804+
transition_resources(&mut state, buffer_transitions, texture_transitions)
805+
.map_pass_err(scope)?;
806+
}
732807
}
733808
}
734809

@@ -1316,4 +1391,49 @@ impl Global {
13161391

13171392
Ok(())
13181393
}
1394+
1395+
pub fn compute_pass_transition_resources(
1396+
&self,
1397+
pass: &mut ComputePass,
1398+
buffer_transitions: impl Iterator<Item = wgt::BufferTransition<id::BufferId>>,
1399+
texture_transitions: impl Iterator<Item = wgt::TextureTransition<id::TextureViewId>>,
1400+
) -> Result<(), PassStateError> {
1401+
let scope = PassErrorScope::TransitionResources;
1402+
let base = pass_base!(pass, scope);
1403+
1404+
let hub = &self.hub;
1405+
let buffer_transitions = pass_try!(
1406+
base,
1407+
scope,
1408+
buffer_transitions
1409+
.map(|buffer_transition| -> Result<_, InvalidResourceError> {
1410+
Ok(wgt::BufferTransition {
1411+
buffer: hub.buffers.get(buffer_transition.buffer).get()?,
1412+
state: buffer_transition.state,
1413+
})
1414+
})
1415+
.collect::<Result<Vec<_>, _>>()
1416+
);
1417+
1418+
let texture_transitions = pass_try!(
1419+
base,
1420+
scope,
1421+
texture_transitions
1422+
.map(|texture_transition| -> Result<_, InvalidResourceError> {
1423+
Ok(wgt::TextureTransition {
1424+
texture: hub.texture_views.get(texture_transition.texture).get()?,
1425+
selector: texture_transition.selector,
1426+
state: texture_transition.state,
1427+
})
1428+
})
1429+
.collect::<Result<Vec<_>, _>>()
1430+
);
1431+
1432+
base.commands.push(ArcComputeCommand::TransitionResources {
1433+
buffer_transitions,
1434+
texture_transitions,
1435+
});
1436+
1437+
Ok(())
1438+
}
13191439
}

wgpu-core/src/command/compute_command.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use alloc::vec::Vec;
2+
13
#[cfg(feature = "serde")]
24
use crate::command::serde_object_reference_struct;
35
use crate::command::{ArcReferences, ReferenceType};
@@ -64,6 +66,11 @@ pub enum ComputeCommand<R: ReferenceType> {
6466
},
6567

6668
EndPipelineStatisticsQuery,
69+
70+
TransitionResources {
71+
buffer_transitions: Vec<wgt::BufferTransition<R::Buffer>>,
72+
texture_transitions: Vec<wgt::TextureTransition<R::TextureView>>,
73+
},
6774
}
6875

6976
/// cbindgen:ignore

wgpu-core/src/command/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2047,6 +2047,8 @@ pub enum PassErrorScope {
20472047
BeginPipelineStatisticsQuery,
20482048
#[error("In a end_pipeline_statistics_query command")]
20492049
EndPipelineStatisticsQuery,
2050+
#[error("In a transition_resources command")]
2051+
TransitionResources,
20502052
#[error("In a execute_bundle command")]
20512053
ExecuteBundle,
20522054
#[error("In a dispatch command, indirect:{indirect}")]

wgpu-core/src/device/trace/record.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,26 @@ impl IntoTrace for ArcComputeCommand {
492492
query_index,
493493
},
494494
C::EndPipelineStatisticsQuery => C::EndPipelineStatisticsQuery,
495+
C::TransitionResources {
496+
buffer_transitions,
497+
texture_transitions,
498+
} => C::TransitionResources {
499+
buffer_transitions: buffer_transitions
500+
.into_iter()
501+
.map(|buffer_transition| wgt::BufferTransition {
502+
buffer: buffer_transition.buffer.into_trace(),
503+
state: buffer_transition.state,
504+
})
505+
.collect(),
506+
texture_transitions: texture_transitions
507+
.into_iter()
508+
.map(|texture_transition| wgt::TextureTransition {
509+
texture: texture_transition.texture.into_trace(),
510+
selector: texture_transition.selector,
511+
state: texture_transition.state,
512+
})
513+
.collect(),
514+
},
495515
}
496516
}
497517
}

wgpu/src/api/compute_pass.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,53 @@ impl ComputePass<'_> {
102102
.dispatch_workgroups_indirect(&indirect_buffer.inner, indirect_offset);
103103
}
104104

105+
/// Transition resources to an underlying hal resource state. Compute pass version of
106+
/// [`CommandEncoder::transition_resources`].
107+
///
108+
/// This is an advanced, native-only API (no-op on web). Useful for native interoperability.
109+
///
110+
/// A user wanting to interoperate with the underlying native graphics APIs (Vulkan, DirectX12, Metal, etc)
111+
/// can use this API to generate barriers between wgpu commands and the native API commands,
112+
/// for synchronization and resource state transition purposes.
113+
/// Unlike [`CommandEncoder::transition_resources`], this does not require ending the pass and will
114+
/// use the same semantics and granularity as the automatic barriers inserted for bindings.
115+
///
116+
/// For example, users might want to pass buffer device addresses into a SPIR-V passthrough shader.
117+
/// These resources cannot be tracked by wgpu since they do not appear in the bindings and will
118+
/// cause data races if not handled - this function allows marking the underlying buffers behind
119+
/// the address as used:
120+
///
121+
/// ```ignore
122+
/// let buffer_transitions =
123+
/// custom_resources
124+
/// .iter()
125+
/// .map(|resource| wgpu::BufferTransition {
126+
/// buffer: &resource.buffer,
127+
/// state: wgpu::BufferUses::STORAGE_READ_WRITE,
128+
/// });
129+
/// pass.transition_resources(buffer_transitions, iter::empty())
130+
///
131+
/// pass.dispatch_workgroups(x, y, z);
132+
/// ```
133+
///
134+
pub fn transition_resources<'a>(
135+
&mut self,
136+
buffer_transitions: impl Iterator<Item = wgt::BufferTransition<&'a Buffer>>,
137+
texture_transitions: impl Iterator<Item = wgt::TextureTransition<&'a TextureView>>,
138+
) {
139+
self.inner.transition_resources(
140+
&mut buffer_transitions.map(|t| wgt::BufferTransition {
141+
buffer: &t.buffer.inner,
142+
state: t.state,
143+
}),
144+
&mut texture_transitions.map(|t| wgt::TextureTransition {
145+
texture: &t.texture.inner,
146+
selector: t.selector,
147+
state: t.state,
148+
}),
149+
);
150+
}
151+
105152
impl_deferred_command_buffer_actions!();
106153

107154
#[cfg(custom)]

wgpu/src/backend/webgpu.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3406,6 +3406,18 @@ impl dispatch::ComputePassInterface for WebComputePassEncoder {
34063406
self.inner
34073407
.dispatch_workgroups_indirect_with_f64(&indirect_buffer.inner, indirect_offset as f64);
34083408
}
3409+
3410+
fn transition_resources<'a>(
3411+
&mut self,
3412+
_buffer_transitions: &mut dyn Iterator<
3413+
Item = wgt::BufferTransition<&'a dispatch::DispatchBuffer>,
3414+
>,
3415+
_texture_transitions: &mut dyn Iterator<
3416+
Item = wgt::TextureTransition<&'a dispatch::DispatchTextureView>,
3417+
>,
3418+
) {
3419+
// noop
3420+
}
34093421
}
34103422
impl Drop for WebComputePassEncoder {
34113423
fn drop(&mut self) {

0 commit comments

Comments
 (0)