use crate::{
error::SarektResult,
renderer::{
buffers_and_images::BufferImageStore,
config::{AntiAliasingConfig, Config, NumSamples},
shaders::ShaderStore,
vertex_bindings::{
DefaultForwardShaderLayout, DefaultForwardShaderVertex, DescriptorLayoutInfo, VertexBindings,
},
vulkan::{
images::ImageAndView,
vulkan_renderer::{
base_pipeline_bundle::BasePipelineBundle,
render_attachments::{DepthAttachment, ResolveAttachment},
render_targets::RenderTargetBundle,
vulkan_core::{VulkanCoreStructures, VulkanDeviceStructures},
DEFAULT_FRAGMENT_SHADER, DEFAULT_VERTEX_SHADER,
},
vulkan_shader_functions::VulkanShaderFunctions,
VulkanShaderHandle,
},
ShaderCode, ShaderHandle, ShaderType, VulkanBufferImageFunctions,
},
};
use ash::{version::DeviceV1_0, vk, vk::DescriptorSetLayout, Device};
use log::info;
use std::{
convert::TryInto,
ffi::CStr,
sync::{Arc, RwLock},
};
pub struct Pipelines {
pub framebuffers: Vec<vk::Framebuffer>,
pub forward_render_pass: vk::RenderPass,
base_graphics_pipeline_bundle: BasePipelineBundle,
}
impl Pipelines {
pub fn new(
config: &Config, vulkan_core: &VulkanCoreStructures, device_bundle: &VulkanDeviceStructures,
render_target_bundle: &RenderTargetBundle,
shader_store: &Arc<RwLock<ShaderStore<VulkanShaderFunctions>>>,
buffer_image_store: &Arc<RwLock<BufferImageStore<VulkanBufferImageFunctions>>>,
) -> SarektResult<Pipelines> {
let dimensions = (
render_target_bundle.extent.width,
render_target_bundle.extent.height,
);
let num_msaa_samples = if let AntiAliasingConfig::MSAA(ns) = config.aa_config {
ns
} else {
NumSamples::One
};
info!(
"Creating pipeline with MSAA sample count of: {:?}",
num_msaa_samples
);
let depth_buffer = DepthAttachment::new(
&vulkan_core.instance,
device_bundle.physical_device,
buffer_image_store,
dimensions,
num_msaa_samples,
)?;
let resolve_attachment = if !matches!(num_msaa_samples, NumSamples::One) {
Some(ResolveAttachment::new(
buffer_image_store,
dimensions,
render_target_bundle
.swapchain_and_extension
.format
.try_into()
.expect("Format not supported by sarekt for msaa color buffer"),
num_msaa_samples,
)?)
} else {
None
};
let forward_render_pass = Self::create_forward_render_pass(
&device_bundle.logical_device,
render_target_bundle.get_render_target_format(),
&depth_buffer,
num_msaa_samples,
)?;
let framebuffers = Self::create_framebuffers(
&device_bundle.logical_device,
forward_render_pass,
resolve_attachment.as_ref(),
&depth_buffer,
&render_target_bundle.render_targets,
render_target_bundle.extent,
)?;
let base_graphics_pipeline_bundle = Self::create_base_graphics_pipeline_and_shaders(
&device_bundle.logical_device,
&shader_store, render_target_bundle.extent,
forward_render_pass,
resolve_attachment,
depth_buffer,
num_msaa_samples,
)?;
Ok(Pipelines {
framebuffers,
forward_render_pass,
base_graphics_pipeline_bundle,
})
}
pub fn get_current_pipeline(&self) -> vk::Pipeline {
self.base_graphics_pipeline_bundle.pipeline
}
pub fn get_pipeline_layout(&self) -> vk::PipelineLayout {
self.base_graphics_pipeline_bundle.pipeline_layout
}
pub fn get_pipeline_descriptor_layouts(&self) -> Vec<vk::DescriptorSetLayout> {
vec![
self
.base_graphics_pipeline_bundle
.descriptor_set_layouts
.as_ref()
.unwrap()[0],
]
}
pub fn get_framebuffer(&self, image_index: usize) -> vk::Framebuffer {
self.framebuffers[image_index]
}
pub fn recreate_renderpasses(
&mut self, logical_device: &Device, new_format: vk::Format, num_msaa_samples: NumSamples,
) -> SarektResult<()> {
self.forward_render_pass = Self::create_forward_render_pass(
logical_device,
new_format,
self
.base_graphics_pipeline_bundle
.depth_resources
.as_ref()
.unwrap(),
num_msaa_samples,
)?;
Ok(())
}
pub fn recreate_framebuffers(
&mut self, logical_device: &Device, resolve_attachment: Option<&ResolveAttachment>,
depth_buffer: &DepthAttachment, render_targets: &[ImageAndView], new_extent: vk::Extent2D,
) -> SarektResult<()> {
self.framebuffers = Self::create_framebuffers(
logical_device,
self.forward_render_pass,
resolve_attachment,
depth_buffer,
render_targets,
new_extent,
)?;
Ok(())
}
pub fn recreate_base_pipeline_bundle(
&mut self, logical_device: &Device,
shader_store: &Arc<RwLock<ShaderStore<VulkanShaderFunctions>>>, new_extent: vk::Extent2D,
resolve_attachment: Option<ResolveAttachment>, depth_buffer: DepthAttachment,
num_msaa_samples: NumSamples, descriptor_set_layouts: Vec<DescriptorSetLayout>,
vertex_shader_handle: ShaderHandle<VulkanShaderFunctions>,
fragment_shader_handle: ShaderHandle<VulkanShaderFunctions>,
) -> SarektResult<()> {
self.base_graphics_pipeline_bundle = Self::create_base_graphics_pipeline(
logical_device,
shader_store,
new_extent,
self.forward_render_pass,
resolve_attachment,
depth_buffer,
num_msaa_samples,
descriptor_set_layouts,
vertex_shader_handle,
fragment_shader_handle,
)?;
Ok(())
}
pub fn take_shaders_and_layouts(
&mut self,
) -> (
Option<ShaderHandle<VulkanShaderFunctions>>,
Option<ShaderHandle<VulkanShaderFunctions>>,
Option<Vec<DescriptorSetLayout>>,
) {
let vertex_shader_handle = self
.base_graphics_pipeline_bundle
.vertex_shader_handle
.take();
let fragment_shader_handle = self
.base_graphics_pipeline_bundle
.fragment_shader_handle
.take();
let descriptor_set_layouts = self
.base_graphics_pipeline_bundle
.descriptor_set_layouts
.take();
(
vertex_shader_handle,
fragment_shader_handle,
descriptor_set_layouts,
)
}
pub unsafe fn cleanup_descriptor_set_layouts(&mut self, logical_device: &Device) {
info!("Destroying default descriptor set layouts for default pipeline...");
if let Some(descriptor_set_layouts) = &self.base_graphics_pipeline_bundle.descriptor_set_layouts
{
for &layout in descriptor_set_layouts.iter() {
logical_device.destroy_descriptor_set_layout(layout, None);
}
}
}
pub unsafe fn cleanup(&self, logical_device: &Device) {
info!("Destroying all framebuffers...");
for &fb in self.framebuffers.iter() {
logical_device.destroy_framebuffer(fb, None);
}
info!("Destroying base graphics pipeline...");
logical_device.destroy_pipeline(self.base_graphics_pipeline_bundle.pipeline, None);
info!("Destroying base pipeline layouts...");
logical_device
.destroy_pipeline_layout(self.base_graphics_pipeline_bundle.pipeline_layout, None);
info!("Destroying render pass...");
logical_device.destroy_render_pass(self.forward_render_pass, None);
}
fn create_forward_render_pass(
logical_device: &Device, format: vk::Format, depth_buffer: &DepthAttachment,
num_msaa_samples: NumSamples,
) -> SarektResult<vk::RenderPass> {
let color_attachment_final_layout = if !matches!(num_msaa_samples, NumSamples::One) {
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL
} else {
vk::ImageLayout::PRESENT_SRC_KHR
};
let color_attachment = vk::AttachmentDescription::builder()
.format(format)
.samples(num_msaa_samples.into())
.load_op(vk::AttachmentLoadOp::CLEAR) .store_op(vk::AttachmentStoreOp::STORE) .stencil_load_op(vk::AttachmentLoadOp::DONT_CARE) .stencil_store_op(vk::AttachmentStoreOp::DONT_CARE) .initial_layout(vk::ImageLayout::UNDEFINED) .final_layout(color_attachment_final_layout)
.build();
let color_attachment_ref = vk::AttachmentReference::builder()
.attachment(0u32) .layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL) .build();
let color_attachment_refs = [color_attachment_ref];
let depth_attachment = vk::AttachmentDescription::builder()
.format(depth_buffer.format)
.samples(num_msaa_samples.into())
.load_op(vk::AttachmentLoadOp::CLEAR)
.store_op(vk::AttachmentStoreOp::DONT_CARE)
.stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
.stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
.initial_layout(vk::ImageLayout::UNDEFINED)
.final_layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
.build();
let depth_attachment_ref = vk::AttachmentReference::builder()
.attachment(1)
.layout(vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL)
.build();
let resolve_attachment = vk::AttachmentDescription::builder()
.format(format)
.samples(vk::SampleCountFlags::TYPE_1)
.load_op(vk::AttachmentLoadOp::DONT_CARE)
.store_op(vk::AttachmentStoreOp::STORE)
.stencil_load_op(vk::AttachmentLoadOp::DONT_CARE)
.stencil_store_op(vk::AttachmentStoreOp::DONT_CARE)
.initial_layout(vk::ImageLayout::UNDEFINED)
.final_layout(vk::ImageLayout::PRESENT_SRC_KHR)
.build();
let resolve_attachment_refs = [vk::AttachmentReference::builder()
.attachment(2)
.layout(vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL)
.build()];
let attachments = if !matches!(num_msaa_samples, NumSamples::One) {
vec![color_attachment, depth_attachment, resolve_attachment]
} else {
vec![color_attachment, depth_attachment]
};
let mut subpass_description = vk::SubpassDescription::builder()
.pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) .color_attachments(&color_attachment_refs)
.depth_stencil_attachment(&depth_attachment_ref);
if !matches!(num_msaa_samples, NumSamples::One) {
subpass_description = subpass_description.resolve_attachments(&resolve_attachment_refs);
}
let subpass_descriptions = [subpass_description.build()];
let dependency = vk::SubpassDependency::builder()
.src_subpass(vk::SUBPASS_EXTERNAL)
.dst_subpass(0u32)
.src_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) .src_access_mask(vk::AccessFlags::empty()) .dst_stage_mask(vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT) .dst_access_mask(vk::AccessFlags::COLOR_ATTACHMENT_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE) .build();
let dependencies = [dependency];
let render_pass_ci = vk::RenderPassCreateInfo::builder()
.attachments(&attachments)
.subpasses(&subpass_descriptions) .dependencies(&dependencies) .build();
Ok(unsafe { logical_device.create_render_pass(&render_pass_ci, None)? })
}
fn create_base_graphics_pipeline_and_shaders(
logical_device: &Device, shader_store: &Arc<RwLock<ShaderStore<VulkanShaderFunctions>>>,
extent: vk::Extent2D, render_pass: vk::RenderPass,
resolve_attachment: Option<ResolveAttachment>, depth_buffer: DepthAttachment,
num_msaa_samples: NumSamples,
) -> SarektResult<BasePipelineBundle> {
let (vertex_shader_handle, fragment_shader_handle) =
Self::create_default_shaders(shader_store)?;
let default_descriptor_set_layouts =
Self::create_default_descriptor_set_layouts(logical_device)?;
Self::create_base_graphics_pipeline(
logical_device,
shader_store,
extent,
render_pass,
resolve_attachment,
depth_buffer,
num_msaa_samples,
default_descriptor_set_layouts,
vertex_shader_handle,
fragment_shader_handle,
)
}
fn create_default_shaders(
shader_store: &Arc<RwLock<ShaderStore<VulkanShaderFunctions>>>,
) -> SarektResult<(VulkanShaderHandle, VulkanShaderHandle)> {
let vertex_shader_handle = ShaderStore::load_shader(
shader_store,
&ShaderCode::Spirv(DEFAULT_VERTEX_SHADER),
ShaderType::Vertex,
)?;
let fragment_shader_handle = ShaderStore::load_shader(
shader_store,
&ShaderCode::Spirv(DEFAULT_FRAGMENT_SHADER),
ShaderType::Vertex,
)?;
Ok((vertex_shader_handle, fragment_shader_handle))
}
fn create_default_descriptor_set_layouts(
logical_device: &Device,
) -> SarektResult<Vec<vk::DescriptorSetLayout>> {
let descriptor_set_layout_bindings =
DefaultForwardShaderLayout::get_descriptor_set_layout_bindings();
let descriptor_set_layout_ci = vk::DescriptorSetLayoutCreateInfo::builder()
.bindings(&descriptor_set_layout_bindings)
.build();
let descriptor_set_layout =
unsafe { logical_device.create_descriptor_set_layout(&descriptor_set_layout_ci, None)? };
Ok(vec![descriptor_set_layout])
}
fn create_base_graphics_pipeline(
logical_device: &Device, shader_store: &Arc<RwLock<ShaderStore<VulkanShaderFunctions>>>,
extent: vk::Extent2D, render_pass: vk::RenderPass,
resolve_attachment: Option<ResolveAttachment>, depth_buffer: DepthAttachment,
num_msaa_samples: NumSamples, descriptor_set_layouts: Vec<vk::DescriptorSetLayout>,
vertex_shader_handle: VulkanShaderHandle, fragment_shader_handle: VulkanShaderHandle,
) -> SarektResult<BasePipelineBundle> {
let shader_store = shader_store.read().unwrap();
let entry_point_name = CStr::from_bytes_with_nul(b"main\0").unwrap();
let vert_shader_stage_ci = vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::VERTEX)
.module(
shader_store
.get_shader(&vertex_shader_handle)
.unwrap()
.shader_handle,
)
.name(entry_point_name)
.build();
let frag_shader_stage_ci = vk::PipelineShaderStageCreateInfo::builder()
.stage(vk::ShaderStageFlags::FRAGMENT)
.module(
shader_store
.get_shader(&fragment_shader_handle)
.unwrap()
.shader_handle,
)
.name(entry_point_name)
.build();
let shader_stage_cis = [vert_shader_stage_ci, frag_shader_stage_ci];
let binding_descs = [DefaultForwardShaderVertex::get_binding_description()];
let attr_descs = DefaultForwardShaderVertex::get_attribute_descriptions();
let vertex_input_ci = vk::PipelineVertexInputStateCreateInfo::builder()
.vertex_binding_descriptions(&binding_descs)
.vertex_attribute_descriptions(&attr_descs)
.build();
let input_assembly_ci = vk::PipelineInputAssemblyStateCreateInfo::builder()
.topology(vk::PrimitiveTopology::TRIANGLE_LIST)
.primitive_restart_enable(false)
.build();
let viewport = vk::Viewport::builder()
.x(0f32)
.y(0f32)
.width(extent.width as f32)
.height(extent.height as f32)
.min_depth(0f32)
.max_depth(1.0f32)
.build();
let viewports = [viewport];
let scissor = vk::Rect2D::builder()
.offset(vk::Offset2D::default())
.extent(extent)
.build();
let scissors = [scissor];
let viewport_state_ci = vk::PipelineViewportStateCreateInfo::builder()
.viewports(&viewports)
.scissors(&scissors)
.build();
let raster_state_ci = vk::PipelineRasterizationStateCreateInfo::builder()
.depth_clamp_enable(false) .rasterizer_discard_enable(false) .polygon_mode(vk::PolygonMode::FILL) .line_width(1.0f32)
.cull_mode(vk::CullModeFlags::BACK) .front_face(vk::FrontFace::COUNTER_CLOCKWISE)
.depth_bias_enable(false)
.build();
let multisample_state_ci = vk::PipelineMultisampleStateCreateInfo::builder()
.sample_shading_enable(false)
.rasterization_samples(num_msaa_samples.into())
.min_sample_shading(1.0f32)
.alpha_to_coverage_enable(false)
.alpha_to_one_enable(false)
.build();
let depth_stencil_ci = vk::PipelineDepthStencilStateCreateInfo::builder()
.depth_test_enable(true)
.depth_write_enable(true)
.depth_compare_op(vk::CompareOp::LESS) .depth_bounds_test_enable(false) .min_depth_bounds(0.0f32)
.max_depth_bounds(1.0f32)
.stencil_test_enable(false)
.build();
let color_blend_attachment_state = vk::PipelineColorBlendAttachmentState::builder()
.color_write_mask(vk::ColorComponentFlags::all()) .blend_enable(false)
.build();
let attachments = [color_blend_attachment_state];
let color_blend_ci = vk::PipelineColorBlendStateCreateInfo::builder()
.logic_op_enable(false)
.logic_op(vk::LogicOp::COPY)
.attachments(&attachments)
.build();
let pipeline_layout_ci = vk::PipelineLayoutCreateInfo::builder()
.set_layouts(&descriptor_set_layouts)
.build();
let pipeline_layout =
unsafe { logical_device.create_pipeline_layout(&pipeline_layout_ci, None)? };
let base_graphics_pipeline_ci = vk::GraphicsPipelineCreateInfo::builder()
.flags(vk::PipelineCreateFlags::ALLOW_DERIVATIVES)
.stages(&shader_stage_cis)
.vertex_input_state(&vertex_input_ci)
.input_assembly_state(&input_assembly_ci)
.viewport_state(&viewport_state_ci)
.rasterization_state(&raster_state_ci)
.multisample_state(&multisample_state_ci)
.depth_stencil_state(&depth_stencil_ci)
.color_blend_state(&color_blend_ci)
.layout(pipeline_layout)
.render_pass(render_pass)
.subpass(0) .build();
let pipeline_create_infos = [base_graphics_pipeline_ci];
let pipeline = unsafe {
logical_device.create_graphics_pipelines(
vk::PipelineCache::null(),
&pipeline_create_infos,
None,
)
};
if let Err(err) = pipeline {
return Err(err.1.into());
}
Ok(BasePipelineBundle::new(
pipeline.unwrap()[0],
pipeline_layout,
base_graphics_pipeline_ci,
descriptor_set_layouts,
resolve_attachment,
depth_buffer,
vertex_shader_handle,
fragment_shader_handle,
))
}
fn create_framebuffers(
logical_device: &Device, render_pass: vk::RenderPass,
resolve_attachment: Option<&ResolveAttachment>, depth_buffer: &DepthAttachment,
render_target_images: &[ImageAndView], extent: vk::Extent2D,
) -> SarektResult<Vec<vk::Framebuffer>> {
let mut framebuffers = Vec::with_capacity(render_target_images.len());
for swapchain_image_and_view in render_target_images.iter() {
let attachments = if let Some(resolve_attachment) = resolve_attachment {
vec![
resolve_attachment.resolve_image.image_and_view.view,
depth_buffer.image_and_memory.image_and_view.view,
swapchain_image_and_view.view,
]
} else {
vec![
swapchain_image_and_view.view,
depth_buffer.image_and_memory.image_and_view.view,
]
};
let framebuffer_ci = vk::FramebufferCreateInfo::builder()
.render_pass(render_pass)
.attachments(&attachments)
.width(extent.width)
.height(extent.height)
.layers(1)
.build();
let framebuffer = unsafe { logical_device.create_framebuffer(&framebuffer_ci, None)? };
framebuffers.push(framebuffer);
}
Ok(framebuffers)
}
}