1#[cfg(feature = "trace")]
2use crate::device::trace::Action;
3use crate::{
4 api_log,
5 command::{
6 extract_texture_selector, validate_linear_texture_data, validate_texture_copy_range,
7 ClearError, CommandAllocator, CommandBuffer, CommandEncoderError, CopySide,
8 TexelCopyTextureInfo, TransferError,
9 },
10 conv,
11 device::{DeviceError, WaitIdleError},
12 get_lowest_common_denom,
13 global::Global,
14 id::{self, QueueId},
15 init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange},
16 lock::{rank, Mutex, MutexGuard, RwLockWriteGuard},
17 resource::{
18 Buffer, BufferAccessError, BufferMapState, DestroyedBuffer, DestroyedResourceError,
19 DestroyedTexture, Fallible, FlushedStagingBuffer, InvalidResourceError, Labeled,
20 ParentDevice, ResourceErrorIdent, StagingBuffer, Texture, TextureInner, Trackable,
21 },
22 resource_log,
23 snatch::SnatchGuard,
24 track::{self, Tracker, TrackerIndex},
25 FastHashMap, SubmissionIndex,
26};
27
28use smallvec::SmallVec;
29
30use crate::scratch::ScratchBuffer;
31use std::{
32 iter,
33 mem::{self, ManuallyDrop},
34 ptr::NonNull,
35 sync::{atomic::Ordering, Arc},
36};
37use thiserror::Error;
38
39use super::{life::LifetimeTracker, Device};
40
41pub struct Queue {
42 raw: Box<dyn hal::DynQueue>,
43 pub(crate) pending_writes: Mutex<PendingWrites>,
44 life_tracker: Mutex<LifetimeTracker>,
45 pub(crate) device: Arc<Device>,
47}
48
49impl Queue {
50 pub(crate) fn new(
51 device: Arc<Device>,
52 raw: Box<dyn hal::DynQueue>,
53 ) -> Result<Self, DeviceError> {
54 let pending_encoder = device
55 .command_allocator
56 .acquire_encoder(device.raw(), raw.as_ref())
57 .map_err(DeviceError::from_hal);
58
59 let pending_encoder = match pending_encoder {
60 Ok(pending_encoder) => pending_encoder,
61 Err(e) => {
62 return Err(e);
63 }
64 };
65
66 let mut pending_writes = PendingWrites::new(pending_encoder);
67
68 let zero_buffer = device.zero_buffer.as_ref();
69 pending_writes.activate();
70 unsafe {
71 pending_writes
72 .command_encoder
73 .transition_buffers(&[hal::BufferBarrier {
74 buffer: zero_buffer,
75 usage: hal::StateTransition {
76 from: hal::BufferUses::empty(),
77 to: hal::BufferUses::COPY_DST,
78 },
79 }]);
80 pending_writes
81 .command_encoder
82 .clear_buffer(zero_buffer, 0..super::ZERO_BUFFER_SIZE);
83 pending_writes
84 .command_encoder
85 .transition_buffers(&[hal::BufferBarrier {
86 buffer: zero_buffer,
87 usage: hal::StateTransition {
88 from: hal::BufferUses::COPY_DST,
89 to: hal::BufferUses::COPY_SRC,
90 },
91 }]);
92 }
93
94 Ok(Queue {
95 raw,
96 device,
97 pending_writes: Mutex::new(rank::QUEUE_PENDING_WRITES, pending_writes),
98 life_tracker: Mutex::new(rank::QUEUE_LIFE_TRACKER, LifetimeTracker::new()),
99 })
100 }
101
102 pub(crate) fn raw(&self) -> &dyn hal::DynQueue {
103 self.raw.as_ref()
104 }
105
106 #[track_caller]
107 pub(crate) fn lock_life<'a>(&'a self) -> MutexGuard<'a, LifetimeTracker> {
108 self.life_tracker.lock()
109 }
110
111 pub(crate) fn maintain(
112 &self,
113 submission_index: u64,
114 snatch_guard: &SnatchGuard,
115 ) -> (
116 SmallVec<[SubmittedWorkDoneClosure; 1]>,
117 Vec<super::BufferMapPendingClosure>,
118 bool,
119 ) {
120 let mut life_tracker = self.lock_life();
121 let submission_closures = life_tracker.triage_submissions(submission_index);
122
123 let mapping_closures = life_tracker.handle_mapping(snatch_guard);
124
125 let queue_empty = life_tracker.queue_empty();
126
127 (submission_closures, mapping_closures, queue_empty)
128 }
129}
130
131crate::impl_resource_type!(Queue);
132impl Labeled for Queue {
134 fn label(&self) -> &str {
135 ""
136 }
137}
138crate::impl_parent_device!(Queue);
139crate::impl_storage_item!(Queue);
140
141impl Drop for Queue {
142 fn drop(&mut self) {
143 resource_log!("Drop {}", self.error_ident());
144
145 let last_successful_submission_index = self
146 .device
147 .last_successful_submission_index
148 .load(Ordering::Acquire);
149
150 let fence = self.device.fence.read();
151
152 let timeouts_in_ms = [100, 200, 400, 800, 1600, 3200];
154
155 for (i, timeout_ms) in timeouts_in_ms.into_iter().enumerate() {
156 let is_last_iter = i == timeouts_in_ms.len() - 1;
157
158 api_log!(
159 "Waiting on last submission. try: {}/{}. timeout: {}ms",
160 i + 1,
161 timeouts_in_ms.len(),
162 timeout_ms
163 );
164
165 let wait_res = unsafe {
166 self.device.raw().wait(
167 fence.as_ref(),
168 last_successful_submission_index,
169 #[cfg(not(target_arch = "wasm32"))]
170 timeout_ms,
171 #[cfg(target_arch = "wasm32")]
172 0, )
174 };
175 match wait_res {
177 Ok(true) => break,
178 Ok(false) => {
179 #[cfg(target_arch = "wasm32")]
186 {
187 break;
188 }
189 #[cfg(not(target_arch = "wasm32"))]
190 {
191 if is_last_iter {
192 panic!(
193 "We timed out while waiting on the last successful submission to complete!"
194 );
195 }
196 }
197 }
198 Err(e) => match e {
199 hal::DeviceError::OutOfMemory => {
200 if is_last_iter {
201 panic!(
202 "We ran into an OOM error while waiting on the last successful submission to complete!"
203 );
204 }
205 }
206 hal::DeviceError::Lost => {
207 self.device.handle_hal_error(e); break;
209 }
210 hal::DeviceError::ResourceCreationFailed => unreachable!(),
211 hal::DeviceError::Unexpected => {
212 panic!(
213 "We ran into an unexpected error while waiting on the last successful submission to complete!"
214 );
215 }
216 },
217 }
218 }
219 drop(fence);
220
221 let snatch_guard = self.device.snatchable_lock.read();
222 let (submission_closures, mapping_closures, queue_empty) =
223 self.maintain(last_successful_submission_index, &snatch_guard);
224 drop(snatch_guard);
225
226 assert!(queue_empty);
227
228 let closures = crate::device::UserClosures {
229 mappings: mapping_closures,
230 submissions: submission_closures,
231 device_lost_invocations: SmallVec::new(),
232 };
233
234 closures.fire();
235 }
236}
237
238#[cfg(send_sync)]
239pub type SubmittedWorkDoneClosure = Box<dyn FnOnce() + Send + 'static>;
240#[cfg(not(send_sync))]
241pub type SubmittedWorkDoneClosure = Box<dyn FnOnce() + 'static>;
242
243#[derive(Debug)]
254pub enum TempResource {
255 StagingBuffer(FlushedStagingBuffer),
256 ScratchBuffer(ScratchBuffer),
257 DestroyedBuffer(DestroyedBuffer),
258 DestroyedTexture(DestroyedTexture),
259}
260
261pub(crate) struct EncoderInFlight {
267 inner: crate::command::CommandEncoder,
268 pub(crate) trackers: Tracker,
269 pub(crate) temp_resources: Vec<TempResource>,
270
271 pub(crate) pending_buffers: FastHashMap<TrackerIndex, Arc<Buffer>>,
273 pub(crate) pending_textures: FastHashMap<TrackerIndex, Arc<Texture>>,
275}
276
277#[derive(Debug)]
298pub(crate) struct PendingWrites {
299 pub command_encoder: Box<dyn hal::DynCommandEncoder>,
301
302 pub is_recording: bool,
308
309 temp_resources: Vec<TempResource>,
310 dst_buffers: FastHashMap<TrackerIndex, Arc<Buffer>>,
311 dst_textures: FastHashMap<TrackerIndex, Arc<Texture>>,
312}
313
314impl PendingWrites {
315 pub fn new(command_encoder: Box<dyn hal::DynCommandEncoder>) -> Self {
316 Self {
317 command_encoder,
318 is_recording: false,
319 temp_resources: Vec::new(),
320 dst_buffers: FastHashMap::default(),
321 dst_textures: FastHashMap::default(),
322 }
323 }
324
325 pub fn insert_buffer(&mut self, buffer: &Arc<Buffer>) {
326 self.dst_buffers
327 .insert(buffer.tracker_index(), buffer.clone());
328 }
329
330 pub fn insert_texture(&mut self, texture: &Arc<Texture>) {
331 self.dst_textures
332 .insert(texture.tracker_index(), texture.clone());
333 }
334
335 pub fn contains_buffer(&self, buffer: &Arc<Buffer>) -> bool {
336 self.dst_buffers.contains_key(&buffer.tracker_index())
337 }
338
339 pub fn contains_texture(&self, texture: &Arc<Texture>) -> bool {
340 self.dst_textures.contains_key(&texture.tracker_index())
341 }
342
343 pub fn consume_temp(&mut self, resource: TempResource) {
344 self.temp_resources.push(resource);
345 }
346
347 pub fn consume(&mut self, buffer: FlushedStagingBuffer) {
348 self.temp_resources
349 .push(TempResource::StagingBuffer(buffer));
350 }
351
352 fn pre_submit(
353 &mut self,
354 command_allocator: &CommandAllocator,
355 device: &Arc<Device>,
356 queue: &Queue,
357 ) -> Result<Option<EncoderInFlight>, DeviceError> {
358 if self.is_recording {
359 let pending_buffers = mem::take(&mut self.dst_buffers);
360 let pending_textures = mem::take(&mut self.dst_textures);
361
362 let cmd_buf = unsafe { self.command_encoder.end_encoding() }
363 .map_err(|e| device.handle_hal_error(e))?;
364 self.is_recording = false;
365
366 let new_encoder = command_allocator
367 .acquire_encoder(device.raw(), queue.raw())
368 .map_err(|e| device.handle_hal_error(e))?;
369
370 let encoder = EncoderInFlight {
371 inner: crate::command::CommandEncoder {
372 raw: ManuallyDrop::new(mem::replace(&mut self.command_encoder, new_encoder)),
373 list: vec![cmd_buf],
374 device: device.clone(),
375 is_open: false,
376 hal_label: None,
377 },
378 trackers: Tracker::new(),
379 temp_resources: mem::take(&mut self.temp_resources),
380 pending_buffers,
381 pending_textures,
382 };
383 Ok(Some(encoder))
384 } else {
385 self.dst_buffers.clear();
386 self.dst_textures.clear();
387 Ok(None)
388 }
389 }
390
391 pub fn activate(&mut self) -> &mut dyn hal::DynCommandEncoder {
392 if !self.is_recording {
393 unsafe {
394 self.command_encoder
395 .begin_encoding(Some("(wgpu internal) PendingWrites"))
396 .unwrap();
397 }
398 self.is_recording = true;
399 }
400 self.command_encoder.as_mut()
401 }
402}
403
404impl Drop for PendingWrites {
405 fn drop(&mut self) {
406 unsafe {
407 if self.is_recording {
408 self.command_encoder.discard_encoding();
409 }
410 }
411 }
412}
413
414#[derive(Clone, Debug, Error)]
415#[non_exhaustive]
416pub enum QueueWriteError {
417 #[error(transparent)]
418 Queue(#[from] DeviceError),
419 #[error(transparent)]
420 Transfer(#[from] TransferError),
421 #[error(transparent)]
422 MemoryInitFailure(#[from] ClearError),
423 #[error(transparent)]
424 DestroyedResource(#[from] DestroyedResourceError),
425 #[error(transparent)]
426 InvalidResource(#[from] InvalidResourceError),
427}
428
429#[derive(Clone, Debug, Error)]
430#[non_exhaustive]
431pub enum QueueSubmitError {
432 #[error(transparent)]
433 Queue(#[from] DeviceError),
434 #[error(transparent)]
435 DestroyedResource(#[from] DestroyedResourceError),
436 #[error(transparent)]
437 Unmap(#[from] BufferAccessError),
438 #[error("{0} is still mapped")]
439 BufferStillMapped(ResourceErrorIdent),
440 #[error(transparent)]
441 InvalidResource(#[from] InvalidResourceError),
442 #[error(transparent)]
443 CommandEncoder(#[from] CommandEncoderError),
444 #[error(transparent)]
445 ValidateBlasActionsError(#[from] crate::ray_tracing::ValidateBlasActionsError),
446 #[error(transparent)]
447 ValidateTlasActionsError(#[from] crate::ray_tracing::ValidateTlasActionsError),
448}
449
450impl Queue {
453 pub fn write_buffer(
454 &self,
455 buffer: Fallible<Buffer>,
456 buffer_offset: wgt::BufferAddress,
457 data: &[u8],
458 ) -> Result<(), QueueWriteError> {
459 profiling::scope!("Queue::write_buffer");
460 api_log!("Queue::write_buffer");
461
462 let buffer = buffer.get()?;
463
464 let data_size = data.len() as wgt::BufferAddress;
465
466 self.same_device_as(buffer.as_ref())?;
467
468 let data_size = if let Some(data_size) = wgt::BufferSize::new(data_size) {
469 data_size
470 } else {
471 log::trace!("Ignoring write_buffer of size 0");
472 return Ok(());
473 };
474
475 let mut staging_buffer = StagingBuffer::new(&self.device, data_size)?;
479 let mut pending_writes = self.pending_writes.lock();
480
481 let staging_buffer = {
482 profiling::scope!("copy");
483 staging_buffer.write(data);
484 staging_buffer.flush()
485 };
486
487 let result = self.write_staging_buffer_impl(
488 &mut pending_writes,
489 &staging_buffer,
490 buffer,
491 buffer_offset,
492 );
493
494 pending_writes.consume(staging_buffer);
495 result
496 }
497
498 pub fn create_staging_buffer(
499 &self,
500 buffer_size: wgt::BufferSize,
501 ) -> Result<(StagingBuffer, NonNull<u8>), QueueWriteError> {
502 profiling::scope!("Queue::create_staging_buffer");
503 resource_log!("Queue::create_staging_buffer");
504
505 let staging_buffer = StagingBuffer::new(&self.device, buffer_size)?;
506 let ptr = unsafe { staging_buffer.ptr() };
507
508 Ok((staging_buffer, ptr))
509 }
510
511 pub fn write_staging_buffer(
512 &self,
513 buffer: Fallible<Buffer>,
514 buffer_offset: wgt::BufferAddress,
515 staging_buffer: StagingBuffer,
516 ) -> Result<(), QueueWriteError> {
517 profiling::scope!("Queue::write_staging_buffer");
518
519 let buffer = buffer.get()?;
520
521 let mut pending_writes = self.pending_writes.lock();
522
523 let staging_buffer = staging_buffer.flush();
528
529 let result = self.write_staging_buffer_impl(
530 &mut pending_writes,
531 &staging_buffer,
532 buffer,
533 buffer_offset,
534 );
535
536 pending_writes.consume(staging_buffer);
537 result
538 }
539
540 pub fn validate_write_buffer(
541 &self,
542 buffer: Fallible<Buffer>,
543 buffer_offset: u64,
544 buffer_size: wgt::BufferSize,
545 ) -> Result<(), QueueWriteError> {
546 profiling::scope!("Queue::validate_write_buffer");
547
548 let buffer = buffer.get()?;
549
550 self.validate_write_buffer_impl(&buffer, buffer_offset, buffer_size)?;
551
552 Ok(())
553 }
554
555 fn validate_write_buffer_impl(
556 &self,
557 buffer: &Buffer,
558 buffer_offset: u64,
559 buffer_size: wgt::BufferSize,
560 ) -> Result<(), TransferError> {
561 buffer.check_usage(wgt::BufferUsages::COPY_DST)?;
562 if buffer_size.get() % wgt::COPY_BUFFER_ALIGNMENT != 0 {
563 return Err(TransferError::UnalignedCopySize(buffer_size.get()));
564 }
565 if buffer_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
566 return Err(TransferError::UnalignedBufferOffset(buffer_offset));
567 }
568 if buffer_offset + buffer_size.get() > buffer.size {
569 return Err(TransferError::BufferOverrun {
570 start_offset: buffer_offset,
571 end_offset: buffer_offset + buffer_size.get(),
572 buffer_size: buffer.size,
573 side: CopySide::Destination,
574 });
575 }
576
577 Ok(())
578 }
579
580 fn write_staging_buffer_impl(
581 &self,
582 pending_writes: &mut PendingWrites,
583 staging_buffer: &FlushedStagingBuffer,
584 buffer: Arc<Buffer>,
585 buffer_offset: u64,
586 ) -> Result<(), QueueWriteError> {
587 let transition = {
588 let mut trackers = self.device.trackers.lock();
589 trackers
590 .buffers
591 .set_single(&buffer, hal::BufferUses::COPY_DST)
592 };
593
594 let snatch_guard = self.device.snatchable_lock.read();
595 let dst_raw = buffer.try_raw(&snatch_guard)?;
596
597 self.same_device_as(buffer.as_ref())?;
598
599 self.validate_write_buffer_impl(&buffer, buffer_offset, staging_buffer.size)?;
600
601 let region = hal::BufferCopy {
602 src_offset: 0,
603 dst_offset: buffer_offset,
604 size: staging_buffer.size,
605 };
606 let barriers = iter::once(hal::BufferBarrier {
607 buffer: staging_buffer.raw(),
608 usage: hal::StateTransition {
609 from: hal::BufferUses::MAP_WRITE,
610 to: hal::BufferUses::COPY_SRC,
611 },
612 })
613 .chain(transition.map(|pending| pending.into_hal(&buffer, &snatch_guard)))
614 .collect::<Vec<_>>();
615 let encoder = pending_writes.activate();
616 unsafe {
617 encoder.transition_buffers(&barriers);
618 encoder.copy_buffer_to_buffer(staging_buffer.raw(), dst_raw, &[region]);
619 }
620
621 pending_writes.insert_buffer(&buffer);
622
623 {
626 buffer
627 .initialization_status
628 .write()
629 .drain(buffer_offset..(buffer_offset + staging_buffer.size.get()));
630 }
631
632 Ok(())
633 }
634
635 pub fn write_texture(
636 &self,
637 destination: wgt::TexelCopyTextureInfo<Fallible<Texture>>,
638 data: &[u8],
639 data_layout: &wgt::TexelCopyBufferLayout,
640 size: &wgt::Extent3d,
641 ) -> Result<(), QueueWriteError> {
642 profiling::scope!("Queue::write_texture");
643 api_log!("Queue::write_texture");
644
645 if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
646 log::trace!("Ignoring write_texture of size 0");
647 return Ok(());
648 }
649
650 let dst = destination.texture.get()?;
651 let destination = wgt::TexelCopyTextureInfo {
652 texture: (),
653 mip_level: destination.mip_level,
654 origin: destination.origin,
655 aspect: destination.aspect,
656 };
657
658 self.same_device_as(dst.as_ref())?;
659
660 dst.check_usage(wgt::TextureUsages::COPY_DST)
661 .map_err(TransferError::MissingTextureUsage)?;
662
663 let (hal_copy_size, array_layer_count) =
666 validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, size)?;
667
668 let (selector, dst_base) = extract_texture_selector(&destination, size, &dst)?;
669
670 if !dst_base.aspect.is_one() {
671 return Err(TransferError::CopyAspectNotOne.into());
672 }
673
674 if !conv::is_valid_copy_dst_texture_format(dst.desc.format, destination.aspect) {
675 return Err(TransferError::CopyToForbiddenTextureFormat {
676 format: dst.desc.format,
677 aspect: destination.aspect,
678 }
679 .into());
680 }
681
682 let (required_bytes_in_copy, _source_bytes_per_array_layer) = validate_linear_texture_data(
685 data_layout,
686 dst.desc.format,
687 destination.aspect,
688 data.len() as wgt::BufferAddress,
689 CopySide::Source,
690 size,
691 false,
692 )?;
693
694 if dst.desc.format.is_depth_stencil_format() {
695 self.device
696 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
697 .map_err(TransferError::from)?;
698 }
699
700 let snatch_guard = self.device.snatchable_lock.read();
701
702 let mut pending_writes = self.pending_writes.lock();
703 let encoder = pending_writes.activate();
704
705 let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
711 0..1
713 } else {
714 destination.origin.z..destination.origin.z + size.depth_or_array_layers
715 };
716 let mut dst_initialization_status = dst.initialization_status.write();
717 if dst_initialization_status.mips[destination.mip_level as usize]
718 .check(init_layer_range.clone())
719 .is_some()
720 {
721 if has_copy_partial_init_tracker_coverage(size, destination.mip_level, &dst.desc) {
722 for layer_range in dst_initialization_status.mips[destination.mip_level as usize]
723 .drain(init_layer_range)
724 .collect::<Vec<std::ops::Range<u32>>>()
725 {
726 let mut trackers = self.device.trackers.lock();
727 crate::command::clear_texture(
728 &dst,
729 TextureInitRange {
730 mip_range: destination.mip_level..(destination.mip_level + 1),
731 layer_range,
732 },
733 encoder,
734 &mut trackers.textures,
735 &self.device.alignments,
736 self.device.zero_buffer.as_ref(),
737 &snatch_guard,
738 )
739 .map_err(QueueWriteError::from)?;
740 }
741 } else {
742 dst_initialization_status.mips[destination.mip_level as usize]
743 .drain(init_layer_range);
744 }
745 }
746
747 let dst_raw = dst.try_raw(&snatch_guard)?;
748
749 let (block_width, block_height) = dst.desc.format.block_dimensions();
750 let width_in_blocks = size.width / block_width;
751 let height_in_blocks = size.height / block_height;
752
753 let block_size = dst
754 .desc
755 .format
756 .block_copy_size(Some(destination.aspect))
757 .unwrap();
758 let bytes_in_last_row = width_in_blocks * block_size;
759
760 let bytes_per_row = data_layout.bytes_per_row.unwrap_or(bytes_in_last_row);
761 let rows_per_image = data_layout.rows_per_image.unwrap_or(height_in_blocks);
762
763 let bytes_per_row_alignment = get_lowest_common_denom(
764 self.device.alignments.buffer_copy_pitch.get() as u32,
765 block_size,
766 );
767 let stage_bytes_per_row = wgt::math::align_to(bytes_in_last_row, bytes_per_row_alignment);
768
769 let staging_buffer = if stage_bytes_per_row == bytes_per_row {
773 profiling::scope!("copy aligned");
774 let stage_size = wgt::BufferSize::new(required_bytes_in_copy).unwrap();
776 let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
777 staging_buffer.write(&data[data_layout.offset as usize..]);
778 staging_buffer
779 } else {
780 profiling::scope!("copy chunked");
781 let block_rows_in_copy =
783 (size.depth_or_array_layers - 1) * rows_per_image + height_in_blocks;
784 let stage_size =
785 wgt::BufferSize::new(stage_bytes_per_row as u64 * block_rows_in_copy as u64)
786 .unwrap();
787 let mut staging_buffer = StagingBuffer::new(&self.device, stage_size)?;
788 let copy_bytes_per_row = stage_bytes_per_row.min(bytes_per_row) as usize;
789 for layer in 0..size.depth_or_array_layers {
790 let rows_offset = layer * rows_per_image;
791 for row in rows_offset..rows_offset + height_in_blocks {
792 let src_offset = data_layout.offset as u32 + row * bytes_per_row;
793 let dst_offset = row * stage_bytes_per_row;
794 unsafe {
795 staging_buffer.write_with_offset(
796 data,
797 src_offset as isize,
798 dst_offset as isize,
799 copy_bytes_per_row,
800 )
801 }
802 }
803 }
804 staging_buffer
805 };
806
807 let staging_buffer = staging_buffer.flush();
808
809 let regions = (0..array_layer_count)
810 .map(|array_layer_offset| {
811 let mut texture_base = dst_base.clone();
812 texture_base.array_layer += array_layer_offset;
813 hal::BufferTextureCopy {
814 buffer_layout: wgt::TexelCopyBufferLayout {
815 offset: array_layer_offset as u64
816 * rows_per_image as u64
817 * stage_bytes_per_row as u64,
818 bytes_per_row: Some(stage_bytes_per_row),
819 rows_per_image: Some(rows_per_image),
820 },
821 texture_base,
822 size: hal_copy_size,
823 }
824 })
825 .collect::<Vec<_>>();
826
827 {
828 let buffer_barrier = hal::BufferBarrier {
829 buffer: staging_buffer.raw(),
830 usage: hal::StateTransition {
831 from: hal::BufferUses::MAP_WRITE,
832 to: hal::BufferUses::COPY_SRC,
833 },
834 };
835
836 let mut trackers = self.device.trackers.lock();
837 let transition =
838 trackers
839 .textures
840 .set_single(&dst, selector, hal::TextureUses::COPY_DST);
841 let texture_barriers = transition
842 .map(|pending| pending.into_hal(dst_raw))
843 .collect::<Vec<_>>();
844
845 unsafe {
846 encoder.transition_textures(&texture_barriers);
847 encoder.transition_buffers(&[buffer_barrier]);
848 encoder.copy_buffer_to_texture(staging_buffer.raw(), dst_raw, ®ions);
849 }
850 }
851
852 pending_writes.consume(staging_buffer);
853 pending_writes.insert_texture(&dst);
854
855 Ok(())
856 }
857
858 #[cfg(webgl)]
859 pub fn copy_external_image_to_texture(
860 &self,
861 source: &wgt::CopyExternalImageSourceInfo,
862 destination: wgt::CopyExternalImageDestInfo<Fallible<Texture>>,
863 size: wgt::Extent3d,
864 ) -> Result<(), QueueWriteError> {
865 profiling::scope!("Queue::copy_external_image_to_texture");
866
867 if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
868 log::trace!("Ignoring write_texture of size 0");
869 return Ok(());
870 }
871
872 let mut needs_flag = false;
873 needs_flag |= matches!(source.source, wgt::ExternalImageSource::OffscreenCanvas(_));
874 needs_flag |= source.origin != wgt::Origin2d::ZERO;
875 needs_flag |= destination.color_space != wgt::PredefinedColorSpace::Srgb;
876 #[allow(clippy::bool_comparison)]
877 if matches!(source.source, wgt::ExternalImageSource::ImageBitmap(_)) {
878 needs_flag |= source.flip_y != false;
879 needs_flag |= destination.premultiplied_alpha != false;
880 }
881
882 if needs_flag {
883 self.device
884 .require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES)
885 .map_err(TransferError::from)?;
886 }
887
888 let src_width = source.source.width();
889 let src_height = source.source.height();
890
891 let dst = destination.texture.get()?;
892 let premultiplied_alpha = destination.premultiplied_alpha;
893 let destination = wgt::TexelCopyTextureInfo {
894 texture: (),
895 mip_level: destination.mip_level,
896 origin: destination.origin,
897 aspect: destination.aspect,
898 };
899
900 if !conv::is_valid_external_image_copy_dst_texture_format(dst.desc.format) {
901 return Err(
902 TransferError::ExternalCopyToForbiddenTextureFormat(dst.desc.format).into(),
903 );
904 }
905 if dst.desc.dimension != wgt::TextureDimension::D2 {
906 return Err(TransferError::InvalidDimensionExternal.into());
907 }
908 dst.check_usage(wgt::TextureUsages::COPY_DST | wgt::TextureUsages::RENDER_ATTACHMENT)
909 .map_err(TransferError::MissingTextureUsage)?;
910 if dst.desc.sample_count != 1 {
911 return Err(TransferError::InvalidSampleCount {
912 sample_count: dst.desc.sample_count,
913 }
914 .into());
915 }
916
917 if source.origin.x + size.width > src_width {
918 return Err(TransferError::TextureOverrun {
919 start_offset: source.origin.x,
920 end_offset: source.origin.x + size.width,
921 texture_size: src_width,
922 dimension: crate::resource::TextureErrorDimension::X,
923 side: CopySide::Source,
924 }
925 .into());
926 }
927 if source.origin.y + size.height > src_height {
928 return Err(TransferError::TextureOverrun {
929 start_offset: source.origin.y,
930 end_offset: source.origin.y + size.height,
931 texture_size: src_height,
932 dimension: crate::resource::TextureErrorDimension::Y,
933 side: CopySide::Source,
934 }
935 .into());
936 }
937 if size.depth_or_array_layers != 1 {
938 return Err(TransferError::TextureOverrun {
939 start_offset: 0,
940 end_offset: size.depth_or_array_layers,
941 texture_size: 1,
942 dimension: crate::resource::TextureErrorDimension::Z,
943 side: CopySide::Source,
944 }
945 .into());
946 }
947
948 let (hal_copy_size, _) =
951 validate_texture_copy_range(&destination, &dst.desc, CopySide::Destination, &size)?;
952
953 let (selector, dst_base) = extract_texture_selector(&destination, &size, &dst)?;
954
955 let mut pending_writes = self.pending_writes.lock();
956 let encoder = pending_writes.activate();
957
958 let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
964 0..1
966 } else {
967 destination.origin.z..destination.origin.z + size.depth_or_array_layers
968 };
969 let mut dst_initialization_status = dst.initialization_status.write();
970 if dst_initialization_status.mips[destination.mip_level as usize]
971 .check(init_layer_range.clone())
972 .is_some()
973 {
974 if has_copy_partial_init_tracker_coverage(&size, destination.mip_level, &dst.desc) {
975 for layer_range in dst_initialization_status.mips[destination.mip_level as usize]
976 .drain(init_layer_range)
977 .collect::<Vec<std::ops::Range<u32>>>()
978 {
979 let mut trackers = self.device.trackers.lock();
980 crate::command::clear_texture(
981 &dst,
982 TextureInitRange {
983 mip_range: destination.mip_level..(destination.mip_level + 1),
984 layer_range,
985 },
986 encoder,
987 &mut trackers.textures,
988 &self.device.alignments,
989 self.device.zero_buffer.as_ref(),
990 &self.device.snatchable_lock.read(),
991 )
992 .map_err(QueueWriteError::from)?;
993 }
994 } else {
995 dst_initialization_status.mips[destination.mip_level as usize]
996 .drain(init_layer_range);
997 }
998 }
999
1000 let snatch_guard = self.device.snatchable_lock.read();
1001 let dst_raw = dst.try_raw(&snatch_guard)?;
1002
1003 let regions = hal::TextureCopy {
1004 src_base: hal::TextureCopyBase {
1005 mip_level: 0,
1006 array_layer: 0,
1007 origin: source.origin.to_3d(0),
1008 aspect: hal::FormatAspects::COLOR,
1009 },
1010 dst_base,
1011 size: hal_copy_size,
1012 };
1013
1014 let mut trackers = self.device.trackers.lock();
1015 let transitions = trackers
1016 .textures
1017 .set_single(&dst, selector, hal::TextureUses::COPY_DST);
1018
1019 let encoder_webgl = encoder
1022 .as_any_mut()
1023 .downcast_mut::<hal::gles::CommandEncoder>()
1024 .unwrap();
1025 let dst_raw_webgl = dst_raw
1026 .as_any()
1027 .downcast_ref::<hal::gles::Texture>()
1028 .unwrap();
1029 let transitions_webgl = transitions.map(|pending| {
1030 let dyn_transition = pending.into_hal(dst_raw);
1031 hal::TextureBarrier {
1032 texture: dst_raw_webgl,
1033 range: dyn_transition.range,
1034 usage: dyn_transition.usage,
1035 }
1036 });
1037
1038 use hal::CommandEncoder as _;
1039 unsafe {
1040 encoder_webgl.transition_textures(transitions_webgl);
1041 encoder_webgl.copy_external_image_to_texture(
1042 source,
1043 dst_raw_webgl,
1044 premultiplied_alpha,
1045 iter::once(regions),
1046 );
1047 }
1048
1049 Ok(())
1050 }
1051
1052 pub fn submit(
1053 &self,
1054 command_buffers: &[Arc<CommandBuffer>],
1055 ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {
1056 profiling::scope!("Queue::submit");
1057 api_log!("Queue::submit");
1058
1059 let submit_index;
1060
1061 let res = 'error: {
1062 let snatch_guard = self.device.snatchable_lock.read();
1063
1064 let mut fence = self.device.fence.write();
1066 submit_index = self
1067 .device
1068 .active_submission_index
1069 .fetch_add(1, Ordering::SeqCst)
1070 + 1;
1071 let mut active_executions = Vec::new();
1072
1073 let mut used_surface_textures = track::TextureUsageScope::default();
1074
1075 let mut submit_surface_textures_owned = FastHashMap::default();
1078
1079 {
1080 if !command_buffers.is_empty() {
1081 profiling::scope!("prepare");
1082
1083 let mut first_error = None;
1084
1085 for command_buffer in command_buffers {
1091 profiling::scope!("process command buffer");
1092
1093 used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1096
1097 #[allow(unused_mut)]
1100 let mut cmd_buf_data = command_buffer.take_finished();
1101
1102 #[cfg(feature = "trace")]
1103 if let Some(ref mut trace) = *self.device.trace.lock() {
1104 if let Ok(ref mut cmd_buf_data) = cmd_buf_data {
1105 trace.add(Action::Submit(
1106 submit_index,
1107 cmd_buf_data.commands.take().unwrap(),
1108 ));
1109 }
1110 }
1111
1112 if first_error.is_some() {
1113 continue;
1114 }
1115
1116 let mut baked = match cmd_buf_data {
1117 Ok(cmd_buf_data) => {
1118 let res = validate_command_buffer(
1119 command_buffer,
1120 self,
1121 &cmd_buf_data,
1122 &snatch_guard,
1123 &mut submit_surface_textures_owned,
1124 &mut used_surface_textures,
1125 );
1126 if let Err(err) = res {
1127 first_error.get_or_insert(err);
1128 continue;
1129 }
1130 cmd_buf_data.into_baked_commands()
1131 }
1132 Err(err) => {
1133 first_error.get_or_insert(err.into());
1134 continue;
1135 }
1136 };
1137
1138 if let Err(e) = baked.encoder.open_pass(Some("(wgpu internal) Transit")) {
1140 break 'error Err(e.into());
1141 }
1142
1143 let mut trackers = self.device.trackers.lock();
1145 if let Err(e) = baked.initialize_buffer_memory(&mut trackers, &snatch_guard)
1146 {
1147 break 'error Err(e.into());
1148 }
1149 if let Err(e) = baked.initialize_texture_memory(
1150 &mut trackers,
1151 &self.device,
1152 &snatch_guard,
1153 ) {
1154 break 'error Err(e.into());
1155 }
1156
1157 CommandBuffer::insert_barriers_from_device_tracker(
1160 baked.encoder.raw.as_mut(),
1161 &mut trackers,
1162 &baked.trackers,
1163 &snatch_guard,
1164 );
1165
1166 if let Err(e) = baked.encoder.close_and_push_front() {
1167 break 'error Err(e.into());
1168 }
1169
1170 if !used_surface_textures.is_empty() {
1174 if let Err(e) = baked.encoder.open_pass(Some("(wgpu internal) Present"))
1175 {
1176 break 'error Err(e.into());
1177 }
1178 let texture_barriers = trackers
1179 .textures
1180 .set_from_usage_scope_and_drain_transitions(
1181 &used_surface_textures,
1182 &snatch_guard,
1183 )
1184 .collect::<Vec<_>>();
1185 unsafe {
1186 baked.encoder.raw.transition_textures(&texture_barriers);
1187 };
1188 if let Err(e) = baked.encoder.close() {
1189 break 'error Err(e.into());
1190 }
1191 used_surface_textures = track::TextureUsageScope::default();
1192 }
1193
1194 active_executions.push(EncoderInFlight {
1196 inner: baked.encoder,
1197 trackers: baked.trackers,
1198 temp_resources: baked.temp_resources,
1199 pending_buffers: FastHashMap::default(),
1200 pending_textures: FastHashMap::default(),
1201 });
1202 }
1203
1204 if let Some(first_error) = first_error {
1205 break 'error Err(first_error);
1206 }
1207 }
1208 }
1209
1210 let mut pending_writes = self.pending_writes.lock();
1211
1212 {
1213 used_surface_textures.set_size(self.device.tracker_indices.textures.size());
1214 for texture in pending_writes.dst_textures.values() {
1215 match texture.try_inner(&snatch_guard) {
1216 Ok(TextureInner::Native { .. }) => {}
1217 Ok(TextureInner::Surface { .. }) => {
1218 submit_surface_textures_owned
1220 .insert(Arc::as_ptr(texture), texture.clone());
1221
1222 unsafe {
1223 used_surface_textures
1224 .merge_single(texture, None, hal::TextureUses::PRESENT)
1225 .unwrap()
1226 };
1227 }
1228 Err(e) => break 'error Err(e.into()),
1229 }
1230 }
1231
1232 if !used_surface_textures.is_empty() {
1233 let mut trackers = self.device.trackers.lock();
1234
1235 let texture_barriers = trackers
1236 .textures
1237 .set_from_usage_scope_and_drain_transitions(
1238 &used_surface_textures,
1239 &snatch_guard,
1240 )
1241 .collect::<Vec<_>>();
1242 unsafe {
1243 pending_writes
1244 .command_encoder
1245 .transition_textures(&texture_barriers);
1246 };
1247 }
1248 }
1249
1250 match pending_writes.pre_submit(&self.device.command_allocator, &self.device, self) {
1251 Ok(Some(pending_execution)) => {
1252 active_executions.insert(0, pending_execution);
1253 }
1254 Ok(None) => {}
1255 Err(e) => break 'error Err(e.into()),
1256 }
1257 let hal_command_buffers = active_executions
1258 .iter()
1259 .flat_map(|e| e.inner.list.iter().map(|b| b.as_ref()))
1260 .collect::<Vec<_>>();
1261
1262 {
1263 let mut submit_surface_textures =
1264 SmallVec::<[&dyn hal::DynSurfaceTexture; 2]>::with_capacity(
1265 submit_surface_textures_owned.len(),
1266 );
1267
1268 for texture in submit_surface_textures_owned.values() {
1269 let raw = match texture.inner.get(&snatch_guard) {
1270 Some(TextureInner::Surface { raw, .. }) => raw.as_ref(),
1271 _ => unreachable!(),
1272 };
1273 submit_surface_textures.push(raw);
1274 }
1275
1276 if let Err(e) = unsafe {
1277 self.raw().submit(
1278 &hal_command_buffers,
1279 &submit_surface_textures,
1280 (fence.as_mut(), submit_index),
1281 )
1282 }
1283 .map_err(|e| self.device.handle_hal_error(e))
1284 {
1285 break 'error Err(e.into());
1286 }
1287
1288 self.device
1290 .last_successful_submission_index
1291 .fetch_max(submit_index, Ordering::SeqCst);
1292 }
1293
1294 profiling::scope!("cleanup");
1295
1296 self.lock_life()
1298 .track_submission(submit_index, active_executions);
1299 drop(pending_writes);
1300
1301 let fence_guard = RwLockWriteGuard::downgrade(fence);
1304 let (closures, _) =
1305 match self
1306 .device
1307 .maintain(fence_guard, wgt::Maintain::Poll, snatch_guard)
1308 {
1309 Ok(closures) => closures,
1310 Err(WaitIdleError::Device(err)) => {
1311 break 'error Err(QueueSubmitError::Queue(err))
1312 }
1313 Err(WaitIdleError::WrongSubmissionIndex(..)) => unreachable!(),
1314 };
1315
1316 Ok(closures)
1317 };
1318
1319 let callbacks = match res {
1320 Ok(ok) => ok,
1321 Err(e) => return Err((submit_index, e)),
1322 };
1323
1324 callbacks.fire();
1326
1327 api_log!("Queue::submit returned submit index {submit_index}");
1328
1329 Ok(submit_index)
1330 }
1331
1332 pub fn get_timestamp_period(&self) -> f32 {
1333 unsafe { self.raw().get_timestamp_period() }
1334 }
1335
1336 pub fn on_submitted_work_done(
1338 &self,
1339 closure: SubmittedWorkDoneClosure,
1340 ) -> Option<SubmissionIndex> {
1341 api_log!("Queue::on_submitted_work_done");
1342 self.lock_life().add_work_done_closure(closure)
1344 }
1345}
1346
1347impl Global {
1348 pub fn queue_write_buffer(
1349 &self,
1350 queue_id: QueueId,
1351 buffer_id: id::BufferId,
1352 buffer_offset: wgt::BufferAddress,
1353 data: &[u8],
1354 ) -> Result<(), QueueWriteError> {
1355 let queue = self.hub.queues.get(queue_id);
1356
1357 #[cfg(feature = "trace")]
1358 if let Some(ref mut trace) = *queue.device.trace.lock() {
1359 let data_path = trace.make_binary("bin", data);
1360 trace.add(Action::WriteBuffer {
1361 id: buffer_id,
1362 data: data_path,
1363 range: buffer_offset..buffer_offset + data.len() as u64,
1364 queued: true,
1365 });
1366 }
1367
1368 let buffer = self.hub.buffers.get(buffer_id);
1369 queue.write_buffer(buffer, buffer_offset, data)
1370 }
1371
1372 pub fn queue_create_staging_buffer(
1373 &self,
1374 queue_id: QueueId,
1375 buffer_size: wgt::BufferSize,
1376 id_in: Option<id::StagingBufferId>,
1377 ) -> Result<(id::StagingBufferId, NonNull<u8>), QueueWriteError> {
1378 let queue = self.hub.queues.get(queue_id);
1379 let (staging_buffer, ptr) = queue.create_staging_buffer(buffer_size)?;
1380
1381 let fid = self.hub.staging_buffers.prepare(id_in);
1382 let id = fid.assign(staging_buffer);
1383
1384 Ok((id, ptr))
1385 }
1386
1387 pub fn queue_write_staging_buffer(
1388 &self,
1389 queue_id: QueueId,
1390 buffer_id: id::BufferId,
1391 buffer_offset: wgt::BufferAddress,
1392 staging_buffer_id: id::StagingBufferId,
1393 ) -> Result<(), QueueWriteError> {
1394 let queue = self.hub.queues.get(queue_id);
1395 let buffer = self.hub.buffers.get(buffer_id);
1396 let staging_buffer = self.hub.staging_buffers.remove(staging_buffer_id);
1397 queue.write_staging_buffer(buffer, buffer_offset, staging_buffer)
1398 }
1399
1400 pub fn queue_validate_write_buffer(
1401 &self,
1402 queue_id: QueueId,
1403 buffer_id: id::BufferId,
1404 buffer_offset: u64,
1405 buffer_size: wgt::BufferSize,
1406 ) -> Result<(), QueueWriteError> {
1407 let queue = self.hub.queues.get(queue_id);
1408 let buffer = self.hub.buffers.get(buffer_id);
1409 queue.validate_write_buffer(buffer, buffer_offset, buffer_size)
1410 }
1411
1412 pub fn queue_write_texture(
1413 &self,
1414 queue_id: QueueId,
1415 destination: &TexelCopyTextureInfo,
1416 data: &[u8],
1417 data_layout: &wgt::TexelCopyBufferLayout,
1418 size: &wgt::Extent3d,
1419 ) -> Result<(), QueueWriteError> {
1420 let queue = self.hub.queues.get(queue_id);
1421
1422 #[cfg(feature = "trace")]
1423 if let Some(ref mut trace) = *queue.device.trace.lock() {
1424 let data_path = trace.make_binary("bin", data);
1425 trace.add(Action::WriteTexture {
1426 to: *destination,
1427 data: data_path,
1428 layout: *data_layout,
1429 size: *size,
1430 });
1431 }
1432
1433 let destination = wgt::TexelCopyTextureInfo {
1434 texture: self.hub.textures.get(destination.texture),
1435 mip_level: destination.mip_level,
1436 origin: destination.origin,
1437 aspect: destination.aspect,
1438 };
1439 queue.write_texture(destination, data, data_layout, size)
1440 }
1441
1442 #[cfg(webgl)]
1443 pub fn queue_copy_external_image_to_texture(
1444 &self,
1445 queue_id: QueueId,
1446 source: &wgt::CopyExternalImageSourceInfo,
1447 destination: crate::command::CopyExternalImageDestInfo,
1448 size: wgt::Extent3d,
1449 ) -> Result<(), QueueWriteError> {
1450 let queue = self.hub.queues.get(queue_id);
1451 let destination = wgt::CopyExternalImageDestInfo {
1452 texture: self.hub.textures.get(destination.texture),
1453 mip_level: destination.mip_level,
1454 origin: destination.origin,
1455 aspect: destination.aspect,
1456 color_space: destination.color_space,
1457 premultiplied_alpha: destination.premultiplied_alpha,
1458 };
1459 queue.copy_external_image_to_texture(source, destination, size)
1460 }
1461
1462 pub fn queue_submit(
1463 &self,
1464 queue_id: QueueId,
1465 command_buffer_ids: &[id::CommandBufferId],
1466 ) -> Result<SubmissionIndex, (SubmissionIndex, QueueSubmitError)> {
1467 let queue = self.hub.queues.get(queue_id);
1468 let command_buffer_guard = self.hub.command_buffers.read();
1469 let command_buffers = command_buffer_ids
1470 .iter()
1471 .map(|id| command_buffer_guard.get(*id))
1472 .collect::<Vec<_>>();
1473 drop(command_buffer_guard);
1474 queue.submit(&command_buffers)
1475 }
1476
1477 pub fn queue_get_timestamp_period(&self, queue_id: QueueId) -> f32 {
1478 let queue = self.hub.queues.get(queue_id);
1479 queue.get_timestamp_period()
1480 }
1481
1482 pub fn queue_on_submitted_work_done(
1483 &self,
1484 queue_id: QueueId,
1485 closure: SubmittedWorkDoneClosure,
1486 ) -> SubmissionIndex {
1487 api_log!("Queue::on_submitted_work_done {queue_id:?}");
1488
1489 let queue = self.hub.queues.get(queue_id);
1491 let result = queue.on_submitted_work_done(closure);
1492 result.unwrap_or(0) }
1494}
1495
1496fn validate_command_buffer(
1497 command_buffer: &CommandBuffer,
1498 queue: &Queue,
1499 cmd_buf_data: &crate::command::CommandBufferMutable,
1500 snatch_guard: &SnatchGuard,
1501 submit_surface_textures_owned: &mut FastHashMap<*const Texture, Arc<Texture>>,
1502 used_surface_textures: &mut track::TextureUsageScope,
1503) -> Result<(), QueueSubmitError> {
1504 command_buffer.same_device_as(queue)?;
1505
1506 {
1507 profiling::scope!("check resource state");
1508
1509 {
1510 profiling::scope!("buffers");
1511 for buffer in cmd_buf_data.trackers.buffers.used_resources() {
1512 buffer.check_destroyed(snatch_guard)?;
1513
1514 match *buffer.map_state.lock() {
1515 BufferMapState::Idle => (),
1516 _ => return Err(QueueSubmitError::BufferStillMapped(buffer.error_ident())),
1517 }
1518 }
1519 }
1520 {
1521 profiling::scope!("textures");
1522 for texture in cmd_buf_data.trackers.textures.used_resources() {
1523 let should_extend = match texture.try_inner(snatch_guard)? {
1524 TextureInner::Native { .. } => false,
1525 TextureInner::Surface { .. } => {
1526 submit_surface_textures_owned.insert(Arc::as_ptr(texture), texture.clone());
1528
1529 true
1530 }
1531 };
1532 if should_extend {
1533 unsafe {
1534 used_surface_textures
1535 .merge_single(texture, None, hal::TextureUses::PRESENT)
1536 .unwrap();
1537 };
1538 }
1539 }
1540 }
1541
1542 if let Err(e) = cmd_buf_data.validate_blas_actions() {
1543 return Err(e.into());
1544 }
1545 if let Err(e) = cmd_buf_data.validate_tlas_actions(snatch_guard) {
1546 return Err(e.into());
1547 }
1548 }
1549 Ok(())
1550}