Skip to content

Commit d575c02

Browse files
Disallow mixing the raw encoding API with the wgpu API (#8373)
1 parent 8eeb2e3 commit d575c02

File tree

8 files changed

+129
-6
lines changed

8 files changed

+129
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ SamplerDescriptor {
7878
#### General
7979

8080
- Texture now has `from_custom`. By @R-Cramer4 in [#8315](https://github.com/gfx-rs/wgpu/pull/8315).
81+
- Using both the wgpu command encoding APIs and `CommandEncoder::as_hal_mut` on the same encoder will now result in a panic.
8182

8283
### Bug Fixes
8384

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//! Tests of [`wgpu::CommandEncoder`] and related.
2+
3+
#[test]
4+
fn as_hal() {
5+
// Sanity-test that the raw encoding API isn't completely broken.
6+
7+
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());
8+
9+
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
10+
unsafe {
11+
encoder.as_hal_mut::<wgpu_hal::api::Noop, _, ()>(|_| ());
12+
}
13+
encoder.finish();
14+
}
15+
16+
#[test]
17+
#[should_panic = "Mixing the wgpu encoding API with the raw encoding API is not permitted"]
18+
fn mix_apis_wgpu_then_hal() {
19+
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());
20+
21+
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
22+
label: None,
23+
size: 256,
24+
usage: wgpu::BufferUsages::COPY_DST,
25+
mapped_at_creation: false,
26+
});
27+
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
28+
encoder.clear_buffer(&buffer, 0, None);
29+
unsafe {
30+
encoder.as_hal_mut::<wgpu_hal::api::Noop, _, ()>(|_| ());
31+
}
32+
}
33+
34+
#[test]
35+
#[should_panic = "Mixing the wgpu encoding API with the raw encoding API is not permitted"]
36+
fn mix_apis_hal_then_wgpu() {
37+
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());
38+
39+
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
40+
label: None,
41+
size: 256,
42+
usage: wgpu::BufferUsages::COPY_DST,
43+
mapped_at_creation: false,
44+
});
45+
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
46+
unsafe {
47+
encoder.as_hal_mut::<wgpu_hal::api::Noop, _, ()>(|_| ());
48+
}
49+
encoder.clear_buffer(&buffer, 0, None);
50+
}

tests/tests/wgpu-validation/api/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod buffer_mapping;
44
mod buffer_slice;
55
mod command_buffer_actions;
66
mod device;
7+
mod encoding;
78
mod experimental;
89
mod external_texture;
910
mod instance;

wgpu-core/src/as_hal.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,12 @@ impl Global {
330330
})
331331
}
332332

333+
/// Encode commands using the raw HAL command encoder.
334+
///
335+
/// # Panics
336+
///
337+
/// If the command encoder has already been used with the wgpu encoding API.
338+
///
333339
/// # Safety
334340
///
335341
/// - The raw command encoder handle must not be manually destroyed

