aboutsummaryrefslogtreecommitdiff
path: root/src/render_pass/framebuffer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/render_pass/framebuffer.rs')
-rw-r--r--src/render_pass/framebuffer.rs990
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!(),
+ }
+ }
+}