mirror of
https://github.com/gfx-rs/wgpu.git
synced 2024-11-22 06:44:14 +00:00
Error handling for data transfer API
This commit is contained in:
parent
d3e204127c
commit
98e4f73a10
@ -108,18 +108,20 @@ impl GlobalExt for wgc::hub::Global<IdentityPassThroughFactory> {
|
||||
dst,
|
||||
dst_offset,
|
||||
size,
|
||||
} => self.command_encoder_copy_buffer_to_buffer::<B>(
|
||||
encoder, src, src_offset, dst, dst_offset, size,
|
||||
),
|
||||
trace::Command::CopyBufferToTexture { src, dst, size } => {
|
||||
self.command_encoder_copy_buffer_to_texture::<B>(encoder, &src, &dst, &size)
|
||||
}
|
||||
trace::Command::CopyTextureToBuffer { src, dst, size } => {
|
||||
self.command_encoder_copy_texture_to_buffer::<B>(encoder, &src, &dst, &size)
|
||||
}
|
||||
trace::Command::CopyTextureToTexture { src, dst, size } => {
|
||||
self.command_encoder_copy_texture_to_texture::<B>(encoder, &src, &dst, &size)
|
||||
}
|
||||
} => self
|
||||
.command_encoder_copy_buffer_to_buffer::<B>(
|
||||
encoder, src, src_offset, dst, dst_offset, size,
|
||||
)
|
||||
.unwrap(),
|
||||
trace::Command::CopyBufferToTexture { src, dst, size } => self
|
||||
.command_encoder_copy_buffer_to_texture::<B>(encoder, &src, &dst, &size)
|
||||
.unwrap(),
|
||||
trace::Command::CopyTextureToBuffer { src, dst, size } => self
|
||||
.command_encoder_copy_texture_to_buffer::<B>(encoder, &src, &dst, &size)
|
||||
.unwrap(),
|
||||
trace::Command::CopyTextureToTexture { src, dst, size } => self
|
||||
.command_encoder_copy_texture_to_texture::<B>(encoder, &src, &dst, &size)
|
||||
.unwrap(),
|
||||
trace::Command::RunComputePass { base } => {
|
||||
self.command_encoder_run_compute_pass_impl::<B>(encoder, base.as_ref());
|
||||
}
|
||||
|
@ -16,9 +16,10 @@ use crate::{
|
||||
use hal::command::CommandBuffer as _;
|
||||
use wgt::{BufferAddress, BufferUsage, Extent3d, Origin3d, TextureDataLayout, TextureUsage};
|
||||
|
||||
use std::convert::TryInto as _;
|
||||
use std::iter;
|
||||
|
||||
type Result = std::result::Result<(), TransferError>;
|
||||
|
||||
pub(crate) const BITS_PER_BYTE: u32 = 8;
|
||||
|
||||
#[repr(C)]
|
||||
@ -40,6 +41,40 @@ pub struct TextureCopyView {
|
||||
pub origin: Origin3d,
|
||||
}
|
||||
|
||||
/// Error encountered while attempting a data transfer.
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum TransferError {
|
||||
/// The source buffer/texture is missing the `COPY_SRC` usage flag.
|
||||
MissingCopySrcUsageFlag,
|
||||
/// The destination buffer/texture is missing the `COPY_DST` usage flag.
|
||||
MissingCopyDstUsageFlag,
|
||||
/// Copy would end up overruning the bounds of the destination buffer/texture.
|
||||
BufferOverrun,
|
||||
/// Buffer offset is not aligned to block size.
|
||||
UnalignedBufferOffset,
|
||||
/// Copy size is not a multiple of block size.
|
||||
UnalignedCopySize,
|
||||
/// Copy width is not a multiple of block size.
|
||||
UnalignedCopyWidth,
|
||||
/// Copy height is not a multiple of block size.
|
||||
UnalignedCopyHeight,
|
||||
/// Bytes per row is not a multiple of the required alignment.
|
||||
UnalignedBytesPerRow,
|
||||
/// Number of rows per image is not a multiple of the required alignment.
|
||||
UnalignedRowsPerImage,
|
||||
/// Number of bytes per row is less than the number of bytes in a complete row.
|
||||
InvalidBytesPerRow,
|
||||
/// Copy size is invalid.
|
||||
///
|
||||
/// This can happen if the image is 1D and the copy height and depth
|
||||
/// are not both set to 1.
|
||||
InvalidCopySize,
|
||||
/// Number of rows per image is invalid.
|
||||
InvalidRowsPerImage,
|
||||
/// The source and destination layers have different aspects.
|
||||
MismatchedAspects,
|
||||
}
|
||||
|
||||
impl TextureCopyView {
|
||||
//TODO: we currently access each texture twice for a transfer,
|
||||
// once only to get the aspect flags, which is unfortunate.
|
||||
@ -90,7 +125,7 @@ pub(crate) fn validate_linear_texture_data(
|
||||
buffer_size: BufferAddress,
|
||||
bytes_per_texel: BufferAddress,
|
||||
copy_size: &Extent3d,
|
||||
) {
|
||||
) -> Result {
|
||||
// Convert all inputs to BufferAddress (u64) to prevent overflow issues
|
||||
let copy_width = copy_size.width as BufferAddress;
|
||||
let copy_height = copy_size.height as BufferAddress;
|
||||
@ -105,27 +140,15 @@ pub(crate) fn validate_linear_texture_data(
|
||||
let block_height: BufferAddress = 1;
|
||||
let block_size = bytes_per_texel;
|
||||
|
||||
assert_eq!(
|
||||
copy_width % block_width,
|
||||
0,
|
||||
"Copy width {} must be a multiple of texture block width {}",
|
||||
copy_size.width,
|
||||
block_width,
|
||||
);
|
||||
assert_eq!(
|
||||
copy_height % block_height,
|
||||
0,
|
||||
"Copy height {} must be a multiple of texture block height {}",
|
||||
copy_size.height,
|
||||
block_height,
|
||||
);
|
||||
assert_eq!(
|
||||
rows_per_image % block_height,
|
||||
0,
|
||||
"Rows per image {} must be a multiple of image format block height {}",
|
||||
rows_per_image,
|
||||
block_height,
|
||||
);
|
||||
if copy_width % block_width != 0 {
|
||||
return Err(TransferError::UnalignedCopyWidth);
|
||||
}
|
||||
if copy_height % block_height != 0 {
|
||||
return Err(TransferError::UnalignedCopyHeight);
|
||||
}
|
||||
if rows_per_image % block_height != 0 {
|
||||
return Err(TransferError::UnalignedRowsPerImage);
|
||||
}
|
||||
|
||||
let bytes_in_a_complete_row = block_size * copy_width / block_width;
|
||||
let required_bytes_in_copy = if copy_width == 0 || copy_height == 0 || copy_depth == 0 {
|
||||
@ -143,45 +166,22 @@ pub(crate) fn validate_linear_texture_data(
|
||||
bytes_per_image * (copy_depth - 1) + bytes_in_last_slice
|
||||
};
|
||||
|
||||
if rows_per_image != 0 {
|
||||
assert!(
|
||||
rows_per_image >= copy_height,
|
||||
"Rows per image {} must be greater or equal to copy_extent.height {}",
|
||||
rows_per_image,
|
||||
copy_height
|
||||
)
|
||||
if rows_per_image != 0 && rows_per_image < copy_height {
|
||||
return Err(TransferError::InvalidRowsPerImage);
|
||||
}
|
||||
assert!(
|
||||
offset + required_bytes_in_copy <= buffer_size,
|
||||
"Texture copy using buffer indices {}..{} would overrun buffer of size {}",
|
||||
offset,
|
||||
offset + required_bytes_in_copy,
|
||||
buffer_size
|
||||
);
|
||||
assert_eq!(
|
||||
offset % block_size,
|
||||
0,
|
||||
"Buffer offset {} must be a multiple of image format block size {}",
|
||||
offset,
|
||||
block_size,
|
||||
);
|
||||
if copy_height > 1 {
|
||||
assert!(
|
||||
bytes_per_row >= bytes_in_a_complete_row,
|
||||
"Bytes per row {} must be at least the size of {} {}-byte texel blocks ({})",
|
||||
bytes_per_row,
|
||||
copy_width / block_width,
|
||||
block_size,
|
||||
bytes_in_a_complete_row,
|
||||
)
|
||||
if offset + required_bytes_in_copy > buffer_size {
|
||||
return Err(TransferError::BufferOverrun);
|
||||
}
|
||||
if copy_depth > 1 {
|
||||
assert_ne!(
|
||||
rows_per_image, 0,
|
||||
"Rows per image {} must be set to a non zero value when copy depth > 1 ({})",
|
||||
rows_per_image, copy_depth,
|
||||
)
|
||||
if offset % block_size != 0 {
|
||||
return Err(TransferError::UnalignedBufferOffset);
|
||||
}
|
||||
if copy_height > 1 && bytes_per_row < bytes_in_a_complete_row {
|
||||
return Err(TransferError::InvalidBytesPerRow);
|
||||
}
|
||||
if copy_depth > 1 && rows_per_image == 0 {
|
||||
return Err(TransferError::InvalidRowsPerImage);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Function copied with minor modifications from webgpu standard https://gpuweb.github.io/gpuweb/#valid-texture-copy-range
|
||||
@ -189,26 +189,17 @@ pub(crate) fn validate_texture_copy_range(
|
||||
texture_copy_view: &TextureCopyView,
|
||||
texture_dimension: hal::image::Kind,
|
||||
copy_size: &Extent3d,
|
||||
) {
|
||||
) -> Result {
|
||||
// TODO: Once compressed textures are supported, these needs to be fixed
|
||||
let block_width: u32 = 1;
|
||||
let block_height: u32 = 1;
|
||||
|
||||
let mut extent = texture_dimension.level_extent(
|
||||
texture_copy_view
|
||||
.mip_level
|
||||
.try_into()
|
||||
.expect("Mip level must be < 256"),
|
||||
);
|
||||
let mut extent = texture_dimension.level_extent(texture_copy_view.mip_level as u8);
|
||||
match texture_dimension {
|
||||
hal::image::Kind::D1(..) => {
|
||||
assert_eq!(
|
||||
(copy_size.height, copy_size.depth),
|
||||
(1, 1),
|
||||
"Copies with 1D textures must have height and depth of 1. Currently: ({}, {})",
|
||||
copy_size.height,
|
||||
copy_size.depth,
|
||||
);
|
||||
if (copy_size.height, copy_size.depth) != (1, 1) {
|
||||
return Err(TransferError::InvalidCopySize);
|
||||
}
|
||||
}
|
||||
hal::image::Kind::D2(_, _, array_layers, _) => {
|
||||
extent.depth = array_layers as u32;
|
||||
@ -217,44 +208,25 @@ pub(crate) fn validate_texture_copy_range(
|
||||
};
|
||||
|
||||
let x_copy_max = texture_copy_view.origin.x + copy_size.width;
|
||||
assert!(
|
||||
x_copy_max <= extent.width,
|
||||
"Texture copy with X range {}..{} overruns texture width {}",
|
||||
texture_copy_view.origin.x,
|
||||
x_copy_max,
|
||||
extent.width,
|
||||
);
|
||||
if x_copy_max > extent.width {
|
||||
return Err(TransferError::BufferOverrun);
|
||||
}
|
||||
let y_copy_max = texture_copy_view.origin.y + copy_size.height;
|
||||
assert!(
|
||||
y_copy_max <= extent.height,
|
||||
"Texture copy with Y range {}..{} overruns texture height {}",
|
||||
texture_copy_view.origin.y,
|
||||
y_copy_max,
|
||||
extent.height,
|
||||
);
|
||||
if y_copy_max > extent.height {
|
||||
return Err(TransferError::BufferOverrun);
|
||||
}
|
||||
let z_copy_max = texture_copy_view.origin.z + copy_size.depth;
|
||||
assert!(
|
||||
z_copy_max <= extent.depth,
|
||||
"Texture copy with Z range {}..{} overruns texture depth {}",
|
||||
texture_copy_view.origin.z,
|
||||
z_copy_max,
|
||||
extent.depth,
|
||||
);
|
||||
if z_copy_max > extent.depth {
|
||||
return Err(TransferError::BufferOverrun);
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
copy_size.width % block_width,
|
||||
0,
|
||||
"Copy width {} must be a multiple of texture block width {}",
|
||||
copy_size.width,
|
||||
block_width,
|
||||
);
|
||||
assert_eq!(
|
||||
copy_size.height % block_height,
|
||||
0,
|
||||
"Copy height {} must be a multiple of texture block height {}",
|
||||
copy_size.height,
|
||||
block_height,
|
||||
);
|
||||
if copy_size.width % block_width != 0 {
|
||||
return Err(TransferError::UnalignedCopyWidth);
|
||||
}
|
||||
if copy_size.height % block_height != 0 {
|
||||
return Err(TransferError::UnalignedCopyHeight);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
@ -266,7 +238,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
destination: BufferId,
|
||||
destination_offset: BufferAddress,
|
||||
size: BufferAddress,
|
||||
) {
|
||||
) -> Result {
|
||||
span!(_guard, INFO, "CommandEncoder::copy_buffer_to_buffer");
|
||||
|
||||
let hub = B::hub(self);
|
||||
@ -293,71 +265,45 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
if size == 0 {
|
||||
log::trace!("Ignoring copy_buffer_to_buffer of size 0");
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (src_buffer, src_pending) =
|
||||
cmb.trackers
|
||||
.buffers
|
||||
.use_replace(&*buffer_guard, source, (), BufferUse::COPY_SRC);
|
||||
assert!(
|
||||
src_buffer.usage.contains(BufferUsage::COPY_SRC),
|
||||
"Source buffer usage {:?} must contain usage flag COPY_SRC",
|
||||
src_buffer.usage
|
||||
);
|
||||
if !src_buffer.usage.contains(BufferUsage::COPY_SRC) {
|
||||
return Err(TransferError::MissingCopySrcUsageFlag);
|
||||
}
|
||||
barriers.extend(src_pending.map(|pending| pending.into_hal(src_buffer)));
|
||||
|
||||
let (dst_buffer, dst_pending) =
|
||||
cmb.trackers
|
||||
.buffers
|
||||
.use_replace(&*buffer_guard, destination, (), BufferUse::COPY_DST);
|
||||
assert!(
|
||||
dst_buffer.usage.contains(BufferUsage::COPY_DST),
|
||||
"Destination buffer usage {:?} must contain usage flag COPY_DST",
|
||||
dst_buffer.usage
|
||||
);
|
||||
if !dst_buffer.usage.contains(BufferUsage::COPY_DST) {
|
||||
return Err(TransferError::MissingCopyDstUsageFlag);
|
||||
}
|
||||
barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_buffer)));
|
||||
|
||||
assert_eq!(
|
||||
size % wgt::COPY_BUFFER_ALIGNMENT,
|
||||
0,
|
||||
"Buffer copy size {} must be a multiple of {}",
|
||||
size,
|
||||
wgt::COPY_BUFFER_ALIGNMENT,
|
||||
);
|
||||
assert_eq!(
|
||||
source_offset % wgt::COPY_BUFFER_ALIGNMENT,
|
||||
0,
|
||||
"Buffer source offset {} must be a multiple of {}",
|
||||
source_offset,
|
||||
wgt::COPY_BUFFER_ALIGNMENT,
|
||||
);
|
||||
assert_eq!(
|
||||
destination_offset % wgt::COPY_BUFFER_ALIGNMENT,
|
||||
0,
|
||||
"Buffer destination offset {} must be a multiple of {}",
|
||||
destination_offset,
|
||||
wgt::COPY_BUFFER_ALIGNMENT,
|
||||
);
|
||||
if size % wgt::COPY_BUFFER_ALIGNMENT != 0 {
|
||||
return Err(TransferError::UnalignedCopySize);
|
||||
}
|
||||
if source_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
|
||||
return Err(TransferError::UnalignedBufferOffset);
|
||||
}
|
||||
if destination_offset % wgt::COPY_BUFFER_ALIGNMENT != 0 {
|
||||
return Err(TransferError::UnalignedBufferOffset);
|
||||
}
|
||||
|
||||
let source_start_offset = source_offset;
|
||||
let source_end_offset = source_offset + size;
|
||||
let destination_start_offset = destination_offset;
|
||||
let destination_end_offset = destination_offset + size;
|
||||
assert!(
|
||||
source_end_offset <= src_buffer.size,
|
||||
"Buffer to buffer copy with indices {}..{} overruns source buffer of size {}",
|
||||
source_start_offset,
|
||||
source_end_offset,
|
||||
src_buffer.size
|
||||
);
|
||||
assert!(
|
||||
destination_end_offset <= dst_buffer.size,
|
||||
"Buffer to buffer copy with indices {}..{} overruns destination buffer of size {}",
|
||||
destination_start_offset,
|
||||
destination_end_offset,
|
||||
dst_buffer.size
|
||||
);
|
||||
if source_end_offset > src_buffer.size {
|
||||
return Err(TransferError::BufferOverrun);
|
||||
}
|
||||
if destination_end_offset > dst_buffer.size {
|
||||
return Err(TransferError::BufferOverrun);
|
||||
}
|
||||
|
||||
let region = hal::command::BufferCopy {
|
||||
src: source_offset,
|
||||
@ -373,6 +319,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
);
|
||||
cmb_raw.copy_buffer(&src_buffer.raw, &dst_buffer.raw, iter::once(region));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn command_encoder_copy_buffer_to_texture<B: GfxBackend>(
|
||||
@ -381,7 +328,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
source: &BufferCopyView,
|
||||
destination: &TextureCopyView,
|
||||
copy_size: &Extent3d,
|
||||
) {
|
||||
) -> Result {
|
||||
span!(_guard, INFO, "CommandEncoder::copy_buffer_to_texture");
|
||||
|
||||
let hub = B::hub(self);
|
||||
@ -404,7 +351,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
if copy_size.width == 0 || copy_size.height == 0 || copy_size.width == 0 {
|
||||
log::trace!("Ignoring copy_buffer_to_texture of size 0");
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (src_buffer, src_pending) = cmb.trackers.buffers.use_replace(
|
||||
@ -413,7 +360,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
(),
|
||||
BufferUse::COPY_SRC,
|
||||
);
|
||||
assert!(src_buffer.usage.contains(BufferUsage::COPY_SRC));
|
||||
if !src_buffer.usage.contains(BufferUsage::COPY_SRC) {
|
||||
return Err(TransferError::MissingCopySrcUsageFlag);
|
||||
}
|
||||
let src_barriers = src_pending.map(|pending| pending.into_hal(src_buffer));
|
||||
|
||||
let (dst_texture, dst_pending) = cmb.trackers.textures.use_replace(
|
||||
@ -422,28 +371,30 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
dst_range,
|
||||
TextureUse::COPY_DST,
|
||||
);
|
||||
assert!(dst_texture.usage.contains(TextureUsage::COPY_DST));
|
||||
if !dst_texture.usage.contains(TextureUsage::COPY_DST) {
|
||||
return Err(TransferError::MissingCopyDstUsageFlag);
|
||||
}
|
||||
let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_texture));
|
||||
|
||||
let bytes_per_row_alignment = wgt::COPY_BYTES_PER_ROW_ALIGNMENT;
|
||||
let bytes_per_texel = conv::map_texture_format(dst_texture.format, cmb.private_features)
|
||||
.surface_desc()
|
||||
.bits as u32
|
||||
/ BITS_PER_BYTE;
|
||||
assert_eq!(wgt::COPY_BYTES_PER_ROW_ALIGNMENT % bytes_per_texel, 0);
|
||||
assert_eq!(
|
||||
source.layout.bytes_per_row % wgt::COPY_BYTES_PER_ROW_ALIGNMENT,
|
||||
0,
|
||||
"Source bytes per row ({}) must be a multiple of {}",
|
||||
source.layout.bytes_per_row,
|
||||
wgt::COPY_BYTES_PER_ROW_ALIGNMENT
|
||||
);
|
||||
validate_texture_copy_range(destination, dst_texture.kind, copy_size);
|
||||
let src_bytes_per_row = source.layout.bytes_per_row;
|
||||
if bytes_per_row_alignment % bytes_per_texel != 0 {
|
||||
return Err(TransferError::UnalignedBytesPerRow);
|
||||
}
|
||||
if src_bytes_per_row % bytes_per_row_alignment != 0 {
|
||||
return Err(TransferError::UnalignedBytesPerRow);
|
||||
}
|
||||
validate_texture_copy_range(destination, dst_texture.kind, copy_size)?;
|
||||
validate_linear_texture_data(
|
||||
&source.layout,
|
||||
src_buffer.size,
|
||||
bytes_per_texel as BufferAddress,
|
||||
copy_size,
|
||||
);
|
||||
)?;
|
||||
|
||||
let buffer_width = source.layout.bytes_per_row / bytes_per_texel;
|
||||
let region = hal::command::BufferImageCopy {
|
||||
@ -468,6 +419,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
iter::once(region),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn command_encoder_copy_texture_to_buffer<B: GfxBackend>(
|
||||
@ -476,7 +428,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
source: &TextureCopyView,
|
||||
destination: &BufferCopyView,
|
||||
copy_size: &Extent3d,
|
||||
) {
|
||||
) -> Result {
|
||||
span!(_guard, INFO, "CommandEncoder::copy_texture_to_buffer");
|
||||
|
||||
let hub = B::hub(self);
|
||||
@ -499,7 +451,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
if copy_size.width == 0 || copy_size.height == 0 || copy_size.width == 0 {
|
||||
log::trace!("Ignoring copy_texture_to_buffer of size 0");
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (src_texture, src_pending) = cmb.trackers.textures.use_replace(
|
||||
@ -508,11 +460,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
src_range,
|
||||
TextureUse::COPY_SRC,
|
||||
);
|
||||
assert!(
|
||||
src_texture.usage.contains(TextureUsage::COPY_SRC),
|
||||
"Source texture usage ({:?}) must contain usage flag COPY_SRC",
|
||||
src_texture.usage
|
||||
);
|
||||
if !src_texture.usage.contains(TextureUsage::COPY_SRC) {
|
||||
return Err(TransferError::MissingCopySrcUsageFlag);
|
||||
}
|
||||
let src_barriers = src_pending.map(|pending| pending.into_hal(src_texture));
|
||||
|
||||
let (dst_buffer, dst_barriers) = cmb.trackers.buffers.use_replace(
|
||||
@ -521,32 +471,30 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
(),
|
||||
BufferUse::COPY_DST,
|
||||
);
|
||||
assert!(
|
||||
dst_buffer.usage.contains(BufferUsage::COPY_DST),
|
||||
"Destination buffer usage {:?} must contain usage flag COPY_DST",
|
||||
dst_buffer.usage
|
||||
);
|
||||
if !dst_buffer.usage.contains(BufferUsage::COPY_DST) {
|
||||
return Err(TransferError::MissingCopyDstUsageFlag);
|
||||
}
|
||||
let dst_barrier = dst_barriers.map(|pending| pending.into_hal(dst_buffer));
|
||||
|
||||
let bytes_per_row_alignment = wgt::COPY_BYTES_PER_ROW_ALIGNMENT;
|
||||
let bytes_per_texel = conv::map_texture_format(src_texture.format, cmb.private_features)
|
||||
.surface_desc()
|
||||
.bits as u32
|
||||
/ BITS_PER_BYTE;
|
||||
assert_eq!(wgt::COPY_BYTES_PER_ROW_ALIGNMENT % bytes_per_texel, 0);
|
||||
assert_eq!(
|
||||
destination.layout.bytes_per_row % wgt::COPY_BYTES_PER_ROW_ALIGNMENT,
|
||||
0,
|
||||
"Destination bytes per row ({}) must be a multiple of {}",
|
||||
destination.layout.bytes_per_row,
|
||||
wgt::COPY_BYTES_PER_ROW_ALIGNMENT
|
||||
);
|
||||
validate_texture_copy_range(source, src_texture.kind, copy_size);
|
||||
let dst_bytes_per_row = destination.layout.bytes_per_row;
|
||||
if bytes_per_row_alignment % bytes_per_texel != 0 {
|
||||
return Err(TransferError::UnalignedBytesPerRow);
|
||||
}
|
||||
if dst_bytes_per_row % bytes_per_row_alignment != 0 {
|
||||
return Err(TransferError::UnalignedBytesPerRow);
|
||||
}
|
||||
validate_texture_copy_range(source, src_texture.kind, copy_size)?;
|
||||
validate_linear_texture_data(
|
||||
&destination.layout,
|
||||
dst_buffer.size,
|
||||
bytes_per_texel as BufferAddress,
|
||||
copy_size,
|
||||
);
|
||||
)?;
|
||||
|
||||
let buffer_width = destination.layout.bytes_per_row / bytes_per_texel;
|
||||
let region = hal::command::BufferImageCopy {
|
||||
@ -571,6 +519,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
iter::once(region),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn command_encoder_copy_texture_to_texture<B: GfxBackend>(
|
||||
@ -579,7 +528,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
source: &TextureCopyView,
|
||||
destination: &TextureCopyView,
|
||||
copy_size: &Extent3d,
|
||||
) {
|
||||
) -> Result {
|
||||
span!(_guard, INFO, "CommandEncoder::copy_texture_to_texture");
|
||||
|
||||
let hub = B::hub(self);
|
||||
@ -594,7 +543,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let mut barriers = Vec::new();
|
||||
let (src_layers, src_range, src_offset) = source.to_hal(&*texture_guard);
|
||||
let (dst_layers, dst_range, dst_offset) = destination.to_hal(&*texture_guard);
|
||||
assert_eq!(src_layers.aspects, dst_layers.aspects);
|
||||
if src_layers.aspects != dst_layers.aspects {
|
||||
return Err(TransferError::MismatchedAspects);
|
||||
}
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
match cmb.commands {
|
||||
@ -608,7 +559,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
if copy_size.width == 0 || copy_size.height == 0 || copy_size.width == 0 {
|
||||
log::trace!("Ignoring copy_texture_to_texture of size 0");
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (src_texture, src_pending) = cmb.trackers.textures.use_replace(
|
||||
@ -617,11 +568,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
src_range,
|
||||
TextureUse::COPY_SRC,
|
||||
);
|
||||
assert!(
|
||||
src_texture.usage.contains(TextureUsage::COPY_SRC),
|
||||
"Source texture usage {:?} must contain usage flag COPY_SRC",
|
||||
src_texture.usage
|
||||
);
|
||||
if !src_texture.usage.contains(TextureUsage::COPY_SRC) {
|
||||
return Err(TransferError::MissingCopySrcUsageFlag);
|
||||
}
|
||||
barriers.extend(src_pending.map(|pending| pending.into_hal(src_texture)));
|
||||
|
||||
let (dst_texture, dst_pending) = cmb.trackers.textures.use_replace(
|
||||
@ -630,16 +579,13 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
dst_range,
|
||||
TextureUse::COPY_DST,
|
||||
);
|
||||
assert!(
|
||||
dst_texture.usage.contains(TextureUsage::COPY_DST),
|
||||
"Destination texture usage {:?} must contain usage flag COPY_DST",
|
||||
dst_texture.usage
|
||||
);
|
||||
if !dst_texture.usage.contains(TextureUsage::COPY_DST) {
|
||||
return Err(TransferError::MissingCopyDstUsageFlag);
|
||||
}
|
||||
barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_texture)));
|
||||
|
||||
assert_eq!(src_texture.dimension, dst_texture.dimension);
|
||||
validate_texture_copy_range(source, src_texture.kind, copy_size);
|
||||
validate_texture_copy_range(destination, dst_texture.kind, copy_size);
|
||||
validate_texture_copy_range(source, src_texture.kind, copy_size)?;
|
||||
validate_texture_copy_range(destination, dst_texture.kind, copy_size)?;
|
||||
|
||||
let region = hal::command::ImageCopy {
|
||||
src_subresource: src_layers,
|
||||
@ -663,5 +609,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
iter::once(region),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -280,7 +280,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
data.len() as wgt::BufferAddress,
|
||||
bytes_per_texel as wgt::BufferAddress,
|
||||
size,
|
||||
);
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let bytes_per_row_alignment = get_lowest_common_denom(
|
||||
device.hal_limits.optimal_buffer_copy_pitch_alignment as u32,
|
||||
@ -329,7 +330,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
"Write texture usage {:?} must contain flag COPY_DST",
|
||||
dst.usage
|
||||
);
|
||||
crate::command::validate_texture_copy_range(destination, dst.kind, size);
|
||||
crate::command::validate_texture_copy_range(destination, dst.kind, size).unwrap();
|
||||
|
||||
dst.life_guard.use_at(device.active_submission_index + 1);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user