diff --git a/cts_runner/test.lst b/cts_runner/test.lst index 1dfe9f431a9..79712e17391 100644 --- a/cts_runner/test.lst +++ b/cts_runner/test.lst @@ -9,6 +9,11 @@ webgpu:api,operation,compute,basic:memcpy:* //FAIL: webgpu:api,operation,compute,basic:large_dispatch:* webgpu:api,operation,compute_pipeline,overrides:* webgpu:api,operation,device,lost:* +webgpu:api,validation,queue,submit:command_buffer,device_mismatch:* +webgpu:api,validation,queue,submit:command_buffer,duplicate_buffers:* +webgpu:api,validation,queue,submit:command_buffer,submit_invalidates:* +//FAIL: webgpu:api,validation,queue,submit:command_buffer,invalid_submit_invalidates:* +// https://github.com/gfx-rs/wgpu/issues/3911#issuecomment-2972995675 webgpu:api,operation,render_pipeline,overrides:* webgpu:api,operation,rendering,basic:clear:* webgpu:api,operation,rendering,basic:fullscreen_quad:* diff --git a/deno_webgpu/command_buffer.rs b/deno_webgpu/command_buffer.rs index 6cb9d714c72..b89795d6545 100644 --- a/deno_webgpu/command_buffer.rs +++ b/deno_webgpu/command_buffer.rs @@ -1,7 +1,5 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use std::cell::OnceCell; - use deno_core::op2; use deno_core::GarbageCollected; use deno_core::WebIDL; @@ -12,15 +10,11 @@ pub struct GPUCommandBuffer { pub instance: Instance, pub id: wgpu_core::id::CommandBufferId, pub label: String, - - pub consumed: OnceCell<()>, } impl Drop for GPUCommandBuffer { fn drop(&mut self) { - if self.consumed.get().is_none() { - self.instance.command_buffer_drop(self.id); - } + self.instance.command_buffer_drop(self.id); } } diff --git a/deno_webgpu/command_encoder.rs b/deno_webgpu/command_encoder.rs index 6a1e8121301..318c52a7102 100644 --- a/deno_webgpu/command_encoder.rs +++ b/deno_webgpu/command_encoder.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use std::cell::RefCell; +use std::sync::atomic::{AtomicBool, Ordering}; use deno_core::cppgc::Ptr; use deno_core::op2; @@ -28,11 +29,19 @@ pub struct GPUCommandEncoder { pub id: wgpu_core::id::CommandEncoderId, pub label: String, + + pub finished: AtomicBool, } impl Drop for GPUCommandEncoder { fn drop(&mut self) { - self.instance.command_encoder_drop(self.id); + // Command encoders and command buffers are both the same wgpu object. + // At the time `finished` is set, ownership of the id (and + // responsibility for dropping it) transfers from the encoder to the + // buffer. + if !self.finished.load(Ordering::SeqCst) { + self.instance.command_encoder_drop(self.id); + } } } @@ -407,23 +416,34 @@ impl GPUCommandEncoder { fn finish( &self, #[webidl] descriptor: crate::command_buffer::GPUCommandBufferDescriptor, - ) -> GPUCommandBuffer { + ) -> Result { let wgpu_descriptor = wgpu_types::CommandBufferDescriptor { label: crate::transform_label(descriptor.label.clone()), }; + // TODO(https://github.com/gfx-rs/wgpu/issues/7812): This is not right, + // it should be a validation error, and it would be nice if we can just + // let wgpu generate it for us. The problem is that if the encoder was + // already finished, we transferred ownership of the id to a command + // buffer, so we have to bail out before we mint a duplicate command + // buffer with the same id below. + if self.finished.fetch_or(true, Ordering::SeqCst) { + return Err(JsErrorBox::type_error( + "The command encoder has already finished.", + )); + } + let (id, err) = self .instance .command_encoder_finish(self.id, &wgpu_descriptor); self.error_handler.push_error(err); - GPUCommandBuffer { + Ok(GPUCommandBuffer { instance: self.instance.clone(), id, label: descriptor.label, - consumed: Default::default(), - } + }) } fn push_debug_group(&self, #[webidl] group_label: String) { diff --git a/deno_webgpu/device.rs b/deno_webgpu/device.rs index 1eb10fdd1f9..0193708b50a 100644 --- a/deno_webgpu/device.rs +++ b/deno_webgpu/device.rs @@ -498,6 +498,7 @@ impl GPUDevice { error_handler: self.error_handler.clone(), id, label, + finished: Default::default(), } } diff --git a/deno_webgpu/queue.rs b/deno_webgpu/queue.rs index e9b736e79c7..59dbffe461b 100644 --- a/deno_webgpu/queue.rs +++ b/deno_webgpu/queue.rs @@ -59,17 +59,8 @@ impl GPUQueue { ) -> Result, JsErrorBox> { let ids = command_buffers .into_iter() - .enumerate() - .map(|(i, cb)| { - if cb.consumed.set(()).is_err() { - Err(JsErrorBox::type_error(format!( - "The command buffer at position {i} has already been submitted." - ))) - } else { - Ok(cb.id) - } - }) - .collect::, _>>()?; + .map(|cb| cb.id) + .collect::>(); let err = self.instance.queue_submit(self.id, &ids).err(); diff --git a/deno_webgpu/texture.rs b/deno_webgpu/texture.rs index bb1d026f12d..1ae74ab16a7 100644 --- a/deno_webgpu/texture.rs +++ b/deno_webgpu/texture.rs @@ -550,7 +550,7 @@ impl From for TextureFormat { channel: AstcChannel::Unorm, }, GPUTextureFormat::Astc4x4UnormSrgb => Self::Astc { - block: AstcBlock::B5x4, + block: AstcBlock::B4x4, channel: AstcChannel::UnormSrgb, }, GPUTextureFormat::Astc5x4Unorm => Self::Astc {