librashader_cache/
compilation.rs

1//!  Cache helpers for `ShaderCompilation` objects to cache compiled SPIRV.
2use librashader_preprocess::ShaderSource;
3#[cfg(all(target_os = "windows", feature = "d3d"))]
4use librashader_reflect::back::targets::DXIL;
5use librashader_reflect::back::targets::{GLSL, HLSL, SPIRV};
6
7use librashader_reflect::back::{CompilerBackend, FromCompilation};
8use librashader_reflect::error::{ShaderCompileError, ShaderReflectError};
9use librashader_reflect::front::{
10    Glslang, ShaderInputCompiler, ShaderReflectObject, SpirvCompilation,
11};
12
13pub struct CachedCompilation<T> {
14    compilation: T,
15}
16
17impl<T: ShaderReflectObject> ShaderReflectObject for CachedCompilation<T> {
18    type Compiler = T::Compiler;
19}
20
21impl<T: ShaderReflectObject + for<'de> serde::Deserialize<'de> + serde::Serialize + Clone>
22    ShaderInputCompiler<CachedCompilation<T>> for Glslang
23where
24    Glslang: ShaderInputCompiler<T>,
25{
26    fn compile(source: &ShaderSource) -> Result<CachedCompilation<T>, ShaderCompileError> {
27        let cache = crate::cache::internal::get_cache();
28
29        let Ok(cache) = cache else {
30            return Ok(CachedCompilation {
31                compilation: Glslang::compile(source)?,
32            });
33        };
34
35        let key = {
36            let mut hasher = blake3::Hasher::new();
37            hasher.update(source.vertex.as_bytes());
38            hasher.update(source.fragment.as_bytes());
39            let hash = hasher.finalize();
40            hash
41        };
42
43        let compilation = 'cached: {
44            if let Ok(Some(cached)) =
45                crate::cache::internal::get_blob(&cache, "spirv", key.as_bytes())
46            {
47                let decoded =
48                    bincode::serde::decode_from_slice(&cached, bincode::config::standard())
49                        .map(|(compilation, _)| CachedCompilation { compilation })
50                        .ok();
51
52                if let Some(compilation) = decoded {
53                    break 'cached compilation;
54                }
55            }
56
57            CachedCompilation {
58                compilation: Glslang::compile(source)?,
59            }
60        };
61
62        if let Ok(updated) =
63            bincode::serde::encode_to_vec(&compilation.compilation, bincode::config::standard())
64        {
65            let Ok(()) =
66                crate::cache::internal::set_blob(&cache, "spirv", key.as_bytes(), &updated)
67            else {
68                return Ok(compilation);
69            };
70        }
71
72        Ok(compilation)
73    }
74}
75
76#[cfg(all(target_os = "windows", feature = "d3d"))]
77impl<T> FromCompilation<CachedCompilation<SpirvCompilation>, T> for DXIL
78where
79    DXIL: FromCompilation<SpirvCompilation, T>,
80{
81    type Target = <DXIL as FromCompilation<SpirvCompilation, T>>::Target;
82    type Options = <DXIL as FromCompilation<SpirvCompilation, T>>::Options;
83    type Context = <DXIL as FromCompilation<SpirvCompilation, T>>::Context;
84    type Output = <DXIL as FromCompilation<SpirvCompilation, T>>::Output;
85
86    fn from_compilation(
87        compile: CachedCompilation<SpirvCompilation>,
88    ) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
89        DXIL::from_compilation(compile.compilation)
90    }
91}
92
93impl<T> FromCompilation<CachedCompilation<SpirvCompilation>, T> for HLSL
94where
95    HLSL: FromCompilation<SpirvCompilation, T>,
96{
97    type Target = <HLSL as FromCompilation<SpirvCompilation, T>>::Target;
98    type Options = <HLSL as FromCompilation<SpirvCompilation, T>>::Options;
99    type Context = <HLSL as FromCompilation<SpirvCompilation, T>>::Context;
100    type Output = <HLSL as FromCompilation<SpirvCompilation, T>>::Output;
101
102    fn from_compilation(
103        compile: CachedCompilation<SpirvCompilation>,
104    ) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
105        HLSL::from_compilation(compile.compilation)
106    }
107}
108
109impl<T> FromCompilation<CachedCompilation<SpirvCompilation>, T> for GLSL
110where
111    GLSL: FromCompilation<SpirvCompilation, T>,
112{
113    type Target = <GLSL as FromCompilation<SpirvCompilation, T>>::Target;
114    type Options = <GLSL as FromCompilation<SpirvCompilation, T>>::Options;
115    type Context = <GLSL as FromCompilation<SpirvCompilation, T>>::Context;
116    type Output = <GLSL as FromCompilation<SpirvCompilation, T>>::Output;
117
118    fn from_compilation(
119        compile: CachedCompilation<SpirvCompilation>,
120    ) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
121        GLSL::from_compilation(compile.compilation)
122    }
123}
124
125impl<T> FromCompilation<CachedCompilation<SpirvCompilation>, T> for SPIRV
126where
127    SPIRV: FromCompilation<SpirvCompilation, T>,
128{
129    type Target = <SPIRV as FromCompilation<SpirvCompilation, T>>::Target;
130    type Options = <SPIRV as FromCompilation<SpirvCompilation, T>>::Options;
131    type Context = <SPIRV as FromCompilation<SpirvCompilation, T>>::Context;
132    type Output = <SPIRV as FromCompilation<SpirvCompilation, T>>::Output;
133
134    fn from_compilation(
135        compile: CachedCompilation<SpirvCompilation>,
136    ) -> Result<CompilerBackend<Self::Output>, ShaderReflectError> {
137        SPIRV::from_compilation(compile.compilation)
138    }
139}