/* * 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 crate::draw::target::SwapchainProperties; use crate::error; use crate::types::*; /// A complete 2D graphics pipeline and associated resources pub struct UIPipeline { /// 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 UIPipeline { pub fn new( device: &mut Device, extent: hal::image::Extent, swapchain_properties: &SwapchainProperties, set_layouts: T, ) -> Result where T: IntoIterator + std::fmt::Debug, T::Item: Borrow, { use hal::format::Format; use hal::pso::*; // Renderpass let renderpass = { use hal::{ image::{Access, Layout}, memory::Dependencies, pass::*, }; let img_attachment = Attachment { format: Some(swapchain_properties.format), samples: 1, ops: AttachmentOps::new(AttachmentLoadOp::Load, AttachmentStoreOp::Store), stencil_ops: AttachmentOps::new( AttachmentLoadOp::DontCare, AttachmentStoreOp::DontCare, ), layouts: Layout::ColorAttachmentOptimal..Layout::Present, }; let subpass = SubpassDesc { colors: &[(0, Layout::ColorAttachmentOptimal)], depth_stencil: None, inputs: &[], resolves: &[], preserves: &[], }; let external_dependency = SubpassDependency { flags: Dependencies::empty(), passes: SubpassRef::External..SubpassRef::Pass(0), stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT ..(PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS), accesses: Access::empty() ..(Access::COLOR_ATTACHMENT_READ | Access::COLOR_ATTACHMENT_WRITE), }; unsafe { device.create_render_pass(&[img_attachment], &[subpass], &[external_dependency]) } .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_ui.vert", ENTRY_NAME, None, ) .map_err(error::CreationError::ShaderCError)?; let fragment_compile_artifact = compiler .compile_into_spirv( FRAGMENT_SOURCE, shaderc::ShaderKind::Fragment, "fragment_ui.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::() * 4) + (size_of::() * 4)) as u32, rate: VertexInputRate::Vertex, }]; let attributes: Vec = pipeline_vb_attributes!(0, size_of::() * 2; Rg32Sfloat, size_of::() * 2; Rg32Sfloat, size_of::() * 4; R32Uint ); // Rasterizer let rasterizer = Rasterizer { polygon_mode: PolygonMode::Fill, cull_face: Face::NONE, front_face: FrontFace::CounterClockwise, depth_clamping: false, depth_bias: None, conservative: true, }; // Depth stencil let depth_stencil = DepthStencilDesc { depth: None, depth_bounds: false, stencil: None, }; log::debug!("ui set layouts: {:?}", set_layouts); // Pipeline layout let layout = unsafe { device.create_pipeline_layout(set_layouts, &[(ShaderStageFlags::VERTEX, 0..8)]) } .map_err(|_| error::CreationError::OutOfMemoryError)?; // Colour blending let blender = { let blend_state = BlendState { color: BlendOp::Add { src: Factor::SrcAlpha, dst: Factor::OneMinusSrcAlpha, }, alpha: BlendOp::Add { src: Factor::OneMinusSrcAlpha, dst: Factor::Zero, }, }; BlendDesc { logic_op: None, 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(UIPipeline { 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))); } } }