wgpu-core/src/command/mod.rs

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ impl CommandEncoderStatus {
177177
) -> Result<(), EncoderStateError> {
178178
match self {
179179
Self::Recording(cmd_buf_data) => {
180+
cmd_buf_data.encoder.api.set(EncodingApi::Wgpu);
180181
match f() {
181182
Ok(cmd) => cmd_buf_data.commands.push(cmd),
182183
Err(err) => {
@@ -220,10 +221,12 @@ impl CommandEncoderStatus {
220221
E: Clone + Into<CommandEncoderError>,
221222
>(
222223
&mut self,
224+
api: EncodingApi,
223225
f: F,
224226
) -> Result<(), EncoderStateError> {
225227
match self {
226-
Self::Recording(_) => {
228+
Self::Recording(inner) => {
229+
inner.encoder.api.set(api);
227230
RecordingGuard { inner: self }.record(f);
228231
Ok(())
229232
}
@@ -256,7 +259,10 @@ impl CommandEncoderStatus {
256259
f: F,
257260
) -> T {
258261
match self {
259-
Self::Recording(_) => RecordingGuard { inner: self }.record_as_hal_mut(f),
262+
Self::Recording(inner) => {
263+
inner.encoder.api.set(EncodingApi::Raw);
264+
RecordingGuard { inner: self }.record_as_hal_mut(f)
265+
}
260266
Self::Locked(_) => {
261267
self.invalidate(EncoderStateError::Locked);
262268
f(None)
@@ -358,8 +364,11 @@ impl CommandEncoderStatus {
358364
// state or an error, to be transferred to the command buffer.
359365
match mem::replace(self, Self::Consumed) {
360366
Self::Recording(inner) => {
361-
// Nothing should have opened the encoder yet.
362-
assert!(!inner.encoder.is_open);
367+
// Raw encoding leaves the encoder open in `command_encoder_as_hal_mut`.
368+
// Otherwise, nothing should have opened it yet.
369+
if inner.encoder.api != EncodingApi::Raw {
370+
assert!(!inner.encoder.is_open);
371+
}
363372
Self::Finished(inner)
364373
}
365374
Self::Consumed | Self::Finished(_) => Self::Error(EncoderStateError::Ended.into()),
@@ -480,6 +489,38 @@ impl Drop for CommandEncoder {
480489
}
481490
}
482491

492+
/// The encoding API being used with a `CommandEncoder`.
493+
///
494+
/// Mixing APIs on the same encoder is not allowed.
495+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
496+
pub enum EncodingApi {
497+
// The regular wgpu encoding APIs are being used.
498+
Wgpu,
499+
500+
// The raw hal encoding API is being used.
501+
Raw,
502+
503+
// Neither encoding API has been called yet.
504+
Undecided,
505+
506+
// The encoder is used internally by wgpu.
507+
InternalUse,
508+
}
509+
510+
impl EncodingApi {
511+
pub(crate) fn set(&mut self, api: EncodingApi) {
512+
match *self {
513+
EncodingApi::Undecided => {
514+
*self = api;
515+
}
516+
self_api if self_api != api => {
517+
panic!("Mixing the wgpu encoding API with the raw encoding API is not permitted");
518+
}
519+
_ => {}
520+
}
521+
}
522+
}
523+
483524
/// A raw [`CommandEncoder`][rce], and the raw [`CommandBuffer`][rcb]s built from it.
484525
///
485526
/// Each wgpu-core [`CommandBuffer`] owns an instance of this type, which is
@@ -528,6 +569,13 @@ pub(crate) struct InnerCommandEncoder {
528569
/// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
529570
pub(crate) is_open: bool,
530571

572+
/// Tracks which API is being used to encode commands.
573+
///
574+
/// Mixing the wgpu encoding API with access to the raw hal encoder via
575+
/// `as_hal_mut` is not supported. this field tracks which API is being used
576+
/// in order to detect and reject invalid usage.
577+
pub(crate) api: EncodingApi,
578+
531579
pub(crate) label: String,
532580
}
533581

@@ -790,6 +838,7 @@ impl CommandEncoder {
790838
list: Vec::new(),
791839
device: device.clone(),
792840
is_open: false,
841+
api: EncodingApi::Undecided,
793842
label: label.to_string(),
794843
},
795844
trackers: Tracker::new(),
@@ -916,6 +965,12 @@ impl CommandEncoder {
916965
let snatch_guard = self.device.snatchable_lock.read();
917966
let mut debug_scope_depth = 0;
918967

968+
if cmd_buf_data.encoder.api == EncodingApi::Raw {
969+
// Should have panicked on the first call that switched APIs,
970+
// but lets be sure.
971+
assert!(cmd_buf_data.commands.is_empty());
972+
}
973+
919974
let mut commands = mem::take(&mut cmd_buf_data.commands);
920975
for command in commands.drain(..) {
921976
if matches!(

wgpu-core/src/command/ray_tracing.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ impl Global {
8787

8888
let mut cmd_buf_data = cmd_enc.data.lock();
8989
cmd_buf_data.with_buffer(
90+
crate::command::EncodingApi::Raw,
9091
|cmd_buf_data| -> Result<(), BuildAccelerationStructureError> {
9192
let device = &cmd_enc.device;
9293
device.check_is_valid()?;

wgpu-core/src/device/queue.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ impl PendingWrites {
406406
list: vec![cmd_buf],
407407
device: device.clone(),
408408
is_open: false,
409+
api: crate::command::EncodingApi::InternalUse,
409410
label: "(wgpu internal) PendingWrites command encoder".into(),
410411
},
411412
trackers: Tracker::new(),

wgpu/src/api/command_encoder.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,16 @@ impl CommandEncoder {
310310

311311
/// [`Features::EXPERIMENTAL_RAY_QUERY`] must be enabled on the device in order to call these functions.
312312
impl CommandEncoder {
313-
/// Mark acceleration structures as being built. ***Should only*** be used with wgpu-hal
314-
/// functions, all wgpu functions already mark acceleration structures as built.
313+
/// When encoding the acceleration structure build with the raw Hal encoder
314+
/// (obtained from [`CommandEncoder::as_hal_mut`]), this function marks the
315+
/// acceleration structures as having been built.
316+
///
317+
/// This function must only be used with the raw encoder API. When using the
318+
/// wgpu encoding API, acceleration structure build is tracked automatically.
319+
///
320+
/// # Panics
321+
///
322+
/// - If the encoder is being used with the wgpu encoding API.
315323
///
316324
/// # Safety
317325
///

0 commit comments

Comments
 (0)