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 does not respect `COPY_BYTES_PER_ROW_ALIGNMENT`")]
121 UnalignedBytesPerRow,
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>(
449 texture_copy_view: &wgt::TexelCopyTextureInfo<T>,
450 aspect: hal::FormatAspects,
451 desc: &wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
452 layout: &wgt::TexelCopyBufferLayout,
453 aligned: bool,
454) -> Result<(), TransferError> {
455 if desc.sample_count != 1 {
456 return Err(TransferError::InvalidSampleCount {
457 sample_count: desc.sample_count,
458 });
459 }
460
461 if !aspect.is_one() {
462 return Err(TransferError::CopyAspectNotOne);
463 }
464
465 let offset_alignment = if desc.format.is_depth_stencil_format() {
466 4
467 } else {
468 desc.format
473 .block_copy_size(Some(texture_copy_view.aspect))
474 .expect("non-copyable formats should have been rejected previously")
475 };
476
477 if aligned && !layout.offset.is_multiple_of(u64::from(offset_alignment)) {
478 return Err(TransferError::UnalignedBufferOffset(layout.offset));
479 }
480
481 if let Some(bytes_per_row) = layout.bytes_per_row {
482 if aligned && bytes_per_row % wgt::COPY_BYTES_PER_ROW_ALIGNMENT != 0 {
483 return Err(TransferError::UnalignedBytesPerRow);
484 }
485 }
486
487 Ok(())
488}
489
490pub(crate) fn validate_texture_copy_range<T>(
501 texture_copy_view: &wgt::TexelCopyTextureInfo<T>,
502 desc: &wgt::TextureDescriptor<String, Vec<wgt::TextureFormat>>,
503 texture_side: CopySide,
504 copy_size: &Extent3d,
505) -> Result<(hal::CopyExtent, u32), TransferError> {
506 let (block_width, block_height) = desc.format.block_dimensions();
507
508 let extent_virtual = desc.mip_level_size(texture_copy_view.mip_level).ok_or(
509 TransferError::InvalidTextureMipLevel {
510 level: texture_copy_view.mip_level,
511 total: desc.mip_level_count,
512 },
513 )?;
514 let extent = extent_virtual.physical_size(desc.format);
516
517 let requires_exact_size = desc.format.is_depth_stencil_format() || desc.sample_count > 1;
520
521 let check_dimension = |dimension: TextureErrorDimension,
524 start_offset: u32,
525 size: u32,
526 texture_size: u32,
527 requires_exact_size: bool|
528 -> Result<(), TransferError> {
529 if requires_exact_size && (start_offset != 0 || size != texture_size) {
530 Err(TransferError::UnsupportedPartialTransfer {
531 format: desc.format,
532 sample_count: desc.sample_count,
533 start_offset,
534 end_offset: start_offset.wrapping_add(size),
535 texture_size,
536 dimension,
537 side: texture_side,
538 })
539 } else if start_offset > texture_size || texture_size - start_offset < size {
542 Err(TransferError::TextureOverrun {
543 start_offset,
544 end_offset: start_offset.wrapping_add(size),
545 texture_size,
546 dimension,
547 side: texture_side,
548 })
549 } else {
550 Ok(())
551 }
552 };
553
554 check_dimension(
555 TextureErrorDimension::X,
556 texture_copy_view.origin.x,
557 copy_size.width,
558 extent.width,
559 requires_exact_size,
560 )?;
561 check_dimension(
562 TextureErrorDimension::Y,
563 texture_copy_view.origin.y,
564 copy_size.height,
565 extent.height,
566 requires_exact_size,
567 )?;
568 check_dimension(
569 TextureErrorDimension::Z,
570 texture_copy_view.origin.z,
571 copy_size.depth_or_array_layers,
572 extent.depth_or_array_layers,
573 false, )?;
575
576 if !texture_copy_view.origin.x.is_multiple_of(block_width) {
577 return Err(TransferError::UnalignedCopyOriginX);
578 }
579 if !texture_copy_view.origin.y.is_multiple_of(block_height) {
580 return Err(TransferError::UnalignedCopyOriginY);
581 }
582 if !copy_size.width.is_multiple_of(block_width) {
583 return Err(TransferError::UnalignedCopyWidth);
584 }
585 if !copy_size.height.is_multiple_of(block_height) {
586 return Err(TransferError::UnalignedCopyHeight);
587 }
588
589 let (depth, array_layer_count) = match desc.dimension {
590 wgt::TextureDimension::D1 => (1, 1),
591 wgt::TextureDimension::D2 => (1, copy_size.depth_or_array_layers),
592 wgt::TextureDimension::D3 => (copy_size.depth_or_array_layers, 1),
593 };
594
595 let copy_extent = hal::CopyExtent {
596 width: copy_size.width,
597 height: copy_size.height,
598 depth,
599 };
600 Ok((copy_extent, array_layer_count))
601}
602
603pub(crate) fn validate_copy_within_same_texture<T>(
614 src: &wgt::TexelCopyTextureInfo<T>,
615 dst: &wgt::TexelCopyTextureInfo<T>,
616 format: wgt::TextureFormat,
617 array_layer_count: u32,
618) -> Result<(), TransferError> {
619 let src_aspects = hal::FormatAspects::new(format, src.aspect);
620 let dst_aspects = hal::FormatAspects::new(format, dst.aspect);
621 if (src_aspects & dst_aspects).is_empty() {
622 return Ok(());
624 }
625
626 if src.origin.z >= dst.origin.z + array_layer_count
627 || dst.origin.z >= src.origin.z + array_layer_count
628 {
629 return Ok(());
631 }
632
633 if src.mip_level != dst.mip_level {
634 return Ok(());
636 }
637
638 Err(TransferError::InvalidCopyWithinSameTexture {
639 src_aspects: src.aspect,
640 dst_aspects: dst.aspect,
641 src_origin_z: src.origin.z,
642 dst_origin_z: dst.origin.z,
643 array_layer_count,
644 })
645}
646
647fn handle_texture_init(
648 state: &mut EncodingState,
649 init_kind: MemoryInitKind,
650 copy_texture: &TexelCopyTextureInfo,
651 copy_size: &Extent3d,
652 texture: &Arc<Texture>,
653) -> Result<(), ClearError> {
654 let init_layer_range = if texture.desc.dimension == wgt::TextureDimension::D3 {
655 0..1
657 } else {
658 copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers
659 };
660 let init_action = TextureInitTrackerAction {
661 texture: texture.clone(),
662 range: TextureInitRange {
663 mip_range: copy_texture.mip_level..copy_texture.mip_level + 1,
664 layer_range: init_layer_range,
665 },
666 kind: init_kind,
667 };
668
669 let accessed_depth_slices = (texture.desc.dimension == wgt::TextureDimension::D3)
678 .then(|| copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers);
679 let immediate_inits = state
680 .texture_memory_actions
681 .register_init_action(&{ init_action }, accessed_depth_slices);
682
683 if !immediate_inits.is_empty() {
685 for init in immediate_inits {
686 let index = init.layer_or_depth_slice;
687 let (layer_range, depth_slice) = if texture.desc.dimension == wgt::TextureDimension::D3
688 {
689 (0..1, Some(index))
690 } else {
691 (index..(index + 1), None)
692 };
693 clear_texture(
694 &init.texture,
695 TextureInitRange {
696 mip_range: init.mip_level..(init.mip_level + 1),
697 layer_range,
698 },
699 depth_slice,
700 state.raw_encoder,
701 &mut state.tracker.textures,
702 &state.device.alignments,
703 state.device.zero_buffer.as_ref(),
704 state.snatch_guard,
705 state.device.instance_flags,
706 )?;
707 }
708 }
709
710 Ok(())
711}
712
713fn handle_src_texture_init(
718 state: &mut EncodingState,
719 source: &TexelCopyTextureInfo,
720 copy_size: &Extent3d,
721 texture: &Arc<Texture>,
722) -> Result<(), TransferError> {
723 handle_texture_init(
724 state,
725 MemoryInitKind::NeedsInitializedMemory,
726 source,
727 copy_size,
728 texture,
729 )?;
730 Ok(())
731}
732
733fn handle_dst_texture_init(
738 state: &mut EncodingState,
739 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
740 copy_size: &Extent3d,
741 texture: &Arc<Texture>,
742) -> Result<(), TransferError> {
743 let dst_init_kind =
748 if has_copy_partial_init_tracker_coverage(copy_size, destination, &texture.desc) {
749 MemoryInitKind::NeedsInitializedMemory
750 } else {
751 MemoryInitKind::ImplicitlyInitialized
752 };
753
754 handle_texture_init(state, dst_init_kind, destination, copy_size, texture)?;
755 Ok(())
756}
757
758fn handle_buffer_init(
763 state: &mut EncodingState,
764 info: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
765 direction: CopySide,
766 required_buffer_bytes_in_copy: BufferAddress,
767 is_contiguous: bool,
768) {
769 const ALIGN_SIZE: BufferAddress = wgt::COPY_BUFFER_ALIGNMENT;
770 const ALIGN_MASK: BufferAddress = wgt::COPY_BUFFER_ALIGNMENT - 1;
771
772 let buffer = &info.buffer;
773 let start = info.layout.offset;
774 let end = info.layout.offset + required_buffer_bytes_in_copy;
775 if !is_contiguous || direction == CopySide::Source {
776 let aligned_start = start & !ALIGN_MASK;
787 let aligned_end = (end + ALIGN_MASK) & !ALIGN_MASK;
788 state
789 .buffer_memory_init_actions
790 .extend(buffer.initialization_status.read().create_action(
791 buffer,
792 aligned_start..aligned_end,
793 MemoryInitKind::NeedsInitializedMemory,
794 ));
795 } else {
796 let aligned_start = (start + ALIGN_MASK) & !ALIGN_MASK;
806 let aligned_end = end & !ALIGN_MASK;
807 if aligned_start != start {
808 state.buffer_memory_init_actions.extend(
809 buffer.initialization_status.read().create_action(
810 buffer,
811 aligned_start - ALIGN_SIZE..aligned_start,
812 MemoryInitKind::NeedsInitializedMemory,
813 ),
814 );
815 }
816 if aligned_start != aligned_end {
817 state.buffer_memory_init_actions.extend(
818 buffer.initialization_status.read().create_action(
819 buffer,
820 aligned_start..aligned_end,
821 MemoryInitKind::ImplicitlyInitialized,
822 ),
823 );
824 }
825 if aligned_end != end {
826 state.buffer_memory_init_actions.extend(
832 buffer.initialization_status.read().create_action(
833 buffer,
834 aligned_end..aligned_end + ALIGN_SIZE,
835 MemoryInitKind::NeedsInitializedMemory,
836 ),
837 );
838 }
839 }
840}
841
842impl super::CommandEncoder {
843 fn copy_buffer_to_buffer_inner(
844 self: &Arc<Self>,
845 source: Arc<Buffer>,
846 source_offset: BufferAddress,
847 destination: Arc<Buffer>,
848 destination_offset: BufferAddress,
849 size: Option<BufferAddress>,
850 ) -> Result<(), EncoderStateError> {
851 profiling::scope!("CommandEncoder::copy_buffer_to_buffer");
852 api_log!(
853 "CommandEncoder::copy_buffer_to_buffer {:?} -> {:?} {size:?}bytes",
854 Arc::as_ptr(&source),
855 Arc::as_ptr(&destination)
856 );
857
858 let mut cmd_buf_data = self.data.lock();
859
860 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
861 source.check_is_valid()?;
862 destination.check_is_valid()?;
863 Ok(ArcCommand::CopyBufferToBuffer {
864 src: source,
865 src_offset: source_offset,
866 dst: destination,
867 dst_offset: destination_offset,
868 size,
869 })
870 })
871 }
872
873 pub fn copy_buffer_to_buffer(
874 self: &Arc<Self>,
875 source: Arc<Buffer>,
876 source_offset: BufferAddress,
877 destination: Arc<Buffer>,
878 destination_offset: BufferAddress,
879 size: Option<BufferAddress>,
880 ) {
881 if let Err(err) = self.copy_buffer_to_buffer_inner(
882 source,
883 source_offset,
884 destination,
885 destination_offset,
886 size,
887 ) {
888 self.device.handle_error(
889 err,
890 Some(self.label()),
891 "CommandEncoder::copy_buffer_to_buffer",
892 );
893 }
894 }
895
896 fn copy_buffer_to_texture_inner(
897 self: &Arc<Self>,
898 source: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
899 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
900 copy_size: &Extent3d,
901 ) -> Result<(), EncoderStateError> {
902 profiling::scope!("CommandEncoder::copy_buffer_to_texture");
903 api_log!(
904 "CommandEncoder::copy_buffer_to_texture {:?} -> {:?} {copy_size:?}",
905 Arc::as_ptr(&source.buffer),
906 Arc::as_ptr(&destination.texture)
907 );
908
909 let mut cmd_buf_data = self.data.lock();
910
911 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
912 let texture = destination.texture.clone();
913 texture.check_valid()?;
914 let source_buffer = source.buffer.clone();
915 source_buffer.check_is_valid()?;
916 Ok(ArcCommand::CopyBufferToTexture {
917 src: wgt::TexelCopyBufferInfo::<Arc<Buffer>> {
918 buffer: source_buffer,
919 layout: source.layout,
920 },
921 dst: wgt::TexelCopyTextureInfo::<Arc<Texture>> {
922 texture,
923 mip_level: destination.mip_level,
924 origin: destination.origin,
925 aspect: destination.aspect,
926 },
927 size: *copy_size,
928 })
929 })
930 }
931
932 pub fn copy_buffer_to_texture(
933 self: &Arc<Self>,
934 source: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
935 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
936 copy_size: &Extent3d,
937 ) {
938 if let Err(err) = self.copy_buffer_to_texture_inner(source, destination, copy_size) {
939 self.device.handle_error(
940 err,
941 Some(self.label()),
942 "CommandEncoder::copy_buffer_to_texture",
943 );
944 }
945 }
946
947 fn copy_texture_to_buffer_inner(
948 self: &Arc<Self>,
949 source: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
950 destination: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
951 copy_size: &Extent3d,
952 ) -> Result<(), EncoderStateError> {
953 profiling::scope!("CommandEncoder::copy_texture_to_buffer");
954 api_log!(
955 "CommandEncoder::copy_texture_to_buffer {:?} -> {:?} {copy_size:?}",
956 Arc::as_ptr(&source.texture),
957 Arc::as_ptr(&destination.buffer)
958 );
959
960 let mut cmd_buf_data = self.data.lock();
961
962 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
963 let texture = source.texture.clone();
964 texture.check_valid()?;
965 let destination_buffer = destination.buffer.clone();
966 destination_buffer.check_is_valid()?;
967 Ok(ArcCommand::CopyTextureToBuffer {
968 src: wgt::TexelCopyTextureInfo::<Arc<Texture>> {
969 texture,
970 mip_level: source.mip_level,
971 origin: source.origin,
972 aspect: source.aspect,
973 },
974 dst: wgt::TexelCopyBufferInfo::<Arc<Buffer>> {
975 buffer: destination_buffer,
976 layout: destination.layout,
977 },
978 size: *copy_size,
979 })
980 })
981 }
982
983 pub fn copy_texture_to_buffer(
984 self: &Arc<Self>,
985 source: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
986 destination: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
987 copy_size: &Extent3d,
988 ) {
989 if let Err(err) = self.copy_texture_to_buffer_inner(source, destination, copy_size) {
990 self.device.handle_error(
991 err,
992 Some(self.label()),
993 "CommandEncoder::copy_texture_to_buffer",
994 );
995 }
996 }
997
998 fn copy_texture_to_texture_inner(
999 self: &Arc<Self>,
1000 source: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
1001 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
1002 copy_size: &Extent3d,
1003 ) -> Result<(), EncoderStateError> {
1004 profiling::scope!("CommandEncoder::copy_texture_to_texture");
1005 api_log!(
1006 "CommandEncoder::copy_texture_to_texture {:?} -> {:?} {copy_size:?}",
1007 Arc::as_ptr(&source.texture),
1008 Arc::as_ptr(&destination.texture)
1009 );
1010
1011 let mut cmd_buf_data = self.data.lock();
1012
1013 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1014 let src_texture = source.texture.clone();
1015 let dst_texture = destination.texture.clone();
1016 src_texture.check_valid()?;
1017 dst_texture.check_valid()?;
1018 Ok(ArcCommand::CopyTextureToTexture {
1019 src: wgt::TexelCopyTextureInfo {
1020 texture: src_texture,
1021 mip_level: source.mip_level,
1022 origin: source.origin,
1023 aspect: source.aspect,
1024 },
1025 dst: wgt::TexelCopyTextureInfo {
1026 texture: dst_texture,
1027 mip_level: destination.mip_level,
1028 origin: destination.origin,
1029 aspect: destination.aspect,
1030 },
1031 size: *copy_size,
1032 })
1033 })
1034 }
1035
1036 pub fn copy_texture_to_texture(
1037 self: &Arc<Self>,
1038 source: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
1039 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
1040 copy_size: &Extent3d,
1041 ) {
1042 if let Err(err) = self.copy_texture_to_texture_inner(source, destination, copy_size) {
1043 self.device.handle_error(
1044 err,
1045 Some(self.label()),
1046 "CommandEncoder::copy_texture_to_texture",
1047 );
1048 }
1049 }
1050}
1051
1052pub(super) fn copy_buffer_to_buffer(
1053 state: &mut EncodingState,
1054 src_buffer: &Arc<Buffer>,
1055 source_offset: BufferAddress,
1056 dst_buffer: &Arc<Buffer>,
1057 destination_offset: BufferAddress,
1058 size: Option<BufferAddress>,
1059) -> Result<(), CommandEncoderError> {
1060 if src_buffer.is_equal(dst_buffer) {
1061 return Err(TransferError::SameSourceDestinationBuffer.into());
1062 }
1063
1064 src_buffer.same_device(state.device)?;
1065
1066 let src_pending = state
1067 .tracker
1068 .buffers
1069 .set_single(src_buffer, wgt::BufferUses::COPY_SRC);
1070
1071 let src_raw = src_buffer.try_raw(state.snatch_guard)?;
1072 src_buffer
1073 .check_usage(BufferUsages::COPY_SRC)
1074 .map_err(TransferError::MissingBufferUsage)?;
1075 let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer, state.snatch_guard));
1077
1078 dst_buffer.same_device(state.device)?;
1079
1080 let dst_pending = state
1081 .tracker
1082 .buffers
1083 .set_single(dst_buffer, wgt::BufferUses::COPY_DST);
1084
1085 let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;
1086 dst_buffer
1087 .check_usage(BufferUsages::COPY_DST)
1088 .map_err(TransferError::MissingBufferUsage)?;
1089 let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));
1090
1091 if source_offset > src_buffer.size {
1092 return Err(TransferError::BufferStartOffsetOverrun {
1093 start_offset: source_offset,
1094 buffer_size: src_buffer.size,
1095 side: CopySide::Source,
1096 }
1097 .into());
1098 }
1099 let size = size.unwrap_or_else(|| {
1100 src_buffer.size - source_offset
1102 });
1103
1104 if !size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
1105 return Err(TransferError::UnalignedCopySize(size).into());
1106 }
1107 if !source_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
1108 return Err(TransferError::UnalignedBufferOffset(source_offset).into());
1109 }
1110 if !destination_offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
1111 return Err(TransferError::UnalignedBufferOffset(destination_offset).into());
1112 }
1113 if !state
1114 .device
1115 .downlevel
1116 .flags
1117 .contains(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)
1118 && (src_buffer.usage.contains(BufferUsages::INDEX)
1119 || dst_buffer.usage.contains(BufferUsages::INDEX))
1120 {
1121 let forbidden_usages = BufferUsages::VERTEX
1122 | BufferUsages::UNIFORM
1123 | BufferUsages::INDIRECT
1124 | BufferUsages::STORAGE;
1125 if src_buffer.usage.intersects(forbidden_usages)
1126 || dst_buffer.usage.intersects(forbidden_usages)
1127 {
1128 return Err(TransferError::MissingDownlevelFlags(MissingDownlevelFlags(
1129 wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
1130 ))
1131 .into());
1132 }
1133 }
1134
1135 if size > src_buffer.size - source_offset {
1136 return Err(TransferError::BufferEndOffsetOverrun {
1137 start_offset: source_offset,
1138 size,
1139 buffer_size: src_buffer.size,
1140 side: CopySide::Source,
1141 }
1142 .into());
1143 }
1144 let source_end_offset = source_offset + size;
1146
1147 if destination_offset > dst_buffer.size {
1148 return Err(TransferError::BufferStartOffsetOverrun {
1149 start_offset: destination_offset,
1150 buffer_size: dst_buffer.size,
1151 side: CopySide::Destination,
1152 }
1153 .into());
1154 }
1155 if size > dst_buffer.size - destination_offset {
1157 return Err(TransferError::BufferEndOffsetOverrun {
1158 start_offset: destination_offset,
1159 size,
1160 buffer_size: dst_buffer.size,
1161 side: CopySide::Destination,
1162 }
1163 .into());
1164 }
1165 let destination_end_offset = destination_offset + size;
1167
1168 if size == 0 {
1171 log::trace!("Ignoring copy_buffer_to_buffer of size 0");
1172 return Ok(());
1173 }
1174
1175 state
1177 .buffer_memory_init_actions
1178 .extend(dst_buffer.initialization_status.read().create_action(
1179 dst_buffer,
1180 destination_offset..destination_end_offset,
1181 MemoryInitKind::ImplicitlyInitialized,
1182 ));
1183 state
1184 .buffer_memory_init_actions
1185 .extend(src_buffer.initialization_status.read().create_action(
1186 src_buffer,
1187 source_offset..source_end_offset,
1188 MemoryInitKind::NeedsInitializedMemory,
1189 ));
1190
1191 let region = hal::BufferCopy {
1192 src_offset: source_offset,
1193 dst_offset: destination_offset,
1194 size: wgt::BufferSize::new(size).unwrap(),
1195 };
1196 let barriers = src_barrier
1197 .into_iter()
1198 .chain(dst_barrier)
1199 .collect::<Vec<_>>();
1200 unsafe {
1201 state.raw_encoder.transition_buffers(&barriers);
1202 state
1203 .raw_encoder
1204 .copy_buffer_to_buffer(src_raw, dst_raw, &[region]);
1205 }
1206
1207 Ok(())
1208}
1209
1210pub(super) fn copy_buffer_to_texture(
1211 state: &mut EncodingState,
1212 source: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
1213 destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
1214 copy_size: &Extent3d,
1215) -> Result<(), CommandEncoderError> {
1216 let dst_texture = &destination.texture;
1217 let src_buffer = &source.buffer;
1218
1219 dst_texture.same_device(state.device)?;
1220 src_buffer.same_device(state.device)?;
1221
1222 let (hal_copy_size, array_layer_count) = validate_texture_copy_range(
1223 destination,
1224 &dst_texture.desc,
1225 CopySide::Destination,
1226 copy_size,
1227 )?;
1228
1229 let (dst_range, dst_base) = extract_texture_selector(destination, copy_size, dst_texture)?;
1230
1231 let src_raw = src_buffer.try_raw(state.snatch_guard)?;
1232 src_buffer
1233 .check_usage(BufferUsages::COPY_SRC)
1234 .map_err(TransferError::MissingBufferUsage)?;
1235
1236 let dst_raw = dst_texture.try_inner(state.snatch_guard)?.raw();
1237 dst_texture
1238 .check_usage(TextureUsages::COPY_DST)
1239 .map_err(TransferError::MissingTextureUsage)?;
1240
1241 validate_texture_copy_dst_format(dst_texture.desc.format, destination.aspect)?;
1242
1243 validate_texture_buffer_copy(
1244 destination,
1245 dst_base.aspect,
1246 &dst_texture.desc,
1247 &source.layout,
1248 true, )?;
1250
1251 let (required_buffer_bytes_in_copy, bytes_per_array_layer, is_contiguous) =
1252 validate_linear_texture_data(
1253 &source.layout,
1254 dst_texture.desc.format,
1255 destination.aspect,
1256 src_buffer.size,
1257 CopySide::Source,
1258 copy_size,
1259 )?;
1260
1261 if dst_texture.desc.format.is_depth_stencil_format() {
1262 state
1263 .device
1264 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
1265 .map_err(TransferError::from)?;
1266 }
1267
1268 if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1271 log::trace!("Ignoring copy_buffer_to_texture of size 0");
1272 return Ok(());
1273 }
1274
1275 handle_dst_texture_init(state, destination, copy_size, dst_texture)?;
1279
1280 let src_pending = state
1281 .tracker
1282 .buffers
1283 .set_single(src_buffer, wgt::BufferUses::COPY_SRC);
1284 let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer, state.snatch_guard));
1285
1286 let dst_pending =
1287 state
1288 .tracker
1289 .textures
1290 .set_single(dst_texture, dst_range, wgt::TextureUses::COPY_DST);
1291 let dst_barrier = dst_pending
1292 .map(|pending| pending.into_hal(dst_raw))
1293 .collect::<Vec<_>>();
1294
1295 handle_buffer_init(
1296 state,
1297 source,
1298 CopySide::Source,
1299 required_buffer_bytes_in_copy,
1300 is_contiguous,
1301 );
1302
1303 let regions = (0..array_layer_count)
1304 .map(|rel_array_layer| {
1305 let mut texture_base = dst_base.clone();
1306 texture_base.array_layer += rel_array_layer;
1307 let mut buffer_layout = source.layout;
1308 buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
1309 hal::BufferTextureCopy {
1310 buffer_layout,
1311 texture_base,
1312 size: hal_copy_size,
1313 }
1314 })
1315 .collect::<Vec<_>>();
1316
1317 unsafe {
1318 state.raw_encoder.transition_textures(&dst_barrier);
1319 state.raw_encoder.transition_buffers(src_barrier.as_slice());
1320 state
1321 .raw_encoder
1322 .copy_buffer_to_texture(src_raw, dst_raw, ®ions);
1323 }
1324
1325 Ok(())
1326}
1327
1328pub(super) fn copy_texture_to_buffer(
1329 state: &mut EncodingState,
1330 source: &TexelCopyTextureInfo,
1331 destination: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
1332 copy_size: &Extent3d,
1333) -> Result<(), CommandEncoderError> {
1334 let src_texture = &source.texture;
1335 let dst_buffer = &destination.buffer;
1336
1337 src_texture.same_device(state.device)?;
1338 dst_buffer.same_device(state.device)?;
1339
1340 let (hal_copy_size, array_layer_count) =
1341 validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
1342
1343 let (src_range, src_base) = extract_texture_selector(source, copy_size, src_texture)?;
1344
1345 let src_raw = src_texture.try_inner(state.snatch_guard)?.raw();
1346 src_texture
1347 .check_usage(TextureUsages::COPY_SRC)
1348 .map_err(TransferError::MissingTextureUsage)?;
1349
1350 if source.mip_level >= src_texture.desc.mip_level_count {
1351 return Err(TransferError::InvalidMipLevel {
1352 requested: source.mip_level,
1353 count: src_texture.desc.mip_level_count,
1354 }
1355 .into());
1356 }
1357
1358 validate_texture_copy_src_format(src_texture.desc.format, source.aspect)?;
1359
1360 validate_texture_buffer_copy(
1361 source,
1362 src_base.aspect,
1363 &src_texture.desc,
1364 &destination.layout,
1365 true, )?;
1367
1368 let (required_buffer_bytes_in_copy, bytes_per_array_layer, is_contiguous) =
1369 validate_linear_texture_data(
1370 &destination.layout,
1371 src_texture.desc.format,
1372 source.aspect,
1373 dst_buffer.size,
1374 CopySide::Destination,
1375 copy_size,
1376 )?;
1377
1378 if src_texture.desc.format.is_depth_stencil_format() {
1379 state
1380 .device
1381 .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
1382 .map_err(TransferError::from)?;
1383 }
1384
1385 let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;
1386 dst_buffer
1387 .check_usage(BufferUsages::COPY_DST)
1388 .map_err(TransferError::MissingBufferUsage)?;
1389
1390 if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1393 log::trace!("Ignoring copy_texture_to_buffer of size 0");
1394 return Ok(());
1395 }
1396
1397 handle_src_texture_init(state, source, copy_size, src_texture)?;
1401
1402 let src_pending =
1403 state
1404 .tracker
1405 .textures
1406 .set_single(src_texture, src_range, wgt::TextureUses::COPY_SRC);
1407 let src_barrier = src_pending
1408 .map(|pending| pending.into_hal(src_raw))
1409 .collect::<Vec<_>>();
1410
1411 let dst_pending = state
1412 .tracker
1413 .buffers
1414 .set_single(dst_buffer, wgt::BufferUses::COPY_DST);
1415
1416 let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));
1417
1418 handle_buffer_init(
1419 state,
1420 destination,
1421 CopySide::Destination,
1422 required_buffer_bytes_in_copy,
1423 is_contiguous,
1424 );
1425
1426 let regions = (0..array_layer_count)
1427 .map(|rel_array_layer| {
1428 let mut texture_base = src_base.clone();
1429 texture_base.array_layer += rel_array_layer;
1430 let mut buffer_layout = destination.layout;
1431 buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
1432 hal::BufferTextureCopy {
1433 buffer_layout,
1434 texture_base,
1435 size: hal_copy_size,
1436 }
1437 })
1438 .collect::<Vec<_>>();
1439 unsafe {
1440 state.raw_encoder.transition_buffers(dst_barrier.as_slice());
1441 state.raw_encoder.transition_textures(&src_barrier);
1442 state.raw_encoder.copy_texture_to_buffer(
1443 src_raw,
1444 wgt::TextureUses::COPY_SRC,
1445 dst_raw,
1446 ®ions,
1447 );
1448 }
1449
1450 Ok(())
1451}
1452
1453pub(super) fn copy_texture_to_texture(
1454 state: &mut EncodingState,
1455 source: &TexelCopyTextureInfo,
1456 destination: &TexelCopyTextureInfo,
1457 copy_size: &Extent3d,
1458) -> Result<(), CommandEncoderError> {
1459 let src_texture = &source.texture;
1460 let dst_texture = &destination.texture;
1461
1462 src_texture.same_device(state.device)?;
1463 dst_texture.same_device(state.device)?;
1464
1465 let src_fmt_no_srgb = src_texture.desc.format.remove_srgb_suffix();
1479 let dst_fmt_no_srgb = dst_texture.desc.format.remove_srgb_suffix();
1480 let planar_split_ok = src_fmt_no_srgb.is_multi_planar_format()
1481 && src_fmt_no_srgb.aspect_specific_format(source.aspect) == Some(dst_fmt_no_srgb);
1482 if src_fmt_no_srgb != dst_fmt_no_srgb && !planar_split_ok {
1483 return Err(TransferError::TextureFormatsNotCopyCompatible {
1484 src_format: src_texture.desc.format,
1485 dst_format: dst_texture.desc.format,
1486 }
1487 .into());
1488 }
1489
1490 let (src_copy_size, array_layer_count) =
1491 validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
1492 let (dst_copy_size, _) = validate_texture_copy_range(
1493 destination,
1494 &dst_texture.desc,
1495 CopySide::Destination,
1496 copy_size,
1497 )?;
1498
1499 if planar_split_ok {
1505 let plane = source.aspect.to_plane().expect("planar_split_ok aspect");
1508 let plane_extent = src_texture
1509 .desc
1510 .compute_render_extent(source.mip_level, Some(plane));
1511 let check = |dimension, start: u32, size: u32, plane_size: u32| {
1512 if start > plane_size || plane_size - start < size {
1513 Err(TransferError::TextureOverrun {
1514 start_offset: start,
1515 end_offset: start.wrapping_add(size),
1516 texture_size: plane_size,
1517 dimension,
1518 side: CopySide::Source,
1519 })
1520 } else {
1521 Ok(())
1522 }
1523 };
1524 check(
1525 TextureErrorDimension::X,
1526 source.origin.x,
1527 copy_size.width,
1528 plane_extent.width,
1529 )?;
1530 check(
1531 TextureErrorDimension::Y,
1532 source.origin.y,
1533 copy_size.height,
1534 plane_extent.height,
1535 )?;
1536 }
1537
1538 if Arc::as_ptr(src_texture) == Arc::as_ptr(dst_texture) {
1539 validate_copy_within_same_texture(
1540 source,
1541 destination,
1542 src_texture.desc.format,
1543 array_layer_count,
1544 )?;
1545 }
1546
1547 let (src_range, src_tex_base) = extract_texture_selector(source, copy_size, src_texture)?;
1548 let (dst_range, dst_tex_base) = extract_texture_selector(destination, copy_size, dst_texture)?;
1549 let src_texture_aspects = hal::FormatAspects::from(src_texture.desc.format);
1550 let dst_texture_aspects = hal::FormatAspects::from(dst_texture.desc.format);
1551 if src_tex_base.aspect != src_texture_aspects && !planar_split_ok {
1553 return Err(TransferError::CopySrcMissingAspects.into());
1554 }
1555 if dst_tex_base.aspect != dst_texture_aspects {
1556 return Err(TransferError::CopyDstMissingAspects.into());
1557 }
1558
1559 if src_texture.desc.sample_count != dst_texture.desc.sample_count {
1560 return Err(TransferError::SampleCountNotEqual {
1561 src_sample_count: src_texture.desc.sample_count,
1562 dst_sample_count: dst_texture.desc.sample_count,
1563 }
1564 .into());
1565 }
1566
1567 let src_raw = src_texture.try_inner(state.snatch_guard)?.raw();
1568 src_texture
1569 .check_usage(TextureUsages::COPY_SRC)
1570 .map_err(TransferError::MissingTextureUsage)?;
1571 let dst_raw = dst_texture.try_inner(state.snatch_guard)?.raw();
1572 dst_texture
1573 .check_usage(TextureUsages::COPY_DST)
1574 .map_err(TransferError::MissingTextureUsage)?;
1575
1576 if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1579 log::trace!("Ignoring copy_texture_to_texture of size 0");
1580 return Ok(());
1581 }
1582
1583 handle_src_texture_init(state, source, copy_size, src_texture)?;
1587 handle_dst_texture_init(state, destination, copy_size, dst_texture)?;
1588
1589 let src_pending =
1590 state
1591 .tracker
1592 .textures
1593 .set_single(src_texture, src_range, wgt::TextureUses::COPY_SRC);
1594
1595 let mut barriers: ArrayVec<_, 2> = src_pending
1598 .map(|pending| pending.into_hal(src_raw))
1599 .collect();
1600
1601 let dst_pending =
1602 state
1603 .tracker
1604 .textures
1605 .set_single(dst_texture, dst_range, wgt::TextureUses::COPY_DST);
1606 barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_raw)));
1607
1608 let hal_copy_size = hal::CopyExtent {
1609 width: src_copy_size.width.min(dst_copy_size.width),
1610 height: src_copy_size.height.min(dst_copy_size.height),
1611 depth: src_copy_size.depth.min(dst_copy_size.depth),
1612 };
1613
1614 let dst_format = dst_texture.desc.format;
1615
1616 let regions = (0..array_layer_count).map(|rel_array_layer| {
1617 let mut src_base = src_tex_base.clone();
1618 let mut dst_base = dst_tex_base.clone();
1619 src_base.array_layer += rel_array_layer;
1620 dst_base.array_layer += rel_array_layer;
1621 hal::TextureCopy {
1622 src_base,
1623 dst_base,
1624 size: hal_copy_size,
1625 }
1626 });
1627
1628 let regions = if dst_tex_base.aspect == hal::FormatAspects::DEPTH_STENCIL {
1629 regions
1630 .flat_map(|region| {
1631 let (mut depth, mut stencil) = (region.clone(), region);
1632 depth.src_base.aspect = hal::FormatAspects::DEPTH;
1633 depth.dst_base.aspect = hal::FormatAspects::DEPTH;
1634 stencil.src_base.aspect = hal::FormatAspects::STENCIL;
1635 stencil.dst_base.aspect = hal::FormatAspects::STENCIL;
1636 [depth, stencil]
1637 })
1638 .collect::<Vec<_>>()
1639 } else if let Some(plane_count) = dst_format.planes() {
1640 regions
1641 .into_iter()
1642 .flat_map(|region| {
1643 (0..plane_count).map(move |plane| {
1644 let mut plane_region = region.clone();
1645
1646 let plane_aspect = wgt::TextureAspect::from_plane(plane)
1647 .expect("expected texture aspect to exist for the plane");
1648 let plane_aspect = hal::FormatAspects::new(dst_format, plane_aspect);
1649 plane_region.src_base.aspect = plane_aspect;
1650 plane_region.dst_base.aspect = plane_aspect;
1651
1652 let (w_subsampling, h_subsampling) =
1653 dst_format.subsampling_factors(Some(plane));
1654 plane_region.src_base.origin.x /= w_subsampling;
1655 plane_region.src_base.origin.y /= h_subsampling;
1656 plane_region.dst_base.origin.x /= w_subsampling;
1657 plane_region.dst_base.origin.y /= h_subsampling;
1658
1659 plane_region.size.width /= w_subsampling;
1660 plane_region.size.height /= h_subsampling;
1661
1662 plane_region
1663 })
1664 })
1665 .collect::<Vec<_>>()
1666 } else {
1667 regions.collect::<Vec<_>>()
1668 };
1669 unsafe {
1670 state.raw_encoder.transition_textures(&barriers);
1671 state.raw_encoder.copy_texture_to_texture(
1672 src_raw,
1673 wgt::TextureUses::COPY_SRC,
1674 dst_raw,
1675 ®ions,
1676 );
1677 }
1678
1679 Ok(())
1680}