blob: 35f57869032ab1c3c72a70a429f94aae224d6e26 [file] [log] [blame]
// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
//! Description of the steps of the rendering process, and the images used as input or output.
//!
//! # Render passes and framebuffers
//!
//! There are two concepts in Vulkan:
//!
//! - A *render pass* describes the overall process of drawing a frame. It is subdivided into one
//! or more subpasses.
//! - A *framebuffer* contains the list of image views that are attached during the drawing of
//! each subpass.
//!
//! Render passes are typically created at initialization only (for example during a loading
//! screen) because they can be costly, while framebuffers can be created and destroyed either at
//! initialization or during the frame.
//!
//! Consequently you can create graphics pipelines from a render pass object alone.
//! A `Framebuffer` object is only needed when you actually add draw commands to a command buffer.
pub use self::{
create::RenderPassCreationError,
framebuffer::{Framebuffer, FramebufferCreateInfo, FramebufferCreationError},
};
use crate::{
device::{Device, DeviceOwned},
format::Format,
image::{ImageAspects, ImageLayout, SampleCount},
macros::{impl_id_counter, vulkan_bitflags_enum, vulkan_enum},
shader::ShaderInterface,
sync::{AccessFlags, DependencyFlags, PipelineStages},
Version, VulkanObject,
};
use std::{cmp::max, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc};
#[macro_use]
mod macros;
mod create;
mod framebuffer;
/// An object representing the discrete steps in which rendering is done.
///
/// A render pass in Vulkan is made up of three parts:
/// - A list of attachments, which are image views that are inputs, outputs or intermediate stages
/// in the rendering process.
/// - One or more subpasses, which are the steps in which the rendering process, takes place,
/// and the attachments that are used for each step.
/// - Dependencies, which describe how the input and output data of each subpass is to be passed
/// from one subpass to the next.
///
/// ```
/// use vulkano::render_pass::{RenderPass, RenderPassCreateInfo, SubpassDescription};
///
/// # let device: std::sync::Arc<vulkano::device::Device> = return;
/// let render_pass = RenderPass::new(
/// device.clone(),
/// RenderPassCreateInfo {
/// subpasses: vec![SubpassDescription::default()],
/// ..Default::default()
/// },
/// ).unwrap();
/// ```
///
/// This example creates a render pass with no attachment and one single subpass that doesn't draw
/// on anything. While it's sometimes useful, most of the time it's not what you want.
///
/// The easiest way to create a "real" render pass is to use the `single_pass_renderpass!` macro.
///
/// ```
/// # #[macro_use] extern crate vulkano;
/// # fn main() {
/// # let device: std::sync::Arc<vulkano::device::Device> = return;
/// use vulkano::format::Format;
///
/// let render_pass = single_pass_renderpass!(
/// device.clone(),
/// attachments: {
/// // `foo` is a custom name we give to the first and only attachment.
/// foo: {
/// load: Clear,
/// store: Store,
/// format: Format::R8G8B8A8_UNORM,
/// samples: 1,
/// },
/// },
/// pass: {
/// color: [foo], // Repeat the attachment name here.
/// depth_stencil: {},
/// },
/// )
/// .unwrap();
/// # }
/// ```
///
/// See the documentation of the macro for more details. TODO: put link here
#[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>,
attachment_uses: Vec<Option<AttachmentUse>>,
granularity: [u32; 2],
views_used: u32,
}
impl RenderPass {
/// Creates a new `RenderPass`.
///
/// # Panics
///
/// - Panics if `create_info.subpasses` is empty.
/// - Panics if any element of `create_info.attachments` has a `format` of `None`.
pub fn new(
device: Arc<Device>,
mut create_info: RenderPassCreateInfo,
) -> Result<Arc<RenderPass>, RenderPassCreationError> {
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)?
}
};
unsafe { Ok(Self::from_handle(device, handle, create_info)) }
}
/// Builds a render pass with one subpass and no attachment.
///
/// This method is useful for quick tests.
#[inline]
pub fn empty_single_pass(
device: Arc<Device>,
) -> Result<Arc<RenderPass>, RenderPassCreationError> {
RenderPass::new(
device,
RenderPassCreateInfo {
subpasses: vec![SubpassDescription::default()],
..Default::default()
},
)
}
/// Creates a new `RenderPass` from a raw object handle.
///
/// # Safety
///
/// - `handle` must be a valid Vulkan object handle created from `device`.
/// - `create_info` must match the info used to create the object.
#[inline]
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::RenderPass,
create_info: RenderPassCreateInfo,
) -> Arc<RenderPass> {
let RenderPassCreateInfo {
attachments,
subpasses,
dependencies,
correlated_view_masks,
_ne: _,
} = create_info;
let granularity = Self::get_granularity(&device, handle);
let mut attachment_uses: Vec<Option<AttachmentUse>> = vec![None; attachments.len()];
let mut views_used = 0;
for (index, subpass_desc) in subpasses.iter().enumerate() {
let index = index as u32;
let &SubpassDescription {
view_mask,
ref input_attachments,
ref color_attachments,
ref resolve_attachments,
ref depth_stencil_attachment,
..
} = subpass_desc;
for atch_ref in (input_attachments.iter().flatten())
.chain(color_attachments.iter().flatten())
.chain(resolve_attachments.iter().flatten())
.chain(depth_stencil_attachment.iter())
{
match &mut attachment_uses[atch_ref.attachment as usize] {
Some(attachment_use) => attachment_use.last_use_subpass = index,
attachment_use @ None => {
*attachment_use = Some(AttachmentUse {
first_use_subpass: index,
last_use_subpass: index,
})
}
}
}
views_used = max(views_used, u32::BITS - view_mask.leading_zeros());
}
Arc::new(RenderPass {
handle,
device,
id: Self::next_id(),
attachments,
subpasses,
dependencies,
correlated_view_masks,
attachment_uses,
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]
}
/// Returns the attachments of the render pass.
#[inline]
pub fn attachments(&self) -> &[AttachmentDescription] {
&self.attachments
}
/// Returns the subpasses of the render pass.
#[inline]
pub fn subpasses(&self) -> &[SubpassDescription] {
&self.subpasses
}
/// Returns the dependencies of the render pass.
#[inline]
pub fn dependencies(&self) -> &[SubpassDependency] {
&self.dependencies
}
/// Returns the correlated view masks of the render pass.
#[inline]
pub fn correlated_view_masks(&self) -> &[u32] {
&self.correlated_view_masks
}
/// If the render pass has multiview enabled, returns the number of views used by the render
/// pass. Returns 0 if multiview is not enabled.
#[inline]
pub fn views_used(&self) -> u32 {
self.views_used
}
/// Returns the granularity of this render pass.
///
/// If the render area of a render pass in a command buffer is a multiple of this granularity,
/// then the performance will be optimal. Performances are always optimal for render areas
/// that cover the whole framebuffer.
#[inline]
pub fn granularity(&self) -> [u32; 2] {
self.granularity
}
/// Returns the first subpass of the render pass.
#[inline]
pub fn first_subpass(self: Arc<Self>) -> Subpass {
Subpass {
render_pass: self,
subpass_id: 0, // Guaranteed to exist
}
}
/// Returns `true` if this render pass is compatible with the other render pass,
/// as defined in the [`Render Pass Compatibility` section of the Vulkan specs](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap8.html#renderpass-compatibility).
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,
attachment_uses: _,
granularity: _,
views_used: _,
} = self;
let Self {
handle: _,
device: _,
id: _,
attachments: attachments2,
subpasses: subpasses2,
dependencies: dependencies2,
attachment_uses: _,
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
}
/// Returns `true` if the subpass of this description is compatible with the shader's fragment
/// output definition.
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); // TODO: implement
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];
// FIXME: compare formats depending on the number of components and data type
/*if attachment_desc.format != element.format {
return false;
}*/
}
}
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
}
}
impl_id_counter!(RenderPass);
/// Represents a subpass within a `RenderPass` object.
///
/// This struct doesn't correspond to anything in Vulkan. It is simply an equivalent to a
/// tuple of a render pass and subpass index. Contrary to a tuple, however, the existence of the
/// subpass is checked when the object is created. When you have a `Subpass` you are guaranteed
/// that the given subpass does exist.
#[derive(Debug, Clone)]
pub struct Subpass {
render_pass: Arc<RenderPass>,
subpass_id: u32,
}
impl Subpass {
/// Returns a handle that represents a subpass of a render pass.
#[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
}
}
/// Returns the render pass of this subpass.
#[inline]
pub fn render_pass(&self) -> &Arc<RenderPass> {
&self.render_pass
}
/// Returns the index of this subpass within the renderpass.
#[inline]
pub fn index(&self) -> u32 {
self.subpass_id
}
/// Returns the subpass description for this subpass.
#[inline]
pub fn subpass_desc(&self) -> &SubpassDescription {
&self.render_pass.subpasses()[self.subpass_id as usize]
}
/// Returns whether this subpass is the last one in the render pass. If `true` is returned,
/// calling `next_subpass` will panic.
#[inline]
pub fn is_last_subpass(&self) -> bool {
self.subpass_id as usize == self.render_pass.subpasses().len() - 1
}
/// Advances to the next subpass after this one.
///
/// # Panics
///
/// - Panics if there are no more render passes.
#[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]
}
/// Returns the number of color attachments in this subpass.
#[inline]
pub fn num_color_attachments(&self) -> u32 {
self.subpass_desc().color_attachments.len() as u32
}
/// Returns true if the subpass has a depth attachment or a depth-stencil attachment.
#[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().intersects(ImageAspects::DEPTH))
}
/// Returns true if the subpass has a depth attachment or a depth-stencil attachment whose
/// layout does not have a read-only depth layout.
#[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().intersects(ImageAspects::DEPTH))
}
/// Returns true if the subpass has a stencil attachment or a depth-stencil attachment.
#[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().intersects(ImageAspects::STENCIL))
}
/// Returns true if the subpass has a stencil attachment or a depth-stencil attachment whose
/// layout does not have a read-only stencil layout.
#[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().intersects(ImageAspects::STENCIL))
}
/// Returns the number of samples in the color and/or depth/stencil attachments. Returns `None`
/// if there is no such attachment in this subpass.
#[inline]
pub fn num_samples(&self) -> Option<SampleCount> {
let subpass_desc = self.subpass_desc();
// TODO: chain input attachments as well?
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)
}
/// Returns `true` if this subpass is compatible with the fragment output definition.
// TODO: return proper error
#[inline]
pub fn is_compatible_with(&self, shader_interface: &ShaderInterface) -> bool {
self.render_pass
.is_compatible_with_shader(self.subpass_id, shader_interface)
}
pub(crate) fn load_op(&self, attachment_index: u32) -> Option<LoadOp> {
self.render_pass.attachment_uses[attachment_index as usize]
.as_ref()
.and_then(|attachment_use| {
(attachment_use.first_use_subpass == self.subpass_id)
.then(|| self.render_pass.attachments[attachment_index as usize].load_op)
})
}
pub(crate) fn store_op(&self, attachment_index: u32) -> Option<StoreOp> {
self.render_pass.attachment_uses[attachment_index as usize]
.as_ref()
.and_then(|attachment_use| {
(attachment_use.last_use_subpass == self.subpass_id)
.then(|| self.render_pass.attachments[attachment_index as usize].store_op)
})
}
pub(crate) fn stencil_load_op(&self, attachment_index: u32) -> Option<LoadOp> {
self.render_pass.attachment_uses[attachment_index as usize]
.as_ref()
.and_then(|attachment_use| {
(attachment_use.first_use_subpass == self.subpass_id).then(|| {
self.render_pass.attachments[attachment_index as usize].stencil_load_op
})
})
}
pub(crate) fn stencil_store_op(&self, attachment_index: u32) -> Option<StoreOp> {
self.render_pass.attachment_uses[attachment_index as usize]
.as_ref()
.and_then(|attachment_use| {
(attachment_use.last_use_subpass == self.subpass_id).then(|| {
self.render_pass.attachments[attachment_index as usize].stencil_store_op
})
})
}
}
impl From<Subpass> for (Arc<RenderPass>, u32) {
#[inline]
fn from(value: Subpass) -> (Arc<RenderPass>, u32) {
(value.render_pass, value.subpass_id)
}
}
/// Parameters to create a new `RenderPass`.
#[derive(Clone, Debug)]
pub struct RenderPassCreateInfo {
/// The attachments available for the render pass.
///
/// The default value is empty.
pub attachments: Vec<AttachmentDescription>,
/// The subpasses that make up this render pass.
///
/// A render pass must contain at least one subpass.
///
/// The default value is empty, which must be overridden.
pub subpasses: Vec<SubpassDescription>,
/// The dependencies between subpasses.
///
/// The default value is empty.
pub dependencies: Vec<SubpassDependency>,
/// If multiview rendering is being used (the subpasses have a nonzero `view_mask`),
/// this specifies sets of views that may be more efficient to render concurrently, for example
/// because they show the same geometry from almost the same perspective. This is an
/// optimization hint to the implementation, and does not affect the final result.
///
/// The value is a bitmask, so that that for example `0b11` means that the first two views are
/// highly correlated, and `0b101` means the first and third view are highly correlated. Each
/// view bit must appear in at most one element of the list.
///
/// If multiview rendering is not being used, the value must be empty.
///
/// The default value is empty.
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(()),
}
}
}
/// Describes an attachment that will be used in a render pass.
#[derive(Clone, Copy, Debug)]
pub struct AttachmentDescription {
/// The format of the image that is going to be bound.
///
/// The default value is `None`, which must be overridden.
pub format: Option<Format>,
/// The number of samples of the image that is going to be bound.
///
/// The default value is [`SampleCount::Sample1`].
pub samples: SampleCount,
/// What the implementation should do with the attachment at the start of the subpass that first
/// uses it.
///
/// The default value is [`LoadOp::DontCare`].
pub load_op: LoadOp,
/// What the implementation should do with the attachment at the end of the subpass that last
/// uses it.
///
/// The default value is [`StoreOp::DontCare`].
pub store_op: StoreOp,
/// The equivalent of `load_op` for the stencil component of the attachment, if any. Irrelevant
/// if there is no stencil component.
///
/// The default value is [`LoadOp::DontCare`].
pub stencil_load_op: LoadOp,
/// The equivalent of `store_op` for the stencil component of the attachment, if any. Irrelevant
/// if there is no stencil component.
///
/// The default value is [`StoreOp::DontCare`].
pub stencil_store_op: StoreOp,
/// The layout that the image must in at the start of the render pass.
///
/// The vulkano library will automatically switch to the correct layout if necessary, but it
/// is more efficient to set this to the correct value.
///
/// The default value is [`ImageLayout::Undefined`], which must be overridden.
pub initial_layout: ImageLayout,
/// The layout that the image will be transitioned to at the end of the render pass.
///
/// The default value is [`ImageLayout::Undefined`], which must be overridden.
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(()),
}
}
}
/// Describes one of the subpasses of a render pass.
///
/// A subpass can use zero or more attachments of various types. Attachment types of which there can
/// be multiple are listed in a `Vec` in this structure. The index in these `Vec`s corresponds to
/// the index used for that attachment type in the shader.
///
/// If a particular index is not used in the shader, it can be set to `None` in this structure.
/// This is useful if an unused index needs to be skipped but a higher index needs to be specified.
///
/// If an attachment is used more than once, i.e. a given `AttachmentReference::attachment` occurs
/// more than once in the `SubpassDescription`, then their `AttachmentReference::layout` must be
/// the same as well.
#[derive(Debug, Clone)]
pub struct SubpassDescription {
/// If not `0`, enables multiview rendering, and specifies the view indices that are rendered to
/// in this subpass. The value is a bitmask, so that that for example `0b11` will draw to the
/// first two views and `0b101` will draw to the first and third view.
///
/// If set to a nonzero value, it must be nonzero for all subpasses in the render pass, and the
/// [`multiview`](crate::device::Features::multiview) feature must be enabled on the device.
///
/// The default value is `0`.
pub view_mask: u32,
/// The attachments of the render pass that are to be used as input attachments in this subpass.
///
/// If an attachment is used here for the first time in this render pass, and it's is not also
/// used as a color or depth/stencil attachment in this subpass, then the attachment's `load_op`
/// must not be [`LoadOp::Clear`].
///
/// The default value is empty.
pub input_attachments: Vec<Option<AttachmentReference>>,
/// The attachments of the render pass that are to be used as color attachments in this subpass.
///
/// The number of color attachments must be less than the
/// [`max_color_attachments`](crate::device::Properties::max_color_attachments) limit of the
/// physical device. All color attachments must have the same `samples` value.
///
/// The default value is empty.
pub color_attachments: Vec<Option<AttachmentReference>>,
/// The attachments of the render pass that are to be used as resolve attachments in this
/// subpass.
///
/// This list must either be empty or have the same length as `color_attachments`. If it's not
/// empty, then each resolve attachment is paired with the color attachment of the same index.
/// The resolve attachments must all have a `samples` value of [`SampleCount::Sample1`], while
/// the color attachments must have a `samples` value other than [`SampleCount::Sample1`].
/// Each resolve attachment must have the same `format` as the corresponding color attachment.
///
/// The default value is empty.
pub resolve_attachments: Vec<Option<AttachmentReference>>,
/// The single attachment of the render pass that is to be used as depth-stencil attachment in
/// this subpass.
///
/// If set to `Some`, the referenced attachment must have the same `samples` value as those in
/// `color_attachments`.
///
/// The default value is `None`.
pub depth_stencil_attachment: Option<AttachmentReference>,
/// The indices of attachments of the render pass that will be preserved during this subpass.
///
/// The referenced attachments must not be used as any other attachment type in the subpass.
///
/// The default value is empty.
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(()),
}
}
}
/// A reference in a subpass description to a particular attachment of the render pass.
#[derive(Clone, Debug)]
pub struct AttachmentReference {
/// The number of the attachment being referred to.
///
/// The default value is `0`.
pub attachment: u32,
/// The image layout that the attachment should be transitioned to at the start of the subpass.
///
/// The layout is restricted by the type of attachment that an attachment is being used as. A
/// full listing of allowed layouts per type can be found in
/// [the Vulkan specification](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap8.html#attachment-type-imagelayout).
///
/// The default value is [`ImageLayout::Undefined`], which must be overridden.
pub layout: ImageLayout,
/// For references to input attachments, the aspects of the image that should be selected.
/// For attachment types other than input attachments, the value must be empty.
///
/// If empty, all aspects available in the input attachment's `format` will be selected.
/// If any fields are set, they must be aspects that are available in the `format` of the
/// attachment.
///
/// If the value is neither empty nor identical to the aspects of the `format`, the device API
/// version must be at least 1.1, or either the
/// [`khr_create_renderpass2`](crate::device::DeviceExtensions::khr_create_renderpass2) or the
/// [`khr_maintenance2`](crate::device::DeviceExtensions::khr_maintenance2) extensions must be
/// enabled on the device.
///
/// The default value is [`ImageAspects::empty()`].
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(()),
}
}
}
/// A dependency between two subpasses of a render pass.
///
/// The implementation is allowed to change the order of the subpasses within a render pass, unless
/// you specify that there exists a dependency between two subpasses (ie. the result of one will be
/// used as the input of another one). Subpass dependencies work similar to pipeline barriers,
/// except that they operate on whole subpasses instead of individual images.
///
/// If `src_subpass` and `dst_subpass` are equal, then this specifies a
/// [subpass self-dependency](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-pipeline-barriers-subpass-self-dependencies).
/// The `src_stages` must all be
/// [logically earlier in the pipeline](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-pipeline-stages-order)
/// than the `dst_stages`, and if they both contain a
/// [framebuffer-space stage](https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-framebuffer-regions),
/// then `by_region` must be activated.
///
/// If `src_subpass` or `dst_subpass` are set to `None`, this specifies an external
/// dependency. An external dependency specifies a dependency on commands that were submitted before
/// the render pass instance began (for `src_subpass`), or on commands that will be submitted
/// after the render pass instance ends (for `dst_subpass`). The values must not both be
/// `None`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SubpassDependency {
/// The index of the subpass that writes the data that `dst_subpass` is going to use.
///
/// `None` specifies an external dependency.
///
/// The default value is `None`.
pub src_subpass: Option<u32>,
/// The index of the subpass that reads the data that `src_subpass` wrote.
///
/// `None` specifies an external dependency.
///
/// The default value is `None`.
pub dst_subpass: Option<u32>,
/// The pipeline stages that must be finished on `src_subpass` before the
/// `dst_stages` of `dst_subpass` can start.
///
/// The default value is [`PipelineStages::empty()`].
pub src_stages: PipelineStages,
/// The pipeline stages of `dst_subpass` that must wait for the `src_stages` of
/// `src_subpass` to be finished. Stages that are earlier than the stages specified here can
/// start before the `src_stages` are finished.
///
/// The default value is [`PipelineStages::empty()`].
pub dst_stages: PipelineStages,
/// The way `src_subpass` accesses the attachments on which we depend.
///
/// The default value is [`AccessFlags::empty()`].
pub src_access: AccessFlags,
/// The way `dst_subpass` accesses the attachments on which we depend.
///
/// The default value is [`AccessFlags::empty()`].
pub dst_access: AccessFlags,
/// Dependency flags that modify behavior of the subpass dependency.
///
/// If a `src_subpass` equals `dst_subpass`, then:
/// - If `src_stages` and `dst_stages` both contain framebuffer-space stages,
/// this must include [`BY_REGION`].
/// - If the subpass's `view_mask` has more than one view,
/// this must include [`VIEW_LOCAL`].
///
/// The default value is [`DependencyFlags::empty()`].
///
/// [`BY_REGION`]: crate::sync::DependencyFlags::BY_REGION
/// [`VIEW_LOCAL`]: crate::sync::DependencyFlags::VIEW_LOCAL
pub dependency_flags: DependencyFlags,
/// If multiview rendering is being used (the subpasses have a nonzero `view_mask`), and
/// `dependency_flags` includes [`VIEW_LOCAL`], specifies an offset relative to the view index
/// of `dst_subpass`: each view `d` in `dst_subpass` depends on view `d + view_offset` in
/// `src_subpass`. If the source view index does not exist, the dependency is ignored for
/// that view.
///
/// If `dependency_flags` does not include [`VIEW_LOCAL`], or if `src_subpass` and
/// `dst_subpass` are the same, the value must be `0`.
///
/// The default value is `0`.
///
/// [`VIEW_LOCAL`]: crate::sync::DependencyFlags::VIEW_LOCAL
pub view_offset: 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(),
dependency_flags: DependencyFlags::empty(),
view_offset: 0,
_ne: crate::NonExhaustive(()),
}
}
}
vulkan_enum! {
#[non_exhaustive]
/// Describes what the implementation should do with an attachment at the start of the subpass.
LoadOp = AttachmentLoadOp(i32);
/// The content of the attachment will be loaded from memory. This is what you want if you want
/// to draw over something existing.
///
/// While this is the most intuitive option, it is also the slowest because it uses a lot of
/// memory bandwidth.
Load = LOAD,
/// The content of the attachment will be filled by the implementation with a uniform value
/// that you must provide when you start drawing.
///
/// This is what you usually use at the start of a frame, in order to reset the content of
/// the color, depth and/or stencil buffers.
Clear = CLEAR,
/// The attachment will have undefined content.
///
/// This is what you should use for attachments that you intend to entirely cover with draw
/// commands.
/// If you are going to fill the attachment with a uniform value, it is better to use `Clear`
/// instead.
DontCare = DONT_CARE,
/* TODO: enable
// TODO: document
None = NONE_EXT {
device_extensions: [ext_load_store_op_none],
},*/
}
vulkan_enum! {
#[non_exhaustive]
/// Describes what the implementation should do with an attachment after all the subpasses have
/// completed.
StoreOp = AttachmentStoreOp(i32);
/// The attachment will be stored. This is what you usually want.
///
/// While this is the most intuitive option, it is also slower than `DontCare` because it can
/// take time to write the data back to memory.
Store = STORE,
/// What happens is implementation-specific.
///
/// This is purely an optimization compared to `Store`. The implementation doesn't need to copy
/// from the internal cache to the memory, which saves memory bandwidth.
///
/// This doesn't mean that the data won't be copied, as an implementation is also free to not
/// use a cache and write the output directly in memory. In other words, the content of the
/// image will be undefined.
DontCare = DONT_CARE,
/* TODO: enable
// TODO: document
None = NONE {
api_version: V1_3,
device_extensions: [khr_dynamic_rendering, ext_load_store_op_none, qcom_render_pass_store_ops],
},*/
}
vulkan_bitflags_enum! {
#[non_exhaustive]
/// A set of [`ResolveMode`] values.
ResolveModes,
/// Possible resolve modes for attachments.
ResolveMode,
= ResolveModeFlags(u32);
/// The resolved sample is taken from sample number zero, the other samples are ignored.
///
/// This mode is supported for depth and stencil formats, and for color images with an integer
/// format.
SAMPLE_ZERO, SampleZero = SAMPLE_ZERO,
/// The resolved sample is calculated from the average of the samples.
///
/// This mode is supported for depth formats, and for color images with a non-integer format.
AVERAGE, Average = AVERAGE,
/// The resolved sample is calculated from the minimum of the samples.
///
/// This mode is supported for depth and stencil formats only.
MIN, Min = MIN,
/// The resolved sample is calculated from the maximum of the samples.
///
/// This mode is supported for depth and stencil formats only.
MAX, Max = MAX,
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct AttachmentUse {
first_use_subpass: u32,
last_use_subpass: u32,
}
#[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; // test ignored
}
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);
}
}