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