1#[cfg(feature = "trace")]
2use crate::device::trace::Action;
3use crate::{
4 command::{
5 extract_texture_selector, validate_linear_texture_data, validate_texture_copy_range,
6 ClearError, CommandBuffer, CopySide, ImageCopyTexture, TransferError,
7 },
8 conv,
9 device::{DeviceError, WaitIdleError},
10 get_lowest_common_denom,
11 global::Global,
12 hal_api::HalApi,
13 hal_label,
14 hub::Token,
15 id,
16 identity::{GlobalIdentityHandlerFactory, Input},
17 init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange},
18 resource::{BufferAccessError, BufferMapState, StagingBuffer, TextureInner},
19 track, FastHashSet, SubmissionIndex,
20};
21
22use hal::{CommandEncoder as _, Device as _, Queue as _};
23use parking_lot::Mutex;
24use smallvec::SmallVec;
25use std::{iter, mem, ptr};
26use thiserror::Error;
27
28const WRITE_COMMAND_BUFFERS_PER_POOL: usize = 64;
34
35#[repr(C)]
36pub struct SubmittedWorkDoneClosureC {
37 pub callback: unsafe extern "C" fn(user_data: *mut u8),
38 pub user_data: *mut u8,
39}
40
41#[cfg(any(
42 not(target_arch = "wasm32"),
43 all(
44 feature = "fragile-send-sync-non-atomic-wasm",
45 not(target_feature = "atomics")
46 )
47))]
48unsafe impl Send for SubmittedWorkDoneClosureC {}
49
50pub struct SubmittedWorkDoneClosure {
51 inner: SubmittedWorkDoneClosureInner,
54}
55
56#[cfg(any(
57 not(target_arch = "wasm32"),
58 all(
59 feature = "fragile-send-sync-non-atomic-wasm",
60 not(target_feature = "atomics")
61 )
62))]
63type SubmittedWorkDoneCallback = Box<dyn FnOnce() + Send + 'static>;
64#[cfg(not(any(
65 not(target_arch = "wasm32"),
66 all(
67 feature = "fragile-send-sync-non-atomic-wasm",
68 not(target_feature = "atomics")
69 )
70)))]
71type SubmittedWorkDoneCallback = Box<dyn FnOnce() + 'static>;
72
73enum SubmittedWorkDoneClosureInner {
74 Rust { callback: SubmittedWorkDoneCallback },
75 C { inner: SubmittedWorkDoneClosureC },
76}
77
78impl SubmittedWorkDoneClosure {
79 pub fn from_rust(callback: SubmittedWorkDoneCallback) -> Self {
80 Self {
81 inner: SubmittedWorkDoneClosureInner::Rust { callback },
82 }
83 }
84
85 pub unsafe fn from_c(inner: SubmittedWorkDoneClosureC) -> Self {
93 Self {
94 inner: SubmittedWorkDoneClosureInner::C { inner },
95 }
96 }
97
98 pub(crate) fn call(self) {
99 match self.inner {
100 SubmittedWorkDoneClosureInner::Rust { callback } => callback(),
101 SubmittedWorkDoneClosureInner::C { inner } => unsafe {
103 (inner.callback)(inner.user_data)
104 },
105 }
106 }
107}
108
109#[repr(C)]
110#[derive(Debug, Copy, Clone)]
111pub struct WrappedSubmissionIndex {
112 pub queue_id: id::QueueId,
113 pub index: SubmissionIndex,
114}
115
116#[derive(Debug)]
130pub enum TempResource<A: hal::Api> {
131 Buffer(A::Buffer),
132 Texture(A::Texture, SmallVec<[A::TextureView; 1]>),
133}
134
135pub(super) struct EncoderInFlight<A: hal::Api> {
137 raw: A::CommandEncoder,
138 cmd_buffers: Vec<A::CommandBuffer>,
139}
140
141impl<A: hal::Api> EncoderInFlight<A> {
142 pub(super) unsafe fn land(mut self) -> A::CommandEncoder {
143 unsafe { self.raw.reset_all(self.cmd_buffers.into_iter()) };
144 self.raw
145 }
146}
147
148#[derive(Debug)]
165pub(crate) struct PendingWrites<A: hal::Api> {
166 pub command_encoder: A::CommandEncoder,
167 pub is_active: bool,
168 pub temp_resources: Vec<TempResource<A>>,
169 pub dst_buffers: FastHashSet<id::BufferId>,
170 pub dst_textures: FastHashSet<id::TextureId>,
171 pub executing_command_buffers: Vec<A::CommandBuffer>,
172}
173
174impl<A: hal::Api> PendingWrites<A> {
175 pub fn new(command_encoder: A::CommandEncoder) -> Self {
176 Self {
177 command_encoder,
178 is_active: false,
179 temp_resources: Vec::new(),
180 dst_buffers: FastHashSet::default(),
181 dst_textures: FastHashSet::default(),
182 executing_command_buffers: Vec::new(),
183 }
184 }
185
186 pub fn dispose(mut self, device: &A::Device) {
187 unsafe {
188 if self.is_active {
189 self.command_encoder.discard_encoding();
190 }
191 self.command_encoder
192 .reset_all(self.executing_command_buffers.into_iter());
193 device.destroy_command_encoder(self.command_encoder);
194 }
195
196 for resource in self.temp_resources {
197 match resource {
198 TempResource::Buffer(buffer) => unsafe {
199 device.destroy_buffer(buffer);
200 },
201 TempResource::Texture(texture, views) => unsafe {
202 for view in views.into_iter() {
203 device.destroy_texture_view(view);
204 }
205 device.destroy_texture(texture);
206 },
207 }
208 }
209 }
210
211 pub fn consume_temp(&mut self, resource: TempResource<A>) {
212 self.temp_resources.push(resource);
213 }
214
215 fn consume(&mut self, buffer: StagingBuffer<A>) {
216 self.temp_resources.push(TempResource::Buffer(buffer.raw));
217 }
218
219 #[must_use]
220 fn pre_submit(&mut self) -> Option<&A::CommandBuffer> {
221 self.dst_buffers.clear();
222 self.dst_textures.clear();
223 if self.is_active {
224 let cmd_buf = unsafe { self.command_encoder.end_encoding().unwrap() };
225 self.is_active = false;
226 self.executing_command_buffers.push(cmd_buf);
227 self.executing_command_buffers.last()
228 } else {
229 None
230 }
231 }
232
233 #[must_use]
234 fn post_submit(
235 &mut self,
236 command_allocator: &Mutex<super::CommandAllocator<A>>,
237 device: &A::Device,
238 queue: &A::Queue,
239 ) -> Option<EncoderInFlight<A>> {
240 if self.executing_command_buffers.len() >= WRITE_COMMAND_BUFFERS_PER_POOL {
241 let new_encoder = command_allocator
242 .lock()
243 .acquire_encoder(device, queue)
244 .unwrap();
245 Some(EncoderInFlight {
246 raw: mem::replace(&mut self.command_encoder, new_encoder),
247 cmd_buffers: mem::take(&mut self.executing_command_buffers),
248 })
249 } else {
250 None
251 }
252 }
253
254 pub fn activate(&mut self) -> &mut A::CommandEncoder {
255 if !self.is_active {
256 unsafe {
257 self.command_encoder
258 .begin_encoding(Some("(wgpu internal) PendingWrites"))
259 .unwrap();
260 }
261 self.is_active = true;
262 }
263 &mut self.command_encoder
264 }
265
266 pub fn deactivate(&mut self) {
267 if self.is_active {
268 unsafe {
269 self.command_encoder.discard_encoding();
270 }
271 self.is_active = false;
272 }
273 }
274}
275
276fn prepare_staging_buffer<A: HalApi>(
277 device: &mut A::Device,
278 size: wgt::BufferAddress,
279 instance_flags: wgt::InstanceFlags,
280) -> Result<(StagingBuffer<A>, *mut u8), DeviceError> {
281 profiling::scope!("prepare_staging_buffer");
282 let stage_desc = hal::BufferDescriptor {
283 label: hal_label(Some("(wgpu internal) Staging"), instance_flags),
284 size,
285 usage: hal::BufferUses::MAP_WRITE | hal::BufferUses::COPY_SRC,
286 memory_flags: hal::MemoryFlags::TRANSIENT,
287 };
288
289 let buffer = unsafe { device.create_buffer(&stage_desc)? };
290 let mapping = unsafe { device.map_buffer(&buffer, 0..size) }?;
291
292 let staging_buffer = StagingBuffer {
293 raw: buffer,
294 size,
295 is_coherent: mapping.is_coherent,
296 };
297
298 Ok((staging_buffer, mapping.ptr.as_ptr()))
299}
300
301impl<A: hal::Api> StagingBuffer<A> {
302 unsafe fn flush(&self, device: &A::Device) -> Result<(), DeviceError> {
303 if !self.is_coherent {
304 unsafe { device.flush_mapped_ranges(&self.raw, iter::once(0..self.size)) };
305 }
306 unsafe { device.unmap_buffer(&self.raw)? };
307 Ok(())
308 }
309}
310
311#[derive(Clone, Debug, Error)]
312#[error("Queue is invalid")]
313pub struct InvalidQueue;
314
315#[derive(Clone, Debug, Error)]
316#[non_exhaustive]
317pub enum QueueWriteError {
318 #[error(transparent)]
319 Queue(#[from] DeviceError),
320 #[error(transparent)]
321 Transfer(#[from] TransferError),
322 #[error(transparent)]
323 MemoryInitFailure(#[from] ClearError),
324}
325
326#[derive(Clone, Debug, Error)]
327#[non_exhaustive]
328pub enum QueueSubmitError {
329 #[error(transparent)]
330 Queue(#[from] DeviceError),
331 #[error("Buffer {0:?} is destroyed")]
332 DestroyedBuffer(id::BufferId),
333 #[error("Texture {0:?} is destroyed")]
334 DestroyedTexture(id::TextureId),
335 #[error(transparent)]
336 Unmap(#[from] BufferAccessError),
337 #[error("Buffer {0:?} is still mapped")]
338 BufferStillMapped(id::BufferId),
339 #[error("Surface output was dropped before the command buffer got submitted")]
340 SurfaceOutputDropped,
341 #[error("Surface was unconfigured before the command buffer got submitted")]
342 SurfaceUnconfigured,
343 #[error("GPU got stuck :(")]
344 StuckGpu,
345}
346
347impl<G: GlobalIdentityHandlerFactory> Global<G> {
350 pub fn queue_write_buffer<A: HalApi>(
351 &self,
352 queue_id: id::QueueId,
353 buffer_id: id::BufferId,
354 buffer_offset: wgt::BufferAddress,
355 data: &[u8],
356 ) -> Result<(), QueueWriteError> {
357 profiling::scope!("Queue::write_buffer");
358
359 let hub = A::hub(self);
360 let root_token = &mut Token::root();
361
362 let (mut device_guard, ref mut device_token) = hub.devices.write(root_token);
363 let device = device_guard
364 .get_mut(queue_id)
365 .map_err(|_| DeviceError::Invalid)?;
366
367 let data_size = data.len() as wgt::BufferAddress;
368
369 #[cfg(feature = "trace")]
370 if let Some(ref trace) = device.trace {
371 let mut trace = trace.lock();
372 let data_path = trace.make_binary("bin", data);
373 trace.add(Action::WriteBuffer {
374 id: buffer_id,
375 data: data_path,
376 range: buffer_offset..buffer_offset + data_size,
377 queued: true,
378 });
379 }
380
381 if data_size == 0 {
382 log::trace!("Ignoring write_buffer of size 0");
383 return Ok(());
384 }
385
386 let (staging_buffer, staging_buffer_ptr) =
390 prepare_staging_buffer(&mut device.raw, data_size, device.instance_flags)?;
391
392 if let Err(flush_error) = unsafe {
393 profiling::scope!("copy");
394 ptr::copy_nonoverlapping(data.as_ptr(), staging_buffer_ptr, data.len());
395 staging_buffer.flush(&device.raw)
396 } {
397 device.pending_writes.consume(staging_buffer);
398 return Err(flush_error.into());
399 }
400
401 let result = self.queue_write_staging_buffer_impl(
402 queue_id,
403 device,
404 device_token,
405 &staging_buffer,
406 buffer_id,
407 buffer_offset,
408 );
409
410 device.pending_writes.consume(staging_buffer);
411 result
412 }
413
414 pub fn queue_create_staging_buffer<A: HalApi>(
415 &self,
416 queue_id: id::QueueId,
417 buffer_size: wgt::BufferSize,
418 id_in: Input<G, id::StagingBufferId>,
419 ) -> Result<(id::StagingBufferId, *mut u8), QueueWriteError> {
420 profiling::scope!("Queue::create_staging_buffer");
421 let hub = A::hub(self);
422 let root_token = &mut Token::root();
423
424 let (mut device_guard, ref mut device_token) = hub.devices.write(root_token);
425 let device = device_guard
426 .get_mut(queue_id)
427 .map_err(|_| DeviceError::Invalid)?;
428
429 let (staging_buffer, staging_buffer_ptr) =
430 prepare_staging_buffer(&mut device.raw, buffer_size.get(), device.instance_flags)?;
431
432 let fid = hub.staging_buffers.prepare(id_in);
433 let id = fid.assign(staging_buffer, device_token);
434
435 Ok((id.0, staging_buffer_ptr))
436 }
437
438 pub fn queue_write_staging_buffer<A: HalApi>(
439 &self,
440 queue_id: id::QueueId,
441 buffer_id: id::BufferId,
442 buffer_offset: wgt::BufferAddress,
443 staging_buffer_id: id::StagingBufferId,
444 ) -> Result<(), QueueWriteError> {
445 profiling::scope!("Queue::write_staging_buffer");
446 let hub = A::hub(self);
447 let root_token = &mut Token::root();
448
449 let (mut device_guard, ref mut device_token) = hub.devices.write(root_token);
450 let device = device_guard
451 .get_mut(queue_id)
452 .map_err(|_| DeviceError::Invalid)?;
453
454 let staging_buffer = hub
455 .staging_buffers
456 .unregister(staging_buffer_id, device_token)
457 .0
458 .ok_or(TransferError::InvalidBuffer(buffer_id))?;
459
460 if let Err(flush_error) = unsafe { staging_buffer.flush(&device.raw) } {
465 device.pending_writes.consume(staging_buffer);
466 return Err(flush_error.into());
467 }
468
469 let result = self.queue_write_staging_buffer_impl(
470 queue_id,
471 device,
472 device_token,
473 &staging_buffer,
474 buffer_id,
475 buffer_offset,
476 );
477
478 device.pending_writes.consume(staging_buffer);
479 result
480 }
481
482 pub fn queue_validate_write_buffer<A: HalApi>(
483 &self,
484 _queue_id: id::QueueId,
485 buffer_id: id::BufferId,
486 buffer_offset: u64,
487 buffer_size: u64,
488 ) -> Result<(), QueueWriteError> {
489 profiling::scope!("Queue::validate_write_buffer");
490 let hub = A::hub(self);
491 let root_token = &mut Token::root();
492
493 let (_, ref mut device_token) = hub.devices.read(root_token);
494
495 let buffer_guard = hub.buffers.read(device_token).0;
496 let buffer = buffer_guard
497 .get(buffer_id)
498 .map_err(|_| TransferError::InvalidBuffer(buffer_id))?;
499
500 self.queue_validate_write_buffer_impl(buffer, buffer_id, buffer_offset, buffer_size)?;
501
502 Ok(())
503 }
504
505 fn queue_validate_write_buffer_impl<A: HalApi>(
506 &self,
507 buffer: &crate::resource::Buffer<A>,
508 buffer_id: id::BufferId,
509 buffer_offset: u64,
510 buffer_size: u64,
511 ) -> Result<(), TransferError> {
512 if !buffer.usage.contains(wgt::BufferUsages::COPY_DST) {
513 return Err(TransferError::MissingCopyDstUsageFlag(
514 Some(buffer_id),
515 None,
516 ));
517 }
518 if buffer_size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
519 return Err(TransferError::UnalignedCopySize(buffer_size));
520 }
521 if buffer_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
522 return Err(TransferError::UnalignedBufferOffset(buffer_offset));
523 }
524 if buffer_offset + buffer_size > buffer.size {
525 return Err(TransferError::BufferOverrun {
526 start_offset: buffer_offset,
527 end_offset: buffer_offset + buffer_size,
528 buffer_size: buffer.size,
529 side: CopySide::Destination,
530 });
531 }
532
533 Ok(())
534 }
535
536 fn queue_write_staging_buffer_impl<A: HalApi>(
537 &self,
538 device_id: id::DeviceId,
539 device: &mut super::Device<A>,
540 device_token: &mut Token<super::Device<A>>,
541 staging_buffer: &StagingBuffer<A>,
542 buffer_id: id::BufferId,
543 buffer_offset: u64,
544 ) -> Result<(), QueueWriteError> {
545 let hub = A::hub(self);
546
547 let buffer_guard = hub.buffers.read(device_token).0;
548
549 let mut trackers = device.trackers.lock();
550 let (dst, transition) = trackers
551 .buffers
552 .set_single(&buffer_guard, buffer_id, hal::BufferUses::COPY_DST)
553 .ok_or(TransferError::InvalidBuffer(buffer_id))?;
554 let dst_raw = dst
555 .raw
556 .as_ref()
557 .ok_or(TransferError::InvalidBuffer(buffer_id))?;
558
559 if dst.device_id.value.0 != device_id {
560 return Err(DeviceError::WrongDevice.into());
561 }
562
563 let src_buffer_size = staging_buffer.size;
564 self.queue_validate_write_buffer_impl(dst, buffer_id, buffer_offset, src_buffer_size)?;
565
566 dst.life_guard.use_at(device.active_submission_index + 1);
567
568 let region = wgt::BufferSize::new(src_buffer_size).map(|size| hal::BufferCopy {
569 src_offset: 0,
570 dst_offset: buffer_offset,
571 size,
572 });
573 let barriers = iter::once(hal::BufferBarrier {
574 buffer: &staging_buffer.raw,
575 usage: hal::BufferUses::MAP_WRITE..hal::BufferUses::COPY_SRC,
576 })
577 .chain(transition.map(|pending| pending.into_hal(dst)));
578 let encoder = device.pending_writes.activate();
579 unsafe {
580 encoder.transition_buffers(barriers);
581 encoder.copy_buffer_to_buffer(&staging_buffer.raw, dst_raw, region.into_iter());
582 }
583
584 device.pending_writes.dst_buffers.insert(buffer_id);
585
586 {
589 drop(buffer_guard);
590 let mut buffer_guard = hub.buffers.write(device_token).0;
591
592 let dst = buffer_guard.get_mut(buffer_id).unwrap();
593 dst.initialization_status
594 .drain(buffer_offset..(buffer_offset + src_buffer_size));
595 }
596
597 Ok(())
598 }
599
600 pub fn queue_write_texture<A: HalApi>(
601 &self,
602 queue_id: id::QueueId,
603 destination: &ImageCopyTexture,
604 data: &[u8],
605 data_layout: &wgt::ImageDataLayout,
606 size: &wgt::Extent3d,
607 ) -> Result<(), QueueWriteError> {
608 profiling::scope!("Queue::write_texture");
609
610 let hub = A::hub(self);
611 let mut token = Token::root();
612 let (mut device_guard, mut token) = hub.devices.write(&mut token);
613 let device = device_guard
614 .get_mut(queue_id)
615 .map_err(|_| DeviceError::Invalid)?;
616
617 #[cfg(feature = "trace")]
618 if let Some(ref trace) = device.trace {
619 let mut trace = trace.lock();
620 let data_path = trace.make_binary("bin", data);
621 trace.add(Action::WriteTexture {
622 to: *destination,
623 data: data_path,
624 layout: *data_layout,
625 size: *size,
626 });
627 }
628
629 if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
630 log::trace!("Ignoring write_texture of size 0");
631 return Ok(());
632 }
633
634 let (mut texture_guard, _) = hub.textures.write(&mut token); let dst = texture_guard
636 .get_mut(destination.texture)
637 .map_err(|_| TransferError::InvalidTexture(destination.texture))?;
638
639 if dst.device_id.value.0 != queue_id {
640 return Err(DeviceError::WrongDevice.into());
641 }
642
643 if !dst.desc.usage.contains(wgt::TextureUsages::COPY_DST) {
644 return Err(
645 TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(),
646 );
647 }
648
649 let (hal_copy_size, array_layer_count) =
652 validate_texture_copy_range(destination, &dst.desc, CopySide::Destination, size)?;
653
654 let (selector, dst_base) = extract_texture_selector(destination, size, dst)?;
655
656 if !dst_base.aspect.is_one() {
657 return Err(TransferError::CopyAspectNotOne.into());
658 }
659
660 if !conv::is_valid_copy_dst_texture_format(dst.desc.format, destination.aspect) {
661 return Err(TransferError::CopyToForbiddenTextureFormat {
662 format: dst.desc.format,
663 aspect: destination.aspect,
664 }
665 .into());
666 }
667
668 let (_, _source_bytes_per_array_layer) = validate_linear_texture_data(
671 data_layout,
672 dst.desc.format,
673 destination.aspect,
674 data.len() as wgt::BufferAddress,
675 CopySide::Source,
676 size,
677 false,
678 )?;
679
680 if dst.desc.format.is_depth_stencil_format() {
681 device
682 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
683 .map_err(TransferError::from)?;
684 }
685
686 let (block_width, block_height) = dst.desc.format.block_dimensions();
687 let width_blocks = size.width / block_width;
688 let height_blocks = size.height / block_height;
689
690 let block_rows_per_image = data_layout.rows_per_image.unwrap_or(
691 size.height,
695 );
696
697 let block_size = dst
698 .desc
699 .format
700 .block_size(Some(destination.aspect))
701 .unwrap();
702 let bytes_per_row_alignment =
703 get_lowest_common_denom(device.alignments.buffer_copy_pitch.get() as u32, block_size);
704 let stage_bytes_per_row =
705 wgt::math::align_to(block_size * width_blocks, bytes_per_row_alignment);
706
707 let block_rows_in_copy =
708 (size.depth_or_array_layers - 1) * block_rows_per_image + height_blocks;
709 let stage_size = stage_bytes_per_row as u64 * block_rows_in_copy as u64;
710
711 let mut trackers = device.trackers.lock();
712 let encoder = device.pending_writes.activate();
713
714 let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
720 0..1
722 } else {
723 destination.origin.z..destination.origin.z + size.depth_or_array_layers
724 };
725 if dst.initialization_status.mips[destination.mip_level as usize]
726 .check(init_layer_range.clone())
727 .is_some()
728 {
729 if has_copy_partial_init_tracker_coverage(size, destination.mip_level, &dst.desc) {
730 for layer_range in dst.initialization_status.mips[destination.mip_level as usize]
731 .drain(init_layer_range)
732 .collect::<Vec<std::ops::Range<u32>>>()
733 {
734 crate::command::clear_texture(
735 &*texture_guard,
736 id::Valid(destination.texture),
737 TextureInitRange {
738 mip_range: destination.mip_level..(destination.mip_level + 1),
739 layer_range,
740 },
741 encoder,
742 &mut trackers.textures,
743 &device.alignments,
744 &device.zero_buffer,
745 )
746 .map_err(QueueWriteError::from)?;
747 }
748 } else {
749 dst.initialization_status.mips[destination.mip_level as usize]
750 .drain(init_layer_range);
751 }
752 }
753
754 let dst = texture_guard.get(destination.texture).unwrap();
759 let transition = trackers
760 .textures
761 .set_single(
762 dst,
763 destination.texture,
764 selector,
765 hal::TextureUses::COPY_DST,
766 )
767 .ok_or(TransferError::InvalidTexture(destination.texture))?;
768
769 dst.life_guard.use_at(device.active_submission_index + 1);
770
771 let dst_raw = dst
772 .inner
773 .as_raw()
774 .ok_or(TransferError::InvalidTexture(destination.texture))?;
775
776 let bytes_per_row = data_layout
777 .bytes_per_row
778 .unwrap_or(width_blocks * block_size);
779
780 let (staging_buffer, staging_buffer_ptr) =
784 prepare_staging_buffer(&mut device.raw, stage_size, device.instance_flags)?;
785
786 if stage_bytes_per_row == bytes_per_row {
787 profiling::scope!("copy aligned");
788 unsafe {
790 ptr::copy_nonoverlapping(
791 data.as_ptr().offset(data_layout.offset as isize),
792 staging_buffer_ptr,
793 stage_size as usize,
794 );
795 }
796 } else {
797 profiling::scope!("copy chunked");
798 let copy_bytes_per_row = stage_bytes_per_row.min(bytes_per_row) as usize;
800 for layer in 0..size.depth_or_array_layers {
801 let rows_offset = layer * block_rows_per_image;
802 for row in 0..height_blocks {
803 unsafe {
804 ptr::copy_nonoverlapping(
805 data.as_ptr().offset(
806 data_layout.offset as isize
807 + (rows_offset + row) as isize * bytes_per_row as isize,
808 ),
809 staging_buffer_ptr.offset(
810 (rows_offset + row) as isize * stage_bytes_per_row as isize,
811 ),
812 copy_bytes_per_row,
813 );
814 }
815 }
816 }
817 }
818
819 if let Err(e) = unsafe { staging_buffer.flush(&device.raw) } {
820 device.pending_writes.consume(staging_buffer);
821 return Err(e.into());
822 }
823
824 let regions = (0..array_layer_count).map(|rel_array_layer| {
825 let mut texture_base = dst_base.clone();
826 texture_base.array_layer += rel_array_layer;
827 hal::BufferTextureCopy {
828 buffer_layout: wgt::ImageDataLayout {
829 offset: rel_array_layer as u64
830 * block_rows_per_image as u64
831 * stage_bytes_per_row as u64,
832 bytes_per_row: Some(stage_bytes_per_row),
833 rows_per_image: Some(block_rows_per_image),
834 },
835 texture_base,
836 size: hal_copy_size,
837 }
838 });
839 let barrier = hal::BufferBarrier {
840 buffer: &staging_buffer.raw,
841 usage: hal::BufferUses::MAP_WRITE..hal::BufferUses::COPY_SRC,
842 };
843
844 unsafe {
845 encoder.transition_textures(transition.map(|pending| pending.into_hal(dst)));
846 encoder.transition_buffers(iter::once(barrier));
847 encoder.copy_buffer_to_texture(&staging_buffer.raw, dst_raw, regions);
848 }
849
850 device.pending_writes.consume(staging_buffer);
851 device
852 .pending_writes
853 .dst_textures
854 .insert(destination.texture);
855
856 Ok(())
857 }
858
859 #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
860 pub fn queue_copy_external_image_to_texture<A: HalApi>(
861 &self,
862 queue_id: id::QueueId,
863 source: &wgt::ImageCopyExternalImage,
864 destination: crate::command::ImageCopyTextureTagged,
865 size: wgt::Extent3d,
866 ) -> Result<(), QueueWriteError> {
867 profiling::scope!("Queue::copy_external_image_to_texture");
868
869 let hub = A::hub(self);
870 let mut token = Token::root();
871 let (mut device_guard, mut token) = hub.devices.write(&mut token);
872 let device = device_guard
873 .get_mut(queue_id)
874 .map_err(|_| DeviceError::Invalid)?;
875
876 if size.width == 0 || size.height == 0 || size.depth_or_array_layers == 0 {
877 log::trace!("Ignoring write_texture of size 0");
878 return Ok(());
879 }
880
881 let mut needs_flag = false;
882 needs_flag |= matches!(source.source, wgt::ExternalImageSource::OffscreenCanvas(_));
883 needs_flag |= source.origin != wgt::Origin2d::ZERO;
884 needs_flag |= destination.color_space != wgt::PredefinedColorSpace::Srgb;
885 #[allow(clippy::bool_comparison)]
886 if matches!(source.source, wgt::ExternalImageSource::ImageBitmap(_)) {
887 needs_flag |= source.flip_y != false;
888 needs_flag |= destination.premultiplied_alpha != false;
889 }
890
891 if needs_flag {
892 device
893 .require_downlevel_flags(wgt::DownlevelFlags::UNRESTRICTED_EXTERNAL_TEXTURE_COPIES)
894 .map_err(TransferError::from)?;
895 }
896
897 let src_width = source.source.width();
898 let src_height = source.source.height();
899
900 let (mut texture_guard, _) = hub.textures.write(&mut token); let dst = texture_guard.get_mut(destination.texture).unwrap();
902
903 if !conv::is_valid_external_image_copy_dst_texture_format(dst.desc.format) {
904 return Err(
905 TransferError::ExternalCopyToForbiddenTextureFormat(dst.desc.format).into(),
906 );
907 }
908 if dst.desc.dimension != wgt::TextureDimension::D2 {
909 return Err(TransferError::InvalidDimensionExternal(destination.texture).into());
910 }
911 if !dst.desc.usage.contains(wgt::TextureUsages::COPY_DST) {
912 return Err(
913 TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(),
914 );
915 }
916 if !dst
917 .desc
918 .usage
919 .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
920 {
921 return Err(
922 TransferError::MissingRenderAttachmentUsageFlag(destination.texture).into(),
923 );
924 }
925 if dst.desc.sample_count != 1 {
926 return Err(TransferError::InvalidSampleCount {
927 sample_count: dst.desc.sample_count,
928 }
929 .into());
930 }
931
932 if source.origin.x + size.width > src_width {
933 return Err(TransferError::TextureOverrun {
934 start_offset: source.origin.x,
935 end_offset: source.origin.x + size.width,
936 texture_size: src_width,
937 dimension: crate::resource::TextureErrorDimension::X,
938 side: CopySide::Source,
939 }
940 .into());
941 }
942 if source.origin.y + size.height > src_height {
943 return Err(TransferError::TextureOverrun {
944 start_offset: source.origin.y,
945 end_offset: source.origin.y + size.height,
946 texture_size: src_height,
947 dimension: crate::resource::TextureErrorDimension::Y,
948 side: CopySide::Source,
949 }
950 .into());
951 }
952 if size.depth_or_array_layers != 1 {
953 return Err(TransferError::TextureOverrun {
954 start_offset: 0,
955 end_offset: size.depth_or_array_layers,
956 texture_size: 1,
957 dimension: crate::resource::TextureErrorDimension::Z,
958 side: CopySide::Source,
959 }
960 .into());
961 }
962
963 let (hal_copy_size, _) = validate_texture_copy_range(
966 &destination.to_untagged(),
967 &dst.desc,
968 CopySide::Destination,
969 &size,
970 )?;
971
972 let (selector, dst_base) =
973 extract_texture_selector(&destination.to_untagged(), &size, dst)?;
974
975 let mut trackers = device.trackers.lock();
976 let encoder = device.pending_writes.activate();
977
978 let init_layer_range = if dst.desc.dimension == wgt::TextureDimension::D3 {
984 0..1
986 } else {
987 destination.origin.z..destination.origin.z + size.depth_or_array_layers
988 };
989 if dst.initialization_status.mips[destination.mip_level as usize]
990 .check(init_layer_range.clone())
991 .is_some()
992 {
993 if has_copy_partial_init_tracker_coverage(&size, destination.mip_level, &dst.desc) {
994 for layer_range in dst.initialization_status.mips[destination.mip_level as usize]
995 .drain(init_layer_range)
996 .collect::<Vec<std::ops::Range<u32>>>()
997 {
998 crate::command::clear_texture(
999 &*texture_guard,
1000 id::Valid(destination.texture),
1001 TextureInitRange {
1002 mip_range: destination.mip_level..(destination.mip_level + 1),
1003 layer_range,
1004 },
1005 encoder,
1006 &mut trackers.textures,
1007 &device.alignments,
1008 &device.zero_buffer,
1009 )
1010 .map_err(QueueWriteError::from)?;
1011 }
1012 } else {
1013 dst.initialization_status.mips[destination.mip_level as usize]
1014 .drain(init_layer_range);
1015 }
1016 }
1017
1018 let dst = texture_guard.get(destination.texture).unwrap();
1019
1020 let transitions = trackers
1021 .textures
1022 .set_single(
1023 dst,
1024 destination.texture,
1025 selector,
1026 hal::TextureUses::COPY_DST,
1027 )
1028 .ok_or(TransferError::InvalidTexture(destination.texture))?;
1029
1030 dst.life_guard.use_at(device.active_submission_index + 1);
1031
1032 let dst_raw = dst
1033 .inner
1034 .as_raw()
1035 .ok_or(TransferError::InvalidTexture(destination.texture))?;
1036
1037 let regions = hal::TextureCopy {
1038 src_base: hal::TextureCopyBase {
1039 mip_level: 0,
1040 array_layer: 0,
1041 origin: source.origin.to_3d(0),
1042 aspect: hal::FormatAspects::COLOR,
1043 },
1044 dst_base,
1045 size: hal_copy_size,
1046 };
1047
1048 unsafe {
1049 encoder.transition_textures(transitions.map(|pending| pending.into_hal(dst)));
1050 encoder.copy_external_image_to_texture(
1051 source,
1052 dst_raw,
1053 destination.premultiplied_alpha,
1054 iter::once(regions),
1055 );
1056 }
1057
1058 Ok(())
1059 }
1060
1061 pub fn queue_submit<A: HalApi>(
1062 &self,
1063 queue_id: id::QueueId,
1064 command_buffer_ids: &[id::CommandBufferId],
1065 ) -> Result<WrappedSubmissionIndex, QueueSubmitError> {
1066 profiling::scope!("Queue::submit");
1067 log::trace!("Queue::submit {queue_id:?}");
1068
1069 let (submit_index, callbacks) = {
1070 let hub = A::hub(self);
1071 let mut token = Token::root();
1072
1073 let (mut device_guard, mut token) = hub.devices.write(&mut token);
1074 let device = device_guard
1075 .get_mut(queue_id)
1076 .map_err(|_| DeviceError::Invalid)?;
1077 device.temp_suspected.clear();
1078 device.active_submission_index += 1;
1079 let submit_index = device.active_submission_index;
1080 let mut active_executions = Vec::new();
1081 let mut used_surface_textures = track::TextureUsageScope::new();
1082
1083 {
1084 let (mut command_buffer_guard, mut token) = hub.command_buffers.write(&mut token);
1085
1086 if !command_buffer_ids.is_empty() {
1087 profiling::scope!("prepare");
1088
1089 let (render_bundle_guard, mut token) = hub.render_bundles.read(&mut token);
1090 let (_, mut token) = hub.pipeline_layouts.read(&mut token);
1091 let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token);
1092 let (compute_pipe_guard, mut token) = hub.compute_pipelines.read(&mut token);
1093 let (render_pipe_guard, mut token) = hub.render_pipelines.read(&mut token);
1094 let (mut buffer_guard, mut token) = hub.buffers.write(&mut token);
1095 let (mut texture_guard, mut token) = hub.textures.write(&mut token);
1096 let (texture_view_guard, mut token) = hub.texture_views.read(&mut token);
1097 let (sampler_guard, mut token) = hub.samplers.read(&mut token);
1098 let (query_set_guard, _) = hub.query_sets.read(&mut token);
1099
1100 let mut trackers = device.trackers.lock();
1102
1103 for &cmb_id in command_buffer_ids {
1109 used_surface_textures.set_size(texture_guard.len());
1112
1113 #[allow(unused_mut)]
1114 let mut cmdbuf = match hub
1115 .command_buffers
1116 .unregister_locked(cmb_id, &mut *command_buffer_guard)
1117 {
1118 Some(cmdbuf) => cmdbuf,
1119 None => continue,
1120 };
1121
1122 if cmdbuf.device_id.value.0 != queue_id {
1123 return Err(DeviceError::WrongDevice.into());
1124 }
1125
1126 #[cfg(feature = "trace")]
1127 if let Some(ref trace) = device.trace {
1128 trace.lock().add(Action::Submit(
1129 submit_index,
1130 cmdbuf.commands.take().unwrap(),
1131 ));
1132 }
1133 if !cmdbuf.is_finished() {
1134 device.destroy_command_buffer(cmdbuf);
1135 continue;
1136 }
1137
1138 for id in cmdbuf.trackers.buffers.used() {
1143 let buffer = &mut buffer_guard[id];
1144 let raw_buf = match buffer.raw {
1145 Some(ref raw) => raw,
1146 None => {
1147 return Err(QueueSubmitError::DestroyedBuffer(id.0));
1148 }
1149 };
1150 if !buffer.life_guard.use_at(submit_index) {
1151 if let BufferMapState::Active { .. } = buffer.map_state {
1152 log::warn!("Dropped buffer has a pending mapping.");
1153 unsafe { device.raw.unmap_buffer(raw_buf) }
1154 .map_err(DeviceError::from)?;
1155 }
1156 device.temp_suspected.buffers.push(id);
1157 } else {
1158 match buffer.map_state {
1159 BufferMapState::Idle => (),
1160 _ => return Err(QueueSubmitError::BufferStillMapped(id.0)),
1161 }
1162 }
1163 }
1164 for id in cmdbuf.trackers.textures.used() {
1165 let texture = &mut texture_guard[id];
1166 let should_extend = match texture.inner {
1167 TextureInner::Native { raw: None } => {
1168 return Err(QueueSubmitError::DestroyedTexture(id.0));
1169 }
1170 TextureInner::Native { raw: Some(_) } => false,
1171 TextureInner::Surface {
1172 ref mut has_work, ..
1173 } => {
1174 *has_work = true;
1175 true
1176 }
1177 };
1178 if !texture.life_guard.use_at(submit_index) {
1179 device.temp_suspected.textures.push(id);
1180 }
1181 if should_extend {
1182 unsafe {
1183 let ref_count = cmdbuf.trackers.textures.get_ref_count(id);
1184 used_surface_textures
1185 .merge_single(
1186 &*texture_guard,
1187 id,
1188 None,
1189 ref_count,
1190 hal::TextureUses::PRESENT,
1191 )
1192 .unwrap();
1193 };
1194 }
1195 }
1196 for id in cmdbuf.trackers.views.used() {
1197 if !texture_view_guard[id].life_guard.use_at(submit_index) {
1198 device.temp_suspected.texture_views.push(id);
1199 }
1200 }
1201 for id in cmdbuf.trackers.bind_groups.used() {
1202 let bg = &bind_group_guard[id];
1203 if !bg.life_guard.use_at(submit_index) {
1204 device.temp_suspected.bind_groups.push(id);
1205 }
1206 for sub_id in bg.used.views.used() {
1210 texture_view_guard[sub_id].life_guard.use_at(submit_index);
1211 }
1212 for sub_id in bg.used.samplers.used() {
1213 sampler_guard[sub_id].life_guard.use_at(submit_index);
1214 }
1215 }
1216 for id in cmdbuf.trackers.compute_pipelines.used() {
1218 if !compute_pipe_guard[id].life_guard.use_at(submit_index) {
1219 device.temp_suspected.compute_pipelines.push(id);
1220 }
1221 }
1222 for id in cmdbuf.trackers.render_pipelines.used() {
1223 if !render_pipe_guard[id].life_guard.use_at(submit_index) {
1224 device.temp_suspected.render_pipelines.push(id);
1225 }
1226 }
1227 for id in cmdbuf.trackers.query_sets.used() {
1228 if !query_set_guard[id].life_guard.use_at(submit_index) {
1229 device.temp_suspected.query_sets.push(id);
1230 }
1231 }
1232 for id in cmdbuf.trackers.bundles.used() {
1233 let bundle = &render_bundle_guard[id];
1234 if !bundle.life_guard.use_at(submit_index) {
1235 device.temp_suspected.render_bundles.push(id);
1236 }
1237 for sub_id in bundle.used.render_pipelines.used() {
1241 render_pipe_guard[sub_id].life_guard.use_at(submit_index);
1242 }
1243 for sub_id in bundle.used.query_sets.used() {
1244 query_set_guard[sub_id].life_guard.use_at(submit_index);
1245 }
1246 }
1247
1248 let mut baked = cmdbuf.into_baked();
1249 unsafe {
1251 baked
1252 .encoder
1253 .begin_encoding(hal_label(
1254 Some("(wgpu internal) Transit"),
1255 device.instance_flags,
1256 ))
1257 .map_err(DeviceError::from)?
1258 };
1259 log::trace!("Stitching command buffer {:?} before submission", cmb_id);
1260 baked
1261 .initialize_buffer_memory(&mut *trackers, &mut *buffer_guard)
1262 .map_err(|err| QueueSubmitError::DestroyedBuffer(err.0))?;
1263 baked
1264 .initialize_texture_memory(&mut *trackers, &mut *texture_guard, device)
1265 .map_err(|err| QueueSubmitError::DestroyedTexture(err.0))?;
1266 CommandBuffer::insert_barriers_from_tracker(
1269 &mut baked.encoder,
1270 &mut *trackers,
1271 &baked.trackers,
1272 &*buffer_guard,
1273 &*texture_guard,
1274 );
1275
1276 let transit = unsafe { baked.encoder.end_encoding().unwrap() };
1277 baked.list.insert(0, transit);
1278
1279 if !used_surface_textures.is_empty() {
1283 unsafe {
1284 baked
1285 .encoder
1286 .begin_encoding(hal_label(
1287 Some("(wgpu internal) Present"),
1288 device.instance_flags,
1289 ))
1290 .map_err(DeviceError::from)?
1291 };
1292 trackers
1293 .textures
1294 .set_from_usage_scope(&*texture_guard, &used_surface_textures);
1295 let texture_barriers = trackers.textures.drain().map(|pending| {
1296 let tex = unsafe { texture_guard.get_unchecked(pending.id) };
1297 pending.into_hal(tex)
1298 });
1299 let present = unsafe {
1300 baked.encoder.transition_textures(texture_barriers);
1301 baked.encoder.end_encoding().unwrap()
1302 };
1303 baked.list.push(present);
1304 used_surface_textures = track::TextureUsageScope::new();
1305 }
1306
1307 active_executions.push(EncoderInFlight {
1309 raw: baked.encoder,
1310 cmd_buffers: baked.list,
1311 });
1312 }
1313
1314 log::trace!("Device after submission {}", submit_index);
1315 }
1316
1317 let super::Device {
1318 ref mut pending_writes,
1319 ref mut queue,
1320 ref mut fence,
1321 ..
1322 } = *device;
1323
1324 {
1325 let (_, mut token) = hub.buffers.read(&mut token); let (mut texture_guard, _) = hub.textures.write(&mut token);
1335
1336 used_surface_textures.set_size(texture_guard.len());
1337
1338 for &id in pending_writes.dst_textures.iter() {
1339 let texture = texture_guard.get_mut(id).unwrap();
1340 match texture.inner {
1341 TextureInner::Native { raw: None } => {
1342 return Err(QueueSubmitError::DestroyedTexture(id));
1343 }
1344 TextureInner::Native { raw: Some(_) } => {}
1345 TextureInner::Surface {
1346 ref mut has_work, ..
1347 } => {
1348 *has_work = true;
1349 let ref_count = texture.life_guard.add_ref();
1350 unsafe {
1351 used_surface_textures
1352 .merge_single(
1353 &*texture_guard,
1354 id::Valid(id),
1355 None,
1356 &ref_count,
1357 hal::TextureUses::PRESENT,
1358 )
1359 .unwrap()
1360 };
1361 }
1362 }
1363 }
1364
1365 if !used_surface_textures.is_empty() {
1366 let mut trackers = device.trackers.lock();
1367
1368 trackers
1369 .textures
1370 .set_from_usage_scope(&*texture_guard, &used_surface_textures);
1371 let texture_barriers = trackers.textures.drain().map(|pending| {
1372 let tex = unsafe { texture_guard.get_unchecked(pending.id) };
1373 pending.into_hal(tex)
1374 });
1375
1376 unsafe {
1377 pending_writes
1378 .command_encoder
1379 .transition_textures(texture_barriers);
1380 };
1381 }
1382 }
1383
1384 let refs = pending_writes
1385 .pre_submit()
1386 .into_iter()
1387 .chain(
1388 active_executions
1389 .iter()
1390 .flat_map(|pool_execution| pool_execution.cmd_buffers.iter()),
1391 )
1392 .collect::<Vec<_>>();
1393 unsafe {
1394 queue
1395 .submit(&refs, Some((fence, submit_index)))
1396 .map_err(DeviceError::from)?;
1397 }
1398 }
1399
1400 profiling::scope!("cleanup");
1401 if let Some(pending_execution) = device.pending_writes.post_submit(
1402 &device.command_allocator,
1403 &device.raw,
1404 &device.queue,
1405 ) {
1406 active_executions.push(pending_execution);
1407 }
1408
1409 let mut pending_write_resources = mem::take(&mut device.pending_writes.temp_resources);
1411 device.lock_life(&mut token).track_submission(
1412 submit_index,
1413 pending_write_resources.drain(..),
1414 active_executions,
1415 );
1416
1417 let (closures, _) = match device.maintain(hub, wgt::Maintain::Poll, &mut token) {
1420 Ok(closures) => closures,
1421 Err(WaitIdleError::Device(err)) => return Err(QueueSubmitError::Queue(err)),
1422 Err(WaitIdleError::StuckGpu) => return Err(QueueSubmitError::StuckGpu),
1423 Err(WaitIdleError::WrongSubmissionIndex(..)) => unreachable!(),
1424 };
1425
1426 device.pending_writes.temp_resources = pending_write_resources;
1429 device.temp_suspected.clear();
1430 device.lock_life(&mut token).post_submit();
1431
1432 (submit_index, closures)
1433 };
1434
1435 callbacks.fire();
1437
1438 Ok(WrappedSubmissionIndex {
1439 queue_id,
1440 index: submit_index,
1441 })
1442 }
1443
1444 pub fn queue_get_timestamp_period<A: HalApi>(
1445 &self,
1446 queue_id: id::QueueId,
1447 ) -> Result<f32, InvalidQueue> {
1448 let hub = A::hub(self);
1449 let mut token = Token::root();
1450 let (device_guard, _) = hub.devices.read(&mut token);
1451 match device_guard.get(queue_id) {
1452 Ok(device) => Ok(unsafe { device.queue.get_timestamp_period() }),
1453 Err(_) => Err(InvalidQueue),
1454 }
1455 }
1456
1457 pub fn queue_on_submitted_work_done<A: HalApi>(
1458 &self,
1459 queue_id: id::QueueId,
1460 closure: SubmittedWorkDoneClosure,
1461 ) -> Result<(), InvalidQueue> {
1462 log::trace!("Queue::on_submitted_work_done {queue_id:?}");
1463
1464 let hub = A::hub(self);
1466 let mut token = Token::root();
1467 let (device_guard, mut token) = hub.devices.read(&mut token);
1468 match device_guard.get(queue_id) {
1469 Ok(device) => device.lock_life(&mut token).add_work_done_closure(closure),
1470 Err(_) => return Err(InvalidQueue),
1471 }
1472 Ok(())
1473 }
1474}