wgpu_core/device/
queue.rs

1#[cfg(feature = "trace")]
2use crate::device::trace::Action;
3use crate::{
4    api_log,
5    command::{
6        extract_texture_selector, validate_linear_texture_data, validate_texture_copy_range,
7        ClearError, CommandAllocator, CommandBuffer, CommandEncoderError, CopySide,
8        TexelCopyTextureInfo, TransferError,
9    },
10    conv,
11    device::{DeviceError, WaitIdleError},
12    get_lowest_common_denom,
13    global::Global,
14    id::{self, QueueId},
15    init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange},
16    lock::{rank, Mutex, MutexGuard, RwLockWriteGuard},
17    resource::{
18        Buffer, BufferAccessError, BufferMapState, DestroyedBuffer, DestroyedResourceError,
19        DestroyedTexture, Fallible, FlushedStagingBuffer, InvalidResourceError, Labeled,
20        ParentDevice, ResourceErrorIdent, StagingBuffer, Texture, TextureInner, Trackable,
21    },
22    resource_log,
23    snatch::SnatchGuard,
24    track::{self, Tracker, TrackerIndex},
25    FastHashMap, SubmissionIndex,
26};
27
28use smallvec::SmallVec;
29
30use crate::scratch::ScratchBuffer;
31use std::{
32    iter,
33    mem::{self, ManuallyDrop},
34    ptr::NonNull,
35    sync::{atomic::Ordering, Arc},
36};
37use thiserror::Error;
38
39use super::{life::LifetimeTracker, Device};
40
41pub struct Queue {
42    raw: Box<dyn hal::DynQueue>,
43    pub(crate) pending_writes: Mutex<PendingWrites>,
44    life_tracker: Mutex<LifetimeTracker>,
45    // The device needs to be dropped last (`Device.zero_buffer` might be referenced by the encoder in pending writes).
46    pub(crate) device: Arc<Device>,
47}
48
49impl Queue {
50    pub(crate) fn new(
51        device: Arc<Device>,
52        raw: Box<dyn hal::DynQueue>,
53    ) -> Result<Self, DeviceError> {
54        let pending_encoder = device
55            .command_allocator
56            .acquire_encoder(device.raw(), raw.as_ref())
57            .map_err(DeviceError::from_hal);
58
59        let pending_encoder = match pending_encoder {
60            Ok(pending_encoder) => pending_encoder,
61            Err(e) => {
62                return Err(e);
63            }
64        };
65
66        let mut pending_writes = PendingWrites::new(pending_encoder);
67
68        let zero_buffer = device.zero_buffer.as_ref();
69        pending_writes.activate();
70        unsafe {
71            pending_writes
72                .command_encoder
73                .transition_buffers(&[hal::BufferBarrier {
74                    buffer: zero_buffer,
75                    usage: hal::StateTransition {
76                        from: hal::BufferUses::empty(),
77                        to: hal::BufferUses::COPY_DST,
78                    },
79                }]);
80            pending_writes
81                .command_encoder
82                .clear_buffer(zero_buffer, 0..super::ZERO_BUFFER_SIZE);
83            pending_writes
84                .command_encoder
85                .transition_buffers(&[hal::BufferBarrier {
86                    buffer: zero_buffer,
87                    usage: hal::StateTransition {
88                        from: hal::BufferUses::COPY_DST,
89                        to: hal::BufferUses::COPY_SRC,
90                    },
91                }]);
92        }
93
94        Ok(Queue {
95            raw,
96            device,
97            pending_writes: Mutex::new(rank::QUEUE_PENDING_WRITES, pending_writes),
98            life_tracker: Mutex::new(rank::QUEUE_LIFE_TRACKER, LifetimeTracker::new()),
99        })
100    }
101
102    pub(crate) fn raw(&self) -> &dyn hal::DynQueue {
103        self.raw.as_ref()
104    }
105
106    #[track_caller]
107    pub(crate) fn lock_life<'a>(&'a self) -> MutexGuard<'a, LifetimeTracker> {
108        self.life_tracker.lock()
109    }
110
111    pub(crate) fn maintain(
112        &self,
113        submission_index: u64,
114        snatch_guard: &SnatchGuard,
115    ) -> (
116        SmallVec<[SubmittedWorkDoneClosure; 1]>,
117        Vec<super::BufferMapPendingClosure>,
118        bool,
119    ) {
120        let mut life_tracker = self.lock_life();
121        let submission_closures = life_tracker.triage_submissions(submission_index);
122
123        let mapping_closures = life_tracker.handle_mapping(snatch_guard);
124
125        let queue_empty = life_tracker.queue_empty();
126
127        (submission_closures, mapping_closures, queue_empty)
128    }
129}
130
131crate::impl_resource_type!(Queue);
132// TODO: https://github.com/gfx-rs/wgpu/issues/4014
133impl Labeled for Queue {
134    fn label(&self) -> &str {
135        ""
136    }
137}
138crate::impl_parent_device!(Queue);
139crate::impl_storage_item!(Queue);
140
141impl Drop for Queue {
142    fn drop(&mut self) {
143        resource_log!("Drop {}", self.error_ident());
144
145        let last_successful_submission_index = self
146            .device
147            .last_successful_submission_index
148            .load(Ordering::Acquire);
149
150        let fence = self.device.fence.read();
151
152        // Try waiting on the last submission using the following sequence of timeouts
153        let timeouts_in_ms = [100, 200, 400, 800, 1600, 3200];
154
155        for (i, timeout_ms) in timeouts_in_ms.into_iter().enumerate() {
156            let is_last_iter = i == timeouts_in_ms.len() - 1;
157
158            api_log!(
159                "Waiting on last submission. try: {}/{}. timeout: {}ms",
160                i + 1,
161                timeouts_in_ms.len(),
162                timeout_ms
163            );
164
165            let wait_res = unsafe {
166                self.device.raw().wait(
167                    fence.as_ref(),
168                    last_successful_submission_index,
169                    #[cfg(not(target_arch = "wasm32"))]
170                    timeout_ms,
171                    #[cfg(target_arch = "wasm32")]
172                    0, // WebKit and Chromium don't support a non-0 timeout
173                )
174            };
175            // Note: If we don't panic below we are in UB land (destroying resources while they are still in use by the GPU).
176            match wait_res {
177                Ok(true) => break,
178                Ok(false) => {
179                    // It's fine that we timed out on WebGL; GL objects can be deleted early as they
180                    // will be kept around by the driver if GPU work hasn't finished.
181                    // Moreover, the way we emulate read mappings on WebGL allows us to execute map_buffer earlier than on other
182                    // backends since getBufferSubData is synchronous with respect to the other previously enqueued GL commands.
183                    // Relying on this behavior breaks the clean abstraction wgpu-hal tries to maintain and
184                    // we should find ways to improve this. See https://github.com/gfx-rs/wgpu/issues/6538.
185                    #[cfg(target_arch = "wasm32")]
186                    {
187                        break;
188                    }
189                    #[cfg(not(target_arch = "wasm32"))]
190                    {
191                        if is_last_iter {
192                            panic!(
193                                "We timed out while waiting on the last successful submission to complete!"
194                            );
195                        }
196                    }
197                }
198                Err(e) => match e {
199                    hal::DeviceError::OutOfMemory => {
200                        if is_last_iter {
201                            panic!(
202                                "We ran into an OOM error while waiting on the last successful submission to complete!"
203                            );
204                        }
205                    }
206                    hal::DeviceError::Lost => {
207                        self.device.handle_hal_error(e); // will lose the device
208                        break;
209                    }
210                    hal::DeviceError::ResourceCreationFailed => unreachable!(),
211                    hal::DeviceError::Unexpected => {
212                        panic!(
213                            "We ran into an unexpected error while waiting on the last successful submission to complete!"
214                        );
215                    }
216                },
217            }
218        }
219        drop(fence);
220
221        let snatch_guard = self.device.snatchable_lock.read();
222        let (submission_closures, mapping_closures, queue_empty) =
223            self.maintain(last_successful_submission_index, &snatch_guard);
224        drop(snatch_guard);
225
226        assert!(queue_empty);
227
228        let closures = crate::device::UserClosures {
229            mappings: mapping_closures,
230            submissions: submission_closures,
231            device_lost_invocations: SmallVec::new(),
232        };
233
234        closures.fire();
235    }
236}
237
238#[cfg(send_sync)]
239pub type SubmittedWorkDoneClosure = Box<dyn FnOnce() + Send + 'static>;
240#[cfg(not(send_sync))]
241pub type SubmittedWorkDoneClosure = Box<dyn FnOnce() + 'static>;
242
243/// A texture or buffer to be freed soon.
244///
245/// This is just a tagged raw texture or buffer, generally about to be added to
246/// some other more specific container like:
247///
248/// - `PendingWrites::temp_resources`: resources used by queue writes and
249///   unmaps, waiting to be folded in with the next queue submission
250///
251/// - `ActiveSubmission::temp_resources`: temporary resources used by a queue
252///   submission, to be freed when it completes
253#[derive(Debug)]
254pub enum TempResource {
255    StagingBuffer(FlushedStagingBuffer),
256    ScratchBuffer(ScratchBuffer),
257    DestroyedBuffer(DestroyedBuffer),
258    DestroyedTexture(DestroyedTexture),
259}
260
261/// A series of raw [`CommandBuffer`]s that have been submitted to a
262/// queue, and the [`wgpu_hal::CommandEncoder`] that built them.
263///
264/// [`CommandBuffer`]: hal::Api::CommandBuffer
265/// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
266pub(crate) struct EncoderInFlight {
267    inner: crate::command::CommandEncoder,
268    pub(crate) trackers: Tracker,
269    pub(crate) temp_resources: Vec<TempResource>,
270
271    /// These are the buffers that have been tracked by `PendingWrites`.
272    pub(crate) pending_buffers: FastHashMap<TrackerIndex, Arc<Buffer>>,
273    /// These are the textures that have been tracked by `PendingWrites`.
274    pub(crate) pending_textures: FastHashMap<TrackerIndex, Arc<Texture>>,
275}
276
277/// A private command encoder for writes made directly on the device
278/// or queue.
279///
280/// Operations like `buffer_unmap`, `queue_write_buffer`, and
281/// `queue_write_texture` need to copy data to the GPU. At the hal
282/// level, this must be done by encoding and submitting commands, but
283/// these operations are not associated with any specific wgpu command
284/// buffer.
285///
286/// Instead, `Device::pending_writes` owns one of these values, which
287/// has its own hal command encoder and resource lists. The commands
288/// accumulated here are automatically submitted to the queue the next
289/// time the user submits a wgpu command buffer, ahead of the user's
290/// commands.
291///
292/// Important:
293/// When locking pending_writes be sure that tracker is not locked
294/// and try to lock trackers for the minimum timespan possible
295///
296/// All uses of [`StagingBuffer`]s end up here.
297#[derive(Debug)]
298pub(crate) struct PendingWrites {
299    // The command encoder needs to be destroyed before any other resource in pending writes.
300    pub command_encoder: Box<dyn hal::DynCommandEncoder>,
301
302    /// True if `command_encoder` is in the "recording" state, as
303    /// described in the docs for the [`wgpu_hal::CommandEncoder`]
304    /// trait.
305    ///
306    /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
307    pub is_recording: bool,
308
309    temp_resources: Vec<TempResource>,
310    dst_buffers: FastHashMap<TrackerIndex, Arc<Buffer>>,
311    dst_textures: FastHashMap<TrackerIndex, Arc<Texture>>,
312}
313
314impl PendingWrites {
315    pub fn new(command_encoder: Box<dyn hal::DynCommandEncoder>) -> Self {
316        Self {
317            command_encoder,
318            is_recording: false,
319            temp_resources: Vec::new(),
320            dst_buffers: FastHashMap::default(),
321            dst_textures: FastHashMap::default(),
322        }
323    }
324
325    pub fn insert_buffer(&mut self, buffer: &Arc<Buffer>) {
326        self.dst_buffers
327            .insert(buffer.tracker_index(), buffer.clone());
328    }
329
330    pub fn insert_texture(&mut self, texture: &Arc<Texture>) {
331        self.dst_textures
332            .insert(texture.tracker_index(), texture.clone());
333    }
334
335    pub fn contains_buffer(&self, buffer: &Arc<Buffer>) -> bool {
336        self.dst_buffers.contains_key(&buffer.tracker_index())
337    }
338
339    pub fn contains_texture(&self, texture: &Arc<Texture>) -> bool {
340        self.dst_textures.contains_key(&texture.tracker_index())
341    }
342
343    pub fn consume_temp(&mut self, resource: TempResource) {
344        self.temp_resources.push(resource);
345    }
346
347    pub fn consume(&mut self, buffer: FlushedStagingBuffer) {
348        self.temp_resources
349            .push(TempResource::StagingBuffer(buffer));
350    }
351
352    fn pre_submit(
353        &mut self,
354        command_allocator: &CommandAllocator,
355        device: &Arc<Device>,
356        queue: &Queue,
357    ) -> Result<Option<EncoderInFlight>, DeviceError> {
358        if self.is_recording {
359            let pending_buffers = mem::take(&mut self.dst_buffers);
360            let pending_textures = mem::take(&mut self.dst_textures);
361
362            let cmd_buf = unsafe { self.command_encoder.end_encoding() }
363                .map_err(|e| device.handle_hal_error(e))?;
364            self.is_recording = false;
365
366            let new_encoder = command_allocator
367                .acquire_encoder(device.raw(), queue.raw())
368                .map_err(|e| device.handle_hal_error(e))?;
369
370            let encoder = EncoderInFlight {
371                inner: crate::command::CommandEncoder {
372                    raw: ManuallyDrop::new(mem::replace(&mut self.command_encoder, new_encoder)),
373                    list: vec![cmd_buf],
374                    device: device.clone(),
375                    is_open: false,
376                    hal_label: None,
377                },
378                trackers: Tracker::new(),
379                temp_resources: mem::take(&mut self.temp_resources),
380                pending_buffers,
381                pending_textures,
382            };
383            Ok(Some(encoder))
384        } else {
385            self.dst_buffers.clear();
386            self.dst_textures.clear();
387            Ok(None)
388        }
389    }
390
391    pub fn activate(&mut self) -> &mut dyn hal::DynCommandEncoder {
392        if !self.is_recording {
393            unsafe {
394                self.command_encoder
395                    .begin_encoding(Some("(wgpu internal) PendingWrites"))
396                    .unwrap();
397            }
398            self.is_recording = true;
399        }
400        self.command_encoder.as_mut()
401    }
402}
403
404impl Drop for PendingWrites {
405    fn drop(&mut self) {
406        unsafe {
407            if self.is_recording {
408                self.command_encoder.discard_encoding();
409            }
410        }
411    }
412}
413
414#[derive(Clone, Debug, Error)]
415#[non_exhaustive]
416pub enum QueueWriteError {
417    #[error(transparent)]
418    Queue(#[from] DeviceError),
419    #[error(transparent)]
420    Transfer(#[from] TransferError),
421    #[error(transparent)]
422    MemoryInitFailure(#[from] ClearError),
423    #[error(transparent)]
424    DestroyedResource(#[from] DestroyedResourceError),
425    #[error(transparent)]
426    InvalidResource(#[from] InvalidResourceError),
427}
428
429#[derive(Clone, Debug, Error)]
430#[non_exhaustive]
431pub enum QueueSubmitError {
432    #[error(transparent)]
433    Queue(#[from] DeviceError),
434    #[error(transparent)]
435    DestroyedResource(#[from] DestroyedResourceError),
436    #[error(transparent)]
437    Unmap(#[from] BufferAccessError),
438    #[error("{0} is still mapped")]
439    BufferStillMapped(ResourceErrorIdent),
440    #[error(transparent)]
441    InvalidResource(#[from] InvalidResourceError),
442    #[error(transparent)]
443    CommandEncoder(#[from] CommandEncoderError),
444    #[error(transparent)]
445    ValidateBlasActionsError(#[from] crate::ray_tracing::ValidateBlasActionsError),
446    #[error(transparent)]
447    ValidateTlasActionsError(#[from] crate::ray_tracing::ValidateTlasActionsError),
448}
449
450//TODO: move out common parts of write_xxx.
451
452impl Queue {
453    pub fn write_buffer(
454        &self,
455        buffer: Fallible<Buffer>,
456        buffer_offset: wgt::BufferAddress,
457        data: &[u8],
458    ) -> Result<(), QueueWriteError> {
459        profiling::scope!("Queue::write_buffer");
460        api_log!("Queue::write_buffer");
461
462        let buffer = buffer.get()?;
463
464        let data_size = data.len() as wgt::BufferAddress;
465
466        self.same_device_as(buffer.as_ref())?;
467
468        let data_size = if let Some(data_size) = wgt::BufferSize::new(data_size) {
469            data_size
470        } else {
471            log::trace!("Ignoring write_buffer of size 0");
472            return Ok(());
473        };
474
475        // Platform validation requires that the staging buffer always be
476        // freed, even if an error occurs. All paths from here must call
477        // `device.pending_writes.consume`.
478        let mut staging_buffer = StagingBuffer::new(&self.device, data_size)?;
479        let mut pending_writes = self.pending_writes.lock();
480
481        let staging_buffer = {
482            profiling::scope!("copy");
483            staging_buffer.write(data);
484            staging_buffer.flush()
485        };
486
487        let result = self.write_staging_buffer_impl(
488            &mut pending_writes,
489            &staging_buffer,
490            buffer,
491            buffer_offset,
492        );
493
494        pending_writes.consume(staging_buffer);
495        result
496    }
497
498    pub fn create_staging_buffer(
499        &self,
500        buffer_size: wgt::BufferSize,
501    ) -> Result<(StagingBuffer, NonNull<u8>), QueueWriteError> {
502        profiling::scope!("Queue::create_staging_buffer");
503        resource_log!("Queue::create_staging_buffer");
504
505        let staging_buffer = StagingBuffer::new(&self.device, buffer_size)?;
506        let ptr = unsafe { staging_buffer.ptr() };
507
508        Ok((staging_buffer, ptr))
509    }
510
511    pub fn write_staging_buffer(
512        &self,
513        buffer: Fallible<Buffer>,
514        buffer_offset: wgt::BufferAddress,
515        staging_buffer: StagingBuffer,
516    ) -> Result<(), QueueWriteError> {
517        profiling::scope!("Queue::write_staging_buffer");
518
519        let buffer = buffer.get()?;
520
521        let mut pending_writes = self.pending_writes.lock();
522
523        // At this point, we have taken ownership of the staging_buffer from the
524        // user. Platform validation requires that the staging buffer always
525        // be freed, even if an error occurs. All paths from here must call
526        // `device.pending_writes.consume`.
527        let staging_buffer = staging_buffer.flush();
528
529        let result = self.write_staging_buffer_impl(
530            &mut pending_writes,
531            &staging_buffer,
532            buffer,
533            buffer_offset,
534        );
535
536        pending_writes.consume(staging_buffer);
537        result
538    }
539
540    pub fn validate_write_buffer(
541        &self,
542        buffer: Fallible<Buffer>,
543        buffer_offset: u64,
544        buffer_size: wgt::BufferSize,
545    ) -> Result<(), QueueWriteError> {
546        profiling::scope!("Queue::validate_write_buffer");
547
548        let buffer = buffer.get()?;
549
550        self.validate_write_buffer_impl(&buffer, buffer_offset, buffer_size)?;
551
552        Ok(())
553    }
554
555    fn validate_write_buffer_impl(
556        &self,
557        buffer: &Buffer,
558        buffer_offset: u64,
559        buffer_size: wgt::BufferSize,
560    ) -> Result<(), TransferError> {
561        buffer.check_usage(wgt::BufferUsages::COPY_DST)?;
562        if buffer_size.get() % wgt::COPY_BUFFER_ALIGNMENT != 0 {
563            return Err(TransferError::UnalignedCopySize(buffer_size.get()));
564        }
565        if buffer_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
566            return Err(TransferError::UnalignedBufferOffset(buffer_offset));
567        }
568        if buffer_offset + buffer_size.get() > buffer.size {
569            return Err(TransferError::BufferOverrun {
570                start_offset: buffer_offset,
571                end_offset: buffer_offset + buffer_size.get(),
572                buffer_size: buffer.size,
573                side: CopySide::Destination,
574            });
575        }
576
577        Ok(())
578    }
579
580    fn write_staging_buffer_impl(
581        &self,
582        pending_writes: &mut PendingWrites,
583        staging_buffer: &FlushedStagingBuffer,
584        buffer: Arc<Buffer>,
585        buffer_offset: u64,
586    ) -> Result<(), QueueWriteError> {
587        let transition = {
588            let mut trackers = self.device.trackers.lock();
589            trackers
590                .buffers
591                .set_single(&buffer, hal::BufferUses::COPY_DST)
592        };
593
594        let snatch_guard = self.device.snatchable_lock.read();
595        let dst_raw = buffer.try_raw(&snatch_guard)?;
596
597        self.same_device_as(buffer.as_ref())?;
598
599        self.validate_write_buffer_impl(&buffer, buffer_offset, staging_buffer.size)?;
600
601        let region = hal::BufferCopy {
602            src_offset: 0,
603            dst_offset: buffer_offset,
604            size: staging_buffer.size,
605        };
606        let barriers = iter::once(hal::BufferBarrier {
607            buffer: staging_buffer.raw(),
608            usage: hal::StateTransition {
609                from: hal::BufferUses::MAP_WRITE,
610                to: hal::BufferUses::COPY_SRC,
611            },
612        })
613        .chain(transition.map(|pending| pending.into_hal(&buffer, &snatch_guard)))
614        .collect::<Vec<_>>();
615        let encoder = pending_writes.activate();
616        unsafe {
617            encoder.transition_buffers(&barriers);
618            encoder.copy_buffer_to_buffer(staging_buffer.raw(), dst_raw, &[region]);
619        }
620
621        pending_writes.insert_buffer(&buffer);
622
623        // Ensure the overwritten bytes are marked as initialized so
624        // they don't need to be nulled prior to mapping or binding.
625        {
626            buffer
627                .initialization_status
628                .write()
629                .drain(buffer_offset..(buffer_offset + staging_buffer.size.get()));
630        }
631
632        Ok(())
633    }
634
635    pub fn write_texture(
636        &self,
637        destination: wgt::TexelCopyTextureInfo<Fallible<Texture>>,
638        data: &[u8],
639        data_layout: &wgt::TexelCopyBufferLayout,
640        size: &wgt::Extent3d,
641    ) -> Result<(), QueueWriteError> {
642        profiling::scope!("Queue::write_texture");
643        api_log!("Queue::write_texture");
644
645        if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
646            log::trace!("Ignoring write_texture of size 0");
647            return Ok(());
648        }
649
650        let dst = destination.texture.get()?;
651        let destination = wgt::TexelCopyTextureInfo {
652            texture: (),
653            mip_level: destination.mip_level,
654            origin: destination.origin,
655            aspect: destination.aspect,
656        };
657
658        self.same_device_as(dst.as_ref())?;
659
660        dst.check_usage(wgt::TextureUsages::COPY_DST)
661            .map_err(TransferError::MissingTextureUsage)?;
662
663        // Note: Doing the copy range validation early is important because ensures that the
664        // dimensions are not going to cause overflow in other parts of the validation.
665        let (hal_copy_size, array_layer_count) =
666            validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, size)?;
667
668        let (selector, dst_base) = extract_texture_selector(&destination, size, &dst)?;
669
670        if !dst_base.aspect.is_one() {
671            return Err(TransferError::CopyAspectNotOne.into());
672        }
673
674        if !conv::is_valid_copy_dst_texture_format(dst.desc.format, destination.aspect) {
675            return Err(TransferError::CopyToForbiddenTextureFormat {
676                format: dst.desc.format,
677                aspect: destination.aspect,
678            }
679            .into());
680        }
681
682        // Note: `_source_bytes_per_array_layer` is ignored since we
683        // have a staging copy, and it can have a different value.
684        let (required_bytes_in_copy, _source_bytes_per_array_layer) = validate_linear_texture_data(
685            data_layout,
686            dst.desc.format,
687            destination.aspect,
688            data.len() as wgt::BufferAddress,
689            CopySide::Source,
690            size,
691            false,
692        )?;
693
694        if dst.desc.format.is_depth_stencil_format() {
695            self.device
696                .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
697                .map_err(TransferError::from)?;
698        }
699
700        let snatch_guard = self.device.snatchable_lock.read();
701
702        let mut pending_writes = self.pending_writes.lock();
703        let encoder = pending_writes.activate();
704
705        // If the copy does not fully cover the layers, we need to initialize to
706        // zero *first* as we don't keep track of partial texture layer inits.
707        //
708        // Strictly speaking we only need to clear the areas of a layer
709        // untouched, but this would get increasingly messy.
710        let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
711            // volume textures don't have a layer range as array volumes aren't supported
712            0..1
713        } else {
714            destination.origin.z..destination.origin.z + size.depth_or_array_layers
715        };
716        let mut dst_initialization_status = dst.initialization_status.write();
717        if dst_initialization_status.mips[destination.mip_level as usize]
718            .check(init_layer_range.clone())
719            .is_some()
720        {
721            if has_copy_partial_init_tracker_coverage(size, destination.mip_level, &dst.desc) {
722                for layer_range in dst_initialization_status.mips[destination.mip_level as usize]
723                    .drain(init_layer_range)
724                    .collect::<Vec<std::ops::Range<u32>>>()
725                {
726                    let mut trackers = self.device.trackers.lock();
727                    crate::command::clear_texture(
728                        &dst,
729                        TextureInitRange {
730                            mip_range: destination.mip_level..(destination.mip_level + 1),
731                            layer_range,
732                        },
733                        encoder,
734                        &mut trackers.textures,
735                        &self.device.alignments,
736                        self.device.zero_buffer.as_ref(),
737                        &snatch_guard,
738                    )
739                    .map_err(QueueWriteError::from)?;
740                }
741            } else {
742                dst_initialization_status.mips[destination.mip_level as usize]
743                    .drain(init_layer_range);
744            }
745        }
746
747        let dst_raw = dst.try_raw(&snatch_guard)?;
748
749        let (block_width, block_height) = dst.desc.format.block_dimensions();
750        let width_in_blocks = size.width / block_width;
751        let height_in_blocks = size.height / block_height;
752
753        let block_size = dst
754            .desc
755            .format
756            .block_copy_size(Some(destination.aspect))
757            .unwrap();
758        let bytes_in_last_row = width_in_blocks * block_size;
759
760        let bytes_per_row = data_layout.bytes_per_row.unwrap_or(bytes_in_last_row);
761        let rows_per_image = data_layout.rows_per_image.unwrap_or(height_in_blocks);
762
763        let bytes_per_row_alignment = get_lowest_common_denom(
764            self.device.alignments.buffer_copy_pitch.get() as u32,
765            block_size,
766        );
767        let stage_bytes_per_row = wgt::math::align_to(bytes_in_last_row, bytes_per_row_alignment);
768
769        // Platform validation requires that the staging buffer always be
770        // freed, even if an error occurs. All paths from here must call
771        // `device.pending_writes.consume`.
772        let staging_buffer = if stage_bytes_per_row == bytes_per_row {
773            profiling::scope!("copy aligned");
774            // Fast path if the data is already being aligned optimally.
775            let stage_size = wgt::BufferSize::new(required_bytes_in_copy).unwrap();
776            let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
777            staging_buffer.write(&data[data_layout.offset as usize..]);
778            staging_buffer
779        } else {
780            profiling::scope!("copy chunked");
781            // Copy row by row into the optimal alignment.
782            let block_rows_in_copy =
783                (size.depth_or_array_layers - 1) * rows_per_image + height_in_blocks;
784            let stage_size =
785                wgt::BufferSize::new(stage_bytes_per_row as u64 * block_rows_in_copy as u64)
786                    .unwrap();
787            let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
788            let copy_bytes_per_row = stage_bytes_per_row.min(bytes_per_row) as usize;
789            for layer in 0..size.depth_or_array_layers {
790                let rows_offset = layer * rows_per_image;
791                for row in rows_offset..rows_offset + height_in_blocks {
792                    let src_offset = data_layout.offset as u32 + row * bytes_per_row;
793                    let dst_offset = row * stage_bytes_per_row;
794                    unsafe {
795                        staging_buffer.write_with_offset(
796                            data,
797                            src_offset as isize,
798                            dst_offset as isize,
799                            copy_bytes_per_row,
800                        )
801                    }
802                }
803            }
804            staging_buffer
805        };
806
807        let staging_buffer = staging_buffer.flush();
808
809        let regions = (0..array_layer_count)
810            .map(|array_layer_offset| {
811                let mut texture_base = dst_base.clone();
812                texture_base.array_layer += array_layer_offset;
813                hal::BufferTextureCopy {
814                    buffer_layout: wgt::TexelCopyBufferLayout {
815                        offset: array_layer_offset as u64
816                            * rows_per_image as u64
817                            * stage_bytes_per_row as u64,
818                        bytes_per_row: Some(stage_bytes_per_row),
819                        rows_per_image: Some(rows_per_image),
820                    },
821                    texture_base,
822                    size: hal_copy_size,
823                }
824            })
825            .collect::<Vec<_>>();
826
827        {
828            let buffer_barrier = hal::BufferBarrier {
829                buffer: staging_buffer.raw(),
830                usage: hal::StateTransition {
831                    from: hal::BufferUses::MAP_WRITE,
832                    to: hal::BufferUses::COPY_SRC,
833                },
834            };
835
836            let mut trackers = self.device.trackers.lock();
837            let transition =
838                trackers
839                    .textures
840                    .set_single(&dst, selector, hal::TextureUses::COPY_DST);
841            let texture_barriers = transition
842                .map(|pending| pending.into_hal(dst_raw))
843                .collect::<Vec<_>>();
844
845            unsafe {
846                encoder.transition_textures(&texture_barriers);
847                encoder.transition_buffers(&[buffer_barrier]);
848                encoder.copy_buffer_to_texture(staging_buffer.raw(), dst_raw, &regions);
849            }
850        }
851
852        pending_writes.consume(staging_buffer);
853        pending_writes.insert_texture(&dst);
854
855        Ok(())
856    }
857
858    #[cfg(webgl)]
859    pub fn copy_external_image_to_texture(
860        &self,
861        source: &wgt::CopyExternalImageSourceInfo,
862        destination: wgt::CopyExternalImageDestInfo<Fallible<Texture>>,
863        size: wgt::Extent3d,
864    ) -> Result<(), QueueWriteError> {
865        profiling::scope!("Queue::copy_external_image_to_texture");
866
867        if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
868            log::trace!("Ignoring write_texture of size 0");
869            return Ok(());
870        }
871
872        let mut needs_flag = false;
873        needs_flag |= matches!(source.source, wgt::ExternalImageSource::OffscreenCanvas(_));
874        needs_flag |= source.origin != wgt::Origin2d::ZERO;
875        needs_flag |= destination.color_space != wgt::PredefinedColorSpace::Srgb;
876        #[allow(clippy::bool_comparison)]
877        if matches!(source.source, wgt::ExternalImageSource::ImageBitmap(_)) {
878            needs_flag |= source.flip_y != false;
879            needs_flag |= destination.premultiplied_alpha != false;
880        }
881
882        if needs_flag {
883            self.device
884                .require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES)
885                .map_err(TransferError::from)?;
886        }
887
888        let src_width = source.source.width();
889        let src_height = source.source.height();
890
891        let dst = destination.texture.get()?;
892        let premultiplied_alpha = destination.premultiplied_alpha;
893        let destination = wgt::TexelCopyTextureInfo {
894            texture: (),
895            mip_level: destination.mip_level,
896            origin: destination.origin,
897            aspect: destination.aspect,
898        };
899
900        if !conv::is_valid_external_image_copy_dst_texture_format(dst.desc.format) {
901            return Err(
902                TransferError::ExternalCopyToForbiddenTextureFormat(dst.desc.format).into(),
903            );
904        }
905        if dst.desc.dimension != wgt::TextureDimension::D2 {
906            return Err(TransferError::InvalidDimensionExternal.into());
907        }
908        dst.check_usage(wgt::TextureUsages::COPY_DST | wgt::TextureUsages::RENDER_ATTACHMENT)
909            .map_err(TransferError::MissingTextureUsage)?;
910        if dst.desc.sample_count != 1 {
911            return Err(TransferError::InvalidSampleCount {
912                sample_count: dst.desc.sample_count,
913            }
914            .into());
915        }
916
917        if source.origin.x + size.width > src_width {
918            return Err(TransferError::TextureOverrun {
919                start_offset: source.origin.x,
920                end_offset: source.origin.x + size.width,
921                texture_size: src_width,
922                dimension: crate::resource::TextureErrorDimension::X,
923                side: CopySide::Source,
924            }
925            .into());
926        }
927        if source.origin.y + size.height > src_height {
928            return Err(TransferError::TextureOverrun {
929                start_offset: source.origin.y,
930                end_offset: source.origin.y + size.height,
931                texture_size: src_height,
932                dimension: crate::resource::TextureErrorDimension::Y,
933                side: CopySide::Source,
934            }
935            .into());
936        }
937        if size.depth_or_array_layers != 1 {
938            return Err(TransferError::TextureOverrun {
939                start_offset: 0,
940                end_offset: size.depth_or_array_layers,
941                texture_size: 1,
942                dimension: crate::resource::TextureErrorDimension::Z,
943                side: CopySide::Source,
944            }
945            .into());
946        }
947
948        // Note: Doing the copy range validation early is important because ensures that the
949        // dimensions are not going to cause overflow in other parts of the validation.
950        let (hal_copy_size, _) =
951            validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, &size)?;
952
953        let (selector, dst_base) = extract_texture_selector(&destination, &size, &dst)?;
954
955        let mut pending_writes = self.pending_writes.lock();
956        let encoder = pending_writes.activate();
957
958        // If the copy does not fully cover the layers, we need to initialize to
959        // zero *first* as we don't keep track of partial texture layer inits.
960        //
961        // Strictly speaking we only need to clear the areas of a layer
962        // untouched, but this would get increasingly messy.
963        let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
964            // volume textures don't have a layer range as array volumes aren't supported
965            0..1
966        } else {
967            destination.origin.z..destination.origin.z + size.depth_or_array_layers
968        };
969        let mut dst_initialization_status = dst.initialization_status.write();
970        if dst_initialization_status.mips[destination.mip_level as usize]
971            .check(init_layer_range.clone())
972            .is_some()
973        {
974            if has_copy_partial_init_tracker_coverage(&size, destination.mip_level, &dst.desc) {
975                for layer_range in dst_initialization_status.mips[destination.mip_level as usize]
976                    .drain(init_layer_range)
977                    .collect::<Vec<std::ops::Range<u32>>>()
978                {
979                    let mut trackers = self.device.trackers.lock();
980                    crate::command::clear_texture(
981                        &dst,
982                        TextureInitRange {
983                            mip_range: destination.mip_level..(destination.mip_level + 1),
984                            layer_range,
985                        },
986                        encoder,
987                        &mut trackers.textures,
988                        &self.device.alignments,
989                        self.device.zero_buffer.as_ref(),
990                        &self.device.snatchable_lock.read(),
991                    )
992                    .map_err(QueueWriteError::from)?;
993                }
994            } else {
995                dst_initialization_status.mips[destination.mip_level as usize]
996                    .drain(init_layer_range);
997            }
998        }
999
1000        let snatch_guard = self.device.snatchable_lock.read();
1001        let dst_raw = dst.try_raw(&snatch_guard)?;
1002
1003        let regions = hal::TextureCopy {
1004            src_base: hal::TextureCopyBase {
1005                mip_level: 0,
1006                array_layer: 0,
1007                origin: source.origin.to_3d(0),
1008                aspect: hal::FormatAspects::COLOR,
1009            },
1010            dst_base,
1011            size: hal_copy_size,
1012        };
1013
1014        let mut trackers = self.device.trackers.lock();
1015        let transitions = trackers
1016            .textures
1017            .set_single(&dst, selector, hal::TextureUses::COPY_DST);
1018
1019        // `copy_external_image_to_texture` is exclusive to the WebGL backend.
1020        // Don't go through the `DynCommandEncoder` abstraction and directly to the WebGL backend.
1021        let encoder_webgl = encoder
1022            .as_any_mut()
1023            .downcast_mut::<hal::gles::CommandEncoder>()
1024            .unwrap();
1025        let dst_raw_webgl = dst_raw
1026            .as_any()
1027            .downcast_ref::<hal::gles::Texture>()
1028            .unwrap();
1029        let transitions_webgl = transitions.map(|pending| {
1030            let dyn_transition = pending.into_hal(dst_raw);
1031            hal::TextureBarrier {
1032                texture: dst_raw_webgl,
1033                range: dyn_transition.range,
1034                usage: dyn_transition.usage,
1035            }
1036        });
1037
1038        use hal::CommandEncoder as _;
1039        unsafe {
1040            encoder_webgl.transition_textures(transitions_webgl);
1041            encoder_webgl.copy_external_image_to_texture(
1042                source,
1043                dst_raw_webgl,
1044                premultiplied_alpha,
1045                iter::once(regions),
1046            );
1047        }
1048
1049        Ok(())
1050    }
1051
1052    pub fn submit(
1053        &self,
1054        command_buffers: &[Arc<CommandBuffer>],
1055    ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {
1056        profiling::scope!("Queue::submit");
1057        api_log!("Queue::submit");
1058
1059        let submit_index;
1060
1061        let res = 'error: {
1062            let snatch_guard = self.device.snatchable_lock.read();
1063
1064            // Fence lock must be acquired after the snatch lock everywhere to avoid deadlocks.
1065            let mut fence = self.device.fence.write();
1066            submit_index = self
1067                .device
1068                .active_submission_index
1069                .fetch_add(1, Ordering::SeqCst)
1070                + 1;
1071            let mut active_executions = Vec::new();
1072
1073            let mut used_surface_textures = track::TextureUsageScope::default();
1074
1075            // Use a hashmap here to deduplicate the surface textures that are used in the command buffers.
1076            // This avoids vulkan deadlocking from the same surface texture being submitted multiple times.
1077            let mut submit_surface_textures_owned = FastHashMap::default();
1078
1079            {
1080                if !command_buffers.is_empty() {
1081                    profiling::scope!("prepare");
1082
1083                    let mut first_error = None;
1084
1085                    //TODO: if multiple command buffers are submitted, we can re-use the last
1086                    // native command buffer of the previous chain instead of always creating
1087                    // a temporary one, since the chains are not finished.
1088
1089                    // finish all the command buffers first
1090                    for command_buffer in command_buffers {
1091                        profiling::scope!("process command buffer");
1092
1093                        // we reset the used surface textures every time we use
1094                        // it, so make sure to set_size on it.
1095                        used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1096
1097                        // Note that we are required to invalidate all command buffers in both the success and failure paths.
1098                        // This is why we `continue` and don't early return via `?`.
1099                        #[allow(unused_mut)]
1100                        let mut cmd_buf_data = command_buffer.take_finished();
1101
1102                        #[cfg(feature = "trace")]
1103                        if let Some(ref mut trace) = *self.device.trace.lock() {
1104                            if let Ok(ref mut cmd_buf_data) = cmd_buf_data {
1105                                trace.add(Action::Submit(
1106                                    submit_index,
1107                                    cmd_buf_data.commands.take().unwrap(),
1108                                ));
1109                            }
1110                        }
1111
1112                        if first_error.is_some() {
1113                            continue;
1114                        }
1115
1116                        let mut baked = match cmd_buf_data {
1117                            Ok(cmd_buf_data) => {
1118                                let res = validate_command_buffer(
1119                                    command_buffer,
1120                                    self,
1121                                    &cmd_buf_data,
1122                                    &snatch_guard,
1123                                    &mut submit_surface_textures_owned,
1124                                    &mut used_surface_textures,
1125                                );
1126                                if let Err(err) = res {
1127                                    first_error.get_or_insert(err);
1128                                    continue;
1129                                }
1130                                cmd_buf_data.into_baked_commands()
1131                            }
1132                            Err(err) => {
1133                                first_error.get_or_insert(err.into());
1134                                continue;
1135                            }
1136                        };
1137
1138                        // execute resource transitions
1139                        if let Err(e) = baked.encoder.open_pass(Some("(wgpu internal) Transit")) {
1140                            break 'error Err(e.into());
1141                        }
1142
1143                        //Note: locking the trackers has to be done after the storages
1144                        let mut trackers = self.device.trackers.lock();
1145                        if let Err(e) = baked.initialize_buffer_memory(&mut trackers, &snatch_guard)
1146                        {
1147                            break 'error Err(e.into());
1148                        }
1149                        if let Err(e) = baked.initialize_texture_memory(
1150                            &mut trackers,
1151                            &self.device,
1152                            &snatch_guard,
1153                        ) {
1154                            break 'error Err(e.into());
1155                        }
1156
1157                        //Note: stateless trackers are not merged:
1158                        // device already knows these resources exist.
1159                        CommandBuffer::insert_barriers_from_device_tracker(
1160                            baked.encoder.raw.as_mut(),
1161                            &mut trackers,
1162                            &baked.trackers,
1163                            &snatch_guard,
1164                        );
1165
1166                        if let Err(e) = baked.encoder.close_and_push_front() {
1167                            break 'error Err(e.into());
1168                        }
1169
1170                        // Transition surface textures into `Present` state.
1171                        // Note: we could technically do it after all of the command buffers,
1172                        // but here we have a command encoder by hand, so it's easier to use it.
1173                        if !used_surface_textures.is_empty() {
1174                            if let Err(e) = baked.encoder.open_pass(Some("(wgpu internal) Present"))
1175                            {
1176                                break 'error Err(e.into());
1177                            }
1178                            let texture_barriers = trackers
1179                                .textures
1180                                .set_from_usage_scope_and_drain_transitions(
1181                                    &used_surface_textures,
1182                                    &snatch_guard,
1183                                )
1184                                .collect::<Vec<_>>();
1185                            unsafe {
1186                                baked.encoder.raw.transition_textures(&texture_barriers);
1187                            };
1188                            if let Err(e) = baked.encoder.close() {
1189                                break 'error Err(e.into());
1190                            }
1191                            used_surface_textures = track::TextureUsageScope::default();
1192                        }
1193
1194                        // done
1195                        active_executions.push(EncoderInFlight {
1196                            inner: baked.encoder,
1197                            trackers: baked.trackers,
1198                            temp_resources: baked.temp_resources,
1199                            pending_buffers: FastHashMap::default(),
1200                            pending_textures: FastHashMap::default(),
1201                        });
1202                    }
1203
1204                    if let Some(first_error) = first_error {
1205                        break 'error Err(first_error);
1206                    }
1207                }
1208            }
1209
1210            let mut pending_writes = self.pending_writes.lock();
1211
1212            {
1213                used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1214                for texture in pending_writes.dst_textures.values() {
1215                    match texture.try_inner(&snatch_guard) {
1216                        Ok(TextureInner::Native { .. }) => {}
1217                        Ok(TextureInner::Surface { .. }) => {
1218                            // Compare the Arcs by pointer as Textures don't implement Eq
1219                            submit_surface_textures_owned
1220                                .insert(Arc::as_ptr(texture), texture.clone());
1221
1222                            unsafe {
1223                                used_surface_textures
1224                                    .merge_single(texture, None, hal::TextureUses::PRESENT)
1225                                    .unwrap()
1226                            };
1227                        }
1228                        Err(e) => break 'error Err(e.into()),
1229                    }
1230                }
1231
1232                if !used_surface_textures.is_empty() {
1233                    let mut trackers = self.device.trackers.lock();
1234
1235                    let texture_barriers = trackers
1236                        .textures
1237                        .set_from_usage_scope_and_drain_transitions(
1238                            &used_surface_textures,
1239                            &snatch_guard,
1240                        )
1241                        .collect::<Vec<_>>();
1242                    unsafe {
1243                        pending_writes
1244                            .command_encoder
1245                            .transition_textures(&texture_barriers);
1246                    };
1247                }
1248            }
1249
1250            match pending_writes.pre_submit(&self.device.command_allocator, &self.device, self) {
1251                Ok(Some(pending_execution)) => {
1252                    active_executions.insert(0, pending_execution);
1253                }
1254                Ok(None) => {}
1255                Err(e) => break 'error Err(e.into()),
1256            }
1257            let hal_command_buffers = active_executions
1258                .iter()
1259                .flat_map(|e| e.inner.list.iter().map(|b| b.as_ref()))
1260                .collect::<Vec<_>>();
1261
1262            {
1263                let mut submit_surface_textures =
1264                    SmallVec::<[&dyn hal::DynSurfaceTexture; 2]>::with_capacity(
1265                        submit_surface_textures_owned.len(),
1266                    );
1267
1268                for texture in submit_surface_textures_owned.values() {
1269                    let raw = match texture.inner.get(&snatch_guard) {
1270                        Some(TextureInner::Surface { raw, .. }) => raw.as_ref(),
1271                        _ => unreachable!(),
1272                    };
1273                    submit_surface_textures.push(raw);
1274                }
1275
1276                if let Err(e) = unsafe {
1277                    self.raw().submit(
1278                        &hal_command_buffers,
1279                        &submit_surface_textures,
1280                        (fence.as_mut(), submit_index),
1281                    )
1282                }
1283                .map_err(|e| self.device.handle_hal_error(e))
1284                {
1285                    break 'error Err(e.into());
1286                }
1287
1288                // Advance the successful submission index.
1289                self.device
1290                    .last_successful_submission_index
1291                    .fetch_max(submit_index, Ordering::SeqCst);
1292            }
1293
1294            profiling::scope!("cleanup");
1295
1296            // this will register the new submission to the life time tracker
1297            self.lock_life()
1298                .track_submission(submit_index, active_executions);
1299            drop(pending_writes);
1300
1301            // This will schedule destruction of all resources that are no longer needed
1302            // by the user but used in the command stream, among other things.
1303            let fence_guard = RwLockWriteGuard::downgrade(fence);
1304            let (closures, _) =
1305                match self
1306                    .device
1307                    .maintain(fence_guard, wgt::Maintain::Poll, snatch_guard)
1308                {
1309                    Ok(closures) => closures,
1310                    Err(WaitIdleError::Device(err)) => {
1311                        break 'error Err(QueueSubmitError::Queue(err))
1312                    }
1313                    Err(WaitIdleError::WrongSubmissionIndex(..)) => unreachable!(),
1314                };
1315
1316            Ok(closures)
1317        };
1318
1319        let callbacks = match res {
1320            Ok(ok) => ok,
1321            Err(e) => return Err((submit_index, e)),
1322        };
1323
1324        // the closures should execute with nothing locked!
1325        callbacks.fire();
1326
1327        api_log!("Queue::submit returned submit index {submit_index}");
1328
1329        Ok(submit_index)
1330    }
1331
1332    pub fn get_timestamp_period(&self) -> f32 {
1333        unsafe { self.raw().get_timestamp_period() }
1334    }
1335
1336    /// `closure` is guaranteed to be called.
1337    pub fn on_submitted_work_done(
1338        &self,
1339        closure: SubmittedWorkDoneClosure,
1340    ) -> Option<SubmissionIndex> {
1341        api_log!("Queue::on_submitted_work_done");
1342        //TODO: flush pending writes
1343        self.lock_life().add_work_done_closure(closure)
1344    }
1345}
1346
1347impl Global {
1348    pub fn queue_write_buffer(
1349        &self,
1350        queue_id: QueueId,
1351        buffer_id: id::BufferId,
1352        buffer_offset: wgt::BufferAddress,
1353        data: &[u8],
1354    ) -> Result<(), QueueWriteError> {
1355        let queue = self.hub.queues.get(queue_id);
1356
1357        #[cfg(feature = "trace")]
1358        if let Some(ref mut trace) = *queue.device.trace.lock() {
1359            let data_path = trace.make_binary("bin", data);
1360            trace.add(Action::WriteBuffer {
1361                id: buffer_id,
1362                data: data_path,
1363                range: buffer_offset..buffer_offset + data.len() as u64,
1364                queued: true,
1365            });
1366        }
1367
1368        let buffer = self.hub.buffers.get(buffer_id);
1369        queue.write_buffer(buffer, buffer_offset, data)
1370    }
1371
1372    pub fn queue_create_staging_buffer(
1373        &self,
1374        queue_id: QueueId,
1375        buffer_size: wgt::BufferSize,
1376        id_in: Option<id::StagingBufferId>,
1377    ) -> Result<(id::StagingBufferId, NonNull<u8>), QueueWriteError> {
1378        let queue = self.hub.queues.get(queue_id);
1379        let (staging_buffer, ptr) = queue.create_staging_buffer(buffer_size)?;
1380
1381        let fid = self.hub.staging_buffers.prepare(id_in);
1382        let id = fid.assign(staging_buffer);
1383
1384        Ok((id, ptr))
1385    }
1386
1387    pub fn queue_write_staging_buffer(
1388        &self,
1389        queue_id: QueueId,
1390        buffer_id: id::BufferId,
1391        buffer_offset: wgt::BufferAddress,
1392        staging_buffer_id: id::StagingBufferId,
1393    ) -> Result<(), QueueWriteError> {
1394        let queue = self.hub.queues.get(queue_id);
1395        let buffer = self.hub.buffers.get(buffer_id);
1396        let staging_buffer = self.hub.staging_buffers.remove(staging_buffer_id);
1397        queue.write_staging_buffer(buffer, buffer_offset, staging_buffer)
1398    }
1399
1400    pub fn queue_validate_write_buffer(
1401        &self,
1402        queue_id: QueueId,
1403        buffer_id: id::BufferId,
1404        buffer_offset: u64,
1405        buffer_size: wgt::BufferSize,
1406    ) -> Result<(), QueueWriteError> {
1407        let queue = self.hub.queues.get(queue_id);
1408        let buffer = self.hub.buffers.get(buffer_id);
1409        queue.validate_write_buffer(buffer, buffer_offset, buffer_size)
1410    }
1411
1412    pub fn queue_write_texture(
1413        &self,
1414        queue_id: QueueId,
1415        destination: &TexelCopyTextureInfo,
1416        data: &[u8],
1417        data_layout: &wgt::TexelCopyBufferLayout,
1418        size: &wgt::Extent3d,
1419    ) -> Result<(), QueueWriteError> {
1420        let queue = self.hub.queues.get(queue_id);
1421
1422        #[cfg(feature = "trace")]
1423        if let Some(ref mut trace) = *queue.device.trace.lock() {
1424            let data_path = trace.make_binary("bin", data);
1425            trace.add(Action::WriteTexture {
1426                to: *destination,
1427                data: data_path,
1428                layout: *data_layout,
1429                size: *size,
1430            });
1431        }
1432
1433        let destination = wgt::TexelCopyTextureInfo {
1434            texture: self.hub.textures.get(destination.texture),
1435            mip_level: destination.mip_level,
1436            origin: destination.origin,
1437            aspect: destination.aspect,
1438        };
1439        queue.write_texture(destination, data, data_layout, size)
1440    }
1441
1442    #[cfg(webgl)]
1443    pub fn queue_copy_external_image_to_texture(
1444        &self,
1445        queue_id: QueueId,
1446        source: &wgt::CopyExternalImageSourceInfo,
1447        destination: crate::command::CopyExternalImageDestInfo,
1448        size: wgt::Extent3d,
1449    ) -> Result<(), QueueWriteError> {
1450        let queue = self.hub.queues.get(queue_id);
1451        let destination = wgt::CopyExternalImageDestInfo {
1452            texture: self.hub.textures.get(destination.texture),
1453            mip_level: destination.mip_level,
1454            origin: destination.origin,
1455            aspect: destination.aspect,
1456            color_space: destination.color_space,
1457            premultiplied_alpha: destination.premultiplied_alpha,
1458        };
1459        queue.copy_external_image_to_texture(source, destination, size)
1460    }
1461
1462    pub fn queue_submit(
1463        &self,
1464        queue_id: QueueId,
1465        command_buffer_ids: &[id::CommandBufferId],
1466    ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {
1467        let queue = self.hub.queues.get(queue_id);
1468        let command_buffer_guard = self.hub.command_buffers.read();
1469        let command_buffers = command_buffer_ids
1470            .iter()
1471            .map(|id| command_buffer_guard.get(*id))
1472            .collect::<Vec<_>>();
1473        drop(command_buffer_guard);
1474        queue.submit(&command_buffers)
1475    }
1476
1477    pub fn queue_get_timestamp_period(&self, queue_id: QueueId) -> f32 {
1478        let queue = self.hub.queues.get(queue_id);
1479        queue.get_timestamp_period()
1480    }
1481
1482    pub fn queue_on_submitted_work_done(
1483        &self,
1484        queue_id: QueueId,
1485        closure: SubmittedWorkDoneClosure,
1486    ) -> SubmissionIndex {
1487        api_log!("Queue::on_submitted_work_done {queue_id:?}");
1488
1489        //TODO: flush pending writes
1490        let queue = self.hub.queues.get(queue_id);
1491        let result = queue.on_submitted_work_done(closure);
1492        result.unwrap_or(0) // '0' means no wait is necessary
1493    }
1494}
1495
1496fn validate_command_buffer(
1497    command_buffer: &CommandBuffer,
1498    queue: &Queue,
1499    cmd_buf_data: &crate::command::CommandBufferMutable,
1500    snatch_guard: &SnatchGuard,
1501    submit_surface_textures_owned: &mut FastHashMap<*const Texture, Arc<Texture>>,
1502    used_surface_textures: &mut track::TextureUsageScope,
1503) -> Result<(), QueueSubmitError> {
1504    command_buffer.same_device_as(queue)?;
1505
1506    {
1507        profiling::scope!("check resource state");
1508
1509        {
1510            profiling::scope!("buffers");
1511            for buffer in cmd_buf_data.trackers.buffers.used_resources() {
1512                buffer.check_destroyed(snatch_guard)?;
1513
1514                match *buffer.map_state.lock() {
1515                    BufferMapState::Idle => (),
1516                    _ => return Err(QueueSubmitError::BufferStillMapped(buffer.error_ident())),
1517                }
1518            }
1519        }
1520        {
1521            profiling::scope!("textures");
1522            for texture in cmd_buf_data.trackers.textures.used_resources() {
1523                let should_extend = match texture.try_inner(snatch_guard)? {
1524                    TextureInner::Native { .. } => false,
1525                    TextureInner::Surface { .. } => {
1526                        // Compare the Arcs by pointer as Textures don't implement Eq.
1527                        submit_surface_textures_owned.insert(Arc::as_ptr(texture), texture.clone());
1528
1529                        true
1530                    }
1531                };
1532                if should_extend {
1533                    unsafe {
1534                        used_surface_textures
1535                            .merge_single(texture, None, hal::TextureUses::PRESENT)
1536                            .unwrap();
1537                    };
1538                }
1539            }
1540        }
1541
1542        if let Err(e) = cmd_buf_data.validate_blas_actions() {
1543            return Err(e.into());
1544        }
1545        if let Err(e) = cmd_buf_data.validate_tlas_actions(snatch_guard) {
1546            return Err(e.into());
1547        }
1548    }
1549    Ok(())
1550}