| 1 | use crate::Error; |
| 2 | use crate::device::Shared; |
| 3 | use ash::vk; |
| 4 | use std::collections::HashMap; |
| 5 | use std::sync::Arc; |
| 6 | |
| 7 | /// Push constants shared by every shader, see `shaders/push.glsl`. |
| 8 | /// `dst` is the quad in normalized device coordinates as (x, y, w, h), |
| 9 | /// `uv` the texture coordinate at each corner (TL, TR, BL, BR). |
| 10 | #[repr(C)] |
| 11 | #[derive(Clone, Copy, Debug, Default)] |
| 12 | pub(crate) struct Push { |
| 13 | pub color: [f32; 4], |
| 14 | pub dst: [f32; 4], |
| 15 | pub uv: [[f32; 2]; 4], |
| 16 | } |
| 17 | |
| 18 | impl Push { |
| 19 | pub fn bytes(&self) -> &[u8] { |
| 20 | unsafe { std::slice::from_raw_parts((self as *const Self).cast::<u8>(), size_of::<Self>()) } |
| 21 | } |
| 22 | } |
| 23 | |
| 24 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
| 25 | pub(crate) enum Kind { |
| 26 | Solid, |
| 27 | Texture, |
| 28 | } |
| 29 | |
| 30 | pub(crate) struct Pipelines { |
| 31 | shared: Arc<Shared>, |
| 32 | pub set_layout: vk::DescriptorSetLayout, |
| 33 | pub layout: vk::PipelineLayout, |
| 34 | vert: vk::ShaderModule, |
| 35 | solid: vk::ShaderModule, |
| 36 | texture: vk::ShaderModule, |
| 37 | cache: HashMap<(vk::Format, Kind), vk::Pipeline>, |
| 38 | } |
| 39 | |
| 40 | const QUAD_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/quad.vert.spv")); |
| 41 | const SOLID_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/solid.frag.spv")); |
| 42 | const TEXTURE_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/texture.frag.spv")); |
| 43 | |
| 44 | impl Pipelines { |
| 45 | pub fn new(shared: &Arc<Shared>) -> Result<Self, Error> { |
| 46 | let d = &shared.device.device; |
| 47 | let bindings = [vk::DescriptorSetLayoutBinding::default() |
| 48 | .binding(0) |
| 49 | .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) |
| 50 | .descriptor_count(1) |
| 51 | .stage_flags(vk::ShaderStageFlags::FRAGMENT)]; |
| 52 | let set_layout = unsafe { |
| 53 | d.create_descriptor_set_layout( |
| 54 | &vk::DescriptorSetLayoutCreateInfo::default() |
| 55 | .flags(vk::DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR_KHR) |
| 56 | .bindings(&bindings), |
| 57 | None, |
| 58 | )? |
| 59 | }; |
| 60 | let ranges = [vk::PushConstantRange::default() |
| 61 | .stage_flags(vk::ShaderStageFlags::VERTEX | vk::ShaderStageFlags::FRAGMENT) |
| 62 | .size(size_of::<Push>() as u32)]; |
| 63 | let set_layouts = [set_layout]; |
| 64 | let layout = unsafe { |
| 65 | d.create_pipeline_layout( |
| 66 | &vk::PipelineLayoutCreateInfo::default() |
| 67 | .set_layouts(&set_layouts) |
| 68 | .push_constant_ranges(&ranges), |
| 69 | None, |
| 70 | )? |
| 71 | }; |
| 72 | let module = |code: &[u8]| -> Result<vk::ShaderModule, Error> { |
| 73 | let words = ash::util::read_spv(&mut std::io::Cursor::new(code)) |
| 74 | .map_err(|_| vk::Result::ERROR_INVALID_SHADER_NV)?; |
| 75 | Ok(unsafe { |
| 76 | d.create_shader_module(&vk::ShaderModuleCreateInfo::default().code(&words), None)? |
| 77 | }) |
| 78 | }; |
| 79 | Ok(Self { |
| 80 | shared: shared.clone(), |
| 81 | set_layout, |
| 82 | layout, |
| 83 | vert: module(QUAD_VERT)?, |
| 84 | solid: module(SOLID_FRAG)?, |
| 85 | texture: module(TEXTURE_FRAG)?, |
| 86 | cache: HashMap::new(), |
| 87 | }) |
| 88 | } |
| 89 | |
| 90 | pub fn get(&mut self, format: vk::Format, kind: Kind) -> Result<vk::Pipeline, Error> { |
| 91 | if let Some(&p) = self.cache.get(&(format, kind)) { |
| 92 | return Ok(p); |
| 93 | } |
| 94 | let d = &self.shared.device.device; |
| 95 | let frag = match kind { |
| 96 | Kind::Solid => self.solid, |
| 97 | Kind::Texture => self.texture, |
| 98 | }; |
| 99 | let stages = [ |
| 100 | vk::PipelineShaderStageCreateInfo::default() |
| 101 | .stage(vk::ShaderStageFlags::VERTEX) |
| 102 | .module(self.vert) |
| 103 | .name(c"main"), |
| 104 | vk::PipelineShaderStageCreateInfo::default() |
| 105 | .stage(vk::ShaderStageFlags::FRAGMENT) |
| 106 | .module(frag) |
| 107 | .name(c"main"), |
| 108 | ]; |
| 109 | let vertex = vk::PipelineVertexInputStateCreateInfo::default(); |
| 110 | let assembly = vk::PipelineInputAssemblyStateCreateInfo::default() |
| 111 | .topology(vk::PrimitiveTopology::TRIANGLE_STRIP); |
| 112 | let viewport = vk::PipelineViewportStateCreateInfo::default() |
| 113 | .viewport_count(1) |
| 114 | .scissor_count(1); |
| 115 | let raster = vk::PipelineRasterizationStateCreateInfo::default() |
| 116 | .polygon_mode(vk::PolygonMode::FILL) |
| 117 | .cull_mode(vk::CullModeFlags::NONE) |
| 118 | .line_width(1.0); |
| 119 | let multisample = vk::PipelineMultisampleStateCreateInfo::default() |
| 120 | .rasterization_samples(vk::SampleCountFlags::TYPE_1); |
| 121 | let attachments = [vk::PipelineColorBlendAttachmentState::default() |
| 122 | .blend_enable(true) |
| 123 | .src_color_blend_factor(vk::BlendFactor::ONE) |
| 124 | .dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA) |
| 125 | .color_blend_op(vk::BlendOp::ADD) |
| 126 | .src_alpha_blend_factor(vk::BlendFactor::ONE) |
| 127 | .dst_alpha_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA) |
| 128 | .alpha_blend_op(vk::BlendOp::ADD) |
| 129 | .color_write_mask(vk::ColorComponentFlags::RGBA)]; |
| 130 | let blend = vk::PipelineColorBlendStateCreateInfo::default().attachments(&attachments); |
| 131 | let dynamic_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]; |
| 132 | let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states); |
| 133 | let formats = [format]; |
| 134 | let mut rendering = |
| 135 | vk::PipelineRenderingCreateInfo::default().color_attachment_formats(&formats); |
| 136 | let info = vk::GraphicsPipelineCreateInfo::default() |
| 137 | .stages(&stages) |
| 138 | .vertex_input_state(&vertex) |
| 139 | .input_assembly_state(&assembly) |
| 140 | .viewport_state(&viewport) |
| 141 | .rasterization_state(&raster) |
| 142 | .multisample_state(&multisample) |
| 143 | .color_blend_state(&blend) |
| 144 | .dynamic_state(&dynamic) |
| 145 | .layout(self.layout) |
| 146 | .push_next(&mut rendering); |
| 147 | let pipeline = |
| 148 | unsafe { d.create_graphics_pipelines(vk::PipelineCache::null(), &[info], None) } |
| 149 | .map_err(|(_, e)| e)?[0]; |
| 150 | self.cache.insert((format, kind), pipeline); |
| 151 | Ok(pipeline) |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | impl Drop for Pipelines { |
| 156 | fn drop(&mut self) { |
| 157 | let d = &self.shared.device.device; |
| 158 | unsafe { |
| 159 | for (_, p) in self.cache.drain() { |
| 160 | d.destroy_pipeline(p, None); |
| 161 | } |
| 162 | d.destroy_shader_module(self.vert, None); |
| 163 | d.destroy_shader_module(self.solid, None); |
| 164 | d.destroy_shader_module(self.texture, None); |
| 165 | d.destroy_pipeline_layout(self.layout, None); |
| 166 | d.destroy_descriptor_set_layout(self.set_layout, None); |
| 167 | } |
| 168 | } |
| 169 | } |