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