1use alloc::{boxed::Box, string::ToString, sync::Arc, vec, vec::Vec};
2use core::{
3 iter,
4 mem::{self, ManuallyDrop},
5 num::NonZeroU64,
6 ptr::NonNull,
7 sync::atomic::Ordering,
8};
9use smallvec::SmallVec;
10use thiserror::Error;
11use wgt::{
12 error::{ErrorType, WebGpuError},
13 AccelerationStructureFlags,
14};
15
16use super::{life::LifetimeTracker, Device};
17#[cfg(feature = "trace")]
18use crate::device::trace::{Action, IntoTrace};
19use crate::{
20 api_log,
21 command::{
22 extract_texture_selector, validate_linear_texture_data, validate_texture_buffer_copy,
23 validate_texture_copy_dst_format, validate_texture_copy_range, ClearError,
24 CommandAllocator, CommandBuffer, CommandEncoder, CommandEncoderError, CopySide,
25 TransferError,
26 },
27 device::{DeviceError, WaitIdleError},
28 get_lowest_common_denom,
29 global::Global,
30 hal_label,
31 id::{self, BlasId, QueueId},
32 init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange},
33 lock::{rank, Mutex, MutexGuard, RwLock, RwLockWriteGuard},
34 ray_tracing::{BlasCompactReadyPendingClosure, CompactBlasError},
35 resource::{
36 Blas, BlasCompactState, Buffer, BufferAccessError, BufferMapState, DestroyedBuffer,
37 DestroyedQuerySet, DestroyedResourceError, DestroyedTexture, Fallible,
38 FlushedStagingBuffer, InvalidResourceError, Labeled, ParentDevice, ResourceErrorIdent,
39 StagingBuffer, Texture, TextureInner, Trackable, TrackingData,
40 },
41 resource_log,
42 scratch::ScratchBuffer,
43 snatch::{SnatchGuard, Snatchable},
44 track::{self, Tracker, TrackerIndex},
45 FastHashMap, SubmissionIndex,
46};
47use crate::{device::resource::CommandIndices, resource::RawResourceAccess};
48
49pub struct Queue {
50 raw: Box<dyn hal::DynQueue>,
51 pub(crate) pending_writes: Mutex<PendingWrites>,
52 life_tracker: Mutex<LifetimeTracker>,
53 pub(crate) device: Arc<Device>,
55}
56
57impl Queue {
58 pub(crate) fn new(
59 device: Arc<Device>,
60 raw: Box<dyn hal::DynQueue>,
61 instance_flags: wgt::InstanceFlags,
62 ) -> Result<Self, DeviceError> {
63 let pending_encoder = device
64 .command_allocator
65 .acquire_encoder(device.raw(), raw.as_ref())
66 .map_err(DeviceError::from_hal);
67
68 let pending_encoder = match pending_encoder {
69 Ok(pending_encoder) => pending_encoder,
70 Err(e) => {
71 return Err(e);
72 }
73 };
74
75 let mut pending_writes = PendingWrites::new(pending_encoder, instance_flags);
76
77 let zero_buffer = device.zero_buffer.as_ref();
78 pending_writes.activate();
79 unsafe {
80 pending_writes
81 .command_encoder
82 .transition_buffers(&[hal::BufferBarrier {
83 buffer: zero_buffer,
84 usage: hal::StateTransition {
85 from: wgt::BufferUses::empty(),
86 to: wgt::BufferUses::COPY_DST,
87 },
88 }]);
89 pending_writes
90 .command_encoder
91 .clear_buffer(zero_buffer, 0..super::ZERO_BUFFER_SIZE);
92 pending_writes
93 .command_encoder
94 .transition_buffers(&[hal::BufferBarrier {
95 buffer: zero_buffer,
96 usage: hal::StateTransition {
97 from: wgt::BufferUses::COPY_DST,
98 to: wgt::BufferUses::COPY_SRC,
99 },
100 }]);
101 }
102
103 Ok(Queue {
104 raw,
105 device,
106 pending_writes: Mutex::new(rank::QUEUE_PENDING_WRITES, pending_writes),
107 life_tracker: Mutex::new(rank::QUEUE_LIFE_TRACKER, LifetimeTracker::new()),
108 })
109 }
110
111 pub(crate) fn raw(&self) -> &dyn hal::DynQueue {
112 self.raw.as_ref()
113 }
114
115 #[track_caller]
116 pub(crate) fn lock_life<'a>(&'a self) -> MutexGuard<'a, LifetimeTracker> {
117 self.life_tracker.lock()
118 }
119
120 pub(crate) fn prepare_surface_texture_for_present(
125 &self,
126 texture: &Arc<Texture>,
127 ) -> Result<(), DeviceError> {
128 let snatch_guard = self.device.snatchable_lock.read();
129 let submission = self
130 .allocate_submission(snatch_guard)
131 .map_err(|(_index, e)| e)?;
132 let device = &self.device;
133
134 let needs_clear = {
136 let status = texture.initialization_status.read();
137 status
138 .mips
139 .first()
140 .is_some_and(|mip| mip.check(0..1).is_some())
141 };
142
143 let mut pending_writes = self.pending_writes.lock();
144
145 if needs_clear {
146 let encoder = pending_writes.activate();
149 let mut trackers = device.trackers.lock();
150 crate::command::clear_texture(
151 texture,
152 TextureInitRange {
153 mip_range: 0..1,
154 layer_range: 0..1,
155 },
156 encoder,
157 &mut trackers.textures,
158 &device.alignments,
159 device.zero_buffer.as_ref(),
160 &submission.snatch_guard,
161 device.instance_flags,
162 )
163 .map_err(|e| match e {
164 ClearError::Device(e) => e,
165 _ => DeviceError::Lost,
166 })?;
167 texture.initialization_status.write().mips[0].drain(0..1);
168 }
169
170 let pending = {
176 let mut trackers = device.trackers.lock();
177 let pending: Vec<track::PendingTransition<wgt::TextureUses>> = trackers
178 .textures
179 .set_single(
180 texture,
181 texture.full_range.clone(),
182 wgt::TextureUses::PRESENT,
183 )
184 .collect();
185 pending
186 };
187
188 if pending.is_empty() {
189 debug_assert!(!needs_clear);
195 return Ok(());
196 }
197
198 {
200 let raw_texture = texture
201 .try_raw(&submission.snatch_guard)
202 .map_err(|_| DeviceError::Lost)?;
203 let barriers: Vec<hal::TextureBarrier<'_, dyn hal::DynTexture>> = pending
204 .into_iter()
205 .map(|pt| pt.into_hal(raw_texture))
206 .collect();
207
208 let encoder = pending_writes.activate();
209 unsafe {
213 encoder.transition_textures(&barriers);
214 }
215 }
216
217 pending_writes.insert_texture(texture);
224
225 submission.submit(pending_writes)?;
226
227 Ok(())
228 }
229
230 pub(crate) fn maintain(
238 &self,
239 submission_index: u64,
240 snatch_guard: &SnatchGuard,
241 ) -> (
242 SmallVec<[SubmittedWorkDoneClosure; 1]>,
243 Vec<super::BufferMapPendingClosure>,
244 Vec<BlasCompactReadyPendingClosure>,
245 bool,
246 ) {
247 let mut life_tracker = self.lock_life();
248 let submission_closures = life_tracker.triage_submissions(submission_index);
249
250 let mapping_closures = life_tracker.handle_mapping(snatch_guard);
251 let blas_closures = life_tracker.handle_compact_read_back();
252
253 let queue_empty = life_tracker.queue_empty();
254
255 (
256 submission_closures,
257 mapping_closures,
258 blas_closures,
259 queue_empty,
260 )
261 }
262}
263
264crate::impl_resource_type!(Queue);
265impl Labeled for Queue {
267 fn label(&self) -> &str {
268 ""
269 }
270}
271crate::impl_parent_device!(Queue);
272crate::impl_storage_item!(Queue);
273
274impl Drop for Queue {
275 fn drop(&mut self) {
276 resource_log!("Drop {}", self.error_ident());
277
278 match unsafe { self.raw.wait_for_idle() } {
281 Ok(()) => {}
282 Err(hal::DeviceError::Lost) => {
283 self.device.handle_hal_error(hal::DeviceError::Lost);
284 }
285 Err(e) => {
286 panic!("Unexpected error while waiting for queue idle on drop: {e:?}");
287 }
288 }
289
290 let last_successful_submission_index = self
291 .device
292 .last_successful_submission_index
293 .load(Ordering::Acquire);
294
295 let snatch_guard = self.device.snatchable_lock.read();
296 let (submission_closures, mapping_closures, blas_compact_ready_closures, queue_empty) =
297 self.maintain(last_successful_submission_index, &snatch_guard);
298 drop(snatch_guard);
299
300 assert!(queue_empty);
301
302 let closures = crate::device::UserClosures {
303 mappings: mapping_closures,
304 blas_compact_ready: blas_compact_ready_closures,
305 submissions: submission_closures,
306 device_lost_invocations: SmallVec::new(),
307 };
308
309 closures.fire();
310 }
311}
312
313#[cfg(send_sync)]
314pub type SubmittedWorkDoneClosure = Box<dyn FnOnce() + Send + 'static>;
315#[cfg(not(send_sync))]
316pub type SubmittedWorkDoneClosure = Box<dyn FnOnce() + 'static>;
317
318#[derive(Debug)]
329pub enum TempResource {
330 StagingBuffer(FlushedStagingBuffer),
331 ScratchBuffer(ScratchBuffer),
332 DestroyedBuffer(DestroyedBuffer),
333 DestroyedTexture(DestroyedTexture),
334 DestroyedQuerySet(DestroyedQuerySet),
335}
336
337pub(crate) struct EncoderInFlight {
343 inner: crate::command::InnerCommandEncoder,
344 pub(crate) trackers: Tracker,
345 pub(crate) temp_resources: Vec<TempResource>,
346 _indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
348
349 pub(crate) pending_buffers: FastHashMap<TrackerIndex, Arc<Buffer>>,
351 pub(crate) pending_textures: FastHashMap<TrackerIndex, Arc<Texture>>,
353 pub(crate) pending_blas_s: FastHashMap<TrackerIndex, Arc<Blas>>,
355}
356
357#[derive(Debug)]
382pub(crate) struct PendingWrites {
383 pub command_encoder: Box<dyn hal::DynCommandEncoder>,
385
386 pub is_recording: bool,
392
393 temp_resources: Vec<TempResource>,
394 dst_buffers: FastHashMap<TrackerIndex, Arc<Buffer>>,
395 dst_textures: FastHashMap<TrackerIndex, Arc<Texture>>,
396 copied_blas_s: FastHashMap<TrackerIndex, Arc<Blas>>,
397 instance_flags: wgt::InstanceFlags,
398}
399
400impl PendingWrites {
401 pub fn new(
402 command_encoder: Box<dyn hal::DynCommandEncoder>,
403 instance_flags: wgt::InstanceFlags,
404 ) -> Self {
405 Self {
406 command_encoder,
407 is_recording: false,
408 temp_resources: Vec::new(),
409 dst_buffers: FastHashMap::default(),
410 dst_textures: FastHashMap::default(),
411 copied_blas_s: FastHashMap::default(),
412 instance_flags,
413 }
414 }
415
416 pub fn insert_buffer(&mut self, buffer: &Arc<Buffer>) {
417 self.dst_buffers
418 .insert(buffer.tracker_index(), buffer.clone());
419 }
420
421 pub fn insert_texture(&mut self, texture: &Arc<Texture>) {
422 self.dst_textures
423 .insert(texture.tracker_index(), texture.clone());
424 }
425
426 pub fn insert_blas(&mut self, blas: &Arc<Blas>) {
427 self.copied_blas_s
428 .insert(blas.tracker_index(), blas.clone());
429 }
430
431 pub fn contains_buffer(&self, buffer: &Arc<Buffer>) -> bool {
432 self.dst_buffers.contains_key(&buffer.tracker_index())
433 }
434
435 pub fn contains_texture(&self, texture: &Arc<Texture>) -> bool {
436 self.dst_textures.contains_key(&texture.tracker_index())
437 }
438
439 pub fn consume_temp(&mut self, resource: TempResource) {
440 self.temp_resources.push(resource);
441 }
442
443 pub fn consume(&mut self, buffer: FlushedStagingBuffer) {
444 self.temp_resources
445 .push(TempResource::StagingBuffer(buffer));
446 }
447
448 fn pre_submit(
449 &mut self,
450 command_allocator: &CommandAllocator,
451 device: &Arc<Device>,
452 queue: &Queue,
453 ) -> Result<Option<EncoderInFlight>, DeviceError> {
454 if self.is_recording {
455 let pending_buffers = mem::take(&mut self.dst_buffers);
456 let pending_textures = mem::take(&mut self.dst_textures);
457 let pending_blas_s = mem::take(&mut self.copied_blas_s);
458
459 let cmd_buf = unsafe { self.command_encoder.end_encoding() }
460 .map_err(|e| device.handle_hal_error(e))?;
461 self.is_recording = false;
462
463 let new_encoder = command_allocator
464 .acquire_encoder(device.raw(), queue.raw())
465 .map_err(|e| device.handle_hal_error(e))?;
466
467 let encoder = EncoderInFlight {
468 inner: crate::command::InnerCommandEncoder {
469 raw: ManuallyDrop::new(mem::replace(&mut self.command_encoder, new_encoder)),
470 list: vec![cmd_buf],
471 device: device.clone(),
472 is_open: false,
473 api: crate::command::EncodingApi::InternalUse,
474 label: "(wgpu internal) PendingWrites command encoder".into(),
475 },
476 trackers: Tracker::new(device.ordered_buffer_usages, device.ordered_texture_usages),
477 temp_resources: mem::take(&mut self.temp_resources),
478 _indirect_draw_validation_resources: crate::indirect_validation::DrawResources::new(
479 device.clone(),
480 ),
481 pending_buffers,
482 pending_textures,
483 pending_blas_s,
484 };
485 Ok(Some(encoder))
486 } else {
487 self.dst_buffers.clear();
488 self.dst_textures.clear();
489 self.copied_blas_s.clear();
490 Ok(None)
491 }
492 }
493
494 pub fn activate(&mut self) -> &mut dyn hal::DynCommandEncoder {
495 if !self.is_recording {
496 unsafe {
497 self.command_encoder
498 .begin_encoding(hal_label(
499 Some("(wgpu internal) PendingWrites"),
500 self.instance_flags,
501 ))
502 .unwrap();
503 }
504 self.is_recording = true;
505 }
506 self.command_encoder.as_mut()
507 }
508}
509
510impl Drop for PendingWrites {
511 fn drop(&mut self) {
512 unsafe {
513 if self.is_recording {
514 self.command_encoder.discard_encoding();
515 }
516 }
517 }
518}
519
520#[derive(Clone, Debug, Error)]
521#[non_exhaustive]
522pub enum QueueWriteError {
523 #[error(transparent)]
524 Queue(#[from] DeviceError),
525 #[error(transparent)]
526 Transfer(#[from] TransferError),
527 #[error(transparent)]
528 MemoryInitFailure(#[from] ClearError),
529 #[error(transparent)]
530 DestroyedResource(#[from] DestroyedResourceError),
531 #[error(transparent)]
532 InvalidResource(#[from] InvalidResourceError),
533}
534
535impl WebGpuError for QueueWriteError {
536 fn webgpu_error_type(&self) -> ErrorType {
537 match self {
538 Self::Queue(e) => e.webgpu_error_type(),
539 Self::Transfer(e) => e.webgpu_error_type(),
540 Self::MemoryInitFailure(e) => e.webgpu_error_type(),
541 Self::DestroyedResource(e) => e.webgpu_error_type(),
542 Self::InvalidResource(e) => e.webgpu_error_type(),
543 }
544 }
545}
546
547#[derive(Clone, Debug, Error)]
548#[non_exhaustive]
549pub enum QueueSubmitError {
550 #[error(transparent)]
551 Queue(#[from] DeviceError),
552 #[error(transparent)]
553 DestroyedResource(#[from] DestroyedResourceError),
554 #[error("{0} is still mapped")]
555 BufferStillMapped(ResourceErrorIdent),
556 #[error(transparent)]
557 InvalidResource(#[from] InvalidResourceError),
558 #[error(transparent)]
559 CommandEncoder(#[from] CommandEncoderError),
560 #[error(transparent)]
561 ValidateAsActionsError(#[from] crate::ray_tracing::ValidateAsActionsError),
562}
563
564impl WebGpuError for QueueSubmitError {
565 fn webgpu_error_type(&self) -> ErrorType {
566 match self {
567 Self::Queue(e) => e.webgpu_error_type(),
568 Self::CommandEncoder(e) => e.webgpu_error_type(),
569 Self::ValidateAsActionsError(e) => e.webgpu_error_type(),
570 Self::InvalidResource(e) => e.webgpu_error_type(),
571 Self::DestroyedResource(_) | Self::BufferStillMapped(_) => ErrorType::Validation,
572 }
573 }
574}
575
576pub(crate) struct PendingSubmission<'a> {
584 queue: &'a Queue,
585 snatch_guard: SnatchGuard<'a>,
586 command_index_guard: RwLockWriteGuard<'a, CommandIndices>,
587 pub executions: Vec<EncoderInFlight>,
589 surface_textures: FastHashMap<*const Texture, Arc<Texture>>,
593 pub index: SubmissionIndex,
594}
595
596pub(crate) struct SubmissionResult<'a> {
597 pub snatch_guard: SnatchGuard<'a>,
598}
599
600impl<'a> PendingSubmission<'a> {
601 fn submit(
602 self,
603 pending_writes: MutexGuard<'a, PendingWrites>,
604 ) -> Result<SubmissionResult<'a>, DeviceError> {
605 self.queue.submit_pending_submission(pending_writes, self)
606 }
607}
608
609impl Queue {
612 pub fn write_buffer(
613 &self,
614 buffer: Arc<Buffer>,
615 buffer_offset: wgt::BufferAddress,
616 data: &[u8],
617 ) -> Result<(), QueueWriteError> {
618 profiling::scope!("Queue::write_buffer");
619 api_log!("Queue::write_buffer");
620
621 self.device.check_is_valid()?;
622
623 let data_size = data.len() as wgt::BufferAddress;
624
625 self.same_device_as(buffer.as_ref())?;
626
627 let data_size = if let Some(data_size) = wgt::BufferSize::new(data_size) {
628 data_size
629 } else {
630 self.validate_write_buffer_impl(buffer.as_ref(), buffer_offset, 0)?;
635
636 log::trace!("Ignoring write_buffer of size 0");
637 return Ok(());
638 };
639
640 let mut staging_buffer = StagingBuffer::new(&self.device, data_size)?;
644
645 let staging_buffer = {
646 profiling::scope!("copy");
647 staging_buffer.write(data);
648 staging_buffer.flush()
649 };
650
651 let snatch_guard = self.device.snatchable_lock.read();
652 let mut pending_writes = self.pending_writes.lock();
653
654 let result = self.write_staging_buffer_impl(
655 &snatch_guard,
656 &mut pending_writes,
657 &staging_buffer,
658 buffer,
659 buffer_offset,
660 );
661
662 drop(snatch_guard);
663
664 pending_writes.consume(staging_buffer);
665
666 drop(pending_writes);
667
668 result
669 }
670
671 pub fn create_staging_buffer(
672 &self,
673 buffer_size: wgt::BufferSize,
674 ) -> Result<(StagingBuffer, NonNull<u8>), QueueWriteError> {
675 profiling::scope!("Queue::create_staging_buffer");
676 resource_log!("Queue::create_staging_buffer");
677
678 self.device.check_is_valid()?;
679
680 let staging_buffer = StagingBuffer::new(&self.device, buffer_size)?;
681 let ptr = unsafe { staging_buffer.ptr() };
682
683 Ok((staging_buffer, ptr))
684 }
685
686 pub fn write_staging_buffer(
687 &self,
688 buffer: Fallible<Buffer>,
689 buffer_offset: wgt::BufferAddress,
690 staging_buffer: StagingBuffer,
691 ) -> Result<(), QueueWriteError> {
692 profiling::scope!("Queue::write_staging_buffer");
693
694 self.device.check_is_valid()?;
695
696 let buffer = buffer.get()?;
697
698 let staging_buffer = staging_buffer.flush();
703
704 let snatch_guard = self.device.snatchable_lock.read();
705 let mut pending_writes = self.pending_writes.lock();
706
707 let result = self.write_staging_buffer_impl(
708 &snatch_guard,
709 &mut pending_writes,
710 &staging_buffer,
711 buffer,
712 buffer_offset,
713 );
714
715 drop(snatch_guard);
716
717 pending_writes.consume(staging_buffer);
718
719 drop(pending_writes);
720
721 result
722 }
723
724 pub fn validate_write_buffer(
725 &self,
726 buffer: Fallible<Buffer>,
727 buffer_offset: u64,
728 buffer_size: wgt::BufferSize,
729 ) -> Result<(), QueueWriteError> {
730 profiling::scope!("Queue::validate_write_buffer");
731
732 self.device.check_is_valid()?;
733
734 let buffer = buffer.get()?;
735
736 self.validate_write_buffer_impl(&buffer, buffer_offset, buffer_size.into())?;
737
738 Ok(())
739 }
740
741 fn validate_write_buffer_impl(
742 &self,
743 buffer: &Buffer,
744 buffer_offset: u64,
745 buffer_size: u64,
746 ) -> Result<(), TransferError> {
747 if !matches!(&*buffer.map_state.lock(), BufferMapState::Idle) {
748 return Err(TransferError::BufferNotAvailable);
749 }
750 buffer.check_usage(wgt::BufferUsages::COPY_DST)?;
751 if !buffer_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
752 return Err(TransferError::UnalignedCopySize(buffer_size));
753 }
754 if !buffer_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
755 return Err(TransferError::UnalignedBufferOffset(buffer_offset));
756 }
757
758 if buffer_offset > buffer.size {
759 return Err(TransferError::BufferStartOffsetOverrun {
760 start_offset: buffer_offset,
761 buffer_size: buffer.size,
762 side: CopySide::Destination,
763 });
764 }
765 if buffer_size > buffer.size - buffer_offset {
766 return Err(TransferError::BufferEndOffsetOverrun {
767 start_offset: buffer_offset,
768 size: buffer_size,
769 buffer_size: buffer.size,
770 side: CopySide::Destination,
771 });
772 }
773
774 Ok(())
775 }
776
777 fn write_staging_buffer_impl(
778 &self,
779 snatch_guard: &SnatchGuard,
780 pending_writes: &mut PendingWrites,
781 staging_buffer: &FlushedStagingBuffer,
782 buffer: Arc<Buffer>,
783 buffer_offset: u64,
784 ) -> Result<(), QueueWriteError> {
785 self.device.check_is_valid()?;
786
787 let transition = {
788 let mut trackers = self.device.trackers.lock();
789 trackers
790 .buffers
791 .set_single(&buffer, wgt::BufferUses::COPY_DST)
792 };
793
794 let dst_raw = buffer.try_raw(snatch_guard)?;
795
796 self.same_device_as(buffer.as_ref())?;
797
798 self.validate_write_buffer_impl(&buffer, buffer_offset, staging_buffer.size.into())?;
799
800 let region = hal::BufferCopy {
801 src_offset: 0,
802 dst_offset: buffer_offset,
803 size: staging_buffer.size,
804 };
805 let barriers = iter::once(hal::BufferBarrier {
806 buffer: staging_buffer.raw(),
807 usage: hal::StateTransition {
808 from: wgt::BufferUses::MAP_WRITE,
809 to: wgt::BufferUses::COPY_SRC,
810 },
811 })
812 .chain(transition.map(|pending| pending.into_hal(&buffer, snatch_guard)))
813 .collect::<Vec<_>>();
814 let encoder = pending_writes.activate();
815 unsafe {
816 encoder.transition_buffers(&barriers);
817 encoder.copy_buffer_to_buffer(staging_buffer.raw(), dst_raw, &[region]);
818 }
819
820 pending_writes.insert_buffer(&buffer);
821
822 {
825 buffer
826 .initialization_status
827 .write()
828 .drain(buffer_offset..(buffer_offset + staging_buffer.size.get()));
829 }
830
831 Ok(())
832 }
833
834 pub fn write_texture(
835 &self,
836 destination: wgt::TexelCopyTextureInfo<Arc<Texture>>,
837 data: &[u8],
838 data_layout: &wgt::TexelCopyBufferLayout,
839 size: &wgt::Extent3d,
840 ) -> Result<(), QueueWriteError> {
841 profiling::scope!("Queue::write_texture");
842 api_log!("Queue::write_texture");
843
844 self.device.check_is_valid()?;
845
846 let dst = destination.texture;
847 let destination = wgt::TexelCopyTextureInfo {
848 texture: (),
849 mip_level: destination.mip_level,
850 origin: destination.origin,
851 aspect: destination.aspect,
852 };
853
854 self.same_device_as(dst.as_ref())?;
855
856 dst.check_usage(wgt::TextureUsages::COPY_DST)
857 .map_err(TransferError::MissingTextureUsage)?;
858
859 let (hal_copy_size, array_layer_count) =
862 validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, size)?;
863
864 let (selector, dst_base) = extract_texture_selector(&destination, size, &dst)?;
865
866 validate_texture_copy_dst_format(dst.desc.format, destination.aspect)?;
867
868 validate_texture_buffer_copy(
869 &destination,
870 dst_base.aspect,
871 &dst.desc,
872 data_layout,
873 false, )?;
875
876 let (required_bytes_in_copy, _source_bytes_per_array_layer, _) =
879 validate_linear_texture_data(
880 data_layout,
881 dst.desc.format,
882 destination.aspect,
883 data.len() as wgt::BufferAddress,
884 CopySide::Source,
885 size,
886 )?;
887
888 if dst.desc.format.is_depth_stencil_format() {
889 self.device
890 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
891 .map_err(TransferError::from)?;
892 }
893
894 let snatch_guard = self.device.snatchable_lock.read();
895
896 let dst_raw = dst.try_raw(&snatch_guard)?;
897
898 if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
901 log::trace!("Ignoring write_texture of size 0");
902 return Ok(());
903 }
904
905 let mut pending_writes = self.pending_writes.lock();
906 let encoder = pending_writes.activate();
907
908 let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
914 0..1
916 } else {
917 destination.origin.z..destination.origin.z + size.depth_or_array_layers
918 };
919 let mut dst_initialization_status = dst.initialization_status.write();
920 if dst_initialization_status.mips[destination.mip_level as usize]
921 .check(init_layer_range.clone())
922 .is_some()
923 {
924 if has_copy_partial_init_tracker_coverage(size, &destination, &dst.desc) {
925 for layer_range in dst_initialization_status.mips[destination.mip_level as usize]
926 .drain(init_layer_range)
927 .collect::<Vec<core::ops::Range<u32>>>()
928 {
929 let mut trackers = self.device.trackers.lock();
930 crate::command::clear_texture(
931 &dst,
932 TextureInitRange {
933 mip_range: destination.mip_level..(destination.mip_level + 1),
934 layer_range,
935 },
936 encoder,
937 &mut trackers.textures,
938 &self.device.alignments,
939 self.device.zero_buffer.as_ref(),
940 &snatch_guard,
941 self.device.instance_flags,
942 )
943 .map_err(QueueWriteError::from)?;
944 }
945 } else {
946 dst_initialization_status.mips[destination.mip_level as usize]
947 .drain(init_layer_range);
948 }
949 }
950
951 let (block_width, block_height) = dst.desc.format.block_dimensions();
952 let width_in_blocks = size.width / block_width;
953 let height_in_blocks = size.height / block_height;
954
955 let block_size = dst
956 .desc
957 .format
958 .block_copy_size(Some(destination.aspect))
959 .unwrap();
960 let bytes_in_last_row = width_in_blocks * block_size;
961
962 let bytes_per_row = data_layout.bytes_per_row.unwrap_or(bytes_in_last_row);
963 let rows_per_image = data_layout.rows_per_image.unwrap_or(height_in_blocks);
964
965 let bytes_per_row_alignment = get_lowest_common_denom(
966 self.device.alignments.buffer_copy_pitch.get() as u32,
967 block_size,
968 );
969 assert!(u32::MAX - bytes_in_last_row >= bytes_per_row_alignment);
970 let stage_bytes_per_row = wgt::math::align_to(bytes_in_last_row, bytes_per_row_alignment);
971
972 let staging_buffer = if stage_bytes_per_row == bytes_per_row {
976 profiling::scope!("copy aligned");
977 let stage_size = wgt::BufferSize::new(required_bytes_in_copy).unwrap();
979 let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
980 staging_buffer.write(&data[data_layout.offset as usize..]);
981 staging_buffer
982 } else {
983 profiling::scope!("copy chunked");
984 let block_rows_in_copy = u64::from(size.depth_or_array_layers - 1)
986 * u64::from(rows_per_image)
987 + u64::from(height_in_blocks);
988 let stage_size = u64::from(stage_bytes_per_row)
991 .checked_mul(block_rows_in_copy)
992 .and_then(wgt::BufferSize::new)
993 .unwrap();
994 let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
995 for layer in 0..u64::from(size.depth_or_array_layers) {
996 let rows_offset = layer * u64::from(rows_per_image);
997 for row in rows_offset..rows_offset + u64::from(height_in_blocks) {
998 let src_offset = data_layout.offset + row * u64::from(bytes_per_row);
999 let dst_offset = row * u64::from(stage_bytes_per_row);
1000 unsafe {
1001 staging_buffer.write_with_offset(
1002 data,
1003 src_offset as isize,
1004 dst_offset as isize,
1005 bytes_in_last_row as usize,
1006 )
1007 }
1008 }
1009 }
1010 staging_buffer
1011 };
1012
1013 let staging_buffer = staging_buffer.flush();
1014
1015 let regions = (0..array_layer_count)
1016 .map(|array_layer_offset| {
1017 let mut texture_base = dst_base.clone();
1018 texture_base.array_layer += array_layer_offset;
1019 hal::BufferTextureCopy {
1020 buffer_layout: wgt::TexelCopyBufferLayout {
1021 offset: array_layer_offset as u64
1022 * rows_per_image as u64
1023 * stage_bytes_per_row as u64,
1024 bytes_per_row: Some(stage_bytes_per_row),
1025 rows_per_image: Some(rows_per_image),
1026 },
1027 texture_base,
1028 size: hal_copy_size,
1029 }
1030 })
1031 .collect::<Vec<_>>();
1032
1033 {
1034 let buffer_barrier = hal::BufferBarrier {
1035 buffer: staging_buffer.raw(),
1036 usage: hal::StateTransition {
1037 from: wgt::BufferUses::MAP_WRITE,
1038 to: wgt::BufferUses::COPY_SRC,
1039 },
1040 };
1041
1042 let mut trackers = self.device.trackers.lock();
1043 let transition =
1044 trackers
1045 .textures
1046 .set_single(&dst, selector, wgt::TextureUses::COPY_DST);
1047 let texture_barriers = transition
1048 .map(|pending| pending.into_hal(dst_raw))
1049 .collect::<Vec<_>>();
1050
1051 unsafe {
1052 encoder.transition_textures(&texture_barriers);
1053 encoder.transition_buffers(&[buffer_barrier]);
1054 encoder.copy_buffer_to_texture(staging_buffer.raw(), dst_raw, ®ions);
1055 }
1056 }
1057
1058 pending_writes.consume(staging_buffer);
1059 pending_writes.insert_texture(&dst);
1060
1061 Ok(())
1062 }
1063
1064 #[cfg(webgl)]
1065 pub fn copy_external_image_to_texture(
1066 &self,
1067 source: &wgt::CopyExternalImageSourceInfo,
1068 destination: wgt::CopyExternalImageDestInfo<Fallible<Texture>>,
1069 size: wgt::Extent3d,
1070 ) -> Result<(), QueueWriteError> {
1071 use crate::conv;
1072
1073 profiling::scope!("Queue::copy_external_image_to_texture");
1074
1075 self.device.check_is_valid()?;
1076
1077 let mut needs_flag = false;
1078 needs_flag |= matches!(source.source, wgt::ExternalImageSource::OffscreenCanvas(_));
1079 needs_flag |= source.origin != wgt::Origin2d::ZERO;
1080 needs_flag |= destination.color_space != wgt::PredefinedColorSpace::Srgb;
1081 #[allow(clippy::bool_comparison)]
1082 if matches!(source.source, wgt::ExternalImageSource::ImageBitmap(_)) {
1083 needs_flag |= source.flip_y != false;
1084 needs_flag |= destination.premultiplied_alpha != false;
1085 }
1086
1087 if needs_flag {
1088 self.device
1089 .require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES)
1090 .map_err(TransferError::from)?;
1091 }
1092
1093 let src_width = source.source.width();
1094 let src_height = source.source.height();
1095
1096 let dst = destination.texture.get()?;
1097 let premultiplied_alpha = destination.premultiplied_alpha;
1098 let destination = wgt::TexelCopyTextureInfo {
1099 texture: (),
1100 mip_level: destination.mip_level,
1101 origin: destination.origin,
1102 aspect: destination.aspect,
1103 };
1104
1105 if !conv::is_valid_external_image_copy_dst_texture_format(dst.desc.format) {
1106 return Err(
1107 TransferError::ExternalCopyToForbiddenTextureFormat(dst.desc.format).into(),
1108 );
1109 }
1110 if dst.desc.dimension != wgt::TextureDimension::D2 {
1111 return Err(TransferError::InvalidDimensionExternal.into());
1112 }
1113 dst.check_usage(wgt::TextureUsages::COPY_DST | wgt::TextureUsages::RENDER_ATTACHMENT)
1114 .map_err(TransferError::MissingTextureUsage)?;
1115 if dst.desc.sample_count != 1 {
1116 return Err(TransferError::InvalidSampleCount {
1117 sample_count: dst.desc.sample_count,
1118 }
1119 .into());
1120 }
1121
1122 if source.origin.x > src_width || src_width - source.origin.x < size.width {
1123 return Err(TransferError::TextureOverrun {
1124 start_offset: source.origin.x,
1125 end_offset: source.origin.x.saturating_add(size.width),
1126 texture_size: src_width,
1127 dimension: crate::resource::TextureErrorDimension::X,
1128 side: CopySide::Source,
1129 }
1130 .into());
1131 }
1132 if source.origin.y > src_height || src_height - source.origin.y < size.height {
1133 return Err(TransferError::TextureOverrun {
1134 start_offset: source.origin.y,
1135 end_offset: source.origin.y.saturating_add(size.height),
1136 texture_size: src_height,
1137 dimension: crate::resource::TextureErrorDimension::Y,
1138 side: CopySide::Source,
1139 }
1140 .into());
1141 }
1142 if size.depth_or_array_layers != 1 {
1143 return Err(TransferError::TextureOverrun {
1144 start_offset: 0,
1145 end_offset: size.depth_or_array_layers,
1146 texture_size: 1,
1147 dimension: crate::resource::TextureErrorDimension::Z,
1148 side: CopySide::Source,
1149 }
1150 .into());
1151 }
1152
1153 let (hal_copy_size, _) =
1156 validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, &size)?;
1157
1158 let (selector, dst_base) = extract_texture_selector(&destination, &size, &dst)?;
1159
1160 if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
1163 log::trace!("Ignoring copy_external_image_to_texture of size 0");
1164 return Ok(());
1165 }
1166
1167 let mut pending_writes = self.pending_writes.lock();
1168 let encoder = pending_writes.activate();
1169
1170 let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
1176 0..1
1178 } else {
1179 destination.origin.z..destination.origin.z + size.depth_or_array_layers
1180 };
1181 let mut dst_initialization_status = dst.initialization_status.write();
1182 if dst_initialization_status.mips[destination.mip_level as usize]
1183 .check(init_layer_range.clone())
1184 .is_some()
1185 {
1186 if has_copy_partial_init_tracker_coverage(&size, &destination, &dst.desc) {
1187 for layer_range in dst_initialization_status.mips[destination.mip_level as usize]
1188 .drain(init_layer_range)
1189 .collect::<Vec<core::ops::Range<u32>>>()
1190 {
1191 let mut trackers = self.device.trackers.lock();
1192 crate::command::clear_texture(
1193 &dst,
1194 TextureInitRange {
1195 mip_range: destination.mip_level..(destination.mip_level + 1),
1196 layer_range,
1197 },
1198 encoder,
1199 &mut trackers.textures,
1200 &self.device.alignments,
1201 self.device.zero_buffer.as_ref(),
1202 &self.device.snatchable_lock.read(),
1203 self.device.instance_flags,
1204 )
1205 .map_err(QueueWriteError::from)?;
1206 }
1207 } else {
1208 dst_initialization_status.mips[destination.mip_level as usize]
1209 .drain(init_layer_range);
1210 }
1211 }
1212
1213 let snatch_guard = self.device.snatchable_lock.read();
1214 let dst_raw = dst.try_raw(&snatch_guard)?;
1215
1216 let regions = hal::TextureCopy {
1217 src_base: hal::TextureCopyBase {
1218 mip_level: 0,
1219 array_layer: 0,
1220 origin: source.origin.to_3d(0),
1221 aspect: hal::FormatAspects::COLOR,
1222 },
1223 dst_base,
1224 size: hal_copy_size,
1225 };
1226
1227 let mut trackers = self.device.trackers.lock();
1228 let transitions = trackers
1229 .textures
1230 .set_single(&dst, selector, wgt::TextureUses::COPY_DST);
1231
1232 let encoder_webgl = encoder
1235 .as_any_mut()
1236 .downcast_mut::<hal::gles::CommandEncoder>()
1237 .unwrap();
1238 let dst_raw_webgl = dst_raw
1239 .as_any()
1240 .downcast_ref::<hal::gles::Texture>()
1241 .unwrap();
1242 let transitions_webgl = transitions.map(|pending| {
1243 let dyn_transition = pending.into_hal(dst_raw);
1244 hal::TextureBarrier {
1245 texture: dst_raw_webgl,
1246 range: dyn_transition.range,
1247 usage: dyn_transition.usage,
1248 }
1249 });
1250
1251 use hal::CommandEncoder as _;
1252 unsafe {
1253 encoder_webgl.transition_textures(transitions_webgl);
1254 encoder_webgl.copy_external_image_to_texture(
1255 source,
1256 dst_raw_webgl,
1257 premultiplied_alpha,
1258 iter::once(regions),
1259 );
1260 }
1261
1262 Ok(())
1263 }
1264
1265 pub fn flush_writes_for_buffer(
1267 &self,
1268 buffer: &Arc<Buffer>,
1269 snatch_guard: SnatchGuard,
1270 ) -> Result<(), BufferAccessError> {
1271 let submission = self
1272 .allocate_submission(snatch_guard)
1273 .map_err(|(_index, e)| e)?;
1274
1275 let pending_writes = self.pending_writes.lock();
1276 if !pending_writes.contains_buffer(buffer) {
1277 return Ok(());
1278 }
1279
1280 submission.submit(pending_writes)?;
1281
1282 Ok(())
1283 }
1284
1285 fn flush_pending_writes(&self) -> Result<Option<SubmissionIndex>, DeviceError> {
1286 let snatch_guard = self.device.snatchable_lock.read();
1287 let submission = self
1288 .allocate_submission(snatch_guard)
1289 .map_err(|(_index, e)| e)?;
1290 let submit_index = submission.index;
1291 let pending_writes = self.pending_writes.lock();
1292 if pending_writes.is_recording {
1293 submission.submit(pending_writes)?;
1294 Ok(Some(submit_index))
1295 } else {
1296 Ok(None)
1297 }
1298 }
1299
1300 #[cfg(feature = "trace")]
1301 fn trace_submission(
1302 &self,
1303 submit_index: SubmissionIndex,
1304 commands: Vec<crate::command::Command<crate::command::PointerReferences>>,
1305 ) {
1306 if let Some(ref mut trace) = *self.device.trace.lock() {
1307 trace.add(Action::Submit(submit_index, commands));
1308 }
1309 }
1310
1311 #[cfg(feature = "trace")]
1312 fn trace_failed_submission(
1313 &self,
1314 submit_index: SubmissionIndex,
1315 commands: Option<Vec<crate::command::Command<crate::command::PointerReferences>>>,
1316 error: alloc::string::String,
1317 ) {
1318 if let Some(ref mut trace) = *self.device.trace.lock() {
1319 trace.add(Action::FailedCommands {
1320 commands,
1321 failed_at_submit: Some(submit_index),
1322 error,
1323 });
1324 }
1325 }
1326
1327 pub fn submit(
1328 &self,
1329 command_buffers: &[Arc<CommandBuffer>],
1330 ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {
1331 profiling::scope!("Queue::submit");
1332 api_log!("Queue::submit");
1333
1334 let snatch_guard = self.device.snatchable_lock.read();
1335 let mut submission = self
1336 .allocate_submission(snatch_guard)
1337 .map_err(|(index, e)| (index, e.into()))?;
1338 let submit_index = submission.index;
1339
1340 let res = 'error: {
1341 let mut used_surface_textures = track::TextureUsageScope::default();
1342
1343 {
1344 if !command_buffers.is_empty() {
1345 profiling::scope!("prepare");
1346
1347 let mut first_error = None;
1348
1349 for command_buffer in command_buffers {
1355 profiling::scope!("process command buffer");
1356
1357 used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1360
1361 #[allow(unused_mut)]
1364 let mut cmd_buf_data = command_buffer.take_finished();
1365
1366 if first_error.is_some() {
1367 continue;
1368 }
1369
1370 #[cfg(feature = "trace")]
1371 let trace_commands = cmd_buf_data
1372 .as_mut()
1373 .ok()
1374 .and_then(|data| mem::take(&mut data.trace_commands));
1375
1376 let mut baked = match cmd_buf_data {
1377 Ok(cmd_buf_data) => {
1378 let res = validate_command_buffer(
1379 command_buffer,
1380 self,
1381 &cmd_buf_data,
1382 &submission.snatch_guard,
1383 &mut submission.surface_textures,
1384 &mut used_surface_textures,
1385 &mut submission.command_index_guard,
1386 );
1387 if let Err(err) = res {
1388 #[cfg(feature = "trace")]
1389 self.trace_failed_submission(
1390 submit_index,
1391 trace_commands,
1392 err.to_string(),
1393 );
1394 first_error.get_or_insert(err);
1395 continue;
1396 }
1397
1398 #[cfg(feature = "trace")]
1399 if let Some(commands) = trace_commands {
1400 self.trace_submission(submit_index, commands);
1401 }
1402
1403 cmd_buf_data.set_acceleration_structure_dependencies(
1404 &submission.snatch_guard,
1405 );
1406 cmd_buf_data.into_baked_commands()
1407 }
1408 Err(err) => {
1409 #[cfg(feature = "trace")]
1410 self.trace_failed_submission(
1411 submit_index,
1412 trace_commands,
1413 err.to_string(),
1414 );
1415 first_error.get_or_insert(err.into());
1416 continue;
1417 }
1418 };
1419
1420 if let Err(e) = baked.process_deferred_query_set_resolves(
1421 &self.device,
1422 &submission.snatch_guard,
1423 ) {
1424 break 'error Err(e.into());
1425 }
1426
1427 if let Err(e) = baked.encoder.open_pass(hal_label(
1429 Some("(wgpu internal) Transit"),
1430 self.device.instance_flags,
1431 )) {
1432 break 'error Err(e.into());
1433 }
1434
1435 let mut trackers = self.device.trackers.lock();
1437 if let Err(e) =
1438 baked.initialize_buffer_memory(&mut trackers, &submission.snatch_guard)
1439 {
1440 break 'error Err(e.into());
1441 }
1442 if let Err(e) = baked.initialize_texture_memory(
1443 &mut trackers,
1444 &self.device,
1445 &submission.snatch_guard,
1446 ) {
1447 break 'error Err(e.into());
1448 }
1449
1450 CommandEncoder::insert_barriers_from_device_tracker(
1453 baked.encoder.raw.as_mut(),
1454 &mut trackers,
1455 &baked.trackers,
1456 &submission.snatch_guard,
1457 );
1458
1459 if let Err(e) = baked.encoder.close_and_push_front() {
1460 break 'error Err(e.into());
1461 }
1462
1463 if !used_surface_textures.is_empty() {
1467 if let Err(e) = baked.encoder.open_pass(hal_label(
1468 Some("(wgpu internal) Present"),
1469 self.device.instance_flags,
1470 )) {
1471 break 'error Err(e.into());
1472 }
1473 let texture_barriers = trackers
1474 .textures
1475 .set_from_usage_scope_and_drain_transitions(
1476 &used_surface_textures,
1477 &submission.snatch_guard,
1478 )
1479 .collect::<Vec<_>>();
1480 unsafe {
1481 baked.encoder.raw.transition_textures(&texture_barriers);
1482 };
1483 if let Err(e) = baked.encoder.close() {
1484 break 'error Err(e.into());
1485 }
1486 used_surface_textures = track::TextureUsageScope::default();
1487 }
1488
1489 submission.executions.push(EncoderInFlight {
1491 inner: baked.encoder,
1492 trackers: baked.trackers,
1493 temp_resources: baked.temp_resources,
1494 _indirect_draw_validation_resources: baked
1495 .indirect_draw_validation_resources,
1496 pending_buffers: FastHashMap::default(),
1497 pending_textures: FastHashMap::default(),
1498 pending_blas_s: FastHashMap::default(),
1499 });
1500 }
1501
1502 if let Some(first_error) = first_error {
1503 break 'error Err(first_error);
1504 }
1505 }
1506 }
1507
1508 let pending_writes = self.pending_writes.lock();
1509
1510 let SubmissionResult { snatch_guard } = match submission.submit(pending_writes) {
1511 Ok(result) => result,
1512 Err(e) => break 'error Err(e.into()),
1513 };
1514
1515 profiling::scope!("cleanup");
1516
1517 let (closures, result) = self.device.maintain(wgt::PollType::Poll, snatch_guard);
1521 match result {
1522 Ok(status) => {
1523 debug_assert!(matches!(
1524 status,
1525 wgt::PollStatus::QueueEmpty | wgt::PollStatus::Poll
1526 ));
1527 }
1528 Err(WaitIdleError::Device(err)) => break 'error Err(QueueSubmitError::Queue(err)),
1529 Err(WaitIdleError::WrongSubmissionIndex(..)) => {
1530 unreachable!("Cannot get WrongSubmissionIndex from Poll")
1531 }
1532 Err(WaitIdleError::Timeout) => unreachable!("Cannot get Timeout from Poll"),
1533 };
1534
1535 Ok(closures)
1536 };
1537
1538 let callbacks = match res {
1539 Ok(ok) => ok,
1540 Err(e) => return Err((submit_index, e)),
1541 };
1542
1543 callbacks.fire();
1545
1546 self.device.lose_if_oom();
1547
1548 api_log!("Queue::submit returned submit index {submit_index}");
1549
1550 Ok(submit_index)
1551 }
1552
1553 fn allocate_submission<'a>(
1576 &'a self,
1577 snatch_guard: SnatchGuard<'a>,
1578 ) -> Result<PendingSubmission<'a>, (SubmissionIndex, DeviceError)> {
1579 let mut command_index_guard = self.device.command_indices.write();
1580 command_index_guard.active_submission_index += 1;
1581 let index = command_index_guard.active_submission_index;
1582
1583 if let Err(e) = self.device.check_is_valid() {
1584 return Err((index, e));
1585 }
1586
1587 let submission = PendingSubmission {
1588 queue: self,
1589 snatch_guard,
1590 command_index_guard,
1591 executions: Vec::new(),
1592 surface_textures: FastHashMap::default(),
1593 index,
1594 };
1595
1596 Ok(submission)
1597 }
1598
1599 fn submit_pending_submission<'a>(
1615 &self,
1616 mut pending_writes: MutexGuard<'_, PendingWrites>,
1617 prepared: PendingSubmission<'a>,
1618 ) -> Result<SubmissionResult<'a>, DeviceError> {
1619 let PendingSubmission {
1620 queue: _,
1621 snatch_guard,
1622 command_index_guard,
1623 mut executions,
1624 mut surface_textures,
1625 index: submit_index,
1626 } = prepared;
1627
1628 let mut used_surface_textures = track::TextureUsageScope::default();
1629 used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1630 for texture in pending_writes.dst_textures.values() {
1631 match texture.try_inner(&snatch_guard) {
1632 Ok(TextureInner::Native { .. }) => {}
1633 Ok(TextureInner::Surface { .. }) => {
1634 surface_textures.insert(Arc::as_ptr(texture), texture.clone());
1636
1637 unsafe {
1638 used_surface_textures
1639 .merge_single(texture, None, wgt::TextureUses::PRESENT)
1640 .unwrap()
1641 };
1642 }
1643 Err(DestroyedResourceError(_)) => {}
1648 }
1649 }
1650
1651 if !used_surface_textures.is_empty() {
1652 let mut trackers = self.device.trackers.lock();
1653
1654 let texture_barriers = trackers
1655 .textures
1656 .set_from_usage_scope_and_drain_transitions(&used_surface_textures, &snatch_guard)
1657 .collect::<Vec<_>>();
1658 unsafe {
1659 pending_writes
1660 .command_encoder
1661 .transition_textures(&texture_barriers);
1662 };
1663 }
1664
1665 match pending_writes.pre_submit(&self.device.command_allocator, &self.device, self) {
1666 Ok(Some(pending_execution)) => {
1667 executions.insert(0, pending_execution);
1668 }
1669 Ok(None) => {}
1670 Err(e) => return Err(e),
1671 }
1672 let hal_command_buffers = executions
1673 .iter()
1674 .flat_map(|e| e.inner.list.iter().map(|b| b.as_ref()))
1675 .collect::<Vec<_>>();
1676
1677 {
1678 let mut submit_surface_textures =
1679 SmallVec::<[&dyn hal::DynSurfaceTexture; 2]>::with_capacity(surface_textures.len());
1680 for texture in surface_textures.values() {
1681 let raw = match texture.inner.get(&snatch_guard) {
1682 Some(TextureInner::Surface { raw, .. }) => raw.as_ref(),
1683 _ => unreachable!(),
1684 };
1685 submit_surface_textures.push(raw);
1686 }
1687
1688 unsafe {
1689 self.raw().submit(
1690 &hal_command_buffers,
1691 &submit_surface_textures,
1692 (self.device.fence.as_ref(), submit_index),
1693 )
1694 }
1695 .map_err(|e| self.device.handle_hal_error(e))?;
1696
1697 drop(pending_writes);
1701
1702 self.device
1704 .last_successful_submission_index
1705 .fetch_max(submit_index, Ordering::SeqCst);
1706 }
1707
1708 self.lock_life().track_submission(submit_index, executions);
1710
1711 drop(command_index_guard);
1717
1718 Ok(SubmissionResult { snatch_guard })
1719 }
1720
1721 pub fn get_timestamp_period(&self) -> f32 {
1722 unsafe { self.raw().get_timestamp_period() }
1723 }
1724
1725 pub fn on_submitted_work_done(
1727 &self,
1728 closure: SubmittedWorkDoneClosure,
1729 ) -> Option<SubmissionIndex> {
1730 api_log!("Queue::on_submitted_work_done");
1731
1732 let _: Result<_, DeviceError> = self.flush_pending_writes();
1736
1737 self.lock_life().add_work_done_closure(closure)
1738 }
1739
1740 pub fn compact_blas(&self, blas: &Arc<Blas>) -> Result<Arc<Blas>, CompactBlasError> {
1741 profiling::scope!("Queue::compact_blas");
1742 api_log!("Queue::compact_blas");
1743
1744 let new_label = blas.label.clone() + " (compacted)";
1745
1746 self.device.check_is_valid()?;
1747 self.same_device_as(blas.as_ref())?;
1748
1749 let device = blas.device.clone();
1750
1751 let snatch_guard = device.snatchable_lock.read();
1752
1753 let BlasCompactState::Ready { size } = *blas.compacted_state.lock() else {
1754 return Err(CompactBlasError::BlasNotReady);
1755 };
1756
1757 let mut size_info = blas.size_info;
1758 size_info.acceleration_structure_size = size;
1759
1760 let mut pending_writes = self.pending_writes.lock();
1761 let cmd_buf_raw = pending_writes.activate();
1762
1763 let raw = unsafe {
1764 device
1765 .raw()
1766 .create_acceleration_structure(&hal::AccelerationStructureDescriptor {
1767 label: hal_label(Some(&new_label), device.instance_flags),
1768 size: size_info.acceleration_structure_size,
1769 format: hal::AccelerationStructureFormat::BottomLevel,
1770 allow_compaction: false,
1771 })
1772 }
1773 .map_err(DeviceError::from_hal)?;
1774
1775 let src_raw = blas.try_raw(&snatch_guard)?;
1776
1777 unsafe {
1778 cmd_buf_raw.copy_acceleration_structure_to_acceleration_structure(
1779 src_raw,
1780 raw.as_ref(),
1781 wgt::AccelerationStructureCopy::Compact,
1782 )
1783 };
1784
1785 let handle = unsafe {
1786 device
1787 .raw()
1788 .get_acceleration_structure_device_address(raw.as_ref())
1789 };
1790
1791 drop(snatch_guard);
1792
1793 let mut command_indices_lock = device.command_indices.write();
1794 command_indices_lock.next_acceleration_structure_build_command_index += 1;
1795 let built_index =
1796 NonZeroU64::new(command_indices_lock.next_acceleration_structure_build_command_index)
1797 .unwrap();
1798
1799 let new_blas = Arc::new(Blas {
1800 raw: Snatchable::new(raw),
1801 device: device.clone(),
1802 size_info,
1803 sizes: blas.sizes.clone(),
1804 flags: blas.flags & !AccelerationStructureFlags::ALLOW_COMPACTION,
1805 update_mode: blas.update_mode,
1806 built_index: RwLock::new(rank::BLAS_BUILT_INDEX, Some(built_index)),
1808 handle,
1809 label: new_label,
1810 tracking_data: TrackingData::new(blas.device.tracker_indices.blas_s.clone()),
1811 compaction_buffer: None,
1812 compacted_state: Mutex::new(rank::BLAS_COMPACTION_STATE, BlasCompactState::Compacted),
1813 });
1814
1815 pending_writes.insert_blas(blas);
1816 pending_writes.insert_blas(&new_blas);
1817
1818 Ok(new_blas)
1819 }
1820}
1821
1822impl Global {
1823 pub fn queue_write_buffer(
1824 &self,
1825 queue_id: QueueId,
1826 buffer_id: id::BufferId,
1827 buffer_offset: wgt::BufferAddress,
1828 data: &[u8],
1829 ) -> Result<(), QueueWriteError> {
1830 let queue = self.hub.queues.get(queue_id);
1831 let buffer = self.hub.buffers.get(buffer_id).get()?;
1832
1833 #[cfg(feature = "trace")]
1834 if let Some(ref mut trace) = *queue.device.trace.lock() {
1835 use crate::device::trace::DataKind;
1836 let size = data.len() as u64;
1837 let data = trace.make_binary(DataKind::Bin, data);
1838 trace.add(Action::WriteBuffer {
1839 id: buffer.to_trace(),
1840 data,
1841 offset: buffer_offset,
1842 size,
1843 queued: true,
1844 });
1845 }
1846
1847 queue.write_buffer(buffer, buffer_offset, data)
1848 }
1849
1850 pub fn queue_create_staging_buffer(
1851 &self,
1852 queue_id: QueueId,
1853 buffer_size: wgt::BufferSize,
1854 id_in: Option<id::StagingBufferId>,
1855 ) -> Result<(id::StagingBufferId, NonNull<u8>), QueueWriteError> {
1856 let queue = self.hub.queues.get(queue_id);
1857 let (staging_buffer, ptr) = queue.create_staging_buffer(buffer_size)?;
1858
1859 let fid = self.hub.staging_buffers.prepare(id_in);
1860 let id = fid.assign(staging_buffer);
1861
1862 Ok((id, ptr))
1863 }
1864
1865 pub fn queue_write_staging_buffer(
1866 &self,
1867 queue_id: QueueId,
1868 buffer_id: id::BufferId,
1869 buffer_offset: wgt::BufferAddress,
1870 staging_buffer_id: id::StagingBufferId,
1871 ) -> Result<(), QueueWriteError> {
1872 let queue = self.hub.queues.get(queue_id);
1873 let buffer = self.hub.buffers.get(buffer_id);
1874 let staging_buffer = self.hub.staging_buffers.remove(staging_buffer_id);
1875 queue.write_staging_buffer(buffer, buffer_offset, staging_buffer)
1876 }
1877
1878 pub fn queue_validate_write_buffer(
1879 &self,
1880 queue_id: QueueId,
1881 buffer_id: id::BufferId,
1882 buffer_offset: u64,
1883 buffer_size: wgt::BufferSize,
1884 ) -> Result<(), QueueWriteError> {
1885 let queue = self.hub.queues.get(queue_id);
1886 let buffer = self.hub.buffers.get(buffer_id);
1887 queue.validate_write_buffer(buffer, buffer_offset, buffer_size)
1888 }
1889
1890 pub fn queue_write_texture(
1891 &self,
1892 queue_id: QueueId,
1893 destination: &wgt::TexelCopyTextureInfo<id::TextureId>,
1894 data: &[u8],
1895 data_layout: &wgt::TexelCopyBufferLayout,
1896 size: &wgt::Extent3d,
1897 ) -> Result<(), QueueWriteError> {
1898 let queue = self.hub.queues.get(queue_id);
1899 let texture = self.hub.textures.get(destination.texture).get()?;
1900 let destination = wgt::TexelCopyTextureInfo {
1901 texture,
1902 mip_level: destination.mip_level,
1903 origin: destination.origin,
1904 aspect: destination.aspect,
1905 };
1906
1907 #[cfg(feature = "trace")]
1908 if let Some(ref mut trace) = *queue.device.trace.lock() {
1909 use crate::device::trace::DataKind;
1910 let data = trace.make_binary(DataKind::Bin, data);
1911 trace.add(Action::WriteTexture {
1912 to: destination.to_trace(),
1913 data,
1914 layout: *data_layout,
1915 size: *size,
1916 });
1917 }
1918
1919 queue.write_texture(destination, data, data_layout, size)
1920 }
1921
1922 #[cfg(webgl)]
1923 pub fn queue_copy_external_image_to_texture(
1924 &self,
1925 queue_id: QueueId,
1926 source: &wgt::CopyExternalImageSourceInfo,
1927 destination: crate::command::CopyExternalImageDestInfo,
1928 size: wgt::Extent3d,
1929 ) -> Result<(), QueueWriteError> {
1930 let queue = self.hub.queues.get(queue_id);
1931 let destination = wgt::CopyExternalImageDestInfo {
1932 texture: self.hub.textures.get(destination.texture),
1933 mip_level: destination.mip_level,
1934 origin: destination.origin,
1935 aspect: destination.aspect,
1936 color_space: destination.color_space,
1937 premultiplied_alpha: destination.premultiplied_alpha,
1938 };
1939 queue.copy_external_image_to_texture(source, destination, size)
1940 }
1941
1942 pub fn queue_submit(
1943 &self,
1944 queue_id: QueueId,
1945 command_buffer_ids: &[id::CommandBufferId],
1946 ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {
1947 let queue = self.hub.queues.get(queue_id);
1948 let command_buffer_guard = self.hub.command_buffers.read();
1949 let command_buffers = command_buffer_ids
1950 .iter()
1951 .map(|id| command_buffer_guard.get(*id))
1952 .collect::<Vec<_>>();
1953 drop(command_buffer_guard);
1954 queue.submit(&command_buffers)
1955 }
1956
1957 pub fn queue_get_timestamp_period(&self, queue_id: QueueId) -> f32 {
1958 let queue = self.hub.queues.get(queue_id);
1959
1960 if queue.device.timestamp_normalizer.get().unwrap().enabled() {
1961 return 1.0;
1962 }
1963
1964 queue.get_timestamp_period()
1965 }
1966
1967 pub fn queue_on_submitted_work_done(
1968 &self,
1969 queue_id: QueueId,
1970 closure: SubmittedWorkDoneClosure,
1971 ) -> SubmissionIndex {
1972 api_log!("Queue::on_submitted_work_done {queue_id:?}");
1973
1974 let queue = self.hub.queues.get(queue_id);
1975 let result = queue.on_submitted_work_done(closure);
1976 result.unwrap_or(0) }
1978
1979 pub fn queue_compact_blas(
1980 &self,
1981 queue_id: QueueId,
1982 blas_id: BlasId,
1983 id_in: Option<BlasId>,
1984 ) -> (BlasId, Option<u64>, Option<CompactBlasError>) {
1985 api_log!("Queue::compact_blas {queue_id:?}, {blas_id:?}");
1986
1987 let fid = self.hub.blas_s.prepare(id_in);
1988
1989 let queue = self.hub.queues.get(queue_id);
1990 let blas = self.hub.blas_s.get(blas_id);
1991 let device = &queue.device;
1992
1993 let error = 'error: {
1996 match device.require_features(wgpu_types::Features::EXPERIMENTAL_RAY_QUERY) {
1997 Ok(_) => {}
1998 Err(err) => break 'error err.into(),
1999 }
2000
2001 let blas = match blas.get() {
2002 Ok(blas) => blas,
2003 Err(err) => break 'error err.into(),
2004 };
2005
2006 let new_blas = match queue.compact_blas(&blas) {
2007 Ok(blas) => blas,
2008 Err(err) => break 'error err,
2009 };
2010
2011 let old_blas_size = blas.size_info.acceleration_structure_size;
2013 let new_blas_size = new_blas.size_info.acceleration_structure_size;
2014 let handle = new_blas.handle;
2015
2016 let id = fid.assign(Fallible::Valid(new_blas));
2017
2018 api_log!("CommandEncoder::compact_blas {blas_id:?} (size: {old_blas_size}) -> {id:?} (size: {new_blas_size})");
2019
2020 return (id, Some(handle), None);
2021 };
2022
2023 let id = fid.assign(Fallible::Invalid(Arc::new(error.to_string())));
2024
2025 (id, None, Some(error))
2026 }
2027}
2028
2029fn validate_command_buffer(
2030 command_buffer: &CommandBuffer,
2031 queue: &Queue,
2032 cmd_buf_data: &crate::command::CommandBufferMutable,
2033 snatch_guard: &SnatchGuard,
2034 surface_textures: &mut FastHashMap<*const Texture, Arc<Texture>>,
2035 used_surface_textures: &mut track::TextureUsageScope,
2036 command_index_guard: &mut RwLockWriteGuard<CommandIndices>,
2037) -> Result<(), QueueSubmitError> {
2038 command_buffer.same_device_as(queue)?;
2039
2040 {
2041 profiling::scope!("check resource state");
2042
2043 {
2044 profiling::scope!("buffers");
2045 for buffer in cmd_buf_data.trackers.buffers.used_resources() {
2046 buffer.check_destroyed(snatch_guard)?;
2047
2048 match *buffer.map_state.lock() {
2049 BufferMapState::Idle => (),
2050 _ => return Err(QueueSubmitError::BufferStillMapped(buffer.error_ident())),
2051 }
2052 }
2053 }
2054 {
2055 profiling::scope!("textures");
2056 for texture in cmd_buf_data.trackers.textures.used_resources() {
2057 let should_extend = match texture.try_inner(snatch_guard)? {
2058 TextureInner::Native { .. } => false,
2059 TextureInner::Surface { .. } => {
2060 surface_textures.insert(Arc::as_ptr(texture), texture.clone());
2062
2063 true
2064 }
2065 };
2066 if should_extend {
2067 unsafe {
2068 used_surface_textures
2069 .merge_single(texture, None, wgt::TextureUses::PRESENT)
2070 .unwrap();
2071 };
2072 }
2073 }
2074 }
2075 {
2076 profiling::scope!("query sets");
2077 for query_set in cmd_buf_data.trackers.query_sets.used_resources() {
2078 query_set.try_raw(snatch_guard)?;
2079 }
2080 }
2081 {
2085 profiling::scope!("bind groups");
2086 for bind_group in &cmd_buf_data.trackers.bind_groups {
2087 bind_group.try_raw(snatch_guard)?;
2089 }
2090 }
2091
2092 if let Err(e) =
2093 cmd_buf_data.validate_acceleration_structure_actions(snatch_guard, command_index_guard)
2094 {
2095 return Err(e.into());
2096 }
2097 }
2098 Ok(())
2099}