Show command encoder label in validation errors.

The plumbing to get it out of `wgpu_core` is awkward but I didn’t see
a clearly better option. I think it would make sense to have something
like `Global::get_label<T>(id: Id<T>) -> String` but that looks like a
lot more work.
This commit is contained in:
Kevin Reid 2025-10-30 11:46:29 -07:00 committed by Connor Fitzgerald
parent 586b344719
commit 5633ae8649
5 changed files with 44 additions and 11 deletions

View File

@ -87,6 +87,7 @@ SamplerDescriptor {
- Using both the wgpu command encoding APIs and `CommandEncoder::as_hal_mut` on the same encoder will now result in a panic.
- Allow `include_spirv!` and `include_spirv_raw!` macros to be used in constants and statics. By @clarfonthey in [#8250](https://github.com/gfx-rs/wgpu/pull/8250).
- Added support for rendering onto multi-planar textures. By @noituri in [#8307](https://github.com/gfx-rs/wgpu/pull/8307).
- Validation errors from `CommandEncoder::finish()` will report the label of the invalid encoder. By @kpreid in [#8449](https://github.com/gfx-rs/wgpu/pull/8449).
- Corrected documentation of the minimum alignment of the *end* of a mapped range of a buffer (it is 4, not 8). By @kpreid in [#8450](https://github.com/gfx-rs/wgpu/pull/8450).
### Bug Fixes

View File

@ -437,12 +437,14 @@ impl GPUCommandEncoder {
label: crate::transform_label(descriptor.label.clone()),
};
let (id, err) =
let (id, opt_label_and_err) =
self
.instance
.command_encoder_finish(self.id, &wgpu_descriptor, None);
self.error_handler.push_error(err);
self
.error_handler
.push_error(opt_label_and_err.map(|(_label, err)| err));
GPUCommandBuffer {
instance: self.instance.clone(),

View File

@ -48,3 +48,23 @@ fn mix_apis_hal_then_wgpu() {
}
encoder.clear_buffer(&buffer, 0, None);
}
/// Test that the command encoders label is remembered and used in errors.
#[test]
#[should_panic = "In a CommandEncoder, label = 'my encoder'"]
fn encoding_error_contains_label_of_encoder() {
let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default());
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("my buffer"),
size: 1024,
usage: wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
});
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("my encoder"),
});
// This is erroneous because it is copying to the same buffer.
encoder.copy_buffer_to_buffer(&buffer, 0, &buffer, 0, 10);
queue.submit([encoder.finish()]);
}

View File

@ -1589,21 +1589,31 @@ impl Global {
self.hub.query_sets.get(query_set_id).get()
}
/// Finishes a command encoder, creating a command buffer and returning errors that were
/// deferred until now.
///
/// The returned `String` is the label of the command encoder, supplied so that `wgpu` can
/// include the label when printing deferred errors without having its own copy of the label.
/// This is a kludge and should be replaced if we think of a better solution to propagating
/// labels.
pub fn command_encoder_finish(
&self,
encoder_id: id::CommandEncoderId,
desc: &wgt::CommandBufferDescriptor<Label>,
id_in: Option<id::CommandBufferId>,
) -> (id::CommandBufferId, Option<CommandEncoderError>) {
) -> (id::CommandBufferId, Option<(String, CommandEncoderError)>) {
profiling::scope!("CommandEncoder::finish");
let hub = &self.hub;
let cmd_enc = hub.command_encoders.get(encoder_id);
let (cmd_buf, error) = cmd_enc.finish(desc);
let (cmd_buf, opt_error) = cmd_enc.finish(desc);
let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(cmd_buf);
(cmd_buf_id, error)
(
cmd_buf_id,
opt_error.map(|error| (cmd_enc.label.clone(), error)),
)
}
pub fn command_encoder_push_debug_group(

View File

@ -2577,13 +2577,13 @@ impl dispatch::CommandEncoderInterface for CoreCommandEncoder {
fn finish(&mut self) -> dispatch::DispatchCommandBuffer {
let descriptor = wgt::CommandBufferDescriptor::default();
let (id, error) = self
.context
.0
.command_encoder_finish(self.id, &descriptor, None);
if let Some(cause) = error {
let (id, opt_label_and_error) =
self.context
.handle_error_nolabel(&self.error_sink, cause, "a CommandEncoder");
.0
.command_encoder_finish(self.id, &descriptor, None);
if let Some((label, cause)) = opt_label_and_error {
self.context
.handle_error(&self.error_sink, cause, Some(&label), "a CommandEncoder");
}
CoreCommandBuffer {
context: self.context.clone(),