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