librashader_reflect/reflect/
presets.rs

1use crate::back::targets::OutputTarget;
2use crate::back::{CompilerBackend, FromCompilation};
3use crate::error::{ShaderCompileError, ShaderReflectError};
4use crate::front::{ShaderInputCompiler, ShaderReflectObject};
5use crate::reflect::semantics::{
6    Semantic, ShaderSemantics, TextureSemantics, UniformSemantic, UniqueSemantics,
7};
8use librashader_common::map::{FastHashMap, ShortString};
9use librashader_pack::PassResource;
10use librashader_preprocess::{PreprocessError, ShaderSource};
11use librashader_presets::{ShaderPreset, TextureMeta};
12
13/// Artifacts of a reflected and compiled shader pass.
14///
15/// The [`CompileReflectShader`](crate::back::CompileReflectShader) trait allows you to name
16/// the type of compiler artifact returned by a pair of output shader target and compilation
17/// instance as a TAIT like so.
18///
19/// ```rust
20/// #![feature(type_alias_impl_trait)]
21/// use librashader_reflect::back::CompileReflectShader;
22/// use librashader_reflect::back::targets::{GLSL, SPIRV};
23/// use librashader_reflect::front::SpirvCompilation;
24/// use librashader_reflect::reflect::cross::SpirvCross;
25/// use librashader_reflect::reflect::presets::ShaderPassArtifact;
26///
27/// type VulkanPassMeta = ShaderPassArtifact<impl CompileReflectShader<SPIRV, SpirvCompilation, SpirvCross>>;
28/// ```
29///
30/// This allows a runtime to not name the backing type of the compiled artifact if not necessary.
31pub type ShaderPassArtifact<T> = (PassResource, CompilerBackend<T>);
32
33impl<T: OutputTarget> CompilePresetTarget for T {}
34
35/// Trait for target shading languages that can compile output with
36/// shader preset metdata.
37pub trait CompilePresetTarget: OutputTarget {
38    /// Compile passes of a shader preset given the applicable
39    /// shader output target, compilation type, and resulting error.
40    fn compile_preset_passes<'a, I, R, E>(
41        passes: impl IntoIterator<Item = PassResource>,
42        textures: impl Iterator<Item = &'a TextureMeta>,
43    ) -> Result<
44        (
45            Vec<ShaderPassArtifact<<Self as FromCompilation<I, R>>::Output>>,
46            ShaderSemantics,
47        ),
48        E,
49    >
50    where
51        I: ShaderReflectObject,
52        Self: Sized,
53        Self: FromCompilation<I, R>,
54        I::Compiler: ShaderInputCompiler<I>,
55        E: From<PreprocessError>,
56        E: From<ShaderReflectError>,
57        E: From<ShaderCompileError>,
58    {
59        compile_preset_passes::<Self, I, R, E>(passes, textures)
60    }
61}
62
63/// Compile passes of a shader preset given the applicable
64/// shader output target, compilation type, and resulting error.
65fn compile_preset_passes<'a, T, I, R, E>(
66    passes: impl IntoIterator<Item = PassResource>,
67    textures: impl Iterator<Item = &'a TextureMeta>,
68) -> Result<
69    (
70        Vec<ShaderPassArtifact<<T as FromCompilation<I, R>>::Output>>,
71        ShaderSemantics,
72    ),
73    E,
74>
75where
76    I: ShaderReflectObject,
77    T: OutputTarget,
78    T: FromCompilation<I, R>,
79    I::Compiler: ShaderInputCompiler<I>,
80    E: From<PreprocessError>,
81    E: From<ShaderReflectError>,
82    E: From<ShaderCompileError>,
83{
84    let mut uniform_semantics: FastHashMap<ShortString, UniformSemantic> = Default::default();
85    let mut texture_semantics: FastHashMap<ShortString, Semantic<TextureSemantics>> =
86        Default::default();
87
88    let artifacts = passes
89        .into_iter()
90        .map(|shader| {
91            let source = &shader.data;
92            let compiled = I::Compiler::compile(source)?;
93            let reflect = T::from_compilation(compiled)?;
94
95            for parameter in source.parameters.values() {
96                uniform_semantics.insert(
97                    parameter.id.clone(),
98                    UniformSemantic::Unique(Semantic {
99                        semantics: UniqueSemantics::FloatParameter,
100                        index: (),
101                    }),
102                );
103            }
104            Ok::<_, E>((shader, reflect))
105        })
106        .collect::<Result<Vec<(PassResource, CompilerBackend<_>)>, E>>()?;
107
108    for (pass, _) in artifacts.iter() {
109        insert_pass_semantics(
110            &mut uniform_semantics,
111            &mut texture_semantics,
112            pass.meta.alias.as_ref(),
113            pass.meta.id as usize,
114        );
115        insert_pass_semantics(
116            &mut uniform_semantics,
117            &mut texture_semantics,
118            pass.data.name.as_ref(),
119            pass.meta.id as usize,
120        );
121    }
122
123    insert_lut_semantics(textures, &mut uniform_semantics, &mut texture_semantics);
124
125    let semantics = ShaderSemantics {
126        uniform_semantics,
127        texture_semantics,
128    };
129
130    Ok((artifacts, semantics))
131}
132
133/// Insert the available semantics for the input pass config into the provided semantic maps.
134fn insert_pass_semantics(
135    uniform_semantics: &mut FastHashMap<ShortString, UniformSemantic>,
136    texture_semantics: &mut FastHashMap<ShortString, Semantic<TextureSemantics>>,
137    alias: Option<&ShortString>,
138    index: usize,
139) {
140    let Some(alias) = alias else {
141        return;
142    };
143
144    // Ignore empty aliases
145    if alias.trim().is_empty() {
146        return;
147    }
148
149    // PassOutput
150    texture_semantics.insert(
151        alias.clone(),
152        Semantic {
153            semantics: TextureSemantics::PassOutput,
154            index,
155        },
156    );
157
158    let mut alias_size = alias.clone();
159    alias_size.push_str("Size");
160    uniform_semantics.insert(
161        alias_size,
162        UniformSemantic::Texture(Semantic {
163            semantics: TextureSemantics::PassOutput,
164            index,
165        }),
166    );
167
168    let mut alias_feedback = alias.clone();
169    alias_feedback.push_str("Feedback");
170    // PassFeedback
171    texture_semantics.insert(
172        alias_feedback,
173        Semantic {
174            semantics: TextureSemantics::PassFeedback,
175            index,
176        },
177    );
178
179    let mut alias_feedback_size = alias.clone();
180    alias_feedback_size.push_str("FeedbackSize");
181    uniform_semantics.insert(
182        alias_feedback_size,
183        UniformSemantic::Texture(Semantic {
184            semantics: TextureSemantics::PassFeedback,
185            index,
186        }),
187    );
188}
189
190/// Insert the available semantics for the input texture config into the provided semantic maps.
191fn insert_lut_semantics<'a>(
192    textures: impl Iterator<Item = &'a TextureMeta>,
193    uniform_semantics: &mut FastHashMap<ShortString, UniformSemantic>,
194    texture_semantics: &mut FastHashMap<ShortString, Semantic<TextureSemantics>>,
195) {
196    for (index, texture) in textures.enumerate() {
197        let mut size_semantic = texture.name.clone();
198        size_semantic.push_str("Size");
199
200        texture_semantics.insert(
201            texture.name.clone(),
202            Semantic {
203                semantics: TextureSemantics::User,
204                index,
205            },
206        );
207
208        uniform_semantics.insert(
209            size_semantic,
210            UniformSemantic::Texture(Semantic {
211                semantics: TextureSemantics::User,
212                index,
213            }),
214        );
215    }
216}
217
218impl ShaderSemantics {
219    /// Create pass semantics for a single pass in the given shader preset.
220    ///
221    /// This is meant as a convenience function for reflection use only.
222    pub fn create_pass_semantics<E>(
223        preset: &ShaderPreset,
224        index: usize,
225    ) -> Result<ShaderSemantics, E>
226    where
227        E: From<ShaderReflectError>,
228        E: From<PreprocessError>,
229    {
230        let mut uniform_semantics: FastHashMap<ShortString, UniformSemantic> = Default::default();
231        let mut texture_semantics: FastHashMap<ShortString, Semantic<TextureSemantics>> =
232            Default::default();
233
234        let config = preset
235            .passes
236            .get(index)
237            .ok_or_else(|| PreprocessError::InvalidStage)?;
238
239        let source = ShaderSource::load(&config.path, preset.features)?;
240
241        for parameter in source.parameters.values() {
242            uniform_semantics.insert(
243                parameter.id.clone(),
244                UniformSemantic::Unique(Semantic {
245                    semantics: UniqueSemantics::FloatParameter,
246                    index: (),
247                }),
248            );
249        }
250
251        insert_pass_semantics(
252            &mut uniform_semantics,
253            &mut texture_semantics,
254            config.meta.alias.as_ref(),
255            config.meta.id as usize,
256        );
257        insert_pass_semantics(
258            &mut uniform_semantics,
259            &mut texture_semantics,
260            source.name.as_ref(),
261            config.meta.id as usize,
262        );
263        insert_lut_semantics(
264            preset.textures.iter().map(|t| &t.meta),
265            &mut uniform_semantics,
266            &mut texture_semantics,
267        );
268
269        Ok(ShaderSemantics {
270            uniform_semantics,
271            texture_semantics,
272        })
273    }
274}