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#[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::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#[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 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
227pub(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 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
337pub(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 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 fn check_dimension(
368 dimension: TextureErrorDimension,
369 side: CopySide,
370 start_offset: u32,
371 size: u32,
372 texture_size: u32,
373 ) -> Result<(), TransferError> {
374 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 let immediate_inits = cmd_buf
458 .texture_memory_actions
459 .register_init_action(&{ init_action }, texture_guard);
460
461 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
482fn 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
508fn 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 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 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 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_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_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); 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 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_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 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}