pub use self::{
create::RenderPassCreationError,
framebuffer::{Framebuffer, FramebufferCreateInfo, FramebufferCreationError},
};
use crate::{
device::{Device, DeviceOwned},
format::Format,
image::{ImageAspects, ImageLayout, SampleCount},
macros::{vulkan_bitflags, vulkan_enum},
shader::ShaderInterface,
sync::{AccessFlags, PipelineStages},
Version, VulkanObject,
};
use std::{cmp::max, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc};
#[macro_use]
mod macros;
mod create;
mod framebuffer;
#[derive(Debug)]
pub struct RenderPass {
handle: ash::vk::RenderPass,
device: Arc<Device>,
id: NonZeroU64,
attachments: Vec<AttachmentDescription>,
subpasses: Vec<SubpassDescription>,
dependencies: Vec<SubpassDependency>,
correlated_view_masks: Vec<u32>,
granularity: [u32; 2],
views_used: u32,
}
impl RenderPass {
pub fn new(
device: Arc<Device>,
mut create_info: RenderPassCreateInfo,
) -> Result<Arc<RenderPass>, RenderPassCreationError> {
let views_used = Self::validate(&device, &mut create_info)?;
let handle = unsafe {
if device.api_version() >= Version::V1_2
|| device.enabled_extensions().khr_create_renderpass2
{
Self::create_v2(&device, &create_info)?
} else {
Self::create_v1(&device, &create_info)?
}
};
let RenderPassCreateInfo {
attachments,
subpasses,
dependencies,
correlated_view_masks,
_ne: _,
} = create_info;
let granularity = unsafe { Self::get_granularity(&device, handle) };
Ok(Arc::new(RenderPass {
handle,
device,
id: Self::next_id(),
attachments,
subpasses,
dependencies,
correlated_view_masks,
granularity,
views_used,
}))
}
unsafe fn get_granularity(device: &Arc<Device>, handle: ash::vk::RenderPass) -> [u32; 2] {
let fns = device.fns();
let mut out = MaybeUninit::uninit();
(fns.v1_0.get_render_area_granularity)(device.handle(), handle, out.as_mut_ptr());
let out = out.assume_init();
debug_assert_ne!(out.width, 0);
debug_assert_ne!(out.height, 0);
[out.width, out.height]
}
#[inline]
pub fn empty_single_pass(
device: Arc<Device>,
) -> Result<Arc<RenderPass>, RenderPassCreationError> {
RenderPass::new(
device,
RenderPassCreateInfo {
subpasses: vec![SubpassDescription::default()],
..Default::default()
},
)
}
#[inline]
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::RenderPass,
create_info: RenderPassCreateInfo,
) -> Result<Arc<RenderPass>, RenderPassCreationError> {
let views_used = create_info
.subpasses
.iter()
.map(|subpass| u32::BITS - subpass.view_mask.leading_zeros())
.max()
.unwrap();
let granularity = Self::get_granularity(&device, handle);
let RenderPassCreateInfo {
attachments,
subpasses,
dependencies,
correlated_view_masks,
_ne: _,
} = create_info;
Ok(Arc::new(RenderPass {
handle,
device,
id: Self::next_id(),
attachments,
subpasses,
dependencies,
correlated_view_masks,
granularity,
views_used,
}))
}
#[inline]
pub fn attachments(&self) -> &[AttachmentDescription] {
&self.attachments
}
#[inline]
pub fn subpasses(&self) -> &[SubpassDescription] {
&self.subpasses
}
#[inline]
pub fn dependencies(&self) -> &[SubpassDependency] {
&self.dependencies
}
#[inline]
pub fn correlated_view_masks(&self) -> &[u32] {
&self.correlated_view_masks
}
#[inline]
pub fn views_used(&self) -> u32 {
self.views_used
}
#[inline]
pub fn granularity(&self) -> [u32; 2] {
self.granularity
}
#[inline]
pub fn first_subpass(self: Arc<Self>) -> Subpass {
Subpass {
render_pass: self,
subpass_id: 0, }
}
pub fn is_compatible_with(&self, other: &RenderPass) -> bool {
if self == other {
return true;
}
let Self {
handle: _,
device: _,
id: _,
attachments: attachments1,
subpasses: subpasses1,
dependencies: dependencies1,
correlated_view_masks: correlated_view_masks1,
granularity: _,
views_used: _,
} = self;
let Self {
handle: _,
device: _,
id: _,
attachments: attachments2,
subpasses: subpasses2,
dependencies: dependencies2,
correlated_view_masks: correlated_view_masks2,
granularity: _,
views_used: _,
} = other;
if attachments1.len() != attachments2.len() {
return false;
}
if !attachments1
.iter()
.zip(attachments2)
.all(|(attachment_desc1, attachment_desc2)| {
let AttachmentDescription {
format: format1,
samples: samples1,
load_op: _,
store_op: _,
stencil_load_op: _,
stencil_store_op: _,
initial_layout: _,
final_layout: _,
_ne: _,
} = attachment_desc1;
let AttachmentDescription {
format: format2,
samples: samples2,
load_op: _,
store_op: _,
stencil_load_op: _,
stencil_store_op: _,
initial_layout: _,
final_layout: _,
_ne: _,
} = attachment_desc2;
format1 == format2 && samples1 == samples2
})
{
return false;
}
let are_atch_refs_compatible = |atch_ref1, atch_ref2| match (atch_ref1, atch_ref2) {
(None, None) => true,
(Some(atch_ref1), Some(atch_ref2)) => {
let &AttachmentReference {
attachment: attachment1,
layout: _,
aspects: aspects1,
_ne: _,
} = atch_ref1;
let AttachmentDescription {
format: format1,
samples: samples1,
load_op: _,
store_op: _,
stencil_load_op: _,
stencil_store_op: _,
initial_layout: _,
final_layout: _,
_ne: _,
} = &attachments1[attachment1 as usize];
let &AttachmentReference {
attachment: attachment2,
layout: _,
aspects: aspects2,
_ne: _,
} = atch_ref2;
let AttachmentDescription {
format: format2,
samples: samples2,
load_op: _,
store_op: _,
stencil_load_op: _,
stencil_store_op: _,
initial_layout: _,
final_layout: _,
_ne: _,
} = &attachments2[attachment2 as usize];
format1 == format2 && samples1 == samples2 && aspects1 == aspects2
}
_ => false,
};
if subpasses1.len() != subpasses2.len() {
return false;
}
if !(subpasses1.iter())
.zip(subpasses2.iter())
.all(|(subpass1, subpass2)| {
let SubpassDescription {
view_mask: view_mask1,
input_attachments: input_attachments1,
color_attachments: color_attachments1,
resolve_attachments: resolve_attachments1,
depth_stencil_attachment: depth_stencil_attachment1,
preserve_attachments: _,
_ne: _,
} = subpass1;
let SubpassDescription {
view_mask: view_mask2,
input_attachments: input_attachments2,
color_attachments: color_attachments2,
resolve_attachments: resolve_attachments2,
depth_stencil_attachment: depth_stencil_attachment2,
preserve_attachments: _,
_ne: _,
} = subpass2;
if !(0..max(input_attachments1.len(), input_attachments2.len())).all(|i| {
are_atch_refs_compatible(
input_attachments1.get(i).and_then(|x| x.as_ref()),
input_attachments2.get(i).and_then(|x| x.as_ref()),
)
}) {
return false;
}
if !(0..max(color_attachments1.len(), color_attachments2.len())).all(|i| {
are_atch_refs_compatible(
color_attachments1.get(i).and_then(|x| x.as_ref()),
color_attachments2.get(i).and_then(|x| x.as_ref()),
)
}) {
return false;
}
if subpasses1.len() > 1
&& !(0..max(resolve_attachments1.len(), resolve_attachments2.len())).all(|i| {
are_atch_refs_compatible(
resolve_attachments1.get(i).and_then(|x| x.as_ref()),
resolve_attachments2.get(i).and_then(|x| x.as_ref()),
)
})
{
return false;
}
if !are_atch_refs_compatible(
depth_stencil_attachment1.as_ref(),
depth_stencil_attachment2.as_ref(),
) {
return false;
}
if view_mask1 != view_mask2 {
return false;
}
true
})
{
return false;
}
if dependencies1 != dependencies2 {
return false;
}
if correlated_view_masks1 != correlated_view_masks2 {
return false;
}
true
}
pub fn is_compatible_with_shader(
&self,
subpass: u32,
shader_interface: &ShaderInterface,
) -> bool {
let subpass_descr = match self.subpasses.get(subpass as usize) {
Some(s) => s,
None => return false,
};
for element in shader_interface.elements() {
assert!(!element.ty.is_64bit); let location_range = element.location..element.location + element.ty.num_locations();
for location in location_range {
let attachment_id = match subpass_descr.color_attachments.get(location as usize) {
Some(Some(atch_ref)) => atch_ref.attachment,
_ => return false,
};
let _attachment_desc = &self.attachments[attachment_id as usize];
}
}
true
}
}
impl Drop for RenderPass {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
(fns.v1_0.destroy_render_pass)(self.device.handle(), self.handle, ptr::null());
}
}
}
unsafe impl VulkanObject for RenderPass {
type Handle = ash::vk::RenderPass;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl DeviceOwned for RenderPass {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
crate::impl_id_counter!(RenderPass);
#[derive(Debug, Clone)]
pub struct Subpass {
render_pass: Arc<RenderPass>,
subpass_id: u32,
}
impl Subpass {
#[inline]
pub fn from(render_pass: Arc<RenderPass>, id: u32) -> Option<Subpass> {
if (id as usize) < render_pass.subpasses().len() {
Some(Subpass {
render_pass,
subpass_id: id,
})
} else {
None
}
}
#[inline]
pub fn render_pass(&self) -> &Arc<RenderPass> {
&self.render_pass
}
#[inline]
pub fn index(&self) -> u32 {
self.subpass_id
}
#[inline]
pub fn subpass_desc(&self) -> &SubpassDescription {
&self.render_pass.subpasses()[self.subpass_id as usize]
}
#[inline]
pub fn is_last_subpass(&self) -> bool {
self.subpass_id as usize == self.render_pass.subpasses().len() - 1
}
#[inline]
pub fn next_subpass(&mut self) {
let next_id = self.subpass_id + 1;
assert!((next_id as usize) < self.render_pass.subpasses().len());
self.subpass_id = next_id;
}
#[inline]
fn attachment_desc(&self, atch_num: u32) -> &AttachmentDescription {
&self.render_pass.attachments()[atch_num as usize]
}
#[inline]
pub fn num_color_attachments(&self) -> u32 {
self.subpass_desc().color_attachments.len() as u32
}
#[inline]
pub fn has_depth(&self) -> bool {
let subpass_desc = self.subpass_desc();
let atch_num = match &subpass_desc.depth_stencil_attachment {
Some(atch_ref) => atch_ref.attachment,
None => return false,
};
self.attachment_desc(atch_num)
.format
.map_or(false, |f| f.aspects().depth)
}
#[inline]
pub fn has_writable_depth(&self) -> bool {
let subpass_desc = self.subpass_desc();
let atch_num = match &subpass_desc.depth_stencil_attachment {
Some(atch_ref) => {
if matches!(
atch_ref.layout,
ImageLayout::DepthStencilReadOnlyOptimal
| ImageLayout::DepthReadOnlyStencilAttachmentOptimal
) {
return false;
}
atch_ref.attachment
}
None => return false,
};
self.attachment_desc(atch_num)
.format
.map_or(false, |f| f.aspects().depth)
}
#[inline]
pub fn has_stencil(&self) -> bool {
let subpass_desc = self.subpass_desc();
let atch_num = match &subpass_desc.depth_stencil_attachment {
Some(atch_ref) => atch_ref.attachment,
None => return false,
};
self.attachment_desc(atch_num)
.format
.map_or(false, |f| f.aspects().stencil)
}
#[inline]
pub fn has_writable_stencil(&self) -> bool {
let subpass_desc = self.subpass_desc();
let atch_num = match &subpass_desc.depth_stencil_attachment {
Some(atch_ref) => {
if matches!(
atch_ref.layout,
ImageLayout::DepthStencilReadOnlyOptimal
| ImageLayout::DepthAttachmentStencilReadOnlyOptimal
) {
return false;
}
atch_ref.attachment
}
None => return false,
};
self.attachment_desc(atch_num)
.format
.map_or(false, |f| f.aspects().stencil)
}
#[inline]
pub fn num_samples(&self) -> Option<SampleCount> {
let subpass_desc = self.subpass_desc();
subpass_desc
.color_attachments
.iter()
.flatten()
.chain(subpass_desc.depth_stencil_attachment.iter())
.filter_map(|atch_ref| {
self.render_pass
.attachments()
.get(atch_ref.attachment as usize)
})
.next()
.map(|atch_desc| atch_desc.samples)
}
#[inline]
pub fn is_compatible_with(&self, shader_interface: &ShaderInterface) -> bool {
self.render_pass
.is_compatible_with_shader(self.subpass_id, shader_interface)
}
}
impl From<Subpass> for (Arc<RenderPass>, u32) {
#[inline]
fn from(value: Subpass) -> (Arc<RenderPass>, u32) {
(value.render_pass, value.subpass_id)
}
}
#[derive(Clone, Debug)]
pub struct RenderPassCreateInfo {
pub attachments: Vec<AttachmentDescription>,
pub subpasses: Vec<SubpassDescription>,
pub dependencies: Vec<SubpassDependency>,
pub correlated_view_masks: Vec<u32>,
pub _ne: crate::NonExhaustive,
}
impl Default for RenderPassCreateInfo {
#[inline]
fn default() -> Self {
Self {
attachments: Vec::new(),
subpasses: Vec::new(),
dependencies: Vec::new(),
correlated_view_masks: Vec::new(),
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct AttachmentDescription {
pub format: Option<Format>,
pub samples: SampleCount,
pub load_op: LoadOp,
pub store_op: StoreOp,
pub stencil_load_op: LoadOp,
pub stencil_store_op: StoreOp,
pub initial_layout: ImageLayout,
pub final_layout: ImageLayout,
pub _ne: crate::NonExhaustive,
}
impl Default for AttachmentDescription {
#[inline]
fn default() -> Self {
Self {
format: None,
samples: SampleCount::Sample1,
load_op: LoadOp::DontCare,
store_op: StoreOp::DontCare,
stencil_load_op: LoadOp::DontCare,
stencil_store_op: StoreOp::DontCare,
initial_layout: ImageLayout::Undefined,
final_layout: ImageLayout::Undefined,
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Debug, Clone)]
pub struct SubpassDescription {
pub view_mask: u32,
pub input_attachments: Vec<Option<AttachmentReference>>,
pub color_attachments: Vec<Option<AttachmentReference>>,
pub resolve_attachments: Vec<Option<AttachmentReference>>,
pub depth_stencil_attachment: Option<AttachmentReference>,
pub preserve_attachments: Vec<u32>,
pub _ne: crate::NonExhaustive,
}
impl Default for SubpassDescription {
#[inline]
fn default() -> Self {
Self {
view_mask: 0,
color_attachments: Vec::new(),
depth_stencil_attachment: None,
input_attachments: Vec::new(),
resolve_attachments: Vec::new(),
preserve_attachments: Vec::new(),
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Debug)]
pub struct AttachmentReference {
pub attachment: u32,
pub layout: ImageLayout,
pub aspects: ImageAspects,
pub _ne: crate::NonExhaustive,
}
impl Default for AttachmentReference {
#[inline]
fn default() -> Self {
Self {
attachment: 0,
layout: ImageLayout::Undefined,
aspects: ImageAspects::empty(),
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SubpassDependency {
pub src_subpass: Option<u32>,
pub dst_subpass: Option<u32>,
pub src_stages: PipelineStages,
pub dst_stages: PipelineStages,
pub src_access: AccessFlags,
pub dst_access: AccessFlags,
pub by_region: bool,
pub view_local: Option<i32>,
pub _ne: crate::NonExhaustive,
}
impl Default for SubpassDependency {
#[inline]
fn default() -> Self {
Self {
src_subpass: None,
dst_subpass: None,
src_stages: PipelineStages::empty(),
dst_stages: PipelineStages::empty(),
src_access: AccessFlags::empty(),
dst_access: AccessFlags::empty(),
by_region: false,
view_local: None,
_ne: crate::NonExhaustive(()),
}
}
}
vulkan_enum! {
#[non_exhaustive]
LoadOp = AttachmentLoadOp(i32);
Load = LOAD,
Clear = CLEAR,
DontCare = DONT_CARE,
}
vulkan_enum! {
#[non_exhaustive]
StoreOp = AttachmentStoreOp(i32);
Store = STORE,
DontCare = DONT_CARE,
}
vulkan_enum! {
#[non_exhaustive]
ResolveMode = ResolveModeFlags(u32);
SampleZero = SAMPLE_ZERO,
Average = AVERAGE,
Min = MIN,
Max = MAX,
}
vulkan_bitflags! {
#[non_exhaustive]
ResolveModes = ResolveModeFlags(u32);
sample_zero = SAMPLE_ZERO,
average = AVERAGE,
min = MIN,
max = MAX,
}
impl ResolveModes {
#[inline]
pub fn contains_mode(&self, mode: ResolveMode) -> bool {
match mode {
ResolveMode::SampleZero => self.sample_zero,
ResolveMode::Average => self.average,
ResolveMode::Min => self.min,
ResolveMode::Max => self.max,
}
}
}
#[cfg(test)]
mod tests {
use crate::{
format::Format,
render_pass::{RenderPass, 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().properties().max_color_attachments >= 10 {
return; }
let rp = single_pass_renderpass! {
device,
attachments: {
a1: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, },
a2: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, },
a3: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, },
a4: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, },
a5: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, },
a6: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, },
a7: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, },
a8: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, },
a9: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, },
a10: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }
},
pass: {
color: [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10],
depth_stencil: {}
}
};
match rp {
Err(RenderPassCreationError::SubpassMaxColorAttachmentsExceeded { .. }) => (),
_ => panic!(),
}
}
#[test]
fn non_zero_granularity() {
let (device, _) = gfx_dev_and_queue!();
let rp = single_pass_renderpass! {
device,
attachments: {
a: { load: Clear, store: DontCare, format: Format::R8G8B8A8_UNORM, samples: 1, }
},
pass: {
color: [a],
depth_stencil: {}
}
}
.unwrap();
let granularity = rp.granularity();
assert_ne!(granularity[0], 0);
assert_ne!(granularity[1], 0);
}
}