pub use self::create::RenderPassCreationError;
pub use self::framebuffer::Framebuffer;
pub use self::framebuffer::FramebufferCreateInfo;
pub use self::framebuffer::FramebufferCreationError;
use crate::{
device::{Device, DeviceOwned},
format::{ClearValue, Format},
image::{ImageAspects, ImageLayout, SampleCount},
shader::ShaderInterface,
sync::{AccessFlags, PipelineStages},
Version, VulkanObject,
};
use std::{
hash::{Hash, Hasher},
mem::MaybeUninit,
ptr,
sync::Arc,
};
#[macro_use]
mod macros;
mod create;
mod framebuffer;
#[derive(Debug)]
pub struct RenderPass {
handle: ash::vk::RenderPass,
device: Arc<Device>,
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 {
let fns = device.fns();
let mut out = MaybeUninit::uninit();
fns.v1_0.get_render_area_granularity(
device.internal_object(),
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]
};
Ok(Arc::new(RenderPass {
handle,
device,
attachments,
subpasses,
dependencies,
correlated_view_masks,
granularity,
views_used,
}))
}
#[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 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.attachments().len() != other.attachments().len() {
return false;
}
for (my_atch, other_atch) in self.attachments.iter().zip(other.attachments.iter()) {
if !my_atch.is_compatible_with(&other_atch) {
return false;
}
}
return 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
}
pub fn convert_clear_values<I>(&self, values: I) -> impl Iterator<Item = ClearValue>
where
I: IntoIterator<Item = ClearValue>,
{
values.into_iter()
}
}
impl Drop for RenderPass {
#[inline]
fn drop(&mut self) {
unsafe {
let fns = self.device.fns();
fns.v1_0
.destroy_render_pass(self.device.internal_object(), self.handle, ptr::null());
}
}
}
unsafe impl VulkanObject for RenderPass {
type Object = ash::vk::RenderPass;
#[inline]
fn internal_object(&self) -> ash::vk::RenderPass {
self.handle
}
}
unsafe impl DeviceOwned for RenderPass {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl PartialEq for RenderPass {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle && self.device() == other.device()
}
}
impl Eq for RenderPass {}
impl Hash for RenderPass {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
self.device.hash(state);
}
}
#[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 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 try_next_subpass(&mut self) -> bool {
let next_id = self.subpass_id + 1;
if (next_id as usize) < self.render_pass.subpasses().len() {
self.subpass_id = next_id;
true
} else {
false
}
}
#[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 atch_ref.layout == ImageLayout::DepthStencilReadOnlyOptimal {
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 atch_ref.layout == ImageLayout::DepthStencilReadOnlyOptimal {
return false;
}
atch_ref.attachment
}
None => return false,
};
self.attachment_desc(atch_num)
.format
.map_or(false, |f| f.aspects().stencil)
}
#[inline]
pub fn has_depth_stencil_attachment(&self) -> bool {
let subpass_desc = self.subpass_desc();
match &subpass_desc.depth_stencil_attachment {
Some(atch_ref) => true,
None => false,
}
}
#[inline]
pub fn has_color_or_depth_stencil_attachment(&self) -> bool {
if self.num_color_attachments() >= 1 {
return true;
}
let subpass_desc = self.subpass_desc();
match &subpass_desc.depth_stencil_attachment {
Some(atch_ref) => true,
None => false,
}
}
#[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 render_pass(&self) -> &Arc<RenderPass> {
&self.render_pass
}
#[inline]
pub fn index(&self) -> u32 {
self.subpass_id
}
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(()),
}
}
}
impl AttachmentDescription {
#[inline]
pub fn is_compatible_with(&self, other: &AttachmentDescription) -> bool {
self.format == other.format && self.samples == other.samples
}
}
#[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::none(),
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Debug)]
pub struct SubpassDependency {
pub source_subpass: Option<u32>,
pub destination_subpass: Option<u32>,
pub source_stages: PipelineStages,
pub destination_stages: PipelineStages,
pub source_access: AccessFlags,
pub destination_access: AccessFlags,
pub by_region: bool,
pub view_local: Option<i32>,
pub _ne: crate::NonExhaustive,
}
impl Default for SubpassDependency {
#[inline]
fn default() -> Self {
Self {
source_subpass: None,
destination_subpass: None,
source_stages: PipelineStages::none(),
destination_stages: PipelineStages::none(),
source_access: AccessFlags::none(),
destination_access: AccessFlags::none(),
by_region: false,
view_local: None,
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(i32)]
#[non_exhaustive]
pub enum LoadOp {
Load = ash::vk::AttachmentLoadOp::LOAD.as_raw(),
Clear = ash::vk::AttachmentLoadOp::CLEAR.as_raw(),
DontCare = ash::vk::AttachmentLoadOp::DONT_CARE.as_raw(),
}
impl From<LoadOp> for ash::vk::AttachmentLoadOp {
#[inline]
fn from(val: LoadOp) -> Self {
Self::from_raw(val as i32)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[repr(i32)]
#[non_exhaustive]
pub enum StoreOp {
Store = ash::vk::AttachmentStoreOp::STORE.as_raw(),
DontCare = ash::vk::AttachmentStoreOp::DONT_CARE.as_raw(),
}
impl From<StoreOp> for ash::vk::AttachmentStoreOp {
#[inline]
fn from(val: StoreOp) -> Self {
Self::from_raw(val as i32)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u32)]
#[non_exhaustive]
pub enum ResolveMode {
None = ash::vk::ResolveModeFlags::NONE.as_raw(),
SampleZero = ash::vk::ResolveModeFlags::SAMPLE_ZERO.as_raw(),
Average = ash::vk::ResolveModeFlags::AVERAGE.as_raw(),
Min = ash::vk::ResolveModeFlags::MIN.as_raw(),
Max = ash::vk::ResolveModeFlags::MAX.as_raw(),
}
#[derive(Clone, Copy, Debug)]
pub struct ResolveModes {
pub none: bool,
pub sample_zero: bool,
pub average: bool,
pub min: bool,
pub max: bool,
}
impl From<ash::vk::ResolveModeFlags> for ResolveModes {
#[inline]
fn from(val: ash::vk::ResolveModeFlags) -> Self {
Self {
none: val.intersects(ash::vk::ResolveModeFlags::NONE),
sample_zero: val.intersects(ash::vk::ResolveModeFlags::SAMPLE_ZERO),
average: val.intersects(ash::vk::ResolveModeFlags::AVERAGE),
min: val.intersects(ash::vk::ResolveModeFlags::MIN),
max: val.intersects(ash::vk::ResolveModeFlags::MAX),
}
}
}
#[cfg(test)]
mod tests {
use crate::format::Format;
use crate::render_pass::RenderPass;
use crate::render_pass::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.clone(),
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.clone(),
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);
}
}