use ahash::{AHashMap, AHashSet, HashMap, HashMapExt};
use itertools::Itertools;
use std::sync::Arc;
use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer, RenderPassBeginInfo, SubpassBeginInfo, SubpassContents, SubpassEndInfo};
use vulkano::format::ClearValue;
use vulkano::image::{ImageLayout, SampleCount};
use vulkano::render_pass::{AttachmentDescription, AttachmentLoadOp, AttachmentReference, AttachmentStoreOp, RenderPass, RenderPassCreateInfo, Subpass, SubpassDependency, SubpassDescription};
use vulkano::sync::{AccessFlags, DependencyFlags, PipelineStages};
use vulkano::{Validated, VulkanError};
use crate::render::frame::EngineFramebuffer;
use crate::render::{EngineDevice, RenderTarget, RenderTargetExt, Renderer, VulkanoError};
pub trait EngineRenderPass: Send + Sync + 'static {
fn vk_render_pass(&self) -> &Arc<RenderPass>;
fn attachment_name_map(&self) -> HashMap<String, u32>;
fn build_commands(
&self,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
frame: &EngineFramebuffer,
) -> Result<(), VulkanoError>;
}
pub trait EngineRenderHandler: Send + Sync + 'static {
fn build_commands(
&self,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
) -> Result<(), VulkanoError>;
}
struct EngineRenderSubpass {
handlers: Vec<Box<dyn EngineRenderHandler>>,
}
impl EngineRenderSubpass {
fn build_commands(
&self,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
) -> Result<(), VulkanoError> {
for handler in &self.handlers {
handler.build_commands(builder)?;
}
Ok(())
}
}
struct StandardEngineRenderPass {
render_pass: Arc<RenderPass>,
subpasses: Vec<EngineRenderSubpass>,
attachment_name_map: HashMap<String, u32>,
clear_values: Vec<Option<ClearValue>>,
}
impl EngineRenderPass for StandardEngineRenderPass {
#[inline]
fn vk_render_pass(&self) -> &Arc<RenderPass> { &self.render_pass }
#[inline]
fn attachment_name_map(&self) -> HashMap<String, u32> { self.attachment_name_map.clone() }
fn build_commands(
&self,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
frame: &EngineFramebuffer,
) -> Result<(), VulkanoError> {
builder.begin_render_pass(
RenderPassBeginInfo {
clear_values: self.clear_values.clone(),
..RenderPassBeginInfo::framebuffer(frame.buffer().clone())
},
SubpassBeginInfo {
contents: SubpassContents::Inline,
..Default::default()
},
).unwrap();
let mut first = true;
for subpass in self.subpasses.iter() {
if !first {
builder.next_subpass(
SubpassEndInfo::default(),
SubpassBeginInfo {
contents: SubpassContents::Inline,
..Default::default()
},
).unwrap();
}
subpass.build_commands(builder)?;
first = false;
}
builder.end_render_pass(SubpassEndInfo::default()).unwrap();
Ok(())
}
}
type CreateHandlerFn = Box<dyn FnOnce(&Arc<Renderer>, &Subpass)
-> Box<dyn EngineRenderHandler>>;
struct SubpassInfo {
description: SubpassDescription,
create_handlers: Vec<CreateHandlerFn>,
attachments: AHashSet<u32>,
}
pub struct EngineRenderPassBuilder {
renderer: Arc<Renderer>,
attachments: Vec<AttachmentDescription>,
attachment_name_map: HashMap<String, u32>,
clear_values: Vec<Option<ClearValue>>,
subpass_infos: Vec<SubpassInfo>,
}
impl EngineRenderPassBuilder {
pub fn new(renderer: Arc<Renderer>) -> Self {
Self {
renderer,
attachments: vec![],
attachment_name_map: HashMap::new(),
clear_values: vec![],
subpass_infos: vec![],
}
}
fn attachment_ref(&self, name: &str) -> AttachmentReference {
let attachment = self.attachment_name_map.get(name).unwrap().clone();
AttachmentReference {
attachment,
..Default::default()
}
}
pub fn attachment(
mut self,
name: impl Into<String>,
atch: AttachmentDescription,
clear_value: Option<impl Into<ClearValue>>,
) -> Self {
let atch_num = self.attachments.len() as u32;
self.attachments.push(atch);
self.attachment_name_map.insert(name.into(), atch_num);
self.clear_values.push(clear_value.map(Into::into));
self
}
#[allow(unused_mut)]
pub fn final_color_attachment(
mut self,
name: impl Into<String>,
clear_value: impl Into<ClearValue>,
) -> Result<Self, Validated<VulkanError>> {
let format = self.renderer.target().format(self.renderer.device())?;
Ok(self.attachment(name.into(), AttachmentDescription {
format,
samples: SampleCount::try_from(1).unwrap(),
load_op: AttachmentLoadOp::Clear,
store_op: AttachmentStoreOp::Store,
..Default::default()
}, Some(clear_value)))
}
#[inline]
#[allow(unused_mut)]
pub fn begin_subpass(mut self) -> EngineRenderSubpassBuilder { EngineRenderSubpassBuilder::new(self) }
pub fn build(self) -> Result<Arc<dyn EngineRenderPass>, Validated<VulkanoError>> {
let mut attachment_to_subpass_map: AHashMap<u32, AHashSet<u32>> = AHashMap::new();
for (subpass_id, subpass_info) in self.subpass_infos.iter().enumerate() {
for attachment in &subpass_info.attachments {
attachment_to_subpass_map.entry(attachment.clone())
.or_default()
.insert(subpass_id as u32);
}
}
let dependencies = self.subpass_infos.iter().enumerate()
.map(|(dst_id, subpass_info)| {
let dst_id = dst_id as u32;
subpass_info.description.input_attachments.iter()
.flatten()
.map(|atch_ref| {
attachment_to_subpass_map[&atch_ref.attachment].iter().cloned()
})
.flatten()
.unique()
.map(move |src_id| SubpassDependency {
src_subpass: Some(src_id),
dst_subpass: Some(dst_id),
src_stages: PipelineStages::ALL_GRAPHICS,
dst_stages: PipelineStages::ALL_GRAPHICS,
src_access: AccessFlags::MEMORY_READ | AccessFlags::MEMORY_WRITE,
dst_access: AccessFlags::MEMORY_READ | AccessFlags::MEMORY_WRITE,
dependency_flags: DependencyFlags::BY_REGION,
..Default::default()
})
})
.flatten()
.collect::<Vec<_>>();
let (subpasses, handler_groups): (Vec<_>, Vec<_>)
= self.subpass_infos.into_iter()
.map(|subpass_info| (subpass_info.description, subpass_info.create_handlers))
.unzip();
let mut attachments = self.attachments;
for subpass in subpasses.iter() {
subpass.color_attachments.iter()
.chain(&subpass.color_resolve_attachments)
.chain(&[subpass.depth_stencil_attachment.clone()])
.chain(&[subpass.depth_stencil_resolve_attachment.clone()])
.chain(&subpass.input_attachments)
.flatten()
.for_each(|atch_ref| {
let attachment = &mut attachments[atch_ref.attachment as usize];
attachment.final_layout = atch_ref.layout;
if attachment.initial_layout == ImageLayout::Undefined {
attachment.initial_layout = attachment.final_layout;
}
});
}
let render_pass = RenderPass::new(
self.renderer.device().vk_device().clone(),
RenderPassCreateInfo {
attachments,
subpasses,
dependencies,
..Default::default()
}
).map_err(|v| v.map(Into::into))?;
let subpasses = handler_groups.into_iter().enumerate()
.map(|(idx, handlers)| {
let subpass = Subpass::from(render_pass.clone(), idx as u32).unwrap();
let handlers = handlers.into_iter()
.map(|handler| handler(&self.renderer, &subpass))
.collect::<Vec<_>>();
EngineRenderSubpass {
handlers,
}
}).collect::<Vec<_>>();
Ok(Arc::new(StandardEngineRenderPass {
render_pass,
subpasses,
attachment_name_map: self.attachment_name_map,
clear_values: self.clear_values,
}))
}
}
pub struct EngineRenderSubpassBuilder {
parent: EngineRenderPassBuilder,
color_attachments: Vec<Option<AttachmentReference>>,
depth_stencil_attachment: Option<AttachmentReference>,
input_attachments: Vec<Option<AttachmentReference>>,
handlers: Vec<CreateHandlerFn>,
}
impl EngineRenderSubpassBuilder {
fn new(parent: EngineRenderPassBuilder) -> Self {
Self {
parent,
color_attachments: vec![],
depth_stencil_attachment: None,
input_attachments: vec![],
handlers: vec![],
}
}
pub fn color_attachment<'a>(
mut self,
name: impl Into<&'a str>,
) -> Self {
let attachment = AttachmentReference {
layout: ImageLayout::ColorAttachmentOptimal,
..self.parent.attachment_ref(name.into())
};
self.color_attachments.push(Some(attachment));
self
}
pub fn depth_stencil_attachment<'a>(
mut self,
name: impl Into<&'a str>,
) -> Self {
let attachment = AttachmentReference {
layout: ImageLayout::DepthStencilAttachmentOptimal,
..self.parent.attachment_ref(name.into())
};
self.depth_stencil_attachment = Some(attachment);
self
}
pub fn input_attachment<'a>(
mut self,
name: impl Into<&'a str>,
) -> Self {
let attachment = AttachmentReference {
layout: ImageLayout::ShaderReadOnlyOptimal,
..self.parent.attachment_ref(name.into())
};
self.input_attachments.push(Some(attachment));
self
}
pub fn handler<H>(
mut self,
handler: impl (FnOnce(&Arc<Renderer>, &Subpass) -> H) + 'static,
) -> Self
where
H: EngineRenderHandler
{
self.handlers.push(Box::new(|renderer, subpass| {
Box::new(handler(renderer, subpass))
}));
self
}
pub fn end_subpass(mut self) -> EngineRenderPassBuilder {
let attachments = self.color_attachments.iter()
.chain(&[self.depth_stencil_attachment.clone()])
.flatten()
.map(|atch_ref| atch_ref.attachment)
.collect::<AHashSet<_>>();
let all_attachments = &attachments | &self.input_attachments.iter()
.flatten()
.map(|atch_ref| atch_ref.attachment)
.collect::<AHashSet<_>>();
let preserve_attachments = (0 .. self.parent.attachments.len() as u32)
.filter(|atch_id| !all_attachments.contains(atch_id))
.collect::<Vec<_>>();
let description = SubpassDescription {
color_attachments: self.color_attachments,
depth_stencil_attachment: self.depth_stencil_attachment,
input_attachments: self.input_attachments,
preserve_attachments,
..Default::default()
};
self.parent.subpass_infos.push(SubpassInfo {
description,
create_handlers: self.handlers,
attachments,
});
self.parent
}
}
pub struct NoOpEngineRenderPass {
vk_render_pass: Arc<RenderPass>,
}
impl NoOpEngineRenderPass {
pub fn new(
target: &Arc<dyn RenderTarget>,
device: &Arc<EngineDevice>,
) -> Result<Arc<dyn EngineRenderPass>, Validated<VulkanError>> {
let vk_render_pass = vulkano::single_pass_renderpass!(
device.vk_device().clone(),
attachments: {
color: {
format: target.format(device)?,
samples: 1,
load_op: Clear,
store_op: Store,
},
},
pass: {
color: [color],
depth_stencil: {},
},
)?;
Ok(Arc::new(Self {
vk_render_pass,
}))
}
}
impl EngineRenderPass for NoOpEngineRenderPass {
#[inline]
fn vk_render_pass(&self) -> &Arc<RenderPass> {
&self.vk_render_pass
}
#[inline]
fn attachment_name_map(&self) -> HashMap<String, u32> {
HashMap::new()
}
fn build_commands(
&self,
builder: &mut AutoCommandBufferBuilder<PrimaryAutoCommandBuffer>,
frame: &EngineFramebuffer,
) -> Result<(), VulkanoError> {
builder
.begin_render_pass(
RenderPassBeginInfo {
clear_values: vec![Some([0.0, 0.0, 0.0, 1.0].into())],
..RenderPassBeginInfo::framebuffer(frame.buffer().clone())
},
SubpassBeginInfo {
contents: SubpassContents::Inline,
..Default::default()
},
).unwrap()
.end_render_pass(SubpassEndInfo::default()).unwrap();
Ok(())
}
}