Skip to main content

wgpu_core/device/
queue.rs

1#[cfg(feature = "trace")]
2use alloc::string::ToString as _;
3use alloc::{boxed::Box, string::String, sync::Arc, vec, vec::Vec};
4use core::{
5    iter,
6    mem::{self, ManuallyDrop},
7    num::NonZeroU64,
8    ptr::NonNull,
9    sync::atomic::Ordering,
10};
11use smallvec::SmallVec;
12use thiserror::Error;
13use wgt::{
14    error::{ErrorType, WebGpuError},
15    AccelerationStructureFlags,
16};
17
18use super::{life::LifetimeTracker, Device};
19#[cfg(feature = "trace")]
20use crate::device::trace::{Action, IntoTrace};
21use crate::{
22    api_log,
23    command::{
24        extract_texture_selector, validate_linear_texture_data, validate_texture_buffer_copy,
25        validate_texture_copy_dst_format, validate_texture_copy_range, ClearError,
26        CommandAllocator, CommandBuffer, CommandEncoder, CommandEncoderError, CopySide,
27        TransferError,
28    },
29    device::{DeviceError, QueueDescriptor, WaitIdleError},
30    get_lowest_common_denom, hal_label,
31    init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange},
32    lock::{rank, Mutex, MutexGuard, RwLock, RwLockWriteGuard},
33    ray_tracing::{BlasCompactReadyPendingClosure, CompactBlasError},
34    resource::{
35        Blas, BlasCompactState, BlasDescriptor, BlasState, Buffer, BufferAccessError,
36        BufferMapState, DestroyedBuffer, DestroyedQuerySet, DestroyedResourceError,
37        DestroyedTexture, FlushedStagingBuffer, InvalidOrDestroyedResourceError,
38        InvalidResourceError, Labeled, ParentDevice, ResourceErrorIdent, ResourceState,
39        StagingBuffer, Texture, TextureInner, Trackable, TrackingData,
40    },
41    resource_log,
42    scratch::ScratchBuffer,
43    snatch::{SnatchGuard, Snatchable},
44    track::{self, Tracker, TrackerIndex},
45    FastHashMap, LabelHelpers, SubmissionIndex,
46};
47use crate::{device::resource::CommandIndices, resource::RawResourceAccess};
48
49pub struct Queue {
50    raw: Box<dyn hal::DynQueue>,
51    pub(crate) pending_writes: Mutex<PendingWrites>,
52    life_tracker: Mutex<LifetimeTracker>,
53    label: String,
54    // The device needs to be dropped last (`Device.zero_buffer` might be referenced by the encoder in pending writes).
55    pub(crate) device: Arc<Device>,
56}
57
58impl Queue {
59    pub(crate) fn new(
60        device: Arc<Device>,
61        raw: Box<dyn hal::DynQueue>,
62        desc: QueueDescriptor,
63        instance_flags: wgt::InstanceFlags,
64    ) -> Result<Self, DeviceError> {
65        let pending_encoder = device
66            .command_allocator
67            .acquire_encoder(device.raw(), raw.as_ref())
68            .map_err(DeviceError::from_hal);
69
70        let pending_encoder = match pending_encoder {
71            Ok(pending_encoder) => pending_encoder,
72            Err(e) => {
73                return Err(e);
74            }
75        };
76
77        let mut pending_writes = PendingWrites::new(pending_encoder, instance_flags);
78
79        let zero_buffer = device.zero_buffer.as_ref();
80        pending_writes.activate();
81        unsafe {
82            pending_writes
83                .command_encoder
84                .transition_buffers(&[hal::BufferBarrier {
85                    buffer: zero_buffer,
86                    usage: hal::StateTransition {
87                        from: wgt::BufferUses::empty(),
88                        to: wgt::BufferUses::COPY_DST,
89                    },
90                }]);
91            pending_writes
92                .command_encoder
93                .clear_buffer(zero_buffer, 0..super::ZERO_BUFFER_SIZE);
94            pending_writes
95                .command_encoder
96                .transition_buffers(&[hal::BufferBarrier {
97                    buffer: zero_buffer,
98                    usage: hal::StateTransition {
99                        from: wgt::BufferUses::COPY_DST,
100                        to: wgt::BufferUses::COPY_SRC,
101                    },
102                }]);
103        }
104
105        Ok(Queue {
106            raw,
107            device,
108            label: desc.label.to_string(),
109            pending_writes: Mutex::new(rank::QUEUE_PENDING_WRITES, pending_writes),
110            life_tracker: Mutex::new(rank::QUEUE_LIFE_TRACKER, LifetimeTracker::new()),
111        })
112    }
113
114    pub(crate) fn raw(&self) -> &dyn hal::DynQueue {
115        self.raw.as_ref()
116    }
117
118    #[track_caller]
119    pub(crate) fn lock_life<'a>(&'a self) -> MutexGuard<'a, LifetimeTracker> {
120        self.life_tracker.lock()
121    }
122
123    /// Ensure the surface texture is in the PRESENT state, clearing it if it was never rendered to.
124    /// Submits any necessary work to the GPU before the HAL present call.
125    ///
126    /// See <https://github.com/gfx-rs/wgpu/issues/6748>
127    pub(crate) fn prepare_surface_texture_for_present(
128        &self,
129        texture: &Arc<Texture>,
130    ) -> Result<(), DeviceError> {
131        let snatch_guard = self.device.snatchable_lock.read();
132        let submission = self
133            .allocate_submission(snatch_guard)
134            .map_err(|(_index, e)| e)?;
135        let device = &self.device;
136
137        // If the texture is uninitialized it needs to be cleared before presenting
138        let needs_clear = {
139            let status = texture.initialization_status.read();
140            status
141                .mips
142                .first()
143                .is_some_and(|mip| mip.check(0..1).is_some())
144        };
145
146        let mut pending_writes = self.pending_writes.lock();
147
148        if needs_clear {
149            // After encoding the clear operation, we must not return without
150            // adding the texture to `pending_writes`.
151            let encoder = pending_writes.activate();
152            let mut trackers = device.trackers.lock();
153            crate::command::clear_texture(
154                texture,
155                TextureInitRange {
156                    mip_range: 0..1,
157                    layer_range: 0..1,
158                },
159                None,
160                encoder,
161                &mut trackers.textures,
162                &device.alignments,
163                device.zero_buffer.as_ref(),
164                &submission.snatch_guard,
165                device.instance_flags,
166            )
167            .map_err(|e| match e {
168                ClearError::Device(e) => e,
169                _ => DeviceError::Lost,
170            })?;
171            texture.initialization_status.write().mips[0].drain(0..1);
172        }
173
174        // Transition the texture to PRESENT in the device tracker.
175        // If it's already in PRESENT, this produces no barriers and we can skip the submission.
176        //
177        // This has to be after any clear_texture call because clear_texture modifies the tracker state internally.
178        // Computing transitions afterward ensures they reflect the actual current state.
179        let pending = {
180            let mut trackers = device.trackers.lock();
181            let pending: Vec<track::PendingTransition<wgt::TextureUses>> = trackers
182                .textures
183                .set_single(
184                    texture,
185                    texture.full_range.clone(),
186                    wgt::TextureUses::PRESENT,
187                )
188                .collect();
189            pending
190        };
191
192        if pending.is_empty() {
193            // This assert checks that we don't return here if we encoded a
194            // clear operation for the texture, which would be a problem since
195            // we haven't done anything yet to ensure it stays alive. If we
196            // cleared the texture, then we must have produced a barrier to put
197            // it in PRESENT state, so `pending` will not be empty.
198            debug_assert!(!needs_clear);
199            return Ok(());
200        }
201
202        // Emit the transition barriers to PRESENT.
203        {
204            let raw_texture = texture
205                .raw(&submission.snatch_guard)
206                .ok_or(DeviceError::Lost)?;
207            let barriers: Vec<hal::TextureBarrier<'_, dyn hal::DynTexture>> = pending
208                .into_iter()
209                .map(|pt| pt.into_hal(raw_texture))
210                .collect();
211
212            let encoder = pending_writes.activate();
213            // SAFETY:
214            // - The encoder is in the recording state after `activate()`
215            // - The texture is kept alive by adding it to `PendingWrites` below
216            unsafe {
217                encoder.transition_textures(&barriers);
218            }
219        }
220
221        // Add the texture to `PendingWrites`. This will cause `submit()` to:
222        // - Flush any pending writes to the texture.
223        // - Include the texture in `surface_textures` for the submission.
224        // - Keep the texture alive so the texture and its clear_view aren't
225        //   destroyed before the GPU finishes the `clear_texture` operation
226        //   encoded above.
227        pending_writes.insert_texture(texture);
228
229        submission.submit(pending_writes)?;
230
231        Ok(())
232    }
233
234    /// Maintains the queue's list of finished command buffers.
235    ///
236    /// Command buffers submitted before `submission_index` was submitted
237    /// stop being tracked and callbacks which are waiting for them are
238    /// returned. Also returned is whether the queue is empty. This may
239    /// be stale unless new submissions are prevented by locking
240    /// [`Device::command_indices`]
241    pub(crate) fn maintain(
242        &self,
243        submission_index: u64,
244        snatch_guard: &SnatchGuard,
245    ) -> (
246        SmallVec<[SubmittedWorkDoneClosure; 1]>,
247        Vec<super::BufferMapPendingClosure>,
248        Vec<BlasCompactReadyPendingClosure>,
249        bool,
250    ) {
251        let mut life_tracker = self.lock_life();
252        let submission_closures = life_tracker.triage_submissions(submission_index);
253
254        let mapping_closures = life_tracker.handle_mapping(snatch_guard);
255        let blas_closures = life_tracker.handle_compact_read_back();
256
257        let queue_empty = life_tracker.queue_empty();
258
259        (
260            submission_closures,
261            mapping_closures,
262            blas_closures,
263            queue_empty,
264        )
265    }
266}
267
268crate::impl_resource_type!(Queue);
269crate::impl_labeled!(Queue);
270crate::impl_parent_device!(Queue);
271crate::impl_storage_item!(Queue);
272
273impl Drop for Queue {
274    #[allow(trivial_casts)]
275    fn drop(&mut self) {
276        profiling::scope!("Queue::drop");
277        api_log!("Queue::drop {:?}", self as *const _);
278        resource_log!("Drop {}", self.error_ident());
279
280        // On Vulkan, pending presents are not tracked by fences.
281        // wait_for_idle covers both fence-tracked submissions and pending presents.
282        match unsafe { self.raw.wait_for_idle() } {
283            Ok(()) => {}
284            Err(hal::DeviceError::Lost) => {
285                self.device.handle_hal_error(hal::DeviceError::Lost);
286            }
287            Err(e) => {
288                panic!("Unexpected error while waiting for queue idle on drop: {e:?}");
289            }
290        }
291
292        let last_successful_submission_index = self
293            .device
294            .last_successful_submission_index
295            .load(Ordering::Acquire);
296
297        let snatch_guard = self.device.snatchable_lock.read();
298        let (submission_closures, mapping_closures, blas_compact_ready_closures, queue_empty) =
299            self.maintain(last_successful_submission_index, &snatch_guard);
300        drop(snatch_guard);
301
302        assert!(queue_empty);
303
304        let closures = crate::device::UserClosures {
305            mappings: mapping_closures,
306            blas_compact_ready: blas_compact_ready_closures,
307            submissions: submission_closures,
308            device_lost_invocations: SmallVec::new(),
309        };
310
311        closures.fire();
312    }
313}
314
315#[cfg(send_sync)]
316pub type SubmittedWorkDoneClosure = Box<dyn FnOnce() + Send + 'static>;
317#[cfg(not(send_sync))]
318pub type SubmittedWorkDoneClosure = Box<dyn FnOnce() + 'static>;
319
320/// A texture or buffer to be freed soon.
321///
322/// This is just a tagged raw texture or buffer, generally about to be added to
323/// some other more specific container like:
324///
325/// - `PendingWrites::temp_resources`: resources used by queue writes and
326///   unmaps, waiting to be folded in with the next queue submission
327///
328/// - `ActiveSubmission::temp_resources`: temporary resources used by a queue
329///   submission, to be freed when it completes
330#[derive(Debug)]
331pub enum TempResource {
332    StagingBuffer(FlushedStagingBuffer),
333    ScratchBuffer(ScratchBuffer),
334    DestroyedBuffer(DestroyedBuffer),
335    DestroyedTexture(DestroyedTexture),
336    DestroyedQuerySet(DestroyedQuerySet),
337}
338
339/// A series of raw [`CommandBuffer`]s that have been submitted to a
340/// queue, and the [`wgpu_hal::CommandEncoder`] that built them.
341///
342/// [`CommandBuffer`]: hal::Api::CommandBuffer
343/// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
344pub(crate) struct EncoderInFlight {
345    inner: crate::command::InnerCommandEncoder,
346    pub(crate) trackers: Tracker,
347    pub(crate) temp_resources: Vec<TempResource>,
348    /// We only need to keep these resources alive.
349    _indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
350
351    /// These are the buffers that have been tracked by `PendingWrites`.
352    pub(crate) pending_buffers: FastHashMap<TrackerIndex, Arc<Buffer>>,
353    /// These are the textures that have been tracked by `PendingWrites`.
354    pub(crate) pending_textures: FastHashMap<TrackerIndex, Arc<Texture>>,
355    /// These are the BLASes that have been tracked by `PendingWrites`.
356    pub(crate) pending_blas_s: FastHashMap<TrackerIndex, Arc<Blas>>,
357}
358
359/// A private command encoder for writes made directly on the device
360/// or queue.
361///
362/// Operations like `buffer_unmap`, `queue_write_buffer`, and
363/// `queue_write_texture` need to copy data to the GPU. At the hal
364/// level, this must be done by encoding and submitting commands, but
365/// these operations are not associated with any specific wgpu command
366/// buffer.
367///
368/// Instead, `Device::pending_writes` owns one of these values, which
369/// has its own hal command encoder and resource lists. The commands
370/// accumulated here are automatically submitted to the queue at the
371/// sooner of:
372///
373/// 1. The user's next wgpu command buffer submission. (Pending writes
374///    are inserted ahead of the user's commands.)
375/// 2. The next `mapAsync` request for a buffer that has pending
376///    writes.
377///
378/// Important:
379/// When locking pending_writes be sure that tracker is not locked
380/// and try to lock trackers for the minimum timespan possible
381///
382/// All uses of [`StagingBuffer`]s end up here.
383#[derive(Debug)]
384pub(crate) struct PendingWrites {
385    // The command encoder needs to be destroyed before any other resource in pending writes.
386    pub command_encoder: Box<dyn hal::DynCommandEncoder>,
387
388    /// True if `command_encoder` is in the "recording" state, as
389    /// described in the docs for the [`wgpu_hal::CommandEncoder`]
390    /// trait.
391    ///
392    /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
393    pub is_recording: bool,
394
395    temp_resources: Vec<TempResource>,
396    dst_buffers: FastHashMap<TrackerIndex, Arc<Buffer>>,
397    dst_textures: FastHashMap<TrackerIndex, Arc<Texture>>,
398    copied_blas_s: FastHashMap<TrackerIndex, Arc<Blas>>,
399    instance_flags: wgt::InstanceFlags,
400}
401
402impl PendingWrites {
403    pub fn new(
404        command_encoder: Box<dyn hal::DynCommandEncoder>,
405        instance_flags: wgt::InstanceFlags,
406    ) -> Self {
407        Self {
408            command_encoder,
409            is_recording: false,
410            temp_resources: Vec::new(),
411            dst_buffers: FastHashMap::default(),
412            dst_textures: FastHashMap::default(),
413            copied_blas_s: FastHashMap::default(),
414            instance_flags,
415        }
416    }
417
418    pub fn insert_buffer(&mut self, buffer: &Arc<Buffer>) {
419        self.dst_buffers
420            .insert(buffer.tracker_index(), buffer.clone());
421    }
422
423    pub fn insert_texture(&mut self, texture: &Arc<Texture>) {
424        self.dst_textures
425            .insert(texture.tracker_index(), texture.clone());
426    }
427
428    pub fn insert_blas(&mut self, blas: &Arc<Blas>) {
429        self.copied_blas_s
430            .insert(blas.tracker_index(), blas.clone());
431    }
432
433    pub fn contains_buffer(&self, buffer: &Arc<Buffer>) -> bool {
434        self.dst_buffers.contains_key(&buffer.tracker_index())
435    }
436
437    pub fn contains_texture(&self, texture: &Arc<Texture>) -> bool {
438        self.dst_textures.contains_key(&texture.tracker_index())
439    }
440
441    pub fn consume_temp(&mut self, resource: TempResource) {
442        self.temp_resources.push(resource);
443    }
444
445    pub fn consume(&mut self, buffer: FlushedStagingBuffer) {
446        self.temp_resources
447            .push(TempResource::StagingBuffer(buffer));
448    }
449
450    pub fn clear_buffer(
451        &mut self,
452        device: &Arc<Device>,
453        buffer: &Arc<Buffer>,
454        range: core::ops::Range<wgt::BufferAddress>,
455        snatch_guard: &SnatchGuard,
456    ) -> Result<(), QueueWriteError> {
457        let barriers = {
458            let mut trackers = device.trackers.lock();
459            trackers
460                .buffers
461                .set_single(buffer, wgt::BufferUses::COPY_DST)
462                .map(|pending| pending.into_hal(buffer, snatch_guard))
463        };
464
465        let dst_raw = buffer.try_raw(snatch_guard)?;
466
467        let encoder = self.activate();
468        unsafe {
469            encoder.transition_buffers(barriers.as_slice());
470            encoder.clear_buffer(dst_raw, range.clone());
471        }
472
473        self.insert_buffer(buffer);
474
475        // Ensure the overwritten bytes are marked as initialized so
476        // they don't need to be nulled prior to mapping or binding.
477        buffer.initialization_status.write().drain(range);
478
479        Ok(())
480    }
481
482    fn pre_submit(
483        &mut self,
484        command_allocator: &CommandAllocator,
485        device: &Arc<Device>,
486        queue: &Queue,
487    ) -> Result<Option<EncoderInFlight>, DeviceError> {
488        if self.is_recording {
489            let pending_buffers = mem::take(&mut self.dst_buffers);
490            let pending_textures = mem::take(&mut self.dst_textures);
491            let pending_blas_s = mem::take(&mut self.copied_blas_s);
492
493            let cmd_buf = unsafe { self.command_encoder.end_encoding() }
494                .map_err(|e| device.handle_hal_error(e))?;
495            self.is_recording = false;
496
497            let new_encoder = command_allocator
498                .acquire_encoder(device.raw(), queue.raw())
499                .map_err(|e| device.handle_hal_error(e))?;
500
501            let encoder = EncoderInFlight {
502                inner: crate::command::InnerCommandEncoder {
503                    raw: ManuallyDrop::new(mem::replace(&mut self.command_encoder, new_encoder)),
504                    list: vec![cmd_buf],
505                    device: device.clone(),
506                    is_open: false,
507                    api: crate::command::EncodingApi::InternalUse,
508                    label: "(wgpu internal) PendingWrites command encoder".into(),
509                },
510                trackers: Tracker::new(device.ordered_buffer_usages, device.ordered_texture_usages),
511                temp_resources: mem::take(&mut self.temp_resources),
512                _indirect_draw_validation_resources: crate::indirect_validation::DrawResources::new(
513                    device.clone(),
514                ),
515                pending_buffers,
516                pending_textures,
517                pending_blas_s,
518            };
519            Ok(Some(encoder))
520        } else {
521            self.dst_buffers.clear();
522            self.dst_textures.clear();
523            self.copied_blas_s.clear();
524            Ok(None)
525        }
526    }
527
528    pub fn activate(&mut self) -> &mut dyn hal::DynCommandEncoder {
529        if !self.is_recording {
530            unsafe {
531                self.command_encoder
532                    .begin_encoding(hal_label(
533                        Some("(wgpu internal) PendingWrites"),
534                        self.instance_flags,
535                    ))
536                    .unwrap();
537            }
538            self.is_recording = true;
539        }
540        self.command_encoder.as_mut()
541    }
542}
543
544impl Drop for PendingWrites {
545    fn drop(&mut self) {
546        unsafe {
547            if self.is_recording {
548                self.command_encoder.discard_encoding();
549            }
550        }
551    }
552}
553
554#[derive(Clone, Debug, Error)]
555#[non_exhaustive]
556pub enum QueueWriteError {
557    #[error(transparent)]
558    Queue(#[from] DeviceError),
559    #[error(transparent)]
560    Transfer(#[from] TransferError),
561    #[error(transparent)]
562    MemoryInitFailure(#[from] ClearError),
563    #[error(transparent)]
564    DestroyedResource(#[from] DestroyedResourceError),
565    #[error(transparent)]
566    InvalidResource(#[from] InvalidResourceError),
567}
568
569impl From<InvalidOrDestroyedResourceError> for QueueWriteError {
570    fn from(e: InvalidOrDestroyedResourceError) -> Self {
571        match e {
572            InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
573            InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
574        }
575    }
576}
577
578impl WebGpuError for QueueWriteError {
579    fn webgpu_error_type(&self) -> ErrorType {
580        match self {
581            Self::Queue(e) => e.webgpu_error_type(),
582            Self::Transfer(e) => e.webgpu_error_type(),
583            Self::MemoryInitFailure(e) => e.webgpu_error_type(),
584            Self::DestroyedResource(e) => e.webgpu_error_type(),
585            Self::InvalidResource(e) => e.webgpu_error_type(),
586        }
587    }
588}
589
590#[derive(Clone, Debug, Error)]
591#[non_exhaustive]
592pub enum QueueSubmitError {
593    #[error(transparent)]
594    Queue(#[from] DeviceError),
595    #[error(transparent)]
596    DestroyedResource(#[from] DestroyedResourceError),
597    #[error("{0} is still mapped")]
598    BufferStillMapped(ResourceErrorIdent),
599    #[error(transparent)]
600    InvalidResource(#[from] InvalidResourceError),
601    #[error(transparent)]
602    CommandEncoder(#[from] CommandEncoderError),
603    #[error(transparent)]
604    ValidateAsActionsError(#[from] crate::ray_tracing::ValidateAsActionsError),
605}
606
607impl From<InvalidOrDestroyedResourceError> for QueueSubmitError {
608    fn from(e: InvalidOrDestroyedResourceError) -> Self {
609        match e {
610            InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
611            InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
612        }
613    }
614}
615
616impl WebGpuError for QueueSubmitError {
617    fn webgpu_error_type(&self) -> ErrorType {
618        match self {
619            Self::Queue(e) => e.webgpu_error_type(),
620            Self::CommandEncoder(e) => e.webgpu_error_type(),
621            Self::ValidateAsActionsError(e) => e.webgpu_error_type(),
622            Self::InvalidResource(e) => e.webgpu_error_type(),
623            Self::DestroyedResource(_) | Self::BufferStillMapped(_) => ErrorType::Validation,
624        }
625    }
626}
627
628/// A command submission in the process of being assembled.
629///
630/// Within `wgpu_core`, enqueuing commands for execution on the GPU is a
631/// three-step process:
632///
633/// 1) Call [`Queue::allocate_submission`] to acquire the necessary locks,
634///    assign a submission index, wrap them all up as a [`PendingSubmission`],
635///    and return it.
636///
637/// 2) Add the command buffers to be submitted to [`executions`], and note any
638///    surface textures they reference in [`surface_textures`]. Contribute to
639///    [`Queue::pending_writes`] as necessary.
640///
641/// 3) Acquire the pending writes lock. This may be done at any point between
642///    the return from [`Queue::allocate_submission`] and the call to
643///    [`submit`]. Typically it should be done as late as is possible given
644///    any necessary pending writes activity.
645///
646/// 4) Call the `PendingSubmission`'s [`submit`] method (which is a convenience
647///    wrapper around [`Queue::submit_pending_submission`]). Pass the pending
648///    writes mutex guard to [`submit`].
649///
650/// It is also acceptable to drop the `PendingSubmission` without submitting;
651/// this frees its locks in the appropriate order. This may be necessary when
652/// those locks are required to access the state that determines whether a
653/// submission is needed at all.
654///
655/// This split allows the various places in `wgpu_core` that need to submit
656/// commands to the GPU to share the common initial code for locking and final
657/// code for actually submitting the commands to `wgpu_hal`:
658///
659/// - [`Queue::submit`] just submits user-constructed [`CommandBuffer`]s.
660///
661/// - [`Queue::prepare_surface_texture_for_present`] examines the surface
662///   texture being presented, and submits deferred initialization commands and
663///   barriers to get it ready.
664///
665/// - [`Queue::flush_writes_for_buffer`] and [`Queue::flush_pending_writes`]
666///   simply submit the operations already staged in [`Queue::pending_writes`].
667///
668/// Returned from [`Queue::allocate_submission`] and consumed by [`submit`].
669/// These are internal APIs used in `Queue::submit` and other places within
670/// `wgpu-core` that need to submit work.
671///
672/// [`submit`]: PendingSubmission::submit
673/// [`executions`]: PendingSubmission::executions
674/// [`surface_textures`]: PendingSubmission::surface_textures
675pub(crate) struct PendingSubmission<'a> {
676    queue: &'a Queue,
677
678    // These lock guards must appear in this struct in the order given.
679    //
680    // The instrumented locks in [`lock::ranked`] require that locks be acquired
681    // and released in a stack-like order. Since `rank::DEVICE_COMMAND_INDICES`
682    // follows `rank::DEVICE_SNATCHABLE_LOCK`, the lock on
683    // `Device::command_indices` must be released before the lock on
684    // `Device::snatchable_lock`. Rust drops struct members from first to last,
685    // so this ordering of fields ensures the order we want.
686    /// A guard for the lock on `Device::command_indices`.
687    command_index_guard: RwLockWriteGuard<'a, CommandIndices>,
688
689    /// A guard for the lock on `Device::snatchable_lock`.
690    snatch_guard: SnatchGuard<'a>,
691
692    /// Command buffers to be submitted, along with trackers for the resources
693    /// they use.
694    pub executions: Vec<EncoderInFlight>,
695
696    /// Surface textures referenced by command buffers in this submission.
697    ///
698    /// These need to be passed to [`wgpu_hal::Queue::submit`], which
699    /// requires that the list contains no duplicates, so we store them in a
700    /// `HashMap` keyed by `SurfaceTexture` address.
701    surface_textures: FastHashMap<*const Texture, Arc<Texture>>,
702
703    /// The index this submission has been assigned.
704    pub index: SubmissionIndex,
705}
706
707pub(crate) struct SubmissionResult<'a> {
708    pub snatch_guard: SnatchGuard<'a>,
709}
710
711impl<'a> PendingSubmission<'a> {
712    fn submit(
713        self,
714        pending_writes: MutexGuard<'a, PendingWrites>,
715    ) -> Result<SubmissionResult<'a>, DeviceError> {
716        self.queue.submit_pending_submission(pending_writes, self)
717    }
718}
719
720//TODO: move out common parts of write_xxx.
721
722impl Queue {
723    pub(crate) fn write_buffer_inner(
724        &self,
725        buffer: Arc<Buffer>,
726        buffer_offset: wgt::BufferAddress,
727        data: &[u8],
728    ) -> Result<(), QueueWriteError> {
729        profiling::scope!("Queue::write_buffer");
730        api_log!("Queue::write_buffer");
731
732        #[cfg(feature = "trace")]
733        if let Some(ref mut trace) = *self.device.trace.lock() {
734            use crate::device::trace::DataKind;
735            let size = data.len() as u64;
736            let data = trace.make_binary(DataKind::Bin, data);
737            trace.add(Action::WriteBuffer {
738                id: buffer.to_trace(),
739                data,
740                offset: buffer_offset,
741                size,
742                queued: true,
743            });
744        }
745
746        buffer.check_is_valid()?;
747        self.device.check_is_valid()?;
748
749        let data_size = data.len() as wgt::BufferAddress;
750
751        self.same_device_as(buffer.as_ref())?;
752
753        let data_size = if let Some(data_size) = wgt::BufferSize::new(data_size) {
754            data_size
755        } else {
756            // even though a zero-length write is a no-op and no copy operation will occur,
757            // we must still validate the copy operation. This ensures that invalid
758            // API calls—like writing to a mapped buffer or out-of-bounds offsets—are
759            // caught consistently, even if no data is actually moved.
760            self.validate_write_buffer_impl(buffer.as_ref(), buffer_offset, 0)?;
761
762            log::trace!("Ignoring write_buffer of size 0");
763            return Ok(());
764        };
765
766        // Platform validation requires that the staging buffer always be
767        // freed, even if an error occurs. All paths from here must call
768        // `device.pending_writes.consume`.
769        let mut staging_buffer = StagingBuffer::new(&self.device, data_size)?;
770
771        let staging_buffer = {
772            profiling::scope!("copy");
773            staging_buffer.write_exact(data);
774            staging_buffer.flush()
775        };
776
777        let snatch_guard = self.device.snatchable_lock.read();
778        let mut pending_writes = self.pending_writes.lock();
779
780        let result = self.write_staging_buffer_impl(
781            &snatch_guard,
782            &mut pending_writes,
783            &staging_buffer,
784            buffer,
785            buffer_offset,
786        );
787
788        pending_writes.consume(staging_buffer);
789
790        result
791    }
792
793    pub fn write_buffer(
794        &self,
795        buffer: Arc<Buffer>,
796        buffer_offset: wgt::BufferAddress,
797        data: &[u8],
798    ) {
799        if let Err(error) = self.write_buffer_inner(buffer, buffer_offset, data) {
800            self.device
801                .handle_error(error, Some(self.label()), "Queue::write_buffer");
802        }
803    }
804
805    pub fn create_staging_buffer(
806        &self,
807        buffer_size: wgt::BufferSize,
808    ) -> Result<(StagingBuffer, NonNull<u8>), QueueWriteError> {
809        profiling::scope!("Queue::create_staging_buffer");
810        resource_log!("Queue::create_staging_buffer");
811
812        self.device.check_is_valid()?;
813
814        let staging_buffer = StagingBuffer::new(&self.device, buffer_size)?;
815        let ptr = unsafe { staging_buffer.ptr() };
816
817        Ok((staging_buffer, ptr))
818    }
819
820    pub fn write_staging_buffer(
821        &self,
822        buffer: Arc<Buffer>,
823        buffer_offset: wgt::BufferAddress,
824        staging_buffer: StagingBuffer,
825    ) -> Result<(), QueueWriteError> {
826        profiling::scope!("Queue::write_staging_buffer");
827
828        buffer.check_is_valid()?;
829        self.device.check_is_valid()?;
830
831        // At this point, we have taken ownership of the staging_buffer from the
832        // user. Platform validation requires that the staging buffer always
833        // be freed, even if an error occurs. All paths from here must call
834        // `device.pending_writes.consume`.
835        let staging_buffer = staging_buffer.flush();
836
837        let snatch_guard = self.device.snatchable_lock.read();
838        let mut pending_writes = self.pending_writes.lock();
839
840        let result = self.write_staging_buffer_impl(
841            &snatch_guard,
842            &mut pending_writes,
843            &staging_buffer,
844            buffer,
845            buffer_offset,
846        );
847
848        drop(snatch_guard);
849
850        pending_writes.consume(staging_buffer);
851
852        drop(pending_writes);
853
854        result
855    }
856
857    pub fn validate_write_buffer(
858        &self,
859        buffer: Arc<Buffer>,
860        buffer_offset: u64,
861        buffer_size: wgt::BufferSize,
862    ) -> Result<(), QueueWriteError> {
863        profiling::scope!("Queue::validate_write_buffer");
864
865        self.device.check_is_valid()?;
866        buffer.check_is_valid()?;
867
868        self.validate_write_buffer_impl(&buffer, buffer_offset, buffer_size.into())?;
869
870        Ok(())
871    }
872
873    fn validate_write_buffer_impl(
874        &self,
875        buffer: &Buffer,
876        buffer_offset: u64,
877        buffer_size: u64,
878    ) -> Result<(), TransferError> {
879        if !matches!(&*buffer.map_state.lock(), BufferMapState::Idle) {
880            return Err(TransferError::BufferNotAvailable);
881        }
882        buffer.check_usage(wgt::BufferUsages::COPY_DST)?;
883        if !buffer_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
884            return Err(TransferError::UnalignedCopySize(buffer_size));
885        }
886        if !buffer_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
887            return Err(TransferError::UnalignedBufferOffset(buffer_offset));
888        }
889
890        if buffer_offset > buffer.size {
891            return Err(TransferError::BufferStartOffsetOverrun {
892                start_offset: buffer_offset,
893                buffer_size: buffer.size,
894                side: CopySide::Destination,
895            });
896        }
897        if buffer_size > buffer.size - buffer_offset {
898            return Err(TransferError::BufferEndOffsetOverrun {
899                start_offset: buffer_offset,
900                size: buffer_size,
901                buffer_size: buffer.size,
902                side: CopySide::Destination,
903            });
904        }
905
906        Ok(())
907    }
908
909    fn write_staging_buffer_impl(
910        &self,
911        snatch_guard: &SnatchGuard,
912        pending_writes: &mut PendingWrites,
913        staging_buffer: &FlushedStagingBuffer,
914        buffer: Arc<Buffer>,
915        buffer_offset: u64,
916    ) -> Result<(), QueueWriteError> {
917        self.device.check_is_valid()?;
918
919        let transition = {
920            let mut trackers = self.device.trackers.lock();
921            trackers
922                .buffers
923                .set_single(&buffer, wgt::BufferUses::COPY_DST)
924        };
925
926        let dst_raw = buffer.try_raw(snatch_guard)?;
927
928        self.same_device_as(buffer.as_ref())?;
929
930        self.validate_write_buffer_impl(&buffer, buffer_offset, staging_buffer.size.into())?;
931
932        let region = hal::BufferCopy {
933            src_offset: 0,
934            dst_offset: buffer_offset,
935            size: staging_buffer.size,
936        };
937        let barriers = iter::once(hal::BufferBarrier {
938            buffer: staging_buffer.raw(),
939            usage: hal::StateTransition {
940                from: wgt::BufferUses::MAP_WRITE,
941                to: wgt::BufferUses::COPY_SRC,
942            },
943        })
944        .chain(transition.map(|pending| pending.into_hal(&buffer, snatch_guard)))
945        .collect::<Vec<_>>();
946        let encoder = pending_writes.activate();
947        unsafe {
948            encoder.transition_buffers(&barriers);
949            encoder.copy_buffer_to_buffer(staging_buffer.raw(), dst_raw, &[region]);
950        }
951
952        pending_writes.insert_buffer(&buffer);
953
954        // Ensure the overwritten bytes are marked as initialized so
955        // they don't need to be nulled prior to mapping or binding.
956        {
957            buffer
958                .initialization_status
959                .write()
960                .drain(buffer_offset..(buffer_offset + staging_buffer.size.get()));
961        }
962
963        Ok(())
964    }
965
966    pub fn write_texture_inner(
967        &self,
968        destination: wgt::TexelCopyTextureInfo<Arc<Texture>>,
969        data: &[u8],
970        data_layout: &wgt::TexelCopyBufferLayout,
971        size: &wgt::Extent3d,
972    ) -> Result<(), QueueWriteError> {
973        profiling::scope!("Queue::write_texture");
974        api_log!("Queue::write_texture");
975
976        #[cfg(feature = "trace")]
977        if let Some(ref mut trace) = *self.device.trace.lock() {
978            use crate::device::trace::DataKind;
979            let data = trace.make_binary(DataKind::Bin, data);
980            trace.add(Action::WriteTexture {
981                to: destination.to_trace(),
982                data,
983                layout: *data_layout,
984                size: *size,
985            });
986        }
987
988        self.device.check_is_valid()?;
989
990        let dst = destination.texture;
991        let destination = wgt::TexelCopyTextureInfo {
992            texture: (),
993            mip_level: destination.mip_level,
994            origin: destination.origin,
995            aspect: destination.aspect,
996        };
997
998        self.same_device_as(dst.as_ref())?;
999
1000        dst.check_valid()?;
1001
1002        dst.check_usage(wgt::TextureUsages::COPY_DST)
1003            .map_err(TransferError::MissingTextureUsage)?;
1004
1005        // Note: Doing the copy range validation early is important because ensures that the
1006        // dimensions are not going to cause overflow in other parts of the validation.
1007        let (hal_copy_size, array_layer_count) =
1008            validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, size)?;
1009
1010        let (selector, dst_base) = extract_texture_selector(&destination, size, &dst)?;
1011
1012        validate_texture_copy_dst_format(dst.desc.format, destination.aspect)?;
1013
1014        validate_texture_buffer_copy(
1015            &destination,
1016            dst_base.aspect,
1017            &dst.desc,
1018            data_layout,
1019            false, // alignment not required for buffer offset or bytes per row
1020        )?;
1021
1022        // Note: `_source_bytes_per_array_layer` is ignored since we
1023        // have a staging copy, and it can have a different value.
1024        let (required_bytes_in_copy, _source_bytes_per_array_layer, _) =
1025            validate_linear_texture_data(
1026                data_layout,
1027                dst.desc.format,
1028                destination.aspect,
1029                data.len() as wgt::BufferAddress,
1030                CopySide::Source,
1031                size,
1032            )?;
1033
1034        if dst.desc.format.is_depth_stencil_format() {
1035            self.device
1036                .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
1037                .map_err(TransferError::from)?;
1038        }
1039
1040        let snatch_guard = self.device.snatchable_lock.read();
1041
1042        let dst_raw = dst.try_inner(&snatch_guard)?.raw();
1043
1044        // This must happen after parameter validation (so that errors are reported
1045        // as required by the spec), but before any side effects.
1046        if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
1047            log::trace!("Ignoring write_texture of size 0");
1048            return Ok(());
1049        }
1050
1051        let mut pending_writes = self.pending_writes.lock();
1052        let encoder = pending_writes.activate();
1053
1054        // If the copy does not fully cover the layers, we need to initialize to
1055        // zero *first* as we don't keep track of partial texture layer inits.
1056        //
1057        // Strictly speaking we only need to clear the areas of a layer
1058        // untouched, but this would get increasingly messy.
1059        let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
1060            // volume textures don't have a layer range as array volumes aren't supported
1061            0..1
1062        } else {
1063            destination.origin.z..destination.origin.z + size.depth_or_array_layers
1064        };
1065        let layer_ranges_to_clear = {
1066            let mut dst_initialization_status = dst.initialization_status.write();
1067            if dst_initialization_status.mips[destination.mip_level as usize]
1068                .check(init_layer_range.clone())
1069                .is_some()
1070            {
1071                if has_copy_partial_init_tracker_coverage(size, &destination, &dst.desc) {
1072                    dst_initialization_status.mips[destination.mip_level as usize]
1073                        .drain(init_layer_range)
1074                        .collect::<Vec<core::ops::Range<u32>>>()
1075                } else {
1076                    dst_initialization_status.mips[destination.mip_level as usize]
1077                        .drain(init_layer_range);
1078                    vec![]
1079                }
1080            } else {
1081                vec![]
1082            }
1083        };
1084        if !layer_ranges_to_clear.is_empty() {
1085            let mut trackers = self.device.trackers.lock();
1086            for layer_range in layer_ranges_to_clear {
1087                crate::command::clear_texture(
1088                    &dst,
1089                    TextureInitRange {
1090                        mip_range: destination.mip_level..(destination.mip_level + 1),
1091                        layer_range,
1092                    },
1093                    None,
1094                    encoder,
1095                    &mut trackers.textures,
1096                    &self.device.alignments,
1097                    self.device.zero_buffer.as_ref(),
1098                    &snatch_guard,
1099                    self.device.instance_flags,
1100                )
1101                .map_err(QueueWriteError::from)?;
1102            }
1103        }
1104
1105        let (block_width, block_height) = dst.desc.format.block_dimensions();
1106        let width_in_blocks = size.width / block_width;
1107        let height_in_blocks = size.height / block_height;
1108
1109        let block_size = dst
1110            .desc
1111            .format
1112            .block_copy_size(Some(destination.aspect))
1113            .unwrap();
1114        let bytes_in_last_row = width_in_blocks * block_size;
1115
1116        let bytes_per_row = data_layout.bytes_per_row.unwrap_or(bytes_in_last_row);
1117        let rows_per_image = data_layout.rows_per_image.unwrap_or(height_in_blocks);
1118
1119        let bytes_per_row_alignment = get_lowest_common_denom(
1120            self.device.alignments.buffer_copy_pitch.get() as u32,
1121            block_size,
1122        );
1123        assert!(u32::MAX - bytes_in_last_row >= bytes_per_row_alignment);
1124        let stage_bytes_per_row = wgt::math::align_to(bytes_in_last_row, bytes_per_row_alignment);
1125
1126        // Platform validation requires that the staging buffer always be
1127        // freed, even if an error occurs. All paths from here must call
1128        // `device.pending_writes.consume`.
1129        let staging_buffer = if stage_bytes_per_row == bytes_per_row {
1130            profiling::scope!("copy aligned");
1131            // Fast path if the data is already being aligned optimally.
1132            let stage_size = wgt::BufferSize::new(required_bytes_in_copy).unwrap();
1133            let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
1134            staging_buffer
1135                .write_exact(&data[data_layout.offset as usize..][..stage_size.get() as usize]);
1136            staging_buffer
1137        } else {
1138            profiling::scope!("copy chunked");
1139            // Copy row by row into the optimal alignment.
1140            let block_rows_in_copy = u64::from(size.depth_or_array_layers - 1)
1141                * u64::from(rows_per_image)
1142                + u64::from(height_in_blocks);
1143            // The copy size was validated against the source buffer, however,
1144            // `stage_bytes_per_row` can differ, so let's be paranoid.
1145            let stage_size = u64::from(stage_bytes_per_row)
1146                .checked_mul(block_rows_in_copy)
1147                .and_then(wgt::BufferSize::new)
1148                .unwrap();
1149            let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
1150            for layer in 0..u64::from(size.depth_or_array_layers) {
1151                let rows_offset = layer * u64::from(rows_per_image);
1152                for row in rows_offset..rows_offset + u64::from(height_in_blocks) {
1153                    let src_offset = data_layout.offset + row * u64::from(bytes_per_row);
1154                    let dst_offset = row * u64::from(stage_bytes_per_row);
1155                    unsafe {
1156                        staging_buffer.write_with_offset(
1157                            data,
1158                            src_offset as isize,
1159                            dst_offset as isize,
1160                            bytes_in_last_row as usize,
1161                        )
1162                    }
1163                }
1164            }
1165            staging_buffer
1166        };
1167
1168        let staging_buffer = staging_buffer.flush();
1169
1170        let regions = (0..array_layer_count)
1171            .map(|array_layer_offset| {
1172                let mut texture_base = dst_base.clone();
1173                texture_base.array_layer += array_layer_offset;
1174                hal::BufferTextureCopy {
1175                    buffer_layout: wgt::TexelCopyBufferLayout {
1176                        offset: array_layer_offset as u64
1177                            * rows_per_image as u64
1178                            * stage_bytes_per_row as u64,
1179                        bytes_per_row: Some(stage_bytes_per_row),
1180                        rows_per_image: Some(rows_per_image),
1181                    },
1182                    texture_base,
1183                    size: hal_copy_size,
1184                }
1185            })
1186            .collect::<Vec<_>>();
1187
1188        {
1189            let buffer_barrier = hal::BufferBarrier {
1190                buffer: staging_buffer.raw(),
1191                usage: hal::StateTransition {
1192                    from: wgt::BufferUses::MAP_WRITE,
1193                    to: wgt::BufferUses::COPY_SRC,
1194                },
1195            };
1196
1197            let mut trackers = self.device.trackers.lock();
1198            let transition =
1199                trackers
1200                    .textures
1201                    .set_single(&dst, selector, wgt::TextureUses::COPY_DST);
1202            let texture_barriers = transition
1203                .map(|pending| pending.into_hal(dst_raw))
1204                .collect::<Vec<_>>();
1205
1206            unsafe {
1207                encoder.transition_textures(&texture_barriers);
1208                encoder.transition_buffers(&[buffer_barrier]);
1209                encoder.copy_buffer_to_texture(staging_buffer.raw(), dst_raw, &regions);
1210            }
1211        }
1212
1213        pending_writes.consume(staging_buffer);
1214        pending_writes.insert_texture(&dst);
1215
1216        Ok(())
1217    }
1218
1219    pub fn write_texture(
1220        &self,
1221        destination: wgt::TexelCopyTextureInfo<Arc<Texture>>,
1222        data: &[u8],
1223        data_layout: &wgt::TexelCopyBufferLayout,
1224        size: &wgt::Extent3d,
1225    ) {
1226        if let Err(error) = self.write_texture_inner(destination, data, data_layout, size) {
1227            self.device
1228                .handle_error(error, Some(self.label()), "Queue::write_texture");
1229        }
1230    }
1231
1232    #[cfg(webgl)]
1233    pub fn copy_external_image_to_texture(
1234        &self,
1235        source: &wgt::CopyExternalImageSourceInfo,
1236        destination: wgt::CopyExternalImageDestInfo<Arc<Texture>>,
1237        size: wgt::Extent3d,
1238    ) -> Result<(), QueueWriteError> {
1239        use crate::conv;
1240
1241        profiling::scope!("Queue::copy_external_image_to_texture");
1242
1243        self.device.check_is_valid()?;
1244
1245        let mut needs_flag = false;
1246        // `OffscreenCanvas` needs no downlevel flag: WebGL2's `texSubImage2D`
1247        // accepts it as a `TexImageSource` and the gles backend uploads it the
1248        // same way as `HTMLCanvasElement`.
1249        needs_flag |= source.origin != wgt::Origin2d::ZERO;
1250        needs_flag |= destination.color_space != wgt::PredefinedColorSpace::Srgb;
1251        #[allow(clippy::bool_comparison)]
1252        if matches!(source.source, wgt::ExternalImageSource::ImageBitmap(_)) {
1253            needs_flag |= source.flip_y != false;
1254            needs_flag |= destination.premultiplied_alpha != false;
1255        }
1256
1257        if needs_flag {
1258            self.device
1259                .require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES)
1260                .map_err(TransferError::from)?;
1261        }
1262
1263        let src_width = source.source.width();
1264        let src_height = source.source.height();
1265
1266        let dst = destination.texture;
1267        let premultiplied_alpha = destination.premultiplied_alpha;
1268        let destination = wgt::TexelCopyTextureInfo {
1269            texture: (),
1270            mip_level: destination.mip_level,
1271            origin: destination.origin,
1272            aspect: destination.aspect,
1273        };
1274
1275        dst.check_valid()?;
1276
1277        if !conv::is_valid_external_image_copy_dst_texture_format(dst.desc.format) {
1278            return Err(
1279                TransferError::ExternalCopyToForbiddenTextureFormat(dst.desc.format).into(),
1280            );
1281        }
1282        if dst.desc.dimension != wgt::TextureDimension::D2 {
1283            return Err(TransferError::InvalidDimensionExternal.into());
1284        }
1285        dst.check_usage(wgt::TextureUsages::COPY_DST | wgt::TextureUsages::RENDER_ATTACHMENT)
1286            .map_err(TransferError::MissingTextureUsage)?;
1287        if dst.desc.sample_count != 1 {
1288            return Err(TransferError::InvalidSampleCount {
1289                sample_count: dst.desc.sample_count,
1290            }
1291            .into());
1292        }
1293
1294        if source.origin.x > src_width || src_width - source.origin.x < size.width {
1295            return Err(TransferError::TextureOverrun {
1296                start_offset: source.origin.x,
1297                end_offset: source.origin.x.saturating_add(size.width),
1298                texture_size: src_width,
1299                dimension: crate::resource::TextureErrorDimension::X,
1300                side: CopySide::Source,
1301            }
1302            .into());
1303        }
1304        if source.origin.y > src_height || src_height - source.origin.y < size.height {
1305            return Err(TransferError::TextureOverrun {
1306                start_offset: source.origin.y,
1307                end_offset: source.origin.y.saturating_add(size.height),
1308                texture_size: src_height,
1309                dimension: crate::resource::TextureErrorDimension::Y,
1310                side: CopySide::Source,
1311            }
1312            .into());
1313        }
1314        if size.depth_or_array_layers != 1 {
1315            return Err(TransferError::TextureOverrun {
1316                start_offset: 0,
1317                end_offset: size.depth_or_array_layers,
1318                texture_size: 1,
1319                dimension: crate::resource::TextureErrorDimension::Z,
1320                side: CopySide::Source,
1321            }
1322            .into());
1323        }
1324
1325        // Note: Doing the copy range validation early is important because ensures that the
1326        // dimensions are not going to cause overflow in other parts of the validation.
1327        let (hal_copy_size, _) =
1328            validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, &size)?;
1329
1330        let (selector, dst_base) = extract_texture_selector(&destination, &size, &dst)?;
1331
1332        let snatch_guard = self.device.snatchable_lock.read();
1333
1334        let dst_raw = dst.try_raw(&snatch_guard)?;
1335
1336        // This must happen after parameter validation (so that errors are reported
1337        // as required by the spec), but before any side effects.
1338        if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
1339            log::trace!("Ignoring copy_external_image_to_texture of size 0");
1340            return Ok(());
1341        }
1342
1343        let mut pending_writes = self.pending_writes.lock();
1344        let encoder = pending_writes.activate();
1345
1346        // If the copy does not fully cover the layers, we need to initialize to
1347        // zero *first* as we don't keep track of partial texture layer inits.
1348        //
1349        // Strictly speaking we only need to clear the areas of a layer
1350        // untouched, but this would get increasingly messy.
1351        let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
1352            // volume textures don't have a layer range as array volumes aren't supported
1353            0..1
1354        } else {
1355            destination.origin.z..destination.origin.z + size.depth_or_array_layers
1356        };
1357        let layer_ranges_to_clear = {
1358            let mut dst_initialization_status = dst.initialization_status.write();
1359            if dst_initialization_status.mips[destination.mip_level as usize]
1360                .check(init_layer_range.clone())
1361                .is_some()
1362            {
1363                if has_copy_partial_init_tracker_coverage(&size, &destination, &dst.desc) {
1364                    dst_initialization_status.mips[destination.mip_level as usize]
1365                        .drain(init_layer_range)
1366                        .collect::<Vec<core::ops::Range<u32>>>()
1367                } else {
1368                    dst_initialization_status.mips[destination.mip_level as usize]
1369                        .drain(init_layer_range);
1370                    vec![]
1371                }
1372            } else {
1373                vec![]
1374            }
1375        };
1376        if !layer_ranges_to_clear.is_empty() {
1377            let mut trackers = self.device.trackers.lock();
1378            for layer_range in layer_ranges_to_clear {
1379                crate::command::clear_texture(
1380                    &dst,
1381                    TextureInitRange {
1382                        mip_range: destination.mip_level..(destination.mip_level + 1),
1383                        layer_range,
1384                    },
1385                    None,
1386                    encoder,
1387                    &mut trackers.textures,
1388                    &self.device.alignments,
1389                    self.device.zero_buffer.as_ref(),
1390                    &snatch_guard,
1391                    self.device.instance_flags,
1392                )
1393                .map_err(QueueWriteError::from)?;
1394            }
1395        }
1396
1397        let regions = hal::TextureCopy {
1398            src_base: hal::TextureCopyBase {
1399                mip_level: 0,
1400                array_layer: 0,
1401                origin: source.origin.to_3d(0),
1402                aspect: hal::FormatAspects::COLOR,
1403            },
1404            dst_base,
1405            size: hal_copy_size,
1406        };
1407
1408        let mut trackers = self.device.trackers.lock();
1409        let transitions = trackers
1410            .textures
1411            .set_single(&dst, selector, wgt::TextureUses::COPY_DST);
1412
1413        // `copy_external_image_to_texture` is exclusive to the WebGL backend.
1414        // Don't go through the `DynCommandEncoder` abstraction and directly to the WebGL backend.
1415        let encoder_webgl = encoder
1416            .as_any_mut()
1417            .downcast_mut::<hal::gles::CommandEncoder>()
1418            .unwrap();
1419        let dst_raw_webgl = dst_raw
1420            .as_any()
1421            .downcast_ref::<hal::gles::Texture>()
1422            .unwrap();
1423        let transitions_webgl = transitions.map(|pending| {
1424            let dyn_transition = pending.into_hal(dst_raw);
1425            hal::TextureBarrier {
1426                texture: dst_raw_webgl,
1427                range: dyn_transition.range,
1428                usage: dyn_transition.usage,
1429                queue_family_ownership_transfer: None,
1430            }
1431        });
1432
1433        use hal::CommandEncoder as _;
1434        unsafe {
1435            encoder_webgl.transition_textures(transitions_webgl);
1436            encoder_webgl.copy_external_image_to_texture(
1437                source,
1438                dst_raw_webgl,
1439                premultiplied_alpha,
1440                iter::once(regions),
1441            );
1442        }
1443
1444        pending_writes.insert_texture(&dst);
1445
1446        Ok(())
1447    }
1448
1449    /// Flush `PendingWrites` if it contains a write to `buffer`.
1450    pub fn flush_writes_for_buffer(
1451        &self,
1452        buffer: &Arc<Buffer>,
1453        snatch_guard: SnatchGuard,
1454    ) -> Result<(), BufferAccessError> {
1455        let submission = self
1456            .allocate_submission(snatch_guard)
1457            .map_err(|(_index, e)| e)?;
1458
1459        let pending_writes = self.pending_writes.lock();
1460        if !pending_writes.contains_buffer(buffer) {
1461            return Ok(());
1462        }
1463
1464        submission.submit(pending_writes)?;
1465
1466        Ok(())
1467    }
1468
1469    fn flush_pending_writes(&self) -> Result<Option<SubmissionIndex>, DeviceError> {
1470        let snatch_guard = self.device.snatchable_lock.read();
1471        let submission = self
1472            .allocate_submission(snatch_guard)
1473            .map_err(|(_index, e)| e)?;
1474        let submit_index = submission.index;
1475        let pending_writes = self.pending_writes.lock();
1476        if pending_writes.is_recording {
1477            submission.submit(pending_writes)?;
1478            Ok(Some(submit_index))
1479        } else {
1480            Ok(None)
1481        }
1482    }
1483
1484    #[cfg(feature = "trace")]
1485    fn trace_submission(
1486        &self,
1487        submit_index: SubmissionIndex,
1488        commands: Vec<crate::command::Command<crate::command::PointerReferences>>,
1489    ) {
1490        if let Some(ref mut trace) = *self.device.trace.lock() {
1491            trace.add(Action::Submit(submit_index, commands));
1492        }
1493    }
1494
1495    #[cfg(feature = "trace")]
1496    fn trace_failed_submission(
1497        &self,
1498        submit_index: SubmissionIndex,
1499        commands: Option<Vec<crate::command::Command<crate::command::PointerReferences>>>,
1500        error: String,
1501    ) {
1502        if let Some(ref mut trace) = *self.device.trace.lock() {
1503            trace.add(Action::FailedCommands {
1504                commands,
1505                failed_at_submit: Some(submit_index),
1506                error,
1507            });
1508        }
1509    }
1510
1511    fn submit_inner(
1512        &self,
1513        command_buffers: &[Arc<CommandBuffer>],
1514    ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {
1515        profiling::scope!("Queue::submit");
1516        api_log!("Queue::submit");
1517
1518        let snatch_guard = self.device.snatchable_lock.read();
1519        let mut submission = self
1520            .allocate_submission(snatch_guard)
1521            .map_err(|(index, e)| (index, e.into()))?;
1522        let submit_index = submission.index;
1523
1524        // If we encounter an error after we have started updating global state and before
1525        // successful submission, we must lose the device to avoid continuing with
1526        // potentially inaccurate resource state.
1527        let mut lose_device_on_error = false;
1528
1529        let res = 'error: {
1530            let mut used_surface_textures = track::TextureUsageScope::default();
1531            let mut baked_command_buffers = Vec::with_capacity(command_buffers.len());
1532
1533            if !command_buffers.is_empty() {
1534                profiling::scope!("prepare");
1535
1536                let mut first_error = None;
1537
1538                // We are required to invalidate all command buffers in both the success and
1539                // failure paths, so we `continue` after errors, and disallow `?`.
1540                #[deny(clippy::question_mark_used)]
1541                for command_buffer in command_buffers {
1542                    profiling::scope!("process command buffer");
1543
1544                    // we reset the used surface textures every time we use
1545                    // it, so make sure to set_size on it.
1546                    used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1547
1548                    // Anything other than our own submission work in the remainder of this
1549                    // function that attempts to use the WebGPU command buffer after this
1550                    // point, will find it vacant (invalid), and produce an error.
1551                    #[cfg_attr(not(feature = "trace"), expect(unused_mut))]
1552                    let mut cmd_buf_data = command_buffer.take_finished();
1553
1554                    if first_error.is_some() {
1555                        continue;
1556                    }
1557
1558                    #[cfg(feature = "trace")]
1559                    let trace_commands = cmd_buf_data
1560                        .as_mut()
1561                        .ok()
1562                        .and_then(|data| mem::take(&mut data.trace_commands));
1563
1564                    let mut baked = match cmd_buf_data {
1565                        Ok(cmd_buf_data) => {
1566                            let res = validate_command_buffer(
1567                                command_buffer,
1568                                self,
1569                                &cmd_buf_data,
1570                                &submission.snatch_guard,
1571                                &mut submission.surface_textures,
1572                                &mut used_surface_textures,
1573                                &mut submission.command_index_guard,
1574                            );
1575                            if let Err(err) = res {
1576                                #[cfg(feature = "trace")]
1577                                self.trace_failed_submission(
1578                                    submit_index,
1579                                    trace_commands,
1580                                    err.to_string(),
1581                                );
1582                                first_error.get_or_insert(err);
1583                                continue;
1584                            }
1585
1586                            #[cfg(feature = "trace")]
1587                            if let Some(commands) = trace_commands {
1588                                self.trace_submission(submit_index, commands);
1589                            }
1590
1591                            cmd_buf_data
1592                                .set_acceleration_structure_dependencies(&submission.snatch_guard);
1593                            cmd_buf_data.into_baked_commands()
1594                        }
1595                        Err(err) => {
1596                            #[cfg(feature = "trace")]
1597                            self.trace_failed_submission(
1598                                submit_index,
1599                                trace_commands,
1600                                err.to_string(),
1601                            );
1602                            first_error.get_or_insert(err.into());
1603                            continue;
1604                        }
1605                    };
1606
1607                    if let Err(e) = baked
1608                        .process_deferred_query_set_resolves(&self.device, &submission.snatch_guard)
1609                    {
1610                        break 'error Err(e.into());
1611                    }
1612
1613                    baked_command_buffers.push(baked);
1614                }
1615
1616                if let Some(first_error) = first_error {
1617                    break 'error Err(first_error);
1618                }
1619
1620                // At this point we have validated all the command buffers, and we start
1621                // making updates to global state. If we fail between here and successful
1622                // submission, we must lose the device, or else we could leave that global
1623                // state inaccurate or inconsistent.
1624                //
1625                // At time of writing, there were two kinds of errors that can occur in
1626                // this stage:
1627                //  - `hal` command encoding errors. These produce device loss in
1628                //    `handle_hal_error`, independent of what we do here.
1629                //  - Errors from texture initialization. The error cases that
1630                //    can actually occur should also be encoder errors, but we map
1631                //    everything to device loss, just in case.
1632                lose_device_on_error = true;
1633
1634                // Note: locking the trackers has to be done after the storages
1635                let mut trackers = self.device.trackers.lock();
1636
1637                for mut baked in baked_command_buffers {
1638                    profiling::scope!("process baked commands");
1639
1640                    // execute resource transitions
1641                    if let Err(e) = baked.encoder.open_pass(hal_label(
1642                        Some("(wgpu internal) Transit"),
1643                        self.device.instance_flags,
1644                    )) {
1645                        break 'error Err(e.into());
1646                    }
1647
1648                    baked.initialize_buffer_memory(&mut trackers, &submission.snatch_guard);
1649
1650                    let depth_slice_discards = match baked.initialize_texture_memory(
1651                        &mut trackers,
1652                        &self.device,
1653                        &submission.snatch_guard,
1654                    ) {
1655                        Ok(discards) => discards,
1656                        Err(e) => {
1657                            break 'error Err(QueueSubmitError::CommandEncoder(
1658                                CommandEncoderError::Clear(e),
1659                            ));
1660                        }
1661                    };
1662
1663                    //Note: stateless trackers are not merged:
1664                    // device already knows these resources exist.
1665                    CommandEncoder::insert_barriers_from_device_tracker(
1666                        baked.encoder.raw.as_mut(),
1667                        &mut trackers,
1668                        &baked.trackers,
1669                        &submission.snatch_guard,
1670                    );
1671
1672                    if let Err(e) = baked.encoder.close_and_push_front() {
1673                        break 'error Err(e.into());
1674                    }
1675
1676                    if !depth_slice_discards.is_empty() || !used_surface_textures.is_empty() {
1677                        if let Err(e) = baked.encoder.open_pass(hal_label(
1678                            Some("(wgpu internal) Finalize"),
1679                            self.device.instance_flags,
1680                        )) {
1681                            break 'error Err(e.into());
1682                        }
1683
1684                        if let Err(e) = baked.initialize_discarded_depth_slices(
1685                            depth_slice_discards,
1686                            &mut trackers,
1687                            &self.device,
1688                            &submission.snatch_guard,
1689                        ) {
1690                            break 'error Err(QueueSubmitError::CommandEncoder(
1691                                CommandEncoderError::Clear(e),
1692                            ));
1693                        }
1694
1695                        // Transition surface textures into `Present` state.
1696                        // Note: we could technically do it after all of the command buffers,
1697                        // but here we have a command encoder by hand, so it's easier to use it.
1698                        let texture_barriers = trackers
1699                            .textures
1700                            .set_from_usage_scope_and_drain_transitions(
1701                                &used_surface_textures,
1702                                &submission.snatch_guard,
1703                            )
1704                            .collect::<Vec<_>>();
1705                        unsafe {
1706                            baked.encoder.raw.transition_textures(&texture_barriers);
1707                        };
1708                        if let Err(e) = baked.encoder.close() {
1709                            break 'error Err(e.into());
1710                        }
1711                        used_surface_textures = track::TextureUsageScope::default();
1712                    }
1713
1714                    // done
1715                    submission.executions.push(EncoderInFlight {
1716                        inner: baked.encoder,
1717                        trackers: baked.trackers,
1718                        temp_resources: baked.temp_resources,
1719                        _indirect_draw_validation_resources: baked
1720                            .indirect_draw_validation_resources,
1721                        pending_buffers: FastHashMap::default(),
1722                        pending_textures: FastHashMap::default(),
1723                        pending_blas_s: FastHashMap::default(),
1724                    });
1725                }
1726            }
1727
1728            let pending_writes = self.pending_writes.lock();
1729
1730            let SubmissionResult { snatch_guard } = match submission.submit(pending_writes) {
1731                Ok(result) => result,
1732                Err(e) => break 'error Err(e.into()),
1733            };
1734
1735            profiling::scope!("cleanup");
1736
1737            // Failing in `Device::maintain` won't put resource state at risk, but returning
1738            // an error from a successful submission could be confusing, do we really want
1739            // to do that?
1740            lose_device_on_error = false;
1741
1742            // This will schedule destruction of all resources that are no longer needed
1743            // by the user but used in the command stream, among other things.
1744            // `device.maintain` consumes and will release the snatch guard.
1745            let (closures, result) = self.device.maintain(wgt::PollType::Poll, snatch_guard);
1746            match result {
1747                Ok(status) => {
1748                    debug_assert!(matches!(
1749                        status,
1750                        wgt::PollStatus::QueueEmpty | wgt::PollStatus::Poll
1751                    ));
1752                }
1753                Err(WaitIdleError::Device(err)) => break 'error Err(QueueSubmitError::Queue(err)),
1754                Err(WaitIdleError::WrongSubmissionIndex(..)) => {
1755                    unreachable!("Cannot get WrongSubmissionIndex from Poll")
1756                }
1757                Err(WaitIdleError::Timeout) => unreachable!("Cannot get Timeout from Poll"),
1758            };
1759
1760            Ok(closures)
1761        }; // 'error
1762
1763        let callbacks = match res {
1764            Ok(ok) => ok,
1765            Err(e) => {
1766                if lose_device_on_error {
1767                    self.device.lose("submission failed");
1768                }
1769                return Err((submit_index, e));
1770            }
1771        };
1772
1773        // the closures should execute with nothing locked!
1774        callbacks.fire();
1775
1776        self.device.lose_if_oom();
1777
1778        api_log!("Queue::submit returned submit index {submit_index}");
1779
1780        Ok(submit_index)
1781    }
1782
1783    pub fn submit(&self, command_buffers: &[Arc<CommandBuffer>]) -> SubmissionIndex {
1784        match self.submit_inner(command_buffers) {
1785            Ok(submit_index) => submit_index,
1786            Err((submit_index, e)) => {
1787                self.device
1788                    .handle_error(e, Some(self.label()), "Queue::submit");
1789                submit_index
1790            }
1791        }
1792    }
1793
1794    /// Allocate a submission index and prepare for a submission.
1795    ///
1796    /// This is an internal API used in [`Queue::submit`] and other places within
1797    /// `wgpu-core` that need to submit work.
1798    ///
1799    /// Returns the index and a [`PendingSubmission`].
1800    ///
1801    /// The caller passes in the already-acquired [`SnatchGuard`]. This function acquires
1802    /// the command index lock.
1803    ///
1804    /// The caller should update [`PendingSubmission::executions`] with details of the
1805    /// submission.
1806    ///
1807    /// To finalize and submit the submission, call [`PendingSubmission::submit`] (which is
1808    /// a convenience wrapper around [`Queue::submit_pending_submission`]).
1809    ///
1810    /// After calling this function and before submitting, the caller must acquire the
1811    /// pending writes lock, and pass it to `submit`.
1812    ///
1813    /// It is also acceptable to drop the `PendingSubmission` without submitting. This may
1814    /// be necessary when locks are required to access the state that determines whether a
1815    /// submission is needed.
1816    fn allocate_submission<'a>(
1817        &'a self,
1818        snatch_guard: SnatchGuard<'a>,
1819    ) -> Result<PendingSubmission<'a>, (SubmissionIndex, DeviceError)> {
1820        let mut command_index_guard = self.device.command_indices.write();
1821        command_index_guard.active_submission_index += 1;
1822        let index = command_index_guard.active_submission_index;
1823
1824        if let Err(e) = self.device.check_is_valid() {
1825            return Err((index, e));
1826        }
1827
1828        let submission = PendingSubmission {
1829            queue: self,
1830            command_index_guard,
1831            snatch_guard,
1832            executions: Vec::new(),
1833            surface_textures: FastHashMap::default(),
1834            index,
1835        };
1836
1837        Ok(submission)
1838    }
1839
1840    /// Finalize and submit a [`PendingSubmission`] that was returned by
1841    /// [`Queue::allocate_submission`].
1842    ///
1843    /// This is an internal API used in `Queue::submit` and other places within
1844    /// `wgpu-core` that need to submit work. See [`Queue::allocate_submission`]
1845    /// for more details.
1846    ///
1847    /// This function:
1848    ///
1849    /// - Performs a HAL submission of the pending writes command
1850    ///   encoder and any other command encoders that were added to the
1851    ///   [`PendingSubmission`].
1852    /// - Advances `last_successful_submission_index` and registers the
1853    ///   submission with the lifetime tracker.
1854    /// - Returns a [`SubmissionResult`], which contains the snatch guard.
1855    fn submit_pending_submission<'a>(
1856        &self,
1857        mut pending_writes: MutexGuard<'_, PendingWrites>,
1858        prepared: PendingSubmission<'a>,
1859    ) -> Result<SubmissionResult<'a>, DeviceError> {
1860        let PendingSubmission {
1861            queue: _,
1862            snatch_guard,
1863            command_index_guard,
1864            mut executions,
1865            mut surface_textures,
1866            index: submit_index,
1867        } = prepared;
1868
1869        let mut used_surface_textures = track::TextureUsageScope::default();
1870        used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1871        for texture in pending_writes.dst_textures.values() {
1872            match texture.try_inner(&snatch_guard) {
1873                Ok(TextureInner::Native { .. }) => {}
1874                Ok(TextureInner::Surface { .. }) => {
1875                    // Compare the Arcs by pointer as Textures don't implement Eq
1876                    surface_textures.insert(Arc::as_ptr(texture), texture.clone());
1877
1878                    unsafe {
1879                        used_surface_textures
1880                            .merge_single(texture, None, wgt::TextureUses::PRESENT)
1881                            .unwrap()
1882                    };
1883                }
1884                // The texture must not have been destroyed when its usage here was
1885                // encoded. If it was destroyed after that, then it was transferred
1886                // to `pending_writes.temp_resources` at the time of destruction, so
1887                // we are still okay to use it.
1888                Err(InvalidOrDestroyedResourceError::DestroyedResource(_)) => {}
1889                Err(InvalidOrDestroyedResourceError::InvalidResource(_)) => {
1890                    unreachable!()
1891                }
1892            }
1893        }
1894
1895        if !used_surface_textures.is_empty() {
1896            let mut trackers = self.device.trackers.lock();
1897
1898            let texture_barriers = trackers
1899                .textures
1900                .set_from_usage_scope_and_drain_transitions(&used_surface_textures, &snatch_guard)
1901                .collect::<Vec<_>>();
1902            unsafe {
1903                pending_writes
1904                    .command_encoder
1905                    .transition_textures(&texture_barriers);
1906            };
1907        }
1908
1909        match pending_writes.pre_submit(&self.device.command_allocator, &self.device, self) {
1910            Ok(Some(pending_execution)) => {
1911                executions.insert(0, pending_execution);
1912            }
1913            Ok(None) => {}
1914            Err(e) => return Err(e),
1915        }
1916        let hal_command_buffers = executions
1917            .iter()
1918            .flat_map(|e| e.inner.list.iter().map(|b| b.as_ref()))
1919            .collect::<Vec<_>>();
1920
1921        {
1922            let mut submit_surface_textures =
1923                SmallVec::<[&dyn hal::DynSurfaceTexture; 2]>::with_capacity(surface_textures.len());
1924            for texture in surface_textures.values() {
1925                let raw = match texture.try_inner(&snatch_guard).ok() {
1926                    Some(TextureInner::Surface { raw, .. }) => raw.as_ref(),
1927                    _ => unreachable!(),
1928                };
1929                submit_surface_textures.push(raw);
1930            }
1931
1932            unsafe {
1933                self.raw().submit(
1934                    &hal_command_buffers,
1935                    &submit_surface_textures,
1936                    (self.device.fence.as_ref(), submit_index),
1937                )
1938            }
1939            .map_err(|e| self.device.handle_hal_error(e))?;
1940
1941            // Submissions must have strictly increasing indices, so we must hold the
1942            // command index guard until we have submitted, to prevent another submission
1943            // from claiming the next index and reaching `submit` before we do.
1944            drop(pending_writes);
1945
1946            // Advance the successful submission index.
1947            self.device
1948                .last_successful_submission_index
1949                .fetch_max(submit_index, Ordering::SeqCst);
1950        }
1951
1952        // this will register the new submission to the life time tracker
1953        self.lock_life().track_submission(submit_index, executions);
1954
1955        // `device.maintain` relies on being able to prevent new submissions by
1956        // using `command_index_guard` while also checking whether there are
1957        // no tracked submissions to guarantee no new submissions will happen
1958        // after a device is lost. This requires `command_index_guard` to be
1959        // held over `self.lock_life()`
1960        drop(command_index_guard);
1961
1962        Ok(SubmissionResult { snatch_guard })
1963    }
1964
1965    pub(crate) fn get_raw_timestamp_period(&self) -> f32 {
1966        unsafe { self.raw().get_timestamp_period() }
1967    }
1968
1969    pub fn get_timestamp_period(&self) -> f32 {
1970        if self.device.timestamp_normalizer.get().unwrap().enabled() {
1971            return 1.0;
1972        }
1973
1974        self.get_raw_timestamp_period()
1975    }
1976
1977    /// `closure` is guaranteed to be called.
1978    pub fn on_submitted_work_done(
1979        &self,
1980        closure: SubmittedWorkDoneClosure,
1981    ) -> Option<SubmissionIndex> {
1982        api_log!("Queue::on_submitted_work_done");
1983
1984        // A `DeviceError` means we're losing the device anyways, so we can ignore it here
1985        // (mostly to avoid a breaking change to the `on_submitted_work_done` signature
1986        // for an error case that it is unlikely the caller will be able to handle).
1987        let _: Result<_, DeviceError> = self.flush_pending_writes();
1988
1989        self.lock_life().add_work_done_closure(closure)
1990    }
1991
1992    #[allow(trivial_casts)]
1993    pub fn compact_blas(&self, blas: &Arc<Blas>) -> (Arc<Blas>, Option<CompactBlasError>) {
1994        api_log!(
1995            "Queue::compact_blas {:?}, {:?}",
1996            self as *const _,
1997            Arc::as_ptr(blas)
1998        );
1999
2000        let (blas, error) = match self.compact_blas_inner(blas) {
2001            Ok(blas) => (blas, None),
2002            Err(err) => {
2003                let new_label = blas.label.clone() + " (compacted)";
2004                (
2005                    Blas::invalid(
2006                        self.device.clone(),
2007                        &BlasDescriptor {
2008                            label: Some(new_label.into()),
2009                            flags: blas.flags,
2010                            update_mode: blas.update_mode,
2011                        },
2012                    ),
2013                    Some(err),
2014                )
2015            }
2016        };
2017
2018        // TODO: Tracing
2019
2020        (blas, error)
2021    }
2022
2023    pub(crate) fn compact_blas_inner(
2024        &self,
2025        blas: &Arc<Blas>,
2026    ) -> Result<Arc<Blas>, CompactBlasError> {
2027        profiling::scope!("Queue::compact_blas");
2028        api_log!("Queue::compact_blas");
2029
2030        let new_label = blas.label.clone() + " (compacted)";
2031
2032        self.device.check_is_valid()?;
2033
2034        self.device
2035            .require_features(wgpu_types::Features::EXPERIMENTAL_RAY_QUERY)?;
2036
2037        blas.check_is_valid()?;
2038        self.same_device_as(blas.as_ref())?;
2039
2040        let device = blas.device.clone();
2041
2042        let snatch_guard = device.snatchable_lock.read();
2043
2044        let BlasCompactState::Ready { size } = *blas.compacted_state.lock() else {
2045            return Err(CompactBlasError::BlasNotReady);
2046        };
2047
2048        let mut size_info = blas.size_info;
2049        size_info.acceleration_structure_size = size;
2050
2051        let mut command_indices_lock = device.command_indices.write();
2052        let mut pending_writes = self.pending_writes.lock();
2053        let cmd_buf_raw = pending_writes.activate();
2054
2055        let raw = unsafe {
2056            device
2057                .raw()
2058                .create_acceleration_structure(&hal::AccelerationStructureDescriptor {
2059                    label: hal_label(Some(&new_label), device.instance_flags),
2060                    size: size_info.acceleration_structure_size,
2061                    format: hal::AccelerationStructureFormat::BottomLevel,
2062                    allow_compaction: false,
2063                })
2064        }
2065        .map_err(DeviceError::from_hal)?;
2066
2067        let src_raw = blas.try_raw(&snatch_guard)?;
2068
2069        unsafe {
2070            cmd_buf_raw.copy_acceleration_structure_to_acceleration_structure(
2071                src_raw,
2072                raw.as_ref(),
2073                wgt::AccelerationStructureCopy::Compact,
2074            )
2075        };
2076
2077        let handle = unsafe {
2078            device
2079                .raw()
2080                .get_acceleration_structure_device_address(raw.as_ref())
2081        };
2082
2083        command_indices_lock.next_acceleration_structure_build_command_index += 1;
2084        let built_index =
2085            NonZeroU64::new(command_indices_lock.next_acceleration_structure_build_command_index)
2086                .unwrap();
2087
2088        let new_blas = Arc::new(Blas {
2089            state: ResourceState::Valid(BlasState {
2090                raw: Snatchable::new(raw),
2091            }),
2092            device: device.clone(),
2093            size_info,
2094            sizes: blas.sizes.clone(),
2095            flags: blas.flags & !AccelerationStructureFlags::ALLOW_COMPACTION,
2096            update_mode: blas.update_mode,
2097            // Bypass the submit checks which update this because we don't submit this normally.
2098            built_index: RwLock::new(rank::BLAS_BUILT_INDEX, Some(built_index)),
2099            handle,
2100            label: new_label,
2101            tracking_data: TrackingData::new(blas.device.tracker_indices.blas_s.clone()),
2102            compaction_buffer: None,
2103            compacted_state: Mutex::new(rank::BLAS_COMPACTION_STATE, BlasCompactState::Compacted),
2104        });
2105
2106        pending_writes.insert_blas(blas);
2107        pending_writes.insert_blas(&new_blas);
2108
2109        // We should have no more errors after this because we have marked the command encoder as successful.
2110        let old_blas_size = blas.size_info.acceleration_structure_size;
2111        let new_blas_size = new_blas.size_info.acceleration_structure_size;
2112
2113        api_log!("CommandEncoder::compact_blas {:?} (size: {old_blas_size}) -> {:?} (size: {new_blas_size})", Arc::as_ptr(blas), Arc::as_ptr(&new_blas));
2114
2115        Ok(new_blas)
2116    }
2117}
2118
2119fn validate_command_buffer(
2120    command_buffer: &CommandBuffer,
2121    queue: &Queue,
2122    cmd_buf_data: &crate::command::CommandBufferMutable,
2123    snatch_guard: &SnatchGuard,
2124    surface_textures: &mut FastHashMap<*const Texture, Arc<Texture>>,
2125    used_surface_textures: &mut track::TextureUsageScope,
2126    command_index_guard: &mut RwLockWriteGuard<CommandIndices>,
2127) -> Result<(), QueueSubmitError> {
2128    command_buffer.same_device_as(queue)?;
2129
2130    {
2131        profiling::scope!("check resource state");
2132
2133        {
2134            profiling::scope!("buffers");
2135            for buffer in cmd_buf_data.trackers.buffers.used_resources() {
2136                buffer.check_destroyed(snatch_guard)?;
2137
2138                match *buffer.map_state.lock() {
2139                    BufferMapState::Idle => (),
2140                    _ => return Err(QueueSubmitError::BufferStillMapped(buffer.error_ident())),
2141                }
2142            }
2143        }
2144        {
2145            profiling::scope!("textures");
2146            for texture in cmd_buf_data.trackers.textures.used_resources() {
2147                let should_extend = match texture.try_inner(snatch_guard)? {
2148                    TextureInner::Native { .. } => false,
2149                    TextureInner::Surface { .. } => {
2150                        // Compare the Arcs by pointer as Textures don't implement Eq.
2151                        surface_textures.insert(Arc::as_ptr(texture), texture.clone());
2152
2153                        true
2154                    }
2155                };
2156                if should_extend {
2157                    unsafe {
2158                        used_surface_textures
2159                            .merge_single(texture, None, wgt::TextureUses::PRESENT)
2160                            .unwrap();
2161                    };
2162                }
2163            }
2164        }
2165        {
2166            profiling::scope!("query sets");
2167            for query_set in cmd_buf_data.trackers.query_sets.used_resources() {
2168                query_set.try_raw(snatch_guard)?;
2169            }
2170        }
2171        // WebGPU requires that we check every bind group referenced during
2172        // encoding, even ones that may have been replaced before being used.
2173        // TODO(<https://github.com/gfx-rs/wgpu/issues/8510>): Optimize this.
2174        {
2175            profiling::scope!("bind groups");
2176            for bind_group in &cmd_buf_data.trackers.bind_groups {
2177                // This checks the bind group and all resources it references.
2178                bind_group.try_raw(snatch_guard)?;
2179            }
2180        }
2181
2182        if let Err(e) =
2183            cmd_buf_data.validate_acceleration_structure_actions(snatch_guard, command_index_guard)
2184        {
2185            return Err(e.into());
2186        }
2187    }
2188    Ok(())
2189}