blob: 77224b14359beaab8f916b65da1bafadd714b7f4 [file] [log] [blame]
// 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)
}
}