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; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 3 | use crate::format::{SUPPORTED, vk_format}; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 4 | use ash::vk; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 5 | use std::collections::HashMap; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 6 | use std::path::PathBuf; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 7 | use std::sync::Arc; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 8 | use std::sync::atomic::{AtomicU32, Ordering}; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 9 | use std::time::Instant; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 10 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 11 | /// Push constants shared by every shader, see `shaders/push.glsl`. |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 12 | /// `dst` is the quad in normalized device coordinates as (x, y, w, h), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 13 | /// `uv` the texture coordinate at each corner (TL, TR, BL, BR). |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 14 | #[repr(C)] |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 15 | #[derive(Clone, Copy, Debug, Default)] |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 16 | pub(crate) struct Push { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 17 | pub color: [f32; 4], |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 18 | pub dst: [f32; 4], |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 19 | pub uv: [[f32; 2]; 4], |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 20 | /// Transition progress and kind, read only by `shaders/effect.frag`. |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 21 | pub effect: [f32; 4], |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 22 | /// See `shaders/clip.glsl`. A zero width turns clipping off. |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 23 | pub clip: [f32; 4], |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 24 | pub radii: [f32; 4], |
47259c39793f | atagen | 2026-10-17 10:13 UTC | 25 | pub ring: [f32; 4], |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 26 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 27 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 28 | impl Push { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 29 | pub fn bytes(&self) -> &[u8] { |
35d6f20f8eb6 | atagen | 2026-10-17 05:07 UTC | 30 | // SAFETY: `Push` is `repr(C)` and all f32, so it has no padding bytes. |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 31 | unsafe { std::slice::from_raw_parts((self as *const Self).cast::<u8>(), size_of::<Self>()) } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 32 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 33 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 34 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 35 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 36 | pub(crate) enum Kind { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 37 | Solid, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 38 | Texture, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 39 | Effect, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 40 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 41 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 42 | pub(crate) struct Pipelines { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 43 | shared: Arc<Shared>, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 44 | pub set_layout: vk::DescriptorSetLayout, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 45 | pub layout: vk::PipelineLayout, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 46 | vert: vk::ShaderModule, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 47 | solid: vk::ShaderModule, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 48 | texture: vk::ShaderModule, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 49 | effect: vk::ShaderModule, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 50 | cache: HashMap<(vk::Format, Kind), vk::Pipeline>, |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 51 | vk_cache: vk::PipelineCache, |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 52 | cache_path: Option<PathBuf>, |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 53 | loaded_len: usize, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 54 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 55 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 56 | const QUAD_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/quad.vert.spv")); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 57 | const SOLID_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/solid.frag.spv")); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 58 | const TEXTURE_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/texture.frag.spv")); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 59 | const EFFECT_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/effect.frag.spv")); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 60 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 61 | impl Pipelines { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 62 | pub fn new(shared: &Arc<Shared>) -> Result<Self, Error> { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 63 | let d = &shared.device.device; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 64 | let bindings = [vk::DescriptorSetLayoutBinding::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 65 | .binding(0) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 66 | .descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 67 | .descriptor_count(1) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 68 | .stage_flags(vk::ShaderStageFlags::FRAGMENT)]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 69 | let set_layout = unsafe { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 70 | d.create_descriptor_set_layout( |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 71 | &vk::DescriptorSetLayoutCreateInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 72 | .flags(vk::DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR_KHR) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 73 | .bindings(&bindings), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 74 | None, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 75 | )? |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 76 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 77 | let ranges = [vk::PushConstantRange::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 78 | .stage_flags(vk::ShaderStageFlags::VERTEX | vk::ShaderStageFlags::FRAGMENT) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 79 | .size(size_of::<Push>() as u32)]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 80 | let set_layouts = [set_layout]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 81 | let layout = unsafe { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 82 | d.create_pipeline_layout( |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 83 | &vk::PipelineLayoutCreateInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 84 | .set_layouts(&set_layouts) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 85 | .push_constant_ranges(&ranges), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 86 | 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 | let module = |code: &[u8]| -> Result<vk::ShaderModule, Error> { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 90 | let words = ash::util::read_spv(&mut std::io::Cursor::new(code)) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 91 | .map_err(|_| vk::Result::ERROR_INVALID_SHADER_NV)?; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 92 | Ok(unsafe { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 93 | d.create_shader_module(&vk::ShaderModuleCreateInfo::default().code(&words), None)? |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 94 | }) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 95 | }; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 96 | let cache_path = cache_path(); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 97 | let props = unsafe { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 98 | shared |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 99 | .device |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 100 | .instance |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 101 | .get_physical_device_properties(shared.device.physical) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 102 | }; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 103 | let loaded = cache_path |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 104 | .as_ref() |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 105 | .and_then(|p| std::fs::read(p).ok()) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 106 | .filter(|data| header_matches(data, &props)) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 107 | .unwrap_or_default(); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 108 | let create = |data: &[u8]| unsafe { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 109 | d.create_pipeline_cache( |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 110 | &vk::PipelineCacheCreateInfo::default().initial_data(data), |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 111 | None, |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 112 | ) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 113 | }; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 114 | let vk_cache = create(&loaded).or_else(|_| create(&[])).unwrap_or_default(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 115 | Ok(Self { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 116 | shared: shared.clone(), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 117 | set_layout, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 118 | layout, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 119 | vert: module(QUAD_VERT)?, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 120 | solid: module(SOLID_FRAG)?, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 121 | texture: module(TEXTURE_FRAG)?, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 122 | effect: module(EFFECT_FRAG)?, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 123 | cache: HashMap::new(), |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 124 | vk_cache, |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 125 | cache_path, |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 126 | loaded_len: loaded.len(), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 127 | }) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 128 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 129 | |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 130 | /// Compiles the pipelines a frame draws with for every render format, so |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 131 | /// the first spawn animation does not stall on the driver compiler. |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 132 | pub fn warm(&mut self) { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 133 | let began = Instant::now(); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 134 | for fourcc in SUPPORTED { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 135 | let Some(format) = vk_format(fourcc) else { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 136 | continue; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 137 | }; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 138 | for kind in [Kind::Solid, Kind::Texture, Kind::Effect] { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 139 | if let Err(e) = self.get(format.format, kind) { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 140 | tracing::warn!(?kind, "cannot warm a pipeline: {e}"); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 141 | return; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 142 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 143 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 144 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 145 | tracing::debug!(elapsed = ?began.elapsed(), "warmed pipelines"); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 146 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 147 | |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 148 | fn save_cache(&self) { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 149 | let Some(path) = &self.cache_path else { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 150 | return; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 151 | }; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 152 | if self.vk_cache == vk::PipelineCache::null() { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 153 | return; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 154 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 155 | let Ok(data) = (unsafe { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 156 | self.shared |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 157 | .device |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 158 | .device |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 159 | .get_pipeline_cache_data(self.vk_cache) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 160 | }) else { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 161 | return; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 162 | }; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 163 | if data.len() == self.loaded_len { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 164 | return; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 165 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 166 | static WRITERS: AtomicU32 = AtomicU32::new(0); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 167 | let tmp = path.with_extension(format!( |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 168 | "{}.{}.tmp", |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 169 | std::process::id(), |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 170 | WRITERS.fetch_add(1, Ordering::Relaxed) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 171 | )); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 172 | let written = path |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 173 | .parent() |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 174 | .map_or(Ok(()), std::fs::create_dir_all) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 175 | .and_then(|()| std::fs::write(&tmp, &data)) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 176 | .and_then(|()| std::fs::rename(&tmp, path)); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 177 | if let Err(e) = written { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 178 | tracing::debug!("cannot save the pipeline cache: {e}"); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 179 | let _ = std::fs::remove_file(&tmp); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 180 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 181 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 182 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 183 | pub fn get(&mut self, format: vk::Format, kind: Kind) -> Result<vk::Pipeline, Error> { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 184 | if let Some(&p) = self.cache.get(&(format, kind)) { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 185 | return Ok(p); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 186 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 187 | let d = &self.shared.device.device; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 188 | let frag = match kind { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 189 | Kind::Solid => self.solid, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 190 | Kind::Texture => self.texture, |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 191 | Kind::Effect => self.effect, |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 192 | }; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 193 | let stages = [ |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 194 | vk::PipelineShaderStageCreateInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 195 | .stage(vk::ShaderStageFlags::VERTEX) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 196 | .module(self.vert) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 197 | .name(c"main"), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 198 | vk::PipelineShaderStageCreateInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 199 | .stage(vk::ShaderStageFlags::FRAGMENT) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 200 | .module(frag) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 201 | .name(c"main"), |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 202 | ]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 203 | let vertex = vk::PipelineVertexInputStateCreateInfo::default(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 204 | let assembly = vk::PipelineInputAssemblyStateCreateInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 205 | .topology(vk::PrimitiveTopology::TRIANGLE_STRIP); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 206 | let viewport = vk::PipelineViewportStateCreateInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 207 | .viewport_count(1) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 208 | .scissor_count(1); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 209 | let raster = vk::PipelineRasterizationStateCreateInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 210 | .polygon_mode(vk::PolygonMode::FILL) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 211 | .cull_mode(vk::CullModeFlags::NONE) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 212 | .line_width(1.0); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 213 | let multisample = vk::PipelineMultisampleStateCreateInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 214 | .rasterization_samples(vk::SampleCountFlags::TYPE_1); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 215 | let attachments = [vk::PipelineColorBlendAttachmentState::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 216 | .blend_enable(true) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 217 | .src_color_blend_factor(vk::BlendFactor::ONE) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 218 | .dst_color_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 219 | .color_blend_op(vk::BlendOp::ADD) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 220 | .src_alpha_blend_factor(vk::BlendFactor::ONE) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 221 | .dst_alpha_blend_factor(vk::BlendFactor::ONE_MINUS_SRC_ALPHA) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 222 | .alpha_blend_op(vk::BlendOp::ADD) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 223 | .color_write_mask(vk::ColorComponentFlags::RGBA)]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 224 | let blend = vk::PipelineColorBlendStateCreateInfo::default().attachments(&attachments); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 225 | let dynamic_states = [vk::DynamicState::VIEWPORT, vk::DynamicState::SCISSOR]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 226 | let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&dynamic_states); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 227 | let formats = [format]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 228 | let mut rendering = |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 229 | vk::PipelineRenderingCreateInfo::default().color_attachment_formats(&formats); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 230 | let info = vk::GraphicsPipelineCreateInfo::default() |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 231 | .stages(&stages) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 232 | .vertex_input_state(&vertex) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 233 | .input_assembly_state(&assembly) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 234 | .viewport_state(&viewport) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 235 | .rasterization_state(&raster) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 236 | .multisample_state(&multisample) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 237 | .color_blend_state(&blend) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 238 | .dynamic_state(&dynamic) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 239 | .layout(self.layout) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 240 | .push_next(&mut rendering); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 241 | let pipeline = unsafe { d.create_graphics_pipelines(self.vk_cache, &[info], None) } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 242 | .map_err(|(_, e)| e)?[0]; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 243 | self.cache.insert((format, kind), pipeline); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 244 | Ok(pipeline) |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 245 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 246 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 247 | |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 248 | impl Drop for Pipelines { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 249 | fn drop(&mut self) { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 250 | self.save_cache(); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 251 | let d = &self.shared.device.device; |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 252 | unsafe { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 253 | for (_, p) in self.cache.drain() { |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 254 | d.destroy_pipeline(p, None); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 255 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 256 | d.destroy_pipeline_cache(self.vk_cache, None); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 257 | d.destroy_shader_module(self.vert, None); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 258 | d.destroy_shader_module(self.solid, None); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 259 | d.destroy_shader_module(self.texture, None); |
96db497859dc | atagen | 2026-10-16 11:27 UTC | 260 | d.destroy_shader_module(self.effect, None); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 261 | d.destroy_pipeline_layout(self.layout, None); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 262 | d.destroy_descriptor_set_layout(self.set_layout, None); |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 263 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 264 | } |
824bf02b6e55 | atagen | 2026-10-16 09:15 UTC | 265 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 266 | |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 267 | fn cache_path() -> Option<PathBuf> { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 268 | let base = std::env::var_os("XDG_CACHE_HOME") |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 269 | .filter(|v| !v.is_empty()) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 270 | .map(PathBuf::from) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 271 | .or_else(|| std::env::var_os("HOME").map(|home| PathBuf::from(home).join(".cache")))?; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 272 | Some(base.join("monolith").join("pipelines.bin")) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 273 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 274 | |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 275 | /// Whether `data` opens with a cache header written by this driver on this |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 276 | /// device. The spec lets a driver reject a foreign cache but some crash on |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 277 | /// one instead, so the header is checked first. |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 278 | fn header_matches(data: &[u8], props: &vk::PhysicalDeviceProperties) -> bool { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 279 | let word = |at: usize| { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 280 | data.get(at..at + 4) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 281 | .and_then(|b| b.try_into().ok()) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 282 | .map(u32::from_ne_bytes) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 283 | }; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 284 | word(0).is_some_and(|size| size >= 32 && size as usize <= data.len()) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 285 | && word(4) == Some(vk::PipelineCacheHeaderVersion::ONE.as_raw() as u32) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 286 | && word(8) == Some(props.vendor_id) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 287 | && word(12) == Some(props.device_id) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 288 | && data.get(16..32) == Some(&props.pipeline_cache_uuid[..]) |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 289 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 290 | |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 291 | #[cfg(test)] |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 292 | mod tests { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 293 | use super::*; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 294 | |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 295 | fn props() -> vk::PhysicalDeviceProperties { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 296 | vk::PhysicalDeviceProperties { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 297 | vendor_id: 0x10de, |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 298 | device_id: 0x2487, |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 299 | pipeline_cache_uuid: [7; 16], |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 300 | ..Default::default() |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 301 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 302 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 303 | |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 304 | fn header(vendor: u32, uuid: u8) -> Vec<u8> { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 305 | let words = [32u32, 1, vendor, 0x2487]; |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 306 | let mut data: Vec<u8> = words.iter().flat_map(|w| w.to_ne_bytes()).collect(); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 307 | data.extend([uuid; 16]); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 308 | data |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 309 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 310 | |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 311 | #[test] |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 312 | fn cache_header_must_match_the_device_and_driver() { |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 313 | assert!(header_matches(&header(0x10de, 7), &props())); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 314 | assert!(!header_matches(&header(0x1002, 7), &props())); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 315 | assert!(!header_matches(&header(0x10de, 8), &props())); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 316 | assert!(!header_matches(&header(0x10de, 7)[..20], &props())); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 317 | assert!(!header_matches(&[], &props())); |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 318 | } |
43e0e8e33c3e | atagen | 2026-10-19 07:35 UTC | 319 | } |