wgpu_core/device/
queue.rs

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