use smallvec::SmallVec;
use std::error;
use std::fmt;
use std::marker::PhantomData;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
use device::Device;
use device::DeviceOwned;
use format::ClearValue;
use framebuffer::EmptySinglePassRenderPassDesc;
use framebuffer::AttachmentDescription;
use framebuffer::PassDependencyDescription;
use framebuffer::PassDescription;
use framebuffer::LoadOp;
use framebuffer::RenderPassAbstract;
use framebuffer::RenderPassDesc;
use framebuffer::RenderPassDescClearValues;
use Error;
use OomError;
use VulkanObject;
use check_errors;
use vk;
pub struct RenderPass<D> {
render_pass: vk::RenderPass,
device: Arc<Device>,
desc: D,
granularity: Mutex<Option<[u32; 2]>>,
}
impl<D> RenderPass<D>
where D: RenderPassDesc
{
pub fn new(device: Arc<Device>, description: D)
-> Result<RenderPass<D>, RenderPassCreationError> {
let vk = device.pointers();
debug_assert!(description.attachment_descs().enumerate().all(|(atch_num,
attachment)| {
if attachment.load != LoadOp::Clear {
return true;
}
for p in description.subpass_descs() {
if p.color_attachments
.iter()
.find(|&&(a, _)| a == atch_num)
.is_some()
{
return true;
}
if let Some((a, _)) = p.depth_stencil {
if a == atch_num {
return true;
}
}
if p.input_attachments
.iter()
.find(|&&(a, _)| a == atch_num)
.is_some()
{
return false;
}
}
true
}));
let attachments = description
.attachment_descs()
.map(|attachment| {
debug_assert!(attachment.samples.is_power_of_two());
vk::AttachmentDescription {
flags: 0,
format: attachment.format as u32,
samples: attachment.samples,
loadOp: attachment.load as u32,
storeOp: attachment.store as u32,
stencilLoadOp: attachment.stencil_load as u32,
stencilStoreOp: attachment.stencil_store as u32,
initialLayout: attachment.initial_layout as u32,
finalLayout: attachment.final_layout as u32,
}
})
.collect::<SmallVec<[_; 16]>>();
let attachment_references = description
.subpass_descs()
.flat_map(|pass| {
debug_assert!(pass.resolve_attachments.is_empty() ||
pass.resolve_attachments.len() == pass.color_attachments.len());
debug_assert!(pass.resolve_attachments
.iter()
.all(|a| attachments[a.0].samples == 1));
debug_assert!(pass.resolve_attachments.is_empty() ||
pass.color_attachments
.iter()
.all(|a| attachments[a.0].samples > 1));
debug_assert!(pass.resolve_attachments.is_empty() ||
pass.resolve_attachments
.iter()
.zip(pass.color_attachments.iter())
.all(|(r, c)| {
attachments[r.0].format == attachments[c.0].format
}));
debug_assert!(pass.color_attachments
.iter()
.cloned()
.chain(pass.depth_stencil.clone().into_iter())
.chain(pass.input_attachments.iter().cloned())
.chain(pass.resolve_attachments.iter().cloned())
.all(|(a, _)| {
pass.preserve_attachments
.iter()
.find(|&&b| a == b)
.is_none()
}));
debug_assert!(
pass.color_attachments
.iter()
.cloned()
.chain(pass.depth_stencil.clone().into_iter())
.all(|(atch, layout)| if let Some(r) =
pass.input_attachments.iter().find(|r| r.0 == atch)
{
r.1 == layout
} else {
true
})
);
let resolve = pass.resolve_attachments
.into_iter()
.map(|(offset, img_la)| {
debug_assert!(offset < attachments.len());
vk::AttachmentReference {
attachment: offset as u32,
layout: img_la as u32,
}
});
let color = pass.color_attachments.into_iter().map(|(offset, img_la)| {
debug_assert!(offset < attachments.len());
vk::AttachmentReference {
attachment: offset as u32,
layout: img_la as u32,
}
});
let input = pass.input_attachments.into_iter().map(|(offset, img_la)| {
debug_assert!(offset < attachments.len());
vk::AttachmentReference {
attachment: offset as u32,
layout: img_la as u32,
}
});
let depthstencil = if let Some((offset, img_la)) = pass.depth_stencil {
Some(vk::AttachmentReference {
attachment: offset as u32,
layout: img_la as u32,
})
} else {
None
}.into_iter();
color.chain(input).chain(resolve).chain(depthstencil)
})
.collect::<SmallVec<[_; 16]>>();
let preserve_attachments_references = description
.subpass_descs()
.flat_map(|pass| {
pass.preserve_attachments
.into_iter()
.map(|offset| offset as u32)
})
.collect::<SmallVec<[_; 16]>>();
let passes = unsafe {
let mut ref_index = 0usize;
let mut preserve_ref_index = 0usize;
let mut out: SmallVec<[_; 16]> = SmallVec::new();
for pass in description.subpass_descs() {
if pass.color_attachments.len() as u32 >
device.physical_device().limits().max_color_attachments()
{
return Err(RenderPassCreationError::ColorAttachmentsLimitExceeded);
}
let color_attachments = attachment_references.as_ptr().offset(ref_index as isize);
ref_index += pass.color_attachments.len();
let input_attachments = attachment_references.as_ptr().offset(ref_index as isize);
ref_index += pass.input_attachments.len();
let resolve_attachments = attachment_references.as_ptr().offset(ref_index as isize);
ref_index += pass.resolve_attachments.len();
let depth_stencil = if pass.depth_stencil.is_some() {
let a = attachment_references.as_ptr().offset(ref_index as isize);
ref_index += 1;
a
} else {
ptr::null()
};
let preserve_attachments = preserve_attachments_references
.as_ptr()
.offset(preserve_ref_index as isize);
preserve_ref_index += pass.preserve_attachments.len();
out.push(vk::SubpassDescription {
flags: 0,
pipelineBindPoint: vk::PIPELINE_BIND_POINT_GRAPHICS,
inputAttachmentCount: pass.input_attachments.len() as u32,
pInputAttachments: if pass.input_attachments.is_empty() {
ptr::null()
} else {
input_attachments
},
colorAttachmentCount: pass.color_attachments.len() as u32,
pColorAttachments: if pass.color_attachments.is_empty() {
ptr::null()
} else {
color_attachments
},
pResolveAttachments: if pass.resolve_attachments.is_empty() {
ptr::null()
} else {
resolve_attachments
},
pDepthStencilAttachment: depth_stencil,
preserveAttachmentCount: pass.preserve_attachments.len() as u32,
pPreserveAttachments: if pass.preserve_attachments.is_empty() {
ptr::null()
} else {
preserve_attachments
},
});
}
assert!(!out.is_empty());
debug_assert!(ref_index == attachment_references.len());
debug_assert!(preserve_ref_index == preserve_attachments_references.len());
out
};
let dependencies = description
.dependency_descs()
.map(|dependency| {
debug_assert!(dependency.source_subpass as u32 == vk::SUBPASS_EXTERNAL || dependency.source_subpass < passes.len());
debug_assert!(dependency.destination_subpass as u32 == vk::SUBPASS_EXTERNAL || dependency.destination_subpass < passes.len());
vk::SubpassDependency {
srcSubpass: dependency.source_subpass as u32,
dstSubpass: dependency.destination_subpass as u32,
srcStageMask: dependency.source_stages.into_vulkan_bits(),
dstStageMask: dependency.destination_stages.into_vulkan_bits(),
srcAccessMask: dependency.source_access.into_vulkan_bits(),
dstAccessMask: dependency.destination_access.into_vulkan_bits(),
dependencyFlags: if dependency.by_region {
vk::DEPENDENCY_BY_REGION_BIT
} else {
0
},
}
})
.collect::<SmallVec<[_; 16]>>();
let render_pass = unsafe {
let infos = vk::RenderPassCreateInfo {
sType: vk::STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
pNext: ptr::null(),
flags: 0,
attachmentCount: attachments.len() as u32,
pAttachments: if attachments.is_empty() {
ptr::null()
} else {
attachments.as_ptr()
},
subpassCount: passes.len() as u32,
pSubpasses: if passes.is_empty() {
ptr::null()
} else {
passes.as_ptr()
},
dependencyCount: dependencies.len() as u32,
pDependencies: if dependencies.is_empty() {
ptr::null()
} else {
dependencies.as_ptr()
},
};
let mut output = mem::uninitialized();
check_errors(vk.CreateRenderPass(device.internal_object(),
&infos,
ptr::null(),
&mut output))?;
output
};
Ok(RenderPass {
device: device.clone(),
render_pass: render_pass,
desc: description,
granularity: Mutex::new(None),
})
}
}
impl RenderPass<EmptySinglePassRenderPassDesc> {
#[inline]
pub fn empty_single_pass(
device: Arc<Device>)
-> Result<RenderPass<EmptySinglePassRenderPassDesc>, RenderPassCreationError> {
RenderPass::new(device, EmptySinglePassRenderPassDesc)
}
}
impl<D> RenderPass<D> {
pub fn granularity(&self) -> [u32; 2] {
let mut granularity = self.granularity.lock().unwrap();
if let Some(&granularity) = granularity.as_ref() {
return granularity;
}
unsafe {
let vk = self.device.pointers();
let mut out = mem::uninitialized();
vk.GetRenderAreaGranularity(self.device.internal_object(), self.render_pass, &mut out);
debug_assert_ne!(out.width, 0);
debug_assert_ne!(out.height, 0);
let gran = [out.width, out.height];
*granularity = Some(gran);
gran
}
}
#[inline]
pub fn desc(&self) -> &D {
&self.desc
}
}
unsafe impl<D> RenderPassDesc for RenderPass<D>
where D: RenderPassDesc
{
#[inline]
fn num_attachments(&self) -> usize {
self.desc.num_attachments()
}
#[inline]
fn attachment_desc(&self, num: usize) -> Option<AttachmentDescription> {
self.desc.attachment_desc(num)
}
#[inline]
fn num_subpasses(&self) -> usize {
self.desc.num_subpasses()
}
#[inline]
fn subpass_desc(&self, num: usize) -> Option<PassDescription> {
self.desc.subpass_desc(num)
}
#[inline]
fn num_dependencies(&self) -> usize {
self.desc.num_dependencies()
}
#[inline]
fn dependency_desc(&self, num: usize) -> Option<PassDependencyDescription> {
self.desc.dependency_desc(num)
}
}
unsafe impl<C, D> RenderPassDescClearValues<C> for RenderPass<D>
where D: RenderPassDescClearValues<C>
{
#[inline]
fn convert_clear_values(&self, vals: C) -> Box<dyn Iterator<Item = ClearValue>> {
self.desc.convert_clear_values(vals)
}
}
unsafe impl<D> RenderPassAbstract for RenderPass<D>
where D: RenderPassDesc
{
#[inline]
fn inner(&self) -> RenderPassSys {
RenderPassSys(self.render_pass, PhantomData)
}
}
unsafe impl<D> DeviceOwned for RenderPass<D> {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl<D> fmt::Debug for RenderPass<D>
where D: fmt::Debug
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
fmt.debug_struct("RenderPass")
.field("raw", &self.render_pass)
.field("device", &self.device)
.field("desc", &self.desc)
.finish()
}
}
impl<D> Drop for RenderPass<D> {
#[inline]
fn drop(&mut self) {
unsafe {
let vk = self.device.pointers();
vk.DestroyRenderPass(self.device.internal_object(), self.render_pass, ptr::null());
}
}
}
#[derive(Debug, Copy, Clone)]
pub struct RenderPassSys<'a>(vk::RenderPass, PhantomData<&'a ()>);
unsafe impl<'a> VulkanObject for RenderPassSys<'a> {
type Object = vk::RenderPass;
const TYPE: vk::DebugReportObjectTypeEXT = vk::DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT;
#[inline]
fn internal_object(&self) -> vk::RenderPass {
self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RenderPassCreationError {
OomError(OomError),
ColorAttachmentsLimitExceeded,
}
impl error::Error for RenderPassCreationError {
#[inline]
fn description(&self) -> &str {
match *self {
RenderPassCreationError::OomError(_) => "not enough memory available",
RenderPassCreationError::ColorAttachmentsLimitExceeded => {
"the maximum number of color attachments has been exceeded"
},
}
}
#[inline]
fn cause(&self) -> Option<&dyn error::Error> {
match *self {
RenderPassCreationError::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for RenderPassCreationError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
impl From<OomError> for RenderPassCreationError {
#[inline]
fn from(err: OomError) -> RenderPassCreationError {
RenderPassCreationError::OomError(err)
}
}
impl From<Error> for RenderPassCreationError {
#[inline]
fn from(err: Error) -> RenderPassCreationError {
match err {
err @ Error::OutOfHostMemory => {
RenderPassCreationError::OomError(OomError::from(err))
},
err @ Error::OutOfDeviceMemory => {
RenderPassCreationError::OomError(OomError::from(err))
},
_ => panic!("unexpected error: {:?}", err),
}
}
}
#[cfg(test)]
mod tests {
use format::Format;
use framebuffer::RenderPass;
use framebuffer::RenderPassCreationError;
#[test]
fn empty() {
let (device, _) = gfx_dev_and_queue!();
let _ = RenderPass::empty_single_pass(device).unwrap();
}
#[test]
fn too_many_color_atch() {
let (device, _) = gfx_dev_and_queue!();
if device.physical_device().limits().max_color_attachments() >= 10 {
return;
}
let rp =
single_pass_renderpass! {
device.clone(),
attachments: {
a1: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, },
a2: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, },
a3: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, },
a4: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, },
a5: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, },
a6: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, },
a7: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, },
a8: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, },
a9: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, },
a10: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }
},
pass: {
color: [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10],
depth_stencil: {}
}
};
match rp {
Err(RenderPassCreationError::ColorAttachmentsLimitExceeded) => (),
_ => panic!(),
}
}
#[test]
fn non_zero_granularity() {
let (device, _) = gfx_dev_and_queue!();
let rp = single_pass_renderpass! {
device.clone(),
attachments: {
a: { load: Clear, store: DontCare, format: Format::R8G8B8A8Unorm, samples: 1, }
},
pass: {
color: [a],
depth_stencil: {}
}
}.unwrap();
let granularity = rp.granularity();
assert_ne!(granularity[0], 0);
assert_ne!(granularity[1], 0);
}
}