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