// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. use super::RenderPass; use crate::{ device::{Device, DeviceOwned}, format::Format, image::{view::ImageViewType, ImageDimensions, ImageUsage, ImageViewAbstract, SampleCount}, macros::impl_id_counter, OomError, VulkanError, VulkanObject, }; use smallvec::SmallVec; use std::{ error::Error, fmt::{Display, Error as FmtError, Formatter}, mem::MaybeUninit, num::NonZeroU64, ops::Range, ptr, sync::Arc, }; /// The image views that are attached to a render pass during drawing. /// /// A framebuffer is a collection of images, and supplies the actual inputs and outputs of each /// attachment within a render pass. Each attachment point in the render pass must have a matching /// image in the framebuffer. /// /// ``` /// # use std::sync::Arc; /// # use vulkano::render_pass::RenderPass; /// # use vulkano::image::AttachmentImage; /// # use vulkano::image::view::ImageView; /// use vulkano::render_pass::{Framebuffer, FramebufferCreateInfo}; /// /// # let render_pass: Arc = return; /// # let view: Arc> = return; /// // let render_pass: Arc<_> = ...; /// let framebuffer = Framebuffer::new( /// render_pass.clone(), /// FramebufferCreateInfo { /// attachments: vec![view], /// ..Default::default() /// }, /// ).unwrap(); /// ``` #[derive(Debug)] pub struct Framebuffer { handle: ash::vk::Framebuffer, render_pass: Arc, id: NonZeroU64, attachments: Vec>, extent: [u32; 2], layers: u32, } impl Framebuffer { /// Creates a new `Framebuffer`. pub fn new( render_pass: Arc, create_info: FramebufferCreateInfo, ) -> Result, FramebufferCreationError> { let FramebufferCreateInfo { attachments, mut extent, mut layers, _ne: _, } = create_info; let device = render_pass.device(); // VUID-VkFramebufferCreateInfo-attachmentCount-00876 if attachments.len() != render_pass.attachments().len() { return Err(FramebufferCreationError::AttachmentCountMismatch { provided: attachments.len() as u32, required: render_pass.attachments().len() as u32, }); } let auto_extent = extent[0] == 0 || extent[1] == 0; let auto_layers = layers == 0; // VUID-VkFramebufferCreateInfo-width-00885 // VUID-VkFramebufferCreateInfo-height-00887 if auto_extent { if attachments.is_empty() { return Err(FramebufferCreationError::AutoExtentAttachmentsEmpty); } extent = [u32::MAX, u32::MAX]; } // VUID-VkFramebufferCreateInfo-layers-00889 if auto_layers { if attachments.is_empty() { return Err(FramebufferCreationError::AutoLayersAttachmentsEmpty); } if render_pass.views_used() != 0 { // VUID-VkFramebufferCreateInfo-renderPass-02531 layers = 1; } else { layers = u32::MAX; } } else { // VUID-VkFramebufferCreateInfo-renderPass-02531 if render_pass.views_used() != 0 && layers != 1 { return Err(FramebufferCreationError::MultiviewLayersInvalid); } } let attachments_vk = attachments .iter() .zip(render_pass.attachments()) .enumerate() .map(|(attachment_num, (image_view, attachment_desc))| { let attachment_num = attachment_num as u32; assert_eq!(device, image_view.device()); for subpass in render_pass.subpasses() { // VUID-VkFramebufferCreateInfo-pAttachments-00877 if subpass .color_attachments .iter() .flatten() .any(|atch_ref| atch_ref.attachment == attachment_num) { if !image_view.usage().intersects(ImageUsage::COLOR_ATTACHMENT) { return Err(FramebufferCreationError::AttachmentMissingUsage { attachment: attachment_num, usage: "color_attachment", }); } } // VUID-VkFramebufferCreateInfo-pAttachments-02633 if let Some(atch_ref) = &subpass.depth_stencil_attachment { if atch_ref.attachment == attachment_num { if !image_view .usage() .intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT) { return Err(FramebufferCreationError::AttachmentMissingUsage { attachment: attachment_num, usage: "depth_stencil", }); } } } // VUID-VkFramebufferCreateInfo-pAttachments-00879 if subpass .input_attachments .iter() .flatten() .any(|atch_ref| atch_ref.attachment == attachment_num) { if !image_view.usage().intersects(ImageUsage::INPUT_ATTACHMENT) { return Err(FramebufferCreationError::AttachmentMissingUsage { attachment: attachment_num, usage: "input_attachment", }); } } } // VUID-VkFramebufferCreateInfo-pAttachments-00880 if image_view.format() != attachment_desc.format { return Err(FramebufferCreationError::AttachmentFormatMismatch { attachment: attachment_num, provided: image_view.format(), required: attachment_desc.format, }); } // VUID-VkFramebufferCreateInfo-pAttachments-00881 if image_view.image().samples() != attachment_desc.samples { return Err(FramebufferCreationError::AttachmentSamplesMismatch { attachment: attachment_num, provided: image_view.image().samples(), required: attachment_desc.samples, }); } let image_view_extent = image_view.image().dimensions().width_height(); let image_view_array_layers = image_view.subresource_range().array_layers.end - image_view.subresource_range().array_layers.start; // VUID-VkFramebufferCreateInfo-renderPass-04536 if image_view_array_layers < render_pass.views_used() { return Err( FramebufferCreationError::MultiviewAttachmentNotEnoughLayers { attachment: attachment_num, provided: image_view_array_layers, min: render_pass.views_used(), }, ); } // VUID-VkFramebufferCreateInfo-flags-04533 // VUID-VkFramebufferCreateInfo-flags-04534 if auto_extent { extent[0] = extent[0].min(image_view_extent[0]); extent[1] = extent[1].min(image_view_extent[1]); } else if image_view_extent[0] < extent[0] || image_view_extent[1] < extent[1] { return Err(FramebufferCreationError::AttachmentExtentTooSmall { attachment: attachment_num, provided: image_view_extent, min: extent, }); } // VUID-VkFramebufferCreateInfo-flags-04535 if auto_layers { layers = layers.min(image_view_array_layers); } else if image_view_array_layers < layers { return Err(FramebufferCreationError::AttachmentNotEnoughLayers { attachment: attachment_num, provided: image_view_array_layers, min: layers, }); } // VUID-VkFramebufferCreateInfo-pAttachments-00883 if image_view.subresource_range().mip_levels.end - image_view.subresource_range().mip_levels.start != 1 { return Err(FramebufferCreationError::AttachmentMultipleMipLevels { attachment: attachment_num, }); } // VUID-VkFramebufferCreateInfo-pAttachments-00884 if !image_view.component_mapping().is_identity() { return Err( FramebufferCreationError::AttachmentComponentMappingNotIdentity { attachment: attachment_num, }, ); } // VUID-VkFramebufferCreateInfo-pAttachments-00891 if matches!( image_view.view_type(), ImageViewType::Dim2d | ImageViewType::Dim2dArray ) && matches!( image_view.image().dimensions(), ImageDimensions::Dim3d { .. } ) && image_view.format().unwrap().type_color().is_none() { return Err( FramebufferCreationError::Attachment2dArrayCompatibleDepthStencil { attachment: attachment_num, }, ); } // VUID-VkFramebufferCreateInfo-flags-04113 if image_view.view_type() == ImageViewType::Dim3d { return Err(FramebufferCreationError::AttachmentViewType3d { attachment: attachment_num, }); } Ok(image_view.handle()) }) .collect::, _>>()?; { let properties = device.physical_device().properties(); // VUID-VkFramebufferCreateInfo-width-00886 // VUID-VkFramebufferCreateInfo-height-00888 if extent[0] > properties.max_framebuffer_width || extent[1] > properties.max_framebuffer_height { return Err(FramebufferCreationError::MaxFramebufferExtentExceeded { provided: extent, max: [ properties.max_framebuffer_width, properties.max_framebuffer_height, ], }); } // VUID-VkFramebufferCreateInfo-layers-00890 if layers > properties.max_framebuffer_layers { return Err(FramebufferCreationError::MaxFramebufferLayersExceeded { provided: layers, max: properties.max_framebuffer_layers, }); } } let create_info = ash::vk::FramebufferCreateInfo { flags: ash::vk::FramebufferCreateFlags::empty(), render_pass: render_pass.handle(), attachment_count: attachments_vk.len() as u32, p_attachments: attachments_vk.as_ptr(), width: extent[0], height: extent[1], layers, ..Default::default() }; let handle = unsafe { let fns = device.fns(); let mut output = MaybeUninit::uninit(); (fns.v1_0.create_framebuffer)( device.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(Arc::new(Framebuffer { handle, render_pass, id: Self::next_id(), attachments, extent, layers, })) } /// Creates a new `Framebuffer` from a raw object handle. /// /// # Safety /// /// - `handle` must be a valid Vulkan object handle created from `render_pass`. /// - `create_info` must match the info used to create the object. #[inline] pub unsafe fn from_handle( render_pass: Arc, handle: ash::vk::Framebuffer, create_info: FramebufferCreateInfo, ) -> Arc { let FramebufferCreateInfo { attachments, extent, layers, _ne: _, } = create_info; Arc::new(Framebuffer { handle, render_pass, id: Self::next_id(), attachments, extent, layers, }) } /// Returns the renderpass that was used to create this framebuffer. #[inline] pub fn render_pass(&self) -> &Arc { &self.render_pass } /// Returns the attachments of the framebuffer. #[inline] pub fn attachments(&self) -> &[Arc] { &self.attachments } /// Returns the extent (width and height) of the framebuffer. #[inline] pub fn extent(&self) -> [u32; 2] { self.extent } /// Returns the number of layers of the framebuffer. #[inline] pub fn layers(&self) -> u32 { self.layers } /// Returns the layer ranges for all attachments. #[inline] pub fn attached_layers_ranges(&self) -> SmallVec<[Range; 4]> { self.attachments .iter() .map(|img| img.subresource_range().array_layers.clone()) .collect() } } impl Drop for Framebuffer { #[inline] fn drop(&mut self) { unsafe { let fns = self.device().fns(); (fns.v1_0.destroy_framebuffer)(self.device().handle(), self.handle, ptr::null()); } } } unsafe impl VulkanObject for Framebuffer { type Handle = ash::vk::Framebuffer; #[inline] fn handle(&self) -> Self::Handle { self.handle } } unsafe impl DeviceOwned for Framebuffer { #[inline] fn device(&self) -> &Arc { self.render_pass.device() } } impl_id_counter!(Framebuffer); /// Parameters to create a new `Framebuffer`. #[derive(Clone, Debug)] pub struct FramebufferCreateInfo { /// The attachment images that are to be used in the framebuffer. /// /// Attachments are specified in the same order as they are defined in the render pass, and /// there must be exactly as many. This implies that the list must be empty if the render pass /// specifies no attachments. Each image must have the correct usages set to be used for the /// types of attachment that the render pass will use it as. /// /// The attachment images must not be smaller than `extent` and `layers`, but can be larger and /// have different sizes from each other. Any leftover parts of an image will be left untouched /// during rendering. /// /// If the render pass has multiview enabled (`views_used` does not return 0), then each /// image must have at least `views_used` array layers. /// /// The default value is empty. pub attachments: Vec>, /// The extent (width and height) of the framebuffer. /// /// This must be no larger than the smallest width and height of the images in `attachments`. /// If one of the elements is set to 0, the extent will be calculated automatically from the /// extents of the attachment images to be the largest allowed. At least one attachment image /// must be specified in that case. /// /// The extent, whether automatically calculated or specified explicitly, must not be larger /// than the [`max_framebuffer_width`](crate::device::Properties::max_framebuffer_width) and /// [`max_framebuffer_height`](crate::device::Properties::max_framebuffer_height) limits. /// /// The default value is `[0, 0]`. pub extent: [u32; 2], /// The number of layers of the framebuffer. /// /// This must be no larger than the smallest number of array layers of the images in /// `attachments`. If set to 0, the number of layers will be calculated automatically from the /// layer ranges of the attachment images to be the largest allowed. At least one attachment /// image must be specified in that case. /// /// The number of layers, whether automatically calculated or specified explicitly, must not be /// larger than the /// [`max_framebuffer_layers`](crate::device::Properties::max_framebuffer_layers) limit. /// /// If the render pass has multiview enabled (`views_used` does not return 0), then this value /// must be 0 or 1. /// /// The default value is `0`. pub layers: u32, pub _ne: crate::NonExhaustive, } impl Default for FramebufferCreateInfo { #[inline] fn default() -> Self { Self { attachments: Vec::new(), extent: [0, 0], layers: 0, _ne: crate::NonExhaustive(()), } } } /// Error that can happen when creating a `Framebuffer`. #[derive(Copy, Clone, Debug)] pub enum FramebufferCreationError { /// Out of memory. OomError(OomError), /// An attachment image is a 2D image view created from a 3D image, and has a depth/stencil /// format. Attachment2dArrayCompatibleDepthStencil { attachment: u32 }, /// An attachment image has a non-identity component mapping. AttachmentComponentMappingNotIdentity { attachment: u32 }, /// The number of attachments doesn't match the number expected by the render pass. AttachmentCountMismatch { provided: u32, required: u32 }, /// An attachment image has an extent smaller than the provided `extent`. AttachmentExtentTooSmall { attachment: u32, provided: [u32; 2], min: [u32; 2], }, /// An attachment image has a `format` different from what the render pass requires. AttachmentFormatMismatch { attachment: u32, provided: Option, required: Option, }, /// An attachment image is missing a usage that the render pass requires it to have. AttachmentMissingUsage { attachment: u32, usage: &'static str, }, /// An attachment image has multiple mip levels. AttachmentMultipleMipLevels { attachment: u32 }, /// An attachment image has less array layers than the provided `layers`. AttachmentNotEnoughLayers { attachment: u32, provided: u32, min: u32, }, /// An attachment image has a `samples` different from what the render pass requires. AttachmentSamplesMismatch { attachment: u32, provided: SampleCount, required: SampleCount, }, /// An attachment image has a `ty` of [`ImageViewType::Dim3d`]. AttachmentViewType3d { attachment: u32 }, /// One of the elements of `extent` is zero, but no attachment images were given to calculate /// the extent from. AutoExtentAttachmentsEmpty, /// `layers` is zero, but no attachment images were given to calculate the number of layers /// from. AutoLayersAttachmentsEmpty, /// The provided `extent` exceeds the `max_framebuffer_width` or `max_framebuffer_height` /// limits. MaxFramebufferExtentExceeded { provided: [u32; 2], max: [u32; 2] }, /// The provided `layers` exceeds the `max_framebuffer_layers` limit. MaxFramebufferLayersExceeded { provided: u32, max: u32 }, /// The render pass has multiview enabled, and an attachment image has less layers than the /// number of views in the render pass. MultiviewAttachmentNotEnoughLayers { attachment: u32, provided: u32, min: u32, }, /// The render pass has multiview enabled, but `layers` was not 0 or 1. MultiviewLayersInvalid, } impl From for FramebufferCreationError { fn from(err: OomError) -> Self { Self::OomError(err) } } impl Error for FramebufferCreationError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { Self::OomError(err) => Some(err), _ => None, } } } impl Display for FramebufferCreationError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { match self { Self::OomError(_) => write!(f, "no memory available",), Self::Attachment2dArrayCompatibleDepthStencil { attachment } => write!( f, "attachment image {} is a 2D image view created from a 3D image, and has a \ depth/stencil format", attachment, ), Self::AttachmentComponentMappingNotIdentity { attachment } => write!( f, "attachment image {} has a non-identity component mapping", attachment, ), Self::AttachmentCountMismatch { .. } => write!( f, "the number of attachments doesn't match the number expected by the render pass", ), Self::AttachmentExtentTooSmall { attachment, provided, min, } => write!( f, "attachment image {} has an extent ({:?}) smaller than the provided `extent` \ ({:?})", attachment, provided, min, ), Self::AttachmentFormatMismatch { attachment, provided, required, } => write!( f, "attachment image {} has a `format` ({:?}) different from what the render pass \ requires ({:?})", attachment, provided, required, ), Self::AttachmentMissingUsage { attachment, usage } => write!( f, "attachment image {} is missing usage `{}` that the render pass requires it to \ have", attachment, usage, ), Self::AttachmentMultipleMipLevels { attachment } => { write!(f, "attachment image {} has multiple mip levels", attachment) } Self::AttachmentNotEnoughLayers { attachment, provided, min, } => write!( f, "attachment image {} has less layers ({}) than the provided `layers` ({})", attachment, provided, min, ), Self::AttachmentSamplesMismatch { attachment, provided, required, } => write!( f, "attachment image {} has a `samples` ({:?}) different from what the render pass \ requires ({:?})", attachment, provided, required, ), Self::AttachmentViewType3d { attachment } => write!( f, "attachment image {} has a `ty` of `ImageViewType::Dim3d`", attachment, ), Self::AutoExtentAttachmentsEmpty => write!( f, "one of the elements of `extent` is zero, but no attachment images were given to \ calculate the extent from", ), Self::AutoLayersAttachmentsEmpty => write!( f, "`layers` is zero, but no attachment images were given to calculate the number of \ layers from", ), Self::MaxFramebufferExtentExceeded { provided, max } => write!( f, "the provided `extent` ({:?}) exceeds the `max_framebuffer_width` or \ `max_framebuffer_height` limits ({:?})", provided, max, ), Self::MaxFramebufferLayersExceeded { provided, max } => write!( f, "the provided `layers` ({}) exceeds the `max_framebuffer_layers` limit ({})", provided, max, ), Self::MultiviewAttachmentNotEnoughLayers { attachment, provided, min, } => write!( f, "the render pass has multiview enabled, and attachment image {} has less layers \ ({}) than the number of views in the render pass ({})", attachment, provided, min, ), Self::MultiviewLayersInvalid => write!( f, "the render pass has multiview enabled, but `layers` was not 0 or 1", ), } } } impl From for FramebufferCreationError { fn from(err: VulkanError) -> Self { Self::from(OomError::from(err)) } } #[cfg(test)] mod tests { use crate::{ format::Format, image::{attachment::AttachmentImage, view::ImageView}, memory::allocator::StandardMemoryAllocator, render_pass::{Framebuffer, FramebufferCreateInfo, FramebufferCreationError, RenderPass}, }; #[test] fn simple_create() { let (device, _) = gfx_dev_and_queue!(); let render_pass = single_pass_renderpass!( device.clone(), attachments: { color: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, }, pass: { color: [color], depth_stencil: {}, }, ) .unwrap(); let memory_allocator = StandardMemoryAllocator::new_default(device); let view = ImageView::new_default( AttachmentImage::new(&memory_allocator, [1024, 768], Format::R8G8B8A8_UNORM).unwrap(), ) .unwrap(); let _ = Framebuffer::new( render_pass, FramebufferCreateInfo { attachments: vec![view], ..Default::default() }, ) .unwrap(); } #[test] fn check_device_limits() { let (device, _) = gfx_dev_and_queue!(); let render_pass = RenderPass::empty_single_pass(device).unwrap(); let res = Framebuffer::new( render_pass.clone(), FramebufferCreateInfo { extent: [0xffffffff, 0xffffffff], layers: 1, ..Default::default() }, ); match res { Err(FramebufferCreationError::MaxFramebufferExtentExceeded { .. }) => (), _ => panic!(), } let res = Framebuffer::new( render_pass, FramebufferCreateInfo { extent: [1, 1], layers: 0xffffffff, ..Default::default() }, ); match res { Err(FramebufferCreationError::MaxFramebufferLayersExceeded { .. }) => (), _ => panic!(), } } #[test] fn attachment_format_mismatch() { let (device, _) = gfx_dev_and_queue!(); let render_pass = single_pass_renderpass!( device.clone(), attachments: { color: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, }, pass: { color: [color], depth_stencil: {}, }, ) .unwrap(); let memory_allocator = StandardMemoryAllocator::new_default(device); let view = ImageView::new_default( AttachmentImage::new(&memory_allocator, [1024, 768], Format::R8_UNORM).unwrap(), ) .unwrap(); match Framebuffer::new( render_pass, FramebufferCreateInfo { attachments: vec![view], ..Default::default() }, ) { Err(FramebufferCreationError::AttachmentFormatMismatch { .. }) => (), _ => panic!(), } } // TODO: check samples mismatch #[test] fn attachment_dims_larger_than_specified_valid() { let (device, _) = gfx_dev_and_queue!(); let render_pass = single_pass_renderpass!( device.clone(), attachments: { color: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, }, pass: { color: [color], depth_stencil: {}, }, ) .unwrap(); let memory_allocator = StandardMemoryAllocator::new_default(device); let view = ImageView::new_default( AttachmentImage::new(&memory_allocator, [600, 600], Format::R8G8B8A8_UNORM).unwrap(), ) .unwrap(); let _ = Framebuffer::new( render_pass, FramebufferCreateInfo { attachments: vec![view], extent: [512, 512], layers: 1, ..Default::default() }, ) .unwrap(); } #[test] fn attachment_dims_smaller_than_specified() { let (device, _) = gfx_dev_and_queue!(); let render_pass = single_pass_renderpass!( device.clone(), attachments: { color: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, }, pass: { color: [color], depth_stencil: {}, }, ) .unwrap(); let memory_allocator = StandardMemoryAllocator::new_default(device); let view = ImageView::new_default( AttachmentImage::new(&memory_allocator, [512, 700], Format::R8G8B8A8_UNORM).unwrap(), ) .unwrap(); match Framebuffer::new( render_pass, FramebufferCreateInfo { attachments: vec![view], extent: [600, 600], layers: 1, ..Default::default() }, ) { Err(FramebufferCreationError::AttachmentExtentTooSmall { .. }) => (), _ => panic!(), } } #[test] fn multi_attachments_auto_smaller() { let (device, _) = gfx_dev_and_queue!(); let render_pass = single_pass_renderpass!( device.clone(), attachments: { a: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, b: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, }, pass: { color: [a, b], depth_stencil: {}, }, ) .unwrap(); let memory_allocator = StandardMemoryAllocator::new_default(device); let a = ImageView::new_default( AttachmentImage::new(&memory_allocator, [256, 512], Format::R8G8B8A8_UNORM).unwrap(), ) .unwrap(); let b = ImageView::new_default( AttachmentImage::new(&memory_allocator, [512, 128], Format::R8G8B8A8_UNORM).unwrap(), ) .unwrap(); let framebuffer = Framebuffer::new( render_pass, FramebufferCreateInfo { attachments: vec![a, b], ..Default::default() }, ) .unwrap(); match (framebuffer.extent(), framebuffer.layers()) { ([256, 128], 1) => (), _ => panic!(), } } #[test] fn not_enough_attachments() { let (device, _) = gfx_dev_and_queue!(); let render_pass = single_pass_renderpass!( device.clone(), attachments: { a: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, b: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, }, pass: { color: [a, b], depth_stencil: {}, }, ) .unwrap(); let memory_allocator = StandardMemoryAllocator::new_default(device); let view = ImageView::new_default( AttachmentImage::new(&memory_allocator, [256, 512], Format::R8G8B8A8_UNORM).unwrap(), ) .unwrap(); let res = Framebuffer::new( render_pass, FramebufferCreateInfo { attachments: vec![view], ..Default::default() }, ); match res { Err(FramebufferCreationError::AttachmentCountMismatch { required: 2, provided: 1, }) => (), _ => panic!(), } } #[test] fn too_many_attachments() { let (device, _) = gfx_dev_and_queue!(); let render_pass = single_pass_renderpass!( device.clone(), attachments: { a: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }, }, pass: { color: [a], depth_stencil: {}, }, ) .unwrap(); let memory_allocator = StandardMemoryAllocator::new_default(device); let a = ImageView::new_default( AttachmentImage::new(&memory_allocator, [256, 512], Format::R8G8B8A8_UNORM).unwrap(), ) .unwrap(); let b = ImageView::new_default( AttachmentImage::new(&memory_allocator, [256, 512], Format::R8G8B8A8_UNORM).unwrap(), ) .unwrap(); let res = Framebuffer::new( render_pass, FramebufferCreateInfo { attachments: vec![a, b], ..Default::default() }, ); match res { Err(FramebufferCreationError::AttachmentCountMismatch { required: 1, provided: 2, }) => (), _ => panic!(), } } #[test] fn empty_working() { let (device, _) = gfx_dev_and_queue!(); let render_pass = RenderPass::empty_single_pass(device).unwrap(); let _ = Framebuffer::new( render_pass, FramebufferCreateInfo { extent: [512, 512], layers: 1, ..Default::default() }, ) .unwrap(); } #[test] fn cant_determine_dimensions_auto() { let (device, _) = gfx_dev_and_queue!(); let render_pass = RenderPass::empty_single_pass(device).unwrap(); let res = Framebuffer::new(render_pass, FramebufferCreateInfo::default()); match res { Err(FramebufferCreationError::AutoExtentAttachmentsEmpty) => (), _ => panic!(), } } }