diff options
Diffstat (limited to 'src/command_buffer/validity/copy_image_buffer.rs')
-rw-r--r-- | src/command_buffer/validity/copy_image_buffer.rs | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/src/command_buffer/validity/copy_image_buffer.rs b/src/command_buffer/validity/copy_image_buffer.rs new file mode 100644 index 0000000..77224b1 --- /dev/null +++ b/src/command_buffer/validity/copy_image_buffer.rs @@ -0,0 +1,266 @@ +// Copyright (c) 2016 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use crate::buffer::TypedBufferAccess; +use crate::device::Device; +use crate::device::DeviceOwned; +use crate::format::Format; +use crate::format::IncompatiblePixelsType; +use crate::format::Pixel; +use crate::image::ImageAccess; +use crate::image::SampleCount; +use crate::DeviceSize; +use crate::VulkanObject; +use std::error; +use std::fmt; + +/// Type of operation to check. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum CheckCopyBufferImageTy { + BufferToImage, + ImageToBuffer, +} + +/// Checks whether a copy buffer-image command is valid. Can check both buffer-to-image copies and +/// image-to-buffer copies. +/// +/// # Panic +/// +/// - Panics if the buffer and image were not created with `device`. +/// +pub fn check_copy_buffer_image<B, I, Px>( + device: &Device, + buffer: &B, + image: &I, + ty: CheckCopyBufferImageTy, + image_offset: [u32; 3], + image_size: [u32; 3], + image_first_layer: u32, + image_num_layers: u32, + image_mipmap: u32, +) -> Result<(), CheckCopyBufferImageError> +where + I: ?Sized + ImageAccess, + B: ?Sized + TypedBufferAccess<Content = [Px]>, + Px: Pixel, // TODO: use a trait on the image itself instead +{ + let buffer_inner = buffer.inner(); + let image_inner = image.inner(); + + assert_eq!( + buffer_inner.buffer.device().internal_object(), + device.internal_object() + ); + assert_eq!( + image_inner.image.device().internal_object(), + device.internal_object() + ); + + match ty { + CheckCopyBufferImageTy::BufferToImage => { + if !buffer_inner.buffer.usage().transfer_source { + return Err(CheckCopyBufferImageError::SourceMissingTransferUsage); + } + if !image_inner.image.usage().transfer_destination { + return Err(CheckCopyBufferImageError::DestinationMissingTransferUsage); + } + } + CheckCopyBufferImageTy::ImageToBuffer => { + if !image_inner.image.usage().transfer_source { + return Err(CheckCopyBufferImageError::SourceMissingTransferUsage); + } + if !buffer_inner.buffer.usage().transfer_destination { + return Err(CheckCopyBufferImageError::DestinationMissingTransferUsage); + } + } + } + + if image.samples() != SampleCount::Sample1 { + return Err(CheckCopyBufferImageError::UnexpectedMultisampled); + } + + let image_dimensions = match image.dimensions().mipmap_dimensions(image_mipmap) { + Some(d) => d, + None => return Err(CheckCopyBufferImageError::ImageCoordinatesOutOfRange), + }; + + if image_first_layer + image_num_layers > image_dimensions.array_layers() { + return Err(CheckCopyBufferImageError::ImageCoordinatesOutOfRange); + } + + if image_offset[0] + image_size[0] > image_dimensions.width() { + return Err(CheckCopyBufferImageError::ImageCoordinatesOutOfRange); + } + + if image_offset[1] + image_size[1] > image_dimensions.height() { + return Err(CheckCopyBufferImageError::ImageCoordinatesOutOfRange); + } + + if image_offset[2] + image_size[2] > image_dimensions.depth() { + return Err(CheckCopyBufferImageError::ImageCoordinatesOutOfRange); + } + + Px::ensure_accepts(image.format())?; + + { + let required_len = + required_len_for_format::<Px>(image.format(), image_size, image_num_layers); + if required_len > buffer.len() { + return Err(CheckCopyBufferImageError::BufferTooSmall { + required_len, + actual_len: buffer.len(), + }); + } + } + + // TODO: check memory overlap? + + Ok(()) +} + +/// Computes the minimum required len in elements for buffer with image data in specified +/// format of specified size. +fn required_len_for_format<Px>( + format: Format, + image_size: [u32; 3], + image_num_layers: u32, +) -> DeviceSize +where + Px: Pixel, +{ + let (block_width, block_height) = format.block_dimensions(); + let num_blocks = (image_size[0] + block_width - 1) / block_width + * ((image_size[1] + block_height - 1) / block_height) + * image_size[2] + * image_num_layers; + let required_len = num_blocks as DeviceSize * Px::rate(format) as DeviceSize; + + return required_len; +} + +#[cfg(test)] +mod tests { + use crate::command_buffer::validity::copy_image_buffer::required_len_for_format; + use crate::format::Format; + + #[test] + fn test_required_len_for_format() { + // issue #1292 + assert_eq!( + required_len_for_format::<u8>(Format::BC1_RGBUnormBlock, [2048, 2048, 1], 1), + 2097152 + ); + // other test cases + assert_eq!( + required_len_for_format::<u8>(Format::R8G8B8A8Unorm, [2048, 2048, 1], 1), + 16777216 + ); + assert_eq!( + required_len_for_format::<u8>(Format::R4G4UnormPack8, [512, 512, 1], 1), + 262144 + ); + assert_eq!( + required_len_for_format::<u8>(Format::R8G8B8Uscaled, [512, 512, 1], 1), + 786432 + ); + assert_eq!( + required_len_for_format::<u8>(Format::R32G32Uint, [512, 512, 1], 1), + 2097152 + ); + assert_eq!( + required_len_for_format::<u32>(Format::R32G32Uint, [512, 512, 1], 1), + 524288 + ); + assert_eq!( + required_len_for_format::<[u32; 2]>(Format::R32G32Uint, [512, 512, 1], 1), + 262144 + ); + assert_eq!( + required_len_for_format::<u8>(Format::ASTC_8x8UnormBlock, [512, 512, 1], 1), + 65536 + ); + assert_eq!( + required_len_for_format::<u8>(Format::ASTC_12x12SrgbBlock, [512, 512, 1], 1), + 29584 + ); + } +} + +/// Error that can happen from `check_copy_buffer_image`. +#[derive(Debug, Copy, Clone)] +pub enum CheckCopyBufferImageError { + /// The source buffer or image is missing the transfer source usage. + SourceMissingTransferUsage, + /// The destination buffer or image is missing the transfer destination usage. + DestinationMissingTransferUsage, + /// The source and destination are overlapping. + OverlappingRanges, + /// The image must not be multisampled. + UnexpectedMultisampled, + /// The image coordinates are out of range. + ImageCoordinatesOutOfRange, + /// The type of pixels in the buffer isn't compatible with the image format. + WrongPixelType(IncompatiblePixelsType), + /// The buffer is too small for the copy operation. + BufferTooSmall { + /// Required number of elements in the buffer. + required_len: DeviceSize, + /// Actual number of elements in the buffer. + actual_len: DeviceSize, + }, +} + +impl error::Error for CheckCopyBufferImageError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + match *self { + CheckCopyBufferImageError::WrongPixelType(ref err) => Some(err), + _ => None, + } + } +} + +impl fmt::Display for CheckCopyBufferImageError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + fmt, + "{}", + match *self { + CheckCopyBufferImageError::SourceMissingTransferUsage => { + "the source buffer is missing the transfer source usage" + } + CheckCopyBufferImageError::DestinationMissingTransferUsage => { + "the destination buffer is missing the transfer destination usage" + } + CheckCopyBufferImageError::OverlappingRanges => { + "the source and destination are overlapping" + } + CheckCopyBufferImageError::UnexpectedMultisampled => { + "the image must not be multisampled" + } + CheckCopyBufferImageError::ImageCoordinatesOutOfRange => { + "the image coordinates are out of range" + } + CheckCopyBufferImageError::WrongPixelType(_) => { + "the type of pixels in the buffer isn't compatible with the image format" + } + CheckCopyBufferImageError::BufferTooSmall { .. } => { + "the buffer is too small for the copy operation" + } + } + ) + } +} + +impl From<IncompatiblePixelsType> for CheckCopyBufferImageError { + #[inline] + fn from(err: IncompatiblePixelsType) -> CheckCopyBufferImageError { + CheckCopyBufferImageError::WrongPixelType(err) + } +} |