wgpu_core/command/
transfer.rs

1#[cfg(feature = "trace")]
2use crate::device::trace::Command as TraceCommand;
3use crate::{
4    api_log,
5    command::{clear_texture, CommandEncoderError},
6    conv,
7    device::{Device, DeviceError, MissingDownlevelFlags},
8    global::Global,
9    id::{BufferId, CommandEncoderId, TextureId},
10    init_tracker::{
11        has_copy_partial_init_tracker_coverage, MemoryInitKind, TextureInitRange,
12        TextureInitTrackerAction,
13    },
14    resource::{
15        DestroyedResourceError, InvalidResourceError, MissingBufferUsageError,
16        MissingTextureUsageError, ParentDevice, Texture, TextureErrorDimension,
17    },
18    snatch::SnatchGuard,
19    track::TextureSelector,
20};
21
22use arrayvec::ArrayVec;
23use thiserror::Error;
24use wgt::{BufferAddress, BufferUsages, Extent3d, TextureUsages};
25
26use std::sync::Arc;
27
28use super::{ClearError, CommandBufferMutable};
29
30pub type TexelCopyBufferInfo = wgt::TexelCopyBufferInfo<BufferId>;
31pub type TexelCopyTextureInfo = wgt::TexelCopyTextureInfo<TextureId>;
32pub type CopyExternalImageDestInfo = wgt::CopyExternalImageDestInfo<TextureId>;
33
34#[deprecated(
35    since = "24.0.0",
36    note = "This has been renamed to `TexelCopyBufferInfo`, and will be removed in 25.0.0."
37)]
38pub type ImageCopyBuffer = wgt::TexelCopyBufferInfo<BufferId>;
39
40#[deprecated(
41    since = "24.0.0",
42    note = "This has been renamed to `TexelCopyTextureInfo`, and will be removed in 25.0.0."
43)]
44pub type ImageCopyTexture = wgt::TexelCopyTextureInfo<TextureId>;
45
46#[deprecated(
47    since = "24.0.0",
48    note = "This has been renamed to `TexelCopyTextureSourceInfo`, and will be removed in 25.0.0."
49)]
50pub type ImageCopyTextureTagged = wgt::CopyExternalImageDestInfo<TextureId>;
51
52#[derive(Clone, Copy, Debug)]
53pub enum CopySide {
54    Source,
55    Destination,
56}
57
58/// Error encountered while attempting a data transfer.
59#[derive(Clone, Debug, Error)]
60#[non_exhaustive]
61pub enum TransferError {
62    #[error("Source and destination cannot be the same buffer")]
63    SameSourceDestinationBuffer,
64    #[error(transparent)]
65    MissingBufferUsage(#[from] MissingBufferUsageError),
66    #[error(transparent)]
67    MissingTextureUsage(#[from] MissingTextureUsageError),
68    #[error("Copy of {start_offset}..{end_offset} would end up overrunning the bounds of the {side:?} buffer of size {buffer_size}")]
69    BufferOverrun {
70        start_offset: BufferAddress,
71        end_offset: BufferAddress,
72        buffer_size: BufferAddress,
73        side: CopySide,
74    },
75    #[error("Copy of {dimension:?} {start_offset}..{end_offset} would end up overrunning the bounds of the {side:?} texture of {dimension:?} size {texture_size}")]
76    TextureOverrun {
77        start_offset: u32,
78        end_offset: u32,
79        texture_size: u32,
80        dimension: TextureErrorDimension,
81        side: CopySide,
82    },
83    #[error("Unable to select texture aspect {aspect:?} from format {format:?}")]
84    InvalidTextureAspect {
85        format: wgt::TextureFormat,
86        aspect: wgt::TextureAspect,
87    },
88    #[error("Unable to select texture mip level {level} out of {total}")]
89    InvalidTextureMipLevel { level: u32, total: u32 },
90    #[error("Texture dimension must be 2D when copying from an external texture")]
91    InvalidDimensionExternal,
92    #[error("Buffer offset {0} is not aligned to block size or `COPY_BUFFER_ALIGNMENT`")]
93    UnalignedBufferOffset(BufferAddress),
94    #[error("Copy size {0} does not respect `COPY_BUFFER_ALIGNMENT`")]
95    UnalignedCopySize(BufferAddress),
96    #[error("Copy width is not a multiple of block width")]
97    UnalignedCopyWidth,
98    #[error("Copy height is not a multiple of block height")]
99    UnalignedCopyHeight,
100    #[error("Copy origin's x component is not a multiple of block width")]
101    UnalignedCopyOriginX,
102    #[error("Copy origin's y component is not a multiple of block height")]
103    UnalignedCopyOriginY,
104    #[error("Bytes per row does not respect `COPY_BYTES_PER_ROW_ALIGNMENT`")]
105    UnalignedBytesPerRow,
106    #[error("Number of bytes per row needs to be specified since more than one row is copied")]
107    UnspecifiedBytesPerRow,
108    #[error("Number of rows per image needs to be specified since more than one image is copied")]
109    UnspecifiedRowsPerImage,
110    #[error("Number of bytes per row is less than the number of bytes in a complete row")]
111    InvalidBytesPerRow,
112    #[error("Number of rows per image is invalid")]
113    InvalidRowsPerImage,
114    #[error("Copy source aspects must refer to all aspects of the source texture format")]
115    CopySrcMissingAspects,
116    #[error(
117        "Copy destination aspects must refer to all aspects of the destination texture format"
118    )]
119    CopyDstMissingAspects,
120    #[error("Copy aspect must refer to a single aspect of texture format")]
121    CopyAspectNotOne,
122    #[error("Copying from textures with format {format:?} and aspect {aspect:?} is forbidden")]
123    CopyFromForbiddenTextureFormat {
124        format: wgt::TextureFormat,
125        aspect: wgt::TextureAspect,
126    },
127    #[error("Copying to textures with format {format:?} and aspect {aspect:?} is forbidden")]
128    CopyToForbiddenTextureFormat {
129        format: wgt::TextureFormat,
130        aspect: wgt::TextureAspect,
131    },
132    #[error(
133        "Copying to textures with format {0:?} is forbidden when copying from external texture"
134    )]
135    ExternalCopyToForbiddenTextureFormat(wgt::TextureFormat),
136    #[error(
137        "Source format ({src_format:?}) and destination format ({dst_format:?}) are not copy-compatible (they may only differ in srgb-ness)"
138    )]
139    TextureFormatsNotCopyCompatible {
140        src_format: wgt::TextureFormat,
141        dst_format: wgt::TextureFormat,
142    },
143    #[error(transparent)]
144    MemoryInitFailure(#[from] ClearError),
145    #[error("Cannot encode this copy because of a missing downelevel flag")]
146    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
147    #[error("Source texture sample count must be 1, got {sample_count}")]
148    InvalidSampleCount { sample_count: u32 },
149    #[error("Requested mip level {requested} does no exist (count: {count})")]
150    InvalidMipLevel { requested: u32, count: u32 },
151}
152
153/// Error encountered while attempting to do a copy on a command encoder.
154#[derive(Clone, Debug, Error)]
155#[non_exhaustive]
156pub enum CopyError {
157    #[error(transparent)]
158    Encoder(#[from] CommandEncoderError),
159    #[error("Copy error")]
160    Transfer(#[from] TransferError),
161    #[error(transparent)]
162    DestroyedResource(#[from] DestroyedResourceError),
163    #[error(transparent)]
164    InvalidResource(#[from] InvalidResourceError),
165}
166
167impl From<DeviceError> for CopyError {
168    fn from(err: DeviceError) -> Self {
169        CopyError::Encoder(CommandEncoderError::Device(err))
170    }
171}
172
173pub(crate) fn extract_texture_selector<T>(
174    copy_texture: &wgt::TexelCopyTextureInfo<T>,
175    copy_size: &Extent3d,
176    texture: &Texture,
177) -> Result<(TextureSelector, hal::TextureCopyBase), TransferError> {
178    let format = texture.desc.format;
179    let copy_aspect = hal::FormatAspects::new(format, copy_texture.aspect);
180    if copy_aspect.is_empty() {
181        return Err(TransferError::InvalidTextureAspect {
182            format,
183            aspect: copy_texture.aspect,
184        });
185    }
186
187    let (layers, origin_z) = match texture.desc.dimension {
188        wgt::TextureDimension::D1 => (0..1, 0),
189        wgt::TextureDimension::D2 => (
190            copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers,
191            0,
192        ),
193        wgt::TextureDimension::D3 => (0..1, copy_texture.origin.z),
194    };
195    let base = hal::TextureCopyBase {
196        origin: wgt::Origin3d {
197            x: copy_texture.origin.x,
198            y: copy_texture.origin.y,
199            z: origin_z,
200        },
201        // this value will be incremented per copied layer
202        array_layer: layers.start,
203        mip_level: copy_texture.mip_level,
204        aspect: copy_aspect,
205    };
206    let selector = TextureSelector {
207        mips: copy_texture.mip_level..copy_texture.mip_level + 1,
208        layers,
209    };
210
211    Ok((selector, base))
212}
213
214/// WebGPU's [validating linear texture data][vltd] algorithm.
215///
216/// Copied with some modifications from WebGPU standard.
217///
218/// If successful, returns a pair `(bytes, stride)`, where:
219/// - `bytes` is the number of buffer bytes required for this copy, and
220/// - `stride` number of bytes between array layers.
221///
222/// [vltd]: https://gpuweb.github.io/gpuweb/#abstract-opdef-validating-linear-texture-data
223pub(crate) fn validate_linear_texture_data(
224    layout: &wgt::TexelCopyBufferLayout,
225    format: wgt::TextureFormat,
226    aspect: wgt::TextureAspect,
227    buffer_size: BufferAddress,
228    buffer_side: CopySide,
229    copy_size: &Extent3d,
230    need_copy_aligned_rows: bool,
231) -> Result<(BufferAddress, BufferAddress), TransferError> {
232    // Convert all inputs to BufferAddress (u64) to avoid some of the overflow issues
233    // Note: u64 is not always enough to prevent overflow, especially when multiplying
234    // something with a potentially large depth value, so it is preferable to validate
235    // the copy size before calling this function (for example via `validate_texture_copy_range`).
236    let copy_width = copy_size.width as BufferAddress;
237    let copy_height = copy_size.height as BufferAddress;
238    let depth_or_array_layers = copy_size.depth_or_array_layers as BufferAddress;
239
240    let offset = layout.offset;
241
242    let block_size = format.block_copy_size(Some(aspect)).unwrap() as BufferAddress;
243    let (block_width, block_height) = format.block_dimensions();
244    let block_width = block_width as BufferAddress;
245    let block_height = block_height as BufferAddress;
246
247    if copy_width % block_width != 0 {
248        return Err(TransferError::UnalignedCopyWidth);
249    }
250    if copy_height % block_height != 0 {
251        return Err(TransferError::UnalignedCopyHeight);
252    }
253
254    let width_in_blocks = copy_width / block_width;
255    let height_in_blocks = copy_height / block_height;
256
257    let bytes_in_last_row = width_in_blocks * block_size;
258
259    let bytes_per_row = if let Some(bytes_per_row) = layout.bytes_per_row {
260        let bytes_per_row = bytes_per_row as BufferAddress;
261        if bytes_per_row < bytes_in_last_row {
262            return Err(TransferError::InvalidBytesPerRow);
263        }
264        bytes_per_row
265    } else {
266        if depth_or_array_layers > 1 || height_in_blocks > 1 {
267            return Err(TransferError::UnspecifiedBytesPerRow);
268        }
269        0
270    };
271    let rows_per_image = if let Some(rows_per_image) = layout.rows_per_image {
272        let rows_per_image = rows_per_image as BufferAddress;
273        if rows_per_image < height_in_blocks {
274            return Err(TransferError::InvalidRowsPerImage);
275        }
276        rows_per_image
277    } else {
278        if depth_or_array_layers > 1 {
279            return Err(TransferError::UnspecifiedRowsPerImage);
280        }
281        0
282    };
283
284    if need_copy_aligned_rows {
285        let bytes_per_row_alignment = wgt::COPY_BYTES_PER_ROW_ALIGNMENT as BufferAddress;
286
287        let mut offset_alignment = block_size;
288        if format.is_depth_stencil_format() {
289            offset_alignment = 4
290        }
291        if offset % offset_alignment != 0 {
292            return Err(TransferError::UnalignedBufferOffset(offset));
293        }
294
295        if bytes_per_row % bytes_per_row_alignment != 0 {
296            return Err(TransferError::UnalignedBytesPerRow);
297        }
298    }
299
300    let bytes_per_image = bytes_per_row * rows_per_image;
301
302    let required_bytes_in_copy = if depth_or_array_layers == 0 {
303        0
304    } else {
305        let mut required_bytes_in_copy = bytes_per_image * (depth_or_array_layers - 1);
306        if height_in_blocks > 0 {
307            required_bytes_in_copy += bytes_per_row * (height_in_blocks - 1) + bytes_in_last_row;
308        }
309        required_bytes_in_copy
310    };
311
312    if offset + required_bytes_in_copy > buffer_size {
313        return Err(TransferError::BufferOverrun {
314            start_offset: offset,
315            end_offset: offset + required_bytes_in_copy,
316            buffer_size,
317            side: buffer_side,
318        });
319    }
320
321    Ok((required_bytes_in_copy, bytes_per_image))
322}
323
324/// WebGPU's [validating texture copy range][vtcr] algorithm.
325///
326/// Copied with minor modifications from WebGPU standard.
327///
328/// Returns the HAL copy extent and the layer count.
329///
330/// [vtcr]: https://gpuweb.github.io/gpuweb/#validating-texture-copy-range
331pub(crate) fn validate_texture_copy_range<T>(
332    texture_copy_view: &wgt::TexelCopyTextureInfo<T>,
333    desc: &wgt::TextureDescriptor<(), Vec<wgt::TextureFormat>>,
334    texture_side: CopySide,
335    copy_size: &Extent3d,
336) -> Result<(hal::CopyExtent, u32), TransferError> {
337    let (block_width, block_height) = desc.format.block_dimensions();
338
339    let extent_virtual = desc.mip_level_size(texture_copy_view.mip_level).ok_or(
340        TransferError::InvalidTextureMipLevel {
341            level: texture_copy_view.mip_level,
342            total: desc.mip_level_count,
343        },
344    )?;
345    // physical size can be larger than the virtual
346    let extent = extent_virtual.physical_size(desc.format);
347
348    /// Return `Ok` if a run `size` texels long starting at `start_offset` falls
349    /// entirely within `texture_size`. Otherwise, return an appropriate a`Err`.
350    fn check_dimension(
351        dimension: TextureErrorDimension,
352        side: CopySide,
353        start_offset: u32,
354        size: u32,
355        texture_size: u32,
356    ) -> Result<(), TransferError> {
357        // Avoid underflow in the subtraction by checking start_offset against
358        // texture_size first.
359        if start_offset <= texture_size && size <= texture_size - start_offset {
360            Ok(())
361        } else {
362            Err(TransferError::TextureOverrun {
363                start_offset,
364                end_offset: start_offset.wrapping_add(size),
365                texture_size,
366                dimension,
367                side,
368            })
369        }
370    }
371
372    check_dimension(
373        TextureErrorDimension::X,
374        texture_side,
375        texture_copy_view.origin.x,
376        copy_size.width,
377        extent.width,
378    )?;
379    check_dimension(
380        TextureErrorDimension::Y,
381        texture_side,
382        texture_copy_view.origin.y,
383        copy_size.height,
384        extent.height,
385    )?;
386    check_dimension(
387        TextureErrorDimension::Z,
388        texture_side,
389        texture_copy_view.origin.z,
390        copy_size.depth_or_array_layers,
391        extent.depth_or_array_layers,
392    )?;
393
394    if texture_copy_view.origin.x % block_width != 0 {
395        return Err(TransferError::UnalignedCopyOriginX);
396    }
397    if texture_copy_view.origin.y % block_height != 0 {
398        return Err(TransferError::UnalignedCopyOriginY);
399    }
400    if copy_size.width % block_width != 0 {
401        return Err(TransferError::UnalignedCopyWidth);
402    }
403    if copy_size.height % block_height != 0 {
404        return Err(TransferError::UnalignedCopyHeight);
405    }
406
407    let (depth, array_layer_count) = match desc.dimension {
408        wgt::TextureDimension::D1 => (1, 1),
409        wgt::TextureDimension::D2 => (1, copy_size.depth_or_array_layers),
410        wgt::TextureDimension::D3 => (copy_size.depth_or_array_layers, 1),
411    };
412
413    let copy_extent = hal::CopyExtent {
414        width: copy_size.width,
415        height: copy_size.height,
416        depth,
417    };
418    Ok((copy_extent, array_layer_count))
419}
420
421fn handle_texture_init(
422    init_kind: MemoryInitKind,
423    cmd_buf_data: &mut CommandBufferMutable,
424    device: &Device,
425    copy_texture: &TexelCopyTextureInfo,
426    copy_size: &Extent3d,
427    texture: &Arc<Texture>,
428    snatch_guard: &SnatchGuard<'_>,
429) -> Result<(), ClearError> {
430    let init_action = TextureInitTrackerAction {
431        texture: texture.clone(),
432        range: TextureInitRange {
433            mip_range: copy_texture.mip_level..copy_texture.mip_level + 1,
434            layer_range: copy_texture.origin.z
435                ..(copy_texture.origin.z + copy_size.depth_or_array_layers),
436        },
437        kind: init_kind,
438    };
439
440    // Register the init action.
441    let immediate_inits = cmd_buf_data
442        .texture_memory_actions
443        .register_init_action(&{ init_action });
444
445    // In rare cases we may need to insert an init operation immediately onto the command buffer.
446    if !immediate_inits.is_empty() {
447        let cmd_buf_raw = cmd_buf_data.encoder.open()?;
448        for init in immediate_inits {
449            clear_texture(
450                &init.texture,
451                TextureInitRange {
452                    mip_range: init.mip_level..(init.mip_level + 1),
453                    layer_range: init.layer..(init.layer + 1),
454                },
455                cmd_buf_raw,
456                &mut cmd_buf_data.trackers.textures,
457                &device.alignments,
458                device.zero_buffer.as_ref(),
459                snatch_guard,
460            )?;
461        }
462    }
463
464    Ok(())
465}
466
467/// Prepare a transfer's source texture.
468///
469/// Ensure the source texture of a transfer is in the right initialization
470/// state, and record the state for after the transfer operation.
471fn handle_src_texture_init(
472    cmd_buf_data: &mut CommandBufferMutable,
473    device: &Device,
474    source: &TexelCopyTextureInfo,
475    copy_size: &Extent3d,
476    texture: &Arc<Texture>,
477    snatch_guard: &SnatchGuard<'_>,
478) -> Result<(), TransferError> {
479    handle_texture_init(
480        MemoryInitKind::NeedsInitializedMemory,
481        cmd_buf_data,
482        device,
483        source,
484        copy_size,
485        texture,
486        snatch_guard,
487    )?;
488    Ok(())
489}
490
491/// Prepare a transfer's destination texture.
492///
493/// Ensure the destination texture of a transfer is in the right initialization
494/// state, and record the state for after the transfer operation.
495fn handle_dst_texture_init(
496    cmd_buf_data: &mut CommandBufferMutable,
497    device: &Device,
498    destination: &TexelCopyTextureInfo,
499    copy_size: &Extent3d,
500    texture: &Arc<Texture>,
501    snatch_guard: &SnatchGuard<'_>,
502) -> Result<(), TransferError> {
503    // Attention: If we don't write full texture subresources, we need to a full
504    // clear first since we don't track subrects. This means that in rare cases
505    // even a *destination* texture of a transfer may need an immediate texture
506    // init.
507    let dst_init_kind = if has_copy_partial_init_tracker_coverage(
508        copy_size,
509        destination.mip_level,
510        &texture.desc,
511    ) {
512        MemoryInitKind::NeedsInitializedMemory
513    } else {
514        MemoryInitKind::ImplicitlyInitialized
515    };
516
517    handle_texture_init(
518        dst_init_kind,
519        cmd_buf_data,
520        device,
521        destination,
522        copy_size,
523        texture,
524        snatch_guard,
525    )?;
526    Ok(())
527}
528
529impl Global {
530    pub fn command_encoder_copy_buffer_to_buffer(
531        &self,
532        command_encoder_id: CommandEncoderId,
533        source: BufferId,
534        source_offset: BufferAddress,
535        destination: BufferId,
536        destination_offset: BufferAddress,
537        size: BufferAddress,
538    ) -> Result<(), CopyError> {
539        profiling::scope!("CommandEncoder::copy_buffer_to_buffer");
540        api_log!(
541            "CommandEncoder::copy_buffer_to_buffer {source:?} -> {destination:?} {size:?}bytes"
542        );
543
544        if source == destination {
545            return Err(TransferError::SameSourceDestinationBuffer.into());
546        }
547        let hub = &self.hub;
548
549        let cmd_buf = hub
550            .command_buffers
551            .get(command_encoder_id.into_command_buffer_id());
552        let mut cmd_buf_data = cmd_buf.data.lock();
553        let mut cmd_buf_data_guard = cmd_buf_data.record()?;
554        let cmd_buf_data = &mut *cmd_buf_data_guard;
555
556        let device = &cmd_buf.device;
557        device.check_is_valid()?;
558
559        #[cfg(feature = "trace")]
560        if let Some(ref mut list) = cmd_buf_data.commands {
561            list.push(TraceCommand::CopyBufferToBuffer {
562                src: source,
563                src_offset: source_offset,
564                dst: destination,
565                dst_offset: destination_offset,
566                size,
567            });
568        }
569
570        let snatch_guard = device.snatchable_lock.read();
571
572        let src_buffer = hub.buffers.get(source).get()?;
573
574        src_buffer.same_device_as(cmd_buf.as_ref())?;
575
576        let src_pending = cmd_buf_data
577            .trackers
578            .buffers
579            .set_single(&src_buffer, hal::BufferUses::COPY_SRC);
580
581        let src_raw = src_buffer.try_raw(&snatch_guard)?;
582        src_buffer
583            .check_usage(BufferUsages::COPY_SRC)
584            .map_err(TransferError::MissingBufferUsage)?;
585        // expecting only a single barrier
586        let src_barrier = src_pending.map(|pending| pending.into_hal(&src_buffer, &snatch_guard));
587
588        let dst_buffer = hub.buffers.get(destination).get()?;
589
590        dst_buffer.same_device_as(cmd_buf.as_ref())?;
591
592        let dst_pending = cmd_buf_data
593            .trackers
594            .buffers
595            .set_single(&dst_buffer, hal::BufferUses::COPY_DST);
596
597        let dst_raw = dst_buffer.try_raw(&snatch_guard)?;
598        dst_buffer
599            .check_usage(BufferUsages::COPY_DST)
600            .map_err(TransferError::MissingBufferUsage)?;
601        let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, &snatch_guard));
602
603        if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
604            return Err(TransferError::UnalignedCopySize(size).into());
605        }
606        if source_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
607            return Err(TransferError::UnalignedBufferOffset(source_offset).into());
608        }
609        if destination_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
610            return Err(TransferError::UnalignedBufferOffset(destination_offset).into());
611        }
612        if !device
613            .downlevel
614            .flags
615            .contains(wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER)
616            && (src_buffer.usage.contains(BufferUsages::INDEX)
617                || dst_buffer.usage.contains(BufferUsages::INDEX))
618        {
619            let forbidden_usages = BufferUsages::VERTEX
620                | BufferUsages::UNIFORM
621                | BufferUsages::INDIRECT
622                | BufferUsages::STORAGE;
623            if src_buffer.usage.intersects(forbidden_usages)
624                || dst_buffer.usage.intersects(forbidden_usages)
625            {
626                return Err(TransferError::MissingDownlevelFlags(MissingDownlevelFlags(
627                    wgt::DownlevelFlags::UNRESTRICTED_INDEX_BUFFER,
628                ))
629                .into());
630            }
631        }
632
633        let source_end_offset = source_offset + size;
634        let destination_end_offset = destination_offset + size;
635        if source_end_offset > src_buffer.size {
636            return Err(TransferError::BufferOverrun {
637                start_offset: source_offset,
638                end_offset: source_end_offset,
639                buffer_size: src_buffer.size,
640                side: CopySide::Source,
641            }
642            .into());
643        }
644        if destination_end_offset > dst_buffer.size {
645            return Err(TransferError::BufferOverrun {
646                start_offset: destination_offset,
647                end_offset: destination_end_offset,
648                buffer_size: dst_buffer.size,
649                side: CopySide::Destination,
650            }
651            .into());
652        }
653
654        if size == 0 {
655            log::trace!("Ignoring copy_buffer_to_buffer of size 0");
656            cmd_buf_data_guard.mark_successful();
657            return Ok(());
658        }
659
660        // Make sure source is initialized memory and mark dest as initialized.
661        cmd_buf_data.buffer_memory_init_actions.extend(
662            dst_buffer.initialization_status.read().create_action(
663                &dst_buffer,
664                destination_offset..(destination_offset + size),
665                MemoryInitKind::ImplicitlyInitialized,
666            ),
667        );
668        cmd_buf_data.buffer_memory_init_actions.extend(
669            src_buffer.initialization_status.read().create_action(
670                &src_buffer,
671                source_offset..(source_offset + size),
672                MemoryInitKind::NeedsInitializedMemory,
673            ),
674        );
675
676        let region = hal::BufferCopy {
677            src_offset: source_offset,
678            dst_offset: destination_offset,
679            size: wgt::BufferSize::new(size).unwrap(),
680        };
681        let cmd_buf_raw = cmd_buf_data.encoder.open()?;
682        let barriers = src_barrier
683            .into_iter()
684            .chain(dst_barrier)
685            .collect::<Vec<_>>();
686        unsafe {
687            cmd_buf_raw.transition_buffers(&barriers);
688            cmd_buf_raw.copy_buffer_to_buffer(src_raw, dst_raw, &[region]);
689        }
690
691        cmd_buf_data_guard.mark_successful();
692        Ok(())
693    }
694
695    pub fn command_encoder_copy_buffer_to_texture(
696        &self,
697        command_encoder_id: CommandEncoderId,
698        source: &TexelCopyBufferInfo,
699        destination: &TexelCopyTextureInfo,
700        copy_size: &Extent3d,
701    ) -> Result<(), CopyError> {
702        profiling::scope!("CommandEncoder::copy_buffer_to_texture");
703        api_log!(
704            "CommandEncoder::copy_buffer_to_texture {:?} -> {:?} {copy_size:?}",
705            source.buffer,
706            destination.texture
707        );
708
709        let hub = &self.hub;
710
711        let cmd_buf = hub
712            .command_buffers
713            .get(command_encoder_id.into_command_buffer_id());
714        let mut cmd_buf_data = cmd_buf.data.lock();
715        let mut cmd_buf_data_guard = cmd_buf_data.record()?;
716        let cmd_buf_data = &mut *cmd_buf_data_guard;
717
718        let device = &cmd_buf.device;
719        device.check_is_valid()?;
720
721        #[cfg(feature = "trace")]
722        if let Some(ref mut list) = cmd_buf_data.commands {
723            list.push(TraceCommand::CopyBufferToTexture {
724                src: *source,
725                dst: *destination,
726                size: *copy_size,
727            });
728        }
729
730        if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
731            log::trace!("Ignoring copy_buffer_to_texture of size 0");
732            cmd_buf_data_guard.mark_successful();
733            return Ok(());
734        }
735
736        let dst_texture = hub.textures.get(destination.texture).get()?;
737
738        dst_texture.same_device_as(cmd_buf.as_ref())?;
739
740        let (hal_copy_size, array_layer_count) = validate_texture_copy_range(
741            destination,
742            &dst_texture.desc,
743            CopySide::Destination,
744            copy_size,
745        )?;
746
747        let (dst_range, dst_base) = extract_texture_selector(destination, copy_size, &dst_texture)?;
748
749        let snatch_guard = device.snatchable_lock.read();
750
751        // Handle texture init *before* dealing with barrier transitions so we
752        // have an easier time inserting "immediate-inits" that may be required
753        // by prior discards in rare cases.
754        handle_dst_texture_init(
755            cmd_buf_data,
756            device,
757            destination,
758            copy_size,
759            &dst_texture,
760            &snatch_guard,
761        )?;
762
763        let src_buffer = hub.buffers.get(source.buffer).get()?;
764
765        src_buffer.same_device_as(cmd_buf.as_ref())?;
766
767        let src_pending = cmd_buf_data
768            .trackers
769            .buffers
770            .set_single(&src_buffer, hal::BufferUses::COPY_SRC);
771
772        let src_raw = src_buffer.try_raw(&snatch_guard)?;
773        src_buffer
774            .check_usage(BufferUsages::COPY_SRC)
775            .map_err(TransferError::MissingBufferUsage)?;
776        let src_barrier = src_pending.map(|pending| pending.into_hal(&src_buffer, &snatch_guard));
777
778        let dst_pending = cmd_buf_data.trackers.textures.set_single(
779            &dst_texture,
780            dst_range,
781            hal::TextureUses::COPY_DST,
782        );
783        let dst_raw = dst_texture.try_raw(&snatch_guard)?;
784        dst_texture
785            .check_usage(TextureUsages::COPY_DST)
786            .map_err(TransferError::MissingTextureUsage)?;
787        let dst_barrier = dst_pending
788            .map(|pending| pending.into_hal(dst_raw))
789            .collect::<Vec<_>>();
790
791        if !dst_base.aspect.is_one() {
792            return Err(TransferError::CopyAspectNotOne.into());
793        }
794
795        if !conv::is_valid_copy_dst_texture_format(dst_texture.desc.format, destination.aspect) {
796            return Err(TransferError::CopyToForbiddenTextureFormat {
797                format: dst_texture.desc.format,
798                aspect: destination.aspect,
799            }
800            .into());
801        }
802
803        let (required_buffer_bytes_in_copy, bytes_per_array_layer) = validate_linear_texture_data(
804            &source.layout,
805            dst_texture.desc.format,
806            destination.aspect,
807            src_buffer.size,
808            CopySide::Source,
809            copy_size,
810            true,
811        )?;
812
813        if dst_texture.desc.format.is_depth_stencil_format() {
814            device
815                .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
816                .map_err(TransferError::from)?;
817        }
818
819        cmd_buf_data.buffer_memory_init_actions.extend(
820            src_buffer.initialization_status.read().create_action(
821                &src_buffer,
822                source.layout.offset..(source.layout.offset + required_buffer_bytes_in_copy),
823                MemoryInitKind::NeedsInitializedMemory,
824            ),
825        );
826
827        let regions = (0..array_layer_count)
828            .map(|rel_array_layer| {
829                let mut texture_base = dst_base.clone();
830                texture_base.array_layer += rel_array_layer;
831                let mut buffer_layout = source.layout;
832                buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
833                hal::BufferTextureCopy {
834                    buffer_layout,
835                    texture_base,
836                    size: hal_copy_size,
837                }
838            })
839            .collect::<Vec<_>>();
840
841        let cmd_buf_raw = cmd_buf_data.encoder.open()?;
842        unsafe {
843            cmd_buf_raw.transition_textures(&dst_barrier);
844            cmd_buf_raw.transition_buffers(src_barrier.as_slice());
845            cmd_buf_raw.copy_buffer_to_texture(src_raw, dst_raw, &regions);
846        }
847
848        cmd_buf_data_guard.mark_successful();
849        Ok(())
850    }
851
852    pub fn command_encoder_copy_texture_to_buffer(
853        &self,
854        command_encoder_id: CommandEncoderId,
855        source: &TexelCopyTextureInfo,
856        destination: &TexelCopyBufferInfo,
857        copy_size: &Extent3d,
858    ) -> Result<(), CopyError> {
859        profiling::scope!("CommandEncoder::copy_texture_to_buffer");
860        api_log!(
861            "CommandEncoder::copy_texture_to_buffer {:?} -> {:?} {copy_size:?}",
862            source.texture,
863            destination.buffer
864        );
865
866        let hub = &self.hub;
867
868        let cmd_buf = hub
869            .command_buffers
870            .get(command_encoder_id.into_command_buffer_id());
871        let mut cmd_buf_data = cmd_buf.data.lock();
872        let mut cmd_buf_data_guard = cmd_buf_data.record()?;
873        let cmd_buf_data = &mut *cmd_buf_data_guard;
874
875        let device = &cmd_buf.device;
876        device.check_is_valid()?;
877
878        #[cfg(feature = "trace")]
879        if let Some(list) = cmd_buf_data.commands.as_mut() {
880            list.push(TraceCommand::CopyTextureToBuffer {
881                src: *source,
882                dst: *destination,
883                size: *copy_size,
884            });
885        }
886
887        if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
888            log::trace!("Ignoring copy_texture_to_buffer of size 0");
889            cmd_buf_data_guard.mark_successful();
890            return Ok(());
891        }
892
893        let src_texture = hub.textures.get(source.texture).get()?;
894
895        src_texture.same_device_as(cmd_buf.as_ref())?;
896
897        let (hal_copy_size, array_layer_count) =
898            validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
899
900        let (src_range, src_base) = extract_texture_selector(source, copy_size, &src_texture)?;
901
902        let snatch_guard = device.snatchable_lock.read();
903
904        // Handle texture init *before* dealing with barrier transitions so we
905        // have an easier time inserting "immediate-inits" that may be required
906        // by prior discards in rare cases.
907        handle_src_texture_init(
908            cmd_buf_data,
909            device,
910            source,
911            copy_size,
912            &src_texture,
913            &snatch_guard,
914        )?;
915
916        let src_pending = cmd_buf_data.trackers.textures.set_single(
917            &src_texture,
918            src_range,
919            hal::TextureUses::COPY_SRC,
920        );
921        let src_raw = src_texture.try_raw(&snatch_guard)?;
922        src_texture
923            .check_usage(TextureUsages::COPY_SRC)
924            .map_err(TransferError::MissingTextureUsage)?;
925        if src_texture.desc.sample_count != 1 {
926            return Err(TransferError::InvalidSampleCount {
927                sample_count: src_texture.desc.sample_count,
928            }
929            .into());
930        }
931        if source.mip_level >= src_texture.desc.mip_level_count {
932            return Err(TransferError::InvalidMipLevel {
933                requested: source.mip_level,
934                count: src_texture.desc.mip_level_count,
935            }
936            .into());
937        }
938        let src_barrier = src_pending
939            .map(|pending| pending.into_hal(src_raw))
940            .collect::<Vec<_>>();
941
942        let dst_buffer = hub.buffers.get(destination.buffer).get()?;
943
944        dst_buffer.same_device_as(cmd_buf.as_ref())?;
945
946        let dst_pending = cmd_buf_data
947            .trackers
948            .buffers
949            .set_single(&dst_buffer, hal::BufferUses::COPY_DST);
950
951        let dst_raw = dst_buffer.try_raw(&snatch_guard)?;
952        dst_buffer
953            .check_usage(BufferUsages::COPY_DST)
954            .map_err(TransferError::MissingBufferUsage)?;
955        let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, &snatch_guard));
956
957        if !src_base.aspect.is_one() {
958            return Err(TransferError::CopyAspectNotOne.into());
959        }
960
961        if !conv::is_valid_copy_src_texture_format(src_texture.desc.format, source.aspect) {
962            return Err(TransferError::CopyFromForbiddenTextureFormat {
963                format: src_texture.desc.format,
964                aspect: source.aspect,
965            }
966            .into());
967        }
968
969        let (required_buffer_bytes_in_copy, bytes_per_array_layer) = validate_linear_texture_data(
970            &destination.layout,
971            src_texture.desc.format,
972            source.aspect,
973            dst_buffer.size,
974            CopySide::Destination,
975            copy_size,
976            true,
977        )?;
978
979        if src_texture.desc.format.is_depth_stencil_format() {
980            device
981                .require_downlevel_flags(wgt::DownlevelFlags::DEPTH_TEXTURE_AND_BUFFER_COPIES)
982                .map_err(TransferError::from)?;
983        }
984
985        cmd_buf_data.buffer_memory_init_actions.extend(
986            dst_buffer.initialization_status.read().create_action(
987                &dst_buffer,
988                destination.layout.offset
989                    ..(destination.layout.offset + required_buffer_bytes_in_copy),
990                MemoryInitKind::ImplicitlyInitialized,
991            ),
992        );
993
994        let regions = (0..array_layer_count)
995            .map(|rel_array_layer| {
996                let mut texture_base = src_base.clone();
997                texture_base.array_layer += rel_array_layer;
998                let mut buffer_layout = destination.layout;
999                buffer_layout.offset += rel_array_layer as u64 * bytes_per_array_layer;
1000                hal::BufferTextureCopy {
1001                    buffer_layout,
1002                    texture_base,
1003                    size: hal_copy_size,
1004                }
1005            })
1006            .collect::<Vec<_>>();
1007        let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1008        unsafe {
1009            cmd_buf_raw.transition_buffers(dst_barrier.as_slice());
1010            cmd_buf_raw.transition_textures(&src_barrier);
1011            cmd_buf_raw.copy_texture_to_buffer(
1012                src_raw,
1013                hal::TextureUses::COPY_SRC,
1014                dst_raw,
1015                &regions,
1016            );
1017        }
1018
1019        cmd_buf_data_guard.mark_successful();
1020        Ok(())
1021    }
1022
1023    pub fn command_encoder_copy_texture_to_texture(
1024        &self,
1025        command_encoder_id: CommandEncoderId,
1026        source: &TexelCopyTextureInfo,
1027        destination: &TexelCopyTextureInfo,
1028        copy_size: &Extent3d,
1029    ) -> Result<(), CopyError> {
1030        profiling::scope!("CommandEncoder::copy_texture_to_texture");
1031        api_log!(
1032            "CommandEncoder::copy_texture_to_texture {:?} -> {:?} {copy_size:?}",
1033            source.texture,
1034            destination.texture
1035        );
1036
1037        let hub = &self.hub;
1038
1039        let cmd_buf = hub
1040            .command_buffers
1041            .get(command_encoder_id.into_command_buffer_id());
1042        let mut cmd_buf_data = cmd_buf.data.lock();
1043        let mut cmd_buf_data_guard = cmd_buf_data.record()?;
1044        let cmd_buf_data = &mut *cmd_buf_data_guard;
1045
1046        let device = &cmd_buf.device;
1047        device.check_is_valid()?;
1048
1049        let snatch_guard = device.snatchable_lock.read();
1050
1051        #[cfg(feature = "trace")]
1052        if let Some(ref mut list) = cmd_buf_data.commands {
1053            list.push(TraceCommand::CopyTextureToTexture {
1054                src: *source,
1055                dst: *destination,
1056                size: *copy_size,
1057            });
1058        }
1059
1060        if copy_size.width == 0 || copy_size.height == 0 || copy_size.depth_or_array_layers == 0 {
1061            log::trace!("Ignoring copy_texture_to_texture of size 0");
1062            cmd_buf_data_guard.mark_successful();
1063            return Ok(());
1064        }
1065
1066        let src_texture = hub.textures.get(source.texture).get()?;
1067        let dst_texture = hub.textures.get(destination.texture).get()?;
1068
1069        src_texture.same_device_as(cmd_buf.as_ref())?;
1070        dst_texture.same_device_as(cmd_buf.as_ref())?;
1071
1072        // src and dst texture format must be copy-compatible
1073        // https://gpuweb.github.io/gpuweb/#copy-compatible
1074        if src_texture.desc.format.remove_srgb_suffix()
1075            != dst_texture.desc.format.remove_srgb_suffix()
1076        {
1077            return Err(TransferError::TextureFormatsNotCopyCompatible {
1078                src_format: src_texture.desc.format,
1079                dst_format: dst_texture.desc.format,
1080            }
1081            .into());
1082        }
1083
1084        let (src_copy_size, array_layer_count) =
1085            validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?;
1086        let (dst_copy_size, _) = validate_texture_copy_range(
1087            destination,
1088            &dst_texture.desc,
1089            CopySide::Destination,
1090            copy_size,
1091        )?;
1092
1093        let (src_range, src_tex_base) = extract_texture_selector(source, copy_size, &src_texture)?;
1094        let (dst_range, dst_tex_base) =
1095            extract_texture_selector(destination, copy_size, &dst_texture)?;
1096        let src_texture_aspects = hal::FormatAspects::from(src_texture.desc.format);
1097        let dst_texture_aspects = hal::FormatAspects::from(dst_texture.desc.format);
1098        if src_tex_base.aspect != src_texture_aspects {
1099            return Err(TransferError::CopySrcMissingAspects.into());
1100        }
1101        if dst_tex_base.aspect != dst_texture_aspects {
1102            return Err(TransferError::CopyDstMissingAspects.into());
1103        }
1104
1105        // Handle texture init *before* dealing with barrier transitions so we
1106        // have an easier time inserting "immediate-inits" that may be required
1107        // by prior discards in rare cases.
1108        handle_src_texture_init(
1109            cmd_buf_data,
1110            device,
1111            source,
1112            copy_size,
1113            &src_texture,
1114            &snatch_guard,
1115        )?;
1116        handle_dst_texture_init(
1117            cmd_buf_data,
1118            device,
1119            destination,
1120            copy_size,
1121            &dst_texture,
1122            &snatch_guard,
1123        )?;
1124
1125        let src_pending = cmd_buf_data.trackers.textures.set_single(
1126            &src_texture,
1127            src_range,
1128            hal::TextureUses::COPY_SRC,
1129        );
1130        let src_raw = src_texture.try_raw(&snatch_guard)?;
1131        src_texture
1132            .check_usage(TextureUsages::COPY_SRC)
1133            .map_err(TransferError::MissingTextureUsage)?;
1134
1135        //TODO: try to avoid this the collection. It's needed because both
1136        // `src_pending` and `dst_pending` try to hold `trackers.textures` mutably.
1137        let mut barriers: ArrayVec<_, 2> = src_pending
1138            .map(|pending| pending.into_hal(src_raw))
1139            .collect();
1140
1141        let dst_pending = cmd_buf_data.trackers.textures.set_single(
1142            &dst_texture,
1143            dst_range,
1144            hal::TextureUses::COPY_DST,
1145        );
1146        let dst_raw = dst_texture.try_raw(&snatch_guard)?;
1147        dst_texture
1148            .check_usage(TextureUsages::COPY_DST)
1149            .map_err(TransferError::MissingTextureUsage)?;
1150
1151        barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_raw)));
1152
1153        let hal_copy_size = hal::CopyExtent {
1154            width: src_copy_size.width.min(dst_copy_size.width),
1155            height: src_copy_size.height.min(dst_copy_size.height),
1156            depth: src_copy_size.depth.min(dst_copy_size.depth),
1157        };
1158        let regions = (0..array_layer_count)
1159            .map(|rel_array_layer| {
1160                let mut src_base = src_tex_base.clone();
1161                let mut dst_base = dst_tex_base.clone();
1162                src_base.array_layer += rel_array_layer;
1163                dst_base.array_layer += rel_array_layer;
1164                hal::TextureCopy {
1165                    src_base,
1166                    dst_base,
1167                    size: hal_copy_size,
1168                }
1169            })
1170            .collect::<Vec<_>>();
1171        let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1172        unsafe {
1173            cmd_buf_raw.transition_textures(&barriers);
1174            cmd_buf_raw.copy_texture_to_texture(
1175                src_raw,
1176                hal::TextureUses::COPY_SRC,
1177                dst_raw,
1178                &regions,
1179            );
1180        }
1181
1182        cmd_buf_data_guard.mark_successful();
1183        Ok(())
1184    }
1185}