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