vulkan_lib/vulkan-rs/src/render_target/sub_pass.rs

442 lines
13 KiB
Rust

use crate::prelude::*;
use anyhow::Result;
use std::sync::{Arc, Mutex};
pub enum ClearValue {
Color([f32; 4]),
Depth(f32, u32),
}
pub struct CustomTarget {
pub usage: VkImageUsageFlagBits,
pub format: VkFormat,
pub clear_on_load: bool,
pub store_on_save: bool,
pub attach_sampler: bool,
pub use_as_input: bool,
pub clear_value: ClearValue,
}
impl CustomTarget {
pub fn depth() -> CustomTarget {
CustomTarget {
usage: VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT.into(),
format: VK_FORMAT_D16_UNORM,
clear_on_load: true,
store_on_save: false,
attach_sampler: false,
use_as_input: false,
clear_value: ClearValue::Depth(1.0, 0),
}
}
fn to_attachment_info(
&self,
device: &Arc<Device>,
queue: &Arc<Mutex<Queue>>,
width: u32,
height: u32,
sample_count: VkSampleCountFlags,
) -> Result<AttachmentInfo> {
let clear_operation = SubPassBuilder::clear_op(self.clear_on_load);
let store_operation = SubPassBuilder::store_op(self.store_on_save);
// set clear values
let clear_value = match self.clear_value {
ClearValue::Color(color) => VkClearValue::color(VkClearColorValue::float32(color)),
ClearValue::Depth(depth, stencil) => {
VkClearValue::depth_stencil(VkClearDepthStencilValue { depth, stencil })
}
};
// check for color attachment flag
let (format, aspect, description, usage, layout) =
if (self.usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0 {
// set color attachment
let description = VkAttachmentDescription::new(
0,
self.format,
sample_count,
clear_operation,
store_operation,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
if self.attach_sampler || self.use_as_input {
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
} else {
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
},
);
(
self.format,
VK_IMAGE_ASPECT_COLOR_BIT,
description,
AttachmentInfoUsage::Output,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
)
// check for depth attachment flag
} else if (self.usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0 {
// set depth attachment
let description = VkAttachmentDescription::new(
0,
self.format,
sample_count,
clear_operation,
store_operation,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
);
// take format and aspect mask
(
self.format,
VK_IMAGE_ASPECT_DEPTH_BIT,
description,
AttachmentInfoUsage::Depth,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
)
} else {
// TODO: add more as required
unimplemented!();
};
let mut image_builder = Image::empty(width, height, self.usage, sample_count)
.format(format)
.aspect_mask(aspect);
if self.attach_sampler {
image_builder = image_builder.attach_sampler(Sampler::nearest_sampler().build(device)?);
}
let image = image_builder.build(device, queue)?;
match aspect {
VK_IMAGE_ASPECT_DEPTH_BIT => {
Image::convert_layout(&image, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL)?
}
VK_IMAGE_ASPECT_COLOR_BIT => {
Image::convert_layout(&image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)?
}
_ => unimplemented!(),
}
Ok(AttachmentInfo {
images: vec![image],
clear_value,
layout,
description,
usage,
})
}
}
pub enum ResolveTarget {
CustomTarget(CustomTarget),
PreparedTargets(Vec<Arc<Image>>),
}
impl From<CustomTarget> for ResolveTarget {
fn from(custom_target: CustomTarget) -> Self {
Self::CustomTarget(custom_target)
}
}
impl From<Vec<Arc<Image>>> for ResolveTarget {
fn from(prepared_targets: Vec<Arc<Image>>) -> Self {
Self::PreparedTargets(prepared_targets)
}
}
impl<'a> From<&'a Vec<Arc<Image>>> for ResolveTarget {
fn from(prepared_targets: &'a Vec<Arc<Image>>) -> Self {
Self::PreparedTargets(prepared_targets.clone())
}
}
#[derive(Debug)]
pub struct InputAttachmentInfo {
pub sub_pass_index: usize,
pub input_indices: Vec<usize>,
}
pub struct SubPassBuilder<'a> {
width: u32,
height: u32,
sample_count: VkSampleCountFlags,
target_infos: Vec<CustomTarget>,
input_info: Option<InputAttachmentInfo>,
// (images, index, clear_color, clear_on_load)
prepared_targets: Option<(&'a [Arc<Image>], usize, [f32; 4], bool)>,
resolve_targets: Vec<ResolveTarget>,
output_usage: VkAccessFlagBits,
}
impl<'a> SubPassBuilder<'a> {
pub fn set_sample_count(mut self, sample_count: VkSampleCountFlags) -> Self {
self.sample_count = sample_count;
self
}
pub fn add_target_info(mut self, target: CustomTarget) -> Self {
self.target_infos.push(target);
self
}
pub fn set_input_attachment_info(mut self, input_info: InputAttachmentInfo) -> Self {
self.input_info = Some(input_info);
self
}
pub fn set_output_usage(mut self, output_usage: impl Into<VkAccessFlagBits>) -> Self {
self.output_usage = output_usage.into();
self
}
pub fn set_prepared_targets(
mut self,
prepared_targets: &'a [Arc<Image>],
target_index: usize,
clear_color: impl Into<[f32; 4]>,
clear_on_load: bool,
) -> Self {
self.prepared_targets = Some((
prepared_targets,
target_index,
clear_color.into(),
clear_on_load,
));
self
}
pub fn add_resolve_targets(mut self, resolve_target: impl Into<ResolveTarget>) -> Self {
self.resolve_targets.push(resolve_target.into());
self
}
pub fn build(self, device: &Arc<Device>, queue: &Arc<Mutex<Queue>>) -> Result<SubPass> {
let attachments = self.create_images(device, queue)?;
Ok(SubPass {
extent: VkExtent2D {
width: self.width,
height: self.height,
},
input_info: self.input_info,
attachments,
output_usage: self.output_usage,
})
}
#[inline]
fn create_images(
&self,
device: &Arc<Device>,
queue: &Arc<Mutex<Queue>>,
) -> Result<Vec<AttachmentInfo>> {
// check for correct sample count
let checked_sample_count = device.max_supported_sample_count(self.sample_count);
// throw an error if we don't use muultisampling and have an resolve target
if checked_sample_count == VK_SAMPLE_COUNT_1_BIT && !self.resolve_targets.is_empty() {
panic!("Sample count 1 and using resolve target is not supported");
}
let mut attachment_infos = Vec::new();
for target_info in self.target_infos.iter() {
attachment_infos.push(target_info.to_attachment_info(
device,
queue,
self.width,
self.height,
self.sample_count,
)?);
}
// insert prepared images
if let Some((prepared_images, index, clear_color, clear_on_load)) = self.prepared_targets {
let clear_operation = Self::clear_op(clear_on_load);
attachment_infos.insert(
index,
AttachmentInfo {
images: prepared_images.iter().map(|image| image.clone()).collect(),
clear_value: VkClearValue::color(VkClearColorValue::float32(clear_color)),
layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
description: VkAttachmentDescription::new(
0,
prepared_images[0].vk_format(),
VK_SAMPLE_COUNT_1_BIT,
clear_operation,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
prepared_images[0].image_layout(),
prepared_images[0].image_layout(),
),
usage: AttachmentInfoUsage::Output,
},
);
}
// add resolve target if possible
for resolve_target in self.resolve_targets.iter() {
match resolve_target {
ResolveTarget::CustomTarget(custom_target) => {
let mut attachment_info = custom_target.to_attachment_info(
device,
queue,
self.width,
self.height,
VK_SAMPLE_COUNT_1_BIT,
)?;
attachment_info.usage = AttachmentInfoUsage::Resolve;
attachment_infos.push(attachment_info);
}
ResolveTarget::PreparedTargets(prepared_targets) => {
attachment_infos.push(AttachmentInfo {
images: prepared_targets.iter().map(|image| image.clone()).collect(),
clear_value: VkClearValue::color(VkClearColorValue::float32([
0.0, 0.0, 0.0, 1.0,
])),
layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
description: VkAttachmentDescription::new(
0,
prepared_targets[0].vk_format(),
VK_SAMPLE_COUNT_1_BIT,
VK_ATTACHMENT_LOAD_OP_CLEAR,
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
prepared_targets[0].image_layout(),
),
usage: AttachmentInfoUsage::Resolve,
});
}
}
}
Ok(attachment_infos)
}
#[inline]
fn clear_op(clear_on_load: bool) -> VkAttachmentLoadOp {
if clear_on_load {
VK_ATTACHMENT_LOAD_OP_CLEAR
} else {
VK_ATTACHMENT_LOAD_OP_LOAD
}
}
#[inline]
fn store_op(store_on_save: bool) -> VkAttachmentStoreOp {
if store_on_save {
VK_ATTACHMENT_STORE_OP_STORE
} else {
VK_ATTACHMENT_STORE_OP_DONT_CARE
}
}
}
#[derive(Debug, Eq, PartialEq, Hash, Clone, Copy)]
pub enum AttachmentInfoUsage {
Depth,
Resolve,
Output,
}
#[derive(Debug)]
pub struct AttachmentInfo {
images: Vec<Arc<Image>>,
pub(crate) clear_value: VkClearValue,
pub(crate) layout: VkImageLayout,
pub(crate) description: VkAttachmentDescription,
pub(crate) usage: AttachmentInfoUsage,
}
impl AttachmentInfo {
pub fn image(&self, mut index: usize) -> &Arc<Image> {
debug_assert!(!self.images.is_empty());
if index >= self.images.len() {
index = self.images.len() - 1;
}
&self.images[index]
}
}
#[derive(Debug)]
pub struct SubPass {
extent: VkExtent2D,
input_info: Option<InputAttachmentInfo>,
attachments: Vec<AttachmentInfo>,
output_usage: VkAccessFlagBits,
}
impl SubPass {
pub fn builder<'a>(width: u32, height: u32) -> SubPassBuilder<'a> {
SubPassBuilder {
width,
height,
sample_count: VK_SAMPLE_COUNT_1_BIT,
input_info: None,
target_infos: Vec::new(),
prepared_targets: None,
resolve_targets: Vec::new(),
output_usage: VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
}
}
pub(crate) fn inputs(&self) -> Option<&InputAttachmentInfo> {
self.input_info.as_ref()
}
pub(crate) fn output_usage(&self) -> VkAccessFlagBits {
self.output_usage
}
pub fn extent(&self) -> VkExtent2D {
self.extent
}
pub fn attachments(&self) -> &[AttachmentInfo] {
&self.attachments
}
pub fn max_images_per_attachment(&self) -> usize {
let mut max_images = 0;
for attachment in self.attachments.iter() {
max_images = max_images.max(attachment.images.len());
}
max_images
}
}