diff options
Diffstat (limited to 'src/render_pass/framebuffer.rs')
-rw-r--r-- | src/render_pass/framebuffer.rs | 990 |
1 files changed, 990 insertions, 0 deletions
diff --git a/src/render_pass/framebuffer.rs b/src/render_pass/framebuffer.rs new file mode 100644 index 0000000..b55be4d --- /dev/null +++ b/src/render_pass/framebuffer.rs @@ -0,0 +1,990 @@ +// 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::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<RenderPass> = return; +/// # let view: Arc<vulkano::image::view::ImageView<Arc<vulkano::image::AttachmentImage<vulkano::format::Format>>>> = 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<Framebuffer<..>>` into an `Arc<FramebufferAbstract + Send + Sync>` 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<A> { + device: Arc<Device>, + render_pass: Arc<RenderPass>, + framebuffer: ash::vk::Framebuffer, + dimensions: [u32; 3], + resources: A, +} + +impl Framebuffer<()> { + /// Starts building a framebuffer. + pub fn start(render_pass: Arc<RenderPass>) -> 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<RenderPass>) -> FramebufferBuilder<()> { + FramebufferBuilder { + render_pass, + raw_ids: SmallVec::new(), + dimensions: FramebufferBuilderDimensions::AutoSmaller(None), + attachments: (), + } + } + + /// Starts building a framebuffer. + pub fn with_dimensions( + render_pass: Arc<RenderPass>, + dimensions: [u32; 3], + ) -> FramebufferBuilder<()> { + FramebufferBuilder { + render_pass, + raw_ids: SmallVec::new(), + dimensions: FramebufferBuilderDimensions::Specific(dimensions), + attachments: (), + } + } +} + +/// Prototype of a framebuffer. +pub struct FramebufferBuilder<A> { + render_pass: Arc<RenderPass>, + raw_ids: SmallVec<[ash::vk::ImageView; 8]>, + dimensions: FramebufferBuilderDimensions, + attachments: A, +} + +impl<A> fmt::Debug for FramebufferBuilder<A> +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<A> FramebufferBuilder<A> +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<T>( + self, + attachment: T, + ) -> Result<FramebufferBuilder<(A, T)>, 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<Rp, Box<AttachmentsList>>`. + /// + /// 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<Box<dyn AttachmentsList>> + 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<Framebuffer<A>, 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<A> Framebuffer<A> { + /// 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<Device> { + &self.device + } + + /// Returns the renderpass that was used to create this framebuffer. + #[inline] + pub fn render_pass(&self) -> &Arc<RenderPass> { + &self.render_pass + } +} + +/// Trait for objects that contain a Vulkan framebuffer object. +/// +/// Any `Framebuffer` object implements this trait. You can therefore turn a `Arc<Framebuffer<_>>` +/// into a `Arc<FramebufferAbstract + Send + Sync>` 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<RenderPass>; + + /// 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<T> 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<RenderPass> { + (**self).render_pass() + } + + #[inline] + fn attached_image_view(&self, index: usize) -> Option<&dyn ImageViewAbstract> { + (**self).attached_image_view(index) + } +} + +unsafe impl<A> FramebufferAbstract for Framebuffer<A> +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<RenderPass> { + &self.render_pass + } + + #[inline] + fn attached_image_view(&self, index: usize) -> Option<&dyn ImageViewAbstract> { + self.resources.as_image_view_access(index) + } +} + +unsafe impl<A> DeviceOwned for Framebuffer<A> { + #[inline] + fn device(&self) -> &Arc<Device> { + &self.device + } +} + +impl<A> Drop for Framebuffer<A> { + #[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<OomError> 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<Error> 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!(), + } + } +} |