wgpu_core/command/
transfer.rs

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/// Error encountered while attempting a data transfer.
41#[derive(Clone, Debug, Error)]
42#[non_exhaustive]
43pub enum TransferError {
44    #[error("Source and destination cannot be the same buffer")]
45    SameSourceDestinationBuffer,
46    #[error(transparent)]
47    MissingBufferUsage(#[from] MissingBufferUsageError),
48    #[error(transparent)]
49    MissingTextureUsage(#[from] MissingTextureUsageError),
50    #[error("Copy of {start_offset}..{end_offset} would end up overrunning the bounds of the {side:?} buffer of size {buffer_size}")]
51    BufferOverrun {
52        start_offset: BufferAddress,
53        end_offset: BufferAddress,
54        buffer_size: BufferAddress,
55        side: CopySide,
56    },
57    #[error("Copy of {dimension:?} {start_offset}..{end_offset} would end up overrunning the bounds of the {side:?} texture of {dimension:?} size {texture_size}")]
58    TextureOverrun {
59        start_offset: u32,
60        end_offset: u32,
61        texture_size: u32,
62        dimension: TextureErrorDimension,
63        side: CopySide,
64    },
65    #[error("Partial copy of {start_offset}..{end_offset} on {dimension:?} dimension with size {texture_size} \
66             is not supported for the {side:?} texture format {format:?} with {sample_count} samples")]
67    UnsupportedPartialTransfer {
68        format: wgt::TextureFormat,
69        sample_count: u32,
70        start_offset: u32,
71        end_offset: u32,
72        texture_size: u32,
73        dimension: TextureErrorDimension,
74        side: CopySide,
75    },
76    #[error(
77        "Copying{} layers {}..{} to{} layers {}..{} of the same texture is not allowed",
78        if *src_aspects == wgt::TextureAspect::All { String::new() } else { format!(" {src_aspects:?}") },
79        src_origin_z,
80        src_origin_z + array_layer_count,
81        if *dst_aspects == wgt::TextureAspect::All { String::new() } else { format!(" {dst_aspects:?}") },
82        dst_origin_z,
83        dst_origin_z + array_layer_count,
84    )]
85    InvalidCopyWithinSameTexture {
86        src_aspects: wgt::TextureAspect,
87        dst_aspects: wgt::TextureAspect,
88        src_origin_z: u32,
89        dst_origin_z: u32,
90        array_layer_count: u32,
91    },
92    #[error("Unable to select texture aspect {aspect:?} from format {format:?}")]
93    InvalidTextureAspect {
94        format: wgt::TextureFormat,
95        aspect: wgt::TextureAspect,
96    },
97    #[error("Unable to select texture mip level {level} out of {total}")]
98    InvalidTextureMipLevel { level: u32, total: u32 },
99    #[error("Texture dimension must be 2D when copying from an external texture")]
100    InvalidDimensionExternal,
101    #[error("Buffer offset {0} is not aligned to block size or `COPY_BUFFER_ALIGNMENT`")]
102    UnalignedBufferOffset(BufferAddress),
103    #[error("Copy size {0} does not respect `COPY_BUFFER_ALIGNMENT`")]
104    UnalignedCopySize(BufferAddress),
105    #[error("Copy width is not a multiple of block width")]
106    UnalignedCopyWidth,
107    #[error("Copy height is not a multiple of block height")]
108    UnalignedCopyHeight,
109    #[error("Copy origin's x component is not a multiple of block width")]
110    UnalignedCopyOriginX,
111    #[error("Copy origin's y component is not a multiple of block height")]
112    UnalignedCopyOriginY,
113    #[error("Bytes per row does not respect `COPY_BYTES_PER_ROW_ALIGNMENT`")]
114    UnalignedBytesPerRow,
115    #[error("Number of bytes per row needs to be specified since more than one row is copied")]
116    UnspecifiedBytesPerRow,
117    #[error("Number of rows per image needs to be specified since more than one image is copied")]
118    UnspecifiedRowsPerImage,
119    #[error("Number of bytes per row is less than the number of bytes in a complete row")]
120    InvalidBytesPerRow,
121    #[error("Number of rows per image is invalid")]
122    InvalidRowsPerImage,
123    #[error("Overflow while computing the size of the copy")]
124    SizeOverflow,
125    #[error("Copy source aspects must refer to all aspects of the source texture format")]
126    CopySrcMissingAspects,
127    #[error(
128        "Copy destination aspects must refer to all aspects of the destination texture format"
129    )]
130    CopyDstMissingAspects,
131    #[error("Copy aspect must refer to a single aspect of texture format")]
132    CopyAspectNotOne,
133    #[error("Copying from textures with format {0:?} is forbidden")]
134    CopyFromForbiddenTextureFormat(wgt::TextureFormat),
135    #[error("Copying from textures with format {format:?} and aspect {aspect:?} is forbidden")]
136    CopyFromForbiddenTextureFormatAspect {
137        format: wgt::TextureFormat,
138        aspect: wgt::TextureAspect,
139    },
140    #[error("Copying to textures with format {0:?} is forbidden")]
141    CopyToForbiddenTextureFormat(wgt::TextureFormat),
142    #[error("Copying to textures with format {format:?} and aspect {aspect:?} is forbidden")]
143    CopyToForbiddenTextureFormatAspect {
144        format: wgt::TextureFormat,
145        aspect: wgt::TextureAspect,
146    },
147    #[error(
148        "Copying to textures with format {0:?} is forbidden when copying from external texture"
149    )]
150    ExternalCopyToForbiddenTextureFormat(wgt::TextureFormat),
151    #[error(
152        "Source format ({src_format:?}) and destination format ({dst_format:?}) are not copy-compatible (they may only differ in srgb-ness)"
153    )]
154    TextureFormatsNotCopyCompatible {
155        src_format: wgt::TextureFormat,
156        dst_format: wgt::TextureFormat,
157    },
158    #[error(transparent)]
159    MemoryInitFailure(#[from] ClearError),
160    #[error("Cannot encode this copy because of a missing downelevel flag")]
161    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
162    #[error("Source texture sample count must be 1, got {sample_count}")]
163    InvalidSampleCount { sample_count: u32 },
164    #[error(
165        "Source sample count ({src_sample_count:?}) and destination sample count ({dst_sample_count:?}) are not equal"
166    )]
167    SampleCountNotEqual {
168        src_sample_count: u32,
169        dst_sample_count: u32,
170    },
171    #[error("Requested mip level {requested} does not exist (count: {count})")]
172    InvalidMipLevel { requested: u32, count: u32 },
173    #[error("Buffer is expected to be unmapped, but was not")]
174    BufferNotAvailable,
175}
176
177impl WebGpuError for TransferError {
178    fn webgpu_error_type(&self) -> ErrorType {
179        let e: &dyn WebGpuError = match self {
180            Self::MissingBufferUsage(e) => e,
181            Self::MissingTextureUsage(e) => e,
182            Self::MemoryInitFailure(e) => e,
183
184            Self::BufferOverrun { .. }
185            | Self::TextureOverrun { .. }
186            | Self::UnsupportedPartialTransfer { .. }
187            | Self::InvalidCopyWithinSameTexture { .. }
188            | Self::InvalidTextureAspect { .. }
189            | Self::InvalidTextureMipLevel { .. }
190            | Self::InvalidDimensionExternal
191            | Self::UnalignedBufferOffset(..)
192            | Self::UnalignedCopySize(..)
193            | Self::UnalignedCopyWidth
194            | Self::UnalignedCopyHeight
195            | Self::UnalignedCopyOriginX
196            | Self::UnalignedCopyOriginY
197            | Self::UnalignedBytesPerRow
198            | Self::UnspecifiedBytesPerRow
199            | Self::UnspecifiedRowsPerImage
200            | Self::InvalidBytesPerRow
201            | Self::InvalidRowsPerImage
202            | Self::SizeOverflow
203            | Self::CopySrcMissingAspects
204            | Self::CopyDstMissingAspects
205            | Self::CopyAspectNotOne
206            | Self::CopyFromForbiddenTextureFormat(..)
207            | Self::CopyFromForbiddenTextureFormatAspect { .. }
208            | Self::CopyToForbiddenTextureFormat(..)
209            | Self::CopyToForbiddenTextureFormatAspect { .. }
210            | Self::ExternalCopyToForbiddenTextureFormat(..)
211            | Self::TextureFormatsNotCopyCompatible { .. }
212            | Self::MissingDownlevelFlags(..)
213            | Self::InvalidSampleCount { .. }
214            | Self::SampleCountNotEqual { .. }
215            | Self::InvalidMipLevel { .. }
216            | Self::SameSourceDestinationBuffer
217            | Self::BufferNotAvailable => return ErrorType::Validation,
218        };
219        e.webgpu_error_type()
220    }
221}
222
223impl From<BufferTextureCopyInfoError> for TransferError {
224    fn from(value: BufferTextureCopyInfoError) -> Self {
225        match value {
226            BufferTextureCopyInfoError::InvalidBytesPerRow => Self::InvalidBytesPerRow,
227            BufferTextureCopyInfoError::InvalidRowsPerImage => Self::InvalidRowsPerImage,
228            BufferTextureCopyInfoError::ImageStrideOverflow
229            | BufferTextureCopyInfoError::ImageBytesOverflow(_)
230            | BufferTextureCopyInfoError::ArraySizeOverflow(_) => Self::SizeOverflow,
231        }
232    }
233}
234
235pub(crate) fn extract_texture_selector<T>(
236    copy_texture: &wgt::TexelCopyTextureInfo<T>,
237    copy_size: &Extent3d,
238    texture: &Texture,
239) -> Result<(TextureSelector, hal::TextureCopyBase), TransferError> {
240    let format = texture.desc.format;
241    let copy_aspect = hal::FormatAspects::new(format, copy_texture.aspect);
242    if copy_aspect.is_empty() {
243        return Err(TransferError::InvalidTextureAspect {
244            format,
245            aspect: copy_texture.aspect,
246        });
247    }
248
249    let (layers, origin_z) = match texture.desc.dimension {
250        wgt::TextureDimension::D1 => (0..1, 0),
251        wgt::TextureDimension::D2 => (
252            copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers,
253            0,
254        ),
255        wgt::TextureDimension::D3 => (0..1, copy_texture.origin.z),
256    };
257    let base = hal::TextureCopyBase {
258        origin: wgt::Origin3d {
259            x: copy_texture.origin.x,
260            y: copy_texture.origin.y,
261            z: origin_z,
262        },
263        // this value will be incremented per copied layer
264        array_layer: layers.start,
265        mip_level: copy_texture.mip_level,
266        aspect: copy_aspect,
267    };
268    let selector = TextureSelector {
269        mips: copy_texture.mip_level..copy_texture.mip_level + 1,
270        layers,
271    };
272
273    Ok((selector, base))
274}
275
276/// WebGPU's [validating linear texture data][vltd] algorithm.
277///
278/// Copied with some modifications from WebGPU standard.
279///
280/// If successful, returns a tuple `(bytes, stride, is_contiguous)`, where:
281/// - `bytes` is the number of buffer bytes required for this copy, and
282/// - `stride` number of bytes between array layers.
283/// - `is_contiguous` is true if the linear texture data does not have padding
284///   between rows or between images.
285///
286/// [vltd]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-linear-texture-data
287pub(crate) fn validate_linear_texture_data(
288    layout: &wgt::TexelCopyBufferLayout,
289    format: wgt::TextureFormat,
290    aspect: wgt::TextureAspect,
291    buffer_size: BufferAddress,
292    buffer_side: CopySide,
293    copy_size: &Extent3d,
294) -> Result<(BufferAddress, BufferAddress, bool), TransferError> {
295    let wgt::BufferTextureCopyInfo {
296        copy_width,
297        copy_height,
298        depth_or_array_layers,
299
300        offset,
301
302        block_size_bytes: _,
303        block_width_texels,
304        block_height_texels,
305
306        width_blocks: _,
307        height_blocks,
308
309        row_bytes_dense,
310        row_stride_bytes,
311
312        image_stride_rows: _,
313        image_stride_bytes,
314
315        image_rows_dense: _,
316        image_bytes_dense,
317
318        bytes_in_copy,
319    } = layout.get_buffer_texture_copy_info(format, aspect, copy_size)?;
320
321    if copy_width % block_width_texels != 0 {
322        return Err(TransferError::UnalignedCopyWidth);
323    }
324    if copy_height % block_height_texels != 0 {
325        return Err(TransferError::UnalignedCopyHeight);
326    }
327
328    let requires_multiple_rows = depth_or_array_layers > 1 || height_blocks > 1;
329    let requires_multiple_images = depth_or_array_layers > 1;
330
331    // `get_buffer_texture_copy_info()` already proceeded with defaults if these
332    // were not specified, and ensured that the values satisfy the minima if
333    // they were, but now we enforce the WebGPU requirement that they be
334    // specified any time they apply.
335    if layout.bytes_per_row.is_none() && requires_multiple_rows {
336        return Err(TransferError::UnspecifiedBytesPerRow);
337    }
338
339    if layout.rows_per_image.is_none() && requires_multiple_images {
340        return Err(TransferError::UnspecifiedRowsPerImage);
341    };
342
343    // Avoid underflow in the subtraction by checking bytes_in_copy against buffer_size first.
344    if bytes_in_copy > buffer_size || offset > buffer_size - bytes_in_copy {
345        return Err(TransferError::BufferOverrun {
346            start_offset: offset,
347            end_offset: offset.wrapping_add(bytes_in_copy),
348            buffer_size,
349            side: buffer_side,
350        });
351    }
352
353    let is_contiguous = (row_stride_bytes == row_bytes_dense || !requires_multiple_rows)
354        && (image_stride_bytes == image_bytes_dense || !requires_multiple_images);
355
356    Ok((bytes_in_copy, image_stride_bytes, is_contiguous))
357}
358
359/// Validate the source format of a texture copy.
360///
361/// This performs the check from WebGPU's [validating texture buffer copy][vtbc]
362/// algorithm that ensures that the format and aspect form a valid texel copy source
363/// as defined in the [depth-stencil formats][dsf].
364///
365/// [vtbc]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-texture-buffer-copy
366/// [dsf]: https://gpuweb.github.io/gpuweb/#depth-formats
367pub(crate) fn validate_texture_copy_src_format(
368    format: wgt::TextureFormat,
369    aspect: wgt::TextureAspect,
370) -> Result<(), TransferError> {
371    use wgt::TextureAspect as Ta;
372    use wgt::TextureFormat as Tf;
373    match (format, aspect) {
374        (Tf::Depth24Plus, _) => Err(TransferError::CopyFromForbiddenTextureFormat(format)),
375        (Tf::Depth24PlusStencil8, Ta::DepthOnly) => {
376            Err(TransferError::CopyFromForbiddenTextureFormatAspect { format, aspect })
377        }
378        _ => Ok(()),
379    }
380}
381
382/// Validate the destination format of a texture copy.
383///
384/// This performs the check from WebGPU's [validating texture buffer copy][vtbc]
385/// algorithm that ensures that the format and aspect form a valid texel copy destination
386/// as defined in the [depth-stencil formats][dsf].
387///
388/// [vtbc]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-texture-buffer-copy
389/// [dsf]: https://gpuweb.github.io/gpuweb/#depth-formats
390pub(crate) fn validate_texture_copy_dst_format(
391    format: wgt::TextureFormat,
392    aspect: wgt::TextureAspect,
393) -> Result<(), TransferError> {
394    use wgt::TextureAspect as Ta;
395    use wgt::TextureFormat as Tf;
396    match (format, aspect) {
397        (Tf::Depth24Plus | Tf::Depth32Float, _) => {
398            Err(TransferError::CopyToForbiddenTextureFormat(format))
399        }
400        (Tf::Depth24PlusStencil8 | Tf::Depth32FloatStencil8, Ta::DepthOnly) => {
401            Err(TransferError::CopyToForbiddenTextureFormatAspect { format, aspect })
402        }
403        _ => Ok(()),
404    }
405}
406
407/// Validation for texture/buffer copies.
408///
409/// This implements the following checks from WebGPU's [validating texture buffer copy][vtbc]
410/// algorithm:
411///  * The texture must not be multisampled.
412///  * The copy must be from/to a single aspect of the texture.
413///  * If `aligned` is true, the buffer offset must be aligned appropriately.
414///
415/// And implements the following check from WebGPU's [validating GPUTexelCopyBufferInfo][vtcbi]
416/// algorithm:
417///  * If `aligned` is true, `bytesPerRow` must be a multiple of 256.
418///
419/// Note that the `bytesPerRow` alignment check is enforced whenever
420/// `bytesPerRow` is specified, even if the transfer is not multiple rows and
421/// `bytesPerRow` could have been omitted.
422///
423/// The following steps in [validating texture buffer copy][vtbc] are implemented elsewhere:
424///  * Invocation of other validation algorithms.
425///  * The texture usage (COPY_DST / COPY_SRC) check.
426///  * The check for non-copyable depth/stencil formats. The caller must perform
427///    this check using `validate_texture_copy_src_format` / `validate_texture_copy_dst_format`
428///    before calling this function. This function will panic if
429///    [`wgt::TextureFormat::block_copy_size`] returns `None` due to a
430///    non-copyable format.
431///
432/// [vtbc]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-texture-buffer-copy
433/// [vtcbi]: https://www.w3.org/TR/webgpu/#abstract-opdef-validating-gputexelcopybufferinfo
434pub(crate) fn validate_texture_buffer_copy<T>(
435    texture_copy_view: &wgt::TexelCopyTextureInfo<T>,
436    aspect: hal::FormatAspects,
437    desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
438    layout: &wgt::TexelCopyBufferLayout,
439    aligned: bool,
440) -> Result<(), TransferError> {
441    if desc.sample_count != 1 {
442        return Err(TransferError::InvalidSampleCount {
443            sample_count: desc.sample_count,
444        });
445    }
446
447    if !aspect.is_one() {
448        return Err(TransferError::CopyAspectNotOne);
449    }
450
451    let offset_alignment = if desc.format.is_depth_stencil_format() {
452        4
453    } else {
454        // The case where `block_copy_size` returns `None` is currently
455        // unreachable both for the reason in the expect message, and also
456        // because the currently-defined non-copyable formats are depth/stencil
457        // formats so would take the `if` branch.
458        desc.format
459            .block_copy_size(Some(texture_copy_view.aspect))
460            .expect("non-copyable formats should have been rejected previously")
461    };
462
463    if aligned && layout.offset % u64::from(offset_alignment) != 0 {
464        return Err(TransferError::UnalignedBufferOffset(layout.offset));
465    }
466
467    if let Some(bytes_per_row) = layout.bytes_per_row {
468        if aligned && bytes_per_row % wgt::COPY_BYTES_PER_ROW_ALIGNMENT != 0 {
469            return Err(TransferError::UnalignedBytesPerRow);
470        }
471    }
472
473    Ok(())
474}
475
476/// Validate the extent and alignment of a texture copy.
477///
478/// Copied with minor modifications from WebGPU standard. This mostly follows
479/// the [validating GPUTexelCopyTextureInfo][vtcti] and [validating texture copy
480/// range][vtcr] algorithms.
481///
482/// Returns the HAL copy extent and the layer count.
483///
484/// [vtcti]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-gputexelcopytextureinfo
485/// [vtcr]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-texture-copy-range
486pub(crate) fn validate_texture_copy_range<T>(
487    texture_copy_view: &wgt::TexelCopyTextureInfo<T>,
488    desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
489    texture_side: CopySide,
490    copy_size: &Extent3d,
491) -> Result<(hal::CopyExtent, u32), TransferError> {
492    let (block_width, block_height) = desc.format.block_dimensions();
493
494    let extent_virtual = desc.mip_level_size(texture_copy_view.mip_level).ok_or(
495        TransferError::InvalidTextureMipLevel {
496            level: texture_copy_view.mip_level,
497            total: desc.mip_level_count,
498        },
499    )?;
500    // physical size can be larger than the virtual
501    let extent = extent_virtual.physical_size(desc.format);
502
503    // Multisampled and depth-stencil formats do not support partial copies
504    // on x and y dimensions, but do support copying a subset of layers.
505    let requires_exact_size = desc.format.is_depth_stencil_format() || desc.sample_count > 1;
506
507    // Return `Ok` if a run `size` texels long starting at `start_offset` is
508    // valid for `texture_size`. Otherwise, return an appropriate a`Err`.
509    let check_dimension = |dimension: TextureErrorDimension,
510                           start_offset: u32,
511                           size: u32,
512                           texture_size: u32,
513                           requires_exact_size: bool|
514     -> Result<(), TransferError> {
515        if requires_exact_size && (start_offset != 0 || size != texture_size) {
516            Err(TransferError::UnsupportedPartialTransfer {
517                format: desc.format,
518                sample_count: desc.sample_count,
519                start_offset,
520                end_offset: start_offset.wrapping_add(size),
521                texture_size,
522                dimension,
523                side: texture_side,
524            })
525        // Avoid underflow in the subtraction by checking start_offset against
526        // texture_size first.
527        } else if start_offset > texture_size || texture_size - start_offset < size {
528            Err(TransferError::TextureOverrun {
529                start_offset,
530                end_offset: start_offset.wrapping_add(size),
531                texture_size,
532                dimension,
533                side: texture_side,
534            })
535        } else {
536            Ok(())
537        }
538    };
539
540    check_dimension(
541        TextureErrorDimension::X,
542        texture_copy_view.origin.x,
543        copy_size.width,
544        extent.width,
545        requires_exact_size,
546    )?;
547    check_dimension(
548        TextureErrorDimension::Y,
549        texture_copy_view.origin.y,
550        copy_size.height,
551        extent.height,
552        requires_exact_size,
553    )?;
554    check_dimension(
555        TextureErrorDimension::Z,
556        texture_copy_view.origin.z,
557        copy_size.depth_or_array_layers,
558        extent.depth_or_array_layers,
559        false, // partial copy always allowed on Z/layer dimension
560    )?;
561
562    if texture_copy_view.origin.x % block_width != 0 {
563        return Err(TransferError::UnalignedCopyOriginX);
564    }
565    if texture_copy_view.origin.y % block_height != 0 {
566        return Err(TransferError::UnalignedCopyOriginY);
567    }
568    if copy_size.width % block_width != 0 {
569        return Err(TransferError::UnalignedCopyWidth);
570    }
571    if copy_size.height % block_height != 0 {
572        return Err(TransferError::UnalignedCopyHeight);
573    }
574
575    let (depth, array_layer_count) = match desc.dimension {
576        wgt::TextureDimension::D1 => (1, 1),
577        wgt::TextureDimension::D2 => (1, copy_size.depth_or_array_layers),
578        wgt::TextureDimension::D3 => (copy_size.depth_or_array_layers, 1),
579    };
580
581    let copy_extent = hal::CopyExtent {
582        width: copy_size.width,
583        height: copy_size.height,
584        depth,
585    };
586    Ok((copy_extent, array_layer_count))
587}
588
589/// Validate a copy within the same texture.
590///
591/// This implements the WebGPU requirement that the [sets of subresources for
592/// texture copy][srtc] of the source and destination be disjoint, i.e. that the
593/// source and destination do not overlap.
594///
595/// This function assumes that the copy ranges have already been validated with
596/// `validate_texture_copy_range`.
597///
598/// [srtc]: https://gpuweb.github.io/gpuweb/#abstract-opdef-set-of-subresources-for-texture-copy
599pub(crate) fn validate_copy_within_same_texture<T>(
600    src: &wgt::TexelCopyTextureInfo<T>,
601    dst: &wgt::TexelCopyTextureInfo<T>,
602    format: wgt::TextureFormat,
603    array_layer_count: u32,
604) -> Result<(), TransferError> {
605    let src_aspects = hal::FormatAspects::new(format, src.aspect);
606    let dst_aspects = hal::FormatAspects::new(format, dst.aspect);
607    if (src_aspects & dst_aspects).is_empty() {
608        // Copying between different aspects (if it even makes sense), is okay.
609        return Ok(());
610    }
611
612    if src.origin.z >= dst.origin.z + array_layer_count
613        || dst.origin.z >= src.origin.z + array_layer_count
614    {
615        // Copying between non-overlapping layer ranges is okay.
616        return Ok(());
617    }
618
619    if src.mip_level != dst.mip_level {
620        // Copying between different mip levels is okay.
621        return Ok(());
622    }
623
624    Err(TransferError::InvalidCopyWithinSameTexture {
625        src_aspects: src.aspect,
626        dst_aspects: dst.aspect,
627        src_origin_z: src.origin.z,
628        dst_origin_z: dst.origin.z,
629        array_layer_count,
630    })
631}
632
633fn handle_texture_init(
634    state: &mut EncodingState,
635    init_kind: MemoryInitKind,
636    copy_texture: &TexelCopyTextureInfo,
637    copy_size: &Extent3d,
638    texture: &Arc<Texture>,
639) -> Result<(), ClearError> {
640    let init_action = TextureInitTrackerAction {
641        texture: texture.clone(),
642        range: TextureInitRange {
643            mip_range: copy_texture.mip_level..copy_texture.mip_level + 1,
644            layer_range: copy_texture.origin.z
645                ..(copy_texture.origin.z + copy_size.depth_or_array_layers),
646        },
647        kind: init_kind,
648    };
649
650    // Register the init action.
651    let immediate_inits = state
652        .texture_memory_actions
653        .register_init_action(&{ init_action });
654
655    // In rare cases we may need to insert an init operation immediately onto the command buffer.
656    if !immediate_inits.is_empty() {
657        for init in immediate_inits {
658            clear_texture(
659                &init.texture,
660                TextureInitRange {
661                    mip_range: init.mip_level..(init.mip_level + 1),
662                    layer_range: init.layer..(init.layer + 1),
663                },
664                state.raw_encoder,
665                &mut state.tracker.textures,
666                &state.device.alignments,
667                state.device.zero_buffer.as_ref(),
668                state.snatch_guard,
669                state.device.instance_flags,
670            )?;
671        }
672    }
673
674    Ok(())
675}
676
677/// Prepare a transfer's source texture.
678///
679/// Ensure the source texture of a transfer is in the right initialization
680/// state, and record the state for after the transfer operation.
681fn handle_src_texture_init(
682    state: &mut EncodingState,
683    source: &TexelCopyTextureInfo,
684    copy_size: &Extent3d,
685    texture: &Arc<Texture>,
686) -> Result<(), TransferError> {
687    handle_texture_init(
688        state,
689        MemoryInitKind::NeedsInitializedMemory,
690        source,
691        copy_size,
692        texture,
693    )?;
694    Ok(())
695}
696
697/// Prepare a transfer's destination texture.
698///
699/// Ensure the destination texture of a transfer is in the right initialization
700/// state, and record the state for after the transfer operation.
701fn handle_dst_texture_init(
702    state: &mut EncodingState,
703    destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
704    copy_size: &Extent3d,
705    texture: &Arc<Texture>,
706) -> Result<(), TransferError> {
707    // Attention: If we don't write full texture subresources, we need to a full
708    // clear first since we don't track subrects. This means that in rare cases
709    // even a *destination* texture of a transfer may need an immediate texture
710    // init.
711    let dst_init_kind = if has_copy_partial_init_tracker_coverage(
712        copy_size,
713        destination.mip_level,
714        &texture.desc,
715    ) {
716        MemoryInitKind::NeedsInitializedMemory
717    } else {
718        MemoryInitKind::ImplicitlyInitialized
719    };
720
721    handle_texture_init(state, dst_init_kind, destination, copy_size, texture)?;
722    Ok(())
723}
724
725/// Handle initialization tracking for a transfer's source or destination buffer.
726///
727/// Ensures that the transfer will not read from uninitialized memory, and updates
728/// the initialization state information to reflect the transfer.
729fn handle_buffer_init(
730    state: &mut EncodingState,
731    info: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
732    direction: CopySide,
733    required_buffer_bytes_in_copy: BufferAddress,
734    is_contiguous: bool,
735) {
736    const ALIGN_SIZE: BufferAddress = wgt::COPY_BUFFER_ALIGNMENT;
737    const ALIGN_MASK: BufferAddress = wgt::COPY_BUFFER_ALIGNMENT - 1;
738
739    let buffer = &info.buffer;
740    let start = info.layout.offset;
741    let end = info.layout.offset + required_buffer_bytes_in_copy;
742    if !is_contiguous || direction == CopySide::Source {
743        // If the transfer will read the buffer, then the whole region needs to
744        // be initialized.
745        //
746        // If the transfer will not write a contiguous region of the buffer,
747        // then we need to make sure the padding areas are initialized. For now,
748        // initialize the whole region, although this could be improved to
749        // initialize only the necessary parts if doing so is likely to be
750        // faster than initializing the whole thing.
751        //
752        // Adjust the start/end outwards to 4B alignment.
753        let aligned_start = start & !ALIGN_MASK;
754        let aligned_end = (end + ALIGN_MASK) & !ALIGN_MASK;
755        state
756            .buffer_memory_init_actions
757            .extend(buffer.initialization_status.read().create_action(
758                buffer,
759                aligned_start..aligned_end,
760                MemoryInitKind::NeedsInitializedMemory,
761            ));
762    } else {
763        // If the transfer will write a contiguous region of the buffer, then we
764        // don't need to initialize that region.
765        //
766        // However, if the start and end are not 4B aligned, we need to make
767        // sure that we don't end up trying to initialize non-4B-aligned regions
768        // later.
769        //
770        // Adjust the start/end inwards to 4B alignment, we will handle the
771        // first/last pieces differently.
772        let aligned_start = (start + ALIGN_MASK) & !ALIGN_MASK;
773        let aligned_end = end & !ALIGN_MASK;
774        if aligned_start != start {
775            state.buffer_memory_init_actions.extend(
776                buffer.initialization_status.read().create_action(
777                    buffer,
778                    aligned_start - ALIGN_SIZE..aligned_start,
779                    MemoryInitKind::NeedsInitializedMemory,
780                ),
781            );
782        }
783        if aligned_start != aligned_end {
784            state.buffer_memory_init_actions.extend(
785                buffer.initialization_status.read().create_action(
786                    buffer,
787                    aligned_start..aligned_end,
788                    MemoryInitKind::ImplicitlyInitialized,
789                ),
790            );
791        }
792        if aligned_end != end {
793            // It is possible that `aligned_end + ALIGN_SIZE > dst_buffer.size`,
794            // because `dst_buffer.size` is the user-requested size, not the
795            // final size of the buffer. The final size of the buffer is not
796            // readily available, but was rounded up to COPY_BUFFER_ALIGNMENT,
797            // so no overrun is possible.
798            state.buffer_memory_init_actions.extend(
799                buffer.initialization_status.read().create_action(
800                    buffer,
801                    aligned_end..aligned_end + ALIGN_SIZE,
802                    MemoryInitKind::NeedsInitializedMemory,
803                ),
804            );
805        }
806    }
807}
808
809impl Global {
810    pub fn command_encoder_copy_buffer_to_buffer(
811        &self,
812        command_encoder_id: CommandEncoderId,
813        source: BufferId,
814        source_offset: BufferAddress,
815        destination: BufferId,
816        destination_offset: BufferAddress,
817        size: Option<BufferAddress>,
818    ) -> Result<(), EncoderStateError> {
819        profiling::scope!("CommandEncoder::copy_buffer_to_buffer");
820        api_log!(
821            "CommandEncoder::copy_buffer_to_buffer {source:?} -> {destination:?} {size:?}bytes"
822        );
823
824        let hub = &self.hub;
825
826        let cmd_enc = hub.command_encoders.get(command_encoder_id);
827        let mut cmd_buf_data = cmd_enc.data.lock();
828
829        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
830            Ok(ArcCommand::CopyBufferToBuffer {
831                src: self.resolve_buffer_id(source)?,
832                src_offset: source_offset,
833                dst: self.resolve_buffer_id(destination)?,
834                dst_offset: destination_offset,
835                size,
836            })
837        })
838    }
839
840    pub fn command_encoder_copy_buffer_to_texture(
841        &self,
842        command_encoder_id: CommandEncoderId,
843        source: &TexelCopyBufferInfo,
844        destination: &wgt::TexelCopyTextureInfo<TextureId>,
845        copy_size: &Extent3d,
846    ) -> Result<(), EncoderStateError> {
847        profiling::scope!("CommandEncoder::copy_buffer_to_texture");
848        api_log!(
849            "CommandEncoder::copy_buffer_to_texture {:?} -> {:?} {copy_size:?}",
850            source.buffer,
851            destination.texture
852        );
853
854        let cmd_enc = self.hub.command_encoders.get(command_encoder_id);
855        let mut cmd_buf_data = cmd_enc.data.lock();
856
857        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
858            Ok(ArcCommand::CopyBufferToTexture {
859                src: wgt::TexelCopyBufferInfo::<Arc<Buffer>> {
860                    buffer: self.resolve_buffer_id(source.buffer)?,
861                    layout: source.layout,
862                },
863                dst: wgt::TexelCopyTextureInfo::<Arc<Texture>> {
864                    texture: self.resolve_texture_id(destination.texture)?,
865                    mip_level: destination.mip_level,
866                    origin: destination.origin,
867                    aspect: destination.aspect,
868                },
869                size: *copy_size,
870            })
871        })
872    }
873
874    pub fn command_encoder_copy_texture_to_buffer(
875        &self,
876        command_encoder_id: CommandEncoderId,
877        source: &wgt::TexelCopyTextureInfo<TextureId>,
878        destination: &TexelCopyBufferInfo,
879        copy_size: &Extent3d,
880    ) -> Result<(), EncoderStateError> {
881        profiling::scope!("CommandEncoder::copy_texture_to_buffer");
882        api_log!(
883            "CommandEncoder::copy_texture_to_buffer {:?} -> {:?} {copy_size:?}",
884            source.texture,
885            destination.buffer
886        );
887
888        let cmd_enc = self.hub.command_encoders.get(command_encoder_id);
889        let mut cmd_buf_data = cmd_enc.data.lock();
890
891        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
892            Ok(ArcCommand::CopyTextureToBuffer {
893                src: wgt::TexelCopyTextureInfo::<Arc<Texture>> {
894                    texture: self.resolve_texture_id(source.texture)?,
895                    mip_level: source.mip_level,
896                    origin: source.origin,
897                    aspect: source.aspect,
898                },
899                dst: wgt::TexelCopyBufferInfo::<Arc<Buffer>> {
900                    buffer: self.resolve_buffer_id(destination.buffer)?,
901                    layout: destination.layout,
902                },
903                size: *copy_size,
904            })
905        })
906    }
907
908    pub fn command_encoder_copy_texture_to_texture(
909        &self,
910        command_encoder_id: CommandEncoderId,
911        source: &wgt::TexelCopyTextureInfo<TextureId>,
912        destination: &wgt::TexelCopyTextureInfo<TextureId>,
913        copy_size: &Extent3d,
914    ) -> Result<(), EncoderStateError> {
915        profiling::scope!("CommandEncoder::copy_texture_to_texture");
916        api_log!(
917            "CommandEncoder::copy_texture_to_texture {:?} -> {:?} {copy_size:?}",
918            source.texture,
919            destination.texture
920        );
921
922        let cmd_enc = self.hub.command_encoders.get(command_encoder_id);
923        let mut cmd_buf_data = cmd_enc.data.lock();
924
925        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
926            Ok(ArcCommand::CopyTextureToTexture {
927                src: wgt::TexelCopyTextureInfo {
928                    texture: self.resolve_texture_id(source.texture)?,
929                    mip_level: source.mip_level,
930                    origin: source.origin,
931                    aspect: source.aspect,
932                },
933                dst: wgt::TexelCopyTextureInfo {
934                    texture: self.resolve_texture_id(destination.texture)?,
935                    mip_level: destination.mip_level,
936                    origin: destination.origin,
937                    aspect: destination.aspect,
938                },
939                size: *copy_size,
940            })
941        })
942    }
943}
944
945pub(super) fn copy_buffer_to_buffer(
946    state: &mut EncodingState,
947    src_buffer: &Arc<Buffer>,
948    source_offset: BufferAddress,
949    dst_buffer: &Arc<Buffer>,
950    destination_offset: BufferAddress,
951    size: Option<BufferAddress>,
952) -> Result<(), CommandEncoderError> {
953    if src_buffer.is_equal(dst_buffer) {
954        return Err(TransferError::SameSourceDestinationBuffer.into());
955    }
956
957    src_buffer.same_device(state.device)?;
958
959    let src_pending = state
960        .tracker
961        .buffers
962        .set_single(src_buffer, wgt::BufferUses::COPY_SRC);
963
964    let src_raw = src_buffer.try_raw(state.snatch_guard)?;
965    src_buffer
966        .check_usage(BufferUsages::COPY_SRC)
967        .map_err(TransferError::MissingBufferUsage)?;
968    // expecting only a single barrier
969    let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer, state.snatch_guard));
970
971    dst_buffer.same_device(state.device)?;
972
973    let dst_pending = state
974        .tracker
975        .buffers
976        .set_single(dst_buffer, wgt::BufferUses::COPY_DST);
977
978    let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;
979    dst_buffer
980        .check_usage(BufferUsages::COPY_DST)
981        .map_err(TransferError::MissingBufferUsage)?;
982    let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));
983
984    let (size, source_end_offset) = match size {
985        Some(size) => (size, source_offset + size),
986        None => (src_buffer.size - source_offset, src_buffer.size),
987    };
988
989    if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
990        return Err(TransferError::UnalignedCopySize(size).into());
991    }
992    if source_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
993        return Err(TransferError::UnalignedBufferOffset(source_offset).into());
994    }
995    if destination_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
996        return Err(TransferError::UnalignedBufferOffset(destination_offset).into());
997    }
998    if !state
999        .device
1000        .downlevel
1001        .flags
1002        .contains(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)
1003        && (src_buffer.usage.contains(BufferUsages::INDEX)
1004            || dst_buffer.usage.contains(BufferUsages::INDEX))
1005    {
1006        let forbidden_usages = BufferUsages::VERTEX
1007            | BufferUsages::UNIFORM
1008            | BufferUsages::INDIRECT
1009            | BufferUsages::STORAGE;
1010        if src_buffer.usage.intersects(forbidden_usages)
1011            || dst_buffer.usage.intersects(forbidden_usages)
1012        {
1013            return Err(TransferError::MissingDownlevelFlags(MissingDownlevelFlags(
1014                wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
1015            ))
1016            .into());
1017        }
1018    }
1019
1020    let destination_end_offset = destination_offset + size;
1021    if source_end_offset > src_buffer.size {
1022        return Err(TransferError::BufferOverrun {
1023            start_offset: source_offset,
1024            end_offset: source_end_offset,
1025            buffer_size: src_buffer.size,
1026            side: CopySide::Source,
1027        }
1028        .into());
1029    }
1030    if destination_end_offset > dst_buffer.size {
1031        return Err(TransferError::BufferOverrun {
1032            start_offset: destination_offset,
1033            end_offset: destination_end_offset,
1034            buffer_size: dst_buffer.size,
1035            side: CopySide::Destination,
1036        }
1037        .into());
1038    }
1039
1040    // This must happen after parameter validation (so that errors are reported
1041    // as required by the spec), but before any side effects.
1042    if size == 0 {
1043        log::trace!("Ignoring copy_buffer_to_buffer of size 0");
1044        return Ok(());
1045    }
1046
1047    // Make sure source is initialized memory and mark dest as initialized.
1048    state
1049        .buffer_memory_init_actions
1050        .extend(dst_buffer.initialization_status.read().create_action(
1051            dst_buffer,
1052            destination_offset..(destination_offset + size),
1053            MemoryInitKind::ImplicitlyInitialized,
1054        ));
1055    state
1056        .buffer_memory_init_actions
1057        .extend(src_buffer.initialization_status.read().create_action(
1058            src_buffer,
1059            source_offset..(source_offset + size),
1060            MemoryInitKind::NeedsInitializedMemory,
1061        ));
1062
1063    let region = hal::BufferCopy {
1064        src_offset: source_offset,
1065        dst_offset: destination_offset,
1066        size: wgt::BufferSize::new(size).unwrap(),
1067    };
1068    let barriers = src_barrier
1069        .into_iter()
1070        .chain(dst_barrier)
1071        .collect::<Vec<_>>();
1072    unsafe {
1073        state.raw_encoder.transition_buffers(&barriers);
1074        state
1075            .raw_encoder
1076            .copy_buffer_to_buffer(src_raw, dst_raw, &[region]);
1077    }
1078
1079    Ok(())
1080}
1081
1082pub(super) fn copy_buffer_to_texture(
1083    state: &mut EncodingState,
1084    source: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
1085    destination: &wgt::TexelCopyTextureInfo<Arc<Texture>>,
1086    copy_size: &Extent3d,
1087) -> Result<(), CommandEncoderError> {
1088    let dst_texture = &destination.texture;
1089    let src_buffer = &source.buffer;
1090
1091    dst_texture.same_device(state.device)?;
1092    src_buffer.same_device(state.device)?;
1093
1094    let (hal_copy_size, array_layer_count) = validate_texture_copy_range(
1095        destination,
1096        &dst_texture.desc,
1097        CopySide::Destination,
1098        copy_size,
1099    )?;
1100
1101    let (dst_range, dst_base) = extract_texture_selector(destination, copy_size, dst_texture)?;
1102
1103    let src_raw = src_buffer.try_raw(state.snatch_guard)?;
1104    src_buffer
1105        .check_usage(BufferUsages::COPY_SRC)
1106        .map_err(TransferError::MissingBufferUsage)?;
1107
1108    let dst_raw = dst_texture.try_raw(state.snatch_guard)?;
1109    dst_texture
1110        .check_usage(TextureUsages::COPY_DST)
1111        .map_err(TransferError::MissingTextureUsage)?;
1112
1113    validate_texture_copy_dst_format(dst_texture.desc.format, destination.aspect)?;
1114
1115    validate_texture_buffer_copy(
1116        destination,
1117        dst_base.aspect,
1118        &dst_texture.desc,
1119        &source.layout,
1120        true, // alignment required for buffer offset
1121    )?;
1122
1123    let (required_buffer_bytes_in_copy, bytes_per_array_layer, is_contiguous) =
1124        validate_linear_texture_data(
1125            &source.layout,
1126            dst_texture.desc.format,
1127            destination.aspect,
1128            src_buffer.size,
1129            CopySide::Source,
1130            copy_size,
1131        )?;
1132
1133    if dst_texture.desc.format.is_depth_stencil_format() {
1134        state
1135            .device
1136            .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
1137            .map_err(TransferError::from)?;
1138    }
1139
1140    // This must happen after parameter validation (so that errors are reported
1141    // as required by the spec), but before any side effects.
1142    if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1143        log::trace!("Ignoring copy_buffer_to_texture of size 0");
1144        return Ok(());
1145    }
1146
1147    // Handle texture init *before* dealing with barrier transitions so we
1148    // have an easier time inserting "immediate-inits" that may be required
1149    // by prior discards in rare cases.
1150    handle_dst_texture_init(state, destination, copy_size, dst_texture)?;
1151
1152    let src_pending = state
1153        .tracker
1154        .buffers
1155        .set_single(src_buffer, wgt::BufferUses::COPY_SRC);
1156    let src_barrier = src_pending.map(|pending| pending.into_hal(src_buffer, state.snatch_guard));
1157
1158    let dst_pending =
1159        state
1160            .tracker
1161            .textures
1162            .set_single(dst_texture, dst_range, wgt::TextureUses::COPY_DST);
1163    let dst_barrier = dst_pending
1164        .map(|pending| pending.into_hal(dst_raw))
1165        .collect::<Vec<_>>();
1166
1167    handle_buffer_init(
1168        state,
1169        source,
1170        CopySide::Source,
1171        required_buffer_bytes_in_copy,
1172        is_contiguous,
1173    );
1174
1175    let regions = (0..array_layer_count)
1176        .map(|rel_array_layer| {
1177            let mut texture_base = dst_base.clone();
1178            texture_base.array_layer += rel_array_layer;
1179            let mut buffer_layout = source.layout;
1180            buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
1181            hal::BufferTextureCopy {
1182                buffer_layout,
1183                texture_base,
1184                size: hal_copy_size,
1185            }
1186        })
1187        .collect::<Vec<_>>();
1188
1189    unsafe {
1190        state.raw_encoder.transition_textures(&dst_barrier);
1191        state.raw_encoder.transition_buffers(src_barrier.as_slice());
1192        state
1193            .raw_encoder
1194            .copy_buffer_to_texture(src_raw, dst_raw, &regions);
1195    }
1196
1197    Ok(())
1198}
1199
1200pub(super) fn copy_texture_to_buffer(
1201    state: &mut EncodingState,
1202    source: &TexelCopyTextureInfo,
1203    destination: &wgt::TexelCopyBufferInfo<Arc<Buffer>>,
1204    copy_size: &Extent3d,
1205) -> Result<(), CommandEncoderError> {
1206    let src_texture = &source.texture;
1207    let dst_buffer = &destination.buffer;
1208
1209    src_texture.same_device(state.device)?;
1210    dst_buffer.same_device(state.device)?;
1211
1212    let (hal_copy_size, array_layer_count) =
1213        validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
1214
1215    let (src_range, src_base) = extract_texture_selector(source, copy_size, src_texture)?;
1216
1217    let src_raw = src_texture.try_raw(state.snatch_guard)?;
1218    src_texture
1219        .check_usage(TextureUsages::COPY_SRC)
1220        .map_err(TransferError::MissingTextureUsage)?;
1221
1222    if source.mip_level >= src_texture.desc.mip_level_count {
1223        return Err(TransferError::InvalidMipLevel {
1224            requested: source.mip_level,
1225            count: src_texture.desc.mip_level_count,
1226        }
1227        .into());
1228    }
1229
1230    validate_texture_copy_src_format(src_texture.desc.format, source.aspect)?;
1231
1232    validate_texture_buffer_copy(
1233        source,
1234        src_base.aspect,
1235        &src_texture.desc,
1236        &destination.layout,
1237        true, // alignment required for buffer offset
1238    )?;
1239
1240    let (required_buffer_bytes_in_copy, bytes_per_array_layer, is_contiguous) =
1241        validate_linear_texture_data(
1242            &destination.layout,
1243            src_texture.desc.format,
1244            source.aspect,
1245            dst_buffer.size,
1246            CopySide::Destination,
1247            copy_size,
1248        )?;
1249
1250    if src_texture.desc.format.is_depth_stencil_format() {
1251        state
1252            .device
1253            .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
1254            .map_err(TransferError::from)?;
1255    }
1256
1257    let dst_raw = dst_buffer.try_raw(state.snatch_guard)?;
1258    dst_buffer
1259        .check_usage(BufferUsages::COPY_DST)
1260        .map_err(TransferError::MissingBufferUsage)?;
1261
1262    // This must happen after parameter validation (so that errors are reported
1263    // as required by the spec), but before any side effects.
1264    if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1265        log::trace!("Ignoring copy_texture_to_buffer of size 0");
1266        return Ok(());
1267    }
1268
1269    // Handle texture init *before* dealing with barrier transitions so we
1270    // have an easier time inserting "immediate-inits" that may be required
1271    // by prior discards in rare cases.
1272    handle_src_texture_init(state, source, copy_size, src_texture)?;
1273
1274    let src_pending =
1275        state
1276            .tracker
1277            .textures
1278            .set_single(src_texture, src_range, wgt::TextureUses::COPY_SRC);
1279    let src_barrier = src_pending
1280        .map(|pending| pending.into_hal(src_raw))
1281        .collect::<Vec<_>>();
1282
1283    let dst_pending = state
1284        .tracker
1285        .buffers
1286        .set_single(dst_buffer, wgt::BufferUses::COPY_DST);
1287
1288    let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer, state.snatch_guard));
1289
1290    handle_buffer_init(
1291        state,
1292        destination,
1293        CopySide::Destination,
1294        required_buffer_bytes_in_copy,
1295        is_contiguous,
1296    );
1297
1298    let regions = (0..array_layer_count)
1299        .map(|rel_array_layer| {
1300            let mut texture_base = src_base.clone();
1301            texture_base.array_layer += rel_array_layer;
1302            let mut buffer_layout = destination.layout;
1303            buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
1304            hal::BufferTextureCopy {
1305                buffer_layout,
1306                texture_base,
1307                size: hal_copy_size,
1308            }
1309        })
1310        .collect::<Vec<_>>();
1311    unsafe {
1312        state.raw_encoder.transition_buffers(dst_barrier.as_slice());
1313        state.raw_encoder.transition_textures(&src_barrier);
1314        state.raw_encoder.copy_texture_to_buffer(
1315            src_raw,
1316            wgt::TextureUses::COPY_SRC,
1317            dst_raw,
1318            &regions,
1319        );
1320    }
1321
1322    Ok(())
1323}
1324
1325pub(super) fn copy_texture_to_texture(
1326    state: &mut EncodingState,
1327    source: &TexelCopyTextureInfo,
1328    destination: &TexelCopyTextureInfo,
1329    copy_size: &Extent3d,
1330) -> Result<(), CommandEncoderError> {
1331    let src_texture = &source.texture;
1332    let dst_texture = &destination.texture;
1333
1334    src_texture.same_device(state.device)?;
1335    dst_texture.same_device(state.device)?;
1336
1337    // src and dst texture format must be copy-compatible
1338    // https://gpuweb.github.io/gpuweb/#copy-compatible
1339    if src_texture.desc.format.remove_srgb_suffix() != dst_texture.desc.format.remove_srgb_suffix()
1340    {
1341        return Err(TransferError::TextureFormatsNotCopyCompatible {
1342            src_format: src_texture.desc.format,
1343            dst_format: dst_texture.desc.format,
1344        }
1345        .into());
1346    }
1347
1348    let (src_copy_size, array_layer_count) =
1349        validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
1350    let (dst_copy_size, _) = validate_texture_copy_range(
1351        destination,
1352        &dst_texture.desc,
1353        CopySide::Destination,
1354        copy_size,
1355    )?;
1356
1357    if Arc::as_ptr(src_texture) == Arc::as_ptr(dst_texture) {
1358        validate_copy_within_same_texture(
1359            source,
1360            destination,
1361            src_texture.desc.format,
1362            array_layer_count,
1363        )?;
1364    }
1365
1366    let (src_range, src_tex_base) = extract_texture_selector(source, copy_size, src_texture)?;
1367    let (dst_range, dst_tex_base) = extract_texture_selector(destination, copy_size, dst_texture)?;
1368    let src_texture_aspects = hal::FormatAspects::from(src_texture.desc.format);
1369    let dst_texture_aspects = hal::FormatAspects::from(dst_texture.desc.format);
1370    if src_tex_base.aspect != src_texture_aspects {
1371        return Err(TransferError::CopySrcMissingAspects.into());
1372    }
1373    if dst_tex_base.aspect != dst_texture_aspects {
1374        return Err(TransferError::CopyDstMissingAspects.into());
1375    }
1376
1377    if src_texture.desc.sample_count != dst_texture.desc.sample_count {
1378        return Err(TransferError::SampleCountNotEqual {
1379            src_sample_count: src_texture.desc.sample_count,
1380            dst_sample_count: dst_texture.desc.sample_count,
1381        }
1382        .into());
1383    }
1384
1385    let src_raw = src_texture.try_raw(state.snatch_guard)?;
1386    src_texture
1387        .check_usage(TextureUsages::COPY_SRC)
1388        .map_err(TransferError::MissingTextureUsage)?;
1389    let dst_raw = dst_texture.try_raw(state.snatch_guard)?;
1390    dst_texture
1391        .check_usage(TextureUsages::COPY_DST)
1392        .map_err(TransferError::MissingTextureUsage)?;
1393
1394    // This must happen after parameter validation (so that errors are reported
1395    // as required by the spec), but before any side effects.
1396    if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1397        log::trace!("Ignoring copy_texture_to_texture of size 0");
1398        return Ok(());
1399    }
1400
1401    // Handle texture init *before* dealing with barrier transitions so we
1402    // have an easier time inserting "immediate-inits" that may be required
1403    // by prior discards in rare cases.
1404    handle_src_texture_init(state, source, copy_size, src_texture)?;
1405    handle_dst_texture_init(state, destination, copy_size, dst_texture)?;
1406
1407    let src_pending =
1408        state
1409            .tracker
1410            .textures
1411            .set_single(src_texture, src_range, wgt::TextureUses::COPY_SRC);
1412
1413    //TODO: try to avoid this the collection. It's needed because both
1414    // `src_pending` and `dst_pending` try to hold `trackers.textures` mutably.
1415    let mut barriers: ArrayVec<_, 2> = src_pending
1416        .map(|pending| pending.into_hal(src_raw))
1417        .collect();
1418
1419    let dst_pending =
1420        state
1421            .tracker
1422            .textures
1423            .set_single(dst_texture, dst_range, wgt::TextureUses::COPY_DST);
1424    barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_raw)));
1425
1426    let hal_copy_size = hal::CopyExtent {
1427        width: src_copy_size.width.min(dst_copy_size.width),
1428        height: src_copy_size.height.min(dst_copy_size.height),
1429        depth: src_copy_size.depth.min(dst_copy_size.depth),
1430    };
1431
1432    let regions = (0..array_layer_count).map(|rel_array_layer| {
1433        let mut src_base = src_tex_base.clone();
1434        let mut dst_base = dst_tex_base.clone();
1435        src_base.array_layer += rel_array_layer;
1436        dst_base.array_layer += rel_array_layer;
1437        hal::TextureCopy {
1438            src_base,
1439            dst_base,
1440            size: hal_copy_size,
1441        }
1442    });
1443
1444    let regions = if dst_tex_base.aspect == hal::FormatAspects::DEPTH_STENCIL {
1445        regions
1446            .flat_map(|region| {
1447                let (mut depth, mut stencil) = (region.clone(), region);
1448                depth.src_base.aspect = hal::FormatAspects::DEPTH;
1449                depth.dst_base.aspect = hal::FormatAspects::DEPTH;
1450                stencil.src_base.aspect = hal::FormatAspects::STENCIL;
1451                stencil.dst_base.aspect = hal::FormatAspects::STENCIL;
1452                [depth, stencil]
1453            })
1454            .collect::<Vec<_>>()
1455    } else {
1456        regions.collect::<Vec<_>>()
1457    };
1458    unsafe {
1459        state.raw_encoder.transition_textures(&barriers);
1460        state.raw_encoder.copy_texture_to_texture(
1461            src_raw,
1462            wgt::TextureUses::COPY_SRC,
1463            dst_raw,
1464            &regions,
1465        );
1466    }
1467
1468    Ok(())
1469}