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 init_tracker::{
18 has_copy_partial_init_tracker_coverage, MemoryInitKind, TextureInitRange,
19 TextureInitTrackerAction,
20 },
21 resource::{
22 Buffer, Labeled, MissingBufferUsageError, MissingTextureUsageError, ParentDevice,
23 RawResourceAccess, Texture, TextureErrorDimension,
24 },
25};
26
27use super::ClearError;
28
29type TexelCopyTextureInfo = wgt::TexelCopyTextureInfo<Arc<Texture>>;
30
31#[derive(Clone, Copy, Debug, Eq, PartialEq)]
32pub enum CopySide {
33 Source,
34 Destination,
35}
36
37#[derive(Clone, Debug, Error)]
39#[non_exhaustive]
40pub enum TransferError {
41 #[error("Source and destination cannot be the same buffer")]
42 SameSourceDestinationBuffer,
43 #[error(transparent)]
44 MissingBufferUsage(#[from] MissingBufferUsageError),
45 #[error(transparent)]
46 MissingTextureUsage(#[from] MissingTextureUsageError),
47 #[error(
48 "Copy at offset {start_offset} bytes would end up overrunning the bounds of the {side:?} buffer of size {buffer_size}"
49 )]
50 BufferStartOffsetOverrun {
51 start_offset: BufferAddress,
52 buffer_size: BufferAddress,
53 side: CopySide,
54 },
55 #[error(
56 "Copy at offset {start_offset} for {size} bytes would end up overrunning the bounds of the {side:?} buffer of size {buffer_size}"
57 )]
58 BufferEndOffsetOverrun {
59 start_offset: BufferAddress,
60 size: BufferAddress,
61 buffer_size: BufferAddress,
62 side: CopySide,
63 },
64 #[error("Copy of {dimension:?} {start_offset}..{end_offset} would end up overrunning the bounds of the {side:?} texture of {dimension:?} size {texture_size}")]
65 TextureOverrun {
66 start_offset: u32,
67 end_offset: u32,
68 texture_size: u32,
69 dimension: TextureErrorDimension,
70 side: CopySide,
71 },
72 #[error("Partial copy of {start_offset}..{end_offset} on {dimension:?} dimension with size {texture_size} \
73 is not supported for the {side:?} texture format {format:?} with {sample_count} samples")]
74 UnsupportedPartialTransfer {
75 format: wgt::TextureFormat,
76 sample_count: u32,
77 start_offset: u32,
78 end_offset: u32,
79 texture_size: u32,
80 dimension: TextureErrorDimension,
81 side: CopySide,
82 },
83 #[error(
84 "Copying{} layers {}..{} to{} layers {}..{} of the same texture is not allowed",
85 if *src_aspects == wgt::TextureAspect::All { String::new() } else { format!(" {src_aspects:?}") },
86 src_origin_z,
87 src_origin_z + array_layer_count,
88 if *dst_aspects == wgt::TextureAspect::All { String::new() } else { format!(" {dst_aspects:?}") },
89 dst_origin_z,
90 dst_origin_z + array_layer_count,
91 )]
92 InvalidCopyWithinSameTexture {
93 src_aspects: wgt::TextureAspect,
94 dst_aspects: wgt::TextureAspect,
95 src_origin_z: u32,
96 dst_origin_z: u32,
97 array_layer_count: u32,
98 },
99 #[error("Unable to select texture aspect {aspect:?} from format {format:?}")]
100 InvalidTextureAspect {
101 format: wgt::TextureFormat,
102 aspect: wgt::TextureAspect,
103 },
104 #[error("Unable to select texture mip level {level} out of {total}")]
105 InvalidTextureMipLevel { level: u32, total: u32 },
106 #[error("Texture dimension must be 2D when copying from an external texture")]
107 InvalidDimensionExternal,
108 #[error("Buffer offset {0} is not aligned to block size or `COPY_BUFFER_ALIGNMENT`")]
109 UnalignedBufferOffset(BufferAddress),
110 #[error("Copy size {0} does not respect `COPY_BUFFER_ALIGNMENT`")]
111 UnalignedCopySize(BufferAddress),
112 #[error("Copy width is not a multiple of block width")]
113 UnalignedCopyWidth,
114 #[error("Copy height is not a multiple of block height")]
115 UnalignedCopyHeight,
116 #[error("Copy origin's x component is not a multiple of block width")]
117 UnalignedCopyOriginX,
118 #[error("Copy origin's y component is not a multiple of block height")]
119 UnalignedCopyOriginY,
120 #[error("Bytes per row ({bytes_per_row}) is not a multiple of {alignment}")]
121 UnalignedBytesPerRow { bytes_per_row: u32, alignment: u32 },
122 #[error("Number of bytes per row needs to be specified since more than one row is copied")]
123 UnspecifiedBytesPerRow,
124 #[error("Number of rows per image needs to be specified since more than one image is copied")]
125 UnspecifiedRowsPerImage,
126 #[error("Number of bytes per row is less than the number of bytes in a complete row")]
127 InvalidBytesPerRow,
128 #[error("Number of rows per image is invalid")]
129 InvalidRowsPerImage,
130 #[error("Overflow while computing the size of the copy")]
131 SizeOverflow,
132 #[error("Copy source aspects must refer to all aspects of the source texture format")]
133 CopySrcMissingAspects,
134 #[error(
135 "Copy destination aspects must refer to all aspects of the destination texture format"
136 )]
137 CopyDstMissingAspects,
138 #[error("Copy aspect must refer to a single aspect of texture format")]
139 CopyAspectNotOne,
140 #[error("Copying from textures with format {0:?} is forbidden")]
141 CopyFromForbiddenTextureFormat(wgt::TextureFormat),
142 #[error("Copying from textures with format {format:?} and aspect {aspect:?} is forbidden")]
143 CopyFromForbiddenTextureFormatAspect {
144 format: wgt::TextureFormat,
145 aspect: wgt::TextureAspect,
146 },
147 #[error("Copying to textures with format {0:?} is forbidden")]
148 CopyToForbiddenTextureFormat(wgt::TextureFormat),
149 #[error("Copying to textures with format {format:?} and aspect {aspect:?} is forbidden")]
150 CopyToForbiddenTextureFormatAspect {
151 format: wgt::TextureFormat,
152 aspect: wgt::TextureAspect,
153 },
154 #[error(
155 "Copying to textures with format {0:?} is forbidden when copying from external texture"
156 )]
157 ExternalCopyToForbiddenTextureFormat(wgt::TextureFormat),
158 #[error(
159 "Source format ({src_format:?}) and destination format ({dst_format:?}) are not copy-compatible (they may only differ in srgb-ness)"
160 )]
161 TextureFormatsNotCopyCompatible {
162 src_format: wgt::TextureFormat,
163 dst_format: wgt::TextureFormat,
164 },
165 #[error(transparent)]
166 MemoryInitFailure(#[from] ClearError),
167 #[error("Cannot encode this copy because of a missing downelevel flag")]
168 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
169 #[error("Source texture sample count must be 1, got {sample_count}")]
170 InvalidSampleCount { sample_count: u32 },
171 #[error(
172 "Source sample count ({src_sample_count:?}) and destination sample count ({dst_sample_count:?}) are not equal"
173 )]
174 SampleCountNotEqual {
175 src_sample_count: u32,
176 dst_sample_count: u32,
177 },
178 #[error("Requested mip level {requested} does not exist (count: {count})")]
179 InvalidMipLevel { requested: u32, count: u32 },
180 #[error("Buffer is expected to be unmapped, but was not")]
181 BufferNotAvailable,
182}
183
184impl WebGpuError for TransferError {
185 fn webgpu_error_type(&self) -> ErrorType {
186 match self {
187 Self::MissingBufferUsage(e) => e.webgpu_error_type(),
188 Self::MissingTextureUsage(e) => e.webgpu_error_type(),
189 Self::MemoryInitFailure(e) => e.webgpu_error_type(),
190
191 Self::BufferEndOffsetOverrun { .. }
192 | Self::TextureOverrun { .. }
193 | Self::BufferStartOffsetOverrun { .. }
194 | Self::UnsupportedPartialTransfer { .. }
195 | Self::InvalidCopyWithinSameTexture { .. }
196 | Self::InvalidTextureAspect { .. }
197 | Self::InvalidTextureMipLevel { .. }
198 | Self::InvalidDimensionExternal
199 | Self::UnalignedBufferOffset(..)
200 | Self::UnalignedCopySize(..)
201 | Self::UnalignedCopyWidth
202 | Self::UnalignedCopyHeight
203 | Self::UnalignedCopyOriginX
204 | Self::UnalignedCopyOriginY
205 | Self::UnalignedBytesPerRow { .. }
206 | Self::UnspecifiedBytesPerRow
207 | Self::UnspecifiedRowsPerImage
208 | Self::InvalidBytesPerRow
209 | Self::InvalidRowsPerImage
210 | Self::SizeOverflow
211 | Self::CopySrcMissingAspects
212 | Self::CopyDstMissingAspects
213 | Self::CopyAspectNotOne
214 | Self::CopyFromForbiddenTextureFormat(..)
215 | Self::CopyFromForbiddenTextureFormatAspect { .. }
216 | Self::CopyToForbiddenTextureFormat(..)
217 | Self::CopyToForbiddenTextureFormatAspect { .. }
218 | Self::ExternalCopyToForbiddenTextureFormat(..)
219 | Self::TextureFormatsNotCopyCompatible { .. }
220 | Self::MissingDownlevelFlags(..)
221 | Self::InvalidSampleCount { .. }
222 | Self::SampleCountNotEqual { .. }
223 | Self::InvalidMipLevel { .. }
224 | Self::SameSourceDestinationBuffer
225 | Self::BufferNotAvailable => ErrorType::Validation,
226 }
227 }
228}
229
230impl From<BufferTextureCopyInfoError> for TransferError {
231 fn from(value: BufferTextureCopyInfoError) -> Self {
232 match value {
233 BufferTextureCopyInfoError::InvalidBytesPerRow => Self::InvalidBytesPerRow,
234 BufferTextureCopyInfoError::InvalidRowsPerImage => Self::InvalidRowsPerImage,
235 BufferTextureCopyInfoError::ImageStrideOverflow
236 | BufferTextureCopyInfoError::ImageBytesOverflow(_)
237 | BufferTextureCopyInfoError::ArraySizeOverflow(_) => Self::SizeOverflow,
238 }
239 }
240}
241
242pub(crate) fn extract_texture_selector<T>(
243 copy_texture: &wgt::TexelCopyTextureInfo<T>,
244 copy_size: &Extent3d,
245 texture: &Texture,
246) -> Result<(TextureSelector, hal::TextureCopyBase), TransferError> {
247 let format = texture.desc.format;
248 let copy_aspect = hal::FormatAspects::new(format, copy_texture.aspect);
249 if copy_aspect.is_empty() {
250 return Err(TransferError::InvalidTextureAspect {
251 format,
252 aspect: copy_texture.aspect,
253 });
254 }
255
256 let (layers, origin_z) = match texture.desc.dimension {
257 wgt::TextureDimension::D1 => (0..1, 0),
258 wgt::TextureDimension::D2 => (
259 copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers,
260 0,
261 ),
262 wgt::TextureDimension::D3 => (0..1, copy_texture.origin.z),
263 };
264 let base = hal::TextureCopyBase {
265 origin: wgt::Origin3d {
266 x: copy_texture.origin.x,
267 y: copy_texture.origin.y,
268 z: origin_z,
269 },
270 array_layer: layers.start,
272 mip_level: copy_texture.mip_level,
273 aspect: copy_aspect,
274 };
275 let selector = TextureSelector {
276 mips: copy_texture.mip_level..copy_texture.mip_level + 1,
277 layers,
278 };
279
280 Ok((selector, base))
281}
282
283pub(crate) fn validate_linear_texture_data(
295 layout: &wgt::TexelCopyBufferLayout,
296 format: wgt::TextureFormat,
297 aspect: wgt::TextureAspect,
298 buffer_size: BufferAddress,
299 buffer_side: CopySide,
300 copy_size: &Extent3d,
301) -> Result<(BufferAddress, BufferAddress, bool), TransferError> {
302 let wgt::BufferTextureCopyInfo {
303 copy_width,
304 copy_height,
305 depth_or_array_layers,
306
307 offset,
308
309 block_size_bytes: _,
310 block_width_texels,
311 block_height_texels,
312
313 width_blocks: _,
314 height_blocks,
315
316 row_bytes_dense,
317 row_stride_bytes,
318
319 image_stride_rows: _,
320 image_stride_bytes,
321
322 image_rows_dense: _,
323 image_bytes_dense,
324
325 bytes_in_copy,
326 } = layout.get_buffer_texture_copy_info(format, aspect, copy_size)?;
327
328 if !copy_width.is_multiple_of(block_width_texels) {
329 return Err(TransferError::UnalignedCopyWidth);
330 }
331 if !copy_height.is_multiple_of(block_height_texels) {
332 return Err(TransferError::UnalignedCopyHeight);
333 }
334
335 let requires_multiple_rows = depth_or_array_layers > 1 || height_blocks > 1;
336 let requires_multiple_images = depth_or_array_layers > 1;
337
338 if layout.bytes_per_row.is_none() && requires_multiple_rows {
343 return Err(TransferError::UnspecifiedBytesPerRow);
344 }
345
346 if layout.rows_per_image.is_none() && requires_multiple_images {
347 return Err(TransferError::UnspecifiedRowsPerImage);
348 };
349
350 if offset > buffer_size {
351 return Err(TransferError::BufferStartOffsetOverrun {
352 start_offset: offset,
353 buffer_size,
354 side: buffer_side,
355 });
356 }
357 if bytes_in_copy > buffer_size - offset {
359 return Err(TransferError::BufferEndOffsetOverrun {
360 start_offset: offset,
361 size: bytes_in_copy,
362 buffer_size,
363 side: buffer_side,
364 });
365 }
366
367 let is_contiguous = (row_stride_bytes == row_bytes_dense || !requires_multiple_rows)
368 && (image_stride_bytes == image_bytes_dense || !requires_multiple_images);
369
370 Ok((bytes_in_copy, image_stride_bytes, is_contiguous))
371}
372
373pub(crate) fn validate_texture_copy_src_format(
382 format: wgt::TextureFormat,
383 aspect: wgt::TextureAspect,
384) -> Result<(), TransferError> {
385 use wgt::TextureAspect as Ta;
386 use wgt::TextureFormat as Tf;
387 match (format, aspect) {
388 (Tf::Depth24Plus, _) => Err(TransferError::CopyFromForbiddenTextureFormat(format)),
389 (Tf::Depth24PlusStencil8, Ta::DepthOnly) => {
390 Err(TransferError::CopyFromForbiddenTextureFormatAspect { format, aspect })
391 }
392 _ => Ok(()),
393 }
394}
395
396pub(crate) fn validate_texture_copy_dst_format(
405 format: wgt::TextureFormat,
406 aspect: wgt::TextureAspect,
407) -> Result<(), TransferError> {
408 use wgt::TextureAspect as Ta;
409 use wgt::TextureFormat as Tf;
410 match (format, aspect) {
411 (Tf::Depth24Plus | Tf::Depth32Float, _) => {
412 Err(TransferError::CopyToForbiddenTextureFormat(format))
413 }
414 (Tf::Depth24PlusStencil8 | Tf::Depth32FloatStencil8, Ta::DepthOnly) => {
415 Err(TransferError::CopyToForbiddenTextureFormatAspect { format, aspect })
416 }
417 _ => Ok(()),
418 }
419}
420
421pub(crate) fn validate_texture_buffer_copy<T>(
450 texture_copy_view: &wgt::TexelCopyTextureInfo<T>,
451 aspect: hal::FormatAspects,
452 desc: &wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
453 layout: &wgt::TexelCopyBufferLayout,
454 aligned: bool,
455 features: wgt::Features,
456) -> Result<(), TransferError> {
457 if desc.sample_count != 1 {
458 return Err(TransferError::InvalidSampleCount {
459 sample_count: desc.sample_count,
460 });
461 }
462
463 if !aspect.is_one() {
464 return Err(TransferError::CopyAspectNotOne);
465 }
466
467 let block_copy_size = || {
468 desc.format
469 .block_copy_size(Some(texture_copy_view.aspect))
470 .expect("non-copyable formats should have been rejected previously")
471 };
472 let offset_alignment = if desc.format.is_depth_stencil_format() {
473 4
474 } else {
475 block_copy_size()
476 };
477
478 if aligned && !layout.offset.is_multiple_of(u64::from(offset_alignment)) {
479 return Err(TransferError::UnalignedBufferOffset(layout.offset));
480 }
481
482 if let Some(bytes_per_row) = layout.bytes_per_row {
483 let alignment = if features.contains(wgt::Features::UNRESTRICTED_BYTES_PER_ROW) {
484 block_copy_size()
485 } else {
486 wgt::COPY_BYTES_PER_ROW_ALIGNMENT
487 };
488 if aligned && !bytes_per_row.is_multiple_of(alignment) {
489 return Err(TransferError::UnalignedBytesPerRow {
490 bytes_per_row,
491 alignment,
492 });
493 }
494 }
495
496 Ok(())
497}
498
499pub(crate) fn validate_texture_copy_range<T>(
510 texture_copy_view: &wgt::TexelCopyTextureInfo<T>,
511 desc: &wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
512 texture_side: CopySide,
513 copy_size: &Extent3d,
514) -> Result<(hal::CopyExtent, u32), TransferError> {
515 let (block_width, block_height) = desc.format.block_dimensions();
516
517 let extent_virtual = desc.mip_level_size(texture_copy_view.mip_level).ok_or(
518 TransferError::InvalidTextureMipLevel {
519 level: texture_copy_view.mip_level,
520 total: desc.mip_level_count,
521 },
522 )?;
523 let extent = extent_virtual.physical_size(desc.format);
525
526 let requires_exact_size = desc.format.is_depth_stencil_format() || desc.sample_count > 1;
529
530 let check_dimension = |dimension: TextureErrorDimension,
533 start_offset: u32,
534 size: u32,
535 texture_size: u32,
536 requires_exact_size: bool|
537 -> Result<(), TransferError> {
538 if requires_exact_size && (start_offset != 0 || size != texture_size) {
539 Err(TransferError::UnsupportedPartialTransfer {
540 format: desc.format,
541 sample_count: desc.sample_count,
542 start_offset,
543 end_offset: start_offset.wrapping_add(size),
544 texture_size,
545 dimension,
546 side: texture_side,
547 })
548 } else if start_offset > texture_size || texture_size - start_offset < size {
551 Err(TransferError::TextureOverrun {
552 start_offset,
553 end_offset: start_offset.wrapping_add(size),
554 texture_size,
555 dimension,
556 side: texture_side,
557 })
558 } else {
559 Ok(())
560 }
561 };
562
563 check_dimension(
564 TextureErrorDimension::X,
565 texture_copy_view.origin.x,
566 copy_size.width,
567 extent.width,
568 requires_exact_size,
569 )?;
570 check_dimension(
571 TextureErrorDimension::Y,
572 texture_copy_view.origin.y,
573 copy_size.height,
574 extent.height,
575 requires_exact_size,
576 )?;
577 check_dimension(
578 TextureErrorDimension::Z,
579 texture_copy_view.origin.z,
580 copy_size.depth_or_array_layers,
581 extent.depth_or_array_layers,
582 false, )?;
584
585 if !texture_copy_view.origin.x.is_multiple_of(block_width) {
586 return Err(TransferError::UnalignedCopyOriginX);
587 }
588 if !texture_copy_view.origin.y.is_multiple_of(block_height) {
589 return Err(TransferError::UnalignedCopyOriginY);
590 }
591 if !copy_size.width.is_multiple_of(block_width) {
592 return Err(TransferError::UnalignedCopyWidth);
593 }
594 if !copy_size.height.is_multiple_of(block_height) {
595 return Err(TransferError::UnalignedCopyHeight);
596 }
597
598 let (depth, array_layer_count) = match desc.dimension {
599 wgt::TextureDimension::D1 => (1, 1),
600 wgt::TextureDimension::D2 => (1, copy_size.depth_or_array_layers),
601 wgt::TextureDimension::D3 => (copy_size.depth_or_array_layers, 1),
602 };
603
604 let copy_extent = hal::CopyExtent {
605 width: copy_size.width,
606 height: copy_size.height,
607 depth,
608 };
609 Ok((copy_extent, array_layer_count))
610}
611
612pub(crate) fn validate_copy_within_same_texture<T>(
623 src: &wgt::TexelCopyTextureInfo<T>,
624 dst: &wgt::TexelCopyTextureInfo<T>,
625 format: wgt::TextureFormat,
626 array_layer_count: u32,
627) -> Result<(), TransferError> {
628 let src_aspects = hal::FormatAspects::new(format, src.aspect);
629 let dst_aspects = hal::FormatAspects::new(format, dst.aspect);
630 if (src_aspects & dst_aspects).is_empty() {
631 return Ok(());
633 }
634
635 if src.origin.z >= dst.origin.z + array_layer_count
636 || dst.origin.z >= src.origin.z + array_layer_count
637 {
638 return Ok(());
640 }
641
642 if src.mip_level != dst.mip_level {
643 return Ok(());
645 }
646
647 Err(TransferError::InvalidCopyWithinSameTexture {
648 src_aspects: src.aspect,
649 dst_aspects: dst.aspect,
650 src_origin_z: src.origin.z,
651 dst_origin_z: dst.origin.z,
652 array_layer_count,
653 })
654}
655
656fn handle_texture_init(
657 state: &mut EncodingState,
658 init_kind: MemoryInitKind,
659 copy_texture: &TexelCopyTextureInfo,
660 copy_size: &Extent3d,
661 texture: &Arc<Texture>,
662) -> Result<(), ClearError> {
663 let init_layer_range = if texture.desc.dimension == wgt::TextureDimension::D3 {
664 0..1
666 } else {
667 copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers
668 };
669 let init_action = TextureInitTrackerAction {
670 texture: texture.clone(),
671 range: TextureInitRange {
672 mip_range: copy_texture.mip_level..copy_texture.mip_level + 1,
673 layer_range: init_layer_range,
674 },
675 kind: init_kind,
676 };
677
678 let accessed_depth_slices = (texture.desc.dimension == wgt::TextureDimension::D3)
687 .then(|| copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers);
688 let immediate_inits = state
689 .texture_memory_actions
690 .register_init_action(&{ init_action }, accessed_depth_slices);
691
692 if !immediate_inits.is_empty() {
694 for init in immediate_inits {
695 let index = init.layer_or_depth_slice;
696 let (layer_range, depth_slice) = if texture.desc.dimension == wgt::TextureDimension::D3
697 {
698 (0..1, Some(index))
699 } else {
700 (index..(index + 1), None)
701 };
702 clear_texture(
703 &init.texture,
704 TextureInitRange {
705 mip_range: init.mip_level..(init.mip_level + 1),
706 layer_range,
707 },
708 depth_slice,
709 state.raw_encoder,
710 &mut state.tracker.textures,
711 &state.device.alignments,
712 state.device.zero_buffer.as_ref(),
713 state.snatch_guard,
714 state.device.instance_flags,
715 )?;
716 }
717 }
718
719 Ok(())
720}
721
722fn handle_src_texture_init(
727 state: &mut EncodingState,
728 source: &TexelCopyTextureInfo,
729 copy_size: &Extent3d,
730 texture: &Arc<Texture>,
731) -> Result<(), TransferError> {
732 handle_texture_init(
733 state,
734 MemoryInitKind::NeedsInitializedMemory,
735 source,
736 copy_size,
737 texture,
738 )?;
739 Ok(())
740}
741
742fn handle_dst_texture_init(
747 state: &mut EncodingState,
748 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
749 copy_size: &Extent3d,
750 texture: &Arc<Texture>,
751) -> Result<(), TransferError> {
752 let dst_init_kind =
757 if has_copy_partial_init_tracker_coverage(copy_size, destination, &texture.desc) {
758 MemoryInitKind::NeedsInitializedMemory
759 } else {
760 MemoryInitKind::ImplicitlyInitialized
761 };
762
763 handle_texture_init(state, dst_init_kind, destination, copy_size, texture)?;
764 Ok(())
765}
766
767fn handle_buffer_init(
772 state: &mut EncodingState,
773 info: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
774 direction: CopySide,
775 required_buffer_bytes_in_copy: BufferAddress,
776 is_contiguous: bool,
777) {
778 const ALIGN_SIZE: BufferAddress = wgt::COPY_BUFFER_ALIGNMENT;
779 const ALIGN_MASK: BufferAddress = wgt::COPY_BUFFER_ALIGNMENT - 1;
780
781 let buffer = &info.buffer;
782 let start = info.layout.offset;
783 let end = info.layout.offset + required_buffer_bytes_in_copy;
784 if !is_contiguous || direction == CopySide::Source {
785 let aligned_start = start & !ALIGN_MASK;
796 let aligned_end = (end + ALIGN_MASK) & !ALIGN_MASK;
797 state
798 .buffer_memory_init_actions
799 .extend(buffer.initialization_status.read().create_action(
800 buffer,
801 aligned_start..aligned_end,
802 MemoryInitKind::NeedsInitializedMemory,
803 ));
804 } else {
805 let aligned_start = (start + ALIGN_MASK) & !ALIGN_MASK;
815 let aligned_end = end & !ALIGN_MASK;
816 if aligned_start != start {
817 state.buffer_memory_init_actions.extend(
818 buffer.initialization_status.read().create_action(
819 buffer,
820 aligned_start - ALIGN_SIZE..aligned_start,
821 MemoryInitKind::NeedsInitializedMemory,
822 ),
823 );
824 }
825 if aligned_start != aligned_end {
826 state.buffer_memory_init_actions.extend(
827 buffer.initialization_status.read().create_action(
828 buffer,
829 aligned_start..aligned_end,
830 MemoryInitKind::ImplicitlyInitialized,
831 ),
832 );
833 }
834 if aligned_end != end {
835 state.buffer_memory_init_actions.extend(
841 buffer.initialization_status.read().create_action(
842 buffer,
843 aligned_end..aligned_end + ALIGN_SIZE,
844 MemoryInitKind::NeedsInitializedMemory,
845 ),
846 );
847 }
848 }
849}
850
851impl super::CommandEncoder {
852 fn copy_buffer_to_buffer_inner(
853 self: &Arc<Self>,
854 source: Arc<Buffer>,
855 source_offset: BufferAddress,
856 destination: Arc<Buffer>,
857 destination_offset: BufferAddress,
858 size: Option<BufferAddress>,
859 ) -> Result<(), EncoderStateError> {
860 profiling::scope!("CommandEncoder::copy_buffer_to_buffer");
861 api_log!(
862 "CommandEncoder::copy_buffer_to_buffer {:?} -> {:?} {size:?}bytes",
863 Arc::as_ptr(&source),
864 Arc::as_ptr(&destination)
865 );
866
867 let mut cmd_buf_data = self.data.lock();
868
869 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
870 source.check_is_valid()?;
871 destination.check_is_valid()?;
872 Ok(ArcCommand::CopyBufferToBuffer {
873 src: source,
874 src_offset: source_offset,
875 dst: destination,
876 dst_offset: destination_offset,
877 size,
878 })
879 })
880 }
881
882 pub fn copy_buffer_to_buffer(
883 self: &Arc<Self>,
884 source: Arc<Buffer>,
885 source_offset: BufferAddress,
886 destination: Arc<Buffer>,
887 destination_offset: BufferAddress,
888 size: Option<BufferAddress>,
889 ) {
890 if let Err(err) = self.copy_buffer_to_buffer_inner(
891 source,
892 source_offset,
893 destination,
894 destination_offset,
895 size,
896 ) {
897 self.device.handle_error(
898 err,
899 Some(self.label()),
900 "CommandEncoder::copy_buffer_to_buffer",
901 );
902 }
903 }
904
905 fn copy_buffer_to_texture_inner(
906 self: &Arc<Self>,
907 source: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
908 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
909 copy_size: &Extent3d,
910 ) -> Result<(), EncoderStateError> {
911 profiling::scope!("CommandEncoder::copy_buffer_to_texture");
912 api_log!(
913 "CommandEncoder::copy_buffer_to_texture {:?} -> {:?} {copy_size:?}",
914 Arc::as_ptr(&source.buffer),
915 Arc::as_ptr(&destination.texture)
916 );
917
918 let mut cmd_buf_data = self.data.lock();
919
920 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
921 let texture = destination.texture.clone();
922 texture.check_valid()?;
923 let source_buffer = source.buffer.clone();
924 source_buffer.check_is_valid()?;
925 Ok(ArcCommand::CopyBufferToTexture {
926 src: wgt::TexelCopyBufferInfo::<Arc<Buffer>> {
927 buffer: source_buffer,
928 layout: source.layout,
929 },
930 dst: wgt::TexelCopyTextureInfo::<Arc<Texture>> {
931 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 pub fn copy_buffer_to_texture(
942 self: &Arc<Self>,
943 source: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
944 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
945 copy_size: &Extent3d,
946 ) {
947 if let Err(err) = self.copy_buffer_to_texture_inner(source, destination, copy_size) {
948 self.device.handle_error(
949 err,
950 Some(self.label()),
951 "CommandEncoder::copy_buffer_to_texture",
952 );
953 }
954 }
955
956 fn copy_texture_to_buffer_inner(
957 self: &Arc<Self>,
958 source: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
959 destination: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
960 copy_size: &Extent3d,
961 ) -> Result<(), EncoderStateError> {
962 profiling::scope!("CommandEncoder::copy_texture_to_buffer");
963 api_log!(
964 "CommandEncoder::copy_texture_to_buffer {:?} -> {:?} {copy_size:?}",
965 Arc::as_ptr(&source.texture),
966 Arc::as_ptr(&destination.buffer)
967 );
968
969 let mut cmd_buf_data = self.data.lock();
970
971 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
972 let texture = source.texture.clone();
973 texture.check_valid()?;
974 let destination_buffer = destination.buffer.clone();
975 destination_buffer.check_is_valid()?;
976 Ok(ArcCommand::CopyTextureToBuffer {
977 src: wgt::TexelCopyTextureInfo::<Arc<Texture>> {
978 texture,
979 mip_level: source.mip_level,
980 origin: source.origin,
981 aspect: source.aspect,
982 },
983 dst: wgt::TexelCopyBufferInfo::<Arc<Buffer>> {
984 buffer: destination_buffer,
985 layout: destination.layout,
986 },
987 size: *copy_size,
988 })
989 })
990 }
991
992 pub fn copy_texture_to_buffer(
993 self: &Arc<Self>,
994 source: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
995 destination: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
996 copy_size: &Extent3d,
997 ) {
998 if let Err(err) = self.copy_texture_to_buffer_inner(source, destination, copy_size) {
999 self.device.handle_error(
1000 err,
1001 Some(self.label()),
1002 "CommandEncoder::copy_texture_to_buffer",
1003 );
1004 }
1005 }
1006
1007 fn copy_texture_to_texture_inner(
1008 self: &Arc<Self>,
1009 source: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
1010 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
1011 copy_size: &Extent3d,
1012 ) -> Result<(), EncoderStateError> {
1013 profiling::scope!("CommandEncoder::copy_texture_to_texture");
1014 api_log!(
1015 "CommandEncoder::copy_texture_to_texture {:?} -> {:?} {copy_size:?}",
1016 Arc::as_ptr(&source.texture),
1017 Arc::as_ptr(&destination.texture)
1018 );
1019
1020 let mut cmd_buf_data = self.data.lock();
1021
1022 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1023 let src_texture = source.texture.clone();
1024 let dst_texture = destination.texture.clone();
1025 src_texture.check_valid()?;
1026 dst_texture.check_valid()?;
1027 Ok(ArcCommand::CopyTextureToTexture {
1028 src: wgt::TexelCopyTextureInfo {
1029 texture: src_texture,
1030 mip_level: source.mip_level,
1031 origin: source.origin,
1032 aspect: source.aspect,
1033 },
1034 dst: wgt::TexelCopyTextureInfo {
1035 texture: dst_texture,
1036 mip_level: destination.mip_level,
1037 origin: destination.origin,
1038 aspect: destination.aspect,
1039 },
1040 size: *copy_size,
1041 })
1042 })
1043 }
1044
1045 pub fn copy_texture_to_texture(
1046 self: &Arc<Self>,
1047 source: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
1048 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
1049 copy_size: &Extent3d,
1050 ) {
1051 if let Err(err) = self.copy_texture_to_texture_inner(source, destination, copy_size) {
1052 self.device.handle_error(
1053 err,
1054 Some(self.label()),
1055 "CommandEncoder::copy_texture_to_texture",
1056 );
1057 }
1058 }
1059}
1060
1061pub(super) fn copy_buffer_to_buffer(
1062 state: &mut EncodingState,
1063 src_buffer: &Arc<Buffer>,
1064 source_offset: BufferAddress,
1065 dst_buffer: &Arc<Buffer>,
1066 destination_offset: BufferAddress,
1067 size: Option<BufferAddress>,
1068) -> Result<(), CommandEncoderError> {
1069 if src_buffer.is_equal(dst_buffer) {
1070 return Err(TransferError::SameSourceDestinationBuffer.into());
1071 }
1072
1073 src_buffer.same_device(state.device)?;
1074
1075 let src_pending = state
1076 .tracker
1077 .buffers
1078 .set_single(src_buffer, wgt::BufferUses::COPY_SRC);
1079
1080 let src_raw = src_buffer.try_raw(state.snatch_guard)?;
1081 src_buffer
1082 .check_usage(BufferUsages::COPY_SRC)
1083 .map_err(TransferError::MissingBufferUsage)?;
1084 let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer, state.snatch_guard));
1086
1087 dst_buffer.same_device(state.device)?;
1088
1089 let dst_pending = state
1090 .tracker
1091 .buffers
1092 .set_single(dst_buffer, wgt::BufferUses::COPY_DST);
1093
1094 let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;
1095 dst_buffer
1096 .check_usage(BufferUsages::COPY_DST)
1097 .map_err(TransferError::MissingBufferUsage)?;
1098 let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));
1099
1100 if source_offset > src_buffer.size {
1101 return Err(TransferError::BufferStartOffsetOverrun {
1102 start_offset: source_offset,
1103 buffer_size: src_buffer.size,
1104 side: CopySide::Source,
1105 }
1106 .into());
1107 }
1108 let size = size.unwrap_or_else(|| {
1109 src_buffer.size - source_offset
1111 });
1112
1113 if !size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
1114 return Err(TransferError::UnalignedCopySize(size).into());
1115 }
1116 if !source_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
1117 return Err(TransferError::UnalignedBufferOffset(source_offset).into());
1118 }
1119 if !destination_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
1120 return Err(TransferError::UnalignedBufferOffset(destination_offset).into());
1121 }
1122 if !state
1123 .device
1124 .downlevel
1125 .flags
1126 .contains(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)
1127 && (src_buffer.usage.contains(BufferUsages::INDEX)
1128 || dst_buffer.usage.contains(BufferUsages::INDEX))
1129 {
1130 let forbidden_usages = BufferUsages::VERTEX
1131 | BufferUsages::UNIFORM
1132 | BufferUsages::INDIRECT
1133 | BufferUsages::STORAGE;
1134 if src_buffer.usage.intersects(forbidden_usages)
1135 || dst_buffer.usage.intersects(forbidden_usages)
1136 {
1137 return Err(TransferError::MissingDownlevelFlags(MissingDownlevelFlags(
1138 wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
1139 ))
1140 .into());
1141 }
1142 }
1143
1144 if size > src_buffer.size - source_offset {
1145 return Err(TransferError::BufferEndOffsetOverrun {
1146 start_offset: source_offset,
1147 size,
1148 buffer_size: src_buffer.size,
1149 side: CopySide::Source,
1150 }
1151 .into());
1152 }
1153 let source_end_offset = source_offset + size;
1155
1156 if destination_offset > dst_buffer.size {
1157 return Err(TransferError::BufferStartOffsetOverrun {
1158 start_offset: destination_offset,
1159 buffer_size: dst_buffer.size,
1160 side: CopySide::Destination,
1161 }
1162 .into());
1163 }
1164 if size > dst_buffer.size - destination_offset {
1166 return Err(TransferError::BufferEndOffsetOverrun {
1167 start_offset: destination_offset,
1168 size,
1169 buffer_size: dst_buffer.size,
1170 side: CopySide::Destination,
1171 }
1172 .into());
1173 }
1174 let destination_end_offset = destination_offset + size;
1176
1177 if size == 0 {
1180 log::trace!("Ignoring copy_buffer_to_buffer of size 0");
1181 return Ok(());
1182 }
1183
1184 state
1186 .buffer_memory_init_actions
1187 .extend(dst_buffer.initialization_status.read().create_action(
1188 dst_buffer,
1189 destination_offset..destination_end_offset,
1190 MemoryInitKind::ImplicitlyInitialized,
1191 ));
1192 state
1193 .buffer_memory_init_actions
1194 .extend(src_buffer.initialization_status.read().create_action(
1195 src_buffer,
1196 source_offset..source_end_offset,
1197 MemoryInitKind::NeedsInitializedMemory,
1198 ));
1199
1200 let region = hal::BufferCopy {
1201 src_offset: source_offset,
1202 dst_offset: destination_offset,
1203 size: wgt::BufferSize::new(size).unwrap(),
1204 };
1205 let barriers = src_barrier
1206 .into_iter()
1207 .chain(dst_barrier)
1208 .collect::<Vec<_>>();
1209 unsafe {
1210 state.raw_encoder.transition_buffers(&barriers);
1211 state
1212 .raw_encoder
1213 .copy_buffer_to_buffer(src_raw, dst_raw, &[region]);
1214 }
1215
1216 Ok(())
1217}
1218
1219pub(super) fn copy_buffer_to_texture(
1220 state: &mut EncodingState,
1221 source: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
1222 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
1223 copy_size: &Extent3d,
1224) -> Result<(), CommandEncoderError> {
1225 let dst_texture = &destination.texture;
1226 let src_buffer = &source.buffer;
1227
1228 dst_texture.same_device(state.device)?;
1229 src_buffer.same_device(state.device)?;
1230
1231 let (hal_copy_size, array_layer_count) = validate_texture_copy_range(
1232 destination,
1233 &dst_texture.desc,
1234 CopySide::Destination,
1235 copy_size,
1236 )?;
1237
1238 let (dst_range, dst_base) = extract_texture_selector(destination, copy_size, dst_texture)?;
1239
1240 let src_raw = src_buffer.try_raw(state.snatch_guard)?;
1241 src_buffer
1242 .check_usage(BufferUsages::COPY_SRC)
1243 .map_err(TransferError::MissingBufferUsage)?;
1244
1245 let dst_raw = dst_texture.try_inner(state.snatch_guard)?.raw();
1246 dst_texture
1247 .check_usage(TextureUsages::COPY_DST)
1248 .map_err(TransferError::MissingTextureUsage)?;
1249
1250 validate_texture_copy_dst_format(dst_texture.desc.format, destination.aspect)?;
1251
1252 validate_texture_buffer_copy(
1253 destination,
1254 dst_base.aspect,
1255 &dst_texture.desc,
1256 &source.layout,
1257 true, state.device.features,
1259 )?;
1260
1261 let (required_buffer_bytes_in_copy, bytes_per_array_layer, is_contiguous) =
1262 validate_linear_texture_data(
1263 &source.layout,
1264 dst_texture.desc.format,
1265 destination.aspect,
1266 src_buffer.size,
1267 CopySide::Source,
1268 copy_size,
1269 )?;
1270
1271 if dst_texture.desc.format.is_depth_stencil_format() {
1272 state
1273 .device
1274 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
1275 .map_err(TransferError::from)?;
1276 }
1277
1278 if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1281 log::trace!("Ignoring copy_buffer_to_texture of size 0");
1282 return Ok(());
1283 }
1284
1285 handle_dst_texture_init(state, destination, copy_size, dst_texture)?;
1289
1290 let src_pending = state
1291 .tracker
1292 .buffers
1293 .set_single(src_buffer, wgt::BufferUses::COPY_SRC);
1294 let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer, state.snatch_guard));
1295
1296 let dst_pending =
1297 state
1298 .tracker
1299 .textures
1300 .set_single(dst_texture, dst_range, wgt::TextureUses::COPY_DST);
1301 let dst_barrier = dst_pending
1302 .map(|pending| pending.into_hal(dst_raw))
1303 .collect::<Vec<_>>();
1304
1305 handle_buffer_init(
1306 state,
1307 source,
1308 CopySide::Source,
1309 required_buffer_bytes_in_copy,
1310 is_contiguous,
1311 );
1312
1313 let regions = (0..array_layer_count)
1314 .map(|rel_array_layer| {
1315 let mut texture_base = dst_base.clone();
1316 texture_base.array_layer += rel_array_layer;
1317 let mut buffer_layout = source.layout;
1318 buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
1319 hal::BufferTextureCopy {
1320 buffer_layout,
1321 texture_base,
1322 size: hal_copy_size,
1323 }
1324 })
1325 .collect::<Vec<_>>();
1326
1327 unsafe {
1328 state.raw_encoder.transition_textures(&dst_barrier);
1329 state.raw_encoder.transition_buffers(src_barrier.as_slice());
1330 state
1331 .raw_encoder
1332 .copy_buffer_to_texture(src_raw, dst_raw, ®ions);
1333 }
1334
1335 Ok(())
1336}
1337
1338pub(super) fn copy_texture_to_buffer(
1339 state: &mut EncodingState,
1340 source: &TexelCopyTextureInfo,
1341 destination: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
1342 copy_size: &Extent3d,
1343) -> Result<(), CommandEncoderError> {
1344 let src_texture = &source.texture;
1345 let dst_buffer = &destination.buffer;
1346
1347 src_texture.same_device(state.device)?;
1348 dst_buffer.same_device(state.device)?;
1349
1350 let (hal_copy_size, array_layer_count) =
1351 validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
1352
1353 let (src_range, src_base) = extract_texture_selector(source, copy_size, src_texture)?;
1354
1355 let src_raw = src_texture.try_inner(state.snatch_guard)?.raw();
1356 src_texture
1357 .check_usage(TextureUsages::COPY_SRC)
1358 .map_err(TransferError::MissingTextureUsage)?;
1359
1360 if source.mip_level >= src_texture.desc.mip_level_count {
1361 return Err(TransferError::InvalidMipLevel {
1362 requested: source.mip_level,
1363 count: src_texture.desc.mip_level_count,
1364 }
1365 .into());
1366 }
1367
1368 validate_texture_copy_src_format(src_texture.desc.format, source.aspect)?;
1369
1370 validate_texture_buffer_copy(
1371 source,
1372 src_base.aspect,
1373 &src_texture.desc,
1374 &destination.layout,
1375 true, state.device.features,
1377 )?;
1378
1379 let (required_buffer_bytes_in_copy, bytes_per_array_layer, is_contiguous) =
1380 validate_linear_texture_data(
1381 &destination.layout,
1382 src_texture.desc.format,
1383 source.aspect,
1384 dst_buffer.size,
1385 CopySide::Destination,
1386 copy_size,
1387 )?;
1388
1389 if src_texture.desc.format.is_depth_stencil_format() {
1390 state
1391 .device
1392 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
1393 .map_err(TransferError::from)?;
1394 }
1395
1396 let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;
1397 dst_buffer
1398 .check_usage(BufferUsages::COPY_DST)
1399 .map_err(TransferError::MissingBufferUsage)?;
1400
1401 if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1404 log::trace!("Ignoring copy_texture_to_buffer of size 0");
1405 return Ok(());
1406 }
1407
1408 handle_src_texture_init(state, source, copy_size, src_texture)?;
1412
1413 let src_pending =
1414 state
1415 .tracker
1416 .textures
1417 .set_single(src_texture, src_range, wgt::TextureUses::COPY_SRC);
1418 let src_barrier = src_pending
1419 .map(|pending| pending.into_hal(src_raw))
1420 .collect::<Vec<_>>();
1421
1422 let dst_pending = state
1423 .tracker
1424 .buffers
1425 .set_single(dst_buffer, wgt::BufferUses::COPY_DST);
1426
1427 let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));
1428
1429 handle_buffer_init(
1430 state,
1431 destination,
1432 CopySide::Destination,
1433 required_buffer_bytes_in_copy,
1434 is_contiguous,
1435 );
1436
1437 let regions = (0..array_layer_count)
1438 .map(|rel_array_layer| {
1439 let mut texture_base = src_base.clone();
1440 texture_base.array_layer += rel_array_layer;
1441 let mut buffer_layout = destination.layout;
1442 buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
1443 hal::BufferTextureCopy {
1444 buffer_layout,
1445 texture_base,
1446 size: hal_copy_size,
1447 }
1448 })
1449 .collect::<Vec<_>>();
1450 unsafe {
1451 state.raw_encoder.transition_buffers(dst_barrier.as_slice());
1452 state.raw_encoder.transition_textures(&src_barrier);
1453 state.raw_encoder.copy_texture_to_buffer(
1454 src_raw,
1455 wgt::TextureUses::COPY_SRC,
1456 dst_raw,
1457 ®ions,
1458 );
1459 }
1460
1461 Ok(())
1462}
1463
1464pub(super) fn copy_texture_to_texture(
1465 state: &mut EncodingState,
1466 source: &TexelCopyTextureInfo,
1467 destination: &TexelCopyTextureInfo,
1468 copy_size: &Extent3d,
1469) -> Result<(), CommandEncoderError> {
1470 let src_texture = &source.texture;
1471 let dst_texture = &destination.texture;
1472
1473 src_texture.same_device(state.device)?;
1474 dst_texture.same_device(state.device)?;
1475
1476 let src_fmt_no_srgb = src_texture.desc.format.remove_srgb_suffix();
1490 let dst_fmt_no_srgb = dst_texture.desc.format.remove_srgb_suffix();
1491 let planar_split_ok = src_fmt_no_srgb.is_multi_planar_format()
1492 && src_fmt_no_srgb.aspect_specific_format(source.aspect) == Some(dst_fmt_no_srgb);
1493 if src_fmt_no_srgb != dst_fmt_no_srgb && !planar_split_ok {
1494 return Err(TransferError::TextureFormatsNotCopyCompatible {
1495 src_format: src_texture.desc.format,
1496 dst_format: dst_texture.desc.format,
1497 }
1498 .into());
1499 }
1500
1501 let (src_copy_size, array_layer_count) =
1502 validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
1503 let (dst_copy_size, _) = validate_texture_copy_range(
1504 destination,
1505 &dst_texture.desc,
1506 CopySide::Destination,
1507 copy_size,
1508 )?;
1509
1510 if planar_split_ok {
1516 let plane = source.aspect.to_plane().expect("planar_split_ok aspect");
1519 let plane_extent = src_texture
1520 .desc
1521 .compute_render_extent(source.mip_level, Some(plane));
1522 let check = |dimension, start: u32, size: u32, plane_size: u32| {
1523 if start > plane_size || plane_size - start < size {
1524 Err(TransferError::TextureOverrun {
1525 start_offset: start,
1526 end_offset: start.wrapping_add(size),
1527 texture_size: plane_size,
1528 dimension,
1529 side: CopySide::Source,
1530 })
1531 } else {
1532 Ok(())
1533 }
1534 };
1535 check(
1536 TextureErrorDimension::X,
1537 source.origin.x,
1538 copy_size.width,
1539 plane_extent.width,
1540 )?;
1541 check(
1542 TextureErrorDimension::Y,
1543 source.origin.y,
1544 copy_size.height,
1545 plane_extent.height,
1546 )?;
1547 }
1548
1549 if Arc::as_ptr(src_texture) == Arc::as_ptr(dst_texture) {
1550 validate_copy_within_same_texture(
1551 source,
1552 destination,
1553 src_texture.desc.format,
1554 array_layer_count,
1555 )?;
1556 }
1557
1558 let (src_range, src_tex_base) = extract_texture_selector(source, copy_size, src_texture)?;
1559 let (dst_range, dst_tex_base) = extract_texture_selector(destination, copy_size, dst_texture)?;
1560 let src_texture_aspects = hal::FormatAspects::from(src_texture.desc.format);
1561 let dst_texture_aspects = hal::FormatAspects::from(dst_texture.desc.format);
1562 if src_tex_base.aspect != src_texture_aspects && !planar_split_ok {
1564 return Err(TransferError::CopySrcMissingAspects.into());
1565 }
1566 if dst_tex_base.aspect != dst_texture_aspects {
1567 return Err(TransferError::CopyDstMissingAspects.into());
1568 }
1569
1570 if src_texture.desc.sample_count != dst_texture.desc.sample_count {
1571 return Err(TransferError::SampleCountNotEqual {
1572 src_sample_count: src_texture.desc.sample_count,
1573 dst_sample_count: dst_texture.desc.sample_count,
1574 }
1575 .into());
1576 }
1577
1578 let src_raw = src_texture.try_inner(state.snatch_guard)?.raw();
1579 src_texture
1580 .check_usage(TextureUsages::COPY_SRC)
1581 .map_err(TransferError::MissingTextureUsage)?;
1582 let dst_raw = dst_texture.try_inner(state.snatch_guard)?.raw();
1583 dst_texture
1584 .check_usage(TextureUsages::COPY_DST)
1585 .map_err(TransferError::MissingTextureUsage)?;
1586
1587 if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1590 log::trace!("Ignoring copy_texture_to_texture of size 0");
1591 return Ok(());
1592 }
1593
1594 handle_src_texture_init(state, source, copy_size, src_texture)?;
1598 handle_dst_texture_init(state, destination, copy_size, dst_texture)?;
1599
1600 let src_pending =
1601 state
1602 .tracker
1603 .textures
1604 .set_single(src_texture, src_range, wgt::TextureUses::COPY_SRC);
1605
1606 let mut barriers: ArrayVec<_, 2> = src_pending
1609 .map(|pending| pending.into_hal(src_raw))
1610 .collect();
1611
1612 let dst_pending =
1613 state
1614 .tracker
1615 .textures
1616 .set_single(dst_texture, dst_range, wgt::TextureUses::COPY_DST);
1617 barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_raw)));
1618
1619 let hal_copy_size = hal::CopyExtent {
1620 width: src_copy_size.width.min(dst_copy_size.width),
1621 height: src_copy_size.height.min(dst_copy_size.height),
1622 depth: src_copy_size.depth.min(dst_copy_size.depth),
1623 };
1624
1625 let dst_format = dst_texture.desc.format;
1626
1627 let regions = (0..array_layer_count).map(|rel_array_layer| {
1628 let mut src_base = src_tex_base.clone();
1629 let mut dst_base = dst_tex_base.clone();
1630 src_base.array_layer += rel_array_layer;
1631 dst_base.array_layer += rel_array_layer;
1632 hal::TextureCopy {
1633 src_base,
1634 dst_base,
1635 size: hal_copy_size,
1636 }
1637 });
1638
1639 let regions = if dst_tex_base.aspect == hal::FormatAspects::DEPTH_STENCIL {
1640 regions
1641 .flat_map(|region| {
1642 let (mut depth, mut stencil) = (region.clone(), region);
1643 depth.src_base.aspect = hal::FormatAspects::DEPTH;
1644 depth.dst_base.aspect = hal::FormatAspects::DEPTH;
1645 stencil.src_base.aspect = hal::FormatAspects::STENCIL;
1646 stencil.dst_base.aspect = hal::FormatAspects::STENCIL;
1647 [depth, stencil]
1648 })
1649 .collect::<Vec<_>>()
1650 } else if let Some(plane_count) = dst_format.planes() {
1651 regions
1652 .into_iter()
1653 .flat_map(|region| {
1654 (0..plane_count).map(move |plane| {
1655 let mut plane_region = region.clone();
1656
1657 let plane_aspect = wgt::TextureAspect::from_plane(plane)
1658 .expect("expected texture aspect to exist for the plane");
1659 let plane_aspect = hal::FormatAspects::new(dst_format, plane_aspect);
1660 plane_region.src_base.aspect = plane_aspect;
1661 plane_region.dst_base.aspect = plane_aspect;
1662
1663 let (w_subsampling, h_subsampling) =
1664 dst_format.subsampling_factors(Some(plane));
1665 plane_region.src_base.origin.x /= w_subsampling;
1666 plane_region.src_base.origin.y /= h_subsampling;
1667 plane_region.dst_base.origin.x /= w_subsampling;
1668 plane_region.dst_base.origin.y /= h_subsampling;
1669
1670 plane_region.size.width /= w_subsampling;
1671 plane_region.size.height /= h_subsampling;
1672
1673 plane_region
1674 })
1675 })
1676 .collect::<Vec<_>>()
1677 } else {
1678 regions.collect::<Vec<_>>()
1679 };
1680 unsafe {
1681 state.raw_encoder.transition_textures(&barriers);
1682 state.raw_encoder.copy_texture_to_texture(
1683 src_raw,
1684 wgt::TextureUses::COPY_SRC,
1685 dst_raw,
1686 ®ions,
1687 );
1688 }
1689
1690 Ok(())
1691}