/*
* Copyright (C) Oscar Shrimpton 2020
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
//! A complete graphics pipeline
/// Entry point name for shaders
const ENTRY_NAME: &str = "main";
/// Source for vertex shader. TODO
const VERTEX_SOURCE: &str = include_str!("./data/stockton.vert");
/// Source for fragment shader. TODO
const FRAGMENT_SOURCE: &str = include_str!("./data/stockton.frag");
use std::{
borrow::Borrow,
mem::{size_of, ManuallyDrop},
};
use hal::prelude::*;
use super::target::SwapchainProperties;
use crate::error;
use crate::types::*;
// TODO: Generalise so we can use for UI also
/// A complete graphics pipeline and associated resources
pub struct CompletePipeline {
/// Our main render pass
pub(crate) renderpass: ManuallyDrop,
/// The layout of our main graphics pipeline
pub(crate) pipeline_layout: ManuallyDrop,
/// Our main graphics pipeline
pub(crate) pipeline: ManuallyDrop,
/// The vertex shader module
pub(crate) vs_module: ManuallyDrop,
/// The fragment shader module
pub(crate) fs_module: ManuallyDrop,
}
impl CompletePipeline {
pub fn new(
device: &mut Device,
extent: hal::image::Extent,
swapchain_properties: &SwapchainProperties,
set_layouts: T,
) -> Result
where
T: IntoIterator,
T::Item: Borrow,
{
use hal::format::Format;
use hal::pso::*;
// Renderpass
let renderpass = {
use hal::{image::Layout, pass::*};
let img_attachment = Attachment {
format: Some(swapchain_properties.format),
samples: 1,
ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store),
stencil_ops: AttachmentOps::new(
AttachmentLoadOp::Clear,
AttachmentStoreOp::DontCare,
),
layouts: Layout::Undefined..Layout::ColorAttachmentOptimal,
};
let depth_attachment = Attachment {
format: Some(swapchain_properties.depth_format),
samples: 1,
ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare),
stencil_ops: AttachmentOps::new(
AttachmentLoadOp::DontCare,
AttachmentStoreOp::DontCare,
),
layouts: Layout::Undefined..Layout::DepthStencilAttachmentOptimal,
};
let subpass = SubpassDesc {
colors: &[(0, Layout::ColorAttachmentOptimal)],
depth_stencil: Some(&(1, Layout::DepthStencilAttachmentOptimal)),
inputs: &[],
resolves: &[],
preserves: &[],
};
unsafe {
device.create_render_pass(&[img_attachment, depth_attachment], &[subpass], &[])
}
.map_err(|_| error::CreationError::OutOfMemoryError)?
};
// Subpass
let subpass = hal::pass::Subpass {
index: 0,
main_pass: &renderpass,
};
// Shader modules
let (vs_module, fs_module) = {
let mut compiler = shaderc::Compiler::new().ok_or(error::CreationError::NoShaderC)?;
let vertex_compile_artifact = compiler
.compile_into_spirv(
VERTEX_SOURCE,
shaderc::ShaderKind::Vertex,
"vertex.vert",
ENTRY_NAME,
None,
)
.map_err(error::CreationError::ShaderCError)?;
let fragment_compile_artifact = compiler
.compile_into_spirv(
FRAGMENT_SOURCE,
shaderc::ShaderKind::Fragment,
"fragment.frag",
ENTRY_NAME,
None,
)
.map_err(error::CreationError::ShaderCError)?;
// Make into shader module
unsafe {
(
device
.create_shader_module(vertex_compile_artifact.as_binary())
.map_err(error::CreationError::ShaderModuleFailed)?,
device
.create_shader_module(fragment_compile_artifact.as_binary())
.map_err(error::CreationError::ShaderModuleFailed)?,
)
}
};
// Shader entry points (ShaderStage)
let (vs_entry, fs_entry) = (
EntryPoint:: {
entry: ENTRY_NAME,
module: &vs_module,
specialization: Specialization::default(),
},
EntryPoint:: {
entry: ENTRY_NAME,
module: &fs_module,
specialization: Specialization::default(),
},
);
// Shader set
let shaders = GraphicsShaderSet {
vertex: vs_entry,
fragment: Some(fs_entry),
hull: None,
domain: None,
geometry: None,
};
// Vertex buffers
let vertex_buffers: Vec = vec![VertexBufferDesc {
binding: 0,
stride: (size_of::() * 6) as u32,
rate: VertexInputRate::Vertex,
}];
let attributes: Vec = pipeline_vb_attributes!(0,
size_of::() * 3; Rgb32Sfloat,
size_of::(); R32Sint,
size_of::() * 2; Rg32Sfloat
);
// Rasterizer
let rasterizer = Rasterizer {
polygon_mode: PolygonMode::Fill,
cull_face: Face::BACK,
front_face: FrontFace::CounterClockwise,
depth_clamping: false,
depth_bias: None,
conservative: true,
};
// Depth stencil
let depth_stencil = DepthStencilDesc {
depth: Some(DepthTest {
fun: Comparison::Less,
write: true,
}),
depth_bounds: false,
stencil: None,
};
// Pipeline layout
let layout = unsafe {
device.create_pipeline_layout(
set_layouts,
// vp matrix, 4x4 f32
&[(ShaderStageFlags::VERTEX, 0..64)],
)
}
.map_err(|_| error::CreationError::OutOfMemoryError)?;
// Colour blending
let blender = {
let blend_state = BlendState {
color: BlendOp::Add {
src: Factor::One,
dst: Factor::Zero,
},
alpha: BlendOp::Add {
src: Factor::One,
dst: Factor::Zero,
},
};
BlendDesc {
logic_op: Some(LogicOp::Copy),
targets: vec![ColorBlendDesc {
mask: ColorMask::ALL,
blend: Some(blend_state),
}],
}
};
// Baked states
let baked_states = BakedStates {
viewport: Some(Viewport {
rect: extent.rect(),
depth: (0.0..1.0),
}),
scissor: Some(extent.rect()),
blend_color: None,
depth_bounds: None,
};
// Input assembler
let input_assembler = InputAssemblerDesc::new(Primitive::TriangleList);
// Pipeline description
let pipeline_desc = GraphicsPipelineDesc {
shaders,
rasterizer,
vertex_buffers,
blender,
depth_stencil,
multisampling: None,
baked_states,
layout: &layout,
subpass,
flags: PipelineCreationFlags::empty(),
parent: BasePipeline::None,
input_assembler,
attributes,
};
// Pipeline
let pipeline = unsafe { device.create_graphics_pipeline(&pipeline_desc, None) }
.map_err(error::CreationError::PipelineError)?;
Ok(CompletePipeline {
renderpass: ManuallyDrop::new(renderpass),
pipeline_layout: ManuallyDrop::new(layout),
pipeline: ManuallyDrop::new(pipeline),
vs_module: ManuallyDrop::new(vs_module),
fs_module: ManuallyDrop::new(fs_module),
})
}
/// Deactivate vulkan resources. Use before dropping
pub fn deactivate(self, device: &mut Device) {
unsafe {
use core::ptr::read;
device.destroy_render_pass(ManuallyDrop::into_inner(read(&self.renderpass)));
device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.vs_module)));
device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.fs_module)));
device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline)));
device.destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout)));
}
}
}