aboutsummaryrefslogtreecommitdiff
path: root/src/command_buffer/validity/copy_image_buffer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/command_buffer/validity/copy_image_buffer.rs')
-rw-r--r--src/command_buffer/validity/copy_image_buffer.rs266
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)
+ }
+}