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    snatch_guard: SnatchGuard<'a>,
642    command_index_guard: RwLockWriteGuard<'a, CommandIndices>,
643    // Command buffers to be executed, along with trackers for the resources they use.
644    pub executions: Vec<EncoderInFlight>,
645    // Surface textures referenced by command buffers in this submission. These need to be
646    // passed to the HAL `submit` call. Deduplicated using a hashmap to avoid vulkan
647    // deadlocking from the same surface texture being submitted multiple times.
648    surface_textures: FastHashMap<*const Texture, Arc<Texture>>,
649    pub index: SubmissionIndex,
650}
651
652pub(crate) struct SubmissionResult<'a> {
653    pub snatch_guard: SnatchGuard<'a>,
654}
655
656impl<'a> PendingSubmission<'a> {
657    fn submit(
658        self,
659        pending_writes: MutexGuard<'a, PendingWrites>,
660    ) -> Result<SubmissionResult<'a>, DeviceError> {
661        self.queue.submit_pending_submission(pending_writes, self)
662    }
663}
664
665//TODO: move out common parts of write_xxx.
666
667impl Queue {
668    pub fn write_buffer(
669        &self,
670        buffer: Arc<Buffer>,
671        buffer_offset: wgt::BufferAddress,
672        data: &[u8],
673    ) -> Result<(), QueueWriteError> {
674        profiling::scope!("Queue::write_buffer");
675        api_log!("Queue::write_buffer");
676
677        #[cfg(feature = "trace")]
678        if let Some(ref mut trace) = *self.device.trace.lock() {
679            use crate::device::trace::DataKind;
680            let size = data.len() as u64;
681            let data = trace.make_binary(DataKind::Bin, data);
682            trace.add(Action::WriteBuffer {
683                id: buffer.to_trace(),
684                data,
685                offset: buffer_offset,
686                size,
687                queued: true,
688            });
689        }
690
691        buffer.check_is_valid()?;
692        self.device.check_is_valid()?;
693
694        let data_size = data.len() as wgt::BufferAddress;
695
696        self.same_device_as(buffer.as_ref())?;
697
698        let data_size = if let Some(data_size) = wgt::BufferSize::new(data_size) {
699            data_size
700        } else {
701            // even though a zero-length write is a no-op and no copy operation will occur,
702            // we must still validate the copy operation. This ensures that invalid
703            // API calls—like writing to a mapped buffer or out-of-bounds offsets—are
704            // caught consistently, even if no data is actually moved.
705            self.validate_write_buffer_impl(buffer.as_ref(), buffer_offset, 0)?;
706
707            log::trace!("Ignoring write_buffer of size 0");
708            return Ok(());
709        };
710
711        // Platform validation requires that the staging buffer always be
712        // freed, even if an error occurs. All paths from here must call
713        // `device.pending_writes.consume`.
714        let mut staging_buffer = StagingBuffer::new(&self.device, data_size)?;
715
716        let staging_buffer = {
717            profiling::scope!("copy");
718            staging_buffer.write(data);
719            staging_buffer.flush()
720        };
721
722        let snatch_guard = self.device.snatchable_lock.read();
723        let mut pending_writes = self.pending_writes.lock();
724
725        let result = self.write_staging_buffer_impl(
726            &snatch_guard,
727            &mut pending_writes,
728            &staging_buffer,
729            buffer,
730            buffer_offset,
731        );
732
733        drop(snatch_guard);
734
735        pending_writes.consume(staging_buffer);
736
737        drop(pending_writes);
738
739        result
740    }
741
742    pub fn create_staging_buffer(
743        &self,
744        buffer_size: wgt::BufferSize,
745    ) -> Result<(StagingBuffer, NonNull<u8>), QueueWriteError> {
746        profiling::scope!("Queue::create_staging_buffer");
747        resource_log!("Queue::create_staging_buffer");
748
749        self.device.check_is_valid()?;
750
751        let staging_buffer = StagingBuffer::new(&self.device, buffer_size)?;
752        let ptr = unsafe { staging_buffer.ptr() };
753
754        Ok((staging_buffer, ptr))
755    }
756
757    pub fn write_staging_buffer(
758        &self,
759        buffer: Arc<Buffer>,
760        buffer_offset: wgt::BufferAddress,
761        staging_buffer: StagingBuffer,
762    ) -> Result<(), QueueWriteError> {
763        profiling::scope!("Queue::write_staging_buffer");
764
765        buffer.check_is_valid()?;
766        self.device.check_is_valid()?;
767
768        // At this point, we have taken ownership of the staging_buffer from the
769        // user. Platform validation requires that the staging buffer always
770        // be freed, even if an error occurs. All paths from here must call
771        // `device.pending_writes.consume`.
772        let staging_buffer = staging_buffer.flush();
773
774        let snatch_guard = self.device.snatchable_lock.read();
775        let mut pending_writes = self.pending_writes.lock();
776
777        let result = self.write_staging_buffer_impl(
778            &snatch_guard,
779            &mut pending_writes,
780            &staging_buffer,
781            buffer,
782            buffer_offset,
783        );
784
785        drop(snatch_guard);
786
787        pending_writes.consume(staging_buffer);
788
789        drop(pending_writes);
790
791        result
792    }
793
794    pub fn validate_write_buffer(
795        &self,
796        buffer: Arc<Buffer>,
797        buffer_offset: u64,
798        buffer_size: wgt::BufferSize,
799    ) -> Result<(), QueueWriteError> {
800        profiling::scope!("Queue::validate_write_buffer");
801
802        self.device.check_is_valid()?;
803        buffer.check_is_valid()?;
804
805        self.validate_write_buffer_impl(&buffer, buffer_offset, buffer_size.into())?;
806
807        Ok(())
808    }
809
810    fn validate_write_buffer_impl(
811        &self,
812        buffer: &Buffer,
813        buffer_offset: u64,
814        buffer_size: u64,
815    ) -> Result<(), TransferError> {
816        if !matches!(&*buffer.map_state.lock(), BufferMapState::Idle) {
817            return Err(TransferError::BufferNotAvailable);
818        }
819        buffer.check_usage(wgt::BufferUsages::COPY_DST)?;
820        if !buffer_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
821            return Err(TransferError::UnalignedCopySize(buffer_size));
822        }
823        if !buffer_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
824            return Err(TransferError::UnalignedBufferOffset(buffer_offset));
825        }
826
827        if buffer_offset > buffer.size {
828            return Err(TransferError::BufferStartOffsetOverrun {
829                start_offset: buffer_offset,
830                buffer_size: buffer.size,
831                side: CopySide::Destination,
832            });
833        }
834        if buffer_size > buffer.size - buffer_offset {
835            return Err(TransferError::BufferEndOffsetOverrun {
836                start_offset: buffer_offset,
837                size: buffer_size,
838                buffer_size: buffer.size,
839                side: CopySide::Destination,
840            });
841        }
842
843        Ok(())
844    }
845
846    fn write_staging_buffer_impl(
847        &self,
848        snatch_guard: &SnatchGuard,
849        pending_writes: &mut PendingWrites,
850        staging_buffer: &FlushedStagingBuffer,
851        buffer: Arc<Buffer>,
852        buffer_offset: u64,
853    ) -> Result<(), QueueWriteError> {
854        self.device.check_is_valid()?;
855
856        let transition = {
857            let mut trackers = self.device.trackers.lock();
858            trackers
859                .buffers
860                .set_single(&buffer, wgt::BufferUses::COPY_DST)
861        };
862
863        let dst_raw = buffer.try_raw(snatch_guard)?;
864
865        self.same_device_as(buffer.as_ref())?;
866
867        self.validate_write_buffer_impl(&buffer, buffer_offset, staging_buffer.size.into())?;
868
869        let region = hal::BufferCopy {
870            src_offset: 0,
871            dst_offset: buffer_offset,
872            size: staging_buffer.size,
873        };
874        let barriers = iter::once(hal::BufferBarrier {
875            buffer: staging_buffer.raw(),
876            usage: hal::StateTransition {
877                from: wgt::BufferUses::MAP_WRITE,
878                to: wgt::BufferUses::COPY_SRC,
879            },
880        })
881        .chain(transition.map(|pending| pending.into_hal(&buffer, snatch_guard)))
882        .collect::<Vec<_>>();
883        let encoder = pending_writes.activate();
884        unsafe {
885            encoder.transition_buffers(&barriers);
886            encoder.copy_buffer_to_buffer(staging_buffer.raw(), dst_raw, &[region]);
887        }
888
889        pending_writes.insert_buffer(&buffer);
890
891        // Ensure the overwritten bytes are marked as initialized so
892        // they don't need to be nulled prior to mapping or binding.
893        {
894            buffer
895                .initialization_status
896                .write()
897                .drain(buffer_offset..(buffer_offset + staging_buffer.size.get()));
898        }
899
900        Ok(())
901    }
902
903    pub fn write_texture(
904        &self,
905        destination: wgt::TexelCopyTextureInfo<Arc<Texture>>,
906        data: &[u8],
907        data_layout: &wgt::TexelCopyBufferLayout,
908        size: &wgt::Extent3d,
909    ) -> Result<(), QueueWriteError> {
910        profiling::scope!("Queue::write_texture");
911        api_log!("Queue::write_texture");
912
913        self.device.check_is_valid()?;
914
915        let dst = destination.texture;
916        let destination = wgt::TexelCopyTextureInfo {
917            texture: (),
918            mip_level: destination.mip_level,
919            origin: destination.origin,
920            aspect: destination.aspect,
921        };
922
923        self.same_device_as(dst.as_ref())?;
924
925        dst.check_valid()?;
926
927        dst.check_usage(wgt::TextureUsages::COPY_DST)
928            .map_err(TransferError::MissingTextureUsage)?;
929
930        // Note: Doing the copy range validation early is important because ensures that the
931        // dimensions are not going to cause overflow in other parts of the validation.
932        let (hal_copy_size, array_layer_count) =
933            validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, size)?;
934
935        let (selector, dst_base) = extract_texture_selector(&destination, size, &dst)?;
936
937        validate_texture_copy_dst_format(dst.desc.format, destination.aspect)?;
938
939        validate_texture_buffer_copy(
940            &destination,
941            dst_base.aspect,
942            &dst.desc,
943            data_layout,
944            false, // alignment not required for buffer offset or bytes per row
945        )?;
946
947        // Note: `_source_bytes_per_array_layer` is ignored since we
948        // have a staging copy, and it can have a different value.
949        let (required_bytes_in_copy, _source_bytes_per_array_layer, _) =
950            validate_linear_texture_data(
951                data_layout,
952                dst.desc.format,
953                destination.aspect,
954                data.len() as wgt::BufferAddress,
955                CopySide::Source,
956                size,
957            )?;
958
959        if dst.desc.format.is_depth_stencil_format() {
960            self.device
961                .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
962                .map_err(TransferError::from)?;
963        }
964
965        let snatch_guard = self.device.snatchable_lock.read();
966
967        let dst_raw = dst.try_inner(&snatch_guard)?.raw();
968
969        // This must happen after parameter validation (so that errors are reported
970        // as required by the spec), but before any side effects.
971        if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
972            log::trace!("Ignoring write_texture of size 0");
973            return Ok(());
974        }
975
976        let mut pending_writes = self.pending_writes.lock();
977        let encoder = pending_writes.activate();
978
979        // If the copy does not fully cover the layers, we need to initialize to
980        // zero *first* as we don't keep track of partial texture layer inits.
981        //
982        // Strictly speaking we only need to clear the areas of a layer
983        // untouched, but this would get increasingly messy.
984        let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
985            // volume textures don't have a layer range as array volumes aren't supported
986            0..1
987        } else {
988            destination.origin.z..destination.origin.z + size.depth_or_array_layers
989        };
990        let mut dst_initialization_status = dst.initialization_status.write();
991        if dst_initialization_status.mips[destination.mip_level as usize]
992            .check(init_layer_range.clone())
993            .is_some()
994        {
995            if has_copy_partial_init_tracker_coverage(size, &destination, &dst.desc) {
996                for layer_range in dst_initialization_status.mips[destination.mip_level as usize]
997                    .drain(init_layer_range)
998                    .collect::<Vec<core::ops::Range<u32>>>()
999                {
1000                    let mut trackers = self.device.trackers.lock();
1001                    crate::command::clear_texture(
1002                        &dst,
1003                        TextureInitRange {
1004                            mip_range: destination.mip_level..(destination.mip_level + 1),
1005                            layer_range,
1006                        },
1007                        encoder,
1008                        &mut trackers.textures,
1009                        &self.device.alignments,
1010                        self.device.zero_buffer.as_ref(),
1011                        &snatch_guard,
1012                        self.device.instance_flags,
1013                    )
1014                    .map_err(QueueWriteError::from)?;
1015                }
1016            } else {
1017                dst_initialization_status.mips[destination.mip_level as usize]
1018                    .drain(init_layer_range);
1019            }
1020        }
1021
1022        let (block_width, block_height) = dst.desc.format.block_dimensions();
1023        let width_in_blocks = size.width / block_width;
1024        let height_in_blocks = size.height / block_height;
1025
1026        let block_size = dst
1027            .desc
1028            .format
1029            .block_copy_size(Some(destination.aspect))
1030            .unwrap();
1031        let bytes_in_last_row = width_in_blocks * block_size;
1032
1033        let bytes_per_row = data_layout.bytes_per_row.unwrap_or(bytes_in_last_row);
1034        let rows_per_image = data_layout.rows_per_image.unwrap_or(height_in_blocks);
1035
1036        let bytes_per_row_alignment = get_lowest_common_denom(
1037            self.device.alignments.buffer_copy_pitch.get() as u32,
1038            block_size,
1039        );
1040        assert!(u32::MAX - bytes_in_last_row >= bytes_per_row_alignment);
1041        let stage_bytes_per_row = wgt::math::align_to(bytes_in_last_row, bytes_per_row_alignment);
1042
1043        // Platform validation requires that the staging buffer always be
1044        // freed, even if an error occurs. All paths from here must call
1045        // `device.pending_writes.consume`.
1046        let staging_buffer = if stage_bytes_per_row == bytes_per_row {
1047            profiling::scope!("copy aligned");
1048            // Fast path if the data is already being aligned optimally.
1049            let stage_size = wgt::BufferSize::new(required_bytes_in_copy).unwrap();
1050            let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
1051            staging_buffer.write(&data[data_layout.offset as usize..]);
1052            staging_buffer
1053        } else {
1054            profiling::scope!("copy chunked");
1055            // Copy row by row into the optimal alignment.
1056            let block_rows_in_copy = u64::from(size.depth_or_array_layers - 1)
1057                * u64::from(rows_per_image)
1058                + u64::from(height_in_blocks);
1059            // The copy size was validated against the source buffer, however,
1060            // `stage_bytes_per_row` can differ, so let's be paranoid.
1061            let stage_size = u64::from(stage_bytes_per_row)
1062                .checked_mul(block_rows_in_copy)
1063                .and_then(wgt::BufferSize::new)
1064                .unwrap();
1065            let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
1066            for layer in 0..u64::from(size.depth_or_array_layers) {
1067                let rows_offset = layer * u64::from(rows_per_image);
1068                for row in rows_offset..rows_offset + u64::from(height_in_blocks) {
1069                    let src_offset = data_layout.offset + row * u64::from(bytes_per_row);
1070                    let dst_offset = row * u64::from(stage_bytes_per_row);
1071                    unsafe {
1072                        staging_buffer.write_with_offset(
1073                            data,
1074                            src_offset as isize,
1075                            dst_offset as isize,
1076                            bytes_in_last_row as usize,
1077                        )
1078                    }
1079                }
1080            }
1081            staging_buffer
1082        };
1083
1084        let staging_buffer = staging_buffer.flush();
1085
1086        let regions = (0..array_layer_count)
1087            .map(|array_layer_offset| {
1088                let mut texture_base = dst_base.clone();
1089                texture_base.array_layer += array_layer_offset;
1090                hal::BufferTextureCopy {
1091                    buffer_layout: wgt::TexelCopyBufferLayout {
1092                        offset: array_layer_offset as u64
1093                            * rows_per_image as u64
1094                            * stage_bytes_per_row as u64,
1095                        bytes_per_row: Some(stage_bytes_per_row),
1096                        rows_per_image: Some(rows_per_image),
1097                    },
1098                    texture_base,
1099                    size: hal_copy_size,
1100                }
1101            })
1102            .collect::<Vec<_>>();
1103
1104        {
1105            let buffer_barrier = hal::BufferBarrier {
1106                buffer: staging_buffer.raw(),
1107                usage: hal::StateTransition {
1108                    from: wgt::BufferUses::MAP_WRITE,
1109                    to: wgt::BufferUses::COPY_SRC,
1110                },
1111            };
1112
1113            let mut trackers = self.device.trackers.lock();
1114            let transition =
1115                trackers
1116                    .textures
1117                    .set_single(&dst, selector, wgt::TextureUses::COPY_DST);
1118            let texture_barriers = transition
1119                .map(|pending| pending.into_hal(dst_raw))
1120                .collect::<Vec<_>>();
1121
1122            unsafe {
1123                encoder.transition_textures(&texture_barriers);
1124                encoder.transition_buffers(&[buffer_barrier]);
1125                encoder.copy_buffer_to_texture(staging_buffer.raw(), dst_raw, &regions);
1126            }
1127        }
1128
1129        pending_writes.consume(staging_buffer);
1130        pending_writes.insert_texture(&dst);
1131
1132        Ok(())
1133    }
1134
1135    #[cfg(webgl)]
1136    pub fn copy_external_image_to_texture(
1137        &self,
1138        source: &wgt::CopyExternalImageSourceInfo,
1139        destination: wgt::CopyExternalImageDestInfo<Arc<Texture>>,
1140        size: wgt::Extent3d,
1141    ) -> Result<(), QueueWriteError> {
1142        use crate::conv;
1143
1144        profiling::scope!("Queue::copy_external_image_to_texture");
1145
1146        self.device.check_is_valid()?;
1147
1148        let mut needs_flag = false;
1149        needs_flag |= matches!(source.source, wgt::ExternalImageSource::OffscreenCanvas(_));
1150        needs_flag |= source.origin != wgt::Origin2d::ZERO;
1151        needs_flag |= destination.color_space != wgt::PredefinedColorSpace::Srgb;
1152        #[allow(clippy::bool_comparison)]
1153        if matches!(source.source, wgt::ExternalImageSource::ImageBitmap(_)) {
1154            needs_flag |= source.flip_y != false;
1155            needs_flag |= destination.premultiplied_alpha != false;
1156        }
1157
1158        if needs_flag {
1159            self.device
1160                .require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES)
1161                .map_err(TransferError::from)?;
1162        }
1163
1164        let src_width = source.source.width();
1165        let src_height = source.source.height();
1166
1167        let dst = destination.texture;
1168        let premultiplied_alpha = destination.premultiplied_alpha;
1169        let destination = wgt::TexelCopyTextureInfo {
1170            texture: (),
1171            mip_level: destination.mip_level,
1172            origin: destination.origin,
1173            aspect: destination.aspect,
1174        };
1175
1176        dst.check_valid()?;
1177
1178        if !conv::is_valid_external_image_copy_dst_texture_format(dst.desc.format) {
1179            return Err(
1180                TransferError::ExternalCopyToForbiddenTextureFormat(dst.desc.format).into(),
1181            );
1182        }
1183        if dst.desc.dimension != wgt::TextureDimension::D2 {
1184            return Err(TransferError::InvalidDimensionExternal.into());
1185        }
1186        dst.check_usage(wgt::TextureUsages::COPY_DST | wgt::TextureUsages::RENDER_ATTACHMENT)
1187            .map_err(TransferError::MissingTextureUsage)?;
1188        if dst.desc.sample_count != 1 {
1189            return Err(TransferError::InvalidSampleCount {
1190                sample_count: dst.desc.sample_count,
1191            }
1192            .into());
1193        }
1194
1195        if source.origin.x > src_width || src_width - source.origin.x < size.width {
1196            return Err(TransferError::TextureOverrun {
1197                start_offset: source.origin.x,
1198                end_offset: source.origin.x.saturating_add(size.width),
1199                texture_size: src_width,
1200                dimension: crate::resource::TextureErrorDimension::X,
1201                side: CopySide::Source,
1202            }
1203            .into());
1204        }
1205        if source.origin.y > src_height || src_height - source.origin.y < size.height {
1206            return Err(TransferError::TextureOverrun {
1207                start_offset: source.origin.y,
1208                end_offset: source.origin.y.saturating_add(size.height),
1209                texture_size: src_height,
1210                dimension: crate::resource::TextureErrorDimension::Y,
1211                side: CopySide::Source,
1212            }
1213            .into());
1214        }
1215        if size.depth_or_array_layers != 1 {
1216            return Err(TransferError::TextureOverrun {
1217                start_offset: 0,
1218                end_offset: size.depth_or_array_layers,
1219                texture_size: 1,
1220                dimension: crate::resource::TextureErrorDimension::Z,
1221                side: CopySide::Source,
1222            }
1223            .into());
1224        }
1225
1226        // Note: Doing the copy range validation early is important because ensures that the
1227        // dimensions are not going to cause overflow in other parts of the validation.
1228        let (hal_copy_size, _) =
1229            validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, &size)?;
1230
1231        let (selector, dst_base) = extract_texture_selector(&destination, &size, &dst)?;
1232
1233        // This must happen after parameter validation (so that errors are reported
1234        // as required by the spec), but before any side effects.
1235        if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
1236            log::trace!("Ignoring copy_external_image_to_texture of size 0");
1237            return Ok(());
1238        }
1239
1240        let mut pending_writes = self.pending_writes.lock();
1241        let encoder = pending_writes.activate();
1242
1243        // If the copy does not fully cover the layers, we need to initialize to
1244        // zero *first* as we don't keep track of partial texture layer inits.
1245        //
1246        // Strictly speaking we only need to clear the areas of a layer
1247        // untouched, but this would get increasingly messy.
1248        let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
1249            // volume textures don't have a layer range as array volumes aren't supported
1250            0..1
1251        } else {
1252            destination.origin.z..destination.origin.z + size.depth_or_array_layers
1253        };
1254        let mut dst_initialization_status = dst.initialization_status.write();
1255        if dst_initialization_status.mips[destination.mip_level as usize]
1256            .check(init_layer_range.clone())
1257            .is_some()
1258        {
1259            if has_copy_partial_init_tracker_coverage(&size, &destination, &dst.desc) {
1260                for layer_range in dst_initialization_status.mips[destination.mip_level as usize]
1261                    .drain(init_layer_range)
1262                    .collect::<Vec<core::ops::Range<u32>>>()
1263                {
1264                    let mut trackers = self.device.trackers.lock();
1265                    crate::command::clear_texture(
1266                        &dst,
1267                        TextureInitRange {
1268                            mip_range: destination.mip_level..(destination.mip_level + 1),
1269                            layer_range,
1270                        },
1271                        encoder,
1272                        &mut trackers.textures,
1273                        &self.device.alignments,
1274                        self.device.zero_buffer.as_ref(),
1275                        &self.device.snatchable_lock.read(),
1276                        self.device.instance_flags,
1277                    )
1278                    .map_err(QueueWriteError::from)?;
1279                }
1280            } else {
1281                dst_initialization_status.mips[destination.mip_level as usize]
1282                    .drain(init_layer_range);
1283            }
1284        }
1285
1286        let snatch_guard = self.device.snatchable_lock.read();
1287        let dst_raw = dst.try_raw(&snatch_guard)?;
1288
1289        let regions = hal::TextureCopy {
1290            src_base: hal::TextureCopyBase {
1291                mip_level: 0,
1292                array_layer: 0,
1293                origin: source.origin.to_3d(0),
1294                aspect: hal::FormatAspects::COLOR,
1295            },
1296            dst_base,
1297            size: hal_copy_size,
1298        };
1299
1300        let mut trackers = self.device.trackers.lock();
1301        let transitions = trackers
1302            .textures
1303            .set_single(&dst, selector, wgt::TextureUses::COPY_DST);
1304
1305        // `copy_external_image_to_texture` is exclusive to the WebGL backend.
1306        // Don't go through the `DynCommandEncoder` abstraction and directly to the WebGL backend.
1307        let encoder_webgl = encoder
1308            .as_any_mut()
1309            .downcast_mut::<hal::gles::CommandEncoder>()
1310            .unwrap();
1311        let dst_raw_webgl = dst_raw
1312            .as_any()
1313            .downcast_ref::<hal::gles::Texture>()
1314            .unwrap();
1315        let transitions_webgl = transitions.map(|pending| {
1316            let dyn_transition = pending.into_hal(dst_raw);
1317            hal::TextureBarrier {
1318                texture: dst_raw_webgl,
1319                range: dyn_transition.range,
1320                usage: dyn_transition.usage,
1321            }
1322        });
1323
1324        use hal::CommandEncoder as _;
1325        unsafe {
1326            encoder_webgl.transition_textures(transitions_webgl);
1327            encoder_webgl.copy_external_image_to_texture(
1328                source,
1329                dst_raw_webgl,
1330                premultiplied_alpha,
1331                iter::once(regions),
1332            );
1333        }
1334
1335        pending_writes.insert_texture(&dst);
1336
1337        Ok(())
1338    }
1339
1340    /// Flush `PendingWrites` if it contains a write to `buffer`.
1341    pub fn flush_writes_for_buffer(
1342        &self,
1343        buffer: &Arc<Buffer>,
1344        snatch_guard: SnatchGuard,
1345    ) -> Result<(), BufferAccessError> {
1346        let submission = self
1347            .allocate_submission(snatch_guard)
1348            .map_err(|(_index, e)| e)?;
1349
1350        let pending_writes = self.pending_writes.lock();
1351        if !pending_writes.contains_buffer(buffer) {
1352            return Ok(());
1353        }
1354
1355        submission.submit(pending_writes)?;
1356
1357        Ok(())
1358    }
1359
1360    fn flush_pending_writes(&self) -> Result<Option<SubmissionIndex>, DeviceError> {
1361        let snatch_guard = self.device.snatchable_lock.read();
1362        let submission = self
1363            .allocate_submission(snatch_guard)
1364            .map_err(|(_index, e)| e)?;
1365        let submit_index = submission.index;
1366        let pending_writes = self.pending_writes.lock();
1367        if pending_writes.is_recording {
1368            submission.submit(pending_writes)?;
1369            Ok(Some(submit_index))
1370        } else {
1371            Ok(None)
1372        }
1373    }
1374
1375    #[cfg(feature = "trace")]
1376    fn trace_submission(
1377        &self,
1378        submit_index: SubmissionIndex,
1379        commands: Vec<crate::command::Command<crate::command::PointerReferences>>,
1380    ) {
1381        if let Some(ref mut trace) = *self.device.trace.lock() {
1382            trace.add(Action::Submit(submit_index, commands));
1383        }
1384    }
1385
1386    #[cfg(feature = "trace")]
1387    fn trace_failed_submission(
1388        &self,
1389        submit_index: SubmissionIndex,
1390        commands: Option<Vec<crate::command::Command<crate::command::PointerReferences>>>,
1391        error: alloc::string::String,
1392    ) {
1393        if let Some(ref mut trace) = *self.device.trace.lock() {
1394            trace.add(Action::FailedCommands {
1395                commands,
1396                failed_at_submit: Some(submit_index),
1397                error,
1398            });
1399        }
1400    }
1401
1402    pub fn submit(
1403        &self,
1404        command_buffers: &[Arc<CommandBuffer>],
1405    ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {
1406        profiling::scope!("Queue::submit");
1407        api_log!("Queue::submit");
1408
1409        let snatch_guard = self.device.snatchable_lock.read();
1410        let mut submission = self
1411            .allocate_submission(snatch_guard)
1412            .map_err(|(index, e)| (index, e.into()))?;
1413        let submit_index = submission.index;
1414
1415        let res = 'error: {
1416            let mut used_surface_textures = track::TextureUsageScope::default();
1417
1418            {
1419                if !command_buffers.is_empty() {
1420                    profiling::scope!("prepare");
1421
1422                    let mut first_error = None;
1423
1424                    //TODO: if multiple command buffers are submitted, we can re-use the last
1425                    // native command buffer of the previous chain instead of always creating
1426                    // a temporary one, since the chains are not finished.
1427
1428                    // finish all the command buffers first
1429                    for command_buffer in command_buffers {
1430                        profiling::scope!("process command buffer");
1431
1432                        // we reset the used surface textures every time we use
1433                        // it, so make sure to set_size on it.
1434                        used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1435
1436                        // Note that we are required to invalidate all command buffers in both the success and failure paths.
1437                        // This is why we `continue` and don't early return via `?`.
1438                        #[allow(unused_mut)]
1439                        let mut cmd_buf_data = command_buffer.take_finished();
1440
1441                        if first_error.is_some() {
1442                            continue;
1443                        }
1444
1445                        #[cfg(feature = "trace")]
1446                        let trace_commands = cmd_buf_data
1447                            .as_mut()
1448                            .ok()
1449                            .and_then(|data| mem::take(&mut data.trace_commands));
1450
1451                        let mut baked = match cmd_buf_data {
1452                            Ok(cmd_buf_data) => {
1453                                let res = validate_command_buffer(
1454                                    command_buffer,
1455                                    self,
1456                                    &cmd_buf_data,
1457                                    &submission.snatch_guard,
1458                                    &mut submission.surface_textures,
1459                                    &mut used_surface_textures,
1460                                    &mut submission.command_index_guard,
1461                                );
1462                                if let Err(err) = res {
1463                                    #[cfg(feature = "trace")]
1464                                    self.trace_failed_submission(
1465                                        submit_index,
1466                                        trace_commands,
1467                                        err.to_string(),
1468                                    );
1469                                    first_error.get_or_insert(err);
1470                                    continue;
1471                                }
1472
1473                                #[cfg(feature = "trace")]
1474                                if let Some(commands) = trace_commands {
1475                                    self.trace_submission(submit_index, commands);
1476                                }
1477
1478                                cmd_buf_data.set_acceleration_structure_dependencies(
1479                                    &submission.snatch_guard,
1480                                );
1481                                cmd_buf_data.into_baked_commands()
1482                            }
1483                            Err(err) => {
1484                                #[cfg(feature = "trace")]
1485                                self.trace_failed_submission(
1486                                    submit_index,
1487                                    trace_commands,
1488                                    err.to_string(),
1489                                );
1490                                first_error.get_or_insert(err.into());
1491                                continue;
1492                            }
1493                        };
1494
1495                        if let Err(e) = baked.process_deferred_query_set_resolves(
1496                            &self.device,
1497                            &submission.snatch_guard,
1498                        ) {
1499                            break 'error Err(e.into());
1500                        }
1501
1502                        // execute resource transitions
1503                        if let Err(e) = baked.encoder.open_pass(hal_label(
1504                            Some("(wgpu internal) Transit"),
1505                            self.device.instance_flags,
1506                        )) {
1507                            break 'error Err(e.into());
1508                        }
1509
1510                        //Note: locking the trackers has to be done after the storages
1511                        let mut trackers = self.device.trackers.lock();
1512                        if let Err(e) =
1513                            baked.initialize_buffer_memory(&mut trackers, &submission.snatch_guard)
1514                        {
1515                            break 'error Err(e.into());
1516                        }
1517                        if let Err(e) = baked.initialize_texture_memory(
1518                            &mut trackers,
1519                            &self.device,
1520                            &submission.snatch_guard,
1521                        ) {
1522                            break 'error Err(e.into());
1523                        }
1524
1525                        //Note: stateless trackers are not merged:
1526                        // device already knows these resources exist.
1527                        CommandEncoder::insert_barriers_from_device_tracker(
1528                            baked.encoder.raw.as_mut(),
1529                            &mut trackers,
1530                            &baked.trackers,
1531                            &submission.snatch_guard,
1532                        );
1533
1534                        if let Err(e) = baked.encoder.close_and_push_front() {
1535                            break 'error Err(e.into());
1536                        }
1537
1538                        // Transition surface textures into `Present` state.
1539                        // Note: we could technically do it after all of the command buffers,
1540                        // but here we have a command encoder by hand, so it's easier to use it.
1541                        if !used_surface_textures.is_empty() {
1542                            if let Err(e) = baked.encoder.open_pass(hal_label(
1543                                Some("(wgpu internal) Present"),
1544                                self.device.instance_flags,
1545                            )) {
1546                                break 'error Err(e.into());
1547                            }
1548                            let texture_barriers = trackers
1549                                .textures
1550                                .set_from_usage_scope_and_drain_transitions(
1551                                    &used_surface_textures,
1552                                    &submission.snatch_guard,
1553                                )
1554                                .collect::<Vec<_>>();
1555                            unsafe {
1556                                baked.encoder.raw.transition_textures(&texture_barriers);
1557                            };
1558                            if let Err(e) = baked.encoder.close() {
1559                                break 'error Err(e.into());
1560                            }
1561                            used_surface_textures = track::TextureUsageScope::default();
1562                        }
1563
1564                        // done
1565                        submission.executions.push(EncoderInFlight {
1566                            inner: baked.encoder,
1567                            trackers: baked.trackers,
1568                            temp_resources: baked.temp_resources,
1569                            _indirect_draw_validation_resources: baked
1570                                .indirect_draw_validation_resources,
1571                            pending_buffers: FastHashMap::default(),
1572                            pending_textures: FastHashMap::default(),
1573                            pending_blas_s: FastHashMap::default(),
1574                        });
1575                    }
1576
1577                    if let Some(first_error) = first_error {
1578                        break 'error Err(first_error);
1579                    }
1580                }
1581            }
1582
1583            let pending_writes = self.pending_writes.lock();
1584
1585            let SubmissionResult { snatch_guard } = match submission.submit(pending_writes) {
1586                Ok(result) => result,
1587                Err(e) => break 'error Err(e.into()),
1588            };
1589
1590            profiling::scope!("cleanup");
1591
1592            // This will schedule destruction of all resources that are no longer needed
1593            // by the user but used in the command stream, among other things.
1594            // `device.maintain` consumes and will release the snatch guard.
1595            let (closures, result) = self.device.maintain(wgt::PollType::Poll, snatch_guard);
1596            match result {
1597                Ok(status) => {
1598                    debug_assert!(matches!(
1599                        status,
1600                        wgt::PollStatus::QueueEmpty | wgt::PollStatus::Poll
1601                    ));
1602                }
1603                Err(WaitIdleError::Device(err)) => break 'error Err(QueueSubmitError::Queue(err)),
1604                Err(WaitIdleError::WrongSubmissionIndex(..)) => {
1605                    unreachable!("Cannot get WrongSubmissionIndex from Poll")
1606                }
1607                Err(WaitIdleError::Timeout) => unreachable!("Cannot get Timeout from Poll"),
1608            };
1609
1610            Ok(closures)
1611        };
1612
1613        let callbacks = match res {
1614            Ok(ok) => ok,
1615            Err(e) => return Err((submit_index, e)),
1616        };
1617
1618        // the closures should execute with nothing locked!
1619        callbacks.fire();
1620
1621        self.device.lose_if_oom();
1622
1623        api_log!("Queue::submit returned submit index {submit_index}");
1624
1625        Ok(submit_index)
1626    }
1627
1628    /// Allocate a submission index and prepare for a submission.
1629    ///
1630    /// This is an internal API used in [`Queue::submit`] and other places within
1631    /// `wgpu-core` that need to submit work.
1632    ///
1633    /// Returns the index and a [`PendingSubmission`].
1634    ///
1635    /// The caller passes in the already-acquired [`SnatchGuard`]. This function acquires
1636    /// the fence lock and the command index lock.
1637    ///
1638    /// The caller should update [`PendingSubmission::executions`] with details of the
1639    /// submission.
1640    ///
1641    /// To finalize and submit the submission, call [`PendingSubmission::submit`] (which is
1642    /// a convenience wrapper around [`Queue::submit_pending_submission`]).
1643    ///
1644    /// After calling this function and before submitting, the caller must acquire the
1645    /// pending writes lock, and pass it to `submit`.
1646    ///
1647    /// It is also acceptable to drop the `PendingSubmission` without submitting. This may
1648    /// be necessary when locks are required to access the state that determines whether a
1649    /// submission is needed.
1650    fn allocate_submission<'a>(
1651        &'a self,
1652        snatch_guard: SnatchGuard<'a>,
1653    ) -> Result<PendingSubmission<'a>, (SubmissionIndex, DeviceError)> {
1654        let mut command_index_guard = self.device.command_indices.write();
1655        command_index_guard.active_submission_index += 1;
1656        let index = command_index_guard.active_submission_index;
1657
1658        if let Err(e) = self.device.check_is_valid() {
1659            return Err((index, e));
1660        }
1661
1662        let submission = PendingSubmission {
1663            queue: self,
1664            snatch_guard,
1665            command_index_guard,
1666            executions: Vec::new(),
1667            surface_textures: FastHashMap::default(),
1668            index,
1669        };
1670
1671        Ok(submission)
1672    }
1673
1674    /// Finalize and submit a [`PendingSubmission`] that was returned by
1675    /// [`Queue::allocate_submission`].
1676    ///
1677    /// This is an internal API used in `Queue::submit` and other places within
1678    /// `wgpu-core` that need to submit work. See [`Queue::allocate_submission`]
1679    /// for more details.
1680    ///
1681    /// This function:
1682    ///
1683    /// - Performs a HAL submission of the pending writes command
1684    ///   encoder and any other command encoders that were added to the
1685    ///   [`PendingSubmission`].
1686    /// - Advances `last_successful_submission_index` and registers the
1687    ///   submission with the lifetime tracker.
1688    /// - Returns a [`SubmissionResult`], which contains the snatch guard.
1689    fn submit_pending_submission<'a>(
1690        &self,
1691        mut pending_writes: MutexGuard<'_, PendingWrites>,
1692        prepared: PendingSubmission<'a>,
1693    ) -> Result<SubmissionResult<'a>, DeviceError> {
1694        let PendingSubmission {
1695            queue: _,
1696            snatch_guard,
1697            command_index_guard,
1698            mut executions,
1699            mut surface_textures,
1700            index: submit_index,
1701        } = prepared;
1702
1703        let mut used_surface_textures = track::TextureUsageScope::default();
1704        used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1705        for texture in pending_writes.dst_textures.values() {
1706            match texture.try_inner(&snatch_guard) {
1707                Ok(TextureInner::Native { .. }) => {}
1708                Ok(TextureInner::Surface { .. }) => {
1709                    // Compare the Arcs by pointer as Textures don't implement Eq
1710                    surface_textures.insert(Arc::as_ptr(texture), texture.clone());
1711
1712                    unsafe {
1713                        used_surface_textures
1714                            .merge_single(texture, None, wgt::TextureUses::PRESENT)
1715                            .unwrap()
1716                    };
1717                }
1718                // The texture must not have been destroyed when its usage here was
1719                // encoded. If it was destroyed after that, then it was transferred
1720                // to `pending_writes.temp_resources` at the time of destruction, so
1721                // we are still okay to use it.
1722                Err(InvalidOrDestroyedResourceError::DestroyedResource(_)) => {}
1723                Err(InvalidOrDestroyedResourceError::InvalidResource(_)) => {
1724                    unreachable!()
1725                }
1726            }
1727        }
1728
1729        if !used_surface_textures.is_empty() {
1730            let mut trackers = self.device.trackers.lock();
1731
1732            let texture_barriers = trackers
1733                .textures
1734                .set_from_usage_scope_and_drain_transitions(&used_surface_textures, &snatch_guard)
1735                .collect::<Vec<_>>();
1736            unsafe {
1737                pending_writes
1738                    .command_encoder
1739                    .transition_textures(&texture_barriers);
1740            };
1741        }
1742
1743        match pending_writes.pre_submit(&self.device.command_allocator, &self.device, self) {
1744            Ok(Some(pending_execution)) => {
1745                executions.insert(0, pending_execution);
1746            }
1747            Ok(None) => {}
1748            Err(e) => return Err(e),
1749        }
1750        let hal_command_buffers = executions
1751            .iter()
1752            .flat_map(|e| e.inner.list.iter().map(|b| b.as_ref()))
1753            .collect::<Vec<_>>();
1754
1755        {
1756            let mut submit_surface_textures =
1757                SmallVec::<[&dyn hal::DynSurfaceTexture; 2]>::with_capacity(surface_textures.len());
1758            for texture in surface_textures.values() {
1759                let raw = match texture.try_inner(&snatch_guard).ok() {
1760                    Some(TextureInner::Surface { raw, .. }) => raw.as_ref(),
1761                    _ => unreachable!(),
1762                };
1763                submit_surface_textures.push(raw);
1764            }
1765
1766            unsafe {
1767                self.raw().submit(
1768                    &hal_command_buffers,
1769                    &submit_surface_textures,
1770                    (self.device.fence.as_ref(), submit_index),
1771                )
1772            }
1773            .map_err(|e| self.device.handle_hal_error(e))?;
1774
1775            // Submissions must have strictly increasing indices, so we must hold the
1776            // command index guard until we have submitted, to prevent another submission
1777            // from claiming the next index and reaching `submit` before we do.
1778            drop(pending_writes);
1779
1780            // Advance the successful submission index.
1781            self.device
1782                .last_successful_submission_index
1783                .fetch_max(submit_index, Ordering::SeqCst);
1784        }
1785
1786        // this will register the new submission to the life time tracker
1787        self.lock_life().track_submission(submit_index, executions);
1788
1789        // `device.maintain` relies on being able to prevent new submissions by
1790        // using `command_index_guard` while also checking whether there are
1791        // no tracked submissions to guarantee no new submissions will happen
1792        // after a device is lost. This requires `command_index_guard` to be
1793        // held over `self.lock_life()`
1794        drop(command_index_guard);
1795
1796        Ok(SubmissionResult { snatch_guard })
1797    }
1798
1799    pub fn get_timestamp_period(&self) -> f32 {
1800        unsafe { self.raw().get_timestamp_period() }
1801    }
1802
1803    /// `closure` is guaranteed to be called.
1804    pub fn on_submitted_work_done(
1805        &self,
1806        closure: SubmittedWorkDoneClosure,
1807    ) -> Option<SubmissionIndex> {
1808        api_log!("Queue::on_submitted_work_done");
1809
1810        // A `DeviceError` means we're losing the device anyways, so we can ignore it here
1811        // (mostly to avoid a breaking change to the `on_submitted_work_done` signature
1812        // for an error case that it is unlikely the caller will be able to handle).
1813        let _: Result<_, DeviceError> = self.flush_pending_writes();
1814
1815        self.lock_life().add_work_done_closure(closure)
1816    }
1817
1818    #[allow(trivial_casts)]
1819    pub fn compact_blas(&self, blas: &Arc<Blas>) -> (Arc<Blas>, Option<CompactBlasError>) {
1820        api_log!(
1821            "Queue::compact_blas {:?}, {:?}",
1822            self as *const _,
1823            Arc::as_ptr(blas)
1824        );
1825
1826        let (blas, error) = match self.compact_blas_inner(blas) {
1827            Ok(blas) => (blas, None),
1828            Err(err) => {
1829                let new_label = blas.label.clone() + " (compacted)";
1830                (
1831                    Blas::invalid(
1832                        self.device.clone(),
1833                        &BlasDescriptor {
1834                            label: Some(new_label.into()),
1835                            flags: blas.flags,
1836                            update_mode: blas.update_mode,
1837                        },
1838                    ),
1839                    Some(err),
1840                )
1841            }
1842        };
1843
1844        // TODO: Tracing
1845
1846        (blas, error)
1847    }
1848
1849    pub(crate) fn compact_blas_inner(
1850        &self,
1851        blas: &Arc<Blas>,
1852    ) -> Result<Arc<Blas>, CompactBlasError> {
1853        profiling::scope!("Queue::compact_blas");
1854        api_log!("Queue::compact_blas");
1855
1856        let new_label = blas.label.clone() + " (compacted)";
1857
1858        self.device.check_is_valid()?;
1859
1860        self.device
1861            .require_features(wgpu_types::Features::EXPERIMENTAL_RAY_QUERY)?;
1862
1863        blas.check_is_valid()?;
1864        self.same_device_as(blas.as_ref())?;
1865
1866        let device = blas.device.clone();
1867
1868        let snatch_guard = device.snatchable_lock.read();
1869
1870        let BlasCompactState::Ready { size } = *blas.compacted_state.lock() else {
1871            return Err(CompactBlasError::BlasNotReady);
1872        };
1873
1874        let mut size_info = blas.size_info;
1875        size_info.acceleration_structure_size = size;
1876
1877        let mut pending_writes = self.pending_writes.lock();
1878        let cmd_buf_raw = pending_writes.activate();
1879
1880        let raw = unsafe {
1881            device
1882                .raw()
1883                .create_acceleration_structure(&hal::AccelerationStructureDescriptor {
1884                    label: hal_label(Some(&new_label), device.instance_flags),
1885                    size: size_info.acceleration_structure_size,
1886                    format: hal::AccelerationStructureFormat::BottomLevel,
1887                    allow_compaction: false,
1888                })
1889        }
1890        .map_err(DeviceError::from_hal)?;
1891
1892        let src_raw = blas.try_raw(&snatch_guard)?;
1893
1894        unsafe {
1895            cmd_buf_raw.copy_acceleration_structure_to_acceleration_structure(
1896                src_raw,
1897                raw.as_ref(),
1898                wgt::AccelerationStructureCopy::Compact,
1899            )
1900        };
1901
1902        let handle = unsafe {
1903            device
1904                .raw()
1905                .get_acceleration_structure_device_address(raw.as_ref())
1906        };
1907
1908        drop(snatch_guard);
1909
1910        let mut command_indices_lock = device.command_indices.write();
1911        command_indices_lock.next_acceleration_structure_build_command_index += 1;
1912        let built_index =
1913            NonZeroU64::new(command_indices_lock.next_acceleration_structure_build_command_index)
1914                .unwrap();
1915
1916        let new_blas = Arc::new(Blas {
1917            state: ResourceState::Valid(BlasState {
1918                raw: Snatchable::new(raw),
1919            }),
1920            device: device.clone(),
1921            size_info,
1922            sizes: blas.sizes.clone(),
1923            flags: blas.flags & !AccelerationStructureFlags::ALLOW_COMPACTION,
1924            update_mode: blas.update_mode,
1925            // Bypass the submit checks which update this because we don't submit this normally.
1926            built_index: RwLock::new(rank::BLAS_BUILT_INDEX, Some(built_index)),
1927            handle,
1928            label: new_label,
1929            tracking_data: TrackingData::new(blas.device.tracker_indices.blas_s.clone()),
1930            compaction_buffer: None,
1931            compacted_state: Mutex::new(rank::BLAS_COMPACTION_STATE, BlasCompactState::Compacted),
1932        });
1933
1934        pending_writes.insert_blas(blas);
1935        pending_writes.insert_blas(&new_blas);
1936
1937        // We should have no more errors after this because we have marked the command encoder as successful.
1938        let old_blas_size = blas.size_info.acceleration_structure_size;
1939        let new_blas_size = new_blas.size_info.acceleration_structure_size;
1940
1941        api_log!("CommandEncoder::compact_blas {:?} (size: {old_blas_size}) -> {:?} (size: {new_blas_size})", Arc::as_ptr(blas), Arc::as_ptr(&new_blas));
1942
1943        Ok(new_blas)
1944    }
1945}
1946
1947impl Global {
1948    pub fn queue_write_buffer(
1949        &self,
1950        queue_id: QueueId,
1951        buffer_id: id::BufferId,
1952        buffer_offset: wgt::BufferAddress,
1953        data: &[u8],
1954    ) -> Result<(), QueueWriteError> {
1955        let queue = self.hub.queues.get(queue_id);
1956        let buffer = self.hub.buffers.get(buffer_id);
1957
1958        queue.write_buffer(buffer, buffer_offset, data)
1959    }
1960
1961    pub fn queue_create_staging_buffer(
1962        &self,
1963        queue_id: QueueId,
1964        buffer_size: wgt::BufferSize,
1965        id_in: Option<id::StagingBufferId>,
1966    ) -> Result<(id::StagingBufferId, NonNull<u8>), QueueWriteError> {
1967        let queue = self.hub.queues.get(queue_id);
1968        let (staging_buffer, ptr) = queue.create_staging_buffer(buffer_size)?;
1969
1970        let fid = self.hub.staging_buffers.prepare(id_in);
1971        let id = fid.assign(staging_buffer);
1972
1973        Ok((id, ptr))
1974    }
1975
1976    pub fn queue_write_staging_buffer(
1977        &self,
1978        queue_id: QueueId,
1979        buffer_id: id::BufferId,
1980        buffer_offset: wgt::BufferAddress,
1981        staging_buffer_id: id::StagingBufferId,
1982    ) -> Result<(), QueueWriteError> {
1983        let queue = self.hub.queues.get(queue_id);
1984        let buffer = self.hub.buffers.get(buffer_id);
1985        let staging_buffer = self.hub.staging_buffers.remove(staging_buffer_id);
1986        queue.write_staging_buffer(buffer, buffer_offset, staging_buffer)
1987    }
1988
1989    pub fn queue_validate_write_buffer(
1990        &self,
1991        queue_id: QueueId,
1992        buffer_id: id::BufferId,
1993        buffer_offset: u64,
1994        buffer_size: wgt::BufferSize,
1995    ) -> Result<(), QueueWriteError> {
1996        let queue = self.hub.queues.get(queue_id);
1997        let buffer = self.hub.buffers.get(buffer_id);
1998        queue.validate_write_buffer(buffer, buffer_offset, buffer_size)
1999    }
2000
2001    pub fn queue_write_texture(
2002        &self,
2003        queue_id: QueueId,
2004        destination: &wgt::TexelCopyTextureInfo<id::TextureId>,
2005        data: &[u8],
2006        data_layout: &wgt::TexelCopyBufferLayout,
2007        size: &wgt::Extent3d,
2008    ) -> Result<(), QueueWriteError> {
2009        let queue = self.hub.queues.get(queue_id);
2010        let texture = self.hub.textures.get(destination.texture);
2011        let destination = wgt::TexelCopyTextureInfo {
2012            texture,
2013            mip_level: destination.mip_level,
2014            origin: destination.origin,
2015            aspect: destination.aspect,
2016        };
2017
2018        #[cfg(feature = "trace")]
2019        if let Some(ref mut trace) = *queue.device.trace.lock() {
2020            use crate::device::trace::DataKind;
2021            let data = trace.make_binary(DataKind::Bin, data);
2022            trace.add(Action::WriteTexture {
2023                to: destination.to_trace(),
2024                data,
2025                layout: *data_layout,
2026                size: *size,
2027            });
2028        }
2029
2030        queue.write_texture(destination, data, data_layout, size)
2031    }
2032
2033    #[cfg(webgl)]
2034    pub fn queue_copy_external_image_to_texture(
2035        &self,
2036        queue_id: QueueId,
2037        source: &wgt::CopyExternalImageSourceInfo,
2038        destination: crate::command::CopyExternalImageDestInfo,
2039        size: wgt::Extent3d,
2040    ) -> Result<(), QueueWriteError> {
2041        let queue = self.hub.queues.get(queue_id);
2042        let destination = wgt::CopyExternalImageDestInfo {
2043            texture: self.hub.textures.get(destination.texture),
2044            mip_level: destination.mip_level,
2045            origin: destination.origin,
2046            aspect: destination.aspect,
2047            color_space: destination.color_space,
2048            premultiplied_alpha: destination.premultiplied_alpha,
2049        };
2050        queue.copy_external_image_to_texture(source, destination, size)
2051    }
2052
2053    pub fn queue_submit(
2054        &self,
2055        queue_id: QueueId,
2056        command_buffer_ids: &[id::CommandBufferId],
2057    ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {
2058        let queue = self.hub.queues.get(queue_id);
2059        let command_buffer_guard = self.hub.command_buffers.read();
2060        let command_buffers = command_buffer_ids
2061            .iter()
2062            .map(|id| command_buffer_guard.get(*id))
2063            .collect::<Vec<_>>();
2064        drop(command_buffer_guard);
2065        queue.submit(&command_buffers)
2066    }
2067
2068    pub fn queue_get_timestamp_period(&self, queue_id: QueueId) -> f32 {
2069        let queue = self.hub.queues.get(queue_id);
2070
2071        if queue.device.timestamp_normalizer.get().unwrap().enabled() {
2072            return 1.0;
2073        }
2074
2075        queue.get_timestamp_period()
2076    }
2077
2078    pub fn queue_on_submitted_work_done(
2079        &self,
2080        queue_id: QueueId,
2081        closure: SubmittedWorkDoneClosure,
2082    ) -> SubmissionIndex {
2083        api_log!("Queue::on_submitted_work_done {queue_id:?}");
2084
2085        let queue = self.hub.queues.get(queue_id);
2086        let result = queue.on_submitted_work_done(closure);
2087        result.unwrap_or(0) // '0' means no wait is necessary
2088    }
2089
2090    pub fn queue_compact_blas(
2091        &self,
2092        queue_id: QueueId,
2093        blas_id: BlasId,
2094        id_in: Option<BlasId>,
2095    ) -> (BlasId, Option<u64>, Option<CompactBlasError>) {
2096        let fid = self.hub.blas_s.prepare(id_in);
2097
2098        let queue = self.hub.queues.get(queue_id);
2099        let blas = self.hub.blas_s.get(blas_id);
2100
2101        let (blas, error) = queue.compact_blas(&blas);
2102
2103        let handle = blas.handle();
2104        let id = fid.assign(blas);
2105
2106        (id, handle, error)
2107    }
2108}
2109
2110fn validate_command_buffer(
2111    command_buffer: &CommandBuffer,
2112    queue: &Queue,
2113    cmd_buf_data: &crate::command::CommandBufferMutable,
2114    snatch_guard: &SnatchGuard,
2115    surface_textures: &mut FastHashMap<*const Texture, Arc<Texture>>,
2116    used_surface_textures: &mut track::TextureUsageScope,
2117    command_index_guard: &mut RwLockWriteGuard<CommandIndices>,
2118) -> Result<(), QueueSubmitError> {
2119    command_buffer.same_device_as(queue)?;
2120
2121    {
2122        profiling::scope!("check resource state");
2123
2124        {
2125            profiling::scope!("buffers");
2126            for buffer in cmd_buf_data.trackers.buffers.used_resources() {
2127                buffer.check_destroyed(snatch_guard)?;
2128
2129                match *buffer.map_state.lock() {
2130                    BufferMapState::Idle => (),
2131                    _ => return Err(QueueSubmitError::BufferStillMapped(buffer.error_ident())),
2132                }
2133            }
2134        }
2135        {
2136            profiling::scope!("textures");
2137            for texture in cmd_buf_data.trackers.textures.used_resources() {
2138                let should_extend = match texture.try_inner(snatch_guard)? {
2139                    TextureInner::Native { .. } => false,
2140                    TextureInner::Surface { .. } => {
2141                        // Compare the Arcs by pointer as Textures don't implement Eq.
2142                        surface_textures.insert(Arc::as_ptr(texture), texture.clone());
2143
2144                        true
2145                    }
2146                };
2147                if should_extend {
2148                    unsafe {
2149                        used_surface_textures
2150                            .merge_single(texture, None, wgt::TextureUses::PRESENT)
2151                            .unwrap();
2152                    };
2153                }
2154            }
2155        }
2156        {
2157            profiling::scope!("query sets");
2158            for query_set in cmd_buf_data.trackers.query_sets.used_resources() {
2159                query_set.try_raw(snatch_guard)?;
2160            }
2161        }
2162        // WebGPU requires that we check every bind group referenced during
2163        // encoding, even ones that may have been replaced before being used.
2164        // TODO(<https://github.com/gfx-rs/wgpu/issues/8510>): Optimize this.
2165        {
2166            profiling::scope!("bind groups");
2167            for bind_group in &cmd_buf_data.trackers.bind_groups {
2168                // This checks the bind group and all resources it references.
2169                bind_group.try_raw(snatch_guard)?;
2170            }
2171        }
2172
2173        if let Err(e) =
2174            cmd_buf_data.validate_acceleration_structure_actions(snatch_guard, command_index_guard)
2175        {
2176            return Err(e.into());
2177        }
2178    }
2179    Ok(())
2180}