1#[cfg(feature = "trace")]
2use alloc::string::ToString as _;
3use alloc::{boxed::Box, string::String, sync::Arc, vec, vec::Vec};
4use core::{
5 mem::{self, ManuallyDrop},
6 num::NonZeroU64,
7 ptr::NonNull,
8 sync::atomic::Ordering,
9};
10use smallvec::SmallVec;
11use thiserror::Error;
12use wgt::{
13 error::{ErrorType, WebGpuError},
14 AccelerationStructureFlags,
15};
16
17use super::{life::LifetimeTracker, Device};
18#[cfg(feature = "trace")]
19use crate::device::trace::{Action, IntoTrace};
20use crate::{
21 api_log,
22 command::{
23 extract_texture_selector, validate_linear_texture_data, validate_texture_buffer_copy,
24 validate_texture_copy_dst_format, validate_texture_copy_range, ClearError,
25 CommandAllocator, CommandBuffer, CommandEncoder, CommandEncoderError, CopySide,
26 TransferError,
27 },
28 device::{DeviceError, QueueDescriptor, WaitIdleError},
29 get_lowest_common_denom, hal_label,
30 init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange},
31 lock::{rank, Mutex, MutexGuard, RwLock, RwLockWriteGuard},
32 ray_tracing::{BlasCompactReadyPendingClosure, CompactBlasError},
33 resource::{
34 Blas, BlasCompactState, BlasDescriptor, BlasState, Buffer, BufferAccessError,
35 BufferMapState, DestroyedBuffer, DestroyedQuerySet, DestroyedResourceError,
36 DestroyedTexture, FlushedStagingBuffer, InvalidOrDestroyedResourceError,
37 InvalidResourceError, Labeled, ParentDevice, ResourceErrorIdent, ResourceState,
38 StagingBuffer, Texture, TextureInner, Trackable, TrackingData,
39 },
40 resource_log,
41 scratch::ScratchBuffer,
42 snatch::{SnatchGuard, Snatchable},
43 track::{self, Tracker, TrackerIndex},
44 FastHashMap, LabelHelpers, SubmissionIndex,
45};
46use crate::{device::resource::CommandIndices, resource::RawResourceAccess};
47
48pub struct Queue {
49 raw: Box<dyn hal::DynQueue>,
50 pub(crate) pending_writes: Mutex<PendingWrites>,
51 life_tracker: Mutex<LifetimeTracker>,
52 label: String,
53 pub(crate) device: Arc<Device>,
55}
56
57impl Queue {
58 pub(crate) fn new(
59 device: Arc<Device>,
60 raw: Box<dyn hal::DynQueue>,
61 desc: QueueDescriptor,
62 instance_flags: wgt::InstanceFlags,
63 ) -> Result<Self, DeviceError> {
64 let pending_encoder = device
65 .command_allocator
66 .acquire_encoder(device.raw(), raw.as_ref())
67 .map_err(DeviceError::from_hal);
68
69 let pending_encoder = match pending_encoder {
70 Ok(pending_encoder) => pending_encoder,
71 Err(e) => {
72 return Err(e);
73 }
74 };
75
76 let mut pending_writes = PendingWrites::new(pending_encoder, instance_flags);
77
78 let zero_buffer = device.zero_buffer.as_ref();
79 pending_writes.activate();
80 unsafe {
81 pending_writes
82 .command_encoder
83 .transition_buffers(&[hal::BufferBarrier {
84 buffer: zero_buffer,
85 usage: hal::StateTransition {
86 from: wgt::BufferUses::empty(),
87 to: wgt::BufferUses::COPY_DST,
88 },
89 }]);
90 pending_writes
91 .command_encoder
92 .clear_buffer(zero_buffer, 0..super::ZERO_BUFFER_SIZE);
93 pending_writes
94 .command_encoder
95 .transition_buffers(&[hal::BufferBarrier {
96 buffer: zero_buffer,
97 usage: hal::StateTransition {
98 from: wgt::BufferUses::COPY_DST,
99 to: wgt::BufferUses::COPY_SRC,
100 },
101 }]);
102 }
103
104 Ok(Queue {
105 raw,
106 device,
107 label: desc.label.to_string(),
108 pending_writes: Mutex::new(rank::QUEUE_PENDING_WRITES, pending_writes),
109 life_tracker: Mutex::new(rank::QUEUE_LIFE_TRACKER, LifetimeTracker::new()),
110 })
111 }
112
113 pub(crate) fn raw(&self) -> &dyn hal::DynQueue {
114 self.raw.as_ref()
115 }
116
117 #[track_caller]
118 pub(crate) fn lock_life<'a>(&'a self) -> MutexGuard<'a, LifetimeTracker> {
119 self.life_tracker.lock()
120 }
121
122 pub(crate) fn prepare_surface_texture_for_present(
127 &self,
128 texture: &Arc<Texture>,
129 ) -> Result<(), DeviceError> {
130 let snatch_guard = self.device.snatchable_lock.read();
131 let submission = self.allocate_submission(snatch_guard)?;
132 let device = &self.device;
133
134 let needs_clear = {
136 let status = texture.initialization_status.read();
137 status
138 .mips
139 .first()
140 .is_some_and(|mip| mip.check(0..1).is_some())
141 };
142
143 let mut pending_writes = self.pending_writes.lock();
144
145 if needs_clear {
146 let encoder = pending_writes.activate();
149 let mut trackers = device.trackers.lock();
150 crate::command::clear_texture(
151 texture,
152 TextureInitRange {
153 mip_range: 0..1,
154 layer_range: 0..1,
155 },
156 None,
157 encoder,
158 &mut trackers.textures,
159 &device.alignments,
160 device.zero_buffer.as_ref(),
161 &submission.snatch_guard,
162 device.instance_flags,
163 )
164 .map_err(|e| match e {
165 ClearError::Device(e) => e,
166 _ => DeviceError::Lost,
167 })?;
168 texture.initialization_status.write().mips[0].drain(0..1);
169 }
170
171 let pending = {
177 let mut trackers = device.trackers.lock();
178 let pending: Vec<track::PendingTransition<wgt::TextureUses>> = trackers
179 .textures
180 .set_single(
181 texture,
182 texture.full_range.clone(),
183 wgt::TextureUses::PRESENT,
184 )
185 .collect();
186 pending
187 };
188
189 if pending.is_empty() {
190 debug_assert!(!needs_clear);
196 return Ok(());
197 }
198
199 {
201 let raw_texture = texture
202 .raw(&submission.snatch_guard)
203 .ok_or(DeviceError::Lost)?;
204 let barriers: Vec<hal::TextureBarrier<'_, dyn hal::DynTexture>> = pending
205 .into_iter()
206 .map(|pt| pt.into_hal(raw_texture))
207 .collect();
208
209 let encoder = pending_writes.activate();
210 unsafe {
214 encoder.transition_textures(&barriers);
215 }
216 }
217
218 pending_writes.insert_texture(texture);
225
226 submission.submit(pending_writes)?;
227
228 Ok(())
229 }
230
231 pub(crate) fn maintain(
239 &self,
240 submission_index: u64,
241 snatch_guard: &SnatchGuard,
242 ) -> (
243 SmallVec<[SubmittedWorkDoneClosure; 1]>,
244 Vec<super::BufferMapPendingClosure>,
245 Vec<BlasCompactReadyPendingClosure>,
246 bool,
247 ) {
248 let mut life_tracker = self.lock_life();
249 let submission_closures = life_tracker.triage_submissions(submission_index);
250
251 let mapping_closures = life_tracker.handle_mapping(snatch_guard);
252 let blas_closures = life_tracker.handle_compact_read_back();
253
254 let queue_empty = life_tracker.queue_empty();
255
256 (
257 submission_closures,
258 mapping_closures,
259 blas_closures,
260 queue_empty,
261 )
262 }
263}
264
265crate::impl_resource_type!(Queue);
266crate::impl_labeled!(Queue);
267crate::impl_parent_device!(Queue);
268crate::impl_storage_item!(Queue);
269
270impl Drop for Queue {
271 #[allow(trivial_casts)]
272 fn drop(&mut self) {
273 profiling::scope!("Queue::drop");
274 api_log!("Queue::drop {:?}", self as *const _);
275 resource_log!("Drop {}", self.error_ident());
276
277 match unsafe { self.raw.wait_for_idle() } {
280 Ok(()) => {}
281 Err(hal::DeviceError::Lost) => {
282 self.device.handle_hal_error(hal::DeviceError::Lost);
283 }
284 Err(e) => {
285 panic!("Unexpected error while waiting for queue idle on drop: {e:?}");
286 }
287 }
288
289 let last_successful_submission_index = self
290 .device
291 .last_successful_submission_index
292 .load(Ordering::Acquire);
293
294 let snatch_guard = self.device.snatchable_lock.read();
295 let (submission_closures, mapping_closures, blas_compact_ready_closures, queue_empty) =
296 self.maintain(last_successful_submission_index, &snatch_guard);
297 drop(snatch_guard);
298
299 assert!(queue_empty);
300
301 let closures = crate::device::UserClosures {
302 mappings: mapping_closures,
303 blas_compact_ready: blas_compact_ready_closures,
304 submissions: submission_closures,
305 device_lost_invocations: SmallVec::new(),
306 };
307
308 closures.fire();
309 }
310}
311
312#[cfg(send_sync)]
313pub type SubmittedWorkDoneClosure = Box<dyn FnOnce() + Send + 'static>;
314#[cfg(not(send_sync))]
315pub type SubmittedWorkDoneClosure = Box<dyn FnOnce() + 'static>;
316
317#[derive(Debug)]
328pub enum TempResource {
329 StagingBuffer(FlushedStagingBuffer),
330 ScratchBuffer(ScratchBuffer),
331 DestroyedBuffer(DestroyedBuffer),
332 DestroyedTexture(DestroyedTexture),
333 DestroyedQuerySet(DestroyedQuerySet),
334}
335
336pub(crate) struct EncoderInFlight {
342 inner: crate::command::InnerCommandEncoder,
343 pub(crate) trackers: Tracker,
344 pub(crate) temp_resources: Vec<TempResource>,
345 _indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
347
348 pub(crate) pending_buffers: FastHashMap<TrackerIndex, Arc<Buffer>>,
350 pub(crate) pending_textures: FastHashMap<TrackerIndex, Arc<Texture>>,
352 pub(crate) pending_blas_s: FastHashMap<TrackerIndex, Arc<Blas>>,
354}
355
356#[derive(Debug)]
381pub(crate) struct PendingWrites {
382 pub command_encoder: Box<dyn hal::DynCommandEncoder>,
384
385 pub is_recording: bool,
391
392 temp_resources: Vec<TempResource>,
393 dst_buffers: FastHashMap<TrackerIndex, Arc<Buffer>>,
394 dst_textures: FastHashMap<TrackerIndex, Arc<Texture>>,
395 copied_blas_s: FastHashMap<TrackerIndex, Arc<Blas>>,
396 instance_flags: wgt::InstanceFlags,
397
398 hoisted_staging_buffers: Vec<FlushedStagingBuffer>,
407
408 hoisted_buffer_barriers: Vec<(Arc<Buffer>, hal::StateTransition<wgt::BufferUses>)>,
430}
431
432impl PendingWrites {
433 pub fn new(
434 command_encoder: Box<dyn hal::DynCommandEncoder>,
435 instance_flags: wgt::InstanceFlags,
436 ) -> Self {
437 Self {
438 command_encoder,
439 is_recording: false,
440 temp_resources: Vec::new(),
441 dst_buffers: FastHashMap::default(),
442 dst_textures: FastHashMap::default(),
443 copied_blas_s: FastHashMap::default(),
444 instance_flags,
445 hoisted_staging_buffers: Vec::new(),
446 hoisted_buffer_barriers: Vec::new(),
447 }
448 }
449
450 pub fn insert_buffer(&mut self, buffer: &Arc<Buffer>) {
451 self.dst_buffers
452 .insert(buffer.tracker_index(), buffer.clone());
453 }
454
455 pub fn insert_texture(&mut self, texture: &Arc<Texture>) {
456 self.dst_textures
457 .insert(texture.tracker_index(), texture.clone());
458 }
459
460 pub fn insert_blas(&mut self, blas: &Arc<Blas>) {
461 self.copied_blas_s
462 .insert(blas.tracker_index(), blas.clone());
463 }
464
465 pub fn contains_buffer(&self, buffer: &Arc<Buffer>) -> bool {
466 self.dst_buffers.contains_key(&buffer.tracker_index())
467 }
468
469 pub fn contains_texture(&self, texture: &Arc<Texture>) -> bool {
470 self.dst_textures.contains_key(&texture.tracker_index())
471 }
472
473 pub fn consume_temp(&mut self, resource: TempResource) {
474 self.temp_resources.push(resource);
475 }
476
477 pub fn consume(&mut self, buffer: FlushedStagingBuffer) {
483 self.hoisted_staging_buffers.push(buffer);
484 }
485
486 #[must_use]
500 fn hoist_buffer_barrier(
501 &mut self,
502 buffer: &Arc<Buffer>,
503 transition: Option<track::PendingTransition<wgt::BufferUses>>,
504 ) -> Option<track::PendingTransition<wgt::BufferUses>> {
505 let transition = transition?;
506
507 if self.dst_buffers.contains_key(&buffer.tracker_index()) {
510 return Some(transition);
511 }
512
513 self.hoisted_buffer_barriers
514 .push((buffer.clone(), transition.usage));
515 None
516 }
517
518 pub fn clear_buffer(
519 &mut self,
520 device: &Arc<Device>,
521 buffer: &Arc<Buffer>,
522 range: core::ops::Range<wgt::BufferAddress>,
523 snatch_guard: &SnatchGuard,
524 ) -> Result<(), QueueWriteError> {
525 let transition = {
526 let mut trackers = device.trackers.lock();
527 trackers
528 .buffers
529 .set_single(buffer, wgt::BufferUses::COPY_DST)
530 };
531 let inline_transition = self.hoist_buffer_barrier(buffer, transition);
532
533 let dst_raw = buffer.try_raw(snatch_guard)?;
534
535 let encoder = self.activate();
536 unsafe {
537 if let Some(transition) = inline_transition {
538 encoder.transition_buffers(&[transition.into_hal(buffer, snatch_guard)]);
539 }
540 encoder.clear_buffer(dst_raw, range.clone());
541 }
542
543 self.insert_buffer(buffer);
544
545 buffer.initialization_status.write().drain(range);
548
549 Ok(())
550 }
551
552 fn record_hoisted_barriers(
559 &mut self,
560 encoder: &mut EncoderInFlight,
561 snatch_guard: &SnatchGuard,
562 ) -> Result<Vec<FlushedStagingBuffer>, DeviceError> {
563 let staging_buffers = mem::take(&mut self.hoisted_staging_buffers);
564 let buffer_barriers = mem::take(&mut self.hoisted_buffer_barriers);
565
566 let barriers = staging_buffers
567 .iter()
568 .map(|staging_buffer| hal::BufferBarrier {
569 buffer: staging_buffer.raw(),
570 usage: hal::StateTransition {
571 from: wgt::BufferUses::MAP_WRITE,
572 to: wgt::BufferUses::COPY_SRC,
573 },
574 })
575 .chain(buffer_barriers.iter().filter_map(|(buffer, usage)| {
576 Some(hal::BufferBarrier {
579 buffer: buffer.raw(snatch_guard)?,
580 usage: usage.clone(),
581 })
582 }))
583 .collect::<Vec<_>>();
584
585 if !barriers.is_empty() {
586 encoder
587 .inner
588 .open_pass(Some("(wgpu internal) PendingWrites Transit"))?;
589 unsafe { encoder.inner.raw.transition_buffers(&barriers) };
590 encoder.inner.close_and_push_front()?;
591 }
592
593 Ok(staging_buffers)
594 }
595
596 fn pre_submit(
597 &mut self,
598 command_allocator: &CommandAllocator,
599 device: &Arc<Device>,
600 queue: &Queue,
601 snatch_guard: &SnatchGuard,
602 ) -> Result<Option<EncoderInFlight>, DeviceError> {
603 if self.is_recording {
604 let pending_buffers = mem::take(&mut self.dst_buffers);
605 let pending_textures = mem::take(&mut self.dst_textures);
606 let pending_blas_s = mem::take(&mut self.copied_blas_s);
607
608 let cmd_buf = unsafe { self.command_encoder.end_encoding() }
609 .map_err(|e| device.handle_hal_error(e))?;
610 self.is_recording = false;
611
612 let new_encoder = command_allocator
613 .acquire_encoder(device.raw(), queue.raw())
614 .map_err(|e| device.handle_hal_error(e))?;
615
616 let mut encoder = EncoderInFlight {
617 inner: crate::command::InnerCommandEncoder {
618 raw: ManuallyDrop::new(mem::replace(&mut self.command_encoder, new_encoder)),
619 list: vec![cmd_buf],
620 device: device.clone(),
621 is_open: false,
622 api: crate::command::EncodingApi::InternalUse,
623 label: "(wgpu internal) PendingWrites command encoder".into(),
624 },
625 trackers: Tracker::new(device.ordered_buffer_usages, device.ordered_texture_usages),
626 temp_resources: mem::take(&mut self.temp_resources),
627 _indirect_draw_validation_resources: crate::indirect_validation::DrawResources::new(
628 device.clone(),
629 ),
630 pending_buffers,
631 pending_textures,
632 pending_blas_s,
633 };
634
635 let staging_buffers = self.record_hoisted_barriers(&mut encoder, snatch_guard)?;
636 encoder
637 .temp_resources
638 .extend(staging_buffers.into_iter().map(TempResource::StagingBuffer));
639
640 Ok(Some(encoder))
641 } else {
642 self.dst_buffers.clear();
643 self.dst_textures.clear();
644 self.copied_blas_s.clear();
645 Ok(None)
646 }
647 }
648
649 pub fn activate(&mut self) -> &mut dyn hal::DynCommandEncoder {
650 if !self.is_recording {
651 unsafe {
652 self.command_encoder
653 .begin_encoding(hal_label(
654 Some("(wgpu internal) PendingWrites"),
655 self.instance_flags,
656 ))
657 .unwrap();
658 }
659 self.is_recording = true;
660 }
661 self.command_encoder.as_mut()
662 }
663}
664
665impl Drop for PendingWrites {
666 fn drop(&mut self) {
667 unsafe {
668 if self.is_recording {
669 self.command_encoder.discard_encoding();
670 }
671 }
672 }
673}
674
675#[derive(Clone, Debug, Error)]
676#[non_exhaustive]
677pub enum QueueWriteError {
678 #[error(transparent)]
679 Queue(#[from] DeviceError),
680 #[error(transparent)]
681 Transfer(#[from] TransferError),
682 #[error(transparent)]
683 MemoryInitFailure(#[from] ClearError),
684 #[error(transparent)]
685 DestroyedResource(#[from] DestroyedResourceError),
686 #[error(transparent)]
687 InvalidResource(#[from] InvalidResourceError),
688}
689
690impl From<InvalidOrDestroyedResourceError> for QueueWriteError {
691 fn from(e: InvalidOrDestroyedResourceError) -> Self {
692 match e {
693 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
694 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
695 }
696 }
697}
698
699impl WebGpuError for QueueWriteError {
700 fn webgpu_error_type(&self) -> ErrorType {
701 match self {
702 Self::Queue(e) => e.webgpu_error_type(),
703 Self::Transfer(e) => e.webgpu_error_type(),
704 Self::MemoryInitFailure(e) => e.webgpu_error_type(),
705 Self::DestroyedResource(e) => e.webgpu_error_type(),
706 Self::InvalidResource(e) => e.webgpu_error_type(),
707 }
708 }
709}
710
711#[derive(Clone, Debug, Error)]
712#[non_exhaustive]
713pub enum QueueSubmitError {
714 #[error(transparent)]
715 Queue(#[from] DeviceError),
716 #[error(transparent)]
717 DestroyedResource(#[from] DestroyedResourceError),
718 #[error("{0} is still mapped")]
719 BufferStillMapped(ResourceErrorIdent),
720 #[error(transparent)]
721 InvalidResource(#[from] InvalidResourceError),
722 #[error(transparent)]
723 CommandEncoder(#[from] CommandEncoderError),
724 #[error(transparent)]
725 ValidateAsActionsError(#[from] crate::ray_tracing::ValidateAsActionsError),
726}
727
728impl From<InvalidOrDestroyedResourceError> for QueueSubmitError {
729 fn from(e: InvalidOrDestroyedResourceError) -> Self {
730 match e {
731 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
732 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
733 }
734 }
735}
736
737impl WebGpuError for QueueSubmitError {
738 fn webgpu_error_type(&self) -> ErrorType {
739 match self {
740 Self::Queue(e) => e.webgpu_error_type(),
741 Self::CommandEncoder(e) => e.webgpu_error_type(),
742 Self::ValidateAsActionsError(e) => e.webgpu_error_type(),
743 Self::InvalidResource(e) => e.webgpu_error_type(),
744 Self::DestroyedResource(_) | Self::BufferStillMapped(_) => ErrorType::Validation,
745 }
746 }
747}
748
749pub(crate) struct PendingSubmission<'a> {
797 queue: &'a Queue,
798
799 command_index_guard: RwLockWriteGuard<'a, CommandIndices>,
809
810 snatch_guard: SnatchGuard<'a>,
812
813 pub executions: Vec<EncoderInFlight>,
816
817 surface_textures: FastHashMap<*const Texture, Arc<Texture>>,
823
824 pub index: SubmissionIndex,
826}
827
828pub(crate) struct SubmissionResult<'a> {
829 pub snatch_guard: SnatchGuard<'a>,
830}
831
832impl<'a> PendingSubmission<'a> {
833 fn submit(
834 self,
835 pending_writes: MutexGuard<'a, PendingWrites>,
836 ) -> Result<SubmissionResult<'a>, DeviceError> {
837 self.queue.submit_pending_submission(pending_writes, self)
838 }
839}
840
841impl Queue {
844 pub(crate) fn write_buffer_inner(
845 &self,
846 buffer: Arc<Buffer>,
847 buffer_offset: wgt::BufferAddress,
848 data: &[u8],
849 ) -> Result<(), QueueWriteError> {
850 profiling::scope!("Queue::write_buffer");
851 api_log!("Queue::write_buffer");
852
853 #[cfg(feature = "trace")]
854 if let Some(ref mut trace) = *self.device.trace.lock() {
855 use crate::device::trace::DataKind;
856 let size = data.len() as u64;
857 let data = trace.make_binary(DataKind::Bin, data);
858 trace.add(Action::WriteBuffer {
859 id: buffer.to_trace(),
860 data,
861 offset: buffer_offset,
862 size,
863 queued: true,
864 });
865 }
866
867 buffer.check_is_valid()?;
868 self.device.check_is_valid()?;
869
870 let data_size = data.len() as wgt::BufferAddress;
871
872 self.same_device_as(buffer.as_ref())?;
873
874 let data_size = if let Some(data_size) = wgt::BufferSize::new(data_size) {
875 data_size
876 } else {
877 self.validate_write_buffer_impl(buffer.as_ref(), buffer_offset, 0)?;
882
883 log::trace!("Ignoring write_buffer of size 0");
884 return Ok(());
885 };
886
887 let mut staging_buffer = StagingBuffer::new(&self.device, data_size)?;
891
892 let staging_buffer = {
893 profiling::scope!("copy");
894 staging_buffer.write_exact(data);
895 staging_buffer.flush()
896 };
897
898 let snatch_guard = self.device.snatchable_lock.read();
899 let mut pending_writes = self.pending_writes.lock();
900
901 let result = self.write_staging_buffer_impl(
902 &snatch_guard,
903 &mut pending_writes,
904 &staging_buffer,
905 buffer,
906 buffer_offset,
907 );
908
909 pending_writes.consume(staging_buffer);
910
911 result
912 }
913
914 pub fn write_buffer(
915 &self,
916 buffer: Arc<Buffer>,
917 buffer_offset: wgt::BufferAddress,
918 data: &[u8],
919 ) {
920 if let Err(error) = self.write_buffer_inner(buffer, buffer_offset, data) {
921 self.device
922 .handle_error(error, Some(self.label()), "Queue::write_buffer");
923 }
924 }
925
926 pub fn create_staging_buffer(
927 &self,
928 buffer_size: wgt::BufferSize,
929 ) -> Result<(StagingBuffer, NonNull<u8>), QueueWriteError> {
930 profiling::scope!("Queue::create_staging_buffer");
931 resource_log!("Queue::create_staging_buffer");
932
933 self.device.check_is_valid()?;
934
935 let staging_buffer = StagingBuffer::new(&self.device, buffer_size)?;
936 let ptr = unsafe { staging_buffer.ptr() };
937
938 Ok((staging_buffer, ptr))
939 }
940
941 pub fn write_staging_buffer(
942 &self,
943 buffer: Arc<Buffer>,
944 buffer_offset: wgt::BufferAddress,
945 staging_buffer: StagingBuffer,
946 ) -> Result<(), QueueWriteError> {
947 profiling::scope!("Queue::write_staging_buffer");
948
949 buffer.check_is_valid()?;
950 self.device.check_is_valid()?;
951
952 let staging_buffer = staging_buffer.flush();
957
958 let snatch_guard = self.device.snatchable_lock.read();
959 let mut pending_writes = self.pending_writes.lock();
960
961 let result = self.write_staging_buffer_impl(
962 &snatch_guard,
963 &mut pending_writes,
964 &staging_buffer,
965 buffer,
966 buffer_offset,
967 );
968
969 drop(snatch_guard);
970
971 pending_writes.consume(staging_buffer);
972
973 drop(pending_writes);
974
975 result
976 }
977
978 pub fn validate_write_buffer(
979 &self,
980 buffer: Arc<Buffer>,
981 buffer_offset: u64,
982 buffer_size: wgt::BufferSize,
983 ) -> Result<(), QueueWriteError> {
984 profiling::scope!("Queue::validate_write_buffer");
985
986 self.device.check_is_valid()?;
987 buffer.check_is_valid()?;
988
989 self.validate_write_buffer_impl(&buffer, buffer_offset, buffer_size.into())?;
990
991 Ok(())
992 }
993
994 fn validate_write_buffer_impl(
995 &self,
996 buffer: &Buffer,
997 buffer_offset: u64,
998 buffer_size: u64,
999 ) -> Result<(), TransferError> {
1000 if !matches!(&*buffer.map_state.read(), BufferMapState::Idle) {
1001 return Err(TransferError::BufferNotAvailable);
1002 }
1003 buffer.check_usage(wgt::BufferUsages::COPY_DST)?;
1004 if !buffer_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
1005 return Err(TransferError::UnalignedCopySize(buffer_size));
1006 }
1007 if !buffer_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
1008 return Err(TransferError::UnalignedBufferOffset(buffer_offset));
1009 }
1010
1011 if buffer_offset > buffer.size {
1012 return Err(TransferError::BufferStartOffsetOverrun {
1013 start_offset: buffer_offset,
1014 buffer_size: buffer.size,
1015 side: CopySide::Destination,
1016 });
1017 }
1018 if buffer_size > buffer.size - buffer_offset {
1019 return Err(TransferError::BufferEndOffsetOverrun {
1020 start_offset: buffer_offset,
1021 size: buffer_size,
1022 buffer_size: buffer.size,
1023 side: CopySide::Destination,
1024 });
1025 }
1026
1027 Ok(())
1028 }
1029
1030 fn write_staging_buffer_impl(
1031 &self,
1032 snatch_guard: &SnatchGuard,
1033 pending_writes: &mut PendingWrites,
1034 staging_buffer: &FlushedStagingBuffer,
1035 buffer: Arc<Buffer>,
1036 buffer_offset: u64,
1037 ) -> Result<(), QueueWriteError> {
1038 self.device.check_is_valid()?;
1039
1040 let transition = {
1041 let mut trackers = self.device.trackers.lock();
1042 trackers
1043 .buffers
1044 .set_single(&buffer, wgt::BufferUses::COPY_DST)
1045 };
1046
1047 let dst_raw = buffer.try_raw(snatch_guard)?;
1048
1049 self.same_device_as(buffer.as_ref())?;
1050
1051 self.validate_write_buffer_impl(&buffer, buffer_offset, staging_buffer.size.into())?;
1052
1053 let region = hal::BufferCopy {
1054 src_offset: 0,
1055 dst_offset: buffer_offset,
1056 size: staging_buffer.size,
1057 };
1058 let inline_transition = pending_writes.hoist_buffer_barrier(&buffer, transition);
1061 let encoder = pending_writes.activate();
1062 unsafe {
1063 if let Some(transition) = inline_transition {
1064 encoder.transition_buffers(&[transition.into_hal(&buffer, snatch_guard)]);
1065 }
1066 encoder.copy_buffer_to_buffer(staging_buffer.raw(), dst_raw, &[region]);
1067 }
1068
1069 pending_writes.insert_buffer(&buffer);
1070
1071 {
1074 buffer
1075 .initialization_status
1076 .write()
1077 .drain(buffer_offset..(buffer_offset + staging_buffer.size.get()));
1078 }
1079
1080 Ok(())
1081 }
1082
1083 pub fn write_texture_inner(
1084 &self,
1085 destination: wgt::TexelCopyTextureInfo<Arc<Texture>>,
1086 data: &[u8],
1087 data_layout: &wgt::TexelCopyBufferLayout,
1088 size: &wgt::Extent3d,
1089 ) -> Result<(), QueueWriteError> {
1090 profiling::scope!("Queue::write_texture");
1091 api_log!("Queue::write_texture");
1092
1093 #[cfg(feature = "trace")]
1094 if let Some(ref mut trace) = *self.device.trace.lock() {
1095 use crate::device::trace::DataKind;
1096 let data = trace.make_binary(DataKind::Bin, data);
1097 trace.add(Action::WriteTexture {
1098 to: destination.to_trace(),
1099 data,
1100 layout: *data_layout,
1101 size: *size,
1102 });
1103 }
1104
1105 self.device.check_is_valid()?;
1106
1107 let dst = destination.texture;
1108 let destination = wgt::TexelCopyTextureInfo {
1109 texture: (),
1110 mip_level: destination.mip_level,
1111 origin: destination.origin,
1112 aspect: destination.aspect,
1113 };
1114
1115 self.same_device_as(dst.as_ref())?;
1116
1117 dst.check_valid()?;
1118
1119 dst.check_usage(wgt::TextureUsages::COPY_DST)
1120 .map_err(TransferError::MissingTextureUsage)?;
1121
1122 let (hal_copy_size, array_layer_count) =
1125 validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, size)?;
1126
1127 let (selector, dst_base) = extract_texture_selector(&destination, size, &dst)?;
1128
1129 validate_texture_copy_dst_format(dst.desc.format, destination.aspect)?;
1130
1131 validate_texture_buffer_copy(
1132 &destination,
1133 dst_base.aspect,
1134 &dst.desc,
1135 data_layout,
1136 false, self.device.features,
1138 )?;
1139
1140 let (required_bytes_in_copy, _source_bytes_per_array_layer, _) =
1143 validate_linear_texture_data(
1144 data_layout,
1145 dst.desc.format,
1146 destination.aspect,
1147 data.len() as wgt::BufferAddress,
1148 CopySide::Source,
1149 size,
1150 )?;
1151
1152 if dst.desc.format.is_depth_stencil_format() {
1153 self.device
1154 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
1155 .map_err(TransferError::from)?;
1156 }
1157
1158 let snatch_guard = self.device.snatchable_lock.read();
1159
1160 let dst_raw = dst.try_inner(&snatch_guard)?.raw();
1161
1162 if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
1165 log::trace!("Ignoring write_texture of size 0");
1166 return Ok(());
1167 }
1168
1169 let mut pending_writes = self.pending_writes.lock();
1170 let encoder = pending_writes.activate();
1171
1172 let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
1178 0..1
1180 } else {
1181 destination.origin.z..destination.origin.z + size.depth_or_array_layers
1182 };
1183 let layer_ranges_to_clear = {
1184 let mut dst_initialization_status = dst.initialization_status.write();
1185 if dst_initialization_status.mips[destination.mip_level as usize]
1186 .check(init_layer_range.clone())
1187 .is_some()
1188 {
1189 if has_copy_partial_init_tracker_coverage(size, &destination, &dst.desc) {
1190 dst_initialization_status.mips[destination.mip_level as usize]
1191 .drain(init_layer_range)
1192 .collect::<Vec<core::ops::Range<u32>>>()
1193 } else {
1194 dst_initialization_status.mips[destination.mip_level as usize]
1195 .drain(init_layer_range);
1196 vec![]
1197 }
1198 } else {
1199 vec![]
1200 }
1201 };
1202 if !layer_ranges_to_clear.is_empty() {
1203 let mut trackers = self.device.trackers.lock();
1204 for layer_range in layer_ranges_to_clear {
1205 crate::command::clear_texture(
1206 &dst,
1207 TextureInitRange {
1208 mip_range: destination.mip_level..(destination.mip_level + 1),
1209 layer_range,
1210 },
1211 None,
1212 encoder,
1213 &mut trackers.textures,
1214 &self.device.alignments,
1215 self.device.zero_buffer.as_ref(),
1216 &snatch_guard,
1217 self.device.instance_flags,
1218 )
1219 .map_err(QueueWriteError::from)?;
1220 }
1221 }
1222
1223 let (block_width, block_height) = dst.desc.format.block_dimensions();
1224 let width_in_blocks = size.width / block_width;
1225 let height_in_blocks = size.height / block_height;
1226
1227 let block_size = dst
1228 .desc
1229 .format
1230 .block_copy_size(Some(destination.aspect))
1231 .unwrap();
1232 let bytes_in_last_row = width_in_blocks * block_size;
1233
1234 let bytes_per_row = data_layout.bytes_per_row.unwrap_or(bytes_in_last_row);
1235 let rows_per_image = data_layout.rows_per_image.unwrap_or(height_in_blocks);
1236
1237 let bytes_per_row_alignment = get_lowest_common_denom(
1238 self.device.alignments.buffer_copy_pitch.get() as u32,
1239 block_size,
1240 );
1241 assert!(u32::MAX - bytes_in_last_row >= bytes_per_row_alignment);
1242 let stage_bytes_per_row = wgt::math::align_to(bytes_in_last_row, bytes_per_row_alignment);
1243
1244 let staging_buffer = if stage_bytes_per_row == bytes_per_row {
1248 profiling::scope!("copy aligned");
1249 let stage_size = wgt::BufferSize::new(required_bytes_in_copy).unwrap();
1251 let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
1252 staging_buffer
1253 .write_exact(&data[data_layout.offset as usize..][..stage_size.get() as usize]);
1254 staging_buffer
1255 } else {
1256 profiling::scope!("copy chunked");
1257 let block_rows_in_copy = u64::from(size.depth_or_array_layers - 1)
1259 * u64::from(rows_per_image)
1260 + u64::from(height_in_blocks);
1261 let stage_size = u64::from(stage_bytes_per_row)
1264 .checked_mul(block_rows_in_copy)
1265 .and_then(wgt::BufferSize::new)
1266 .unwrap();
1267 let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
1268 for layer in 0..u64::from(size.depth_or_array_layers) {
1269 let rows_offset = layer * u64::from(rows_per_image);
1270 for row in rows_offset..rows_offset + u64::from(height_in_blocks) {
1271 let src_offset = data_layout.offset + row * u64::from(bytes_per_row);
1272 let dst_offset = row * u64::from(stage_bytes_per_row);
1273 unsafe {
1274 staging_buffer.write_with_offset(
1275 data,
1276 src_offset as isize,
1277 dst_offset as isize,
1278 bytes_in_last_row as usize,
1279 )
1280 }
1281 }
1282 }
1283 staging_buffer
1284 };
1285
1286 let staging_buffer = staging_buffer.flush();
1287
1288 let regions = (0..array_layer_count)
1289 .map(|array_layer_offset| {
1290 let mut texture_base = dst_base.clone();
1291 texture_base.array_layer += array_layer_offset;
1292 hal::BufferTextureCopy {
1293 buffer_layout: wgt::TexelCopyBufferLayout {
1294 offset: array_layer_offset as u64
1295 * rows_per_image as u64
1296 * stage_bytes_per_row as u64,
1297 bytes_per_row: Some(stage_bytes_per_row),
1298 rows_per_image: Some(rows_per_image),
1299 },
1300 texture_base,
1301 size: hal_copy_size,
1302 }
1303 })
1304 .collect::<Vec<_>>();
1305
1306 {
1307 let mut trackers = self.device.trackers.lock();
1308 let transition =
1309 trackers
1310 .textures
1311 .set_single(&dst, selector, wgt::TextureUses::COPY_DST);
1312 let texture_barriers = transition
1313 .map(|pending| pending.into_hal(dst_raw))
1314 .collect::<Vec<_>>();
1315
1316 unsafe {
1320 encoder.transition_textures(&texture_barriers);
1321 encoder.copy_buffer_to_texture(staging_buffer.raw(), dst_raw, ®ions);
1322 }
1323 }
1324
1325 pending_writes.consume(staging_buffer);
1326 pending_writes.insert_texture(&dst);
1327
1328 Ok(())
1329 }
1330
1331 pub fn write_texture(
1332 &self,
1333 destination: wgt::TexelCopyTextureInfo<Arc<Texture>>,
1334 data: &[u8],
1335 data_layout: &wgt::TexelCopyBufferLayout,
1336 size: &wgt::Extent3d,
1337 ) {
1338 if let Err(error) = self.write_texture_inner(destination, data, data_layout, size) {
1339 self.device
1340 .handle_error(error, Some(self.label()), "Queue::write_texture");
1341 }
1342 }
1343
1344 #[cfg(webgl)]
1345 pub fn copy_external_image_to_texture(
1346 &self,
1347 source: &wgt::CopyExternalImageSourceInfo,
1348 destination: wgt::CopyExternalImageDestInfo<Arc<Texture>>,
1349 size: wgt::Extent3d,
1350 ) -> Result<(), QueueWriteError> {
1351 use crate::conv;
1352
1353 profiling::scope!("Queue::copy_external_image_to_texture");
1354
1355 self.device.check_is_valid()?;
1356
1357 let mut needs_flag = false;
1358 needs_flag |= source.origin != wgt::Origin2d::ZERO;
1362 needs_flag |= destination.color_space != wgt::PredefinedColorSpace::Srgb;
1363 #[allow(clippy::bool_comparison)]
1364 if matches!(source.source, wgt::ExternalImageSource::ImageBitmap(_)) {
1365 needs_flag |= source.flip_y != false;
1366 needs_flag |= destination.premultiplied_alpha != false;
1367 }
1368
1369 if needs_flag {
1370 self.device
1371 .require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES)
1372 .map_err(TransferError::from)?;
1373 }
1374
1375 let src_width = source.source.width();
1376 let src_height = source.source.height();
1377
1378 let dst = destination.texture;
1379 let premultiplied_alpha = destination.premultiplied_alpha;
1380 let destination = wgt::TexelCopyTextureInfo {
1381 texture: (),
1382 mip_level: destination.mip_level,
1383 origin: destination.origin,
1384 aspect: destination.aspect,
1385 };
1386
1387 dst.check_valid()?;
1388
1389 if !conv::is_valid_external_image_copy_dst_texture_format(dst.desc.format) {
1390 return Err(
1391 TransferError::ExternalCopyToForbiddenTextureFormat(dst.desc.format).into(),
1392 );
1393 }
1394 if dst.desc.dimension != wgt::TextureDimension::D2 {
1395 return Err(TransferError::InvalidDimensionExternal.into());
1396 }
1397 dst.check_usage(wgt::TextureUsages::COPY_DST | wgt::TextureUsages::RENDER_ATTACHMENT)
1398 .map_err(TransferError::MissingTextureUsage)?;
1399 if dst.desc.sample_count != 1 {
1400 return Err(TransferError::InvalidSampleCount {
1401 sample_count: dst.desc.sample_count,
1402 }
1403 .into());
1404 }
1405
1406 if source.origin.x > src_width || src_width - source.origin.x < size.width {
1407 return Err(TransferError::TextureOverrun {
1408 start_offset: source.origin.x,
1409 end_offset: source.origin.x.saturating_add(size.width),
1410 texture_size: src_width,
1411 dimension: crate::resource::TextureErrorDimension::X,
1412 side: CopySide::Source,
1413 }
1414 .into());
1415 }
1416 if source.origin.y > src_height || src_height - source.origin.y < size.height {
1417 return Err(TransferError::TextureOverrun {
1418 start_offset: source.origin.y,
1419 end_offset: source.origin.y.saturating_add(size.height),
1420 texture_size: src_height,
1421 dimension: crate::resource::TextureErrorDimension::Y,
1422 side: CopySide::Source,
1423 }
1424 .into());
1425 }
1426 if size.depth_or_array_layers != 1 {
1427 return Err(TransferError::TextureOverrun {
1428 start_offset: 0,
1429 end_offset: size.depth_or_array_layers,
1430 texture_size: 1,
1431 dimension: crate::resource::TextureErrorDimension::Z,
1432 side: CopySide::Source,
1433 }
1434 .into());
1435 }
1436
1437 let (hal_copy_size, _) =
1440 validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, &size)?;
1441
1442 let (selector, dst_base) = extract_texture_selector(&destination, &size, &dst)?;
1443
1444 let snatch_guard = self.device.snatchable_lock.read();
1445
1446 let dst_raw = dst.try_raw(&snatch_guard)?;
1447
1448 if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
1451 log::trace!("Ignoring copy_external_image_to_texture of size 0");
1452 return Ok(());
1453 }
1454
1455 let mut pending_writes = self.pending_writes.lock();
1456 let encoder = pending_writes.activate();
1457
1458 let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
1464 0..1
1466 } else {
1467 destination.origin.z..destination.origin.z + size.depth_or_array_layers
1468 };
1469 let layer_ranges_to_clear = {
1470 let mut dst_initialization_status = dst.initialization_status.write();
1471 if dst_initialization_status.mips[destination.mip_level as usize]
1472 .check(init_layer_range.clone())
1473 .is_some()
1474 {
1475 if has_copy_partial_init_tracker_coverage(&size, &destination, &dst.desc) {
1476 dst_initialization_status.mips[destination.mip_level as usize]
1477 .drain(init_layer_range)
1478 .collect::<Vec<core::ops::Range<u32>>>()
1479 } else {
1480 dst_initialization_status.mips[destination.mip_level as usize]
1481 .drain(init_layer_range);
1482 vec![]
1483 }
1484 } else {
1485 vec![]
1486 }
1487 };
1488 if !layer_ranges_to_clear.is_empty() {
1489 let mut trackers = self.device.trackers.lock();
1490 for layer_range in layer_ranges_to_clear {
1491 crate::command::clear_texture(
1492 &dst,
1493 TextureInitRange {
1494 mip_range: destination.mip_level..(destination.mip_level + 1),
1495 layer_range,
1496 },
1497 None,
1498 encoder,
1499 &mut trackers.textures,
1500 &self.device.alignments,
1501 self.device.zero_buffer.as_ref(),
1502 &snatch_guard,
1503 self.device.instance_flags,
1504 )
1505 .map_err(QueueWriteError::from)?;
1506 }
1507 }
1508
1509 let regions = hal::TextureCopy {
1510 src_base: hal::TextureCopyBase {
1511 mip_level: 0,
1512 array_layer: 0,
1513 origin: source.origin.to_3d(0),
1514 aspect: hal::FormatAspects::COLOR,
1515 },
1516 dst_base,
1517 size: hal_copy_size,
1518 };
1519
1520 let mut trackers = self.device.trackers.lock();
1521 let transitions = trackers
1522 .textures
1523 .set_single(&dst, selector, wgt::TextureUses::COPY_DST);
1524
1525 let encoder_webgl = encoder
1528 .as_any_mut()
1529 .downcast_mut::<hal::gles::CommandEncoder>()
1530 .unwrap();
1531 let dst_raw_webgl = dst_raw
1532 .as_any()
1533 .downcast_ref::<hal::gles::Texture>()
1534 .unwrap();
1535 let transitions_webgl = transitions.map(|pending| {
1536 let dyn_transition = pending.into_hal(dst_raw);
1537 hal::TextureBarrier {
1538 texture: dst_raw_webgl,
1539 range: dyn_transition.range,
1540 usage: dyn_transition.usage,
1541 queue_family_ownership_transfer: None,
1542 }
1543 });
1544
1545 use hal::CommandEncoder as _;
1546 unsafe {
1547 encoder_webgl.transition_textures(transitions_webgl);
1548 encoder_webgl.copy_external_image_to_texture(
1549 source,
1550 dst_raw_webgl,
1551 premultiplied_alpha,
1552 core::iter::once(regions),
1553 );
1554 }
1555
1556 pending_writes.insert_texture(&dst);
1557
1558 Ok(())
1559 }
1560
1561 pub fn flush_writes_for_buffer(
1563 &self,
1564 buffer: &Arc<Buffer>,
1565 snatch_guard: SnatchGuard,
1566 ) -> Result<(), BufferAccessError> {
1567 let submission = self.allocate_submission(snatch_guard)?;
1568
1569 let pending_writes = self.pending_writes.lock();
1570 if !pending_writes.contains_buffer(buffer) {
1571 return Ok(());
1572 }
1573
1574 submission.submit(pending_writes)?;
1575
1576 Ok(())
1577 }
1578
1579 fn flush_pending_writes(&self) -> Result<Option<SubmissionIndex>, DeviceError> {
1580 let snatch_guard = self.device.snatchable_lock.read();
1581 let submission = self.allocate_submission(snatch_guard)?;
1582 let submit_index = submission.index;
1583 let pending_writes = self.pending_writes.lock();
1584 if pending_writes.is_recording {
1585 submission.submit(pending_writes)?;
1586 Ok(Some(submit_index))
1587 } else {
1588 Ok(None)
1589 }
1590 }
1591
1592 #[cfg(feature = "trace")]
1593 fn trace_submission(
1594 &self,
1595 submit_index: SubmissionIndex,
1596 commands: Vec<crate::command::Command<crate::command::PointerReferences>>,
1597 ) {
1598 if let Some(ref mut trace) = *self.device.trace.lock() {
1599 trace.add(Action::Submit(submit_index, commands));
1600 }
1601 }
1602
1603 #[cfg(feature = "trace")]
1604 fn trace_failed_submission(
1605 &self,
1606 submit_index: SubmissionIndex,
1607 commands: Option<Vec<crate::command::Command<crate::command::PointerReferences>>>,
1608 error: String,
1609 ) {
1610 if let Some(ref mut trace) = *self.device.trace.lock() {
1611 trace.add(Action::FailedCommands {
1612 commands,
1613 failed_at_submit: Some(submit_index),
1614 error,
1615 });
1616 }
1617 }
1618
1619 fn submit_inner(
1620 &self,
1621 command_buffers: &[Arc<CommandBuffer>],
1622 ) -> Result<SubmissionIndex, Box<(Option<SubmissionIndex>, QueueSubmitError)>> {
1623 profiling::scope!("Queue::submit");
1624 api_log!("Queue::submit");
1625
1626 let snatch_guard = self.device.snatchable_lock.read();
1627 let mut submission = self
1628 .allocate_submission(snatch_guard)
1629 .map_err(|e| (None, e.into()))?;
1630 let submit_index = submission.index;
1631
1632 let mut lose_device_on_error = false;
1636 let mut successfully_submitted = false;
1637
1638 let res = 'error: {
1639 let mut used_surface_textures = track::TextureUsageScope::default();
1640 let mut baked_command_buffers = Vec::with_capacity(command_buffers.len());
1641
1642 if !command_buffers.is_empty() {
1643 profiling::scope!("prepare");
1644
1645 let mut first_error = None;
1646
1647 #[deny(clippy::question_mark_used)]
1650 for command_buffer in command_buffers {
1651 profiling::scope!("process command buffer");
1652
1653 used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1656
1657 #[cfg_attr(not(feature = "trace"), expect(unused_mut))]
1661 let mut cmd_buf_data = command_buffer.take_finished();
1662
1663 if first_error.is_some() {
1664 continue;
1665 }
1666
1667 #[cfg(feature = "trace")]
1668 let trace_commands = cmd_buf_data
1669 .as_mut()
1670 .ok()
1671 .and_then(|data| mem::take(&mut data.trace_commands));
1672
1673 let mut baked = match cmd_buf_data {
1674 Ok(cmd_buf_data) => {
1675 let res = validate_command_buffer(
1676 command_buffer,
1677 self,
1678 &cmd_buf_data,
1679 &submission.snatch_guard,
1680 &mut submission.surface_textures,
1681 &mut used_surface_textures,
1682 &mut submission.command_index_guard,
1683 );
1684 if let Err(err) = res {
1685 #[cfg(feature = "trace")]
1686 self.trace_failed_submission(
1687 submit_index,
1688 trace_commands,
1689 err.to_string(),
1690 );
1691 first_error.get_or_insert(err);
1692 continue;
1693 }
1694
1695 #[cfg(feature = "trace")]
1696 if let Some(commands) = trace_commands {
1697 self.trace_submission(submit_index, commands);
1698 }
1699
1700 cmd_buf_data
1701 .set_acceleration_structure_dependencies(&submission.snatch_guard);
1702 cmd_buf_data.into_baked_commands()
1703 }
1704 Err(err) => {
1705 #[cfg(feature = "trace")]
1706 self.trace_failed_submission(
1707 submit_index,
1708 trace_commands,
1709 err.to_string(),
1710 );
1711 first_error.get_or_insert(err.into());
1712 continue;
1713 }
1714 };
1715
1716 if let Err(e) = baked
1717 .process_deferred_query_set_resolves(&self.device, &submission.snatch_guard)
1718 {
1719 break 'error Err(e.into());
1720 }
1721
1722 baked_command_buffers.push(baked);
1723 }
1724
1725 if let Some(first_error) = first_error {
1726 break 'error Err(first_error);
1727 }
1728
1729 lose_device_on_error = true;
1742
1743 let mut trackers = self.device.trackers.lock();
1745
1746 for mut baked in baked_command_buffers {
1747 profiling::scope!("process baked commands");
1748
1749 if let Err(e) = baked.encoder.open_pass(hal_label(
1751 Some("(wgpu internal) Transit"),
1752 self.device.instance_flags,
1753 )) {
1754 break 'error Err(e.into());
1755 }
1756
1757 baked.initialize_buffer_memory(&mut trackers, &submission.snatch_guard);
1758
1759 let depth_slice_discards = match baked.initialize_texture_memory(
1760 &mut trackers,
1761 &self.device,
1762 &submission.snatch_guard,
1763 ) {
1764 Ok(discards) => discards,
1765 Err(e) => {
1766 break 'error Err(QueueSubmitError::CommandEncoder(
1767 CommandEncoderError::Clear(e),
1768 ));
1769 }
1770 };
1771
1772 CommandEncoder::insert_barriers_from_device_tracker(
1775 baked.encoder.raw.as_mut(),
1776 &mut trackers,
1777 &baked.trackers,
1778 &submission.snatch_guard,
1779 );
1780
1781 if let Err(e) = baked.encoder.close_and_push_front() {
1782 break 'error Err(e.into());
1783 }
1784
1785 if !depth_slice_discards.is_empty() || !used_surface_textures.is_empty() {
1786 if let Err(e) = baked.encoder.open_pass(hal_label(
1787 Some("(wgpu internal) Finalize"),
1788 self.device.instance_flags,
1789 )) {
1790 break 'error Err(e.into());
1791 }
1792
1793 if let Err(e) = baked.initialize_discarded_depth_slices(
1794 depth_slice_discards,
1795 &mut trackers,
1796 &self.device,
1797 &submission.snatch_guard,
1798 ) {
1799 break 'error Err(QueueSubmitError::CommandEncoder(
1800 CommandEncoderError::Clear(e),
1801 ));
1802 }
1803
1804 let texture_barriers = trackers
1808 .textures
1809 .set_from_usage_scope_and_drain_transitions(
1810 &used_surface_textures,
1811 &submission.snatch_guard,
1812 )
1813 .collect::<Vec<_>>();
1814 unsafe {
1815 baked.encoder.raw.transition_textures(&texture_barriers);
1816 };
1817 if let Err(e) = baked.encoder.close() {
1818 break 'error Err(e.into());
1819 }
1820 used_surface_textures = track::TextureUsageScope::default();
1821 }
1822
1823 submission.executions.push(EncoderInFlight {
1825 inner: baked.encoder,
1826 trackers: baked.trackers,
1827 temp_resources: baked.temp_resources,
1828 _indirect_draw_validation_resources: baked
1829 .indirect_draw_validation_resources,
1830 pending_buffers: FastHashMap::default(),
1831 pending_textures: FastHashMap::default(),
1832 pending_blas_s: FastHashMap::default(),
1833 });
1834 }
1835 }
1836
1837 let pending_writes = self.pending_writes.lock();
1838
1839 let SubmissionResult { snatch_guard } = match submission.submit(pending_writes) {
1840 Ok(result) => result,
1841 Err(e) => break 'error Err(e.into()),
1842 };
1843 successfully_submitted = true;
1844
1845 profiling::scope!("cleanup");
1846
1847 lose_device_on_error = false;
1851
1852 let (closures, result) = self.device.maintain(wgt::PollType::Poll, snatch_guard);
1856 match result {
1857 Ok(status) => {
1858 debug_assert!(matches!(
1859 status,
1860 wgt::PollStatus::QueueEmpty | wgt::PollStatus::Poll
1861 ));
1862 }
1863 Err(WaitIdleError::Device(err)) => break 'error Err(QueueSubmitError::Queue(err)),
1864 Err(WaitIdleError::WrongSubmissionIndex(..)) => {
1865 unreachable!("Cannot get WrongSubmissionIndex from Poll")
1866 }
1867 Err(WaitIdleError::Timeout) => unreachable!("Cannot get Timeout from Poll"),
1868 };
1869
1870 Ok(closures)
1871 }; let callbacks = match res {
1874 Ok(ok) => ok,
1875 Err(e) => {
1876 if lose_device_on_error {
1877 self.device.lose("submission failed");
1878 }
1879 return Err(Box::new((
1880 successfully_submitted.then_some(submit_index),
1881 e,
1882 )));
1883 }
1884 };
1885
1886 callbacks.fire();
1888
1889 self.device.lose_if_oom();
1890
1891 api_log!("Queue::submit returned submit index {submit_index}");
1892
1893 Ok(submit_index)
1894 }
1895
1896 pub fn submit(&self, command_buffers: &[Arc<CommandBuffer>]) -> Option<SubmissionIndex> {
1897 match self.submit_inner(command_buffers) {
1898 Ok(submit_index) => Some(submit_index),
1899 Err(e) => {
1900 let (submit_index, e) = *e;
1901 self.device
1902 .handle_error(e, Some(self.label()), "Queue::submit");
1903 submit_index
1904 }
1905 }
1906 }
1907
1908 fn allocate_submission<'a>(
1931 &'a self,
1932 snatch_guard: SnatchGuard<'a>,
1933 ) -> Result<PendingSubmission<'a>, DeviceError> {
1934 let mut command_index_guard = self.device.command_indices.write();
1935 command_index_guard.active_submission_index += 1;
1936 let index = command_index_guard.active_submission_index;
1937
1938 self.device.check_is_valid()?;
1939
1940 let submission = PendingSubmission {
1941 queue: self,
1942 command_index_guard,
1943 snatch_guard,
1944 executions: Vec::new(),
1945 surface_textures: FastHashMap::default(),
1946 index,
1947 };
1948
1949 Ok(submission)
1950 }
1951
1952 fn submit_pending_submission<'a>(
1968 &self,
1969 mut pending_writes: MutexGuard<'_, PendingWrites>,
1970 prepared: PendingSubmission<'a>,
1971 ) -> Result<SubmissionResult<'a>, DeviceError> {
1972 let PendingSubmission {
1973 queue: _,
1974 snatch_guard,
1975 command_index_guard,
1976 mut executions,
1977 mut surface_textures,
1978 index: submit_index,
1979 } = prepared;
1980
1981 let mut used_surface_textures = track::TextureUsageScope::default();
1982 used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1983 for texture in pending_writes.dst_textures.values() {
1984 match texture.try_inner(&snatch_guard) {
1985 Ok(TextureInner::Native { .. }) => {}
1986 Ok(TextureInner::Surface { .. }) => {
1987 surface_textures.insert(Arc::as_ptr(texture), texture.clone());
1989
1990 unsafe {
1991 used_surface_textures
1992 .merge_single(texture, None, wgt::TextureUses::PRESENT)
1993 .unwrap()
1994 };
1995 }
1996 Err(InvalidOrDestroyedResourceError::DestroyedResource(_)) => {}
2001 Err(InvalidOrDestroyedResourceError::InvalidResource(_)) => {
2002 unreachable!()
2003 }
2004 }
2005 }
2006
2007 if !used_surface_textures.is_empty() {
2008 let mut trackers = self.device.trackers.lock();
2009
2010 let texture_barriers = trackers
2011 .textures
2012 .set_from_usage_scope_and_drain_transitions(&used_surface_textures, &snatch_guard)
2013 .collect::<Vec<_>>();
2014 unsafe {
2015 pending_writes
2016 .command_encoder
2017 .transition_textures(&texture_barriers);
2018 };
2019 }
2020
2021 match pending_writes.pre_submit(
2022 &self.device.command_allocator,
2023 &self.device,
2024 self,
2025 &snatch_guard,
2026 ) {
2027 Ok(Some(pending_execution)) => {
2028 executions.insert(0, pending_execution);
2029 }
2030 Ok(None) => {}
2031 Err(e) => return Err(e),
2032 }
2033 let hal_command_buffers = executions
2034 .iter()
2035 .flat_map(|e| e.inner.list.iter().map(|b| b.as_ref()))
2036 .collect::<Vec<_>>();
2037
2038 {
2039 let mut submit_surface_textures =
2040 SmallVec::<[&dyn hal::DynSurfaceTexture; 2]>::with_capacity(surface_textures.len());
2041 for texture in surface_textures.values() {
2042 let raw = match texture.try_inner(&snatch_guard).ok() {
2043 Some(TextureInner::Surface { raw, .. }) => raw.as_ref(),
2044 _ => unreachable!(),
2045 };
2046 submit_surface_textures.push(raw);
2047 }
2048
2049 unsafe {
2050 self.raw().submit(
2051 &hal_command_buffers,
2052 &submit_surface_textures,
2053 (self.device.fence.as_ref(), submit_index),
2054 )
2055 }
2056 .map_err(|e| self.device.handle_hal_error(e))?;
2057
2058 drop(pending_writes);
2062
2063 self.device
2065 .last_successful_submission_index
2066 .fetch_max(submit_index, Ordering::SeqCst);
2067 }
2068
2069 self.lock_life().track_submission(submit_index, executions);
2071
2072 drop(command_index_guard);
2078
2079 Ok(SubmissionResult { snatch_guard })
2080 }
2081
2082 pub(crate) fn get_raw_timestamp_period(&self) -> f32 {
2083 unsafe { self.raw().get_timestamp_period() }
2084 }
2085
2086 pub fn get_timestamp_period(&self) -> f32 {
2087 if self.device.timestamp_normalizer.get().unwrap().enabled() {
2088 return 1.0;
2089 }
2090
2091 self.get_raw_timestamp_period()
2092 }
2093
2094 pub fn on_submitted_work_done(
2096 &self,
2097 closure: SubmittedWorkDoneClosure,
2098 ) -> Option<SubmissionIndex> {
2099 api_log!("Queue::on_submitted_work_done");
2100
2101 let _: Result<_, DeviceError> = self.flush_pending_writes();
2105
2106 self.lock_life().add_work_done_closure(closure)
2107 }
2108
2109 #[allow(trivial_casts)]
2110 pub fn compact_blas(&self, blas: &Arc<Blas>) -> (Arc<Blas>, Option<CompactBlasError>) {
2111 api_log!(
2112 "Queue::compact_blas {:?}, {:?}",
2113 self as *const _,
2114 Arc::as_ptr(blas)
2115 );
2116
2117 let (blas, error) = match self.compact_blas_inner(blas) {
2118 Ok(blas) => (blas, None),
2119 Err(err) => {
2120 let new_label = blas.label.clone() + " (compacted)";
2121 (
2122 Blas::invalid(
2123 self.device.clone(),
2124 &BlasDescriptor {
2125 label: Some(new_label.into()),
2126 flags: blas.flags,
2127 update_mode: blas.update_mode,
2128 },
2129 ),
2130 Some(err),
2131 )
2132 }
2133 };
2134
2135 (blas, error)
2138 }
2139
2140 pub(crate) fn compact_blas_inner(
2141 &self,
2142 blas: &Arc<Blas>,
2143 ) -> Result<Arc<Blas>, CompactBlasError> {
2144 profiling::scope!("Queue::compact_blas");
2145 api_log!("Queue::compact_blas");
2146
2147 let new_label = blas.label.clone() + " (compacted)";
2148
2149 self.device.check_is_valid()?;
2150
2151 self.device
2152 .require_features(wgpu_types::Features::EXPERIMENTAL_RAY_QUERY)?;
2153
2154 blas.check_is_valid()?;
2155 self.same_device_as(blas.as_ref())?;
2156
2157 let device = blas.device.clone();
2158
2159 let snatch_guard = device.snatchable_lock.read();
2160
2161 let BlasCompactState::Ready { size } = *blas.compacted_state.lock() else {
2162 return Err(CompactBlasError::BlasNotReady);
2163 };
2164
2165 let mut size_info = blas.size_info;
2166 size_info.acceleration_structure_size = size;
2167
2168 let mut command_indices_lock = device.command_indices.write();
2169 let mut pending_writes = self.pending_writes.lock();
2170 let cmd_buf_raw = pending_writes.activate();
2171
2172 let raw = unsafe {
2173 device
2174 .raw()
2175 .create_acceleration_structure(&hal::AccelerationStructureDescriptor {
2176 label: hal_label(Some(&new_label), device.instance_flags),
2177 size: size_info.acceleration_structure_size,
2178 format: hal::AccelerationStructureFormat::BottomLevel,
2179 allow_compaction: false,
2180 })
2181 }
2182 .map_err(DeviceError::from_hal)?;
2183
2184 let src_raw = blas.try_raw(&snatch_guard)?;
2185
2186 unsafe {
2187 cmd_buf_raw.copy_acceleration_structure_to_acceleration_structure(
2188 src_raw,
2189 raw.as_ref(),
2190 wgt::AccelerationStructureCopy::Compact,
2191 )
2192 };
2193
2194 let handle = unsafe {
2195 device
2196 .raw()
2197 .get_acceleration_structure_device_address(raw.as_ref())
2198 };
2199
2200 command_indices_lock.next_acceleration_structure_build_command_index += 1;
2201 let built_index =
2202 NonZeroU64::new(command_indices_lock.next_acceleration_structure_build_command_index)
2203 .unwrap();
2204
2205 let new_blas = Arc::new(Blas {
2206 state: ResourceState::Valid(BlasState {
2207 raw: Snatchable::new(raw),
2208 }),
2209 device: device.clone(),
2210 size_info,
2211 sizes: blas.sizes.clone(),
2212 flags: blas.flags & !AccelerationStructureFlags::ALLOW_COMPACTION,
2213 update_mode: blas.update_mode,
2214 built_index: RwLock::new(rank::BLAS_BUILT_INDEX, Some(built_index)),
2216 handle,
2217 label: new_label,
2218 tracking_data: TrackingData::new(blas.device.tracker_indices.blas_s.clone()),
2219 compaction_buffer: None,
2220 compacted_state: Mutex::new(rank::BLAS_COMPACTION_STATE, BlasCompactState::Compacted),
2221 });
2222
2223 pending_writes.insert_blas(blas);
2224 pending_writes.insert_blas(&new_blas);
2225
2226 let old_blas_size = blas.size_info.acceleration_structure_size;
2228 let new_blas_size = new_blas.size_info.acceleration_structure_size;
2229
2230 api_log!("CommandEncoder::compact_blas {:?} (size: {old_blas_size}) -> {:?} (size: {new_blas_size})", Arc::as_ptr(blas), Arc::as_ptr(&new_blas));
2231
2232 Ok(new_blas)
2233 }
2234}
2235
2236fn validate_command_buffer(
2237 command_buffer: &CommandBuffer,
2238 queue: &Queue,
2239 cmd_buf_data: &crate::command::CommandBufferMutable,
2240 snatch_guard: &SnatchGuard,
2241 surface_textures: &mut FastHashMap<*const Texture, Arc<Texture>>,
2242 used_surface_textures: &mut track::TextureUsageScope,
2243 command_index_guard: &mut RwLockWriteGuard<CommandIndices>,
2244) -> Result<(), QueueSubmitError> {
2245 command_buffer.same_device_as(queue)?;
2246
2247 {
2248 profiling::scope!("check resource state");
2249
2250 {
2251 profiling::scope!("buffers");
2252 for buffer in cmd_buf_data.trackers.buffers.used_resources() {
2253 buffer.check_destroyed(snatch_guard)?;
2254
2255 match *buffer.map_state.read() {
2256 BufferMapState::Idle => (),
2257 _ => return Err(QueueSubmitError::BufferStillMapped(buffer.error_ident())),
2258 }
2259 }
2260 }
2261 {
2262 profiling::scope!("textures");
2263 for texture in cmd_buf_data.trackers.textures.used_resources() {
2264 let should_extend = match texture.try_inner(snatch_guard)? {
2265 TextureInner::Native { .. } => false,
2266 TextureInner::Surface { .. } => {
2267 surface_textures.insert(Arc::as_ptr(texture), texture.clone());
2269
2270 true
2271 }
2272 };
2273 if should_extend {
2274 unsafe {
2275 used_surface_textures
2276 .merge_single(texture, None, wgt::TextureUses::PRESENT)
2277 .unwrap();
2278 };
2279 }
2280 }
2281 }
2282 {
2283 profiling::scope!("query sets");
2284 for query_set in cmd_buf_data.trackers.query_sets.used_resources() {
2285 query_set.try_raw(snatch_guard)?;
2286 }
2287 }
2288 {
2292 profiling::scope!("bind groups");
2293 for bind_group in &cmd_buf_data.trackers.bind_groups {
2294 bind_group.try_raw(snatch_guard)?;
2296 }
2297 }
2298
2299 if let Err(e) =
2300 cmd_buf_data.validate_acceleration_structure_actions(snatch_guard, command_index_guard)
2301 {
2302 return Err(e.into());
2303 }
2304 }
2305 Ok(())
2306}