blob: 381f4f354446e33814e114c7a660e1127656eccd [file] [log] [blame]
// Copyright (c) 2017 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.
//! This module contains the `ensure_image_view_compatible` function, which verifies whether
//! an image view can be used as a render pass attachment.
use crate::image::view::ImageViewAbstract;
use crate::render_pass::RenderPassDesc;
use crate::{format::Format, image::SampleCount};
use std::error;
use std::fmt;
/// Checks whether the given image view is allowed to be the nth attachment of the given render
/// pass.
///
/// # Panic
///
/// Panics if the attachment number is out of range.
// TODO: add a specializable trait instead, that uses this function
// TODO: ImageView instead of ImageViewAbstract?
pub fn ensure_image_view_compatible<I>(
render_pass_desc: &RenderPassDesc,
attachment_num: usize,
image_view: &I,
) -> Result<(), IncompatibleRenderPassAttachmentError>
where
I: ?Sized + ImageViewAbstract,
{
let attachment_desc = render_pass_desc
.attachments()
.get(attachment_num)
.expect("Attachment num out of range");
if image_view.format() != attachment_desc.format {
return Err(IncompatibleRenderPassAttachmentError::FormatMismatch {
expected: attachment_desc.format,
obtained: image_view.format(),
});
}
if image_view.image().samples() != attachment_desc.samples {
return Err(IncompatibleRenderPassAttachmentError::SamplesMismatch {
expected: attachment_desc.samples,
obtained: image_view.image().samples(),
});
}
if !image_view.component_mapping().is_identity() {
return Err(IncompatibleRenderPassAttachmentError::NotIdentitySwizzled);
}
for subpass in render_pass_desc.subpasses() {
if subpass
.color_attachments
.iter()
.any(|&(n, _)| n == attachment_num)
{
debug_assert!(image_view.image().has_color()); // Was normally checked by the render pass.
if !image_view.image().inner().image.usage().color_attachment {
return Err(IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage);
}
}
if let Some((ds, _)) = subpass.depth_stencil {
if ds == attachment_num {
// Was normally checked by the render pass.
debug_assert!(image_view.image().has_depth() || image_view.image().has_stencil());
if !image_view
.image()
.inner()
.image
.usage()
.depth_stencil_attachment
{
return Err(
IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage,
);
}
}
}
if subpass
.input_attachments
.iter()
.any(|&(n, _)| n == attachment_num)
{
if !image_view.image().inner().image.usage().input_attachment {
return Err(IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage);
}
}
}
// TODO: consider forbidding LoadOp::Load if image is transient
// TODO: are all image layouts allowed? check this
Ok(())
}
/// Error that can happen when an image is not compatible with a render pass attachment slot.
#[derive(Copy, Clone, Debug)]
pub enum IncompatibleRenderPassAttachmentError {
/// The image format expected by the render pass doesn't match the actual format of
/// the image.
FormatMismatch {
/// Format expected by the render pass.
expected: Format,
/// Format of the image.
obtained: Format,
},
/// The number of samples expected by the render pass doesn't match the number of samples of
/// the image.
SamplesMismatch {
/// Number of samples expected by the render pass.
expected: SampleCount,
/// Number of samples of the image.
obtained: SampleCount,
},
/// The image view has a component swizzle that is different from identity.
NotIdentitySwizzled,
/// The image is used as a color attachment but is missing the color attachment usage.
MissingColorAttachmentUsage,
/// The image is used as a depth/stencil attachment but is missing the depth-stencil attachment
/// usage.
MissingDepthStencilAttachmentUsage,
/// The image is used as an input attachment but is missing the input attachment usage.
MissingInputAttachmentUsage,
}
impl error::Error for IncompatibleRenderPassAttachmentError {}
impl fmt::Display for IncompatibleRenderPassAttachmentError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
fmt,
"{}",
match *self {
IncompatibleRenderPassAttachmentError::FormatMismatch { .. } => {
"mismatch between the format expected by the render pass and the actual format"
}
IncompatibleRenderPassAttachmentError::SamplesMismatch { .. } => {
"mismatch between the number of samples expected by the render pass and the actual \
number of samples"
}
IncompatibleRenderPassAttachmentError::NotIdentitySwizzled => {
"the image view's component mapping is not identity swizzled"
}
IncompatibleRenderPassAttachmentError::MissingColorAttachmentUsage => {
"the image is used as a color attachment but is missing the color attachment usage"
}
IncompatibleRenderPassAttachmentError::MissingDepthStencilAttachmentUsage => {
"the image is used as a depth/stencil attachment but is missing the depth-stencil \
attachment usage"
}
IncompatibleRenderPassAttachmentError::MissingInputAttachmentUsage => {
"the image is used as an input attachment but is missing the input \
attachment usage"
}
}
)
}
}
#[cfg(test)]
mod tests {
use super::ensure_image_view_compatible;
use super::IncompatibleRenderPassAttachmentError;
use crate::format::Format;
use crate::image::view::ImageView;
use crate::image::AttachmentImage;
use crate::render_pass::RenderPassDesc;
#[test]
fn basic_ok() {
let (device, _) = gfx_dev_and_queue!();
let rp = single_pass_renderpass!(device.clone(),
attachments: {
color: {
load: Clear,
store: Store,
format: Format::R8G8B8A8Unorm,
samples: 1,
}
},
pass: {
color: [color],
depth_stencil: {}
}
)
.unwrap();
let view = ImageView::new(
AttachmentImage::new(device, [128, 128], Format::R8G8B8A8Unorm).unwrap(),
)
.unwrap();
ensure_image_view_compatible(rp.desc(), 0, &view).unwrap();
}
#[test]
fn format_mismatch() {
let (device, _) = gfx_dev_and_queue!();
let rp = single_pass_renderpass!(device.clone(),
attachments: {
color: {
load: Clear,
store: Store,
format: Format::R16G16Sfloat,
samples: 1,
}
},
pass: {
color: [color],
depth_stencil: {}
}
)
.unwrap();
let view = ImageView::new(
AttachmentImage::new(device, [128, 128], Format::R8G8B8A8Unorm).unwrap(),
)
.unwrap();
match ensure_image_view_compatible(rp.desc(), 0, &view) {
Err(IncompatibleRenderPassAttachmentError::FormatMismatch {
expected: Format::R16G16Sfloat,
obtained: Format::R8G8B8A8Unorm,
}) => (),
e => panic!("{:?}", e),
}
}
#[test]
fn attachment_out_of_range() {
let (device, _) = gfx_dev_and_queue!();
let rp = RenderPassDesc::empty();
let view = ImageView::new(
AttachmentImage::new(device, [128, 128], Format::R8G8B8A8Unorm).unwrap(),
)
.unwrap();
assert_should_panic!("Attachment num out of range", {
let _ = ensure_image_view_compatible(&rp, 0, &view);
});
}
// TODO: more tests
}