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