diff --git a/Cargo.lock b/Cargo.lock index 2eb5cdfe712..f27c62358e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1002,6 +1002,7 @@ dependencies = [ "deno_web", "deno_webgpu", "deno_webidl", + "env_logger", "termcolor", "tokio", ] diff --git a/cts_runner/Cargo.toml b/cts_runner/Cargo.toml index 715172f6e52..6b96ae66c30 100644 --- a/cts_runner/Cargo.toml +++ b/cts_runner/Cargo.toml @@ -7,6 +7,9 @@ description = "CTS runner for wgpu" license.workspace = true publish = false +[dependencies] +env_logger.workspace = true + # We make all dependencies conditional on not being wasm, # so the whole workspace can built as wasm. [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/cts_runner/src/main.rs b/cts_runner/src/main.rs index 585ba464e29..157567fe6e7 100644 --- a/cts_runner/src/main.rs +++ b/cts_runner/src/main.rs @@ -147,5 +147,6 @@ impl deno_web::TimersPermission for Permissions { #[tokio::main(flavor = "current_thread")] async fn main() { + env_logger::init(); unwrap_or_exit(run().await) } diff --git a/cts_runner/test.lst b/cts_runner/test.lst index 2d8e2553f45..ed0b58e3ad5 100644 --- a/cts_runner/test.lst +++ b/cts_runner/test.lst @@ -9,6 +9,7 @@ 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,image_copy,texture_related:format:dimension="1d";* 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:* diff --git a/tests/tests/wgpu-gpu/queue_transfer.rs b/tests/tests/wgpu-gpu/queue_transfer.rs index 14a552dcff4..9196f7fcbf5 100644 --- a/tests/tests/wgpu-gpu/queue_transfer.rs +++ b/tests/tests/wgpu-gpu/queue_transfer.rs @@ -1,7 +1,57 @@ //! Tests for buffer copy validation. +use wgpu::PollType; use wgpu_test::{fail, gpu_test, GpuTestConfiguration}; +#[gpu_test] +static QUEUE_WRITE_TEXTURE_THEN_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new() + .run_sync(|ctx| { + let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: 64, + height: 32, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba32Float, + usage: wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }); + + let data = vec![255; 1024]; + + ctx.queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: &texture, + mip_level: 0, + origin: wgpu::Origin3d { x: 0, y: 0, z: 0 }, + aspect: wgpu::TextureAspect::All, + }, + &data, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(1024), + rows_per_image: Some(32), + }, + wgpu::Extent3d { + width: 64, + height: 1, + depth_or_array_layers: 1, + }, + ); + + // Unlike textures used in a command buffer, which must not be destroyed prior to calling + // submit, it is permissible to destroy a texture used in an immediate queue operation + // before calling submit. + texture.destroy(); + + ctx.queue.submit([]); + ctx.device.poll(PollType::wait()).unwrap(); + }); + #[gpu_test] static QUEUE_WRITE_TEXTURE_OVERFLOW: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index 8c5bf9d6c10..63ce53e9cbd 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -323,6 +323,7 @@ pub(crate) struct PendingWrites { pub is_recording: bool, temp_resources: Vec, + temp_textures: FastHashMap, dst_buffers: FastHashMap>, dst_textures: FastHashMap>, copied_blas_s: FastHashMap>, @@ -334,6 +335,7 @@ impl PendingWrites { command_encoder, is_recording: false, temp_resources: Vec::new(), + temp_textures: FastHashMap::default(), dst_buffers: FastHashMap::default(), dst_textures: FastHashMap::default(), copied_blas_s: FastHashMap::default(), @@ -367,6 +369,10 @@ impl PendingWrites { self.temp_resources.push(resource); } + pub fn consume_texture(&mut self, index: TrackerIndex, resource: DestroyedTexture) { + self.temp_textures.insert(index, resource); + } + pub fn consume(&mut self, buffer: FlushedStagingBuffer) { self.temp_resources .push(TempResource::StagingBuffer(buffer)); @@ -1279,6 +1285,12 @@ impl Queue { .unwrap() }; } + // It's okay if the texture was destroyed if we still + // have the "pending deletion" handle to it. + Err(DestroyedResourceError(_)) + if pending_writes + .temp_textures + .contains_key(&texture.tracker_index()) => {} Err(e) => break 'error Err(e.into()), } } diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 46be1c1e689..1667fdd7f33 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -1203,25 +1203,28 @@ impl Texture { mem::take(&mut *guard) }; - queue::TempResource::DestroyedTexture(DestroyedTexture { + DestroyedTexture { raw: ManuallyDrop::new(raw), views, clear_mode: mem::replace(&mut *self.clear_mode.write(), TextureClearMode::None), bind_groups, device: Arc::clone(&self.device), label: self.label().to_owned(), - }) + } }; if let Some(queue) = device.get_queue() { let mut pending_writes = queue.pending_writes.lock(); if pending_writes.contains_texture(self) { - pending_writes.consume_temp(temp); + pending_writes.consume_texture(self.tracker_index(), temp); } else { let mut life_lock = queue.lock_life(); let last_submit_index = life_lock.get_texture_latest_submission_index(self); if let Some(last_submit_index) = last_submit_index { - life_lock.schedule_resource_destruction(temp, last_submit_index); + life_lock.schedule_resource_destruction( + queue::TempResource::DestroyedTexture(temp), + last_submit_index, + ); } } }