1use alloc::{format, string::String, sync::Arc, vec::Vec};
2
3use arrayvec::ArrayVec;
4use thiserror::Error;
5use wgt::{
6 error::{ErrorType, WebGpuError},
7 BufferAddress, BufferTextureCopyInfoError, BufferUsages, Extent3d, TextureSelector,
8 TextureUsages,
9};
10
11use crate::{
12 api_log,
13 command::{
14 clear_texture, encoder::EncodingState, ArcCommand, CommandEncoderError, EncoderStateError,
15 },
16 device::MissingDownlevelFlags,
17 global::Global,
18 id::{BufferId, CommandEncoderId, TextureId},
19 init_tracker::{
20 has_copy_partial_init_tracker_coverage, MemoryInitKind, TextureInitRange,
21 TextureInitTrackerAction,
22 },
23 resource::{
24 Buffer, MissingBufferUsageError, MissingTextureUsageError, ParentDevice, RawResourceAccess,
25 Texture, TextureErrorDimension,
26 },
27};
28
29use super::ClearError;
30
31type TexelCopyBufferInfo = wgt::TexelCopyBufferInfo<BufferId>;
32type TexelCopyTextureInfo = wgt::TexelCopyTextureInfo<Arc<Texture>>;
33
34#[derive(Clone, Copy, Debug, Eq, PartialEq)]
35pub enum CopySide {
36 Source,
37 Destination,
38}
39
40#[derive(Clone, Debug, Error)]
42#[non_exhaustive]
43pub enum TransferError {
44 #[error("Source and destination cannot be the same buffer")]
45 SameSourceDestinationBuffer,
46 #[error(transparent)]
47 MissingBufferUsage(#[from] MissingBufferUsageError),
48 #[error(transparent)]
49 MissingTextureUsage(#[from] MissingTextureUsageError),
50 #[error("Copy of {start_offset}..{end_offset} would end up overrunning the bounds of the {side:?} buffer of size {buffer_size}")]
51 BufferOverrun {
52 start_offset: BufferAddress,
53 end_offset: BufferAddress,
54 buffer_size: BufferAddress,
55 side: CopySide,
56 },
57 #[error("Copy of {dimension:?} {start_offset}..{end_offset} would end up overrunning the bounds of the {side:?} texture of {dimension:?} size {texture_size}")]
58 TextureOverrun {
59 start_offset: u32,
60 end_offset: u32,
61 texture_size: u32,
62 dimension: TextureErrorDimension,
63 side: CopySide,
64 },
65 #[error("Partial copy of {start_offset}..{end_offset} on {dimension:?} dimension with size {texture_size} \
66 is not supported for the {side:?} texture format {format:?} with {sample_count} samples")]
67 UnsupportedPartialTransfer {
68 format: wgt::TextureFormat,
69 sample_count: u32,
70 start_offset: u32,
71 end_offset: u32,
72 texture_size: u32,
73 dimension: TextureErrorDimension,
74 side: CopySide,
75 },
76 #[error(
77 "Copying{} layers {}..{} to{} layers {}..{} of the same texture is not allowed",
78 if *src_aspects == wgt::TextureAspect::All { String::new() } else { format!(" {src_aspects:?}") },
79 src_origin_z,
80 src_origin_z + array_layer_count,
81 if *dst_aspects == wgt::TextureAspect::All { String::new() } else { format!(" {dst_aspects:?}") },
82 dst_origin_z,
83 dst_origin_z + array_layer_count,
84 )]
85 InvalidCopyWithinSameTexture {
86 src_aspects: wgt::TextureAspect,
87 dst_aspects: wgt::TextureAspect,
88 src_origin_z: u32,
89 dst_origin_z: u32,
90 array_layer_count: u32,
91 },
92 #[error("Unable to select texture aspect {aspect:?} from format {format:?}")]
93 InvalidTextureAspect {
94 format: wgt::TextureFormat,
95 aspect: wgt::TextureAspect,
96 },
97 #[error("Unable to select texture mip level {level} out of {total}")]
98 InvalidTextureMipLevel { level: u32, total: u32 },
99 #[error("Texture dimension must be 2D when copying from an external texture")]
100 InvalidDimensionExternal,
101 #[error("Buffer offset {0} is not aligned to block size or `COPY_BUFFER_ALIGNMENT`")]
102 UnalignedBufferOffset(BufferAddress),
103 #[error("Copy size {0} does not respect `COPY_BUFFER_ALIGNMENT`")]
104 UnalignedCopySize(BufferAddress),
105 #[error("Copy width is not a multiple of block width")]
106 UnalignedCopyWidth,
107 #[error("Copy height is not a multiple of block height")]
108 UnalignedCopyHeight,
109 #[error("Copy origin's x component is not a multiple of block width")]
110 UnalignedCopyOriginX,
111 #[error("Copy origin's y component is not a multiple of block height")]
112 UnalignedCopyOriginY,
113 #[error("Bytes per row does not respect `COPY_BYTES_PER_ROW_ALIGNMENT`")]
114 UnalignedBytesPerRow,
115 #[error("Number of bytes per row needs to be specified since more than one row is copied")]
116 UnspecifiedBytesPerRow,
117 #[error("Number of rows per image needs to be specified since more than one image is copied")]
118 UnspecifiedRowsPerImage,
119 #[error("Number of bytes per row is less than the number of bytes in a complete row")]
120 InvalidBytesPerRow,
121 #[error("Number of rows per image is invalid")]
122 InvalidRowsPerImage,
123 #[error("Overflow while computing the size of the copy")]
124 SizeOverflow,
125 #[error("Copy source aspects must refer to all aspects of the source texture format")]
126 CopySrcMissingAspects,
127 #[error(
128 "Copy destination aspects must refer to all aspects of the destination texture format"
129 )]
130 CopyDstMissingAspects,
131 #[error("Copy aspect must refer to a single aspect of texture format")]
132 CopyAspectNotOne,
133 #[error("Copying from textures with format {0:?} is forbidden")]
134 CopyFromForbiddenTextureFormat(wgt::TextureFormat),
135 #[error("Copying from textures with format {format:?} and aspect {aspect:?} is forbidden")]
136 CopyFromForbiddenTextureFormatAspect {
137 format: wgt::TextureFormat,
138 aspect: wgt::TextureAspect,
139 },
140 #[error("Copying to textures with format {0:?} is forbidden")]
141 CopyToForbiddenTextureFormat(wgt::TextureFormat),
142 #[error("Copying to textures with format {format:?} and aspect {aspect:?} is forbidden")]
143 CopyToForbiddenTextureFormatAspect {
144 format: wgt::TextureFormat,
145 aspect: wgt::TextureAspect,
146 },
147 #[error(
148 "Copying to textures with format {0:?} is forbidden when copying from external texture"
149 )]
150 ExternalCopyToForbiddenTextureFormat(wgt::TextureFormat),
151 #[error(
152 "Source format ({src_format:?}) and destination format ({dst_format:?}) are not copy-compatible (they may only differ in srgb-ness)"
153 )]
154 TextureFormatsNotCopyCompatible {
155 src_format: wgt::TextureFormat,
156 dst_format: wgt::TextureFormat,
157 },
158 #[error(transparent)]
159 MemoryInitFailure(#[from] ClearError),
160 #[error("Cannot encode this copy because of a missing downelevel flag")]
161 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
162 #[error("Source texture sample count must be 1, got {sample_count}")]
163 InvalidSampleCount { sample_count: u32 },
164 #[error(
165 "Source sample count ({src_sample_count:?}) and destination sample count ({dst_sample_count:?}) are not equal"
166 )]
167 SampleCountNotEqual {
168 src_sample_count: u32,
169 dst_sample_count: u32,
170 },
171 #[error("Requested mip level {requested} does not exist (count: {count})")]
172 InvalidMipLevel { requested: u32, count: u32 },
173}
174
175impl WebGpuError for TransferError {
176 fn webgpu_error_type(&self) -> ErrorType {
177 let e: &dyn WebGpuError = match self {
178 Self::MissingBufferUsage(e) => e,
179 Self::MissingTextureUsage(e) => e,
180 Self::MemoryInitFailure(e) => e,
181
182 Self::BufferOverrun { .. }
183 | Self::TextureOverrun { .. }
184 | Self::UnsupportedPartialTransfer { .. }
185 | Self::InvalidCopyWithinSameTexture { .. }
186 | Self::InvalidTextureAspect { .. }
187 | Self::InvalidTextureMipLevel { .. }
188 | Self::InvalidDimensionExternal
189 | Self::UnalignedBufferOffset(..)
190 | Self::UnalignedCopySize(..)
191 | Self::UnalignedCopyWidth
192 | Self::UnalignedCopyHeight
193 | Self::UnalignedCopyOriginX
194 | Self::UnalignedCopyOriginY
195 | Self::UnalignedBytesPerRow
196 | Self::UnspecifiedBytesPerRow
197 | Self::UnspecifiedRowsPerImage
198 | Self::InvalidBytesPerRow
199 | Self::InvalidRowsPerImage
200 | Self::SizeOverflow
201 | Self::CopySrcMissingAspects
202 | Self::CopyDstMissingAspects
203 | Self::CopyAspectNotOne
204 | Self::CopyFromForbiddenTextureFormat(..)
205 | Self::CopyFromForbiddenTextureFormatAspect { .. }
206 | Self::CopyToForbiddenTextureFormat(..)
207 | Self::CopyToForbiddenTextureFormatAspect { .. }
208 | Self::ExternalCopyToForbiddenTextureFormat(..)
209 | Self::TextureFormatsNotCopyCompatible { .. }
210 | Self::MissingDownlevelFlags(..)
211 | Self::InvalidSampleCount { .. }
212 | Self::SampleCountNotEqual { .. }
213 | Self::InvalidMipLevel { .. }
214 | Self::SameSourceDestinationBuffer => return ErrorType::Validation,
215 };
216 e.webgpu_error_type()
217 }
218}
219
220impl From<BufferTextureCopyInfoError> for TransferError {
221 fn from(value: BufferTextureCopyInfoError) -> Self {
222 match value {
223 BufferTextureCopyInfoError::InvalidBytesPerRow => Self::InvalidBytesPerRow,
224 BufferTextureCopyInfoError::InvalidRowsPerImage => Self::InvalidRowsPerImage,
225 BufferTextureCopyInfoError::ImageStrideOverflow
226 | BufferTextureCopyInfoError::ImageBytesOverflow(_)
227 | BufferTextureCopyInfoError::ArraySizeOverflow(_) => Self::SizeOverflow,
228 }
229 }
230}
231
232pub(crate) fn extract_texture_selector<T>(
233 copy_texture: &wgt::TexelCopyTextureInfo<T>,
234 copy_size: &Extent3d,
235 texture: &Texture,
236) -> Result<(TextureSelector, hal::TextureCopyBase), TransferError> {
237 let format = texture.desc.format;
238 let copy_aspect = hal::FormatAspects::new(format, copy_texture.aspect);
239 if copy_aspect.is_empty() {
240 return Err(TransferError::InvalidTextureAspect {
241 format,
242 aspect: copy_texture.aspect,
243 });
244 }
245
246 let (layers, origin_z) = match texture.desc.dimension {
247 wgt::TextureDimension::D1 => (0..1, 0),
248 wgt::TextureDimension::D2 => (
249 copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers,
250 0,
251 ),
252 wgt::TextureDimension::D3 => (0..1, copy_texture.origin.z),
253 };
254 let base = hal::TextureCopyBase {
255 origin: wgt::Origin3d {
256 x: copy_texture.origin.x,
257 y: copy_texture.origin.y,
258 z: origin_z,
259 },
260 array_layer: layers.start,
262 mip_level: copy_texture.mip_level,
263 aspect: copy_aspect,
264 };
265 let selector = TextureSelector {
266 mips: copy_texture.mip_level..copy_texture.mip_level + 1,
267 layers,
268 };
269
270 Ok((selector, base))
271}
272
273pub(crate) fn validate_linear_texture_data(
285 layout: &wgt::TexelCopyBufferLayout,
286 format: wgt::TextureFormat,
287 aspect: wgt::TextureAspect,
288 buffer_size: BufferAddress,
289 buffer_side: CopySide,
290 copy_size: &Extent3d,
291) -> Result<(BufferAddress, BufferAddress, bool), TransferError> {
292 let wgt::BufferTextureCopyInfo {
293 copy_width,
294 copy_height,
295 depth_or_array_layers,
296
297 offset,
298
299 block_size_bytes: _,
300 block_width_texels,
301 block_height_texels,
302
303 width_blocks: _,
304 height_blocks,
305
306 row_bytes_dense,
307 row_stride_bytes,
308
309 image_stride_rows: _,
310 image_stride_bytes,
311
312 image_rows_dense: _,
313 image_bytes_dense,
314
315 bytes_in_copy,
316 } = layout.get_buffer_texture_copy_info(format, aspect, copy_size)?;
317
318 if copy_width % block_width_texels != 0 {
319 return Err(TransferError::UnalignedCopyWidth);
320 }
321 if copy_height % block_height_texels != 0 {
322 return Err(TransferError::UnalignedCopyHeight);
323 }
324
325 let requires_multiple_rows = depth_or_array_layers > 1 || height_blocks > 1;
326 let requires_multiple_images = depth_or_array_layers > 1;
327
328 if layout.bytes_per_row.is_none() && requires_multiple_rows {
333 return Err(TransferError::UnspecifiedBytesPerRow);
334 }
335
336 if layout.rows_per_image.is_none() && requires_multiple_images {
337 return Err(TransferError::UnspecifiedRowsPerImage);
338 };
339
340 if bytes_in_copy > buffer_size || offset > buffer_size - bytes_in_copy {
342 return Err(TransferError::BufferOverrun {
343 start_offset: offset,
344 end_offset: offset.wrapping_add(bytes_in_copy),
345 buffer_size,
346 side: buffer_side,
347 });
348 }
349
350 let is_contiguous = (row_stride_bytes == row_bytes_dense || !requires_multiple_rows)
351 && (image_stride_bytes == image_bytes_dense || !requires_multiple_images);
352
353 Ok((bytes_in_copy, image_stride_bytes, is_contiguous))
354}
355
356pub(crate) fn validate_texture_copy_src_format(
365 format: wgt::TextureFormat,
366 aspect: wgt::TextureAspect,
367) -> Result<(), TransferError> {
368 use wgt::TextureAspect as Ta;
369 use wgt::TextureFormat as Tf;
370 match (format, aspect) {
371 (Tf::Depth24Plus, _) => Err(TransferError::CopyFromForbiddenTextureFormat(format)),
372 (Tf::Depth24PlusStencil8, Ta::DepthOnly) => {
373 Err(TransferError::CopyFromForbiddenTextureFormatAspect { format, aspect })
374 }
375 _ => Ok(()),
376 }
377}
378
379pub(crate) fn validate_texture_copy_dst_format(
388 format: wgt::TextureFormat,
389 aspect: wgt::TextureAspect,
390) -> Result<(), TransferError> {
391 use wgt::TextureAspect as Ta;
392 use wgt::TextureFormat as Tf;
393 match (format, aspect) {
394 (Tf::Depth24Plus | Tf::Depth32Float, _) => {
395 Err(TransferError::CopyToForbiddenTextureFormat(format))
396 }
397 (Tf::Depth24PlusStencil8 | Tf::Depth32FloatStencil8, Ta::DepthOnly) => {
398 Err(TransferError::CopyToForbiddenTextureFormatAspect { format, aspect })
399 }
400 _ => Ok(()),
401 }
402}
403
404pub(crate) fn validate_texture_buffer_copy<T>(
432 texture_copy_view: &wgt::TexelCopyTextureInfo<T>,
433 aspect: hal::FormatAspects,
434 desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
435 layout: &wgt::TexelCopyBufferLayout,
436 aligned: bool,
437) -> Result<(), TransferError> {
438 if desc.sample_count != 1 {
439 return Err(TransferError::InvalidSampleCount {
440 sample_count: desc.sample_count,
441 });
442 }
443
444 if !aspect.is_one() {
445 return Err(TransferError::CopyAspectNotOne);
446 }
447
448 let offset_alignment = if desc.format.is_depth_stencil_format() {
449 4
450 } else {
451 desc.format
456 .block_copy_size(Some(texture_copy_view.aspect))
457 .expect("non-copyable formats should have been rejected previously")
458 };
459
460 if aligned && layout.offset % u64::from(offset_alignment) != 0 {
461 return Err(TransferError::UnalignedBufferOffset(layout.offset));
462 }
463
464 if let Some(bytes_per_row) = layout.bytes_per_row {
465 if aligned && bytes_per_row % wgt::COPY_BYTES_PER_ROW_ALIGNMENT != 0 {
466 return Err(TransferError::UnalignedBytesPerRow);
467 }
468 }
469
470 Ok(())
471}
472
473pub(crate) fn validate_texture_copy_range<T>(
484 texture_copy_view: &wgt::TexelCopyTextureInfo<T>,
485 desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
486 texture_side: CopySide,
487 copy_size: &Extent3d,
488) -> Result<(hal::CopyExtent, u32), TransferError> {
489 let (block_width, block_height) = desc.format.block_dimensions();
490
491 let extent_virtual = desc.mip_level_size(texture_copy_view.mip_level).ok_or(
492 TransferError::InvalidTextureMipLevel {
493 level: texture_copy_view.mip_level,
494 total: desc.mip_level_count,
495 },
496 )?;
497 let extent = extent_virtual.physical_size(desc.format);
499
500 let requires_exact_size = desc.format.is_depth_stencil_format() || desc.sample_count > 1;
503
504 let check_dimension = |dimension: TextureErrorDimension,
507 start_offset: u32,
508 size: u32,
509 texture_size: u32,
510 requires_exact_size: bool|
511 -> Result<(), TransferError> {
512 if requires_exact_size && (start_offset != 0 || size != texture_size) {
513 Err(TransferError::UnsupportedPartialTransfer {
514 format: desc.format,
515 sample_count: desc.sample_count,
516 start_offset,
517 end_offset: start_offset.wrapping_add(size),
518 texture_size,
519 dimension,
520 side: texture_side,
521 })
522 } else if start_offset > texture_size || texture_size - start_offset < size {
525 Err(TransferError::TextureOverrun {
526 start_offset,
527 end_offset: start_offset.wrapping_add(size),
528 texture_size,
529 dimension,
530 side: texture_side,
531 })
532 } else {
533 Ok(())
534 }
535 };
536
537 check_dimension(
538 TextureErrorDimension::X,
539 texture_copy_view.origin.x,
540 copy_size.width,
541 extent.width,
542 requires_exact_size,
543 )?;
544 check_dimension(
545 TextureErrorDimension::Y,
546 texture_copy_view.origin.y,
547 copy_size.height,
548 extent.height,
549 requires_exact_size,
550 )?;
551 check_dimension(
552 TextureErrorDimension::Z,
553 texture_copy_view.origin.z,
554 copy_size.depth_or_array_layers,
555 extent.depth_or_array_layers,
556 false, )?;
558
559 if texture_copy_view.origin.x % block_width != 0 {
560 return Err(TransferError::UnalignedCopyOriginX);
561 }
562 if texture_copy_view.origin.y % block_height != 0 {
563 return Err(TransferError::UnalignedCopyOriginY);
564 }
565 if copy_size.width % block_width != 0 {
566 return Err(TransferError::UnalignedCopyWidth);
567 }
568 if copy_size.height % block_height != 0 {
569 return Err(TransferError::UnalignedCopyHeight);
570 }
571
572 let (depth, array_layer_count) = match desc.dimension {
573 wgt::TextureDimension::D1 => (1, 1),
574 wgt::TextureDimension::D2 => (1, copy_size.depth_or_array_layers),
575 wgt::TextureDimension::D3 => (copy_size.depth_or_array_layers, 1),
576 };
577
578 let copy_extent = hal::CopyExtent {
579 width: copy_size.width,
580 height: copy_size.height,
581 depth,
582 };
583 Ok((copy_extent, array_layer_count))
584}
585
586pub(crate) fn validate_copy_within_same_texture<T>(
597 src: &wgt::TexelCopyTextureInfo<T>,
598 dst: &wgt::TexelCopyTextureInfo<T>,
599 format: wgt::TextureFormat,
600 array_layer_count: u32,
601) -> Result<(), TransferError> {
602 let src_aspects = hal::FormatAspects::new(format, src.aspect);
603 let dst_aspects = hal::FormatAspects::new(format, dst.aspect);
604 if (src_aspects & dst_aspects).is_empty() {
605 return Ok(());
607 }
608
609 if src.origin.z >= dst.origin.z + array_layer_count
610 || dst.origin.z >= src.origin.z + array_layer_count
611 {
612 return Ok(());
614 }
615
616 if src.mip_level != dst.mip_level {
617 return Ok(());
619 }
620
621 Err(TransferError::InvalidCopyWithinSameTexture {
622 src_aspects: src.aspect,
623 dst_aspects: dst.aspect,
624 src_origin_z: src.origin.z,
625 dst_origin_z: dst.origin.z,
626 array_layer_count,
627 })
628}
629
630fn handle_texture_init(
631 state: &mut EncodingState,
632 init_kind: MemoryInitKind,
633 copy_texture: &TexelCopyTextureInfo,
634 copy_size: &Extent3d,
635 texture: &Arc<Texture>,
636) -> Result<(), ClearError> {
637 let init_action = TextureInitTrackerAction {
638 texture: texture.clone(),
639 range: TextureInitRange {
640 mip_range: copy_texture.mip_level..copy_texture.mip_level + 1,
641 layer_range: copy_texture.origin.z
642 ..(copy_texture.origin.z + copy_size.depth_or_array_layers),
643 },
644 kind: init_kind,
645 };
646
647 let immediate_inits = state
649 .texture_memory_actions
650 .register_init_action(&{ init_action });
651
652 if !immediate_inits.is_empty() {
654 for init in immediate_inits {
655 clear_texture(
656 &init.texture,
657 TextureInitRange {
658 mip_range: init.mip_level..(init.mip_level + 1),
659 layer_range: init.layer..(init.layer + 1),
660 },
661 state.raw_encoder,
662 &mut state.tracker.textures,
663 &state.device.alignments,
664 state.device.zero_buffer.as_ref(),
665 state.snatch_guard,
666 state.device.instance_flags,
667 )?;
668 }
669 }
670
671 Ok(())
672}
673
674fn handle_src_texture_init(
679 state: &mut EncodingState,
680 source: &TexelCopyTextureInfo,
681 copy_size: &Extent3d,
682 texture: &Arc<Texture>,
683) -> Result<(), TransferError> {
684 handle_texture_init(
685 state,
686 MemoryInitKind::NeedsInitializedMemory,
687 source,
688 copy_size,
689 texture,
690 )?;
691 Ok(())
692}
693
694fn handle_dst_texture_init(
699 state: &mut EncodingState,
700 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
701 copy_size: &Extent3d,
702 texture: &Arc<Texture>,
703) -> Result<(), TransferError> {
704 let dst_init_kind = if has_copy_partial_init_tracker_coverage(
709 copy_size,
710 destination.mip_level,
711 &texture.desc,
712 ) {
713 MemoryInitKind::NeedsInitializedMemory
714 } else {
715 MemoryInitKind::ImplicitlyInitialized
716 };
717
718 handle_texture_init(state, dst_init_kind, destination, copy_size, texture)?;
719 Ok(())
720}
721
722fn handle_buffer_init(
727 state: &mut EncodingState,
728 info: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
729 direction: CopySide,
730 required_buffer_bytes_in_copy: BufferAddress,
731 is_contiguous: bool,
732) {
733 const ALIGN_SIZE: BufferAddress = wgt::COPY_BUFFER_ALIGNMENT;
734 const ALIGN_MASK: BufferAddress = wgt::COPY_BUFFER_ALIGNMENT - 1;
735
736 let buffer = &info.buffer;
737 let start = info.layout.offset;
738 let end = info.layout.offset + required_buffer_bytes_in_copy;
739 if !is_contiguous || direction == CopySide::Source {
740 let aligned_start = start & !ALIGN_MASK;
751 let aligned_end = (end + ALIGN_MASK) & !ALIGN_MASK;
752 state
753 .buffer_memory_init_actions
754 .extend(buffer.initialization_status.read().create_action(
755 buffer,
756 aligned_start..aligned_end,
757 MemoryInitKind::NeedsInitializedMemory,
758 ));
759 } else {
760 let aligned_start = (start + ALIGN_MASK) & !ALIGN_MASK;
770 let aligned_end = end & !ALIGN_MASK;
771 if aligned_start != start {
772 state.buffer_memory_init_actions.extend(
773 buffer.initialization_status.read().create_action(
774 buffer,
775 aligned_start - ALIGN_SIZE..aligned_start,
776 MemoryInitKind::NeedsInitializedMemory,
777 ),
778 );
779 }
780 if aligned_start != aligned_end {
781 state.buffer_memory_init_actions.extend(
782 buffer.initialization_status.read().create_action(
783 buffer,
784 aligned_start..aligned_end,
785 MemoryInitKind::ImplicitlyInitialized,
786 ),
787 );
788 }
789 if aligned_end != end {
790 state.buffer_memory_init_actions.extend(
796 buffer.initialization_status.read().create_action(
797 buffer,
798 aligned_end..aligned_end + ALIGN_SIZE,
799 MemoryInitKind::NeedsInitializedMemory,
800 ),
801 );
802 }
803 }
804}
805
806impl Global {
807 pub fn command_encoder_copy_buffer_to_buffer(
808 &self,
809 command_encoder_id: CommandEncoderId,
810 source: BufferId,
811 source_offset: BufferAddress,
812 destination: BufferId,
813 destination_offset: BufferAddress,
814 size: Option<BufferAddress>,
815 ) -> Result<(), EncoderStateError> {
816 profiling::scope!("CommandEncoder::copy_buffer_to_buffer");
817 api_log!(
818 "CommandEncoder::copy_buffer_to_buffer {source:?} -> {destination:?} {size:?}bytes"
819 );
820
821 let hub = &self.hub;
822
823 let cmd_enc = hub.command_encoders.get(command_encoder_id);
824 let mut cmd_buf_data = cmd_enc.data.lock();
825
826 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
827 Ok(ArcCommand::CopyBufferToBuffer {
828 src: self.resolve_buffer_id(source)?,
829 src_offset: source_offset,
830 dst: self.resolve_buffer_id(destination)?,
831 dst_offset: destination_offset,
832 size,
833 })
834 })
835 }
836
837 pub fn command_encoder_copy_buffer_to_texture(
838 &self,
839 command_encoder_id: CommandEncoderId,
840 source: &TexelCopyBufferInfo,
841 destination: &wgt::TexelCopyTextureInfo<TextureId>,
842 copy_size: &Extent3d,
843 ) -> Result<(), EncoderStateError> {
844 profiling::scope!("CommandEncoder::copy_buffer_to_texture");
845 api_log!(
846 "CommandEncoder::copy_buffer_to_texture {:?} -> {:?} {copy_size:?}",
847 source.buffer,
848 destination.texture
849 );
850
851 let cmd_enc = self.hub.command_encoders.get(command_encoder_id);
852 let mut cmd_buf_data = cmd_enc.data.lock();
853
854 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
855 Ok(ArcCommand::CopyBufferToTexture {
856 src: wgt::TexelCopyBufferInfo::<Arc<Buffer>> {
857 buffer: self.resolve_buffer_id(source.buffer)?,
858 layout: source.layout,
859 },
860 dst: wgt::TexelCopyTextureInfo::<Arc<Texture>> {
861 texture: self.resolve_texture_id(destination.texture)?,
862 mip_level: destination.mip_level,
863 origin: destination.origin,
864 aspect: destination.aspect,
865 },
866 size: *copy_size,
867 })
868 })
869 }
870
871 pub fn command_encoder_copy_texture_to_buffer(
872 &self,
873 command_encoder_id: CommandEncoderId,
874 source: &wgt::TexelCopyTextureInfo<TextureId>,
875 destination: &TexelCopyBufferInfo,
876 copy_size: &Extent3d,
877 ) -> Result<(), EncoderStateError> {
878 profiling::scope!("CommandEncoder::copy_texture_to_buffer");
879 api_log!(
880 "CommandEncoder::copy_texture_to_buffer {:?} -> {:?} {copy_size:?}",
881 source.texture,
882 destination.buffer
883 );
884
885 let cmd_enc = self.hub.command_encoders.get(command_encoder_id);
886 let mut cmd_buf_data = cmd_enc.data.lock();
887
888 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
889 Ok(ArcCommand::CopyTextureToBuffer {
890 src: wgt::TexelCopyTextureInfo::<Arc<Texture>> {
891 texture: self.resolve_texture_id(source.texture)?,
892 mip_level: source.mip_level,
893 origin: source.origin,
894 aspect: source.aspect,
895 },
896 dst: wgt::TexelCopyBufferInfo::<Arc<Buffer>> {
897 buffer: self.resolve_buffer_id(destination.buffer)?,
898 layout: destination.layout,
899 },
900 size: *copy_size,
901 })
902 })
903 }
904
905 pub fn command_encoder_copy_texture_to_texture(
906 &self,
907 command_encoder_id: CommandEncoderId,
908 source: &wgt::TexelCopyTextureInfo<TextureId>,
909 destination: &wgt::TexelCopyTextureInfo<TextureId>,
910 copy_size: &Extent3d,
911 ) -> Result<(), EncoderStateError> {
912 profiling::scope!("CommandEncoder::copy_texture_to_texture");
913 api_log!(
914 "CommandEncoder::copy_texture_to_texture {:?} -> {:?} {copy_size:?}",
915 source.texture,
916 destination.texture
917 );
918
919 let cmd_enc = self.hub.command_encoders.get(command_encoder_id);
920 let mut cmd_buf_data = cmd_enc.data.lock();
921
922 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
923 Ok(ArcCommand::CopyTextureToTexture {
924 src: wgt::TexelCopyTextureInfo {
925 texture: self.resolve_texture_id(source.texture)?,
926 mip_level: source.mip_level,
927 origin: source.origin,
928 aspect: source.aspect,
929 },
930 dst: wgt::TexelCopyTextureInfo {
931 texture: self.resolve_texture_id(destination.texture)?,
932 mip_level: destination.mip_level,
933 origin: destination.origin,
934 aspect: destination.aspect,
935 },
936 size: *copy_size,
937 })
938 })
939 }
940}
941
942pub(super) fn copy_buffer_to_buffer(
943 state: &mut EncodingState,
944 src_buffer: &Arc<Buffer>,
945 source_offset: BufferAddress,
946 dst_buffer: &Arc<Buffer>,
947 destination_offset: BufferAddress,
948 size: Option<BufferAddress>,
949) -> Result<(), CommandEncoderError> {
950 if src_buffer.is_equal(dst_buffer) {
951 return Err(TransferError::SameSourceDestinationBuffer.into());
952 }
953
954 src_buffer.same_device(state.device)?;
955
956 let src_pending = state
957 .tracker
958 .buffers
959 .set_single(src_buffer, wgt::BufferUses::COPY_SRC);
960
961 let src_raw = src_buffer.try_raw(state.snatch_guard)?;
962 src_buffer
963 .check_usage(BufferUsages::COPY_SRC)
964 .map_err(TransferError::MissingBufferUsage)?;
965 let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer, state.snatch_guard));
967
968 dst_buffer.same_device(state.device)?;
969
970 let dst_pending = state
971 .tracker
972 .buffers
973 .set_single(dst_buffer, wgt::BufferUses::COPY_DST);
974
975 let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;
976 dst_buffer
977 .check_usage(BufferUsages::COPY_DST)
978 .map_err(TransferError::MissingBufferUsage)?;
979 let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));
980
981 let (size, source_end_offset) = match size {
982 Some(size) => (size, source_offset + size),
983 None => (src_buffer.size - source_offset, src_buffer.size),
984 };
985
986 if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
987 return Err(TransferError::UnalignedCopySize(size).into());
988 }
989 if source_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
990 return Err(TransferError::UnalignedBufferOffset(source_offset).into());
991 }
992 if destination_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
993 return Err(TransferError::UnalignedBufferOffset(destination_offset).into());
994 }
995 if !state
996 .device
997 .downlevel
998 .flags
999 .contains(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)
1000 && (src_buffer.usage.contains(BufferUsages::INDEX)
1001 || dst_buffer.usage.contains(BufferUsages::INDEX))
1002 {
1003 let forbidden_usages = BufferUsages::VERTEX
1004 | BufferUsages::UNIFORM
1005 | BufferUsages::INDIRECT
1006 | BufferUsages::STORAGE;
1007 if src_buffer.usage.intersects(forbidden_usages)
1008 || dst_buffer.usage.intersects(forbidden_usages)
1009 {
1010 return Err(TransferError::MissingDownlevelFlags(MissingDownlevelFlags(
1011 wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
1012 ))
1013 .into());
1014 }
1015 }
1016
1017 let destination_end_offset = destination_offset + size;
1018 if source_end_offset > src_buffer.size {
1019 return Err(TransferError::BufferOverrun {
1020 start_offset: source_offset,
1021 end_offset: source_end_offset,
1022 buffer_size: src_buffer.size,
1023 side: CopySide::Source,
1024 }
1025 .into());
1026 }
1027 if destination_end_offset > dst_buffer.size {
1028 return Err(TransferError::BufferOverrun {
1029 start_offset: destination_offset,
1030 end_offset: destination_end_offset,
1031 buffer_size: dst_buffer.size,
1032 side: CopySide::Destination,
1033 }
1034 .into());
1035 }
1036
1037 if size == 0 {
1038 log::trace!("Ignoring copy_buffer_to_buffer of size 0");
1039 return Ok(());
1040 }
1041
1042 state
1044 .buffer_memory_init_actions
1045 .extend(dst_buffer.initialization_status.read().create_action(
1046 dst_buffer,
1047 destination_offset..(destination_offset + size),
1048 MemoryInitKind::ImplicitlyInitialized,
1049 ));
1050 state
1051 .buffer_memory_init_actions
1052 .extend(src_buffer.initialization_status.read().create_action(
1053 src_buffer,
1054 source_offset..(source_offset + size),
1055 MemoryInitKind::NeedsInitializedMemory,
1056 ));
1057
1058 let region = hal::BufferCopy {
1059 src_offset: source_offset,
1060 dst_offset: destination_offset,
1061 size: wgt::BufferSize::new(size).unwrap(),
1062 };
1063 let barriers = src_barrier
1064 .into_iter()
1065 .chain(dst_barrier)
1066 .collect::<Vec<_>>();
1067 unsafe {
1068 state.raw_encoder.transition_buffers(&barriers);
1069 state
1070 .raw_encoder
1071 .copy_buffer_to_buffer(src_raw, dst_raw, &[region]);
1072 }
1073
1074 Ok(())
1075}
1076
1077pub(super) fn copy_buffer_to_texture(
1078 state: &mut EncodingState,
1079 source: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
1080 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
1081 copy_size: &Extent3d,
1082) -> Result<(), CommandEncoderError> {
1083 let dst_texture = &destination.texture;
1084 let src_buffer = &source.buffer;
1085
1086 dst_texture.same_device(state.device)?;
1087 src_buffer.same_device(state.device)?;
1088
1089 let (hal_copy_size, array_layer_count) = validate_texture_copy_range(
1090 destination,
1091 &dst_texture.desc,
1092 CopySide::Destination,
1093 copy_size,
1094 )?;
1095
1096 let (dst_range, dst_base) = extract_texture_selector(destination, copy_size, dst_texture)?;
1097
1098 let src_raw = src_buffer.try_raw(state.snatch_guard)?;
1099 let dst_raw = dst_texture.try_raw(state.snatch_guard)?;
1100
1101 if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1102 log::trace!("Ignoring copy_buffer_to_texture of size 0");
1103 return Ok(());
1104 }
1105
1106 handle_dst_texture_init(state, destination, copy_size, dst_texture)?;
1110
1111 let src_pending = state
1112 .tracker
1113 .buffers
1114 .set_single(src_buffer, wgt::BufferUses::COPY_SRC);
1115
1116 src_buffer
1117 .check_usage(BufferUsages::COPY_SRC)
1118 .map_err(TransferError::MissingBufferUsage)?;
1119 let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer, state.snatch_guard));
1120
1121 let dst_pending =
1122 state
1123 .tracker
1124 .textures
1125 .set_single(dst_texture, dst_range, wgt::TextureUses::COPY_DST);
1126 dst_texture
1127 .check_usage(TextureUsages::COPY_DST)
1128 .map_err(TransferError::MissingTextureUsage)?;
1129 let dst_barrier = dst_pending
1130 .map(|pending| pending.into_hal(dst_raw))
1131 .collect::<Vec<_>>();
1132
1133 validate_texture_copy_dst_format(dst_texture.desc.format, destination.aspect)?;
1134
1135 validate_texture_buffer_copy(
1136 destination,
1137 dst_base.aspect,
1138 &dst_texture.desc,
1139 &source.layout,
1140 true, )?;
1142
1143 let (required_buffer_bytes_in_copy, bytes_per_array_layer, is_contiguous) =
1144 validate_linear_texture_data(
1145 &source.layout,
1146 dst_texture.desc.format,
1147 destination.aspect,
1148 src_buffer.size,
1149 CopySide::Source,
1150 copy_size,
1151 )?;
1152
1153 if dst_texture.desc.format.is_depth_stencil_format() {
1154 state
1155 .device
1156 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
1157 .map_err(TransferError::from)?;
1158 }
1159
1160 handle_buffer_init(
1161 state,
1162 source,
1163 CopySide::Source,
1164 required_buffer_bytes_in_copy,
1165 is_contiguous,
1166 );
1167
1168 let regions = (0..array_layer_count)
1169 .map(|rel_array_layer| {
1170 let mut texture_base = dst_base.clone();
1171 texture_base.array_layer += rel_array_layer;
1172 let mut buffer_layout = source.layout;
1173 buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
1174 hal::BufferTextureCopy {
1175 buffer_layout,
1176 texture_base,
1177 size: hal_copy_size,
1178 }
1179 })
1180 .collect::<Vec<_>>();
1181
1182 unsafe {
1183 state.raw_encoder.transition_textures(&dst_barrier);
1184 state.raw_encoder.transition_buffers(src_barrier.as_slice());
1185 state
1186 .raw_encoder
1187 .copy_buffer_to_texture(src_raw, dst_raw, ®ions);
1188 }
1189
1190 Ok(())
1191}
1192
1193pub(super) fn copy_texture_to_buffer(
1194 state: &mut EncodingState,
1195 source: &TexelCopyTextureInfo,
1196 destination: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
1197 copy_size: &Extent3d,
1198) -> Result<(), CommandEncoderError> {
1199 let src_texture = &source.texture;
1200 let dst_buffer = &destination.buffer;
1201
1202 src_texture.same_device(state.device)?;
1203 dst_buffer.same_device(state.device)?;
1204
1205 let (hal_copy_size, array_layer_count) =
1206 validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
1207
1208 let (src_range, src_base) = extract_texture_selector(source, copy_size, src_texture)?;
1209
1210 let src_raw = src_texture.try_raw(state.snatch_guard)?;
1211 src_texture
1212 .check_usage(TextureUsages::COPY_SRC)
1213 .map_err(TransferError::MissingTextureUsage)?;
1214
1215 if source.mip_level >= src_texture.desc.mip_level_count {
1216 return Err(TransferError::InvalidMipLevel {
1217 requested: source.mip_level,
1218 count: src_texture.desc.mip_level_count,
1219 }
1220 .into());
1221 }
1222
1223 validate_texture_copy_src_format(src_texture.desc.format, source.aspect)?;
1224
1225 validate_texture_buffer_copy(
1226 source,
1227 src_base.aspect,
1228 &src_texture.desc,
1229 &destination.layout,
1230 true, )?;
1232
1233 let (required_buffer_bytes_in_copy, bytes_per_array_layer, is_contiguous) =
1234 validate_linear_texture_data(
1235 &destination.layout,
1236 src_texture.desc.format,
1237 source.aspect,
1238 dst_buffer.size,
1239 CopySide::Destination,
1240 copy_size,
1241 )?;
1242
1243 if src_texture.desc.format.is_depth_stencil_format() {
1244 state
1245 .device
1246 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
1247 .map_err(TransferError::from)?;
1248 }
1249
1250 let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;
1251 dst_buffer
1252 .check_usage(BufferUsages::COPY_DST)
1253 .map_err(TransferError::MissingBufferUsage)?;
1254
1255 if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1256 log::trace!("Ignoring copy_texture_to_buffer of size 0");
1257 return Ok(());
1258 }
1259
1260 handle_src_texture_init(state, source, copy_size, src_texture)?;
1264
1265 let src_pending =
1266 state
1267 .tracker
1268 .textures
1269 .set_single(src_texture, src_range, wgt::TextureUses::COPY_SRC);
1270 let src_barrier = src_pending
1271 .map(|pending| pending.into_hal(src_raw))
1272 .collect::<Vec<_>>();
1273
1274 let dst_pending = state
1275 .tracker
1276 .buffers
1277 .set_single(dst_buffer, wgt::BufferUses::COPY_DST);
1278
1279 let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));
1280
1281 handle_buffer_init(
1282 state,
1283 destination,
1284 CopySide::Destination,
1285 required_buffer_bytes_in_copy,
1286 is_contiguous,
1287 );
1288
1289 let regions = (0..array_layer_count)
1290 .map(|rel_array_layer| {
1291 let mut texture_base = src_base.clone();
1292 texture_base.array_layer += rel_array_layer;
1293 let mut buffer_layout = destination.layout;
1294 buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
1295 hal::BufferTextureCopy {
1296 buffer_layout,
1297 texture_base,
1298 size: hal_copy_size,
1299 }
1300 })
1301 .collect::<Vec<_>>();
1302 unsafe {
1303 state.raw_encoder.transition_buffers(dst_barrier.as_slice());
1304 state.raw_encoder.transition_textures(&src_barrier);
1305 state.raw_encoder.copy_texture_to_buffer(
1306 src_raw,
1307 wgt::TextureUses::COPY_SRC,
1308 dst_raw,
1309 ®ions,
1310 );
1311 }
1312
1313 Ok(())
1314}
1315
1316pub(super) fn copy_texture_to_texture(
1317 state: &mut EncodingState,
1318 source: &TexelCopyTextureInfo,
1319 destination: &TexelCopyTextureInfo,
1320 copy_size: &Extent3d,
1321) -> Result<(), CommandEncoderError> {
1322 let src_texture = &source.texture;
1323 let dst_texture = &destination.texture;
1324
1325 src_texture.same_device(state.device)?;
1326 dst_texture.same_device(state.device)?;
1327
1328 if src_texture.desc.format.remove_srgb_suffix() != dst_texture.desc.format.remove_srgb_suffix()
1331 {
1332 return Err(TransferError::TextureFormatsNotCopyCompatible {
1333 src_format: src_texture.desc.format,
1334 dst_format: dst_texture.desc.format,
1335 }
1336 .into());
1337 }
1338
1339 let (src_copy_size, array_layer_count) =
1340 validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
1341 let (dst_copy_size, _) = validate_texture_copy_range(
1342 destination,
1343 &dst_texture.desc,
1344 CopySide::Destination,
1345 copy_size,
1346 )?;
1347
1348 if Arc::as_ptr(src_texture) == Arc::as_ptr(dst_texture) {
1349 validate_copy_within_same_texture(
1350 source,
1351 destination,
1352 src_texture.desc.format,
1353 array_layer_count,
1354 )?;
1355 }
1356
1357 let (src_range, src_tex_base) = extract_texture_selector(source, copy_size, src_texture)?;
1358 let (dst_range, dst_tex_base) = extract_texture_selector(destination, copy_size, dst_texture)?;
1359 let src_texture_aspects = hal::FormatAspects::from(src_texture.desc.format);
1360 let dst_texture_aspects = hal::FormatAspects::from(dst_texture.desc.format);
1361 if src_tex_base.aspect != src_texture_aspects {
1362 return Err(TransferError::CopySrcMissingAspects.into());
1363 }
1364 if dst_tex_base.aspect != dst_texture_aspects {
1365 return Err(TransferError::CopyDstMissingAspects.into());
1366 }
1367
1368 if src_texture.desc.sample_count != dst_texture.desc.sample_count {
1369 return Err(TransferError::SampleCountNotEqual {
1370 src_sample_count: src_texture.desc.sample_count,
1371 dst_sample_count: dst_texture.desc.sample_count,
1372 }
1373 .into());
1374 }
1375
1376 handle_src_texture_init(state, source, copy_size, src_texture)?;
1380 handle_dst_texture_init(state, destination, copy_size, dst_texture)?;
1381
1382 let src_raw = src_texture.try_raw(state.snatch_guard)?;
1383 src_texture
1384 .check_usage(TextureUsages::COPY_SRC)
1385 .map_err(TransferError::MissingTextureUsage)?;
1386 let dst_raw = dst_texture.try_raw(state.snatch_guard)?;
1387 dst_texture
1388 .check_usage(TextureUsages::COPY_DST)
1389 .map_err(TransferError::MissingTextureUsage)?;
1390
1391 if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1392 log::trace!("Ignoring copy_texture_to_texture of size 0");
1393 return Ok(());
1394 }
1395
1396 let src_pending =
1397 state
1398 .tracker
1399 .textures
1400 .set_single(src_texture, src_range, wgt::TextureUses::COPY_SRC);
1401
1402 let mut barriers: ArrayVec<_, 2> = src_pending
1405 .map(|pending| pending.into_hal(src_raw))
1406 .collect();
1407
1408 let dst_pending =
1409 state
1410 .tracker
1411 .textures
1412 .set_single(dst_texture, dst_range, wgt::TextureUses::COPY_DST);
1413 barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_raw)));
1414
1415 let hal_copy_size = hal::CopyExtent {
1416 width: src_copy_size.width.min(dst_copy_size.width),
1417 height: src_copy_size.height.min(dst_copy_size.height),
1418 depth: src_copy_size.depth.min(dst_copy_size.depth),
1419 };
1420
1421 let regions = (0..array_layer_count).map(|rel_array_layer| {
1422 let mut src_base = src_tex_base.clone();
1423 let mut dst_base = dst_tex_base.clone();
1424 src_base.array_layer += rel_array_layer;
1425 dst_base.array_layer += rel_array_layer;
1426 hal::TextureCopy {
1427 src_base,
1428 dst_base,
1429 size: hal_copy_size,
1430 }
1431 });
1432
1433 let regions = if dst_tex_base.aspect == hal::FormatAspects::DEPTH_STENCIL {
1434 regions
1435 .flat_map(|region| {
1436 let (mut depth, mut stencil) = (region.clone(), region);
1437 depth.src_base.aspect = hal::FormatAspects::DEPTH;
1438 depth.dst_base.aspect = hal::FormatAspects::DEPTH;
1439 stencil.src_base.aspect = hal::FormatAspects::STENCIL;
1440 stencil.dst_base.aspect = hal::FormatAspects::STENCIL;
1441 [depth, stencil]
1442 })
1443 .collect::<Vec<_>>()
1444 } else {
1445 regions.collect::<Vec<_>>()
1446 };
1447 unsafe {
1448 state.raw_encoder.transition_textures(&barriers);
1449 state.raw_encoder.copy_texture_to_texture(
1450 src_raw,
1451 wgt::TextureUses::COPY_SRC,
1452 dst_raw,
1453 ®ions,
1454 );
1455 }
1456
1457 Ok(())
1458}