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 command_index_guard: RwLockWriteGuard<'a, CommandIndices>,
644 snatch_guard: SnatchGuard<'a>,
645 pub executions: Vec<EncoderInFlight>,
647 surface_textures: FastHashMap<*const Texture, Arc<Texture>>,
651 pub index: SubmissionIndex,
652}
653
654pub(crate) struct SubmissionResult<'a> {
655 pub snatch_guard: SnatchGuard<'a>,
656}
657
658impl<'a> PendingSubmission<'a> {
659 fn submit(
660 self,
661 pending_writes: MutexGuard<'a, PendingWrites>,
662 ) -> Result<SubmissionResult<'a>, DeviceError> {
663 self.queue.submit_pending_submission(pending_writes, self)
664 }
665}
666
667impl Queue {
670 pub fn write_buffer(
671 &self,
672 buffer: Arc<Buffer>,
673 buffer_offset: wgt::BufferAddress,
674 data: &[u8],
675 ) -> Result<(), QueueWriteError> {
676 profiling::scope!("Queue::write_buffer");
677 api_log!("Queue::write_buffer");
678
679 #[cfg(feature = "trace")]
680 if let Some(ref mut trace) = *self.device.trace.lock() {
681 use crate::device::trace::DataKind;
682 let size = data.len() as u64;
683 let data = trace.make_binary(DataKind::Bin, data);
684 trace.add(Action::WriteBuffer {
685 id: buffer.to_trace(),
686 data,
687 offset: buffer_offset,
688 size,
689 queued: true,
690 });
691 }
692
693 buffer.check_is_valid()?;
694 self.device.check_is_valid()?;
695
696 let data_size = data.len() as wgt::BufferAddress;
697
698 self.same_device_as(buffer.as_ref())?;
699
700 let data_size = if let Some(data_size) = wgt::BufferSize::new(data_size) {
701 data_size
702 } else {
703 self.validate_write_buffer_impl(buffer.as_ref(), buffer_offset, 0)?;
708
709 log::trace!("Ignoring write_buffer of size 0");
710 return Ok(());
711 };
712
713 let mut staging_buffer = StagingBuffer::new(&self.device, data_size)?;
717
718 let staging_buffer = {
719 profiling::scope!("copy");
720 staging_buffer.write(data);
721 staging_buffer.flush()
722 };
723
724 let snatch_guard = self.device.snatchable_lock.read();
725 let mut pending_writes = self.pending_writes.lock();
726
727 let result = self.write_staging_buffer_impl(
728 &snatch_guard,
729 &mut pending_writes,
730 &staging_buffer,
731 buffer,
732 buffer_offset,
733 );
734
735 drop(snatch_guard);
736
737 pending_writes.consume(staging_buffer);
738
739 drop(pending_writes);
740
741 result
742 }
743
744 pub fn create_staging_buffer(
745 &self,
746 buffer_size: wgt::BufferSize,
747 ) -> Result<(StagingBuffer, NonNull<u8>), QueueWriteError> {
748 profiling::scope!("Queue::create_staging_buffer");
749 resource_log!("Queue::create_staging_buffer");
750
751 self.device.check_is_valid()?;
752
753 let staging_buffer = StagingBuffer::new(&self.device, buffer_size)?;
754 let ptr = unsafe { staging_buffer.ptr() };
755
756 Ok((staging_buffer, ptr))
757 }
758
759 pub fn write_staging_buffer(
760 &self,
761 buffer: Arc<Buffer>,
762 buffer_offset: wgt::BufferAddress,
763 staging_buffer: StagingBuffer,
764 ) -> Result<(), QueueWriteError> {
765 profiling::scope!("Queue::write_staging_buffer");
766
767 buffer.check_is_valid()?;
768 self.device.check_is_valid()?;
769
770 let staging_buffer = staging_buffer.flush();
775
776 let snatch_guard = self.device.snatchable_lock.read();
777 let mut pending_writes = self.pending_writes.lock();
778
779 let result = self.write_staging_buffer_impl(
780 &snatch_guard,
781 &mut pending_writes,
782 &staging_buffer,
783 buffer,
784 buffer_offset,
785 );
786
787 drop(snatch_guard);
788
789 pending_writes.consume(staging_buffer);
790
791 drop(pending_writes);
792
793 result
794 }
795
796 pub fn validate_write_buffer(
797 &self,
798 buffer: Arc<Buffer>,
799 buffer_offset: u64,
800 buffer_size: wgt::BufferSize,
801 ) -> Result<(), QueueWriteError> {
802 profiling::scope!("Queue::validate_write_buffer");
803
804 self.device.check_is_valid()?;
805 buffer.check_is_valid()?;
806
807 self.validate_write_buffer_impl(&buffer, buffer_offset, buffer_size.into())?;
808
809 Ok(())
810 }
811
812 fn validate_write_buffer_impl(
813 &self,
814 buffer: &Buffer,
815 buffer_offset: u64,
816 buffer_size: u64,
817 ) -> Result<(), TransferError> {
818 if !matches!(&*buffer.map_state.lock(), BufferMapState::Idle) {
819 return Err(TransferError::BufferNotAvailable);
820 }
821 buffer.check_usage(wgt::BufferUsages::COPY_DST)?;
822 if !buffer_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
823 return Err(TransferError::UnalignedCopySize(buffer_size));
824 }
825 if !buffer_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
826 return Err(TransferError::UnalignedBufferOffset(buffer_offset));
827 }
828
829 if buffer_offset > buffer.size {
830 return Err(TransferError::BufferStartOffsetOverrun {
831 start_offset: buffer_offset,
832 buffer_size: buffer.size,
833 side: CopySide::Destination,
834 });
835 }
836 if buffer_size > buffer.size - buffer_offset {
837 return Err(TransferError::BufferEndOffsetOverrun {
838 start_offset: buffer_offset,
839 size: buffer_size,
840 buffer_size: buffer.size,
841 side: CopySide::Destination,
842 });
843 }
844
845 Ok(())
846 }
847
848 fn write_staging_buffer_impl(
849 &self,
850 snatch_guard: &SnatchGuard,
851 pending_writes: &mut PendingWrites,
852 staging_buffer: &FlushedStagingBuffer,
853 buffer: Arc<Buffer>,
854 buffer_offset: u64,
855 ) -> Result<(), QueueWriteError> {
856 self.device.check_is_valid()?;
857
858 let transition = {
859 let mut trackers = self.device.trackers.lock();
860 trackers
861 .buffers
862 .set_single(&buffer, wgt::BufferUses::COPY_DST)
863 };
864
865 let dst_raw = buffer.try_raw(snatch_guard)?;
866
867 self.same_device_as(buffer.as_ref())?;
868
869 self.validate_write_buffer_impl(&buffer, buffer_offset, staging_buffer.size.into())?;
870
871 let region = hal::BufferCopy {
872 src_offset: 0,
873 dst_offset: buffer_offset,
874 size: staging_buffer.size,
875 };
876 let barriers = iter::once(hal::BufferBarrier {
877 buffer: staging_buffer.raw(),
878 usage: hal::StateTransition {
879 from: wgt::BufferUses::MAP_WRITE,
880 to: wgt::BufferUses::COPY_SRC,
881 },
882 })
883 .chain(transition.map(|pending| pending.into_hal(&buffer, snatch_guard)))
884 .collect::<Vec<_>>();
885 let encoder = pending_writes.activate();
886 unsafe {
887 encoder.transition_buffers(&barriers);
888 encoder.copy_buffer_to_buffer(staging_buffer.raw(), dst_raw, &[region]);
889 }
890
891 pending_writes.insert_buffer(&buffer);
892
893 {
896 buffer
897 .initialization_status
898 .write()
899 .drain(buffer_offset..(buffer_offset + staging_buffer.size.get()));
900 }
901
902 Ok(())
903 }
904
905 pub fn write_texture(
906 &self,
907 destination: wgt::TexelCopyTextureInfo<Arc<Texture>>,
908 data: &[u8],
909 data_layout: &wgt::TexelCopyBufferLayout,
910 size: &wgt::Extent3d,
911 ) -> Result<(), QueueWriteError> {
912 profiling::scope!("Queue::write_texture");
913 api_log!("Queue::write_texture");
914
915 #[cfg(feature = "trace")]
916 if let Some(ref mut trace) = *self.device.trace.lock() {
917 use crate::device::trace::DataKind;
918 let data = trace.make_binary(DataKind::Bin, data);
919 trace.add(Action::WriteTexture {
920 to: destination.to_trace(),
921 data,
922 layout: *data_layout,
923 size: *size,
924 });
925 }
926
927 self.device.check_is_valid()?;
928
929 let dst = destination.texture;
930 let destination = wgt::TexelCopyTextureInfo {
931 texture: (),
932 mip_level: destination.mip_level,
933 origin: destination.origin,
934 aspect: destination.aspect,
935 };
936
937 self.same_device_as(dst.as_ref())?;
938
939 dst.check_valid()?;
940
941 dst.check_usage(wgt::TextureUsages::COPY_DST)
942 .map_err(TransferError::MissingTextureUsage)?;
943
944 let (hal_copy_size, array_layer_count) =
947 validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, size)?;
948
949 let (selector, dst_base) = extract_texture_selector(&destination, size, &dst)?;
950
951 validate_texture_copy_dst_format(dst.desc.format, destination.aspect)?;
952
953 validate_texture_buffer_copy(
954 &destination,
955 dst_base.aspect,
956 &dst.desc,
957 data_layout,
958 false, )?;
960
961 let (required_bytes_in_copy, _source_bytes_per_array_layer, _) =
964 validate_linear_texture_data(
965 data_layout,
966 dst.desc.format,
967 destination.aspect,
968 data.len() as wgt::BufferAddress,
969 CopySide::Source,
970 size,
971 )?;
972
973 if dst.desc.format.is_depth_stencil_format() {
974 self.device
975 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
976 .map_err(TransferError::from)?;
977 }
978
979 let snatch_guard = self.device.snatchable_lock.read();
980
981 let dst_raw = dst.try_inner(&snatch_guard)?.raw();
982
983 if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
986 log::trace!("Ignoring write_texture of size 0");
987 return Ok(());
988 }
989
990 let mut pending_writes = self.pending_writes.lock();
991 let encoder = pending_writes.activate();
992
993 let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
999 0..1
1001 } else {
1002 destination.origin.z..destination.origin.z + size.depth_or_array_layers
1003 };
1004 let mut dst_initialization_status = dst.initialization_status.write();
1005 if dst_initialization_status.mips[destination.mip_level as usize]
1006 .check(init_layer_range.clone())
1007 .is_some()
1008 {
1009 if has_copy_partial_init_tracker_coverage(size, &destination, &dst.desc) {
1010 for layer_range in dst_initialization_status.mips[destination.mip_level as usize]
1011 .drain(init_layer_range)
1012 .collect::<Vec<core::ops::Range<u32>>>()
1013 {
1014 let mut trackers = self.device.trackers.lock();
1015 crate::command::clear_texture(
1016 &dst,
1017 TextureInitRange {
1018 mip_range: destination.mip_level..(destination.mip_level + 1),
1019 layer_range,
1020 },
1021 encoder,
1022 &mut trackers.textures,
1023 &self.device.alignments,
1024 self.device.zero_buffer.as_ref(),
1025 &snatch_guard,
1026 self.device.instance_flags,
1027 )
1028 .map_err(QueueWriteError::from)?;
1029 }
1030 } else {
1031 dst_initialization_status.mips[destination.mip_level as usize]
1032 .drain(init_layer_range);
1033 }
1034 }
1035
1036 let (block_width, block_height) = dst.desc.format.block_dimensions();
1037 let width_in_blocks = size.width / block_width;
1038 let height_in_blocks = size.height / block_height;
1039
1040 let block_size = dst
1041 .desc
1042 .format
1043 .block_copy_size(Some(destination.aspect))
1044 .unwrap();
1045 let bytes_in_last_row = width_in_blocks * block_size;
1046
1047 let bytes_per_row = data_layout.bytes_per_row.unwrap_or(bytes_in_last_row);
1048 let rows_per_image = data_layout.rows_per_image.unwrap_or(height_in_blocks);
1049
1050 let bytes_per_row_alignment = get_lowest_common_denom(
1051 self.device.alignments.buffer_copy_pitch.get() as u32,
1052 block_size,
1053 );
1054 assert!(u32::MAX - bytes_in_last_row >= bytes_per_row_alignment);
1055 let stage_bytes_per_row = wgt::math::align_to(bytes_in_last_row, bytes_per_row_alignment);
1056
1057 let staging_buffer = if stage_bytes_per_row == bytes_per_row {
1061 profiling::scope!("copy aligned");
1062 let stage_size = wgt::BufferSize::new(required_bytes_in_copy).unwrap();
1064 let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
1065 staging_buffer.write(&data[data_layout.offset as usize..]);
1066 staging_buffer
1067 } else {
1068 profiling::scope!("copy chunked");
1069 let block_rows_in_copy = u64::from(size.depth_or_array_layers - 1)
1071 * u64::from(rows_per_image)
1072 + u64::from(height_in_blocks);
1073 let stage_size = u64::from(stage_bytes_per_row)
1076 .checked_mul(block_rows_in_copy)
1077 .and_then(wgt::BufferSize::new)
1078 .unwrap();
1079 let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
1080 for layer in 0..u64::from(size.depth_or_array_layers) {
1081 let rows_offset = layer * u64::from(rows_per_image);
1082 for row in rows_offset..rows_offset + u64::from(height_in_blocks) {
1083 let src_offset = data_layout.offset + row * u64::from(bytes_per_row);
1084 let dst_offset = row * u64::from(stage_bytes_per_row);
1085 unsafe {
1086 staging_buffer.write_with_offset(
1087 data,
1088 src_offset as isize,
1089 dst_offset as isize,
1090 bytes_in_last_row as usize,
1091 )
1092 }
1093 }
1094 }
1095 staging_buffer
1096 };
1097
1098 let staging_buffer = staging_buffer.flush();
1099
1100 let regions = (0..array_layer_count)
1101 .map(|array_layer_offset| {
1102 let mut texture_base = dst_base.clone();
1103 texture_base.array_layer += array_layer_offset;
1104 hal::BufferTextureCopy {
1105 buffer_layout: wgt::TexelCopyBufferLayout {
1106 offset: array_layer_offset as u64
1107 * rows_per_image as u64
1108 * stage_bytes_per_row as u64,
1109 bytes_per_row: Some(stage_bytes_per_row),
1110 rows_per_image: Some(rows_per_image),
1111 },
1112 texture_base,
1113 size: hal_copy_size,
1114 }
1115 })
1116 .collect::<Vec<_>>();
1117
1118 {
1119 let buffer_barrier = hal::BufferBarrier {
1120 buffer: staging_buffer.raw(),
1121 usage: hal::StateTransition {
1122 from: wgt::BufferUses::MAP_WRITE,
1123 to: wgt::BufferUses::COPY_SRC,
1124 },
1125 };
1126
1127 let mut trackers = self.device.trackers.lock();
1128 let transition =
1129 trackers
1130 .textures
1131 .set_single(&dst, selector, wgt::TextureUses::COPY_DST);
1132 let texture_barriers = transition
1133 .map(|pending| pending.into_hal(dst_raw))
1134 .collect::<Vec<_>>();
1135
1136 unsafe {
1137 encoder.transition_textures(&texture_barriers);
1138 encoder.transition_buffers(&[buffer_barrier]);
1139 encoder.copy_buffer_to_texture(staging_buffer.raw(), dst_raw, ®ions);
1140 }
1141 }
1142
1143 pending_writes.consume(staging_buffer);
1144 pending_writes.insert_texture(&dst);
1145
1146 Ok(())
1147 }
1148
1149 #[cfg(webgl)]
1150 pub fn copy_external_image_to_texture(
1151 &self,
1152 source: &wgt::CopyExternalImageSourceInfo,
1153 destination: wgt::CopyExternalImageDestInfo<Arc<Texture>>,
1154 size: wgt::Extent3d,
1155 ) -> Result<(), QueueWriteError> {
1156 use crate::conv;
1157
1158 profiling::scope!("Queue::copy_external_image_to_texture");
1159
1160 self.device.check_is_valid()?;
1161
1162 let mut needs_flag = false;
1163 needs_flag |= matches!(source.source, wgt::ExternalImageSource::OffscreenCanvas(_));
1164 needs_flag |= source.origin != wgt::Origin2d::ZERO;
1165 needs_flag |= destination.color_space != wgt::PredefinedColorSpace::Srgb;
1166 #[allow(clippy::bool_comparison)]
1167 if matches!(source.source, wgt::ExternalImageSource::ImageBitmap(_)) {
1168 needs_flag |= source.flip_y != false;
1169 needs_flag |= destination.premultiplied_alpha != false;
1170 }
1171
1172 if needs_flag {
1173 self.device
1174 .require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES)
1175 .map_err(TransferError::from)?;
1176 }
1177
1178 let src_width = source.source.width();
1179 let src_height = source.source.height();
1180
1181 let dst = destination.texture;
1182 let premultiplied_alpha = destination.premultiplied_alpha;
1183 let destination = wgt::TexelCopyTextureInfo {
1184 texture: (),
1185 mip_level: destination.mip_level,
1186 origin: destination.origin,
1187 aspect: destination.aspect,
1188 };
1189
1190 dst.check_valid()?;
1191
1192 if !conv::is_valid_external_image_copy_dst_texture_format(dst.desc.format) {
1193 return Err(
1194 TransferError::ExternalCopyToForbiddenTextureFormat(dst.desc.format).into(),
1195 );
1196 }
1197 if dst.desc.dimension != wgt::TextureDimension::D2 {
1198 return Err(TransferError::InvalidDimensionExternal.into());
1199 }
1200 dst.check_usage(wgt::TextureUsages::COPY_DST | wgt::TextureUsages::RENDER_ATTACHMENT)
1201 .map_err(TransferError::MissingTextureUsage)?;
1202 if dst.desc.sample_count != 1 {
1203 return Err(TransferError::InvalidSampleCount {
1204 sample_count: dst.desc.sample_count,
1205 }
1206 .into());
1207 }
1208
1209 if source.origin.x > src_width || src_width - source.origin.x < size.width {
1210 return Err(TransferError::TextureOverrun {
1211 start_offset: source.origin.x,
1212 end_offset: source.origin.x.saturating_add(size.width),
1213 texture_size: src_width,
1214 dimension: crate::resource::TextureErrorDimension::X,
1215 side: CopySide::Source,
1216 }
1217 .into());
1218 }
1219 if source.origin.y > src_height || src_height - source.origin.y < size.height {
1220 return Err(TransferError::TextureOverrun {
1221 start_offset: source.origin.y,
1222 end_offset: source.origin.y.saturating_add(size.height),
1223 texture_size: src_height,
1224 dimension: crate::resource::TextureErrorDimension::Y,
1225 side: CopySide::Source,
1226 }
1227 .into());
1228 }
1229 if size.depth_or_array_layers != 1 {
1230 return Err(TransferError::TextureOverrun {
1231 start_offset: 0,
1232 end_offset: size.depth_or_array_layers,
1233 texture_size: 1,
1234 dimension: crate::resource::TextureErrorDimension::Z,
1235 side: CopySide::Source,
1236 }
1237 .into());
1238 }
1239
1240 let (hal_copy_size, _) =
1243 validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, &size)?;
1244
1245 let (selector, dst_base) = extract_texture_selector(&destination, &size, &dst)?;
1246
1247 if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
1250 log::trace!("Ignoring copy_external_image_to_texture of size 0");
1251 return Ok(());
1252 }
1253
1254 let mut pending_writes = self.pending_writes.lock();
1255 let encoder = pending_writes.activate();
1256
1257 let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
1263 0..1
1265 } else {
1266 destination.origin.z..destination.origin.z + size.depth_or_array_layers
1267 };
1268 let mut dst_initialization_status = dst.initialization_status.write();
1269 if dst_initialization_status.mips[destination.mip_level as usize]
1270 .check(init_layer_range.clone())
1271 .is_some()
1272 {
1273 if has_copy_partial_init_tracker_coverage(&size, &destination, &dst.desc) {
1274 for layer_range in dst_initialization_status.mips[destination.mip_level as usize]
1275 .drain(init_layer_range)
1276 .collect::<Vec<core::ops::Range<u32>>>()
1277 {
1278 let mut trackers = self.device.trackers.lock();
1279 crate::command::clear_texture(
1280 &dst,
1281 TextureInitRange {
1282 mip_range: destination.mip_level..(destination.mip_level + 1),
1283 layer_range,
1284 },
1285 encoder,
1286 &mut trackers.textures,
1287 &self.device.alignments,
1288 self.device.zero_buffer.as_ref(),
1289 &self.device.snatchable_lock.read(),
1290 self.device.instance_flags,
1291 )
1292 .map_err(QueueWriteError::from)?;
1293 }
1294 } else {
1295 dst_initialization_status.mips[destination.mip_level as usize]
1296 .drain(init_layer_range);
1297 }
1298 }
1299
1300 let snatch_guard = self.device.snatchable_lock.read();
1301 let dst_raw = dst.try_raw(&snatch_guard)?;
1302
1303 let regions = hal::TextureCopy {
1304 src_base: hal::TextureCopyBase {
1305 mip_level: 0,
1306 array_layer: 0,
1307 origin: source.origin.to_3d(0),
1308 aspect: hal::FormatAspects::COLOR,
1309 },
1310 dst_base,
1311 size: hal_copy_size,
1312 };
1313
1314 let mut trackers = self.device.trackers.lock();
1315 let transitions = trackers
1316 .textures
1317 .set_single(&dst, selector, wgt::TextureUses::COPY_DST);
1318
1319 let encoder_webgl = encoder
1322 .as_any_mut()
1323 .downcast_mut::<hal::gles::CommandEncoder>()
1324 .unwrap();
1325 let dst_raw_webgl = dst_raw
1326 .as_any()
1327 .downcast_ref::<hal::gles::Texture>()
1328 .unwrap();
1329 let transitions_webgl = transitions.map(|pending| {
1330 let dyn_transition = pending.into_hal(dst_raw);
1331 hal::TextureBarrier {
1332 texture: dst_raw_webgl,
1333 range: dyn_transition.range,
1334 usage: dyn_transition.usage,
1335 }
1336 });
1337
1338 use hal::CommandEncoder as _;
1339 unsafe {
1340 encoder_webgl.transition_textures(transitions_webgl);
1341 encoder_webgl.copy_external_image_to_texture(
1342 source,
1343 dst_raw_webgl,
1344 premultiplied_alpha,
1345 iter::once(regions),
1346 );
1347 }
1348
1349 pending_writes.insert_texture(&dst);
1350
1351 Ok(())
1352 }
1353
1354 pub fn flush_writes_for_buffer(
1356 &self,
1357 buffer: &Arc<Buffer>,
1358 snatch_guard: SnatchGuard,
1359 ) -> Result<(), BufferAccessError> {
1360 let submission = self
1361 .allocate_submission(snatch_guard)
1362 .map_err(|(_index, e)| e)?;
1363
1364 let pending_writes = self.pending_writes.lock();
1365 if !pending_writes.contains_buffer(buffer) {
1366 return Ok(());
1367 }
1368
1369 submission.submit(pending_writes)?;
1370
1371 Ok(())
1372 }
1373
1374 fn flush_pending_writes(&self) -> Result<Option<SubmissionIndex>, DeviceError> {
1375 let snatch_guard = self.device.snatchable_lock.read();
1376 let submission = self
1377 .allocate_submission(snatch_guard)
1378 .map_err(|(_index, e)| e)?;
1379 let submit_index = submission.index;
1380 let pending_writes = self.pending_writes.lock();
1381 if pending_writes.is_recording {
1382 submission.submit(pending_writes)?;
1383 Ok(Some(submit_index))
1384 } else {
1385 Ok(None)
1386 }
1387 }
1388
1389 #[cfg(feature = "trace")]
1390 fn trace_submission(
1391 &self,
1392 submit_index: SubmissionIndex,
1393 commands: Vec<crate::command::Command<crate::command::PointerReferences>>,
1394 ) {
1395 if let Some(ref mut trace) = *self.device.trace.lock() {
1396 trace.add(Action::Submit(submit_index, commands));
1397 }
1398 }
1399
1400 #[cfg(feature = "trace")]
1401 fn trace_failed_submission(
1402 &self,
1403 submit_index: SubmissionIndex,
1404 commands: Option<Vec<crate::command::Command<crate::command::PointerReferences>>>,
1405 error: alloc::string::String,
1406 ) {
1407 if let Some(ref mut trace) = *self.device.trace.lock() {
1408 trace.add(Action::FailedCommands {
1409 commands,
1410 failed_at_submit: Some(submit_index),
1411 error,
1412 });
1413 }
1414 }
1415
1416 pub fn submit(
1417 &self,
1418 command_buffers: &[Arc<CommandBuffer>],
1419 ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {
1420 profiling::scope!("Queue::submit");
1421 api_log!("Queue::submit");
1422
1423 let snatch_guard = self.device.snatchable_lock.read();
1424 let mut submission = self
1425 .allocate_submission(snatch_guard)
1426 .map_err(|(index, e)| (index, e.into()))?;
1427 let submit_index = submission.index;
1428
1429 let mut lose_device_on_error = false;
1433
1434 let res = 'error: {
1435 let mut used_surface_textures = track::TextureUsageScope::default();
1436 let mut baked_command_buffers = Vec::with_capacity(command_buffers.len());
1437
1438 if !command_buffers.is_empty() {
1439 profiling::scope!("prepare");
1440
1441 let mut first_error = None;
1442
1443 #[deny(clippy::question_mark_used)]
1446 for command_buffer in command_buffers {
1447 profiling::scope!("process command buffer");
1448
1449 used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1452
1453 #[cfg_attr(not(feature = "trace"), expect(unused_mut))]
1457 let mut cmd_buf_data = command_buffer.take_finished();
1458
1459 if first_error.is_some() {
1460 continue;
1461 }
1462
1463 #[cfg(feature = "trace")]
1464 let trace_commands = cmd_buf_data
1465 .as_mut()
1466 .ok()
1467 .and_then(|data| mem::take(&mut data.trace_commands));
1468
1469 let mut baked = match cmd_buf_data {
1470 Ok(cmd_buf_data) => {
1471 let res = validate_command_buffer(
1472 command_buffer,
1473 self,
1474 &cmd_buf_data,
1475 &submission.snatch_guard,
1476 &mut submission.surface_textures,
1477 &mut used_surface_textures,
1478 &mut submission.command_index_guard,
1479 );
1480 if let Err(err) = res {
1481 #[cfg(feature = "trace")]
1482 self.trace_failed_submission(
1483 submit_index,
1484 trace_commands,
1485 err.to_string(),
1486 );
1487 first_error.get_or_insert(err);
1488 continue;
1489 }
1490
1491 #[cfg(feature = "trace")]
1492 if let Some(commands) = trace_commands {
1493 self.trace_submission(submit_index, commands);
1494 }
1495
1496 cmd_buf_data
1497 .set_acceleration_structure_dependencies(&submission.snatch_guard);
1498 cmd_buf_data.into_baked_commands()
1499 }
1500 Err(err) => {
1501 #[cfg(feature = "trace")]
1502 self.trace_failed_submission(
1503 submit_index,
1504 trace_commands,
1505 err.to_string(),
1506 );
1507 first_error.get_or_insert(err.into());
1508 continue;
1509 }
1510 };
1511
1512 if let Err(e) = baked
1513 .process_deferred_query_set_resolves(&self.device, &submission.snatch_guard)
1514 {
1515 break 'error Err(e.into());
1516 }
1517
1518 baked_command_buffers.push(baked);
1519 }
1520
1521 if let Some(first_error) = first_error {
1522 break 'error Err(first_error);
1523 }
1524
1525 lose_device_on_error = true;
1538
1539 let mut trackers = self.device.trackers.lock();
1541
1542 for mut baked in baked_command_buffers {
1543 profiling::scope!("process baked commands");
1544
1545 if let Err(e) = baked.encoder.open_pass(hal_label(
1547 Some("(wgpu internal) Transit"),
1548 self.device.instance_flags,
1549 )) {
1550 break 'error Err(e.into());
1551 }
1552
1553 baked.initialize_buffer_memory(&mut trackers, &submission.snatch_guard);
1554
1555 if let Err(e) = baked.initialize_texture_memory(
1556 &mut trackers,
1557 &self.device,
1558 &submission.snatch_guard,
1559 ) {
1560 break 'error Err(QueueSubmitError::CommandEncoder(
1561 CommandEncoderError::Clear(e),
1562 ));
1563 }
1564
1565 CommandEncoder::insert_barriers_from_device_tracker(
1568 baked.encoder.raw.as_mut(),
1569 &mut trackers,
1570 &baked.trackers,
1571 &submission.snatch_guard,
1572 );
1573
1574 if let Err(e) = baked.encoder.close_and_push_front() {
1575 break 'error Err(e.into());
1576 }
1577
1578 if !used_surface_textures.is_empty() {
1582 if let Err(e) = baked.encoder.open_pass(hal_label(
1583 Some("(wgpu internal) Present"),
1584 self.device.instance_flags,
1585 )) {
1586 break 'error Err(e.into());
1587 }
1588 let texture_barriers = trackers
1589 .textures
1590 .set_from_usage_scope_and_drain_transitions(
1591 &used_surface_textures,
1592 &submission.snatch_guard,
1593 )
1594 .collect::<Vec<_>>();
1595 unsafe {
1596 baked.encoder.raw.transition_textures(&texture_barriers);
1597 };
1598 if let Err(e) = baked.encoder.close() {
1599 break 'error Err(e.into());
1600 }
1601 used_surface_textures = track::TextureUsageScope::default();
1602 }
1603
1604 submission.executions.push(EncoderInFlight {
1606 inner: baked.encoder,
1607 trackers: baked.trackers,
1608 temp_resources: baked.temp_resources,
1609 _indirect_draw_validation_resources: baked
1610 .indirect_draw_validation_resources,
1611 pending_buffers: FastHashMap::default(),
1612 pending_textures: FastHashMap::default(),
1613 pending_blas_s: FastHashMap::default(),
1614 });
1615 }
1616 }
1617
1618 let pending_writes = self.pending_writes.lock();
1619
1620 let SubmissionResult { snatch_guard } = match submission.submit(pending_writes) {
1621 Ok(result) => result,
1622 Err(e) => break 'error Err(e.into()),
1623 };
1624
1625 profiling::scope!("cleanup");
1626
1627 lose_device_on_error = false;
1631
1632 let (closures, result) = self.device.maintain(wgt::PollType::Poll, snatch_guard);
1636 match result {
1637 Ok(status) => {
1638 debug_assert!(matches!(
1639 status,
1640 wgt::PollStatus::QueueEmpty | wgt::PollStatus::Poll
1641 ));
1642 }
1643 Err(WaitIdleError::Device(err)) => break 'error Err(QueueSubmitError::Queue(err)),
1644 Err(WaitIdleError::WrongSubmissionIndex(..)) => {
1645 unreachable!("Cannot get WrongSubmissionIndex from Poll")
1646 }
1647 Err(WaitIdleError::Timeout) => unreachable!("Cannot get Timeout from Poll"),
1648 };
1649
1650 Ok(closures)
1651 }; let callbacks = match res {
1654 Ok(ok) => ok,
1655 Err(e) => {
1656 if lose_device_on_error {
1657 self.device.lose("submission failed");
1658 }
1659 return Err((submit_index, e));
1660 }
1661 };
1662
1663 callbacks.fire();
1665
1666 self.device.lose_if_oom();
1667
1668 api_log!("Queue::submit returned submit index {submit_index}");
1669
1670 Ok(submit_index)
1671 }
1672
1673 fn allocate_submission<'a>(
1696 &'a self,
1697 snatch_guard: SnatchGuard<'a>,
1698 ) -> Result<PendingSubmission<'a>, (SubmissionIndex, DeviceError)> {
1699 let mut command_index_guard = self.device.command_indices.write();
1700 command_index_guard.active_submission_index += 1;
1701 let index = command_index_guard.active_submission_index;
1702
1703 if let Err(e) = self.device.check_is_valid() {
1704 return Err((index, e));
1705 }
1706
1707 let submission = PendingSubmission {
1708 queue: self,
1709 snatch_guard,
1710 command_index_guard,
1711 executions: Vec::new(),
1712 surface_textures: FastHashMap::default(),
1713 index,
1714 };
1715
1716 Ok(submission)
1717 }
1718
1719 fn submit_pending_submission<'a>(
1735 &self,
1736 mut pending_writes: MutexGuard<'_, PendingWrites>,
1737 prepared: PendingSubmission<'a>,
1738 ) -> Result<SubmissionResult<'a>, DeviceError> {
1739 let PendingSubmission {
1740 queue: _,
1741 snatch_guard,
1742 command_index_guard,
1743 mut executions,
1744 mut surface_textures,
1745 index: submit_index,
1746 } = prepared;
1747
1748 let mut used_surface_textures = track::TextureUsageScope::default();
1749 used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1750 for texture in pending_writes.dst_textures.values() {
1751 match texture.try_inner(&snatch_guard) {
1752 Ok(TextureInner::Native { .. }) => {}
1753 Ok(TextureInner::Surface { .. }) => {
1754 surface_textures.insert(Arc::as_ptr(texture), texture.clone());
1756
1757 unsafe {
1758 used_surface_textures
1759 .merge_single(texture, None, wgt::TextureUses::PRESENT)
1760 .unwrap()
1761 };
1762 }
1763 Err(InvalidOrDestroyedResourceError::DestroyedResource(_)) => {}
1768 Err(InvalidOrDestroyedResourceError::InvalidResource(_)) => {
1769 unreachable!()
1770 }
1771 }
1772 }
1773
1774 if !used_surface_textures.is_empty() {
1775 let mut trackers = self.device.trackers.lock();
1776
1777 let texture_barriers = trackers
1778 .textures
1779 .set_from_usage_scope_and_drain_transitions(&used_surface_textures, &snatch_guard)
1780 .collect::<Vec<_>>();
1781 unsafe {
1782 pending_writes
1783 .command_encoder
1784 .transition_textures(&texture_barriers);
1785 };
1786 }
1787
1788 match pending_writes.pre_submit(&self.device.command_allocator, &self.device, self) {
1789 Ok(Some(pending_execution)) => {
1790 executions.insert(0, pending_execution);
1791 }
1792 Ok(None) => {}
1793 Err(e) => return Err(e),
1794 }
1795 let hal_command_buffers = executions
1796 .iter()
1797 .flat_map(|e| e.inner.list.iter().map(|b| b.as_ref()))
1798 .collect::<Vec<_>>();
1799
1800 {
1801 let mut submit_surface_textures =
1802 SmallVec::<[&dyn hal::DynSurfaceTexture; 2]>::with_capacity(surface_textures.len());
1803 for texture in surface_textures.values() {
1804 let raw = match texture.try_inner(&snatch_guard).ok() {
1805 Some(TextureInner::Surface { raw, .. }) => raw.as_ref(),
1806 _ => unreachable!(),
1807 };
1808 submit_surface_textures.push(raw);
1809 }
1810
1811 unsafe {
1812 self.raw().submit(
1813 &hal_command_buffers,
1814 &submit_surface_textures,
1815 (self.device.fence.as_ref(), submit_index),
1816 )
1817 }
1818 .map_err(|e| self.device.handle_hal_error(e))?;
1819
1820 drop(pending_writes);
1824
1825 self.device
1827 .last_successful_submission_index
1828 .fetch_max(submit_index, Ordering::SeqCst);
1829 }
1830
1831 self.lock_life().track_submission(submit_index, executions);
1833
1834 drop(command_index_guard);
1840
1841 Ok(SubmissionResult { snatch_guard })
1842 }
1843
1844 pub(crate) fn get_raw_timestamp_period(&self) -> f32 {
1845 unsafe { self.raw().get_timestamp_period() }
1846 }
1847
1848 pub fn get_timestamp_period(&self) -> f32 {
1849 if self.device.timestamp_normalizer.get().unwrap().enabled() {
1850 return 1.0;
1851 }
1852
1853 self.get_raw_timestamp_period()
1854 }
1855
1856 pub fn on_submitted_work_done(
1858 &self,
1859 closure: SubmittedWorkDoneClosure,
1860 ) -> Option<SubmissionIndex> {
1861 api_log!("Queue::on_submitted_work_done");
1862
1863 let _: Result<_, DeviceError> = self.flush_pending_writes();
1867
1868 self.lock_life().add_work_done_closure(closure)
1869 }
1870
1871 #[allow(trivial_casts)]
1872 pub fn compact_blas(&self, blas: &Arc<Blas>) -> (Arc<Blas>, Option<CompactBlasError>) {
1873 api_log!(
1874 "Queue::compact_blas {:?}, {:?}",
1875 self as *const _,
1876 Arc::as_ptr(blas)
1877 );
1878
1879 let (blas, error) = match self.compact_blas_inner(blas) {
1880 Ok(blas) => (blas, None),
1881 Err(err) => {
1882 let new_label = blas.label.clone() + " (compacted)";
1883 (
1884 Blas::invalid(
1885 self.device.clone(),
1886 &BlasDescriptor {
1887 label: Some(new_label.into()),
1888 flags: blas.flags,
1889 update_mode: blas.update_mode,
1890 },
1891 ),
1892 Some(err),
1893 )
1894 }
1895 };
1896
1897 (blas, error)
1900 }
1901
1902 pub(crate) fn compact_blas_inner(
1903 &self,
1904 blas: &Arc<Blas>,
1905 ) -> Result<Arc<Blas>, CompactBlasError> {
1906 profiling::scope!("Queue::compact_blas");
1907 api_log!("Queue::compact_blas");
1908
1909 let new_label = blas.label.clone() + " (compacted)";
1910
1911 self.device.check_is_valid()?;
1912
1913 self.device
1914 .require_features(wgpu_types::Features::EXPERIMENTAL_RAY_QUERY)?;
1915
1916 blas.check_is_valid()?;
1917 self.same_device_as(blas.as_ref())?;
1918
1919 let device = blas.device.clone();
1920
1921 let snatch_guard = device.snatchable_lock.read();
1922
1923 let BlasCompactState::Ready { size } = *blas.compacted_state.lock() else {
1924 return Err(CompactBlasError::BlasNotReady);
1925 };
1926
1927 let mut size_info = blas.size_info;
1928 size_info.acceleration_structure_size = size;
1929
1930 let mut pending_writes = self.pending_writes.lock();
1931 let cmd_buf_raw = pending_writes.activate();
1932
1933 let raw = unsafe {
1934 device
1935 .raw()
1936 .create_acceleration_structure(&hal::AccelerationStructureDescriptor {
1937 label: hal_label(Some(&new_label), device.instance_flags),
1938 size: size_info.acceleration_structure_size,
1939 format: hal::AccelerationStructureFormat::BottomLevel,
1940 allow_compaction: false,
1941 })
1942 }
1943 .map_err(DeviceError::from_hal)?;
1944
1945 let src_raw = blas.try_raw(&snatch_guard)?;
1946
1947 unsafe {
1948 cmd_buf_raw.copy_acceleration_structure_to_acceleration_structure(
1949 src_raw,
1950 raw.as_ref(),
1951 wgt::AccelerationStructureCopy::Compact,
1952 )
1953 };
1954
1955 let handle = unsafe {
1956 device
1957 .raw()
1958 .get_acceleration_structure_device_address(raw.as_ref())
1959 };
1960
1961 drop(snatch_guard);
1962
1963 let mut command_indices_lock = device.command_indices.write();
1964 command_indices_lock.next_acceleration_structure_build_command_index += 1;
1965 let built_index =
1966 NonZeroU64::new(command_indices_lock.next_acceleration_structure_build_command_index)
1967 .unwrap();
1968
1969 let new_blas = Arc::new(Blas {
1970 state: ResourceState::Valid(BlasState {
1971 raw: Snatchable::new(raw),
1972 }),
1973 device: device.clone(),
1974 size_info,
1975 sizes: blas.sizes.clone(),
1976 flags: blas.flags & !AccelerationStructureFlags::ALLOW_COMPACTION,
1977 update_mode: blas.update_mode,
1978 built_index: RwLock::new(rank::BLAS_BUILT_INDEX, Some(built_index)),
1980 handle,
1981 label: new_label,
1982 tracking_data: TrackingData::new(blas.device.tracker_indices.blas_s.clone()),
1983 compaction_buffer: None,
1984 compacted_state: Mutex::new(rank::BLAS_COMPACTION_STATE, BlasCompactState::Compacted),
1985 });
1986
1987 pending_writes.insert_blas(blas);
1988 pending_writes.insert_blas(&new_blas);
1989
1990 let old_blas_size = blas.size_info.acceleration_structure_size;
1992 let new_blas_size = new_blas.size_info.acceleration_structure_size;
1993
1994 api_log!("CommandEncoder::compact_blas {:?} (size: {old_blas_size}) -> {:?} (size: {new_blas_size})", Arc::as_ptr(blas), Arc::as_ptr(&new_blas));
1995
1996 Ok(new_blas)
1997 }
1998}
1999
2000impl Global {
2001 pub fn queue_write_buffer(
2002 &self,
2003 queue_id: QueueId,
2004 buffer_id: id::BufferId,
2005 buffer_offset: wgt::BufferAddress,
2006 data: &[u8],
2007 ) -> Result<(), QueueWriteError> {
2008 let queue = self.hub.queues.get(queue_id);
2009 let buffer = self.hub.buffers.get(buffer_id);
2010
2011 queue.write_buffer(buffer, buffer_offset, data)
2012 }
2013
2014 pub fn queue_create_staging_buffer(
2015 &self,
2016 queue_id: QueueId,
2017 buffer_size: wgt::BufferSize,
2018 id_in: Option<id::StagingBufferId>,
2019 ) -> Result<(id::StagingBufferId, NonNull<u8>), QueueWriteError> {
2020 let queue = self.hub.queues.get(queue_id);
2021 let (staging_buffer, ptr) = queue.create_staging_buffer(buffer_size)?;
2022
2023 let fid = self.hub.staging_buffers.prepare(id_in);
2024 let id = fid.assign(staging_buffer);
2025
2026 Ok((id, ptr))
2027 }
2028
2029 pub fn queue_write_staging_buffer(
2030 &self,
2031 queue_id: QueueId,
2032 buffer_id: id::BufferId,
2033 buffer_offset: wgt::BufferAddress,
2034 staging_buffer_id: id::StagingBufferId,
2035 ) -> Result<(), QueueWriteError> {
2036 let queue = self.hub.queues.get(queue_id);
2037 let buffer = self.hub.buffers.get(buffer_id);
2038 let staging_buffer = self.hub.staging_buffers.remove(staging_buffer_id);
2039 queue.write_staging_buffer(buffer, buffer_offset, staging_buffer)
2040 }
2041
2042 pub fn queue_validate_write_buffer(
2043 &self,
2044 queue_id: QueueId,
2045 buffer_id: id::BufferId,
2046 buffer_offset: u64,
2047 buffer_size: wgt::BufferSize,
2048 ) -> Result<(), QueueWriteError> {
2049 let queue = self.hub.queues.get(queue_id);
2050 let buffer = self.hub.buffers.get(buffer_id);
2051 queue.validate_write_buffer(buffer, buffer_offset, buffer_size)
2052 }
2053
2054 pub fn queue_write_texture(
2055 &self,
2056 queue_id: QueueId,
2057 destination: &wgt::TexelCopyTextureInfo<id::TextureId>,
2058 data: &[u8],
2059 data_layout: &wgt::TexelCopyBufferLayout,
2060 size: &wgt::Extent3d,
2061 ) -> Result<(), QueueWriteError> {
2062 let queue = self.hub.queues.get(queue_id);
2063 let texture = self.hub.textures.get(destination.texture);
2064 let destination = wgt::TexelCopyTextureInfo {
2065 texture,
2066 mip_level: destination.mip_level,
2067 origin: destination.origin,
2068 aspect: destination.aspect,
2069 };
2070
2071 queue.write_texture(destination, data, data_layout, size)
2072 }
2073
2074 #[cfg(webgl)]
2075 pub fn queue_copy_external_image_to_texture(
2076 &self,
2077 queue_id: QueueId,
2078 source: &wgt::CopyExternalImageSourceInfo,
2079 destination: crate::command::CopyExternalImageDestInfo,
2080 size: wgt::Extent3d,
2081 ) -> Result<(), QueueWriteError> {
2082 let queue = self.hub.queues.get(queue_id);
2083 let destination = wgt::CopyExternalImageDestInfo {
2084 texture: self.hub.textures.get(destination.texture),
2085 mip_level: destination.mip_level,
2086 origin: destination.origin,
2087 aspect: destination.aspect,
2088 color_space: destination.color_space,
2089 premultiplied_alpha: destination.premultiplied_alpha,
2090 };
2091 queue.copy_external_image_to_texture(source, destination, size)
2092 }
2093
2094 pub fn queue_submit(
2095 &self,
2096 queue_id: QueueId,
2097 command_buffer_ids: &[id::CommandBufferId],
2098 ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {
2099 let queue = self.hub.queues.get(queue_id);
2100 let command_buffer_guard = self.hub.command_buffers.read();
2101 let command_buffers = command_buffer_ids
2102 .iter()
2103 .map(|id| command_buffer_guard.get(*id))
2104 .collect::<Vec<_>>();
2105 drop(command_buffer_guard);
2106 queue.submit(&command_buffers)
2107 }
2108
2109 pub fn queue_get_timestamp_period(&self, queue_id: QueueId) -> f32 {
2110 let queue = self.hub.queues.get(queue_id);
2111
2112 queue.get_timestamp_period()
2113 }
2114
2115 pub fn queue_on_submitted_work_done(
2116 &self,
2117 queue_id: QueueId,
2118 closure: SubmittedWorkDoneClosure,
2119 ) -> SubmissionIndex {
2120 api_log!("Queue::on_submitted_work_done {queue_id:?}");
2121
2122 let queue = self.hub.queues.get(queue_id);
2123 let result = queue.on_submitted_work_done(closure);
2124 result.unwrap_or(0) }
2126
2127 pub fn queue_compact_blas(
2128 &self,
2129 queue_id: QueueId,
2130 blas_id: BlasId,
2131 id_in: Option<BlasId>,
2132 ) -> (BlasId, Option<u64>, Option<CompactBlasError>) {
2133 let fid = self.hub.blas_s.prepare(id_in);
2134
2135 let queue = self.hub.queues.get(queue_id);
2136 let blas = self.hub.blas_s.get(blas_id);
2137
2138 let (blas, error) = queue.compact_blas(&blas);
2139
2140 let handle = blas.handle();
2141 let id = fid.assign(blas);
2142
2143 (id, handle, error)
2144 }
2145}
2146
2147fn validate_command_buffer(
2148 command_buffer: &CommandBuffer,
2149 queue: &Queue,
2150 cmd_buf_data: &crate::command::CommandBufferMutable,
2151 snatch_guard: &SnatchGuard,
2152 surface_textures: &mut FastHashMap<*const Texture, Arc<Texture>>,
2153 used_surface_textures: &mut track::TextureUsageScope,
2154 command_index_guard: &mut RwLockWriteGuard<CommandIndices>,
2155) -> Result<(), QueueSubmitError> {
2156 command_buffer.same_device_as(queue)?;
2157
2158 {
2159 profiling::scope!("check resource state");
2160
2161 {
2162 profiling::scope!("buffers");
2163 for buffer in cmd_buf_data.trackers.buffers.used_resources() {
2164 buffer.check_destroyed(snatch_guard)?;
2165
2166 match *buffer.map_state.lock() {
2167 BufferMapState::Idle => (),
2168 _ => return Err(QueueSubmitError::BufferStillMapped(buffer.error_ident())),
2169 }
2170 }
2171 }
2172 {
2173 profiling::scope!("textures");
2174 for texture in cmd_buf_data.trackers.textures.used_resources() {
2175 let should_extend = match texture.try_inner(snatch_guard)? {
2176 TextureInner::Native { .. } => false,
2177 TextureInner::Surface { .. } => {
2178 surface_textures.insert(Arc::as_ptr(texture), texture.clone());
2180
2181 true
2182 }
2183 };
2184 if should_extend {
2185 unsafe {
2186 used_surface_textures
2187 .merge_single(texture, None, wgt::TextureUses::PRESENT)
2188 .unwrap();
2189 };
2190 }
2191 }
2192 }
2193 {
2194 profiling::scope!("query sets");
2195 for query_set in cmd_buf_data.trackers.query_sets.used_resources() {
2196 query_set.try_raw(snatch_guard)?;
2197 }
2198 }
2199 {
2203 profiling::scope!("bind groups");
2204 for bind_group in &cmd_buf_data.trackers.bind_groups {
2205 bind_group.try_raw(snatch_guard)?;
2207 }
2208 }
2209
2210 if let Err(e) =
2211 cmd_buf_data.validate_acceleration_structure_actions(snatch_guard, command_index_guard)
2212 {
2213 return Err(e.into());
2214 }
2215 }
2216 Ok(())
2217}