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