wgpu_core/device/
queue.rs

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