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