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