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