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