Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ SamplerDescriptor {
#### General

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

### Bug Fixes

Expand Down
50 changes: 50 additions & 0 deletions tests/tests/wgpu-validation/api/encoding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//! Tests of [`wgpu::CommandEncoder`] and related.

#[test]
fn as_hal() {
// Sanity-test that the raw encoding API isn't completely broken.

let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
unsafe {
encoder.as_hal_mut::<wgpu_hal::api::Noop, _, ()>(|_| ());
}
encoder.finish();
}

#[test]
#[should_panic = "Mixing the wgpu encoding API with the raw encoding API is not permitted"]
fn mix_apis_wgpu_then_hal() {
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 256,
usage: wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
encoder.clear_buffer(&buffer, 0, None);
unsafe {
encoder.as_hal_mut::<wgpu_hal::api::Noop, _, ()>(|_| ());
}
}

#[test]
#[should_panic = "Mixing the wgpu encoding API with the raw encoding API is not permitted"]
fn mix_apis_hal_then_wgpu() {
let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());

let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: None,
size: 256,
usage: wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
unsafe {
encoder.as_hal_mut::<wgpu_hal::api::Noop, _, ()>(|_| ());
}
encoder.clear_buffer(&buffer, 0, None);
}
1 change: 1 addition & 0 deletions tests/tests/wgpu-validation/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod buffer_mapping;
mod buffer_slice;
mod command_buffer_actions;
mod device;
mod encoding;
mod experimental;
mod external_texture;
mod instance;
Expand Down
6 changes: 6 additions & 0 deletions wgpu-core/src/as_hal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,12 @@ impl Global {
})
}

/// Encode commands using the raw HAL command encoder.
///
/// # Panics
///
/// If the command encoder has already been used with the wgpu encoding API.
///
/// # Safety
///
/// - The raw command encoder handle must not be manually destroyed
Expand Down
63 changes: 59 additions & 4 deletions wgpu-core/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ impl CommandEncoderStatus {
) -> Result<(), EncoderStateError> {
match self {
Self::Recording(cmd_buf_data) => {
cmd_buf_data.encoder.api.set(EncodingApi::Wgpu);
match f() {
Ok(cmd) => cmd_buf_data.commands.push(cmd),
Err(err) => {
Expand Down Expand Up @@ -220,10 +221,12 @@ impl CommandEncoderStatus {
E: Clone + Into<CommandEncoderError>,
>(
&mut self,
api: EncodingApi,
f: F,
) -> Result<(), EncoderStateError> {
match self {
Self::Recording(_) => {
Self::Recording(inner) => {
inner.encoder.api.set(api);
RecordingGuard { inner: self }.record(f);
Ok(())
}
Expand Down Expand Up @@ -256,7 +259,10 @@ impl CommandEncoderStatus {
f: F,
) -> T {
match self {
Self::Recording(_) => RecordingGuard { inner: self }.record_as_hal_mut(f),
Self::Recording(inner) => {
inner.encoder.api.set(EncodingApi::Raw);
RecordingGuard { inner: self }.record_as_hal_mut(f)
}
Self::Locked(_) => {
self.invalidate(EncoderStateError::Locked);
f(None)
Expand Down Expand Up @@ -358,8 +364,11 @@ impl CommandEncoderStatus {
// state or an error, to be transferred to the command buffer.
match mem::replace(self, Self::Consumed) {
Self::Recording(inner) => {
// Nothing should have opened the encoder yet.
assert!(!inner.encoder.is_open);
// Raw encoding leaves the encoder open in `command_encoder_as_hal_mut`.
// Otherwise, nothing should have opened it yet.
if inner.encoder.api != EncodingApi::Raw {
assert!(!inner.encoder.is_open);
}
Self::Finished(inner)
}
Self::Consumed | Self::Finished(_) => Self::Error(EncoderStateError::Ended.into()),
Expand Down Expand Up @@ -480,6 +489,38 @@ impl Drop for CommandEncoder {
}
}

/// The encoding API being used with a `CommandEncoder`.
///
/// Mixing APIs on the same encoder is not allowed.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum EncodingApi {
// The regular wgpu encoding APIs are being used.
Wgpu,

// The raw hal encoding API is being used.
Raw,

// Neither encoding API has been called yet.
Undecided,

// The encoder is used internally by wgpu.
InternalUse,
}

impl EncodingApi {
pub(crate) fn set(&mut self, api: EncodingApi) {
match *self {
EncodingApi::Undecided => {
*self = api;
}
self_api if self_api != api => {
panic!("Mixing the wgpu encoding API with the raw encoding API is not permitted");
}
_ => {}
}
}
}

/// A raw [`CommandEncoder`][rce], and the raw [`CommandBuffer`][rcb]s built from it.
///
/// Each wgpu-core [`CommandBuffer`] owns an instance of this type, which is
Expand Down Expand Up @@ -528,6 +569,13 @@ pub(crate) struct InnerCommandEncoder {
/// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
pub(crate) is_open: bool,

/// Tracks which API is being used to encode commands.
///
/// Mixing the wgpu encoding API with access to the raw hal encoder via
/// `as_hal_mut` is not supported. this field tracks which API is being used
/// in order to detect and reject invalid usage.
pub(crate) api: EncodingApi,

pub(crate) label: String,
}

Expand Down Expand Up @@ -790,6 +838,7 @@ impl CommandEncoder {
list: Vec::new(),
device: device.clone(),
is_open: false,
api: EncodingApi::Undecided,
label: label.to_string(),
},
trackers: Tracker::new(),
Expand Down Expand Up @@ -916,6 +965,12 @@ impl CommandEncoder {
let snatch_guard = self.device.snatchable_lock.read();
let mut debug_scope_depth = 0;

if cmd_buf_data.encoder.api == EncodingApi::Raw {
// Should have panicked on the first call that switched APIs,
// but lets be sure.
assert!(cmd_buf_data.commands.is_empty());
}

let mut commands = mem::take(&mut cmd_buf_data.commands);
for command in commands.drain(..) {
if matches!(
Expand Down
1 change: 1 addition & 0 deletions wgpu-core/src/command/ray_tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ impl Global {

let mut cmd_buf_data = cmd_enc.data.lock();
cmd_buf_data.with_buffer(
crate::command::EncodingApi::Raw,
|cmd_buf_data| -> Result<(), BuildAccelerationStructureError> {
let device = &cmd_enc.device;
device.check_is_valid()?;
Expand Down
1 change: 1 addition & 0 deletions wgpu-core/src/device/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ impl PendingWrites {
list: vec![cmd_buf],
device: device.clone(),
is_open: false,
api: crate::command::EncodingApi::InternalUse,
label: "(wgpu internal) PendingWrites command encoder".into(),
},
trackers: Tracker::new(),
Expand Down
12 changes: 10 additions & 2 deletions wgpu/src/api/command_encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,16 @@ impl CommandEncoder {

/// [`Features::EXPERIMENTAL_RAY_QUERY`] must be enabled on the device in order to call these functions.
impl CommandEncoder {
/// Mark acceleration structures as being built. ***Should only*** be used with wgpu-hal
/// functions, all wgpu functions already mark acceleration structures as built.
/// When encoding the acceleration structure build with the raw Hal encoder
/// (obtained from [`CommandEncoder::as_hal_mut`]), this function marks the
/// acceleration structures as having been built.
///
/// This function must only be used with the raw encoder API. When using the
/// wgpu encoding API, acceleration structure build is tracked automatically.
///
/// # Panics
///
/// - If the encoder is being used with the wgpu encoding API.
///
/// # Safety
///
Expand Down
Loading