wgpu_core/command/
transfer.rs

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