use vulkano::pipeline::GraphicsPipelineAbstract;
use crate::graphics;
use graphics::device::Device;
use graphics::swapchain::Swapchain;
use graphics::pass::graphical_pass;
use graphical_pass::{GraphicalPass, GraphicalRenderPassDescription};
use vulkano::format::{Format, PossibleDepthFormatDesc};
use vulkano::pipeline::{GraphicsPipeline, GraphicsPipelineCreationError};
use vulkano::pipeline::depth_stencil::{Compare, DepthStencil};
use vulkano::pipeline::shader::{SpecializationConstants, GraphicsEntryPointAbstract};
use vulkano::pipeline::raster::{CullMode, FrontFace, PolygonMode, Rasterization};
use vulkano::pipeline::vertex::{SingleBufferDefinition, VertexDefinition};
use vulkano::framebuffer::{AttachmentDescription, RenderPassDesc, RenderPassCreationError, Subpass};
use vulkano::image::ImageLayout;
use std::sync::Arc;
pub use vulkano::pipeline::input_assembly::PrimitiveTopology;
pub use vulkano::framebuffer::{StoreOp, LoadOp};
pub struct GraphicalPassBuilder<VI, VS, VSS, FS, FSS> {
vertex_input: VI,
vertex_shader: (VS, VSS),
primitive_topology: PrimitiveTopology,
rasterization: Rasterization,
fragment_shader: (FS, FSS),
depth_stencil: DepthStencil,
samples: u32,
attachments: Vec<AttachmentDescription>,
depth_attachment: Option<usize>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AttachmentError {
InvalidFormat,
DepthAttachmentAlreadyExists(usize),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BuildError {
RenderPassCreation(RenderPassCreationError),
GraphicsPipelineCreation(GraphicsPipelineCreationError),
NoAttachments,
}
impl GraphicalPassBuilder<(), (), (), (), ()> {
pub(super) fn new() -> Self {
Self {
vertex_input: (),
vertex_shader: ((), ()),
primitive_topology: PrimitiveTopology::TriangleList,
rasterization: Rasterization::default(),
fragment_shader: ((), ()),
depth_stencil: DepthStencil::default(),
samples: 1,
attachments: Vec::default(),
depth_attachment: None,
}
}
}
impl<VI, VS, VSS, FS, FSS> GraphicalPassBuilder<VI, VS, VSS, FS, FSS> {
pub fn vertex_input<T>(self, vertex_input: T) -> GraphicalPassBuilder<T, VS, VSS, FS, FSS> {
GraphicalPassBuilder {
vertex_input: vertex_input,
vertex_shader: self.vertex_shader,
primitive_topology: self.primitive_topology,
rasterization: self.rasterization,
fragment_shader: self.fragment_shader,
depth_stencil: self.depth_stencil,
samples: self.samples,
attachments: self.attachments,
depth_attachment: self.depth_attachment,
}
}
pub fn single_buffer_input<V>(self) -> GraphicalPassBuilder<SingleBufferDefinition<V>, VS, VSS, FS, FSS> { self.vertex_input(SingleBufferDefinition::<V>::new()) }
pub fn primitive_topology(self, topology: PrimitiveTopology) -> Self { Self { primitive_topology: topology, .. self } }
pub fn point_list(self) -> Self { self.primitive_topology(PrimitiveTopology::PointList) }
pub fn line_list(self) -> Self { self.primitive_topology(PrimitiveTopology::LineList) }
pub fn line_strip(self) -> Self { self.primitive_topology(PrimitiveTopology::LineStrip) }
pub fn triangle_list(self) -> Self { self.primitive_topology(PrimitiveTopology::TriangleList) }
pub fn triangle_strip(self) -> Self { self.primitive_topology(PrimitiveTopology::TriangleStrip) }
pub fn triangle_fan(self) -> Self { self.primitive_topology(PrimitiveTopology::TriangleFan) }
pub fn line_list_with_adjacency(self) -> Self { self.primitive_topology(PrimitiveTopology::LineListWithAdjacency) }
pub fn line_strip_with_adjacency(self) -> Self { self.primitive_topology(PrimitiveTopology::LineStripWithAdjacency) }
pub fn triangle_list_with_adjacency(self) -> Self { self.primitive_topology(PrimitiveTopology::TriangleListWithAdjacency) }
pub fn triangle_strip_with_adjacency(self) -> Self { self.primitive_topology(PrimitiveTopology::TriangleStripWithAdjacency) }
pub fn patch_list(self, vertices_per_patch: u32) -> Self { self.primitive_topology(PrimitiveTopology::PatchList{ vertices_per_patch }) }
pub fn clamp_depth(mut self, clamp: bool) -> Self { self.rasterization.depth_clamp = clamp; self }
pub fn raster_polygon_mode(mut self, mode: PolygonMode) -> Self { self.rasterization.polygon_mode = mode; self }
pub fn cull_mode(mut self, mode: CullMode) -> Self { self.rasterization.cull_mode = mode; self }
pub fn cull_none(self) -> Self { self.cull_mode(CullMode::None) }
pub fn cull_front(self) -> Self { self.cull_mode(CullMode::Front) }
pub fn cull_back(self) -> Self { self.cull_mode(CullMode::Back) }
pub fn cull_front_and_back(self) -> Self { self.cull_mode(CullMode::FrontAndBack) }
pub fn front_face(mut self, face: FrontFace) -> Self { self.rasterization.front_face = face; self }
pub fn front_face_clockwise(self) -> Self { self.front_face(FrontFace::Clockwise) }
pub fn front_face_counter_clockwise(self) -> Self { self.front_face(FrontFace::CounterClockwise) }
pub fn line_width(mut self, width: f32) -> Self { self.rasterization.line_width = Some(width); self }
pub fn depth_write(mut self, write: bool) -> Self { self.depth_stencil.depth_write = write; self }
pub fn depth_test_op(mut self, operation: Compare) -> Self { self.depth_stencil.depth_compare = operation; self }
pub fn depth_test_never(self) -> Self { self.depth_test_op(Compare::Never) }
pub fn depth_test_less(self) -> Self { self.depth_test_op(Compare::Less) }
pub fn depth_test_equal(self) -> Self { self.depth_test_op(Compare::Equal) }
pub fn depth_test_less_or_equal(self) -> Self { self.depth_test_op(Compare::LessOrEqual) }
pub fn depth_test_greater(self) -> Self { self.depth_test_op(Compare::Greater) }
pub fn depth_test_not_equal(self) -> Self { self.depth_test_op(Compare::NotEqual) }
pub fn depth_test_greater_or_equal(self) -> Self { self.depth_test_op(Compare::GreaterOrEqual) }
pub fn depth_test_always(self) -> Self { self.depth_test_op(Compare::Always) }
pub fn basic_depth_test(self) -> Self { self.depth_write(true).depth_test_less() }
pub fn inverse_depth_test(self) -> Self { self.depth_write(true).depth_test_greater() }
pub fn vertex_shader<S, SC>(self, shader: S, specialization: SC)
-> GraphicalPassBuilder<VI, S, SC, FS, FSS>
where
S : GraphicsEntryPointAbstract<SpecializationConstants = SC>,
SC : SpecializationConstants,
{
GraphicalPassBuilder {
vertex_input: self.vertex_input,
vertex_shader: (shader, specialization),
primitive_topology: self.primitive_topology,
rasterization: self.rasterization,
fragment_shader: self.fragment_shader,
depth_stencil: self.depth_stencil,
samples: self.samples,
attachments: self.attachments,
depth_attachment: self.depth_attachment,
}
}
pub fn fragment_shader<S, SC>(self, shader: S, specialization: SC)
-> GraphicalPassBuilder<VI, VS, VSS, S, SC>
where
S : GraphicsEntryPointAbstract<SpecializationConstants = SC>,
SC : SpecializationConstants,
{
GraphicalPassBuilder {
vertex_input: self.vertex_input,
vertex_shader: self.vertex_shader,
primitive_topology: self.primitive_topology,
rasterization: self.rasterization,
fragment_shader: (shader, specialization),
depth_stencil: self.depth_stencil,
samples: self.samples,
attachments: self.attachments,
depth_attachment: self.depth_attachment,
}
}
pub fn add_image_attachment(mut self, format: Format, load: LoadOp, store: StoreOp) -> Self {
self.attachments.push(AttachmentDescription{
format,
samples: self.samples,
load,
store,
stencil_load: LoadOp::DontCare,
stencil_store: StoreOp::DontCare,
initial_layout: ImageLayout::ColorAttachmentOptimal,
final_layout: ImageLayout::ColorAttachmentOptimal,
});
self
}
pub fn add_image_attachment_swapchain(self, swapchain: &Swapchain, load: LoadOp) -> Self {
self.add_image_attachment(swapchain.swapchain.format(), load, StoreOp::Store)
}
pub fn add_image_attachment_swapchain_cleared(self, swapchain: &Swapchain) -> Self {
self.add_image_attachment_swapchain(swapchain, LoadOp::Clear)
}
pub fn add_depth_attachment(mut self, format: Format, load: LoadOp, store: StoreOp) -> Result<Self, AttachmentError> {
if format.is_depth() {
match self.depth_attachment {
Some(index) => Err(AttachmentError::DepthAttachmentAlreadyExists(index)),
None => {
self.depth_attachment = Some(self.attachments.len());
self.attachments.push(AttachmentDescription{
format: format,
samples: self.samples,
load: load,
store: store,
stencil_load: load,
stencil_store: store,
initial_layout: ImageLayout::DepthStencilAttachmentOptimal,
final_layout: ImageLayout::DepthStencilAttachmentOptimal,
});
Ok(self)
}
}
} else {
Err(AttachmentError::InvalidFormat)
}
}
pub fn add_depth_attachment_swapchain(self, swapchain: &Swapchain, load: LoadOp, store: StoreOp) -> Result<Self, AttachmentError> {
self.add_depth_attachment(swapchain.depth_format, load, store)
}
pub fn add_depth_attachment_swapchain_discard(self, swapchain: &Swapchain, load: LoadOp) -> Result<Self, AttachmentError> {
self.add_depth_attachment_swapchain(swapchain, load, StoreOp::DontCare)
}
pub fn add_depth_attachment_swapchain_preserve(self, swapchain: &Swapchain, load: LoadOp) -> Result<Self, AttachmentError> {
self.add_depth_attachment_swapchain(swapchain, load, StoreOp::Store)
}
}
impl<VI, VS, VSS, FS, FSS> GraphicalPassBuilder<VI, VS, VSS, FS, FSS>
where
VS : GraphicsEntryPointAbstract<SpecializationConstants=VSS>,
FS : GraphicsEntryPointAbstract<SpecializationConstants=FSS>,
VSS : SpecializationConstants,
FSS : SpecializationConstants,
VS::PipelineLayout : Send + Sync + Clone + 'static,
FS::PipelineLayout : Send + Sync + Clone + 'static,
VI : VertexDefinition<VS::InputDefinition> + Send + Sync + 'static,
{
pub fn build(self, device: &Device)
-> Result<GraphicalPass<dyn GraphicsPipelineAbstract + Send + Sync + 'static>, BuildError>
{
if self.attachments.is_empty() {
return Err(BuildError::NoAttachments)
};
let render_pass = {
let description = GraphicalRenderPassDescription {
attachments: self.attachments,
depth_attachment: self.depth_attachment,
};
Arc::new(description.build_render_pass(device.device.clone())?)
};
let pipeline = {
let builder = GraphicsPipeline::start()
.vertex_input(self.vertex_input)
.vertex_shader(self.vertex_shader.0, self.vertex_shader.1)
.primitive_topology(self.primitive_topology)
.viewports_dynamic_scissors_irrelevant(1)
.fragment_shader(self.fragment_shader.0, self.fragment_shader.1)
.depth_stencil(self.depth_stencil)
.render_pass(Subpass::from(render_pass, 0).unwrap())
.depth_clamp(self.rasterization.depth_clamp)
;
let builder = match self.rasterization.polygon_mode {
PolygonMode::Point => builder.polygon_mode_point(),
PolygonMode::Line => builder.polygon_mode_line(),
PolygonMode::Fill => builder.polygon_mode_fill(),
};
let builder = match self.rasterization.cull_mode {
CullMode::None => builder.cull_mode_disabled(),
CullMode::Front => builder.cull_mode_front(),
CullMode::Back => builder.cull_mode_back(),
CullMode::FrontAndBack => builder.cull_mode_front_and_back(),
};
let builder = match self.rasterization.front_face {
FrontFace::Clockwise => builder.front_face_clockwise(),
FrontFace::CounterClockwise => builder.front_face_counter_clockwise(),
};
let builder = match self.rasterization.line_width {
Some(width) => builder.line_width(width),
None => builder,
};
Arc::new(builder.build(device.logical_device())?)
};
Ok(GraphicalPass { pipeline, })
}
}
impl From<RenderPassCreationError> for BuildError {
fn from(err: RenderPassCreationError) -> Self { Self::RenderPassCreation(err) }
}
impl From<GraphicsPipelineCreationError> for BuildError {
fn from(err: GraphicsPipelineCreationError) -> Self { Self::GraphicsPipelineCreation(err) }
}