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;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::format::ClearValue;
use crate::framebuffer::ensure_image_view_compatible;
use crate::framebuffer::AttachmentDescription;
use crate::framebuffer::AttachmentsList;
use crate::framebuffer::FramebufferAbstract;
use crate::framebuffer::IncompatibleRenderPassAttachmentError;
use crate::framebuffer::PassDependencyDescription;
use crate::framebuffer::PassDescription;
use crate::framebuffer::RenderPassAbstract;
use crate::framebuffer::RenderPassDesc;
use crate::framebuffer::RenderPassDescClearValues;
use crate::framebuffer::RenderPassSys;
use crate::image::view::ImageViewAbstract;
use crate::check_errors;
use crate::vk;
use crate::Error;
use crate::OomError;
use crate::VulkanObject;
#[derive(Debug)]
pub struct Framebuffer<Rp, A> {
device: Arc<Device>,
render_pass: Rp,
framebuffer: vk::Framebuffer,
dimensions: [u32; 3],
resources: A,
}
impl<Rp> Framebuffer<Rp, ()> {
pub fn start(render_pass: Rp) -> FramebufferBuilder<Rp, ()> {
FramebufferBuilder {
render_pass,
raw_ids: SmallVec::new(),
dimensions: FramebufferBuilderDimensions::AutoIdentical(None),
attachments: (),
}
}
pub fn with_intersecting_dimensions(render_pass: Rp) -> FramebufferBuilder<Rp, ()> {
FramebufferBuilder {
render_pass,
raw_ids: SmallVec::new(),
dimensions: FramebufferBuilderDimensions::AutoSmaller(None),
attachments: (),
}
}
pub fn with_dimensions(render_pass: Rp, dimensions: [u32; 3]) -> FramebufferBuilder<Rp, ()> {
FramebufferBuilder {
render_pass,
raw_ids: SmallVec::new(),
dimensions: FramebufferBuilderDimensions::Specific(dimensions),
attachments: (),
}
}
}
pub struct FramebufferBuilder<Rp, A> {
render_pass: Rp,
raw_ids: SmallVec<[vk::ImageView; 8]>,
dimensions: FramebufferBuilderDimensions,
attachments: A,
}
impl<Rp, A> fmt::Debug for FramebufferBuilder<Rp, A>
where
Rp: fmt::Debug,
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<Rp, A> FramebufferBuilder<Rp, A>
where
Rp: RenderPassAbstract,
A: AttachmentsList,
{
pub fn add<T>(
self,
attachment: T,
) -> Result<FramebufferBuilder<Rp, (A, T)>, FramebufferCreationError>
where
T: ImageViewAbstract,
{
if self.raw_ids.len() >= self.render_pass.num_attachments() {
return Err(FramebufferCreationError::AttachmentsCountMismatch {
expected: self.render_pass.num_attachments(),
obtained: self.raw_ids.len() + 1,
});
}
match ensure_image_view_compatible(&self.render_pass, 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),
})
}
#[inline]
pub fn boxed(self) -> FramebufferBuilder<Rp, 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<_>,
}
}
pub fn build(self) -> Result<Framebuffer<Rp, A>, FramebufferCreationError> {
let device = self.render_pass.device().clone();
if self.raw_ids.len() != self.render_pass.num_attachments() {
return Err(FramebufferCreationError::AttachmentsCountMismatch {
expected: self.render_pass.num_attachments(),
obtained: self.raw_ids.len(),
});
}
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);
}
};
{
let limits = device.physical_device().limits();
let limits = [
limits.max_framebuffer_width(),
limits.max_framebuffer_height(),
limits.max_framebuffer_layers(),
];
if dimensions[0] > limits[0] || dimensions[1] > limits[1] || dimensions[2] > limits[2] {
return Err(FramebufferCreationError::DimensionsTooLarge);
}
}
let framebuffer = unsafe {
let vk = device.pointers();
let infos = vk::FramebufferCreateInfo {
sType: vk::STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
renderPass: self.render_pass.inner().internal_object(),
attachmentCount: self.raw_ids.len() as u32,
pAttachments: self.raw_ids.as_ptr(),
width: dimensions[0],
height: dimensions[1],
layers: dimensions[2],
};
let mut output = MaybeUninit::uninit();
check_errors(vk.CreateFramebuffer(
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<Rp, A> Framebuffer<Rp, A> {
#[inline]
pub fn dimensions(&self) -> [u32; 3] {
self.dimensions
}
#[inline]
pub fn width(&self) -> u32 {
self.dimensions[0]
}
#[inline]
pub fn height(&self) -> u32 {
self.dimensions[1]
}
#[inline]
pub fn layers(&self) -> u32 {
self.dimensions[2]
}
#[inline]
pub fn device(&self) -> &Arc<Device> {
&self.device
}
#[inline]
pub fn render_pass(&self) -> &Rp {
&self.render_pass
}
}
unsafe impl<Rp, A> FramebufferAbstract for Framebuffer<Rp, A>
where
Rp: RenderPassAbstract,
A: AttachmentsList,
{
#[inline]
fn inner(&self) -> FramebufferSys {
FramebufferSys(self.framebuffer, PhantomData)
}
#[inline]
fn dimensions(&self) -> [u32; 3] {
self.dimensions
}
#[inline]
fn attached_image_view(&self, index: usize) -> Option<&dyn ImageViewAbstract> {
self.resources.as_image_view_access(index)
}
}
unsafe impl<Rp, A> RenderPassDesc for Framebuffer<Rp, A>
where
Rp: RenderPassDesc,
{
#[inline]
fn num_attachments(&self) -> usize {
self.render_pass.num_attachments()
}
#[inline]
fn attachment_desc(&self, num: usize) -> Option<AttachmentDescription> {
self.render_pass.attachment_desc(num)
}
#[inline]
fn num_subpasses(&self) -> usize {
self.render_pass.num_subpasses()
}
#[inline]
fn subpass_desc(&self, num: usize) -> Option<PassDescription> {
self.render_pass.subpass_desc(num)
}
#[inline]
fn num_dependencies(&self) -> usize {
self.render_pass.num_dependencies()
}
#[inline]
fn dependency_desc(&self, num: usize) -> Option<PassDependencyDescription> {
self.render_pass.dependency_desc(num)
}
}
unsafe impl<C, Rp, A> RenderPassDescClearValues<C> for Framebuffer<Rp, A>
where
Rp: RenderPassDescClearValues<C>,
{
#[inline]
fn convert_clear_values(&self, vals: C) -> Box<dyn Iterator<Item = ClearValue>> {
self.render_pass.convert_clear_values(vals)
}
}
unsafe impl<Rp, A> RenderPassAbstract for Framebuffer<Rp, A>
where
Rp: RenderPassAbstract,
{
#[inline]
fn inner(&self) -> RenderPassSys {
self.render_pass.inner()
}
}
unsafe impl<Rp, A> DeviceOwned for Framebuffer<Rp, A> {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl<Rp, A> Drop for Framebuffer<Rp, A> {
#[inline]
fn drop(&mut self) {
unsafe {
let vk = self.device.pointers();
vk.DestroyFramebuffer(self.device.internal_object(), self.framebuffer, ptr::null());
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct FramebufferSys<'a>(vk::Framebuffer, PhantomData<&'a ()>);
unsafe impl<'a> VulkanObject for FramebufferSys<'a> {
type Object = vk::Framebuffer;
const TYPE: vk::ObjectType = vk::OBJECT_TYPE_FRAMEBUFFER;
#[inline]
fn internal_object(&self) -> vk::Framebuffer {
self.0
}
}
#[derive(Copy, Clone, Debug)]
pub enum FramebufferCreationError {
OomError(OomError),
DimensionsTooLarge,
AttachmentDimensionsIncompatible {
expected: [u32; 3],
obtained: [u32; 3],
},
AttachmentsCountMismatch {
expected: usize,
obtained: usize,
},
IncompatibleAttachment(IncompatibleRenderPassAttachmentError),
CantDetermineDimensions,
}
impl From<OomError> for FramebufferCreationError {
#[inline]
fn from(err: OomError) -> FramebufferCreationError {
FramebufferCreationError::OomError(err)
}
}
impl error::Error for FramebufferCreationError {
#[inline]
fn cause(&self) -> Option<&dyn error::Error> {
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::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::framebuffer::EmptySinglePassRenderPassDesc;
use crate::framebuffer::Framebuffer;
use crate::framebuffer::FramebufferCreationError;
use crate::framebuffer::RenderPassDesc;
use crate::image::attachment::AttachmentImage;
use crate::image::view::ImageView;
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 = EmptySinglePassRenderPassDesc
.build_render_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!(),
}
}
#[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 = EmptySinglePassRenderPassDesc
.build_render_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 = EmptySinglePassRenderPassDesc
.build_render_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 = EmptySinglePassRenderPassDesc
.build_render_pass(device)
.unwrap();
let res = Framebuffer::with_intersecting_dimensions(rp).build();
match res {
Err(FramebufferCreationError::CantDetermineDimensions) => (),
_ => panic!(),
}
}
}