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