li_wgpu_core/device/
queue.rs

1#[cfg(feature = "trace")]
2use crate::device::trace::Action;
3use crate::{
4    command::{
5        extract_texture_selector, validate_linear_texture_data, validate_texture_copy_range,
6        ClearError, CommandBuffer, CopySide, ImageCopyTexture, TransferError,
7    },
8    conv,
9    device::{DeviceError, WaitIdleError},
10    get_lowest_common_denom,
11    global::Global,
12    hal_api::HalApi,
13    hal_label,
14    hub::Token,
15    id,
16    identity::{GlobalIdentityHandlerFactory, Input},
17    init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange},
18    resource::{BufferAccessError, BufferMapState, StagingBuffer, TextureInner},
19    track, FastHashSet, SubmissionIndex,
20};
21
22use hal::{CommandEncoder as _, Device as _, Queue as _};
23use parking_lot::Mutex;
24use smallvec::SmallVec;
25use std::{iter, mem, ptr};
26use thiserror::Error;
27
28/// Number of command buffers that we generate from the same pool
29/// for the write_xxx commands, before the pool is recycled.
30///
31/// If we don't stop at some point, the pool will grow forever,
32/// without a concrete moment of when it can be cleared.
33const WRITE_COMMAND_BUFFERS_PER_POOL: usize = 64;
34
35#[repr(C)]
36pub struct SubmittedWorkDoneClosureC {
37    pub callback: unsafe extern "C" fn(user_data: *mut u8),
38    pub user_data: *mut u8,
39}
40
41#[cfg(any(
42    not(target_arch = "wasm32"),
43    all(
44        feature = "fragile-send-sync-non-atomic-wasm",
45        not(target_feature = "atomics")
46    )
47))]
48unsafe impl Send for SubmittedWorkDoneClosureC {}
49
50pub struct SubmittedWorkDoneClosure {
51    // We wrap this so creating the enum in the C variant can be unsafe,
52    // allowing our call function to be safe.
53    inner: SubmittedWorkDoneClosureInner,
54}
55
56#[cfg(any(
57    not(target_arch = "wasm32"),
58    all(
59        feature = "fragile-send-sync-non-atomic-wasm",
60        not(target_feature = "atomics")
61    )
62))]
63type SubmittedWorkDoneCallback = Box<dyn FnOnce() + Send + 'static>;
64#[cfg(not(any(
65    not(target_arch = "wasm32"),
66    all(
67        feature = "fragile-send-sync-non-atomic-wasm",
68        not(target_feature = "atomics")
69    )
70)))]
71type SubmittedWorkDoneCallback = Box<dyn FnOnce() + 'static>;
72
73enum SubmittedWorkDoneClosureInner {
74    Rust { callback: SubmittedWorkDoneCallback },
75    C { inner: SubmittedWorkDoneClosureC },
76}
77
78impl SubmittedWorkDoneClosure {
79    pub fn from_rust(callback: SubmittedWorkDoneCallback) -> Self {
80        Self {
81            inner: SubmittedWorkDoneClosureInner::Rust { callback },
82        }
83    }
84
85    /// # Safety
86    ///
87    /// - The callback pointer must be valid to call with the provided `user_data`
88    ///   pointer.
89    ///
90    /// - Both pointers must point to `'static` data, as the callback may happen at
91    ///   an unspecified time.
92    pub unsafe fn from_c(inner: SubmittedWorkDoneClosureC) -> Self {
93        Self {
94            inner: SubmittedWorkDoneClosureInner::C { inner },
95        }
96    }
97
98    pub(crate) fn call(self) {
99        match self.inner {
100            SubmittedWorkDoneClosureInner::Rust { callback } => callback(),
101            // SAFETY: the contract of the call to from_c says that this unsafe is sound.
102            SubmittedWorkDoneClosureInner::C { inner } => unsafe {
103                (inner.callback)(inner.user_data)
104            },
105        }
106    }
107}
108
109#[repr(C)]
110#[derive(Debug, Copy, Clone)]
111pub struct WrappedSubmissionIndex {
112    pub queue_id: id::QueueId,
113    pub index: SubmissionIndex,
114}
115
116/// A texture or buffer to be freed soon.
117///
118/// This is just a tagged raw texture or buffer, generally about to be added to
119/// some other more specific container like:
120///
121/// - `PendingWrites::temp_resources`: resources used by queue writes and
122///   unmaps, waiting to be folded in with the next queue submission
123///
124/// - `ActiveSubmission::last_resources`: temporary resources used by a queue
125///   submission, to be freed when it completes
126///
127/// - `LifetimeTracker::free_resources`: resources to be freed in the next
128///   `maintain` call, no longer used anywhere
129#[derive(Debug)]
130pub enum TempResource<A: hal::Api> {
131    Buffer(A::Buffer),
132    Texture(A::Texture, SmallVec<[A::TextureView; 1]>),
133}
134
135/// A queue execution for a particular command encoder.
136pub(super) struct EncoderInFlight<A: hal::Api> {
137    raw: A::CommandEncoder,
138    cmd_buffers: Vec<A::CommandBuffer>,
139}
140
141impl<A: hal::Api> EncoderInFlight<A> {
142    pub(super) unsafe fn land(mut self) -> A::CommandEncoder {
143        unsafe { self.raw.reset_all(self.cmd_buffers.into_iter()) };
144        self.raw
145    }
146}
147
148/// A private command encoder for writes made directly on the device
149/// or queue.
150///
151/// Operations like `buffer_unmap`, `queue_write_buffer`, and
152/// `queue_write_texture` need to copy data to the GPU. At the hal
153/// level, this must be done by encoding and submitting commands, but
154/// these operations are not associated with any specific wgpu command
155/// buffer.
156///
157/// Instead, `Device::pending_writes` owns one of these values, which
158/// has its own hal command encoder and resource lists. The commands
159/// accumulated here are automatically submitted to the queue the next
160/// time the user submits a wgpu command buffer, ahead of the user's
161/// commands.
162///
163/// All uses of [`StagingBuffer`]s end up here.
164#[derive(Debug)]
165pub(crate) struct PendingWrites<A: hal::Api> {
166    pub command_encoder: A::CommandEncoder,
167    pub is_active: bool,
168    pub temp_resources: Vec<TempResource<A>>,
169    pub dst_buffers: FastHashSet<id::BufferId>,
170    pub dst_textures: FastHashSet<id::TextureId>,
171    pub executing_command_buffers: Vec<A::CommandBuffer>,
172}
173
174impl<A: hal::Api> PendingWrites<A> {
175    pub fn new(command_encoder: A::CommandEncoder) -> Self {
176        Self {
177            command_encoder,
178            is_active: false,
179            temp_resources: Vec::new(),
180            dst_buffers: FastHashSet::default(),
181            dst_textures: FastHashSet::default(),
182            executing_command_buffers: Vec::new(),
183        }
184    }
185
186    pub fn dispose(mut self, device: &A::Device) {
187        unsafe {
188            if self.is_active {
189                self.command_encoder.discard_encoding();
190            }
191            self.command_encoder
192                .reset_all(self.executing_command_buffers.into_iter());
193            device.destroy_command_encoder(self.command_encoder);
194        }
195
196        for resource in self.temp_resources {
197            match resource {
198                TempResource::Buffer(buffer) => unsafe {
199                    device.destroy_buffer(buffer);
200                },
201                TempResource::Texture(texture, views) => unsafe {
202                    for view in views.into_iter() {
203                        device.destroy_texture_view(view);
204                    }
205                    device.destroy_texture(texture);
206                },
207            }
208        }
209    }
210
211    pub fn consume_temp(&mut self, resource: TempResource<A>) {
212        self.temp_resources.push(resource);
213    }
214
215    fn consume(&mut self, buffer: StagingBuffer<A>) {
216        self.temp_resources.push(TempResource::Buffer(buffer.raw));
217    }
218
219    #[must_use]
220    fn pre_submit(&mut self) -> Option<&A::CommandBuffer> {
221        self.dst_buffers.clear();
222        self.dst_textures.clear();
223        if self.is_active {
224            let cmd_buf = unsafe { self.command_encoder.end_encoding().unwrap() };
225            self.is_active = false;
226            self.executing_command_buffers.push(cmd_buf);
227            self.executing_command_buffers.last()
228        } else {
229            None
230        }
231    }
232
233    #[must_use]
234    fn post_submit(
235        &mut self,
236        command_allocator: &Mutex<super::CommandAllocator<A>>,
237        device: &A::Device,
238        queue: &A::Queue,
239    ) -> Option<EncoderInFlight<A>> {
240        if self.executing_command_buffers.len() >= WRITE_COMMAND_BUFFERS_PER_POOL {
241            let new_encoder = command_allocator
242                .lock()
243                .acquire_encoder(device, queue)
244                .unwrap();
245            Some(EncoderInFlight {
246                raw: mem::replace(&mut self.command_encoder, new_encoder),
247                cmd_buffers: mem::take(&mut self.executing_command_buffers),
248            })
249        } else {
250            None
251        }
252    }
253
254    pub fn activate(&mut self) -> &mut A::CommandEncoder {
255        if !self.is_active {
256            unsafe {
257                self.command_encoder
258                    .begin_encoding(Some("(wgpu internal) PendingWrites"))
259                    .unwrap();
260            }
261            self.is_active = true;
262        }
263        &mut self.command_encoder
264    }
265
266    pub fn deactivate(&mut self) {
267        if self.is_active {
268            unsafe {
269                self.command_encoder.discard_encoding();
270            }
271            self.is_active = false;
272        }
273    }
274}
275
276fn prepare_staging_buffer<A: HalApi>(
277    device: &mut A::Device,
278    size: wgt::BufferAddress,
279    instance_flags: wgt::InstanceFlags,
280) -> Result<(StagingBuffer<A>, *mut u8), DeviceError> {
281    profiling::scope!("prepare_staging_buffer");
282    let stage_desc = hal::BufferDescriptor {
283        label: hal_label(Some("(wgpu internal) Staging"), instance_flags),
284        size,
285        usage: hal::BufferUses::MAP_WRITE | hal::BufferUses::COPY_SRC,
286        memory_flags: hal::MemoryFlags::TRANSIENT,
287    };
288
289    let buffer = unsafe { device.create_buffer(&stage_desc)? };
290    let mapping = unsafe { device.map_buffer(&buffer, 0..size) }?;
291
292    let staging_buffer = StagingBuffer {
293        raw: buffer,
294        size,
295        is_coherent: mapping.is_coherent,
296    };
297
298    Ok((staging_buffer, mapping.ptr.as_ptr()))
299}
300
301impl<A: hal::Api> StagingBuffer<A> {
302    unsafe fn flush(&self, device: &A::Device) -> Result<(), DeviceError> {
303        if !self.is_coherent {
304            unsafe { device.flush_mapped_ranges(&self.raw, iter::once(0..self.size)) };
305        }
306        unsafe { device.unmap_buffer(&self.raw)? };
307        Ok(())
308    }
309}
310
311#[derive(Clone, Debug, Error)]
312#[error("Queue is invalid")]
313pub struct InvalidQueue;
314
315#[derive(Clone, Debug, Error)]
316#[non_exhaustive]
317pub enum QueueWriteError {
318    #[error(transparent)]
319    Queue(#[from] DeviceError),
320    #[error(transparent)]
321    Transfer(#[from] TransferError),
322    #[error(transparent)]
323    MemoryInitFailure(#[from] ClearError),
324}
325
326#[derive(Clone, Debug, Error)]
327#[non_exhaustive]
328pub enum QueueSubmitError {
329    #[error(transparent)]
330    Queue(#[from] DeviceError),
331    #[error("Buffer {0:?} is destroyed")]
332    DestroyedBuffer(id::BufferId),
333    #[error("Texture {0:?} is destroyed")]
334    DestroyedTexture(id::TextureId),
335    #[error(transparent)]
336    Unmap(#[from] BufferAccessError),
337    #[error("Buffer {0:?} is still mapped")]
338    BufferStillMapped(id::BufferId),
339    #[error("Surface output was dropped before the command buffer got submitted")]
340    SurfaceOutputDropped,
341    #[error("Surface was unconfigured before the command buffer got submitted")]
342    SurfaceUnconfigured,
343    #[error("GPU got stuck :(")]
344    StuckGpu,
345}
346
347//TODO: move out common parts of write_xxx.
348
349impl<G: GlobalIdentityHandlerFactory> Global<G> {
350    pub fn queue_write_buffer<A: HalApi>(
351        &self,
352        queue_id: id::QueueId,
353        buffer_id: id::BufferId,
354        buffer_offset: wgt::BufferAddress,
355        data: &[u8],
356    ) -> Result<(), QueueWriteError> {
357        profiling::scope!("Queue::write_buffer");
358
359        let hub = A::hub(self);
360        let root_token = &mut Token::root();
361
362        let (mut device_guard, ref mut device_token) = hub.devices.write(root_token);
363        let device = device_guard
364            .get_mut(queue_id)
365            .map_err(|_| DeviceError::Invalid)?;
366
367        let data_size = data.len() as wgt::BufferAddress;
368
369        #[cfg(feature = "trace")]
370        if let Some(ref trace) = device.trace {
371            let mut trace = trace.lock();
372            let data_path = trace.make_binary("bin", data);
373            trace.add(Action::WriteBuffer {
374                id: buffer_id,
375                data: data_path,
376                range: buffer_offset..buffer_offset + data_size,
377                queued: true,
378            });
379        }
380
381        if data_size == 0 {
382            log::trace!("Ignoring write_buffer of size 0");
383            return Ok(());
384        }
385
386        // Platform validation requires that the staging buffer always be
387        // freed, even if an error occurs. All paths from here must call
388        // `device.pending_writes.consume`.
389        let (staging_buffer, staging_buffer_ptr) =
390            prepare_staging_buffer(&mut device.raw, data_size, device.instance_flags)?;
391
392        if let Err(flush_error) = unsafe {
393            profiling::scope!("copy");
394            ptr::copy_nonoverlapping(data.as_ptr(), staging_buffer_ptr, data.len());
395            staging_buffer.flush(&device.raw)
396        } {
397            device.pending_writes.consume(staging_buffer);
398            return Err(flush_error.into());
399        }
400
401        let result = self.queue_write_staging_buffer_impl(
402            queue_id,
403            device,
404            device_token,
405            &staging_buffer,
406            buffer_id,
407            buffer_offset,
408        );
409
410        device.pending_writes.consume(staging_buffer);
411        result
412    }
413
414    pub fn queue_create_staging_buffer<A: HalApi>(
415        &self,
416        queue_id: id::QueueId,
417        buffer_size: wgt::BufferSize,
418        id_in: Input<G, id::StagingBufferId>,
419    ) -> Result<(id::StagingBufferId, *mut u8), QueueWriteError> {
420        profiling::scope!("Queue::create_staging_buffer");
421        let hub = A::hub(self);
422        let root_token = &mut Token::root();
423
424        let (mut device_guard, ref mut device_token) = hub.devices.write(root_token);
425        let device = device_guard
426            .get_mut(queue_id)
427            .map_err(|_| DeviceError::Invalid)?;
428
429        let (staging_buffer, staging_buffer_ptr) =
430            prepare_staging_buffer(&mut device.raw, buffer_size.get(), device.instance_flags)?;
431
432        let fid = hub.staging_buffers.prepare(id_in);
433        let id = fid.assign(staging_buffer, device_token);
434
435        Ok((id.0, staging_buffer_ptr))
436    }
437
438    pub fn queue_write_staging_buffer<A: HalApi>(
439        &self,
440        queue_id: id::QueueId,
441        buffer_id: id::BufferId,
442        buffer_offset: wgt::BufferAddress,
443        staging_buffer_id: id::StagingBufferId,
444    ) -> Result<(), QueueWriteError> {
445        profiling::scope!("Queue::write_staging_buffer");
446        let hub = A::hub(self);
447        let root_token = &mut Token::root();
448
449        let (mut device_guard, ref mut device_token) = hub.devices.write(root_token);
450        let device = device_guard
451            .get_mut(queue_id)
452            .map_err(|_| DeviceError::Invalid)?;
453
454        let staging_buffer = hub
455            .staging_buffers
456            .unregister(staging_buffer_id, device_token)
457            .0
458            .ok_or(TransferError::InvalidBuffer(buffer_id))?;
459
460        // At this point, we have taken ownership of the staging_buffer from the
461        // user. Platform validation requires that the staging buffer always
462        // be freed, even if an error occurs. All paths from here must call
463        // `device.pending_writes.consume`.
464        if let Err(flush_error) = unsafe { staging_buffer.flush(&device.raw) } {
465            device.pending_writes.consume(staging_buffer);
466            return Err(flush_error.into());
467        }
468
469        let result = self.queue_write_staging_buffer_impl(
470            queue_id,
471            device,
472            device_token,
473            &staging_buffer,
474            buffer_id,
475            buffer_offset,
476        );
477
478        device.pending_writes.consume(staging_buffer);
479        result
480    }
481
482    pub fn queue_validate_write_buffer<A: HalApi>(
483        &self,
484        _queue_id: id::QueueId,
485        buffer_id: id::BufferId,
486        buffer_offset: u64,
487        buffer_size: u64,
488    ) -> Result<(), QueueWriteError> {
489        profiling::scope!("Queue::validate_write_buffer");
490        let hub = A::hub(self);
491        let root_token = &mut Token::root();
492
493        let (_, ref mut device_token) = hub.devices.read(root_token);
494
495        let buffer_guard = hub.buffers.read(device_token).0;
496        let buffer = buffer_guard
497            .get(buffer_id)
498            .map_err(|_| TransferError::InvalidBuffer(buffer_id))?;
499
500        self.queue_validate_write_buffer_impl(buffer, buffer_id, buffer_offset, buffer_size)?;
501
502        Ok(())
503    }
504
505    fn queue_validate_write_buffer_impl<A: HalApi>(
506        &self,
507        buffer: &crate::resource::Buffer<A>,
508        buffer_id: id::BufferId,
509        buffer_offset: u64,
510        buffer_size: u64,
511    ) -> Result<(), TransferError> {
512        if !buffer.usage.contains(wgt::BufferUsages::COPY_DST) {
513            return Err(TransferError::MissingCopyDstUsageFlag(
514                Some(buffer_id),
515                None,
516            ));
517        }
518        if buffer_size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
519            return Err(TransferError::UnalignedCopySize(buffer_size));
520        }
521        if buffer_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
522            return Err(TransferError::UnalignedBufferOffset(buffer_offset));
523        }
524        if buffer_offset + buffer_size > buffer.size {
525            return Err(TransferError::BufferOverrun {
526                start_offset: buffer_offset,
527                end_offset: buffer_offset + buffer_size,
528                buffer_size: buffer.size,
529                side: CopySide::Destination,
530            });
531        }
532
533        Ok(())
534    }
535
536    fn queue_write_staging_buffer_impl<A: HalApi>(
537        &self,
538        device_id: id::DeviceId,
539        device: &mut super::Device<A>,
540        device_token: &mut Token<super::Device<A>>,
541        staging_buffer: &StagingBuffer<A>,
542        buffer_id: id::BufferId,
543        buffer_offset: u64,
544    ) -> Result<(), QueueWriteError> {
545        let hub = A::hub(self);
546
547        let buffer_guard = hub.buffers.read(device_token).0;
548
549        let mut trackers = device.trackers.lock();
550        let (dst, transition) = trackers
551            .buffers
552            .set_single(&buffer_guard, buffer_id, hal::BufferUses::COPY_DST)
553            .ok_or(TransferError::InvalidBuffer(buffer_id))?;
554        let dst_raw = dst
555            .raw
556            .as_ref()
557            .ok_or(TransferError::InvalidBuffer(buffer_id))?;
558
559        if dst.device_id.value.0 != device_id {
560            return Err(DeviceError::WrongDevice.into());
561        }
562
563        let src_buffer_size = staging_buffer.size;
564        self.queue_validate_write_buffer_impl(dst, buffer_id, buffer_offset, src_buffer_size)?;
565
566        dst.life_guard.use_at(device.active_submission_index + 1);
567
568        let region = wgt::BufferSize::new(src_buffer_size).map(|size| hal::BufferCopy {
569            src_offset: 0,
570            dst_offset: buffer_offset,
571            size,
572        });
573        let barriers = iter::once(hal::BufferBarrier {
574            buffer: &staging_buffer.raw,
575            usage: hal::BufferUses::MAP_WRITE..hal::BufferUses::COPY_SRC,
576        })
577        .chain(transition.map(|pending| pending.into_hal(dst)));
578        let encoder = device.pending_writes.activate();
579        unsafe {
580            encoder.transition_buffers(barriers);
581            encoder.copy_buffer_to_buffer(&staging_buffer.raw, dst_raw, region.into_iter());
582        }
583
584        device.pending_writes.dst_buffers.insert(buffer_id);
585
586        // Ensure the overwritten bytes are marked as initialized so
587        // they don't need to be nulled prior to mapping or binding.
588        {
589            drop(buffer_guard);
590            let mut buffer_guard = hub.buffers.write(device_token).0;
591
592            let dst = buffer_guard.get_mut(buffer_id).unwrap();
593            dst.initialization_status
594                .drain(buffer_offset..(buffer_offset + src_buffer_size));
595        }
596
597        Ok(())
598    }
599
600    pub fn queue_write_texture<A: HalApi>(
601        &self,
602        queue_id: id::QueueId,
603        destination: &ImageCopyTexture,
604        data: &[u8],
605        data_layout: &wgt::ImageDataLayout,
606        size: &wgt::Extent3d,
607    ) -> Result<(), QueueWriteError> {
608        profiling::scope!("Queue::write_texture");
609
610        let hub = A::hub(self);
611        let mut token = Token::root();
612        let (mut device_guard, mut token) = hub.devices.write(&mut token);
613        let device = device_guard
614            .get_mut(queue_id)
615            .map_err(|_| DeviceError::Invalid)?;
616
617        #[cfg(feature = "trace")]
618        if let Some(ref trace) = device.trace {
619            let mut trace = trace.lock();
620            let data_path = trace.make_binary("bin", data);
621            trace.add(Action::WriteTexture {
622                to: *destination,
623                data: data_path,
624                layout: *data_layout,
625                size: *size,
626            });
627        }
628
629        if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
630            log::trace!("Ignoring write_texture of size 0");
631            return Ok(());
632        }
633
634        let (mut texture_guard, _) = hub.textures.write(&mut token); // For clear we need write access to the texture. TODO: Can we acquire write lock later?
635        let dst = texture_guard
636            .get_mut(destination.texture)
637            .map_err(|_| TransferError::InvalidTexture(destination.texture))?;
638
639        if dst.device_id.value.0 != queue_id {
640            return Err(DeviceError::WrongDevice.into());
641        }
642
643        if !dst.desc.usage.contains(wgt::TextureUsages::COPY_DST) {
644            return Err(
645                TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(),
646            );
647        }
648
649        // Note: Doing the copy range validation early is important because ensures that the
650        // dimensions are not going to cause overflow in other parts of the validation.
651        let (hal_copy_size, array_layer_count) =
652            validate_texture_copy_range(destination, &dst.desc, CopySide::Destination, size)?;
653
654        let (selector, dst_base) = extract_texture_selector(destination, size, dst)?;
655
656        if !dst_base.aspect.is_one() {
657            return Err(TransferError::CopyAspectNotOne.into());
658        }
659
660        if !conv::is_valid_copy_dst_texture_format(dst.desc.format, destination.aspect) {
661            return Err(TransferError::CopyToForbiddenTextureFormat {
662                format: dst.desc.format,
663                aspect: destination.aspect,
664            }
665            .into());
666        }
667
668        // Note: `_source_bytes_per_array_layer` is ignored since we
669        // have a staging copy, and it can have a different value.
670        let (_, _source_bytes_per_array_layer) = validate_linear_texture_data(
671            data_layout,
672            dst.desc.format,
673            destination.aspect,
674            data.len() as wgt::BufferAddress,
675            CopySide::Source,
676            size,
677            false,
678        )?;
679
680        if dst.desc.format.is_depth_stencil_format() {
681            device
682                .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
683                .map_err(TransferError::from)?;
684        }
685
686        let (block_width, block_height) = dst.desc.format.block_dimensions();
687        let width_blocks = size.width / block_width;
688        let height_blocks = size.height / block_height;
689
690        let block_rows_per_image = data_layout.rows_per_image.unwrap_or(
691            // doesn't really matter because we need this only if we copy
692            // more than one layer, and then we validate for this being not
693            // None
694            size.height,
695        );
696
697        let block_size = dst
698            .desc
699            .format
700            .block_size(Some(destination.aspect))
701            .unwrap();
702        let bytes_per_row_alignment =
703            get_lowest_common_denom(device.alignments.buffer_copy_pitch.get() as u32, block_size);
704        let stage_bytes_per_row =
705            wgt::math::align_to(block_size * width_blocks, bytes_per_row_alignment);
706
707        let block_rows_in_copy =
708            (size.depth_or_array_layers - 1) * block_rows_per_image + height_blocks;
709        let stage_size = stage_bytes_per_row as u64 * block_rows_in_copy as u64;
710
711        let mut trackers = device.trackers.lock();
712        let encoder = device.pending_writes.activate();
713
714        // If the copy does not fully cover the layers, we need to initialize to
715        // zero *first* as we don't keep track of partial texture layer inits.
716        //
717        // Strictly speaking we only need to clear the areas of a layer
718        // untouched, but this would get increasingly messy.
719        let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
720            // volume textures don't have a layer range as array volumes aren't supported
721            0..1
722        } else {
723            destination.origin.z..destination.origin.z + size.depth_or_array_layers
724        };
725        if dst.initialization_status.mips[destination.mip_level as usize]
726            .check(init_layer_range.clone())
727            .is_some()
728        {
729            if has_copy_partial_init_tracker_coverage(size, destination.mip_level, &dst.desc) {
730                for layer_range in dst.initialization_status.mips[destination.mip_level as usize]
731                    .drain(init_layer_range)
732                    .collect::<Vec<std::ops::Range<u32>>>()
733                {
734                    crate::command::clear_texture(
735                        &*texture_guard,
736                        id::Valid(destination.texture),
737                        TextureInitRange {
738                            mip_range: destination.mip_level..(destination.mip_level + 1),
739                            layer_range,
740                        },
741                        encoder,
742                        &mut trackers.textures,
743                        &device.alignments,
744                        &device.zero_buffer,
745                    )
746                    .map_err(QueueWriteError::from)?;
747                }
748            } else {
749                dst.initialization_status.mips[destination.mip_level as usize]
750                    .drain(init_layer_range);
751            }
752        }
753
754        // Re-get `dst` immutably here, so that the mutable borrow of the
755        // `texture_guard.get_mut` above ends in time for the `clear_texture`
756        // call above. Since we've held `texture_guard` the whole time, we know
757        // the texture hasn't gone away in the mean time, so we can unwrap.
758        let dst = texture_guard.get(destination.texture).unwrap();
759        let transition = trackers
760            .textures
761            .set_single(
762                dst,
763                destination.texture,
764                selector,
765                hal::TextureUses::COPY_DST,
766            )
767            .ok_or(TransferError::InvalidTexture(destination.texture))?;
768
769        dst.life_guard.use_at(device.active_submission_index + 1);
770
771        let dst_raw = dst
772            .inner
773            .as_raw()
774            .ok_or(TransferError::InvalidTexture(destination.texture))?;
775
776        let bytes_per_row = data_layout
777            .bytes_per_row
778            .unwrap_or(width_blocks * block_size);
779
780        // Platform validation requires that the staging buffer always be
781        // freed, even if an error occurs. All paths from here must call
782        // `device.pending_writes.consume`.
783        let (staging_buffer, staging_buffer_ptr) =
784            prepare_staging_buffer(&mut device.raw, stage_size, device.instance_flags)?;
785
786        if stage_bytes_per_row == bytes_per_row {
787            profiling::scope!("copy aligned");
788            // Fast path if the data is already being aligned optimally.
789            unsafe {
790                ptr::copy_nonoverlapping(
791                    data.as_ptr().offset(data_layout.offset as isize),
792                    staging_buffer_ptr,
793                    stage_size as usize,
794                );
795            }
796        } else {
797            profiling::scope!("copy chunked");
798            // Copy row by row into the optimal alignment.
799            let copy_bytes_per_row = stage_bytes_per_row.min(bytes_per_row) as usize;
800            for layer in 0..size.depth_or_array_layers {
801                let rows_offset = layer * block_rows_per_image;
802                for row in 0..height_blocks {
803                    unsafe {
804                        ptr::copy_nonoverlapping(
805                            data.as_ptr().offset(
806                                data_layout.offset as isize
807                                    + (rows_offset + row) as isize * bytes_per_row as isize,
808                            ),
809                            staging_buffer_ptr.offset(
810                                (rows_offset + row) as isize * stage_bytes_per_row as isize,
811                            ),
812                            copy_bytes_per_row,
813                        );
814                    }
815                }
816            }
817        }
818
819        if let Err(e) = unsafe { staging_buffer.flush(&device.raw) } {
820            device.pending_writes.consume(staging_buffer);
821            return Err(e.into());
822        }
823
824        let regions = (0..array_layer_count).map(|rel_array_layer| {
825            let mut texture_base = dst_base.clone();
826            texture_base.array_layer += rel_array_layer;
827            hal::BufferTextureCopy {
828                buffer_layout: wgt::ImageDataLayout {
829                    offset: rel_array_layer as u64
830                        * block_rows_per_image as u64
831                        * stage_bytes_per_row as u64,
832                    bytes_per_row: Some(stage_bytes_per_row),
833                    rows_per_image: Some(block_rows_per_image),
834                },
835                texture_base,
836                size: hal_copy_size,
837            }
838        });
839        let barrier = hal::BufferBarrier {
840            buffer: &staging_buffer.raw,
841            usage: hal::BufferUses::MAP_WRITE..hal::BufferUses::COPY_SRC,
842        };
843
844        unsafe {
845            encoder.transition_textures(transition.map(|pending| pending.into_hal(dst)));
846            encoder.transition_buffers(iter::once(barrier));
847            encoder.copy_buffer_to_texture(&staging_buffer.raw, dst_raw, regions);
848        }
849
850        device.pending_writes.consume(staging_buffer);
851        device
852            .pending_writes
853            .dst_textures
854            .insert(destination.texture);
855
856        Ok(())
857    }
858
859    #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
860    pub fn queue_copy_external_image_to_texture<A: HalApi>(
861        &self,
862        queue_id: id::QueueId,
863        source: &wgt::ImageCopyExternalImage,
864        destination: crate::command::ImageCopyTextureTagged,
865        size: wgt::Extent3d,
866    ) -> Result<(), QueueWriteError> {
867        profiling::scope!("Queue::copy_external_image_to_texture");
868
869        let hub = A::hub(self);
870        let mut token = Token::root();
871        let (mut device_guard, mut token) = hub.devices.write(&mut token);
872        let device = device_guard
873            .get_mut(queue_id)
874            .map_err(|_| DeviceError::Invalid)?;
875
876        if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
877            log::trace!("Ignoring write_texture of size 0");
878            return Ok(());
879        }
880
881        let mut needs_flag = false;
882        needs_flag |= matches!(source.source, wgt::ExternalImageSource::OffscreenCanvas(_));
883        needs_flag |= source.origin != wgt::Origin2d::ZERO;
884        needs_flag |= destination.color_space != wgt::PredefinedColorSpace::Srgb;
885        #[allow(clippy::bool_comparison)]
886        if matches!(source.source, wgt::ExternalImageSource::ImageBitmap(_)) {
887            needs_flag |= source.flip_y != false;
888            needs_flag |= destination.premultiplied_alpha != false;
889        }
890
891        if needs_flag {
892            device
893                .require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES)
894                .map_err(TransferError::from)?;
895        }
896
897        let src_width = source.source.width();
898        let src_height = source.source.height();
899
900        let (mut texture_guard, _) = hub.textures.write(&mut token); // For clear we need write access to the texture. TODO: Can we acquire write lock later?
901        let dst = texture_guard.get_mut(destination.texture).unwrap();
902
903        if !conv::is_valid_external_image_copy_dst_texture_format(dst.desc.format) {
904            return Err(
905                TransferError::ExternalCopyToForbiddenTextureFormat(dst.desc.format).into(),
906            );
907        }
908        if dst.desc.dimension != wgt::TextureDimension::D2 {
909            return Err(TransferError::InvalidDimensionExternal(destination.texture).into());
910        }
911        if !dst.desc.usage.contains(wgt::TextureUsages::COPY_DST) {
912            return Err(
913                TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(),
914            );
915        }
916        if !dst
917            .desc
918            .usage
919            .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
920        {
921            return Err(
922                TransferError::MissingRenderAttachmentUsageFlag(destination.texture).into(),
923            );
924        }
925        if dst.desc.sample_count != 1 {
926            return Err(TransferError::InvalidSampleCount {
927                sample_count: dst.desc.sample_count,
928            }
929            .into());
930        }
931
932        if source.origin.x + size.width > src_width {
933            return Err(TransferError::TextureOverrun {
934                start_offset: source.origin.x,
935                end_offset: source.origin.x + size.width,
936                texture_size: src_width,
937                dimension: crate::resource::TextureErrorDimension::X,
938                side: CopySide::Source,
939            }
940            .into());
941        }
942        if source.origin.y + size.height > src_height {
943            return Err(TransferError::TextureOverrun {
944                start_offset: source.origin.y,
945                end_offset: source.origin.y + size.height,
946                texture_size: src_height,
947                dimension: crate::resource::TextureErrorDimension::Y,
948                side: CopySide::Source,
949            }
950            .into());
951        }
952        if size.depth_or_array_layers != 1 {
953            return Err(TransferError::TextureOverrun {
954                start_offset: 0,
955                end_offset: size.depth_or_array_layers,
956                texture_size: 1,
957                dimension: crate::resource::TextureErrorDimension::Z,
958                side: CopySide::Source,
959            }
960            .into());
961        }
962
963        // Note: Doing the copy range validation early is important because ensures that the
964        // dimensions are not going to cause overflow in other parts of the validation.
965        let (hal_copy_size, _) = validate_texture_copy_range(
966            &destination.to_untagged(),
967            &dst.desc,
968            CopySide::Destination,
969            &size,
970        )?;
971
972        let (selector, dst_base) =
973            extract_texture_selector(&destination.to_untagged(), &size, dst)?;
974
975        let mut trackers = device.trackers.lock();
976        let encoder = device.pending_writes.activate();
977
978        // If the copy does not fully cover the layers, we need to initialize to
979        // zero *first* as we don't keep track of partial texture layer inits.
980        //
981        // Strictly speaking we only need to clear the areas of a layer
982        // untouched, but this would get increasingly messy.
983        let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
984            // volume textures don't have a layer range as array volumes aren't supported
985            0..1
986        } else {
987            destination.origin.z..destination.origin.z + size.depth_or_array_layers
988        };
989        if dst.initialization_status.mips[destination.mip_level as usize]
990            .check(init_layer_range.clone())
991            .is_some()
992        {
993            if has_copy_partial_init_tracker_coverage(&size, destination.mip_level, &dst.desc) {
994                for layer_range in dst.initialization_status.mips[destination.mip_level as usize]
995                    .drain(init_layer_range)
996                    .collect::<Vec<std::ops::Range<u32>>>()
997                {
998                    crate::command::clear_texture(
999                        &*texture_guard,
1000                        id::Valid(destination.texture),
1001                        TextureInitRange {
1002                            mip_range: destination.mip_level..(destination.mip_level + 1),
1003                            layer_range,
1004                        },
1005                        encoder,
1006                        &mut trackers.textures,
1007                        &device.alignments,
1008                        &device.zero_buffer,
1009                    )
1010                    .map_err(QueueWriteError::from)?;
1011                }
1012            } else {
1013                dst.initialization_status.mips[destination.mip_level as usize]
1014                    .drain(init_layer_range);
1015            }
1016        }
1017
1018        let dst = texture_guard.get(destination.texture).unwrap();
1019
1020        let transitions = trackers
1021            .textures
1022            .set_single(
1023                dst,
1024                destination.texture,
1025                selector,
1026                hal::TextureUses::COPY_DST,
1027            )
1028            .ok_or(TransferError::InvalidTexture(destination.texture))?;
1029
1030        dst.life_guard.use_at(device.active_submission_index + 1);
1031
1032        let dst_raw = dst
1033            .inner
1034            .as_raw()
1035            .ok_or(TransferError::InvalidTexture(destination.texture))?;
1036
1037        let regions = hal::TextureCopy {
1038            src_base: hal::TextureCopyBase {
1039                mip_level: 0,
1040                array_layer: 0,
1041                origin: source.origin.to_3d(0),
1042                aspect: hal::FormatAspects::COLOR,
1043            },
1044            dst_base,
1045            size: hal_copy_size,
1046        };
1047
1048        unsafe {
1049            encoder.transition_textures(transitions.map(|pending| pending.into_hal(dst)));
1050            encoder.copy_external_image_to_texture(
1051                source,
1052                dst_raw,
1053                destination.premultiplied_alpha,
1054                iter::once(regions),
1055            );
1056        }
1057
1058        Ok(())
1059    }
1060
1061    pub fn queue_submit<A: HalApi>(
1062        &self,
1063        queue_id: id::QueueId,
1064        command_buffer_ids: &[id::CommandBufferId],
1065    ) -> Result<WrappedSubmissionIndex, QueueSubmitError> {
1066        profiling::scope!("Queue::submit");
1067        log::trace!("Queue::submit {queue_id:?}");
1068
1069        let (submit_index, callbacks) = {
1070            let hub = A::hub(self);
1071            let mut token = Token::root();
1072
1073            let (mut device_guard, mut token) = hub.devices.write(&mut token);
1074            let device = device_guard
1075                .get_mut(queue_id)
1076                .map_err(|_| DeviceError::Invalid)?;
1077            device.temp_suspected.clear();
1078            device.active_submission_index += 1;
1079            let submit_index = device.active_submission_index;
1080            let mut active_executions = Vec::new();
1081            let mut used_surface_textures = track::TextureUsageScope::new();
1082
1083            {
1084                let (mut command_buffer_guard, mut token) = hub.command_buffers.write(&mut token);
1085
1086                if !command_buffer_ids.is_empty() {
1087                    profiling::scope!("prepare");
1088
1089                    let (render_bundle_guard, mut token) = hub.render_bundles.read(&mut token);
1090                    let (_, mut token) = hub.pipeline_layouts.read(&mut token);
1091                    let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token);
1092                    let (compute_pipe_guard, mut token) = hub.compute_pipelines.read(&mut token);
1093                    let (render_pipe_guard, mut token) = hub.render_pipelines.read(&mut token);
1094                    let (mut buffer_guard, mut token) = hub.buffers.write(&mut token);
1095                    let (mut texture_guard, mut token) = hub.textures.write(&mut token);
1096                    let (texture_view_guard, mut token) = hub.texture_views.read(&mut token);
1097                    let (sampler_guard, mut token) = hub.samplers.read(&mut token);
1098                    let (query_set_guard, _) = hub.query_sets.read(&mut token);
1099
1100                    //Note: locking the trackers has to be done after the storages
1101                    let mut trackers = device.trackers.lock();
1102
1103                    //TODO: if multiple command buffers are submitted, we can re-use the last
1104                    // native command buffer of the previous chain instead of always creating
1105                    // a temporary one, since the chains are not finished.
1106
1107                    // finish all the command buffers first
1108                    for &cmb_id in command_buffer_ids {
1109                        // we reset the used surface textures every time we use
1110                        // it, so make sure to set_size on it.
1111                        used_surface_textures.set_size(texture_guard.len());
1112
1113                        #[allow(unused_mut)]
1114                        let mut cmdbuf = match hub
1115                            .command_buffers
1116                            .unregister_locked(cmb_id, &mut *command_buffer_guard)
1117                        {
1118                            Some(cmdbuf) => cmdbuf,
1119                            None => continue,
1120                        };
1121
1122                        if cmdbuf.device_id.value.0 != queue_id {
1123                            return Err(DeviceError::WrongDevice.into());
1124                        }
1125
1126                        #[cfg(feature = "trace")]
1127                        if let Some(ref trace) = device.trace {
1128                            trace.lock().add(Action::Submit(
1129                                submit_index,
1130                                cmdbuf.commands.take().unwrap(),
1131                            ));
1132                        }
1133                        if !cmdbuf.is_finished() {
1134                            device.destroy_command_buffer(cmdbuf);
1135                            continue;
1136                        }
1137
1138                        // optimize the tracked states
1139                        // cmdbuf.trackers.optimize();
1140
1141                        // update submission IDs
1142                        for id in cmdbuf.trackers.buffers.used() {
1143                            let buffer = &mut buffer_guard[id];
1144                            let raw_buf = match buffer.raw {
1145                                Some(ref raw) => raw,
1146                                None => {
1147                                    return Err(QueueSubmitError::DestroyedBuffer(id.0));
1148                                }
1149                            };
1150                            if !buffer.life_guard.use_at(submit_index) {
1151                                if let BufferMapState::Active { .. } = buffer.map_state {
1152                                    log::warn!("Dropped buffer has a pending mapping.");
1153                                    unsafe { device.raw.unmap_buffer(raw_buf) }
1154                                        .map_err(DeviceError::from)?;
1155                                }
1156                                device.temp_suspected.buffers.push(id);
1157                            } else {
1158                                match buffer.map_state {
1159                                    BufferMapState::Idle => (),
1160                                    _ => return Err(QueueSubmitError::BufferStillMapped(id.0)),
1161                                }
1162                            }
1163                        }
1164                        for id in cmdbuf.trackers.textures.used() {
1165                            let texture = &mut texture_guard[id];
1166                            let should_extend = match texture.inner {
1167                                TextureInner::Native { raw: None } => {
1168                                    return Err(QueueSubmitError::DestroyedTexture(id.0));
1169                                }
1170                                TextureInner::Native { raw: Some(_) } => false,
1171                                TextureInner::Surface {
1172                                    ref mut has_work, ..
1173                                } => {
1174                                    *has_work = true;
1175                                    true
1176                                }
1177                            };
1178                            if !texture.life_guard.use_at(submit_index) {
1179                                device.temp_suspected.textures.push(id);
1180                            }
1181                            if should_extend {
1182                                unsafe {
1183                                    let ref_count = cmdbuf.trackers.textures.get_ref_count(id);
1184                                    used_surface_textures
1185                                        .merge_single(
1186                                            &*texture_guard,
1187                                            id,
1188                                            None,
1189                                            ref_count,
1190                                            hal::TextureUses::PRESENT,
1191                                        )
1192                                        .unwrap();
1193                                };
1194                            }
1195                        }
1196                        for id in cmdbuf.trackers.views.used() {
1197                            if !texture_view_guard[id].life_guard.use_at(submit_index) {
1198                                device.temp_suspected.texture_views.push(id);
1199                            }
1200                        }
1201                        for id in cmdbuf.trackers.bind_groups.used() {
1202                            let bg = &bind_group_guard[id];
1203                            if !bg.life_guard.use_at(submit_index) {
1204                                device.temp_suspected.bind_groups.push(id);
1205                            }
1206                            // We need to update the submission indices for the contained
1207                            // state-less (!) resources as well, so that they don't get
1208                            // deleted too early if the parent bind group goes out of scope.
1209                            for sub_id in bg.used.views.used() {
1210                                texture_view_guard[sub_id].life_guard.use_at(submit_index);
1211                            }
1212                            for sub_id in bg.used.samplers.used() {
1213                                sampler_guard[sub_id].life_guard.use_at(submit_index);
1214                            }
1215                        }
1216                        // assert!(cmdbuf.trackers.samplers.is_empty());
1217                        for id in cmdbuf.trackers.compute_pipelines.used() {
1218                            if !compute_pipe_guard[id].life_guard.use_at(submit_index) {
1219                                device.temp_suspected.compute_pipelines.push(id);
1220                            }
1221                        }
1222                        for id in cmdbuf.trackers.render_pipelines.used() {
1223                            if !render_pipe_guard[id].life_guard.use_at(submit_index) {
1224                                device.temp_suspected.render_pipelines.push(id);
1225                            }
1226                        }
1227                        for id in cmdbuf.trackers.query_sets.used() {
1228                            if !query_set_guard[id].life_guard.use_at(submit_index) {
1229                                device.temp_suspected.query_sets.push(id);
1230                            }
1231                        }
1232                        for id in cmdbuf.trackers.bundles.used() {
1233                            let bundle = &render_bundle_guard[id];
1234                            if !bundle.life_guard.use_at(submit_index) {
1235                                device.temp_suspected.render_bundles.push(id);
1236                            }
1237                            // We need to update the submission indices for the contained
1238                            // state-less (!) resources as well, excluding the bind groups.
1239                            // They don't get deleted too early if the bundle goes out of scope.
1240                            for sub_id in bundle.used.render_pipelines.used() {
1241                                render_pipe_guard[sub_id].life_guard.use_at(submit_index);
1242                            }
1243                            for sub_id in bundle.used.query_sets.used() {
1244                                query_set_guard[sub_id].life_guard.use_at(submit_index);
1245                            }
1246                        }
1247
1248                        let mut baked = cmdbuf.into_baked();
1249                        // execute resource transitions
1250                        unsafe {
1251                            baked
1252                                .encoder
1253                                .begin_encoding(hal_label(
1254                                    Some("(wgpu internal) Transit"),
1255                                    device.instance_flags,
1256                                ))
1257                                .map_err(DeviceError::from)?
1258                        };
1259                        log::trace!("Stitching command buffer {:?} before submission", cmb_id);
1260                        baked
1261                            .initialize_buffer_memory(&mut *trackers, &mut *buffer_guard)
1262                            .map_err(|err| QueueSubmitError::DestroyedBuffer(err.0))?;
1263                        baked
1264                            .initialize_texture_memory(&mut *trackers, &mut *texture_guard, device)
1265                            .map_err(|err| QueueSubmitError::DestroyedTexture(err.0))?;
1266                        //Note: stateless trackers are not merged:
1267                        // device already knows these resources exist.
1268                        CommandBuffer::insert_barriers_from_tracker(
1269                            &mut baked.encoder,
1270                            &mut *trackers,
1271                            &baked.trackers,
1272                            &*buffer_guard,
1273                            &*texture_guard,
1274                        );
1275
1276                        let transit = unsafe { baked.encoder.end_encoding().unwrap() };
1277                        baked.list.insert(0, transit);
1278
1279                        // Transition surface textures into `Present` state.
1280                        // Note: we could technically do it after all of the command buffers,
1281                        // but here we have a command encoder by hand, so it's easier to use it.
1282                        if !used_surface_textures.is_empty() {
1283                            unsafe {
1284                                baked
1285                                    .encoder
1286                                    .begin_encoding(hal_label(
1287                                        Some("(wgpu internal) Present"),
1288                                        device.instance_flags,
1289                                    ))
1290                                    .map_err(DeviceError::from)?
1291                            };
1292                            trackers
1293                                .textures
1294                                .set_from_usage_scope(&*texture_guard, &used_surface_textures);
1295                            let texture_barriers = trackers.textures.drain().map(|pending| {
1296                                let tex = unsafe { texture_guard.get_unchecked(pending.id) };
1297                                pending.into_hal(tex)
1298                            });
1299                            let present = unsafe {
1300                                baked.encoder.transition_textures(texture_barriers);
1301                                baked.encoder.end_encoding().unwrap()
1302                            };
1303                            baked.list.push(present);
1304                            used_surface_textures = track::TextureUsageScope::new();
1305                        }
1306
1307                        // done
1308                        active_executions.push(EncoderInFlight {
1309                            raw: baked.encoder,
1310                            cmd_buffers: baked.list,
1311                        });
1312                    }
1313
1314                    log::trace!("Device after submission {}", submit_index);
1315                }
1316
1317                let super::Device {
1318                    ref mut pending_writes,
1319                    ref mut queue,
1320                    ref mut fence,
1321                    ..
1322                } = *device;
1323
1324                {
1325                    // TODO: These blocks have a few organizational issues, and
1326                    // should be refactored.
1327                    //
1328                    // 1) It's similar to the code we have per-command-buffer
1329                    //    (at the begin and end) Maybe we can merge some?
1330                    //
1331                    // 2) It's doing the extra locking unconditionally. Maybe we
1332                    //    can only do so if any surfaces are being written to?
1333                    let (_, mut token) = hub.buffers.read(&mut token); // skip token
1334                    let (mut texture_guard, _) = hub.textures.write(&mut token);
1335
1336                    used_surface_textures.set_size(texture_guard.len());
1337
1338                    for &id in pending_writes.dst_textures.iter() {
1339                        let texture = texture_guard.get_mut(id).unwrap();
1340                        match texture.inner {
1341                            TextureInner::Native { raw: None } => {
1342                                return Err(QueueSubmitError::DestroyedTexture(id));
1343                            }
1344                            TextureInner::Native { raw: Some(_) } => {}
1345                            TextureInner::Surface {
1346                                ref mut has_work, ..
1347                            } => {
1348                                *has_work = true;
1349                                let ref_count = texture.life_guard.add_ref();
1350                                unsafe {
1351                                    used_surface_textures
1352                                        .merge_single(
1353                                            &*texture_guard,
1354                                            id::Valid(id),
1355                                            None,
1356                                            &ref_count,
1357                                            hal::TextureUses::PRESENT,
1358                                        )
1359                                        .unwrap()
1360                                };
1361                            }
1362                        }
1363                    }
1364
1365                    if !used_surface_textures.is_empty() {
1366                        let mut trackers = device.trackers.lock();
1367
1368                        trackers
1369                            .textures
1370                            .set_from_usage_scope(&*texture_guard, &used_surface_textures);
1371                        let texture_barriers = trackers.textures.drain().map(|pending| {
1372                            let tex = unsafe { texture_guard.get_unchecked(pending.id) };
1373                            pending.into_hal(tex)
1374                        });
1375
1376                        unsafe {
1377                            pending_writes
1378                                .command_encoder
1379                                .transition_textures(texture_barriers);
1380                        };
1381                    }
1382                }
1383
1384                let refs = pending_writes
1385                    .pre_submit()
1386                    .into_iter()
1387                    .chain(
1388                        active_executions
1389                            .iter()
1390                            .flat_map(|pool_execution| pool_execution.cmd_buffers.iter()),
1391                    )
1392                    .collect::<Vec<_>>();
1393                unsafe {
1394                    queue
1395                        .submit(&refs, Some((fence, submit_index)))
1396                        .map_err(DeviceError::from)?;
1397                }
1398            }
1399
1400            profiling::scope!("cleanup");
1401            if let Some(pending_execution) = device.pending_writes.post_submit(
1402                &device.command_allocator,
1403                &device.raw,
1404                &device.queue,
1405            ) {
1406                active_executions.push(pending_execution);
1407            }
1408
1409            // this will register the new submission to the life time tracker
1410            let mut pending_write_resources = mem::take(&mut device.pending_writes.temp_resources);
1411            device.lock_life(&mut token).track_submission(
1412                submit_index,
1413                pending_write_resources.drain(..),
1414                active_executions,
1415            );
1416
1417            // This will schedule destruction of all resources that are no longer needed
1418            // by the user but used in the command stream, among other things.
1419            let (closures, _) = match device.maintain(hub, wgt::Maintain::Poll, &mut token) {
1420                Ok(closures) => closures,
1421                Err(WaitIdleError::Device(err)) => return Err(QueueSubmitError::Queue(err)),
1422                Err(WaitIdleError::StuckGpu) => return Err(QueueSubmitError::StuckGpu),
1423                Err(WaitIdleError::WrongSubmissionIndex(..)) => unreachable!(),
1424            };
1425
1426            // pending_write_resources has been drained, so it's empty, but we
1427            // want to retain its heap allocation.
1428            device.pending_writes.temp_resources = pending_write_resources;
1429            device.temp_suspected.clear();
1430            device.lock_life(&mut token).post_submit();
1431
1432            (submit_index, closures)
1433        };
1434
1435        // the closures should execute with nothing locked!
1436        callbacks.fire();
1437
1438        Ok(WrappedSubmissionIndex {
1439            queue_id,
1440            index: submit_index,
1441        })
1442    }
1443
1444    pub fn queue_get_timestamp_period<A: HalApi>(
1445        &self,
1446        queue_id: id::QueueId,
1447    ) -> Result<f32, InvalidQueue> {
1448        let hub = A::hub(self);
1449        let mut token = Token::root();
1450        let (device_guard, _) = hub.devices.read(&mut token);
1451        match device_guard.get(queue_id) {
1452            Ok(device) => Ok(unsafe { device.queue.get_timestamp_period() }),
1453            Err(_) => Err(InvalidQueue),
1454        }
1455    }
1456
1457    pub fn queue_on_submitted_work_done<A: HalApi>(
1458        &self,
1459        queue_id: id::QueueId,
1460        closure: SubmittedWorkDoneClosure,
1461    ) -> Result<(), InvalidQueue> {
1462        log::trace!("Queue::on_submitted_work_done {queue_id:?}");
1463
1464        //TODO: flush pending writes
1465        let hub = A::hub(self);
1466        let mut token = Token::root();
1467        let (device_guard, mut token) = hub.devices.read(&mut token);
1468        match device_guard.get(queue_id) {
1469            Ok(device) => device.lock_life(&mut token).add_work_done_closure(closure),
1470            Err(_) => return Err(InvalidQueue),
1471        }
1472        Ok(())
1473    }
1474}