// 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 crate::check_errors; use crate::device::Device; use crate::device::DeviceOwned; use crate::image::view::ImageViewAbstract; use crate::render_pass::ensure_image_view_compatible; use crate::render_pass::AttachmentsList; use crate::render_pass::IncompatibleRenderPassAttachmentError; use crate::render_pass::RenderPass; use crate::Error; use crate::OomError; use crate::SafeDeref; use crate::VulkanObject; use smallvec::SmallVec; use std::cmp; use std::error; use std::fmt; use std::marker::PhantomData; use std::mem::MaybeUninit; use std::ptr; use std::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 /// subpass within a render pass. It is created from a subpass and must match it: each attachment /// point in the subpass must have a matching image in the framebuffer. /// /// Creating a framebuffer is done by calling `Framebuffer::start`, which returns a /// `FramebufferBuilder` object. You can then add the framebuffer attachments one by one by /// calling `add(image)`. When you are done, call `build()`. /// /// Both the `add` and the `build` functions perform various checks to make sure that the number /// of images is correct and that each image is compatible with the attachment definition in the /// render pass. /// /// ``` /// # use std::sync::Arc; /// # use vulkano::render_pass::RenderPass; /// use vulkano::render_pass::Framebuffer; /// /// # let render_pass: Arc = return; /// # let view: Arc>>> = return; /// // let render_pass: Arc<_> = ...; /// let framebuffer = Framebuffer::start(render_pass.clone()) /// .add(view).unwrap() /// .build().unwrap(); /// ``` /// /// All framebuffer objects implement the `FramebufferAbstract` trait. This means that you can cast /// any `Arc>` into an `Arc` for easier storage. /// /// ## Framebuffer dimensions /// /// If you use `Framebuffer::start()` to create a framebuffer then vulkano will automatically /// make sure that all the attachments have the same dimensions, as this is the most common /// situation. /// /// Alternatively you can also use `with_intersecting_dimensions`, in which case the dimensions of /// the framebuffer will be the intersection of the dimensions of all attachments, or /// `with_dimensions` if you want to specify exact dimensions. If you use `with_dimensions`, you /// are allowed to attach images that are larger than these dimensions. /// /// If the dimensions of the framebuffer don't match the dimensions of one of its attachment, then /// only the top-left hand corner of the image will be drawn to. /// #[derive(Debug)] pub struct Framebuffer { device: Arc, render_pass: Arc, framebuffer: ash::vk::Framebuffer, dimensions: [u32; 3], resources: A, } impl Framebuffer<()> { /// Starts building a framebuffer. pub fn start(render_pass: Arc) -> FramebufferBuilder<()> { FramebufferBuilder { render_pass, raw_ids: SmallVec::new(), dimensions: FramebufferBuilderDimensions::AutoIdentical(None), attachments: (), } } /// Starts building a framebuffer. The dimensions of the framebuffer will automatically be /// the intersection of the dimensions of all the attachments. pub fn with_intersecting_dimensions(render_pass: Arc) -> FramebufferBuilder<()> { FramebufferBuilder { render_pass, raw_ids: SmallVec::new(), dimensions: FramebufferBuilderDimensions::AutoSmaller(None), attachments: (), } } /// Starts building a framebuffer. pub fn with_dimensions( render_pass: Arc, dimensions: [u32; 3], ) -> FramebufferBuilder<()> { FramebufferBuilder { render_pass, raw_ids: SmallVec::new(), dimensions: FramebufferBuilderDimensions::Specific(dimensions), attachments: (), } } } /// Prototype of a framebuffer. pub struct FramebufferBuilder { render_pass: Arc, raw_ids: SmallVec<[ash::vk::ImageView; 8]>, dimensions: FramebufferBuilderDimensions, attachments: A, } impl fmt::Debug for FramebufferBuilder where A: fmt::Debug, { #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt.debug_struct("FramebufferBuilder") .field("render_pass", &self.render_pass) .field("dimensions", &self.dimensions) .field("attachments", &self.attachments) .finish() } } #[derive(Debug)] enum FramebufferBuilderDimensions { AutoIdentical(Option<[u32; 3]>), AutoSmaller(Option<[u32; 3]>), Specific([u32; 3]), } impl FramebufferBuilder where A: AttachmentsList, { /// Appends an attachment to the prototype of the framebuffer. /// /// Attachments must be added in the same order as the one defined in the render pass. pub fn add( self, attachment: T, ) -> Result, FramebufferCreationError> where T: ImageViewAbstract, { if self.raw_ids.len() >= self.render_pass.desc().attachments().len() { return Err(FramebufferCreationError::AttachmentsCountMismatch { expected: self.render_pass.desc().attachments().len(), obtained: self.raw_ids.len() + 1, }); } match ensure_image_view_compatible(self.render_pass.desc(), self.raw_ids.len(), &attachment) { Ok(()) => (), Err(err) => return Err(FramebufferCreationError::IncompatibleAttachment(err)), }; let image_dimensions = attachment.image().dimensions(); let array_layers = attachment.array_layers(); debug_assert_eq!(image_dimensions.depth(), 1); let view_dimensions = [ image_dimensions.width(), image_dimensions.height(), array_layers.end - array_layers.start, ]; let dimensions = match self.dimensions { FramebufferBuilderDimensions::AutoIdentical(None) => { FramebufferBuilderDimensions::AutoIdentical(Some(view_dimensions)) } FramebufferBuilderDimensions::AutoIdentical(Some(current)) => { if view_dimensions != current { return Err(FramebufferCreationError::AttachmentDimensionsIncompatible { expected: current, obtained: view_dimensions, }); } FramebufferBuilderDimensions::AutoIdentical(Some(current)) } FramebufferBuilderDimensions::AutoSmaller(None) => { FramebufferBuilderDimensions::AutoSmaller(Some(view_dimensions)) } FramebufferBuilderDimensions::AutoSmaller(Some(current)) => { let new_dims = [ cmp::min(current[0], view_dimensions[0]), cmp::min(current[1], view_dimensions[1]), cmp::min(current[2], view_dimensions[2]), ]; FramebufferBuilderDimensions::AutoSmaller(Some(new_dims)) } FramebufferBuilderDimensions::Specific(current) => { if view_dimensions[0] < current[0] || view_dimensions[1] < current[1] || view_dimensions[2] < current[2] { return Err(FramebufferCreationError::AttachmentDimensionsIncompatible { expected: current, obtained: view_dimensions, }); } FramebufferBuilderDimensions::Specific(view_dimensions) } }; let mut raw_ids = self.raw_ids; raw_ids.push(attachment.inner().internal_object()); Ok(FramebufferBuilder { render_pass: self.render_pass, raw_ids, dimensions, attachments: (self.attachments, attachment), }) } /// Turns this builder into a `FramebufferBuilder>`. /// /// This allows you to store the builder in situations where you don't know in advance the /// number of attachments. /// /// > **Note**: This is a very rare corner case and you shouldn't have to use this function /// > in most situations. #[inline] pub fn boxed(self) -> FramebufferBuilder> where A: 'static, { FramebufferBuilder { render_pass: self.render_pass, raw_ids: self.raw_ids, dimensions: self.dimensions, attachments: Box::new(self.attachments) as Box<_>, } } /// Builds the framebuffer. pub fn build(self) -> Result, FramebufferCreationError> { let device = self.render_pass.device().clone(); // Check the number of attachments. if self.raw_ids.len() != self.render_pass.desc().attachments().len() { return Err(FramebufferCreationError::AttachmentsCountMismatch { expected: self.render_pass.desc().attachments().len(), obtained: self.raw_ids.len(), }); } // Compute the dimensions. let dimensions = match self.dimensions { FramebufferBuilderDimensions::Specific(dims) | FramebufferBuilderDimensions::AutoIdentical(Some(dims)) | FramebufferBuilderDimensions::AutoSmaller(Some(dims)) => dims, FramebufferBuilderDimensions::AutoIdentical(None) | FramebufferBuilderDimensions::AutoSmaller(None) => { return Err(FramebufferCreationError::CantDetermineDimensions); } }; // Checking the dimensions against the limits. { let properties = device.physical_device().properties(); let limits = [ properties.max_framebuffer_width, properties.max_framebuffer_height, properties.max_framebuffer_layers, ]; if dimensions[0] > limits[0] || dimensions[1] > limits[1] || dimensions[2] > limits[2] { return Err(FramebufferCreationError::DimensionsTooLarge); } } let mut layers = 1; if let Some(multiview) = self.render_pass.desc().multiview() { // There needs to be at least as many layers in the framebuffer // as the highest layer that gets referenced by the multiview masking. if multiview.highest_used_layer() > dimensions[2] { return Err(FramebufferCreationError::InsufficientLayerCount { minimum: multiview.highest_used_layer(), current: dimensions[2], }); } // VUID-VkFramebufferCreateInfo-renderPass-02531 // The framebuffer has to be created with one layer if multiview is enabled even though // the underlying images generally have more layers // but these layers get used by the multiview functionality. if multiview.view_masks.iter().any(|&mask| mask != 0) { layers = 1; } } let framebuffer = unsafe { let fns = device.fns(); let infos = ash::vk::FramebufferCreateInfo { flags: ash::vk::FramebufferCreateFlags::empty(), render_pass: self.render_pass.inner().internal_object(), attachment_count: self.raw_ids.len() as u32, p_attachments: self.raw_ids.as_ptr(), width: dimensions[0], height: dimensions[1], layers, ..Default::default() }; let mut output = MaybeUninit::uninit(); check_errors(fns.v1_0.create_framebuffer( device.internal_object(), &infos, ptr::null(), output.as_mut_ptr(), ))?; output.assume_init() }; Ok(Framebuffer { device, render_pass: self.render_pass, framebuffer, dimensions, resources: self.attachments, }) } } impl Framebuffer { /// Returns the width, height and layers of this framebuffer. #[inline] pub fn dimensions(&self) -> [u32; 3] { self.dimensions } /// Returns the width of the framebuffer in pixels. #[inline] pub fn width(&self) -> u32 { self.dimensions[0] } /// Returns the height of the framebuffer in pixels. #[inline] pub fn height(&self) -> u32 { self.dimensions[1] } /// Returns the number of layers (or depth) of the framebuffer. #[inline] pub fn layers(&self) -> u32 { self.dimensions[2] } /// Returns the device that was used to create this framebuffer. #[inline] pub fn device(&self) -> &Arc { &self.device } /// Returns the renderpass that was used to create this framebuffer. #[inline] pub fn render_pass(&self) -> &Arc { &self.render_pass } } /// Trait for objects that contain a Vulkan framebuffer object. /// /// Any `Framebuffer` object implements this trait. You can therefore turn a `Arc>` /// into a `Arc` for easier storage. pub unsafe trait FramebufferAbstract { /// Returns an opaque struct that represents the framebuffer's internals. fn inner(&self) -> FramebufferSys; /// Returns the width, height and array layers of the framebuffer. fn dimensions(&self) -> [u32; 3]; /// Returns the render pass this framebuffer was created for. fn render_pass(&self) -> &Arc; /// Returns the attachment of the framebuffer with the given index. /// /// If the `index` is not between `0` and `num_attachments`, then `None` should be returned. fn attached_image_view(&self, index: usize) -> Option<&dyn ImageViewAbstract>; /// Returns the width of the framebuffer in pixels. #[inline] fn width(&self) -> u32 { self.dimensions()[0] } /// Returns the height of the framebuffer in pixels. #[inline] fn height(&self) -> u32 { self.dimensions()[1] } /// Returns the number of layers (or depth) of the framebuffer. #[inline] fn layers(&self) -> u32 { self.dimensions()[2] } } unsafe impl FramebufferAbstract for T where T: SafeDeref, T::Target: FramebufferAbstract, { #[inline] fn inner(&self) -> FramebufferSys { FramebufferAbstract::inner(&**self) } #[inline] fn dimensions(&self) -> [u32; 3] { (**self).dimensions() } #[inline] fn render_pass(&self) -> &Arc { (**self).render_pass() } #[inline] fn attached_image_view(&self, index: usize) -> Option<&dyn ImageViewAbstract> { (**self).attached_image_view(index) } } unsafe impl FramebufferAbstract for Framebuffer where A: AttachmentsList, { #[inline] fn inner(&self) -> FramebufferSys { FramebufferSys(self.framebuffer, PhantomData) } #[inline] fn dimensions(&self) -> [u32; 3] { self.dimensions } #[inline] fn render_pass(&self) -> &Arc { &self.render_pass } #[inline] fn attached_image_view(&self, index: usize) -> Option<&dyn ImageViewAbstract> { self.resources.as_image_view_access(index) } } unsafe impl DeviceOwned for Framebuffer { #[inline] fn device(&self) -> &Arc { &self.device } } impl Drop for Framebuffer { #[inline] fn drop(&mut self) { unsafe { let fns = self.device.fns(); fns.v1_0.destroy_framebuffer( self.device.internal_object(), self.framebuffer, ptr::null(), ); } } } /// Opaque object that represents the internals of a framebuffer. #[derive(Debug, Copy, Clone)] pub struct FramebufferSys<'a>(ash::vk::Framebuffer, PhantomData<&'a ()>); unsafe impl<'a> VulkanObject for FramebufferSys<'a> { type Object = ash::vk::Framebuffer; #[inline] fn internal_object(&self) -> ash::vk::Framebuffer { self.0 } } /// Error that can happen when creating a framebuffer object. #[derive(Copy, Clone, Debug)] pub enum FramebufferCreationError { /// Out of memory. OomError(OomError), /// The requested dimensions exceed the device's limits. DimensionsTooLarge, /// The number of minimum layers expected by the render pass exceed the framebuffer layers. /// This can happen when the multiview feature is enabled and the specified view or correlation /// masks refer to more layers than the framebuffer has. InsufficientLayerCount { /// Minimum number of layers. minimum: u32, /// Number of framebuffer layers. current: u32, }, /// The attachment has a size that isn't compatible with the requested framebuffer dimensions. AttachmentDimensionsIncompatible { /// Expected dimensions. expected: [u32; 3], /// Attachment dimensions. obtained: [u32; 3], }, /// The number of attachments doesn't match the number expected by the render pass. AttachmentsCountMismatch { /// Expected number of attachments. expected: usize, /// Number of attachments that were given. obtained: usize, }, /// One of the images cannot be used as the requested attachment. IncompatibleAttachment(IncompatibleRenderPassAttachmentError), /// The framebuffer has no attachment and no dimension was specified. CantDetermineDimensions, } impl From for FramebufferCreationError { #[inline] fn from(err: OomError) -> FramebufferCreationError { FramebufferCreationError::OomError(err) } } impl error::Error for FramebufferCreationError { #[inline] fn source(&self) -> Option<&(dyn error::Error + 'static)> { match *self { FramebufferCreationError::OomError(ref err) => Some(err), FramebufferCreationError::IncompatibleAttachment(ref err) => Some(err), _ => None, } } } impl fmt::Display for FramebufferCreationError { #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!( fmt, "{}", match *self { FramebufferCreationError::OomError(_) => "no memory available", FramebufferCreationError::DimensionsTooLarge => { "the dimensions of the framebuffer are too large" } FramebufferCreationError::InsufficientLayerCount { .. } => { "the number of minimum layers expected by the render pass exceed the framebuffer layers" } FramebufferCreationError::AttachmentDimensionsIncompatible { .. } => { "the attachment has a size that isn't compatible with the framebuffer dimensions" } FramebufferCreationError::AttachmentsCountMismatch { .. } => { "the number of attachments doesn't match the number expected by the render pass" } FramebufferCreationError::IncompatibleAttachment(_) => { "one of the images cannot be used as the requested attachment" } FramebufferCreationError::CantDetermineDimensions => { "the framebuffer has no attachment and no dimension was specified" } } ) } } impl From for FramebufferCreationError { #[inline] fn from(err: Error) -> FramebufferCreationError { FramebufferCreationError::from(OomError::from(err)) } } #[cfg(test)] mod tests { use crate::format::Format; use crate::image::attachment::AttachmentImage; use crate::image::view::ImageView; use crate::render_pass::Framebuffer; use crate::render_pass::FramebufferCreationError; use crate::render_pass::RenderPass; use std::sync::Arc; #[test] fn simple_create() { let (device, _) = gfx_dev_and_queue!(); let render_pass = Arc::new( single_pass_renderpass!(device.clone(), attachments: { color: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, } }, pass: { color: [color], depth_stencil: {} } ) .unwrap(), ); let view = ImageView::new( AttachmentImage::new(device.clone(), [1024, 768], Format::R8G8B8A8Unorm).unwrap(), ) .unwrap(); let _ = Framebuffer::start(render_pass) .add(view) .unwrap() .build() .unwrap(); } #[test] fn check_device_limits() { let (device, _) = gfx_dev_and_queue!(); let rp = Arc::new(RenderPass::empty_single_pass(device).unwrap()); let res = Framebuffer::with_dimensions(rp, [0xffffffff, 0xffffffff, 0xffffffff]).build(); match res { Err(FramebufferCreationError::DimensionsTooLarge) => (), _ => panic!(), } } #[test] fn attachment_format_mismatch() { let (device, _) = gfx_dev_and_queue!(); let render_pass = Arc::new( single_pass_renderpass!(device.clone(), attachments: { color: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, } }, pass: { color: [color], depth_stencil: {} } ) .unwrap(), ); let view = ImageView::new( AttachmentImage::new(device.clone(), [1024, 768], Format::R8Unorm).unwrap(), ) .unwrap(); match Framebuffer::start(render_pass).add(view) { Err(FramebufferCreationError::IncompatibleAttachment(_)) => (), _ => panic!(), } } // TODO: check samples mismatch #[test] fn attachment_dims_larger_than_specified_valid() { let (device, _) = gfx_dev_and_queue!(); let render_pass = Arc::new( single_pass_renderpass!(device.clone(), attachments: { color: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, } }, pass: { color: [color], depth_stencil: {} } ) .unwrap(), ); let view = ImageView::new( AttachmentImage::new(device.clone(), [600, 600], Format::R8G8B8A8Unorm).unwrap(), ) .unwrap(); let _ = Framebuffer::with_dimensions(render_pass, [512, 512, 1]) .add(view) .unwrap() .build() .unwrap(); } #[test] fn attachment_dims_smaller_than_specified() { let (device, _) = gfx_dev_and_queue!(); let render_pass = Arc::new( single_pass_renderpass!(device.clone(), attachments: { color: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, } }, pass: { color: [color], depth_stencil: {} } ) .unwrap(), ); let view = ImageView::new( AttachmentImage::new(device.clone(), [512, 700], Format::R8G8B8A8Unorm).unwrap(), ) .unwrap(); match Framebuffer::with_dimensions(render_pass, [600, 600, 1]).add(view) { Err(FramebufferCreationError::AttachmentDimensionsIncompatible { expected, obtained, }) => { assert_eq!(expected, [600, 600, 1]); assert_eq!(obtained, [512, 700, 1]); } _ => panic!(), } } #[test] fn multi_attachments_dims_not_identical() { let (device, _) = gfx_dev_and_queue!(); let render_pass = Arc::new( single_pass_renderpass!(device.clone(), attachments: { a: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, b: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, } }, pass: { color: [a, b], depth_stencil: {} } ) .unwrap(), ); let a = ImageView::new( AttachmentImage::new(device.clone(), [512, 512], Format::R8G8B8A8Unorm).unwrap(), ) .unwrap(); let b = ImageView::new( AttachmentImage::new(device.clone(), [512, 513], Format::R8G8B8A8Unorm).unwrap(), ) .unwrap(); match Framebuffer::start(render_pass).add(a).unwrap().add(b) { Err(FramebufferCreationError::AttachmentDimensionsIncompatible { expected, obtained, }) => { assert_eq!(expected, [512, 512, 1]); assert_eq!(obtained, [512, 513, 1]); } _ => panic!(), } } #[test] fn multi_attachments_auto_smaller() { let (device, _) = gfx_dev_and_queue!(); let render_pass = Arc::new( single_pass_renderpass!(device.clone(), attachments: { a: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, b: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, } }, pass: { color: [a, b], depth_stencil: {} } ) .unwrap(), ); let a = ImageView::new( AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap(), ) .unwrap(); let b = ImageView::new( AttachmentImage::new(device.clone(), [512, 128], Format::R8G8B8A8Unorm).unwrap(), ) .unwrap(); let fb = Framebuffer::with_intersecting_dimensions(render_pass) .add(a) .unwrap() .add(b) .unwrap() .build() .unwrap(); match (fb.width(), fb.height(), fb.layers()) { (256, 128, 1) => (), _ => panic!(), } } #[test] fn not_enough_attachments() { let (device, _) = gfx_dev_and_queue!(); let render_pass = Arc::new( single_pass_renderpass!(device.clone(), attachments: { a: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }, b: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, } }, pass: { color: [a, b], depth_stencil: {} } ) .unwrap(), ); let view = ImageView::new( AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap(), ) .unwrap(); let res = Framebuffer::with_intersecting_dimensions(render_pass) .add(view) .unwrap() .build(); match res { Err(FramebufferCreationError::AttachmentsCountMismatch { expected: 2, obtained: 1, }) => (), _ => panic!(), } } #[test] fn too_many_attachments() { let (device, _) = gfx_dev_and_queue!(); let render_pass = Arc::new( single_pass_renderpass!(device.clone(), attachments: { a: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, } }, pass: { color: [a], depth_stencil: {} } ) .unwrap(), ); let a = ImageView::new( AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap(), ) .unwrap(); let b = ImageView::new( AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap(), ) .unwrap(); let res = Framebuffer::with_intersecting_dimensions(render_pass) .add(a) .unwrap() .add(b); match res { Err(FramebufferCreationError::AttachmentsCountMismatch { expected: 1, obtained: 2, }) => (), _ => panic!(), } } #[test] fn empty_working() { let (device, _) = gfx_dev_and_queue!(); let rp = Arc::new(RenderPass::empty_single_pass(device).unwrap()); let _ = Framebuffer::with_dimensions(rp, [512, 512, 1]) .build() .unwrap(); } #[test] fn cant_determine_dimensions_auto() { let (device, _) = gfx_dev_and_queue!(); let rp = Arc::new(RenderPass::empty_single_pass(device).unwrap()); let res = Framebuffer::start(rp).build(); match res { Err(FramebufferCreationError::CantDetermineDimensions) => (), _ => panic!(), } } #[test] fn cant_determine_dimensions_intersect() { let (device, _) = gfx_dev_and_queue!(); let rp = Arc::new(RenderPass::empty_single_pass(device).unwrap()); let res = Framebuffer::with_intersecting_dimensions(rp).build(); match res { Err(FramebufferCreationError::CantDetermineDimensions) => (), _ => panic!(), } } }