naga/front/glsl/
ast.rs

1use alloc::{borrow::Cow, string::String, vec::Vec};
2use core::fmt;
3
4use super::{builtins::MacroCall, Span};
5use crate::{
6    AddressSpace, BinaryOperator, Binding, Constant, Expression, Function, GlobalVariable, Handle,
7    Interpolation, Literal, Override, Sampling, StorageAccess, Type, UnaryOperator,
8};
9
10#[derive(Debug, Clone, Copy)]
11pub enum GlobalLookupKind {
12    Variable(Handle<GlobalVariable>),
13    Constant(Handle<Constant>, Handle<Type>),
14    Override(Handle<Override>, Handle<Type>),
15    BlockSelect(Handle<GlobalVariable>, u32),
16}
17
18#[derive(Debug, Clone, Copy)]
19pub struct GlobalLookup {
20    pub kind: GlobalLookupKind,
21    pub entry_arg: Option<usize>,
22    pub mutable: bool,
23}
24
25#[derive(Debug, Clone)]
26pub struct ParameterInfo {
27    pub qualifier: ParameterQualifier,
28    /// Whether the parameter should be treated as a depth image instead of a
29    /// sampled image.
30    pub depth: bool,
31}
32
33/// How the function is implemented
34#[derive(Clone, Copy)]
35pub enum FunctionKind {
36    /// The function is user defined
37    Call(Handle<Function>),
38    /// The function is a builtin
39    Macro(MacroCall),
40}
41
42impl fmt::Debug for FunctionKind {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        match *self {
45            Self::Call(_) => write!(f, "Call"),
46            Self::Macro(_) => write!(f, "Macro"),
47        }
48    }
49}
50
51#[derive(Debug)]
52pub struct Overload {
53    /// Normalized function parameters, modifiers are not applied
54    pub parameters: Vec<Handle<Type>>,
55    pub parameters_info: Vec<ParameterInfo>,
56    /// How the function is implemented
57    pub kind: FunctionKind,
58    /// Whether this function was already defined or is just a prototype
59    pub defined: bool,
60    /// Whether this overload is the one provided by the language or has
61    /// been redeclared by the user (builtins only)
62    pub internal: bool,
63    /// Whether or not this function returns void (nothing)
64    pub void: bool,
65}
66
67bitflags::bitflags! {
68    /// Tracks the variations of the builtin already generated, this is needed because some
69    /// builtins overloads can't be generated unless explicitly used, since they might cause
70    /// unneeded capabilities to be requested
71    #[derive(Default)]
72    #[derive(Clone, Copy, Debug, Eq, PartialEq)]
73    pub struct BuiltinVariations: u32 {
74        /// Request the standard overloads
75        const STANDARD = 1 << 0;
76        /// Request overloads that use the double type
77        const DOUBLE = 1 << 1;
78        /// Request overloads that use `samplerCubeArray(Shadow)`
79        const CUBE_TEXTURES_ARRAY = 1 << 2;
80        /// Request overloads that use `sampler2DMSArray`
81        const D2_MULTI_TEXTURES_ARRAY = 1 << 3;
82    }
83}
84
85#[derive(Debug, Default)]
86pub struct FunctionDeclaration {
87    pub overloads: Vec<Overload>,
88    /// Tracks the builtin overload variations that were already generated
89    pub variations: BuiltinVariations,
90}
91
92#[derive(Debug)]
93pub struct EntryArg {
94    pub name: Option<String>,
95    pub binding: Binding,
96    pub handle: Handle<GlobalVariable>,
97    pub storage: StorageQualifier,
98}
99
100#[derive(Debug, Clone)]
101pub struct VariableReference {
102    pub expr: Handle<Expression>,
103    /// Whether the variable is of a pointer type (and needs loading) or not
104    pub load: bool,
105    /// Whether the value of the variable can be changed or not
106    pub mutable: bool,
107    pub constant: Option<(Handle<Constant>, Handle<Type>)>,
108    pub entry_arg: Option<usize>,
109}
110
111#[derive(Debug, Clone)]
112pub struct HirExpr {
113    pub kind: HirExprKind,
114    pub meta: Span,
115}
116
117#[derive(Debug, Clone)]
118pub enum HirExprKind {
119    /// Represents a sequence of expressions. It returns the type and value of the last (i.e. right-most) expression.
120    Sequence {
121        exprs: Vec<Handle<HirExpr>>,
122    },
123    Access {
124        base: Handle<HirExpr>,
125        index: Handle<HirExpr>,
126    },
127    Select {
128        base: Handle<HirExpr>,
129        field: String,
130    },
131    Literal(Literal),
132    Binary {
133        left: Handle<HirExpr>,
134        op: BinaryOperator,
135        right: Handle<HirExpr>,
136    },
137    Unary {
138        op: UnaryOperator,
139        expr: Handle<HirExpr>,
140    },
141    Variable(VariableReference),
142    Call(FunctionCall),
143    /// Represents the ternary operator in glsl (`:?`)
144    Conditional {
145        /// The expression that will decide which branch to take, must evaluate to a boolean
146        condition: Handle<HirExpr>,
147        /// The expression that will be evaluated if [`condition`] returns `true`
148        ///
149        /// [`condition`]: Self::Conditional::condition
150        accept: Handle<HirExpr>,
151        /// The expression that will be evaluated if [`condition`] returns `false`
152        ///
153        /// [`condition`]: Self::Conditional::condition
154        reject: Handle<HirExpr>,
155    },
156    Assign {
157        tgt: Handle<HirExpr>,
158        value: Handle<HirExpr>,
159    },
160    /// A prefix/postfix operator like `++`
161    PrePostfix {
162        /// The operation to be performed
163        op: BinaryOperator,
164        /// Whether this is a postfix or a prefix
165        postfix: bool,
166        /// The target expression
167        expr: Handle<HirExpr>,
168    },
169    /// A method call like `what.something(a, b, c)`
170    Method {
171        /// expression the method call applies to (`what` in the example)
172        expr: Handle<HirExpr>,
173        /// the method name (`something` in the example)
174        name: String,
175        /// the arguments to the method (`a`, `b`, and `c` in the example)
176        args: Vec<Handle<HirExpr>>,
177    },
178}
179
180#[derive(Debug, Hash, PartialEq, Eq)]
181pub enum QualifierKey<'a> {
182    String(Cow<'a, str>),
183    /// Used for `std140` and `std430` layout qualifiers
184    Layout,
185    /// Used for image formats
186    Format,
187    /// Used for `index` layout qualifiers
188    Index,
189}
190
191#[derive(Debug)]
192pub enum QualifierValue {
193    None,
194    Uint(u32),
195    Layout(StructLayout),
196    Format(crate::StorageFormat),
197}
198
199#[derive(Debug, Default)]
200pub struct TypeQualifiers<'a> {
201    pub span: Span,
202    pub storage: (StorageQualifier, Span),
203    pub invariant: Option<Span>,
204    pub interpolation: Option<(Interpolation, Span)>,
205    pub precision: Option<(Precision, Span)>,
206    pub sampling: Option<(Sampling, Span)>,
207    /// Memory qualifiers used in the declaration to set the storage access to be used
208    /// in declarations that support it (storage images and buffers)
209    pub storage_access: Option<(StorageAccess, Span)>,
210    pub layout_qualifiers: crate::FastHashMap<QualifierKey<'a>, (QualifierValue, Span)>,
211}
212
213impl<'a> TypeQualifiers<'a> {
214    /// Appends `errors` with errors for all unused qualifiers
215    pub fn unused_errors(&self, errors: &mut Vec<super::Error>) {
216        if let Some(meta) = self.invariant {
217            errors.push(super::Error {
218                kind: super::ErrorKind::SemanticError(
219                    "Invariant qualifier can only be used in in/out variables".into(),
220                ),
221                meta,
222            });
223        }
224
225        if let Some((_, meta)) = self.interpolation {
226            errors.push(super::Error {
227                kind: super::ErrorKind::SemanticError(
228                    "Interpolation qualifiers can only be used in in/out variables".into(),
229                ),
230                meta,
231            });
232        }
233
234        if let Some((_, meta)) = self.sampling {
235            errors.push(super::Error {
236                kind: super::ErrorKind::SemanticError(
237                    "Sampling qualifiers can only be used in in/out variables".into(),
238                ),
239                meta,
240            });
241        }
242
243        if let Some((_, meta)) = self.storage_access {
244            errors.push(super::Error {
245                kind: super::ErrorKind::SemanticError(
246                    "Memory qualifiers can only be used in storage variables".into(),
247                ),
248                meta,
249            });
250        }
251
252        for &(_, meta) in self.layout_qualifiers.values() {
253            errors.push(super::Error {
254                kind: super::ErrorKind::SemanticError("Unexpected qualifier".into()),
255                meta,
256            });
257        }
258    }
259
260    /// Removes the layout qualifier with `name`, if it exists and adds an error if it isn't
261    /// a [`QualifierValue::Uint`]
262    pub fn uint_layout_qualifier(
263        &mut self,
264        name: &'a str,
265        errors: &mut Vec<super::Error>,
266    ) -> Option<u32> {
267        match self
268            .layout_qualifiers
269            .remove(&QualifierKey::String(name.into()))
270        {
271            Some((QualifierValue::Uint(v), _)) => Some(v),
272            Some((_, meta)) => {
273                errors.push(super::Error {
274                    kind: super::ErrorKind::SemanticError("Qualifier expects a uint value".into()),
275                    meta,
276                });
277                // Return a dummy value instead of `None` to differentiate from
278                // the qualifier not existing, since some parts might require the
279                // qualifier to exist and throwing another error that it doesn't
280                // exist would be unhelpful
281                Some(0)
282            }
283            _ => None,
284        }
285    }
286
287    /// Removes the layout qualifier with `name`, if it exists and adds an error if it isn't
288    /// a [`QualifierValue::None`]
289    pub fn none_layout_qualifier(&mut self, name: &'a str, errors: &mut Vec<super::Error>) -> bool {
290        match self
291            .layout_qualifiers
292            .remove(&QualifierKey::String(name.into()))
293        {
294            Some((QualifierValue::None, _)) => true,
295            Some((_, meta)) => {
296                errors.push(super::Error {
297                    kind: super::ErrorKind::SemanticError(
298                        "Qualifier doesn't expect a value".into(),
299                    ),
300                    meta,
301                });
302                // Return a `true` to since the qualifier is defined and adding
303                // another error for it not being defined would be unhelpful
304                true
305            }
306            _ => false,
307        }
308    }
309}
310
311#[derive(Debug, Clone)]
312pub enum FunctionCallKind {
313    TypeConstructor(Handle<Type>),
314    Function(String),
315}
316
317#[derive(Debug, Clone)]
318pub struct FunctionCall {
319    pub kind: FunctionCallKind,
320    pub args: Vec<Handle<HirExpr>>,
321}
322
323#[derive(Debug, Clone, Copy, PartialEq)]
324pub enum StorageQualifier {
325    AddressSpace(AddressSpace),
326    Input,
327    Output,
328    Const,
329}
330
331impl Default for StorageQualifier {
332    fn default() -> Self {
333        StorageQualifier::AddressSpace(AddressSpace::Function)
334    }
335}
336
337#[derive(Debug, Clone, Copy, PartialEq, Eq)]
338pub enum StructLayout {
339    Std140,
340    Std430,
341}
342
343// TODO: Encode precision hints in the IR
344/// A precision hint used in GLSL declarations.
345///
346/// Precision hints can be used to either speed up shader execution or control
347/// the precision of arithmetic operations.
348///
349/// To use a precision hint simply add it before the type in the declaration.
350/// ```glsl
351/// mediump float a;
352/// ```
353///
354/// The default when no precision is declared is `highp` which means that all
355/// operations operate with the type defined width.
356///
357/// For `mediump` and `lowp` operations follow the spir-v
358/// [`RelaxedPrecision`][RelaxedPrecision] decoration semantics.
359///
360/// [RelaxedPrecision]: https://www.khronos.org/registry/SPIR-V/specs/unified1/SPIRV.html#_a_id_relaxedprecisionsection_a_relaxed_precision
361#[derive(Debug, Clone, PartialEq, Copy)]
362pub enum Precision {
363    /// `lowp` precision
364    Low,
365    /// `mediump` precision
366    Medium,
367    /// `highp` precision
368    High,
369}
370
371#[derive(Debug, Clone, PartialEq, Copy)]
372pub enum ParameterQualifier {
373    In,
374    Out,
375    InOut,
376    Const,
377}
378
379impl ParameterQualifier {
380    /// Returns true if the argument should be passed as a lhs expression
381    pub const fn is_lhs(&self) -> bool {
382        match *self {
383            ParameterQualifier::Out | ParameterQualifier::InOut => true,
384            _ => false,
385        }
386    }
387}
388
389/// The GLSL profile used by a shader.
390#[derive(Debug, Clone, Copy, PartialEq)]
391pub enum Profile {
392    /// The `core` profile, default when no profile is specified.
393    Core,
394}