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