use super::ranges::{is_overlapping_ranges, is_overlapping_regions};
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::format::NumericType;
use crate::image::ImageAccess;
use crate::image::ImageDimensions;
use crate::image::SampleCount;
use crate::sampler::Filter;
use crate::VulkanObject;
use std::error;
use std::fmt;
pub fn check_blit_image<S, D>(
device: &Device,
source: &S,
source_top_left: [i32; 3],
source_bottom_right: [i32; 3],
source_base_array_layer: u32,
source_mip_level: u32,
destination: &D,
destination_top_left: [i32; 3],
destination_bottom_right: [i32; 3],
destination_base_array_layer: u32,
destination_mip_level: u32,
layer_count: u32,
filter: Filter,
) -> Result<(), CheckBlitImageError>
where
S: ?Sized + ImageAccess,
D: ?Sized + ImageAccess,
{
let source_inner = source.inner();
let destination_inner = destination.inner();
assert_eq!(
source_inner.image.device().internal_object(),
device.internal_object()
);
assert_eq!(
destination_inner.image.device().internal_object(),
device.internal_object()
);
if !source_inner.image.usage().transfer_source {
return Err(CheckBlitImageError::MissingTransferSourceUsage);
}
if !destination_inner.image.usage().transfer_destination {
return Err(CheckBlitImageError::MissingTransferDestinationUsage);
}
if !source_inner.image.format_features().blit_src {
return Err(CheckBlitImageError::SourceFormatNotSupported);
}
if !destination_inner.image.format_features().blit_dst {
return Err(CheckBlitImageError::DestinationFormatNotSupported);
}
if source.samples() != SampleCount::Sample1 || destination.samples() != SampleCount::Sample1 {
return Err(CheckBlitImageError::UnexpectedMultisampled);
}
if let (Some(source_type), Some(destination_type)) = (
source.format().type_color(),
destination.format().type_color(),
) {
let types_should_be_same = source_type == NumericType::UINT
|| destination_type == NumericType::UINT
|| source_type == NumericType::SINT
|| destination_type == NumericType::SINT;
if types_should_be_same && (source_type != destination_type) {
return Err(CheckBlitImageError::IncompatibleFormatTypes {
source_type,
destination_type,
});
}
} else {
if source.format() != destination.format() {
return Err(CheckBlitImageError::DepthStencilFormatMismatch);
}
if filter != Filter::Nearest {
return Err(CheckBlitImageError::DepthStencilNearestMandatory);
}
}
let source_dimensions = match source.dimensions().mip_level_dimensions(source_mip_level) {
Some(d) => d,
None => return Err(CheckBlitImageError::SourceCoordinatesOutOfRange),
};
let destination_dimensions = match destination
.dimensions()
.mip_level_dimensions(destination_mip_level)
{
Some(d) => d,
None => return Err(CheckBlitImageError::DestinationCoordinatesOutOfRange),
};
if source_base_array_layer + layer_count > source_dimensions.array_layers() {
return Err(CheckBlitImageError::SourceCoordinatesOutOfRange);
}
if destination_base_array_layer + layer_count > destination_dimensions.array_layers() {
return Err(CheckBlitImageError::DestinationCoordinatesOutOfRange);
}
if source_top_left[0] < 0 || source_top_left[0] > source_dimensions.width() as i32 {
return Err(CheckBlitImageError::SourceCoordinatesOutOfRange);
}
if source_top_left[1] < 0 || source_top_left[1] > source_dimensions.height() as i32 {
return Err(CheckBlitImageError::SourceCoordinatesOutOfRange);
}
if source_top_left[2] < 0 || source_top_left[2] > source_dimensions.depth() as i32 {
return Err(CheckBlitImageError::SourceCoordinatesOutOfRange);
}
if source_bottom_right[0] < 0 || source_bottom_right[0] > source_dimensions.width() as i32 {
return Err(CheckBlitImageError::SourceCoordinatesOutOfRange);
}
if source_bottom_right[1] < 0 || source_bottom_right[1] > source_dimensions.height() as i32 {
return Err(CheckBlitImageError::SourceCoordinatesOutOfRange);
}
if source_bottom_right[2] < 0 || source_bottom_right[2] > source_dimensions.depth() as i32 {
return Err(CheckBlitImageError::SourceCoordinatesOutOfRange);
}
if destination_top_left[0] < 0
|| destination_top_left[0] > destination_dimensions.width() as i32
{
return Err(CheckBlitImageError::DestinationCoordinatesOutOfRange);
}
if destination_top_left[1] < 0
|| destination_top_left[1] > destination_dimensions.height() as i32
{
return Err(CheckBlitImageError::DestinationCoordinatesOutOfRange);
}
if destination_top_left[2] < 0
|| destination_top_left[2] > destination_dimensions.depth() as i32
{
return Err(CheckBlitImageError::DestinationCoordinatesOutOfRange);
}
if destination_bottom_right[0] < 0
|| destination_bottom_right[0] > destination_dimensions.width() as i32
{
return Err(CheckBlitImageError::DestinationCoordinatesOutOfRange);
}
if destination_bottom_right[1] < 0
|| destination_bottom_right[1] > destination_dimensions.height() as i32
{
return Err(CheckBlitImageError::DestinationCoordinatesOutOfRange);
}
if destination_bottom_right[2] < 0
|| destination_bottom_right[2] > destination_dimensions.depth() as i32
{
return Err(CheckBlitImageError::DestinationCoordinatesOutOfRange);
}
match source_dimensions {
ImageDimensions::Dim1d { .. } => {
if source_top_left[1] != 0 || source_bottom_right[1] != 1 {
return Err(CheckBlitImageError::IncompatibleRangeForImageType);
}
if source_top_left[2] != 0 || source_bottom_right[2] != 1 {
return Err(CheckBlitImageError::IncompatibleRangeForImageType);
}
}
ImageDimensions::Dim2d { .. } => {
if source_top_left[2] != 0 || source_bottom_right[2] != 1 {
return Err(CheckBlitImageError::IncompatibleRangeForImageType);
}
}
ImageDimensions::Dim3d { .. } => {}
}
match destination_dimensions {
ImageDimensions::Dim1d { .. } => {
if destination_top_left[1] != 0 || destination_bottom_right[1] != 1 {
return Err(CheckBlitImageError::IncompatibleRangeForImageType);
}
if destination_top_left[2] != 0 || destination_bottom_right[2] != 1 {
return Err(CheckBlitImageError::IncompatibleRangeForImageType);
}
}
ImageDimensions::Dim2d { .. } => {
if destination_top_left[2] != 0 || destination_bottom_right[2] != 1 {
return Err(CheckBlitImageError::IncompatibleRangeForImageType);
}
}
ImageDimensions::Dim3d { .. } => {}
}
if source.conflict_key() == destination.conflict_key() {
if source_mip_level == destination_mip_level
&& is_overlapping_ranges(
source_base_array_layer as u64,
layer_count as u64,
destination_base_array_layer as u64,
layer_count as u64,
)
{
let mut source_render_top_left = [0; 3];
let mut source_extent = [0; 3];
let mut destination_render_top_left = [0; 3];
let mut destination_extent = [0; 3];
for i in 0..3 {
if source_top_left[i] < source_bottom_right[i] {
source_render_top_left[i] = source_top_left[i];
source_extent[i] = (source_bottom_right[i] - source_top_left[i]) as u32;
} else {
source_render_top_left[i] = source_bottom_right[i];
source_extent[i] = (source_top_left[i] - source_bottom_right[i]) as u32;
}
if destination_top_left[i] < destination_bottom_right[i] {
destination_render_top_left[i] = destination_top_left[i];
destination_extent[i] =
(destination_bottom_right[i] - destination_top_left[i]) as u32;
} else {
destination_render_top_left[i] = destination_bottom_right[i];
destination_extent[i] =
(destination_top_left[i] - destination_bottom_right[i]) as u32;
}
}
if is_overlapping_regions(
source_render_top_left,
source_extent,
destination_render_top_left,
destination_extent,
source_dimensions,
) {
return Err(CheckBlitImageError::OverlappingRegions);
}
}
}
match filter {
Filter::Nearest => (),
Filter::Linear => {
if !source_inner
.image
.format_features()
.sampled_image_filter_linear
{
return Err(CheckBlitImageError::FilterFormatNotSupported);
}
}
Filter::Cubic => {
if !device.enabled_extensions().ext_filter_cubic {
return Err(CheckBlitImageError::ExtensionNotEnabled {
extension: "ext_filter_cubic",
reason: "the specified filter was Cubic",
});
}
if !source_inner
.image
.format_features()
.sampled_image_filter_cubic
{
return Err(CheckBlitImageError::FilterFormatNotSupported);
}
if !matches!(source.dimensions(), ImageDimensions::Dim2d { .. }) {
return Err(CheckBlitImageError::FilterDimensionalityNotSupported);
}
}
}
Ok(())
}
#[derive(Debug, Copy, Clone)]
pub enum CheckBlitImageError {
ExtensionNotEnabled {
extension: &'static str,
reason: &'static str,
},
FilterDimensionalityNotSupported,
FilterFormatNotSupported,
MissingTransferSourceUsage,
MissingTransferDestinationUsage,
SourceFormatNotSupported,
DestinationFormatNotSupported,
DepthStencilNearestMandatory,
DepthStencilFormatMismatch,
IncompatibleFormatTypes {
source_type: NumericType,
destination_type: NumericType,
},
UnexpectedMultisampled,
SourceCoordinatesOutOfRange,
DestinationCoordinatesOutOfRange,
IncompatibleRangeForImageType,
OverlappingRegions,
}
impl error::Error for CheckBlitImageError {}
impl fmt::Display for CheckBlitImageError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Self::ExtensionNotEnabled { extension, reason } => write!(
fmt,
"the extension {} must be enabled: {}",
extension, reason
),
Self::FilterDimensionalityNotSupported => write!(
fmt,
"the chosen filter type does not support the dimensionality of the source image"
),
Self::FilterFormatNotSupported => write!(
fmt,
"the chosen filter type does not support the format of the source image"
),
Self::MissingTransferSourceUsage => {
write!(fmt, "the source is missing the transfer source usage")
}
Self::MissingTransferDestinationUsage => {
write!(
fmt,
"the destination is missing the transfer destination usage"
)
}
Self::SourceFormatNotSupported => {
write!(
fmt,
"the format of the source image doesn't support blit operations"
)
}
Self::DestinationFormatNotSupported => {
write!(
fmt,
"the format of the destination image doesn't support blit operations"
)
}
Self::DepthStencilNearestMandatory => {
write!(
fmt,
"you must use the nearest filter when blitting depth/stencil images"
)
}
Self::DepthStencilFormatMismatch => {
write!(fmt, "the format of the source and destination must be equal when blitting depth/stencil images")
}
Self::IncompatibleFormatTypes { .. } => {
write!(
fmt,
"the types of the source format and the destination format aren't compatible"
)
}
Self::UnexpectedMultisampled => {
write!(fmt, "blitting between multisampled images is forbidden")
}
Self::SourceCoordinatesOutOfRange => {
write!(fmt, "the offsets, array layers and/or mipmap levels are out of range in the source image")
}
Self::DestinationCoordinatesOutOfRange => {
write!(fmt, "the offsets, array layers and/or mipmap levels are out of range in the destination image")
}
Self::IncompatibleRangeForImageType => {
write!(fmt, "the top-left and/or bottom-right coordinates are incompatible with the image type")
}
Self::OverlappingRegions => {
write!(fmt, "the source and destination regions are overlapping")
}
}
}
}