naga/front/glsl/
mod.rs

1/*!
2Frontend for [GLSL][glsl] (OpenGL Shading Language).
3
4To begin, take a look at the documentation for the [`Frontend`].
5
6# Supported versions
7## Vulkan
8- 440 (partial)
9- 450
10- 460
11
12[glsl]: https://www.khronos.org/registry/OpenGL/index_gl.php
13*/
14
15pub use ast::{Precision, Profile};
16pub use error::{Error, ErrorKind, ExpectedToken, ParseErrors};
17pub use token::TokenValue;
18
19use alloc::{string::String, vec::Vec};
20
21use crate::{proc::Layouter, FastHashMap, FastHashSet, Handle, Module, ShaderStage, Span, Type};
22use ast::{EntryArg, FunctionDeclaration, GlobalLookup};
23use parser::ParsingContext;
24
25mod ast;
26mod builtins;
27mod context;
28mod error;
29mod functions;
30mod lex;
31mod offset;
32mod parser;
33#[cfg(test)]
34mod parser_tests;
35mod token;
36mod types;
37mod variables;
38
39type Result<T> = core::result::Result<T, Error>;
40
41/// Per-shader options passed to [`parse`](Frontend::parse).
42///
43/// The [`From`] trait is implemented for [`ShaderStage`] to provide a quick way
44/// to create an `Options` instance.
45///
46/// ```rust
47/// # use naga::ShaderStage;
48/// # use naga::front::glsl::Options;
49/// Options::from(ShaderStage::Vertex);
50/// ```
51#[derive(Debug)]
52pub struct Options {
53    /// The shader stage in the pipeline.
54    pub stage: ShaderStage,
55    /// Preprocessor definitions to be used, akin to having
56    /// ```glsl
57    /// #define key value
58    /// ```
59    /// for each key value pair in the map.
60    pub defines: FastHashMap<String, String>,
61}
62
63impl From<ShaderStage> for Options {
64    fn from(stage: ShaderStage) -> Self {
65        Options {
66            stage,
67            defines: FastHashMap::default(),
68        }
69    }
70}
71
72/// Additional information about the GLSL shader.
73///
74/// Stores additional information about the GLSL shader which might not be
75/// stored in the shader [`Module`].
76#[derive(Debug)]
77pub struct ShaderMetadata {
78    /// The GLSL version specified in the shader through the use of the
79    /// `#version` preprocessor directive.
80    pub version: u16,
81    /// The GLSL profile specified in the shader through the use of the
82    /// `#version` preprocessor directive.
83    pub profile: Profile,
84    /// The shader stage in the pipeline, passed to the [`parse`](Frontend::parse)
85    /// method via the [`Options`] struct.
86    pub stage: ShaderStage,
87
88    /// The workgroup size for compute shaders, defaults to `[1; 3]` for
89    /// compute shaders and `[0; 3]` for non compute shaders.
90    pub workgroup_size: [u32; 3],
91    /// Whether or not early fragment tests where requested by the shader.
92    /// Defaults to `false`.
93    pub early_fragment_tests: bool,
94
95    /// The shader can request extensions via the
96    /// `#extension` preprocessor directive, in the directive a behavior
97    /// parameter is used to control whether the extension should be disabled,
98    /// warn on usage, enabled if possible or required.
99    ///
100    /// This field only stores extensions which were required or requested to
101    /// be enabled if possible and they are supported.
102    pub extensions: FastHashSet<String>,
103}
104
105impl ShaderMetadata {
106    fn reset(&mut self, stage: ShaderStage) {
107        self.version = 0;
108        self.profile = Profile::Core;
109        self.stage = stage;
110        self.workgroup_size = [u32::from(stage == ShaderStage::Compute); 3];
111        self.early_fragment_tests = false;
112        self.extensions.clear();
113    }
114}
115
116impl Default for ShaderMetadata {
117    fn default() -> Self {
118        ShaderMetadata {
119            version: 0,
120            profile: Profile::Core,
121            stage: ShaderStage::Vertex,
122            workgroup_size: [0; 3],
123            early_fragment_tests: false,
124            extensions: FastHashSet::default(),
125        }
126    }
127}
128
129/// The `Frontend` is the central structure of the GLSL frontend.
130///
131/// To instantiate a new `Frontend` the [`Default`] trait is used, so a
132/// call to the associated function [`Frontend::default`](Frontend::default) will
133/// return a new `Frontend` instance.
134///
135/// To parse a shader simply call the [`parse`](Frontend::parse) method with a
136/// [`Options`] struct and a [`&str`](str) holding the glsl code.
137///
138/// The `Frontend` also provides the [`metadata`](Frontend::metadata) to get some
139/// further information about the previously parsed shader, like version and
140/// extensions used (see the documentation for
141/// [`ShaderMetadata`] to see all the returned information)
142///
143/// # Example usage
144/// ```rust
145/// use naga::ShaderStage;
146/// use naga::front::glsl::{Frontend, Options};
147///
148/// let glsl = r#"
149///     #version 450 core
150///
151///     void main() {}
152/// "#;
153///
154/// let mut frontend = Frontend::default();
155/// let options = Options::from(ShaderStage::Vertex);
156/// frontend.parse(&options, glsl);
157/// ```
158///
159/// # Reusability
160///
161/// If there's a need to parse more than one shader reusing the same `Frontend`
162/// instance may be beneficial since internal allocations will be reused.
163///
164/// Calling the [`parse`](Frontend::parse) method multiple times will reset the
165/// `Frontend` so no extra care is needed when reusing.
166#[derive(Debug, Default)]
167pub struct Frontend {
168    meta: ShaderMetadata,
169
170    lookup_function: FastHashMap<String, FunctionDeclaration>,
171    lookup_type: FastHashMap<String, Handle<Type>>,
172
173    global_variables: Vec<(String, GlobalLookup)>,
174
175    entry_args: Vec<EntryArg>,
176
177    layouter: Layouter,
178
179    errors: Vec<Error>,
180}
181
182impl Frontend {
183    fn reset(&mut self, stage: ShaderStage) {
184        self.meta.reset(stage);
185
186        self.lookup_function.clear();
187        self.lookup_type.clear();
188        self.global_variables.clear();
189        self.entry_args.clear();
190        self.layouter.clear();
191    }
192
193    /// Parses a shader either outputting a shader [`Module`] or a list of
194    /// [`Error`]s.
195    ///
196    /// Multiple calls using the same `Frontend` and different shaders are supported.
197    pub fn parse(
198        &mut self,
199        options: &Options,
200        source: &str,
201    ) -> core::result::Result<Module, ParseErrors> {
202        self.reset(options.stage);
203
204        let lexer = lex::Lexer::new(source, &options.defines);
205        let mut ctx = ParsingContext::new(lexer);
206
207        match ctx.parse(self) {
208            Ok(module) => {
209                if self.errors.is_empty() {
210                    Ok(module)
211                } else {
212                    Err(core::mem::take(&mut self.errors).into())
213                }
214            }
215            Err(e) => {
216                self.errors.push(e);
217                Err(core::mem::take(&mut self.errors).into())
218            }
219        }
220    }
221
222    /// Returns additional information about the parsed shader which might not
223    /// be stored in the [`Module`], see the documentation for
224    /// [`ShaderMetadata`] for more information about the returned data.
225    ///
226    /// # Notes
227    ///
228    /// Following an unsuccessful parsing the state of the returned information
229    /// is undefined, it might contain only partial information about the
230    /// current shader, the previous shader or both.
231    pub const fn metadata(&self) -> &ShaderMetadata {
232        &self.meta
233    }
234}