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