Skip to main content

concinnity_device/
precompile.rs

1//! Export-time compilation of the engine's built-in shaders. The DirectX and
2//! Vulkan backends declare their compile set as static data (each backend's
3//! builtins.rs); this module iterates those declarations and makes sure every
4//! enumerable variant's artifact exists in a bundle's shader-cache/ directory.
5//! Compilation is pure CPU (FXC / DXC / shaderc need no GPU device), so this
6//! runs inside `cn export` with no window, no adapter, and no child process.
7//! Renderer init compiles through the same declarations and the same cache
8//! keys, so a shipped bundle's first launch reuses every artifact written here;
9//! anything not enumerable (a world-authored SdfVolume fragment, an unusual
10//! runtime parameter) still compiles at init exactly as before.
11
12use std::path::Path;
13
14use crate::shader_cache::Ensured;
15
16/// Outcome of a built-in shader precompile: how many artifacts were already in
17/// place or copied from the local cache, how many compiled fresh, and the
18/// programs that failed (with their compile diagnostics). Failures do not
19/// abort the run -- the affected shader falls back to compiling at the
20/// bundle's first launch.
21#[derive(Debug, Default)]
22pub struct Report {
23    /// Artifacts already in place or copied from the local cache.
24    pub reused: usize,
25    /// Artifacts compiled fresh this run.
26    pub compiled: usize,
27    /// Programs that failed, with their compile diagnostics.
28    pub failed: Vec<String>,
29}
30
31impl Report {
32    pub(crate) fn record(&mut self, label: &str, result: Result<Ensured, String>) {
33        match result {
34            Ok(Ensured::Present) | Ok(Ensured::Copied) => self.reused += 1,
35            Ok(Ensured::Compiled) => self.compiled += 1,
36            Err(e) => self.failed.push(format!("{label}: {e}")),
37        }
38    }
39
40    /// Total artifacts now present in the output directory.
41    pub fn cached(&self) -> usize {
42        self.reused + self.compiled
43    }
44}
45
46/// Compile every enumerable built-in shader variant into `out_dir` (a bundle's
47/// `shader-cache/`). `texture_count` is the exported world's texture-table
48/// length, which sizes the Vulkan bindless texture pool baked into its
49/// pool-sized shaders; DirectX ignores it (its pools are unbounded arrays).
50pub fn precompile_builtin_shaders(out_dir: &Path, texture_count: usize) -> Report {
51    let mut report = Report::default();
52    #[cfg(not(backend_vk))]
53    let _ = texture_count;
54    #[cfg(backend_dx)]
55    {
56        crate::directx::builtins::precompile(out_dir, &mut report);
57        crate::directx::slang_builtins::precompile(out_dir, &mut report);
58    }
59    #[cfg(backend_vk)]
60    crate::vulkan::builtins::precompile(out_dir, texture_count, &mut report);
61    report
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn report_tallies_reuse_compile_and_failure() {
70        let mut r = Report::default();
71        r.record("a", Ok(Ensured::Present));
72        r.record("b", Ok(Ensured::Copied));
73        r.record("c", Ok(Ensured::Compiled));
74        r.record("d ps_5_1", Err("boom".to_string()));
75        assert_eq!(r.reused, 2);
76        assert_eq!(r.compiled, 1);
77        assert_eq!(r.cached(), 3);
78        assert_eq!(r.failed, vec!["d ps_5_1: boom".to_string()]);
79    }
80}