use ash::vk;
use concinnity_core::render::error::{RenderError, RenderResult};
use concinnity_core::render::post::device::PostLoadOp;
use concinnity_core::render::render_graph::PixelFormat;
use std::sync::Mutex;
use crate::vulkan::owned::{OwnedFramebuffer, OwnedRenderPass, VkDevice};
use crate::vulkan::transient_pool::image_format;
#[derive(Copy, Clone, PartialEq, Eq)]
struct PassKey {
format: PixelFormat,
load: PostLoadOp,
}
fn attachment_open(load: PostLoadOp) -> (vk::AttachmentLoadOp, vk::ImageLayout) {
match load {
PostLoadOp::DontCare => (vk::AttachmentLoadOp::DONT_CARE, vk::ImageLayout::UNDEFINED),
PostLoadOp::Load => (
vk::AttachmentLoadOp::LOAD,
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
),
}
}
fn attachment_access() -> vk::AccessFlags {
vk::AccessFlags::COLOR_ATTACHMENT_WRITE
| vk::AccessFlags::COLOR_ATTACHMENT_READ
| vk::AccessFlags::SHADER_READ
}
fn create_render_pass(
device: &VkDevice,
format: PixelFormat,
load: PostLoadOp,
) -> RenderResult<OwnedRenderPass> {
let (load_op, initial) = attachment_open(load);
let attachment = vk::AttachmentDescription::default()
.format(image_format(format))
.samples(vk::SampleCountFlags::TYPE_1)
.load_op(load_op)
.store_op(vk::AttachmentStoreOp::STORE)
.stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
.stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
.initial_layout(initial)
.final_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);
let color_ref = vk::AttachmentReference::default()
.attachment(0)
.layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL);
let subpass = vk::SubpassDescription::default()
.pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS)
.color_attachments(std::slice::from_ref(&color_ref));
let dependency = vk::SubpassDependency::default()
.src_subpass(vk::SUBPASS_EXTERNAL)
.dst_subpass(0)
.src_stage_mask(
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
| vk::PipelineStageFlags::FRAGMENT_SHADER,
)
.src_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_WRITE)
.dst_stage_mask(
vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT
| vk::PipelineStageFlags::FRAGMENT_SHADER,
)
.dst_access_mask(attachment_access());
let info = vk::RenderPassCreateInfo::default()
.attachments(std::slice::from_ref(&attachment))
.subpasses(std::slice::from_ref(&subpass))
.dependencies(std::slice::from_ref(&dependency));
device
.create_render_pass(&info)
.map_err(|e| crate::vulkan::error::map_vk_result(e, "post render pass"))
}
#[derive(Default)]
pub(in crate::vulkan) struct PostPassCache {
passes: Mutex<Vec<(PassKey, OwnedRenderPass)>>,
framebuffers: Mutex<Vec<(vk::ImageView, vk::RenderPass, OwnedFramebuffer)>>,
}
impl PostPassCache {
pub(in crate::vulkan) fn new() -> Self {
Self::default()
}
pub(in crate::vulkan) fn render_pass(
&self,
device: &VkDevice,
format: PixelFormat,
load: PostLoadOp,
) -> RenderResult<vk::RenderPass> {
let key = PassKey { format, load };
let mut passes = self
.passes
.lock()
.map_err(|_| RenderError::Other("post render-pass cache poisoned".to_string()))?;
if let Some((_, rp)) = passes.iter().find(|(k, _)| *k == key) {
return Ok(rp.handle());
}
let rp = create_render_pass(device, format, load)?;
let handle = rp.handle();
passes.push((key, rp));
Ok(handle)
}
pub(in crate::vulkan) fn framebuffer(
&self,
device: &VkDevice,
render_pass: vk::RenderPass,
view: vk::ImageView,
extent: vk::Extent2D,
) -> RenderResult<vk::Framebuffer> {
let mut fbs = self
.framebuffers
.lock()
.map_err(|_| RenderError::Other("post framebuffer cache poisoned".to_string()))?;
if let Some((_, _, fb)) = fbs
.iter()
.find(|(v, rp, _)| *v == view && *rp == render_pass)
{
return Ok(fb.handle());
}
let fb = device
.create_framebuffer(
&vk::FramebufferCreateInfo::default()
.render_pass(render_pass)
.attachments(std::slice::from_ref(&view))
.width(extent.width)
.height(extent.height)
.layers(1),
)
.map_err(|e| crate::vulkan::error::map_vk_result(e, "post framebuffer"))?;
let handle = fb.handle();
fbs.push((view, render_pass, fb));
Ok(handle)
}
pub(in crate::vulkan) fn forget_views(&self) {
if let Ok(mut fbs) = self.framebuffers.lock() {
fbs.clear();
}
}
pub(in crate::vulkan) fn destroy(&self) {
self.forget_views();
if let Ok(mut passes) = self.passes.lock() {
passes.clear();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_preserving_load_begins_readable_and_a_discarding_one_undefined() {
assert_eq!(
attachment_open(PostLoadOp::Load),
(
vk::AttachmentLoadOp::LOAD,
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL
)
);
assert_eq!(
attachment_open(PostLoadOp::DontCare),
(vk::AttachmentLoadOp::DONT_CARE, vk::ImageLayout::UNDEFINED)
);
}
#[test]
fn the_dependency_declares_the_read_a_load_performs() {
let access = attachment_access();
assert!(access.contains(vk::AccessFlags::COLOR_ATTACHMENT_READ));
assert!(access.contains(vk::AccessFlags::COLOR_ATTACHMENT_WRITE));
assert!(access.contains(vk::AccessFlags::SHADER_READ));
}
}