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