librashader_preprocess/
lib.rs

1//! Shader preprocessing for librashader.
2//!
3//! This crate contains facilities and types for resolving `#include` directives in `.slang`
4//! into a single compilation unit. `#pragma` directives are also parsed and resolved as
5//! [`ShaderParameter`] structs.
6//!
7//! The resulting [`ShaderSource`]can then be passed into a
8//! reflection target for reflection and compilation into the target shader format.
9//!
10//! Re-exported as [`librashader::preprocess`](https://docs.rs/librashader/latest/librashader/preprocess/index.html).
11mod error;
12mod include;
13mod pragma;
14mod stage;
15
16use crate::include::read_source;
17pub use error::*;
18use librashader_common::map::{FastHashMap, ShortString};
19use librashader_common::shader_features::ShaderFeatures;
20use librashader_common::ImageFormat;
21use std::path::Path;
22
23/// The source file for a single shader pass.
24#[derive(Debug, Clone, PartialEq)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub struct ShaderSource {
27    /// The source contents for the vertex shader.
28    pub vertex: String,
29
30    /// The source contents for the fragment shader.
31    pub fragment: String,
32
33    /// The alias of the shader if available.
34    pub name: Option<ShortString>,
35
36    /// The list of shader parameters found in the shader source.
37    pub parameters: FastHashMap<ShortString, ShaderParameter>,
38
39    /// The image format the shader expects.
40    pub format: ImageFormat,
41}
42
43/// A user tweakable parameter for the shader as declared in source.
44#[derive(Debug, Clone, PartialEq)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
46pub struct ShaderParameter {
47    /// The name of the parameter.
48    pub id: ShortString,
49    /// The description of the parameter.
50    pub description: String,
51    /// The initial value the parameter is set to.
52    pub initial: f32,
53    /// The minimum value that the parameter can be set to.
54    pub minimum: f32,
55    /// The maximum value that the parameter can be set to.
56    pub maximum: f32,
57    /// The step by which this parameter can be incremented or decremented.
58    pub step: f32,
59}
60
61impl ShaderSource {
62    /// Load the source file at the given path, resolving includes relative to the location of the
63    /// source file.
64    pub fn load(
65        path: impl AsRef<Path>,
66        features: ShaderFeatures,
67    ) -> Result<ShaderSource, PreprocessError> {
68        load_shader_source(path, features)
69    }
70}
71
72pub(crate) trait SourceOutput {
73    fn push_line(&mut self, str: &str);
74    fn mark_line(&mut self, line_no: usize, comment: &str) {
75        #[cfg(feature = "line_directives")]
76        self.push_line(&format!("#line {line_no} \"{comment}\""))
77    }
78}
79
80impl SourceOutput for String {
81    fn push_line(&mut self, str: &str) {
82        self.push_str(str);
83        self.push('\n');
84    }
85}
86
87pub(crate) fn load_shader_source(
88    path: impl AsRef<Path>,
89    features: ShaderFeatures,
90) -> Result<ShaderSource, PreprocessError> {
91    let source = read_source(path, features)?;
92    let meta = pragma::parse_pragma_meta(&source)?;
93
94    let text = stage::process_stages(&source)?;
95    let parameters = FastHashMap::from_iter(meta.parameters.into_iter().map(|p| (p.id.clone(), p)));
96
97    Ok(ShaderSource {
98        vertex: text.vertex,
99        fragment: text.fragment,
100        name: meta.name,
101        parameters,
102        format: meta.format,
103    })
104}
105
106#[cfg(test)]
107mod test {
108    use crate::include::read_source;
109    use crate::{load_shader_source, pragma};
110    use librashader_common::shader_features::ShaderFeatures;
111
112    #[test]
113    pub fn load_file() {
114        let result = load_shader_source(
115            "../test/shaders_slang/blurs/shaders/royale/blur3x3-last-pass.slang",
116            ShaderFeatures::NONE,
117        )
118        .unwrap();
119        eprintln!("{:#}", result.vertex)
120    }
121
122    #[test]
123    pub fn preprocess_file() {
124        let result = read_source(
125            "../test/slang-shaders/blurs/shaders/royale/blur3x3-last-pass.slang",
126            ShaderFeatures::NONE,
127        )
128        .unwrap();
129        eprintln!("{result}")
130    }
131
132    #[test]
133    pub fn get_param_pragmas() {
134        let result = read_source(
135            "../test/slang-shaders/crt/shaders/crt-maximus-royale/src/ntsc_pass1.slang",
136            ShaderFeatures::NONE,
137        )
138        .unwrap();
139
140        let params = pragma::parse_pragma_meta(result).unwrap();
141        eprintln!("{params:?}")
142    }
143
144    #[test]
145    pub fn include_optional() {
146        let result =
147            read_source("../test/include_optional/pass.slang", ShaderFeatures::NONE).unwrap();
148
149        eprintln!("{result}")
150    }
151}