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 device::Device;
use device::DeviceOwned;
use format::ClearValue;
use framebuffer::AttachmentsList;
use framebuffer::FramebufferAbstract;
use framebuffer::IncompatibleRenderPassAttachmentError;
use framebuffer::AttachmentDescription;
use framebuffer::PassDependencyDescription;
use framebuffer::PassDescription;
use framebuffer::RenderPassAbstract;
use framebuffer::RenderPassDesc;
use framebuffer::RenderPassDescClearValues;
use framebuffer::RenderPassSys;
use framebuffer::ensure_image_view_compatible;
use image::ImageViewAccess;
use Error;
use OomError;
use VulkanObject;
use check_errors;
use vk;
#[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: render_pass,
raw_ids: SmallVec::new(),
dimensions: FramebufferBuilderDimensions::AutoIdentical(None),
attachments: (),
}
}
pub fn with_intersecting_dimensions(render_pass: Rp) -> FramebufferBuilder<Rp, ()> {
FramebufferBuilder {
render_pass: 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: 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: ImageViewAccess
{
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 img_dims = attachment.dimensions();
debug_assert_eq!(img_dims.depth(), 1);
let dimensions = match self.dimensions {
FramebufferBuilderDimensions::AutoIdentical(None) => {
let dims = [img_dims.width(), img_dims.height(), img_dims.array_layers()];
FramebufferBuilderDimensions::AutoIdentical(Some(dims))
},
FramebufferBuilderDimensions::AutoIdentical(Some(current)) => {
if img_dims.width() != current[0] || img_dims.height() != current[1] ||
img_dims.array_layers() != current[2]
{
return Err(FramebufferCreationError::AttachmentDimensionsIncompatible {
expected: current,
obtained: [img_dims.width(), img_dims.height(), img_dims.array_layers()],
});
}
FramebufferBuilderDimensions::AutoIdentical(Some(current))
},
FramebufferBuilderDimensions::AutoSmaller(None) => {
let dims = [img_dims.width(), img_dims.height(), img_dims.array_layers()];
FramebufferBuilderDimensions::AutoSmaller(Some(dims))
},
FramebufferBuilderDimensions::AutoSmaller(Some(current)) => {
let new_dims = [
cmp::min(current[0], img_dims.width()),
cmp::min(current[1], img_dims.height()),
cmp::min(current[2], img_dims.array_layers()),
];
FramebufferBuilderDimensions::AutoSmaller(Some(new_dims))
},
FramebufferBuilderDimensions::Specific(current) => {
if img_dims.width() < current[0] || img_dims.height() < current[1] ||
img_dims.array_layers() < current[2]
{
return Err(FramebufferCreationError::AttachmentDimensionsIncompatible {
expected: current,
obtained: [img_dims.width(), img_dims.height(), img_dims.array_layers()],
});
}
FramebufferBuilderDimensions::Specific(
[img_dims.width(), img_dims.height(), img_dims.array_layers()],
)
},
};
let mut raw_ids = self.raw_ids;
raw_ids.push(attachment.inner().internal_object());
Ok(FramebufferBuilder {
render_pass: self.render_pass,
raw_ids: raw_ids,
dimensions: 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: device,
render_pass: self.render_pass,
framebuffer: framebuffer,
dimensions: 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 ImageViewAccess> {
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 description(&self) -> &str {
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"
},
}
}
#[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, "{}", error::Error::description(self))
}
}
impl From<Error> for FramebufferCreationError {
#[inline]
fn from(err: Error) -> FramebufferCreationError {
FramebufferCreationError::from(OomError::from(err))
}
}
#[cfg(test)]
mod tests {
use format::Format;
use framebuffer::EmptySinglePassRenderPassDesc;
use framebuffer::Framebuffer;
use framebuffer::FramebufferCreationError;
use framebuffer::RenderPassDesc;
use image::attachment::AttachmentImage;
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 image = AttachmentImage::new(device.clone(), [1024, 768], Format::R8G8B8A8Unorm)
.unwrap();
let _ = Framebuffer::start(render_pass)
.add(image.clone())
.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 image = AttachmentImage::new(device.clone(), [1024, 768], Format::R8Unorm).unwrap();
match Framebuffer::start(render_pass).add(image.clone()) {
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 img = AttachmentImage::new(device.clone(), [600, 600], Format::R8G8B8A8Unorm).unwrap();
let _ = Framebuffer::with_dimensions(render_pass, [512, 512, 1])
.add(img)
.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 img = AttachmentImage::new(device.clone(), [512, 700], Format::R8G8B8A8Unorm).unwrap();
match Framebuffer::with_dimensions(render_pass, [600, 600, 1]).add(img) {
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 = AttachmentImage::new(device.clone(), [512, 512], Format::R8G8B8A8Unorm).unwrap();
let b = AttachmentImage::new(device.clone(), [512, 513], Format::R8G8B8A8Unorm).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 = AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap();
let b = AttachmentImage::new(device.clone(), [512, 128], Format::R8G8B8A8Unorm).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 img = AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap();
let res = Framebuffer::with_intersecting_dimensions(render_pass)
.add(img)
.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 = AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).unwrap();
let b = AttachmentImage::new(device.clone(), [256, 512], Format::R8G8B8A8Unorm).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!(),
}
}
}