naga/front/glsl/
variables.rs

1use alloc::{format, string::String, vec::Vec};
2
3use super::{
4    ast::*,
5    context::{Context, ExprPos},
6    error::{Error, ErrorKind},
7    Frontend, Result, Span,
8};
9use crate::{
10    AddressSpace, Binding, BuiltIn, Constant, Expression, GlobalVariable, Handle, Interpolation,
11    LocalVariable, ResourceBinding, Scalar, ScalarKind, ShaderStage, SwizzleComponent, Type,
12    TypeInner, VectorSize,
13};
14
15pub struct VarDeclaration<'a, 'key> {
16    pub qualifiers: &'a mut TypeQualifiers<'key>,
17    pub ty: Handle<Type>,
18    pub name: Option<String>,
19    pub init: Option<Handle<Expression>>,
20    pub meta: Span,
21}
22
23/// Information about a builtin used in [`add_builtin`](Frontend::add_builtin).
24struct BuiltInData {
25    /// The type of the builtin.
26    inner: TypeInner,
27    /// The associated builtin class.
28    builtin: BuiltIn,
29    /// Whether the builtin can be written to or not.
30    mutable: bool,
31    /// The storage used for the builtin.
32    storage: StorageQualifier,
33}
34
35pub enum GlobalOrConstant {
36    Global(Handle<GlobalVariable>),
37    Constant(Handle<Constant>),
38}
39
40impl Frontend {
41    /// Adds a builtin and returns a variable reference to it
42    fn add_builtin(
43        &mut self,
44        ctx: &mut Context,
45        name: &str,
46        data: BuiltInData,
47        meta: Span,
48    ) -> Result<Option<VariableReference>> {
49        let ty = ctx.module.types.insert(
50            Type {
51                name: None,
52                inner: data.inner,
53            },
54            meta,
55        );
56
57        let handle = ctx.module.global_variables.append(
58            GlobalVariable {
59                name: Some(name.into()),
60                space: AddressSpace::Private,
61                binding: None,
62                ty,
63                init: None,
64            },
65            meta,
66        );
67
68        let idx = self.entry_args.len();
69        self.entry_args.push(EntryArg {
70            name: Some(name.into()),
71            binding: Binding::BuiltIn(data.builtin),
72            handle,
73            storage: data.storage,
74        });
75
76        self.global_variables.push((
77            name.into(),
78            GlobalLookup {
79                kind: GlobalLookupKind::Variable(handle),
80                entry_arg: Some(idx),
81                mutable: data.mutable,
82            },
83        ));
84
85        let expr = ctx.add_expression(Expression::GlobalVariable(handle), meta)?;
86
87        let var = VariableReference {
88            expr,
89            load: true,
90            mutable: data.mutable,
91            constant: None,
92            entry_arg: Some(idx),
93        };
94
95        ctx.symbol_table.add_root(name.into(), var.clone());
96
97        Ok(Some(var))
98    }
99
100    pub(crate) fn lookup_variable(
101        &mut self,
102        ctx: &mut Context,
103        name: &str,
104        meta: Span,
105    ) -> Result<Option<VariableReference>> {
106        if let Some(var) = ctx.symbol_table.lookup(name).cloned() {
107            return Ok(Some(var));
108        }
109
110        let data = match name {
111            "gl_Position" => BuiltInData {
112                inner: TypeInner::Vector {
113                    size: VectorSize::Quad,
114                    scalar: Scalar::F32,
115                },
116                builtin: BuiltIn::Position { invariant: false },
117                mutable: true,
118                storage: StorageQualifier::Output,
119            },
120            "gl_FragCoord" => BuiltInData {
121                inner: TypeInner::Vector {
122                    size: VectorSize::Quad,
123                    scalar: Scalar::F32,
124                },
125                builtin: BuiltIn::Position { invariant: false },
126                mutable: false,
127                storage: StorageQualifier::Input,
128            },
129            "gl_PointCoord" => BuiltInData {
130                inner: TypeInner::Vector {
131                    size: VectorSize::Bi,
132                    scalar: Scalar::F32,
133                },
134                builtin: BuiltIn::PointCoord,
135                mutable: false,
136                storage: StorageQualifier::Input,
137            },
138            "gl_GlobalInvocationID"
139            | "gl_NumWorkGroups"
140            | "gl_WorkGroupSize"
141            | "gl_WorkGroupID"
142            | "gl_LocalInvocationID" => BuiltInData {
143                inner: TypeInner::Vector {
144                    size: VectorSize::Tri,
145                    scalar: Scalar::U32,
146                },
147                builtin: match name {
148                    "gl_GlobalInvocationID" => BuiltIn::GlobalInvocationId,
149                    "gl_NumWorkGroups" => BuiltIn::NumWorkGroups,
150                    "gl_WorkGroupSize" => BuiltIn::WorkGroupSize,
151                    "gl_WorkGroupID" => BuiltIn::WorkGroupId,
152                    "gl_LocalInvocationID" => BuiltIn::LocalInvocationId,
153                    _ => unreachable!(),
154                },
155                mutable: false,
156                storage: StorageQualifier::Input,
157            },
158            "gl_FrontFacing" => BuiltInData {
159                inner: TypeInner::Scalar(Scalar::BOOL),
160                builtin: BuiltIn::FrontFacing,
161                mutable: false,
162                storage: StorageQualifier::Input,
163            },
164            "gl_PointSize" | "gl_FragDepth" => BuiltInData {
165                inner: TypeInner::Scalar(Scalar::F32),
166                builtin: match name {
167                    "gl_PointSize" => BuiltIn::PointSize,
168                    "gl_FragDepth" => BuiltIn::FragDepth,
169                    _ => unreachable!(),
170                },
171                mutable: true,
172                storage: StorageQualifier::Output,
173            },
174            "gl_ClipDistance" | "gl_CullDistance" => {
175                let base = ctx.module.types.insert(
176                    Type {
177                        name: None,
178                        inner: TypeInner::Scalar(Scalar::F32),
179                    },
180                    meta,
181                );
182
183                BuiltInData {
184                    inner: TypeInner::Array {
185                        base,
186                        size: crate::ArraySize::Dynamic,
187                        stride: 4,
188                    },
189                    builtin: match name {
190                        "gl_ClipDistance" => BuiltIn::ClipDistance,
191                        "gl_CullDistance" => BuiltIn::CullDistance,
192                        _ => unreachable!(),
193                    },
194                    mutable: self.meta.stage == ShaderStage::Vertex,
195                    storage: StorageQualifier::Output,
196                }
197            }
198            _ => {
199                let builtin = match name {
200                    "gl_BaseVertex" => BuiltIn::BaseVertex,
201                    "gl_BaseInstance" => BuiltIn::BaseInstance,
202                    "gl_PrimitiveID" => BuiltIn::PrimitiveIndex,
203                    "gl_BaryCoordEXT" => BuiltIn::Barycentric,
204                    "gl_InstanceIndex" => BuiltIn::InstanceIndex,
205                    "gl_VertexIndex" => BuiltIn::VertexIndex,
206                    "gl_SampleID" => BuiltIn::SampleIndex,
207                    "gl_LocalInvocationIndex" => BuiltIn::LocalInvocationIndex,
208                    "gl_DrawID" => BuiltIn::DrawID,
209                    _ => return Ok(None),
210                };
211
212                BuiltInData {
213                    inner: TypeInner::Scalar(Scalar::U32),
214                    builtin,
215                    mutable: false,
216                    storage: StorageQualifier::Input,
217                }
218            }
219        };
220
221        self.add_builtin(ctx, name, data, meta)
222    }
223
224    pub(crate) fn make_variable_invariant(
225        &mut self,
226        ctx: &mut Context,
227        name: &str,
228        meta: Span,
229    ) -> Result<()> {
230        if let Some(var) = self.lookup_variable(ctx, name, meta)? {
231            if let Some(index) = var.entry_arg {
232                if let Binding::BuiltIn(BuiltIn::Position { ref mut invariant }) =
233                    self.entry_args[index].binding
234                {
235                    *invariant = true;
236                }
237            }
238        }
239        Ok(())
240    }
241
242    pub(crate) fn field_selection(
243        &mut self,
244        ctx: &mut Context,
245        pos: ExprPos,
246        expression: Handle<Expression>,
247        name: &str,
248        meta: Span,
249    ) -> Result<Handle<Expression>> {
250        let (ty, is_pointer) = match *ctx.resolve_type(expression, meta)? {
251            TypeInner::Pointer { base, .. } => (&ctx.module.types[base].inner, true),
252            ref ty => (ty, false),
253        };
254        match *ty {
255            TypeInner::Struct { ref members, .. } => {
256                let index = members
257                    .iter()
258                    .position(|m| m.name == Some(name.into()))
259                    .ok_or_else(|| Error {
260                        kind: ErrorKind::UnknownField(name.into()),
261                        meta,
262                    })?;
263                let pointer = ctx.add_expression(
264                    Expression::AccessIndex {
265                        base: expression,
266                        index: index as u32,
267                    },
268                    meta,
269                )?;
270
271                Ok(match pos {
272                    ExprPos::Rhs if is_pointer => {
273                        ctx.add_expression(Expression::Load { pointer }, meta)?
274                    }
275                    _ => pointer,
276                })
277            }
278            // swizzles (xyzw, rgba, stpq)
279            TypeInner::Vector { size, .. } => {
280                let check_swizzle_components = |comps: &str| {
281                    name.chars()
282                        .map(|c| {
283                            comps
284                                .find(c)
285                                .filter(|i| *i < size as usize)
286                                .map(|i| SwizzleComponent::from_index(i as u32))
287                        })
288                        .collect::<Option<Vec<SwizzleComponent>>>()
289                };
290
291                let components = check_swizzle_components("xyzw")
292                    .or_else(|| check_swizzle_components("rgba"))
293                    .or_else(|| check_swizzle_components("stpq"));
294
295                if let Some(components) = components {
296                    if let ExprPos::Lhs = pos {
297                        let not_unique = (1..components.len())
298                            .any(|i| components[i..].contains(&components[i - 1]));
299                        if not_unique {
300                            self.errors.push(Error {
301                                kind: ErrorKind::SemanticError(
302                                    format!(
303                                        concat!(
304                                            "swizzle cannot have duplicate components in ",
305                                            "left-hand-side expression for \"{:?}\""
306                                        ),
307                                        name
308                                    )
309                                    .into(),
310                                ),
311                                meta,
312                            })
313                        }
314                    }
315
316                    let mut pattern = [SwizzleComponent::X; 4];
317                    for (pat, component) in pattern.iter_mut().zip(&components) {
318                        *pat = *component;
319                    }
320
321                    // flatten nested swizzles (vec.zyx.xy.x => vec.z)
322                    let mut expression = expression;
323                    if let Expression::Swizzle {
324                        size: _,
325                        vector,
326                        pattern: ref src_pattern,
327                    } = ctx[expression]
328                    {
329                        expression = vector;
330                        for pat in &mut pattern {
331                            *pat = src_pattern[pat.index() as usize];
332                        }
333                    }
334
335                    let size = match components.len() {
336                        // Swizzles with just one component are accesses and not swizzles
337                        1 => {
338                            match pos {
339                                // If the position is in the right hand side and the base
340                                // vector is a pointer, load it, otherwise the swizzle would
341                                // produce a pointer
342                                ExprPos::Rhs if is_pointer => {
343                                    expression = ctx.add_expression(
344                                        Expression::Load {
345                                            pointer: expression,
346                                        },
347                                        meta,
348                                    )?;
349                                }
350                                _ => {}
351                            };
352                            return ctx.add_expression(
353                                Expression::AccessIndex {
354                                    base: expression,
355                                    index: pattern[0].index(),
356                                },
357                                meta,
358                            );
359                        }
360                        2 => VectorSize::Bi,
361                        3 => VectorSize::Tri,
362                        4 => VectorSize::Quad,
363                        _ => {
364                            self.errors.push(Error {
365                                kind: ErrorKind::SemanticError(
366                                    format!("Bad swizzle size for \"{name:?}\"").into(),
367                                ),
368                                meta,
369                            });
370
371                            VectorSize::Quad
372                        }
373                    };
374
375                    if is_pointer {
376                        // NOTE: for lhs expression, this extra load ends up as an unused expr, because the
377                        // assignment will extract the pointer and use it directly anyway. Unfortunately we
378                        // need it for validation to pass, as swizzles cannot operate on pointer values.
379                        expression = ctx.add_expression(
380                            Expression::Load {
381                                pointer: expression,
382                            },
383                            meta,
384                        )?;
385                    }
386
387                    Ok(ctx.add_expression(
388                        Expression::Swizzle {
389                            size,
390                            vector: expression,
391                            pattern,
392                        },
393                        meta,
394                    )?)
395                } else {
396                    Err(Error {
397                        kind: ErrorKind::SemanticError(
398                            format!("Invalid swizzle for vector \"{name}\"").into(),
399                        ),
400                        meta,
401                    })
402                }
403            }
404            _ => Err(Error {
405                kind: ErrorKind::SemanticError(
406                    format!("Can't lookup field on this type \"{name}\"").into(),
407                ),
408                meta,
409            }),
410        }
411    }
412
413    pub(crate) fn add_global_var(
414        &mut self,
415        ctx: &mut Context,
416        VarDeclaration {
417            qualifiers,
418            mut ty,
419            name,
420            init,
421            meta,
422        }: VarDeclaration,
423    ) -> Result<GlobalOrConstant> {
424        let storage = qualifiers.storage.0;
425        let (ret, lookup) = match storage {
426            StorageQualifier::Input | StorageQualifier::Output => {
427                let input = storage == StorageQualifier::Input;
428                // TODO: glslang seems to use a counter for variables without
429                // explicit location (even if that causes collisions)
430                let location = qualifiers
431                    .uint_layout_qualifier("location", &mut self.errors)
432                    .unwrap_or(0);
433                let interpolation = qualifiers.interpolation.take().map(|(i, _)| i).or_else(|| {
434                    let kind = ctx.module.types[ty].inner.scalar_kind()?;
435                    Some(match kind {
436                        ScalarKind::Float => Interpolation::Perspective,
437                        _ => Interpolation::Flat,
438                    })
439                });
440                let sampling = qualifiers.sampling.take().map(|(s, _)| s);
441
442                let handle = ctx.module.global_variables.append(
443                    GlobalVariable {
444                        name: name.clone(),
445                        space: AddressSpace::Private,
446                        binding: None,
447                        ty,
448                        init,
449                    },
450                    meta,
451                );
452
453                let blend_src = qualifiers
454                    .layout_qualifiers
455                    .remove(&QualifierKey::Index)
456                    .and_then(|(value, _span)| match value {
457                        QualifierValue::Uint(index) => Some(index),
458                        _ => None,
459                    });
460
461                let idx = self.entry_args.len();
462                self.entry_args.push(EntryArg {
463                    name: name.clone(),
464                    binding: Binding::Location {
465                        location,
466                        interpolation,
467                        sampling,
468                        blend_src,
469                        per_primitive: false,
470                    },
471                    handle,
472                    storage,
473                });
474
475                let lookup = GlobalLookup {
476                    kind: GlobalLookupKind::Variable(handle),
477                    entry_arg: Some(idx),
478                    mutable: !input,
479                };
480
481                (GlobalOrConstant::Global(handle), lookup)
482            }
483            StorageQualifier::Const => {
484                let init = init.ok_or_else(|| Error {
485                    kind: ErrorKind::SemanticError("const values must have an initializer".into()),
486                    meta,
487                })?;
488
489                let constant = Constant {
490                    name: name.clone(),
491                    ty,
492                    init,
493                };
494                let handle = ctx.module.constants.append(constant, meta);
495
496                let lookup = GlobalLookup {
497                    kind: GlobalLookupKind::Constant(handle, ty),
498                    entry_arg: None,
499                    mutable: false,
500                };
501
502                (GlobalOrConstant::Constant(handle), lookup)
503            }
504            StorageQualifier::AddressSpace(mut space) => {
505                match space {
506                    AddressSpace::Storage { ref mut access } => {
507                        if let Some((allowed_access, _)) = qualifiers.storage_access.take() {
508                            *access = allowed_access;
509                        }
510                    }
511                    AddressSpace::Uniform => match ctx.module.types[ty].inner {
512                        TypeInner::Image {
513                            class,
514                            dim,
515                            arrayed,
516                        } => {
517                            if let crate::ImageClass::Storage {
518                                mut access,
519                                mut format,
520                            } = class
521                            {
522                                if let Some((allowed_access, _)) = qualifiers.storage_access.take()
523                                {
524                                    access = allowed_access;
525                                }
526
527                                match qualifiers.layout_qualifiers.remove(&QualifierKey::Format) {
528                                    Some((QualifierValue::Format(f), _)) => format = f,
529                                    // TODO: glsl supports images without format qualifier
530                                    // if they are `writeonly`
531                                    None => self.errors.push(Error {
532                                        kind: ErrorKind::SemanticError(
533                                            "image types require a format layout qualifier".into(),
534                                        ),
535                                        meta,
536                                    }),
537                                    _ => unreachable!(),
538                                }
539
540                                ty = ctx.module.types.insert(
541                                    Type {
542                                        name: None,
543                                        inner: TypeInner::Image {
544                                            dim,
545                                            arrayed,
546                                            class: crate::ImageClass::Storage { format, access },
547                                        },
548                                    },
549                                    meta,
550                                );
551                            }
552
553                            space = AddressSpace::Handle
554                        }
555                        TypeInner::Sampler { .. } => space = AddressSpace::Handle,
556                        _ => {
557                            if qualifiers.none_layout_qualifier("push_constant", &mut self.errors) {
558                                space = AddressSpace::PushConstant
559                            }
560                        }
561                    },
562                    AddressSpace::Function => space = AddressSpace::Private,
563                    _ => {}
564                };
565
566                let binding = match space {
567                    AddressSpace::Uniform | AddressSpace::Storage { .. } | AddressSpace::Handle => {
568                        let binding = qualifiers.uint_layout_qualifier("binding", &mut self.errors);
569                        if binding.is_none() {
570                            self.errors.push(Error {
571                                kind: ErrorKind::SemanticError(
572                                    "uniform/buffer blocks require layout(binding=X)".into(),
573                                ),
574                                meta,
575                            });
576                        }
577                        let set = qualifiers.uint_layout_qualifier("set", &mut self.errors);
578                        binding.map(|binding| ResourceBinding {
579                            group: set.unwrap_or(0),
580                            binding,
581                        })
582                    }
583                    _ => None,
584                };
585
586                let handle = ctx.module.global_variables.append(
587                    GlobalVariable {
588                        name: name.clone(),
589                        space,
590                        binding,
591                        ty,
592                        init,
593                    },
594                    meta,
595                );
596
597                let lookup = GlobalLookup {
598                    kind: GlobalLookupKind::Variable(handle),
599                    entry_arg: None,
600                    mutable: true,
601                };
602
603                (GlobalOrConstant::Global(handle), lookup)
604            }
605        };
606
607        if let Some(name) = name {
608            ctx.add_global(&name, lookup)?;
609
610            self.global_variables.push((name, lookup));
611        }
612
613        qualifiers.unused_errors(&mut self.errors);
614
615        Ok(ret)
616    }
617
618    pub(crate) fn add_local_var(
619        &mut self,
620        ctx: &mut Context,
621        decl: VarDeclaration,
622    ) -> Result<Handle<Expression>> {
623        let storage = decl.qualifiers.storage;
624        let mutable = match storage.0 {
625            StorageQualifier::AddressSpace(AddressSpace::Function) => true,
626            StorageQualifier::Const => false,
627            _ => {
628                self.errors.push(Error {
629                    kind: ErrorKind::SemanticError("Locals cannot have a storage qualifier".into()),
630                    meta: storage.1,
631                });
632                true
633            }
634        };
635
636        let handle = ctx.locals.append(
637            LocalVariable {
638                name: decl.name.clone(),
639                ty: decl.ty,
640                init: decl.init,
641            },
642            decl.meta,
643        );
644        let expr = ctx.add_expression(Expression::LocalVariable(handle), decl.meta)?;
645
646        if let Some(name) = decl.name {
647            let maybe_var = ctx.add_local_var(name.clone(), expr, mutable);
648
649            if maybe_var.is_some() {
650                self.errors.push(Error {
651                    kind: ErrorKind::VariableAlreadyDeclared(name),
652                    meta: decl.meta,
653                })
654            }
655        }
656
657        decl.qualifiers.unused_errors(&mut self.errors);
658
659        Ok(expr)
660    }
661}