1#[cfg(feature = "trace")]
2use crate::device::trace::Command as TraceCommand;
3use crate::{
4 api_log,
5 command::{clear_texture, CommandBuffer, CommandEncoderError},
6 conv,
7 device::{Device, DeviceError, MissingDownlevelFlags},
8 error::{ErrorFormatter, PrettyError},
9 global::Global,
10 hal_api::HalApi,
11 id::{BufferId, CommandEncoderId, DeviceId, TextureId},
12 init_tracker::{
13 has_copy_partial_init_tracker_coverage, MemoryInitKind, TextureInitRange,
14 TextureInitTrackerAction,
15 },
16 resource::{Resource, Texture, TextureErrorDimension},
17 snatch::SnatchGuard,
18 track::{TextureSelector, Tracker},
19};
20
21use arrayvec::ArrayVec;
22use hal::CommandEncoder as _;
23use thiserror::Error;
24use wgt::{BufferAddress, BufferUsages, Extent3d, TextureUsages};
25
26use std::{iter, sync::Arc};
27
28use super::{memory_init::CommandBufferTextureMemoryActions, ClearError, CommandEncoder};
29
30pub type ImageCopyBuffer = wgt::ImageCopyBuffer<BufferId>;
31pub type ImageCopyTexture = wgt::ImageCopyTexture<TextureId>;
32pub type ImageCopyTextureTagged = wgt::ImageCopyTextureTagged<TextureId>;
33
34#[derive(Clone, Copy, Debug)]
35pub enum CopySide {
36 Source,
37 Destination,
38}
39
40#[derive(Clone, Debug, Error)]
42#[non_exhaustive]
43pub enum TransferError {
44 #[error("Device {0:?} is invalid")]
45 InvalidDevice(DeviceId),
46 #[error("Buffer {0:?} is invalid or destroyed")]
47 InvalidBuffer(BufferId),
48 #[error("Texture {0:?} is invalid or destroyed")]
49 InvalidTexture(TextureId),
50 #[error("Source and destination cannot be the same buffer")]
51 SameSourceDestinationBuffer,
52 #[error("Source buffer/texture is missing the `COPY_SRC` usage flag")]
53 MissingCopySrcUsageFlag,
54 #[error("Destination buffer/texture is missing the `COPY_DST` usage flag")]
55 MissingCopyDstUsageFlag(Option<BufferId>, Option<TextureId>),
56 #[error("Destination texture is missing the `RENDER_ATTACHMENT` usage flag")]
57 MissingRenderAttachmentUsageFlag(TextureId),
58 #[error("Copy of {start_offset}..{end_offset} would end up overrunning the bounds of the {side:?} buffer of size {buffer_size}")]
59 BufferOverrun {
60 start_offset: BufferAddress,
61 end_offset: BufferAddress,
62 buffer_size: BufferAddress,
63 side: CopySide,
64 },
65 #[error("Copy of {dimension:?} {start_offset}..{end_offset} would end up overrunning the bounds of the {side:?} texture of {dimension:?} size {texture_size}")]
66 TextureOverrun {
67 start_offset: u32,
68 end_offset: u32,
69 texture_size: u32,
70 dimension: TextureErrorDimension,
71 side: CopySide,
72 },
73 #[error("Unable to select texture aspect {aspect:?} from format {format:?}")]
74 InvalidTextureAspect {
75 format: wgt::TextureFormat,
76 aspect: wgt::TextureAspect,
77 },
78 #[error("Unable to select texture mip level {level} out of {total}")]
79 InvalidTextureMipLevel { level: u32, total: u32 },
80 #[error("Texture dimension must be 2D when copying from an external texture")]
81 InvalidDimensionExternal(TextureId),
82 #[error("Buffer offset {0} is not aligned to block size or `COPY_BUFFER_ALIGNMENT`")]
83 UnalignedBufferOffset(BufferAddress),
84 #[error("Copy size {0} does not respect `COPY_BUFFER_ALIGNMENT`")]
85 UnalignedCopySize(BufferAddress),
86 #[error("Copy width is not a multiple of block width")]
87 UnalignedCopyWidth,
88 #[error("Copy height is not a multiple of block height")]
89 UnalignedCopyHeight,
90 #[error("Copy origin's x component is not a multiple of block width")]
91 UnalignedCopyOriginX,
92 #[error("Copy origin's y component is not a multiple of block height")]
93 UnalignedCopyOriginY,
94 #[error("Bytes per row does not respect `COPY_BYTES_PER_ROW_ALIGNMENT`")]
95 UnalignedBytesPerRow,
96 #[error("Number of bytes per row needs to be specified since more than one row is copied")]
97 UnspecifiedBytesPerRow,
98 #[error("Number of rows per image needs to be specified since more than one image is copied")]
99 UnspecifiedRowsPerImage,
100 #[error("Number of bytes per row is less than the number of bytes in a complete row")]
101 InvalidBytesPerRow,
102 #[error("Image is 1D and the copy height and depth are not both set to 1")]
103 InvalidCopySize,
104 #[error("Number of rows per image is invalid")]
105 InvalidRowsPerImage,
106 #[error("Copy source aspects must refer to all aspects of the source texture format")]
107 CopySrcMissingAspects,
108 #[error(
109 "Copy destination aspects must refer to all aspects of the destination texture format"
110 )]
111 CopyDstMissingAspects,
112 #[error("Copy aspect must refer to a single aspect of texture format")]
113 CopyAspectNotOne,
114 #[error("Copying from textures with format {format:?} and aspect {aspect:?} is forbidden")]
115 CopyFromForbiddenTextureFormat {
116 format: wgt::TextureFormat,
117 aspect: wgt::TextureAspect,
118 },
119 #[error("Copying to textures with format {format:?} and aspect {aspect:?} is forbidden")]
120 CopyToForbiddenTextureFormat {
121 format: wgt::TextureFormat,
122 aspect: wgt::TextureAspect,
123 },
124 #[error(
125 "Copying to textures with format {0:?} is forbidden when copying from external texture"
126 )]
127 ExternalCopyToForbiddenTextureFormat(wgt::TextureFormat),
128 #[error("The entire texture must be copied when copying from depth texture")]
129 InvalidDepthTextureExtent,
130 #[error(
131 "Source format ({src_format:?}) and destination format ({dst_format:?}) are not copy-compatible (they may only differ in srgb-ness)"
132 )]
133 TextureFormatsNotCopyCompatible {
134 src_format: wgt::TextureFormat,
135 dst_format: wgt::TextureFormat,
136 },
137 #[error(transparent)]
138 MemoryInitFailure(#[from] ClearError),
139 #[error("Cannot encode this copy because of a missing downelevel flag")]
140 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
141 #[error("Source texture sample count must be 1, got {sample_count}")]
142 InvalidSampleCount { sample_count: u32 },
143 #[error("Requested mip level {requested} does no exist (count: {count})")]
144 InvalidMipLevel { requested: u32, count: u32 },
145}
146
147impl PrettyError for TransferError {
148 fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
149 fmt.error(self);
150 match *self {
151 Self::InvalidBuffer(id) => {
152 fmt.buffer_label(&id);
153 }
154 Self::InvalidTexture(id) => {
155 fmt.texture_label(&id);
156 }
157 Self::MissingCopyDstUsageFlag(buf_opt, tex_opt) => {
168 if let Some(buf) = buf_opt {
169 fmt.buffer_label_with_key(&buf, "destination");
170 }
171 if let Some(tex) = tex_opt {
172 fmt.texture_label_with_key(&tex, "destination");
173 }
174 }
175 _ => {}
176 };
177 }
178}
179#[derive(Clone, Debug, Error)]
181#[non_exhaustive]
182pub enum CopyError {
183 #[error(transparent)]
184 Encoder(#[from] CommandEncoderError),
185 #[error("Copy error")]
186 Transfer(#[from] TransferError),
187}
188
189impl From<DeviceError> for CopyError {
190 fn from(err: DeviceError) -> Self {
191 CopyError::Encoder(CommandEncoderError::Device(err))
192 }
193}
194
195pub(crate) fn extract_texture_selector<A: HalApi>(
196 copy_texture: &ImageCopyTexture,
197 copy_size: &Extent3d,
198 texture: &Texture<A>,
199) -> Result<(TextureSelector, hal::TextureCopyBase), TransferError> {
200 let format = texture.desc.format;
201 let copy_aspect = hal::FormatAspects::new(format, copy_texture.aspect);
202 if copy_aspect.is_empty() {
203 return Err(TransferError::InvalidTextureAspect {
204 format,
205 aspect: copy_texture.aspect,
206 });
207 }
208
209 let (layers, origin_z) = match texture.desc.dimension {
210 wgt::TextureDimension::D1 => (0..1, 0),
211 wgt::TextureDimension::D2 => (
212 copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers,
213 0,
214 ),
215 wgt::TextureDimension::D3 => (0..1, copy_texture.origin.z),
216 };
217 let base = hal::TextureCopyBase {
218 origin: wgt::Origin3d {
219 x: copy_texture.origin.x,
220 y: copy_texture.origin.y,
221 z: origin_z,
222 },
223 array_layer: layers.start,
225 mip_level: copy_texture.mip_level,
226 aspect: copy_aspect,
227 };
228 let selector = TextureSelector {
229 mips: copy_texture.mip_level..copy_texture.mip_level + 1,
230 layers,
231 };
232
233 Ok((selector, base))
234}
235
236pub(crate) fn validate_linear_texture_data(
246 layout: &wgt::ImageDataLayout,
247 format: wgt::TextureFormat,
248 aspect: wgt::TextureAspect,
249 buffer_size: BufferAddress,
250 buffer_side: CopySide,
251 copy_size: &Extent3d,
252 need_copy_aligned_rows: bool,
253) -> Result<(BufferAddress, BufferAddress), TransferError> {
254 let copy_width = copy_size.width as BufferAddress;
259 let copy_height = copy_size.height as BufferAddress;
260 let copy_depth = copy_size.depth_or_array_layers as BufferAddress;
261
262 let offset = layout.offset;
263
264 let block_size = format.block_copy_size(Some(aspect)).unwrap() as BufferAddress;
265 let (block_width, block_height) = format.block_dimensions();
266 let block_width = block_width as BufferAddress;
267 let block_height = block_height as BufferAddress;
268
269 if copy_width % block_width != 0 {
270 return Err(TransferError::UnalignedCopyWidth);
271 }
272 if copy_height % block_height != 0 {
273 return Err(TransferError::UnalignedCopyHeight);
274 }
275
276 let width_in_blocks = copy_width / block_width;
277 let height_in_blocks = copy_height / block_height;
278
279 let bytes_in_last_row = width_in_blocks * block_size;
280
281 let bytes_per_row = if let Some(bytes_per_row) = layout.bytes_per_row {
282 let bytes_per_row = bytes_per_row as BufferAddress;
283 if bytes_per_row < bytes_in_last_row {
284 return Err(TransferError::InvalidBytesPerRow);
285 }
286 bytes_per_row
287 } else {
288 if copy_depth > 1 || height_in_blocks > 1 {
289 return Err(TransferError::UnspecifiedBytesPerRow);
290 }
291 0
292 };
293 let block_rows_per_image = if let Some(rows_per_image) = layout.rows_per_image {
294 let rows_per_image = rows_per_image as BufferAddress;
295 if rows_per_image < height_in_blocks {
296 return Err(TransferError::InvalidRowsPerImage);
297 }
298 rows_per_image
299 } else {
300 if copy_depth > 1 {
301 return Err(TransferError::UnspecifiedRowsPerImage);
302 }
303 0
304 };
305
306 if need_copy_aligned_rows {
307 let bytes_per_row_alignment = wgt::COPY_BYTES_PER_ROW_ALIGNMENT as BufferAddress;
308
309 let mut offset_alignment = block_size;
310 if format.is_depth_stencil_format() {
311 offset_alignment = 4
312 }
313 if offset % offset_alignment != 0 {
314 return Err(TransferError::UnalignedBufferOffset(offset));
315 }
316
317 if bytes_per_row % bytes_per_row_alignment != 0 {
318 return Err(TransferError::UnalignedBytesPerRow);
319 }
320 }
321
322 let bytes_per_image = bytes_per_row * block_rows_per_image;
323
324 let required_bytes_in_copy = if copy_depth == 0 {
325 0
326 } else {
327 let mut required_bytes_in_copy = bytes_per_image * (copy_depth - 1);
328 if height_in_blocks > 0 {
329 required_bytes_in_copy += bytes_per_row * (height_in_blocks - 1) + bytes_in_last_row;
330 }
331 required_bytes_in_copy
332 };
333
334 if offset + required_bytes_in_copy > buffer_size {
335 return Err(TransferError::BufferOverrun {
336 start_offset: offset,
337 end_offset: offset + required_bytes_in_copy,
338 buffer_size,
339 side: buffer_side,
340 });
341 }
342
343 Ok((required_bytes_in_copy, bytes_per_image))
344}
345
346pub(crate) fn validate_texture_copy_range(
354 texture_copy_view: &ImageCopyTexture,
355 desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
356 texture_side: CopySide,
357 copy_size: &Extent3d,
358) -> Result<(hal::CopyExtent, u32), TransferError> {
359 let (block_width, block_height) = desc.format.block_dimensions();
360
361 let extent_virtual = desc.mip_level_size(texture_copy_view.mip_level).ok_or(
362 TransferError::InvalidTextureMipLevel {
363 level: texture_copy_view.mip_level,
364 total: desc.mip_level_count,
365 },
366 )?;
367 let extent = extent_virtual.physical_size(desc.format);
369
370 if desc.format.is_depth_stencil_format() && *copy_size != extent {
371 return Err(TransferError::InvalidDepthTextureExtent);
372 }
373
374 fn check_dimension(
377 dimension: TextureErrorDimension,
378 side: CopySide,
379 start_offset: u32,
380 size: u32,
381 texture_size: u32,
382 ) -> Result<(), TransferError> {
383 if start_offset <= texture_size && size <= texture_size - start_offset {
386 Ok(())
387 } else {
388 Err(TransferError::TextureOverrun {
389 start_offset,
390 end_offset: start_offset.wrapping_add(size),
391 texture_size,
392 dimension,
393 side,
394 })
395 }
396 }
397
398 check_dimension(
399 TextureErrorDimension::X,
400 texture_side,
401 texture_copy_view.origin.x,
402 copy_size.width,
403 extent.width,
404 )?;
405 check_dimension(
406 TextureErrorDimension::Y,
407 texture_side,
408 texture_copy_view.origin.y,
409 copy_size.height,
410 extent.height,
411 )?;
412 check_dimension(
413 TextureErrorDimension::Z,
414 texture_side,
415 texture_copy_view.origin.z,
416 copy_size.depth_or_array_layers,
417 extent.depth_or_array_layers,
418 )?;
419
420 if texture_copy_view.origin.x % block_width != 0 {
421 return Err(TransferError::UnalignedCopyOriginX);
422 }
423 if texture_copy_view.origin.y % block_height != 0 {
424 return Err(TransferError::UnalignedCopyOriginY);
425 }
426 if copy_size.width % block_width != 0 {
427 return Err(TransferError::UnalignedCopyWidth);
428 }
429 if copy_size.height % block_height != 0 {
430 return Err(TransferError::UnalignedCopyHeight);
431 }
432
433 let (depth, array_layer_count) = match desc.dimension {
434 wgt::TextureDimension::D1 => (1, 1),
435 wgt::TextureDimension::D2 => (1, copy_size.depth_or_array_layers),
436 wgt::TextureDimension::D3 => (copy_size.depth_or_array_layers, 1),
437 };
438
439 let copy_extent = hal::CopyExtent {
440 width: copy_size.width,
441 height: copy_size.height,
442 depth,
443 };
444 Ok((copy_extent, array_layer_count))
445}
446
447fn handle_texture_init<A: HalApi>(
448 init_kind: MemoryInitKind,
449 encoder: &mut CommandEncoder<A>,
450 trackers: &mut Tracker<A>,
451 texture_memory_actions: &mut CommandBufferTextureMemoryActions<A>,
452 device: &Device<A>,
453 copy_texture: &ImageCopyTexture,
454 copy_size: &Extent3d,
455 texture: &Arc<Texture<A>>,
456 snatch_guard: &SnatchGuard<'_>,
457) -> Result<(), ClearError> {
458 let init_action = TextureInitTrackerAction {
459 texture: texture.clone(),
460 range: TextureInitRange {
461 mip_range: copy_texture.mip_level..copy_texture.mip_level + 1,
462 layer_range: copy_texture.origin.z
463 ..(copy_texture.origin.z + copy_size.depth_or_array_layers),
464 },
465 kind: init_kind,
466 };
467
468 let immediate_inits = texture_memory_actions.register_init_action(&{ init_action });
470
471 if !immediate_inits.is_empty() {
473 let cmd_buf_raw = encoder.open()?;
474 for init in immediate_inits {
475 clear_texture(
476 &init.texture,
477 TextureInitRange {
478 mip_range: init.mip_level..(init.mip_level + 1),
479 layer_range: init.layer..(init.layer + 1),
480 },
481 cmd_buf_raw,
482 &mut trackers.textures,
483 &device.alignments,
484 device.zero_buffer.as_ref().unwrap(),
485 snatch_guard,
486 )?;
487 }
488 }
489
490 Ok(())
491}
492
493fn handle_src_texture_init<A: HalApi>(
498 encoder: &mut CommandEncoder<A>,
499 trackers: &mut Tracker<A>,
500 texture_memory_actions: &mut CommandBufferTextureMemoryActions<A>,
501 device: &Device<A>,
502 source: &ImageCopyTexture,
503 copy_size: &Extent3d,
504 texture: &Arc<Texture<A>>,
505 snatch_guard: &SnatchGuard<'_>,
506) -> Result<(), TransferError> {
507 handle_texture_init(
508 MemoryInitKind::NeedsInitializedMemory,
509 encoder,
510 trackers,
511 texture_memory_actions,
512 device,
513 source,
514 copy_size,
515 texture,
516 snatch_guard,
517 )?;
518 Ok(())
519}
520
521fn handle_dst_texture_init<A: HalApi>(
526 encoder: &mut CommandEncoder<A>,
527 trackers: &mut Tracker<A>,
528 texture_memory_actions: &mut CommandBufferTextureMemoryActions<A>,
529 device: &Device<A>,
530 destination: &ImageCopyTexture,
531 copy_size: &Extent3d,
532 texture: &Arc<Texture<A>>,
533 snatch_guard: &SnatchGuard<'_>,
534) -> Result<(), TransferError> {
535 let dst_init_kind = if has_copy_partial_init_tracker_coverage(
540 copy_size,
541 destination.mip_level,
542 &texture.desc,
543 ) {
544 MemoryInitKind::NeedsInitializedMemory
545 } else {
546 MemoryInitKind::ImplicitlyInitialized
547 };
548
549 handle_texture_init(
550 dst_init_kind,
551 encoder,
552 trackers,
553 texture_memory_actions,
554 device,
555 destination,
556 copy_size,
557 texture,
558 snatch_guard,
559 )?;
560 Ok(())
561}
562
563impl Global {
564 pub fn command_encoder_copy_buffer_to_buffer<A: HalApi>(
565 &self,
566 command_encoder_id: CommandEncoderId,
567 source: BufferId,
568 source_offset: BufferAddress,
569 destination: BufferId,
570 destination_offset: BufferAddress,
571 size: BufferAddress,
572 ) -> Result<(), CopyError> {
573 profiling::scope!("CommandEncoder::copy_buffer_to_buffer");
574 api_log!(
575 "CommandEncoder::copy_buffer_to_buffer {source:?} -> {destination:?} {size:?}bytes"
576 );
577
578 if source == destination {
579 return Err(TransferError::SameSourceDestinationBuffer.into());
580 }
581 let hub = A::hub(self);
582
583 let cmd_buf = CommandBuffer::get_encoder(hub, command_encoder_id)?;
584 let mut cmd_buf_data = cmd_buf.data.lock();
585 let cmd_buf_data = cmd_buf_data.as_mut().unwrap();
586
587 let device = &cmd_buf.device;
588 if !device.is_valid() {
589 return Err(TransferError::InvalidDevice(cmd_buf.device.as_info().id()).into());
590 }
591
592 #[cfg(feature = "trace")]
593 if let Some(ref mut list) = cmd_buf_data.commands {
594 list.push(TraceCommand::CopyBufferToBuffer {
595 src: source,
596 src_offset: source_offset,
597 dst: destination,
598 dst_offset: destination_offset,
599 size,
600 });
601 }
602
603 let snatch_guard = device.snatchable_lock.read();
604
605 let (src_buffer, src_pending) = {
606 let buffer_guard = hub.buffers.read();
607 let src_buffer = buffer_guard
608 .get(source)
609 .map_err(|_| TransferError::InvalidBuffer(source))?;
610
611 if src_buffer.device.as_info().id() != device.as_info().id() {
612 return Err(DeviceError::WrongDevice.into());
613 }
614
615 cmd_buf_data
616 .trackers
617 .buffers
618 .set_single(src_buffer, hal::BufferUses::COPY_SRC)
619 .ok_or(TransferError::InvalidBuffer(source))?
620 };
621 let src_raw = src_buffer
622 .raw
623 .get(&snatch_guard)
624 .ok_or(TransferError::InvalidBuffer(source))?;
625 if !src_buffer.usage.contains(BufferUsages::COPY_SRC) {
626 return Err(TransferError::MissingCopySrcUsageFlag.into());
627 }
628 let src_barrier = src_pending.map(|pending| pending.into_hal(&src_buffer, &snatch_guard));
630
631 let (dst_buffer, dst_pending) = {
632 let buffer_guard = hub.buffers.read();
633 let dst_buffer = buffer_guard
634 .get(destination)
635 .map_err(|_| TransferError::InvalidBuffer(destination))?;
636
637 if dst_buffer.device.as_info().id() != device.as_info().id() {
638 return Err(DeviceError::WrongDevice.into());
639 }
640
641 cmd_buf_data
642 .trackers
643 .buffers
644 .set_single(dst_buffer, hal::BufferUses::COPY_DST)
645 .ok_or(TransferError::InvalidBuffer(destination))?
646 };
647 let dst_raw = dst_buffer
648 .raw
649 .get(&snatch_guard)
650 .ok_or(TransferError::InvalidBuffer(destination))?;
651 if !dst_buffer.usage.contains(BufferUsages::COPY_DST) {
652 return Err(TransferError::MissingCopyDstUsageFlag(Some(destination), None).into());
653 }
654 let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, &snatch_guard));
655
656 if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
657 return Err(TransferError::UnalignedCopySize(size).into());
658 }
659 if source_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
660 return Err(TransferError::UnalignedBufferOffset(source_offset).into());
661 }
662 if destination_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
663 return Err(TransferError::UnalignedBufferOffset(destination_offset).into());
664 }
665 if !device
666 .downlevel
667 .flags
668 .contains(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)
669 && (src_buffer.usage.contains(wgt::BufferUsages::INDEX)
670 || dst_buffer.usage.contains(wgt::BufferUsages::INDEX))
671 {
672 let forbidden_usages = wgt::BufferUsages::VERTEX
673 | wgt::BufferUsages::UNIFORM
674 | wgt::BufferUsages::INDIRECT
675 | wgt::BufferUsages::STORAGE;
676 if src_buffer.usage.intersects(forbidden_usages)
677 || dst_buffer.usage.intersects(forbidden_usages)
678 {
679 return Err(TransferError::MissingDownlevelFlags(MissingDownlevelFlags(
680 wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
681 ))
682 .into());
683 }
684 }
685
686 let source_end_offset = source_offset + size;
687 let destination_end_offset = destination_offset + size;
688 if source_end_offset > src_buffer.size {
689 return Err(TransferError::BufferOverrun {
690 start_offset: source_offset,
691 end_offset: source_end_offset,
692 buffer_size: src_buffer.size,
693 side: CopySide::Source,
694 }
695 .into());
696 }
697 if destination_end_offset > dst_buffer.size {
698 return Err(TransferError::BufferOverrun {
699 start_offset: destination_offset,
700 end_offset: destination_end_offset,
701 buffer_size: dst_buffer.size,
702 side: CopySide::Destination,
703 }
704 .into());
705 }
706
707 if size == 0 {
708 log::trace!("Ignoring copy_buffer_to_buffer of size 0");
709 return Ok(());
710 }
711
712 cmd_buf_data.buffer_memory_init_actions.extend(
714 dst_buffer.initialization_status.read().create_action(
715 &dst_buffer,
716 destination_offset..(destination_offset + size),
717 MemoryInitKind::ImplicitlyInitialized,
718 ),
719 );
720 cmd_buf_data.buffer_memory_init_actions.extend(
721 src_buffer.initialization_status.read().create_action(
722 &src_buffer,
723 source_offset..(source_offset + size),
724 MemoryInitKind::NeedsInitializedMemory,
725 ),
726 );
727
728 let region = hal::BufferCopy {
729 src_offset: source_offset,
730 dst_offset: destination_offset,
731 size: wgt::BufferSize::new(size).unwrap(),
732 };
733 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
734 unsafe {
735 cmd_buf_raw.transition_buffers(src_barrier.into_iter().chain(dst_barrier));
736 cmd_buf_raw.copy_buffer_to_buffer(src_raw, dst_raw, iter::once(region));
737 }
738 Ok(())
739 }
740
741 pub fn command_encoder_copy_buffer_to_texture<A: HalApi>(
742 &self,
743 command_encoder_id: CommandEncoderId,
744 source: &ImageCopyBuffer,
745 destination: &ImageCopyTexture,
746 copy_size: &Extent3d,
747 ) -> Result<(), CopyError> {
748 profiling::scope!("CommandEncoder::copy_buffer_to_texture");
749 api_log!(
750 "CommandEncoder::copy_buffer_to_texture {:?} -> {:?} {copy_size:?}",
751 source.buffer,
752 destination.texture
753 );
754
755 let hub = A::hub(self);
756
757 let cmd_buf = CommandBuffer::get_encoder(hub, command_encoder_id)?;
758 let device = &cmd_buf.device;
759 if !device.is_valid() {
760 return Err(TransferError::InvalidDevice(cmd_buf.device.as_info().id()).into());
761 }
762
763 let mut cmd_buf_data = cmd_buf.data.lock();
764 let cmd_buf_data = cmd_buf_data.as_mut().unwrap();
765
766 #[cfg(feature = "trace")]
767 if let Some(ref mut list) = cmd_buf_data.commands {
768 list.push(TraceCommand::CopyBufferToTexture {
769 src: *source,
770 dst: *destination,
771 size: *copy_size,
772 });
773 }
774
775 let encoder = &mut cmd_buf_data.encoder;
776 let tracker = &mut cmd_buf_data.trackers;
777 let buffer_memory_init_actions = &mut cmd_buf_data.buffer_memory_init_actions;
778 let texture_memory_actions = &mut cmd_buf_data.texture_memory_actions;
779
780 if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
781 log::trace!("Ignoring copy_buffer_to_texture of size 0");
782 return Ok(());
783 }
784
785 let dst_texture = hub
786 .textures
787 .get(destination.texture)
788 .map_err(|_| TransferError::InvalidTexture(destination.texture))?;
789
790 if dst_texture.device.as_info().id() != device.as_info().id() {
791 return Err(DeviceError::WrongDevice.into());
792 }
793
794 let (hal_copy_size, array_layer_count) = validate_texture_copy_range(
795 destination,
796 &dst_texture.desc,
797 CopySide::Destination,
798 copy_size,
799 )?;
800
801 let (dst_range, dst_base) = extract_texture_selector(destination, copy_size, &dst_texture)?;
802
803 let snatch_guard = device.snatchable_lock.read();
804
805 handle_dst_texture_init(
809 encoder,
810 tracker,
811 texture_memory_actions,
812 device,
813 destination,
814 copy_size,
815 &dst_texture,
816 &snatch_guard,
817 )?;
818
819 let (src_buffer, src_pending) = {
820 let buffer_guard = hub.buffers.read();
821 let src_buffer = buffer_guard
822 .get(source.buffer)
823 .map_err(|_| TransferError::InvalidBuffer(source.buffer))?;
824
825 if src_buffer.device.as_info().id() != device.as_info().id() {
826 return Err(DeviceError::WrongDevice.into());
827 }
828
829 tracker
830 .buffers
831 .set_single(src_buffer, hal::BufferUses::COPY_SRC)
832 .ok_or(TransferError::InvalidBuffer(source.buffer))?
833 };
834 let src_raw = src_buffer
835 .raw
836 .get(&snatch_guard)
837 .ok_or(TransferError::InvalidBuffer(source.buffer))?;
838 if !src_buffer.usage.contains(BufferUsages::COPY_SRC) {
839 return Err(TransferError::MissingCopySrcUsageFlag.into());
840 }
841 let src_barrier = src_pending.map(|pending| pending.into_hal(&src_buffer, &snatch_guard));
842
843 let dst_pending = tracker
844 .textures
845 .set_single(&dst_texture, dst_range, hal::TextureUses::COPY_DST)
846 .ok_or(TransferError::InvalidTexture(destination.texture))?;
847 let dst_raw = dst_texture
848 .raw(&snatch_guard)
849 .ok_or(TransferError::InvalidTexture(destination.texture))?;
850 if !dst_texture.desc.usage.contains(TextureUsages::COPY_DST) {
851 return Err(
852 TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(),
853 );
854 }
855 let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_raw));
856
857 if !dst_base.aspect.is_one() {
858 return Err(TransferError::CopyAspectNotOne.into());
859 }
860
861 if !conv::is_valid_copy_dst_texture_format(dst_texture.desc.format, destination.aspect) {
862 return Err(TransferError::CopyToForbiddenTextureFormat {
863 format: dst_texture.desc.format,
864 aspect: destination.aspect,
865 }
866 .into());
867 }
868
869 let (required_buffer_bytes_in_copy, bytes_per_array_layer) = validate_linear_texture_data(
870 &source.layout,
871 dst_texture.desc.format,
872 destination.aspect,
873 src_buffer.size,
874 CopySide::Source,
875 copy_size,
876 true,
877 )?;
878
879 if dst_texture.desc.format.is_depth_stencil_format() {
880 device
881 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
882 .map_err(TransferError::from)?;
883 }
884
885 buffer_memory_init_actions.extend(src_buffer.initialization_status.read().create_action(
886 &src_buffer,
887 source.layout.offset..(source.layout.offset + required_buffer_bytes_in_copy),
888 MemoryInitKind::NeedsInitializedMemory,
889 ));
890
891 let regions = (0..array_layer_count).map(|rel_array_layer| {
892 let mut texture_base = dst_base.clone();
893 texture_base.array_layer += rel_array_layer;
894 let mut buffer_layout = source.layout;
895 buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
896 hal::BufferTextureCopy {
897 buffer_layout,
898 texture_base,
899 size: hal_copy_size,
900 }
901 });
902
903 let cmd_buf_raw = encoder.open()?;
904 unsafe {
905 cmd_buf_raw.transition_textures(dst_barrier.into_iter());
906 cmd_buf_raw.transition_buffers(src_barrier.into_iter());
907 cmd_buf_raw.copy_buffer_to_texture(src_raw, dst_raw, regions);
908 }
909 Ok(())
910 }
911
912 pub fn command_encoder_copy_texture_to_buffer<A: HalApi>(
913 &self,
914 command_encoder_id: CommandEncoderId,
915 source: &ImageCopyTexture,
916 destination: &ImageCopyBuffer,
917 copy_size: &Extent3d,
918 ) -> Result<(), CopyError> {
919 profiling::scope!("CommandEncoder::copy_texture_to_buffer");
920 api_log!(
921 "CommandEncoder::copy_texture_to_buffer {:?} -> {:?} {copy_size:?}",
922 source.texture,
923 destination.buffer
924 );
925
926 let hub = A::hub(self);
927
928 let cmd_buf = CommandBuffer::get_encoder(hub, command_encoder_id)?;
929 let device = &cmd_buf.device;
930 if !device.is_valid() {
931 return Err(TransferError::InvalidDevice(cmd_buf.device.as_info().id()).into());
932 }
933
934 let mut cmd_buf_data = cmd_buf.data.lock();
935 let cmd_buf_data = cmd_buf_data.as_mut().unwrap();
936
937 #[cfg(feature = "trace")]
938 if let Some(ref mut list) = cmd_buf_data.commands {
939 list.push(TraceCommand::CopyTextureToBuffer {
940 src: *source,
941 dst: *destination,
942 size: *copy_size,
943 });
944 }
945 let encoder = &mut cmd_buf_data.encoder;
946 let tracker = &mut cmd_buf_data.trackers;
947 let buffer_memory_init_actions = &mut cmd_buf_data.buffer_memory_init_actions;
948 let texture_memory_actions = &mut cmd_buf_data.texture_memory_actions;
949
950 if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
951 log::trace!("Ignoring copy_texture_to_buffer of size 0");
952 return Ok(());
953 }
954
955 let src_texture = hub
956 .textures
957 .get(source.texture)
958 .map_err(|_| TransferError::InvalidTexture(source.texture))?;
959
960 if src_texture.device.as_info().id() != device.as_info().id() {
961 return Err(DeviceError::WrongDevice.into());
962 }
963
964 let (hal_copy_size, array_layer_count) =
965 validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
966
967 let (src_range, src_base) = extract_texture_selector(source, copy_size, &src_texture)?;
968
969 let snatch_guard = device.snatchable_lock.read();
970
971 handle_src_texture_init(
975 encoder,
976 tracker,
977 texture_memory_actions,
978 device,
979 source,
980 copy_size,
981 &src_texture,
982 &snatch_guard,
983 )?;
984
985 let src_pending = tracker
986 .textures
987 .set_single(&src_texture, src_range, hal::TextureUses::COPY_SRC)
988 .ok_or(TransferError::InvalidTexture(source.texture))?;
989 let src_raw = src_texture
990 .raw(&snatch_guard)
991 .ok_or(TransferError::InvalidTexture(source.texture))?;
992 if !src_texture.desc.usage.contains(TextureUsages::COPY_SRC) {
993 return Err(TransferError::MissingCopySrcUsageFlag.into());
994 }
995 if src_texture.desc.sample_count != 1 {
996 return Err(TransferError::InvalidSampleCount {
997 sample_count: src_texture.desc.sample_count,
998 }
999 .into());
1000 }
1001 if source.mip_level >= src_texture.desc.mip_level_count {
1002 return Err(TransferError::InvalidMipLevel {
1003 requested: source.mip_level,
1004 count: src_texture.desc.mip_level_count,
1005 }
1006 .into());
1007 }
1008 let src_barrier = src_pending.map(|pending| pending.into_hal(src_raw));
1009
1010 let (dst_buffer, dst_pending) = {
1011 let buffer_guard = hub.buffers.read();
1012 let dst_buffer = buffer_guard
1013 .get(destination.buffer)
1014 .map_err(|_| TransferError::InvalidBuffer(destination.buffer))?;
1015
1016 if dst_buffer.device.as_info().id() != device.as_info().id() {
1017 return Err(DeviceError::WrongDevice.into());
1018 }
1019
1020 tracker
1021 .buffers
1022 .set_single(dst_buffer, hal::BufferUses::COPY_DST)
1023 .ok_or(TransferError::InvalidBuffer(destination.buffer))?
1024 };
1025 let dst_raw = dst_buffer
1026 .raw
1027 .get(&snatch_guard)
1028 .ok_or(TransferError::InvalidBuffer(destination.buffer))?;
1029 if !dst_buffer.usage.contains(BufferUsages::COPY_DST) {
1030 return Err(
1031 TransferError::MissingCopyDstUsageFlag(Some(destination.buffer), None).into(),
1032 );
1033 }
1034 let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, &snatch_guard));
1035
1036 if !src_base.aspect.is_one() {
1037 return Err(TransferError::CopyAspectNotOne.into());
1038 }
1039
1040 if !conv::is_valid_copy_src_texture_format(src_texture.desc.format, source.aspect) {
1041 return Err(TransferError::CopyFromForbiddenTextureFormat {
1042 format: src_texture.desc.format,
1043 aspect: source.aspect,
1044 }
1045 .into());
1046 }
1047
1048 let (required_buffer_bytes_in_copy, bytes_per_array_layer) = validate_linear_texture_data(
1049 &destination.layout,
1050 src_texture.desc.format,
1051 source.aspect,
1052 dst_buffer.size,
1053 CopySide::Destination,
1054 copy_size,
1055 true,
1056 )?;
1057
1058 if src_texture.desc.format.is_depth_stencil_format() {
1059 device
1060 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
1061 .map_err(TransferError::from)?;
1062 }
1063
1064 buffer_memory_init_actions.extend(dst_buffer.initialization_status.read().create_action(
1065 &dst_buffer,
1066 destination.layout.offset..(destination.layout.offset + required_buffer_bytes_in_copy),
1067 MemoryInitKind::ImplicitlyInitialized,
1068 ));
1069
1070 let regions = (0..array_layer_count).map(|rel_array_layer| {
1071 let mut texture_base = src_base.clone();
1072 texture_base.array_layer += rel_array_layer;
1073 let mut buffer_layout = destination.layout;
1074 buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
1075 hal::BufferTextureCopy {
1076 buffer_layout,
1077 texture_base,
1078 size: hal_copy_size,
1079 }
1080 });
1081 let cmd_buf_raw = encoder.open()?;
1082 unsafe {
1083 cmd_buf_raw.transition_buffers(dst_barrier.into_iter());
1084 cmd_buf_raw.transition_textures(src_barrier.into_iter());
1085 cmd_buf_raw.copy_texture_to_buffer(
1086 src_raw,
1087 hal::TextureUses::COPY_SRC,
1088 dst_raw,
1089 regions,
1090 );
1091 }
1092 Ok(())
1093 }
1094
1095 pub fn command_encoder_copy_texture_to_texture<A: HalApi>(
1096 &self,
1097 command_encoder_id: CommandEncoderId,
1098 source: &ImageCopyTexture,
1099 destination: &ImageCopyTexture,
1100 copy_size: &Extent3d,
1101 ) -> Result<(), CopyError> {
1102 profiling::scope!("CommandEncoder::copy_texture_to_texture");
1103 api_log!(
1104 "CommandEncoder::copy_texture_to_texture {:?} -> {:?} {copy_size:?}",
1105 source.texture,
1106 destination.texture
1107 );
1108
1109 let hub = A::hub(self);
1110
1111 let cmd_buf = CommandBuffer::get_encoder(hub, command_encoder_id)?;
1112 let device = &cmd_buf.device;
1113 if !device.is_valid() {
1114 return Err(TransferError::InvalidDevice(cmd_buf.device.as_info().id()).into());
1115 }
1116
1117 let snatch_guard = device.snatchable_lock.read();
1118
1119 let mut cmd_buf_data = cmd_buf.data.lock();
1120 let cmd_buf_data = cmd_buf_data.as_mut().unwrap();
1121
1122 #[cfg(feature = "trace")]
1123 if let Some(ref mut list) = cmd_buf_data.commands {
1124 list.push(TraceCommand::CopyTextureToTexture {
1125 src: *source,
1126 dst: *destination,
1127 size: *copy_size,
1128 });
1129 }
1130 let encoder = &mut cmd_buf_data.encoder;
1131 let tracker = &mut cmd_buf_data.trackers;
1132 let texture_memory_actions = &mut cmd_buf_data.texture_memory_actions;
1133
1134 if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1135 log::trace!("Ignoring copy_texture_to_texture of size 0");
1136 return Ok(());
1137 }
1138
1139 let src_texture = hub
1140 .textures
1141 .get(source.texture)
1142 .map_err(|_| TransferError::InvalidTexture(source.texture))?;
1143 let dst_texture = hub
1144 .textures
1145 .get(destination.texture)
1146 .map_err(|_| TransferError::InvalidTexture(source.texture))?;
1147
1148 if src_texture.device.as_info().id() != device.as_info().id() {
1149 return Err(DeviceError::WrongDevice.into());
1150 }
1151 if dst_texture.device.as_info().id() != device.as_info().id() {
1152 return Err(DeviceError::WrongDevice.into());
1153 }
1154
1155 if src_texture.desc.format.remove_srgb_suffix()
1158 != dst_texture.desc.format.remove_srgb_suffix()
1159 {
1160 return Err(TransferError::TextureFormatsNotCopyCompatible {
1161 src_format: src_texture.desc.format,
1162 dst_format: dst_texture.desc.format,
1163 }
1164 .into());
1165 }
1166
1167 let (src_copy_size, array_layer_count) =
1168 validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
1169 let (dst_copy_size, _) = validate_texture_copy_range(
1170 destination,
1171 &dst_texture.desc,
1172 CopySide::Destination,
1173 copy_size,
1174 )?;
1175
1176 let (src_range, src_tex_base) = extract_texture_selector(source, copy_size, &src_texture)?;
1177 let (dst_range, dst_tex_base) =
1178 extract_texture_selector(destination, copy_size, &dst_texture)?;
1179 let src_texture_aspects = hal::FormatAspects::from(src_texture.desc.format);
1180 let dst_texture_aspects = hal::FormatAspects::from(dst_texture.desc.format);
1181 if src_tex_base.aspect != src_texture_aspects {
1182 return Err(TransferError::CopySrcMissingAspects.into());
1183 }
1184 if dst_tex_base.aspect != dst_texture_aspects {
1185 return Err(TransferError::CopyDstMissingAspects.into());
1186 }
1187
1188 handle_src_texture_init(
1192 encoder,
1193 tracker,
1194 texture_memory_actions,
1195 device,
1196 source,
1197 copy_size,
1198 &src_texture,
1199 &snatch_guard,
1200 )?;
1201 handle_dst_texture_init(
1202 encoder,
1203 tracker,
1204 texture_memory_actions,
1205 device,
1206 destination,
1207 copy_size,
1208 &dst_texture,
1209 &snatch_guard,
1210 )?;
1211
1212 let src_pending = cmd_buf_data
1213 .trackers
1214 .textures
1215 .set_single(&src_texture, src_range, hal::TextureUses::COPY_SRC)
1216 .ok_or(TransferError::InvalidTexture(source.texture))?;
1217 let src_raw = src_texture
1218 .raw(&snatch_guard)
1219 .ok_or(TransferError::InvalidTexture(source.texture))?;
1220 if !src_texture.desc.usage.contains(TextureUsages::COPY_SRC) {
1221 return Err(TransferError::MissingCopySrcUsageFlag.into());
1222 }
1223
1224 let mut barriers: ArrayVec<_, 2> = src_pending
1227 .map(|pending| pending.into_hal(src_raw))
1228 .collect();
1229
1230 let dst_pending = cmd_buf_data
1231 .trackers
1232 .textures
1233 .set_single(&dst_texture, dst_range, hal::TextureUses::COPY_DST)
1234 .ok_or(TransferError::InvalidTexture(destination.texture))?;
1235 let dst_raw = dst_texture
1236 .raw(&snatch_guard)
1237 .ok_or(TransferError::InvalidTexture(destination.texture))?;
1238 if !dst_texture.desc.usage.contains(TextureUsages::COPY_DST) {
1239 return Err(
1240 TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(),
1241 );
1242 }
1243
1244 barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_raw)));
1245
1246 let hal_copy_size = hal::CopyExtent {
1247 width: src_copy_size.width.min(dst_copy_size.width),
1248 height: src_copy_size.height.min(dst_copy_size.height),
1249 depth: src_copy_size.depth.min(dst_copy_size.depth),
1250 };
1251 let regions = (0..array_layer_count).map(|rel_array_layer| {
1252 let mut src_base = src_tex_base.clone();
1253 let mut dst_base = dst_tex_base.clone();
1254 src_base.array_layer += rel_array_layer;
1255 dst_base.array_layer += rel_array_layer;
1256 hal::TextureCopy {
1257 src_base,
1258 dst_base,
1259 size: hal_copy_size,
1260 }
1261 });
1262 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1263 unsafe {
1264 cmd_buf_raw.transition_textures(barriers.into_iter());
1265 cmd_buf_raw.copy_texture_to_texture(
1266 src_raw,
1267 hal::TextureUses::COPY_SRC,
1268 dst_raw,
1269 regions,
1270 );
1271 }
1272
1273 Ok(())
1274 }
1275}