wgpu_core/device/
queue.rs

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