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