naga/front/glsl/
offset.rs

1/*!
2Module responsible for calculating the offset and span for types.
3
4There exists two types of layouts std140 and std430 (there's technically
5two more layouts, shared and packed. Shared is not supported by spirv. Packed is
6implementation dependent and for now it's just implemented as an alias to
7std140).
8
9The OpenGl spec (the layout rules are defined by the OpenGl spec in section
107.6.2.2 as opposed to the GLSL spec) uses the term basic machine units which are
11equivalent to bytes.
12*/
13
14use alloc::vec::Vec;
15
16use super::{
17    ast::StructLayout,
18    error::{Error, ErrorKind},
19    Span,
20};
21use crate::{proc::Alignment, Handle, Scalar, Type, TypeInner, UniqueArena};
22
23/// Struct with information needed for defining a struct member.
24///
25/// Returned by [`calculate_offset`].
26#[derive(Debug)]
27pub struct TypeAlignSpan {
28    /// The handle to the type, this might be the same handle passed to
29    /// [`calculate_offset`] or a new such a new array type with a different
30    /// stride set.
31    pub ty: Handle<Type>,
32    /// The alignment required by the type.
33    pub align: Alignment,
34    /// The size of the type.
35    pub span: u32,
36}
37
38/// Returns the type, alignment and span of a struct member according to a [`StructLayout`].
39///
40/// The functions returns a [`TypeAlignSpan`] which has a `ty` member this
41/// should be used as the struct member type because for example arrays may have
42/// to change the stride and as such need to have a different type.
43pub fn calculate_offset(
44    mut ty: Handle<Type>,
45    meta: Span,
46    layout: StructLayout,
47    types: &mut UniqueArena<Type>,
48    errors: &mut Vec<Error>,
49) -> TypeAlignSpan {
50    // When using the std430 storage layout, shader storage blocks will be laid out in buffer storage
51    // identically to uniform and shader storage blocks using the std140 layout, except
52    // that the base alignment and stride of arrays of scalars and vectors in rule 4 and of
53    // structures in rule 9 are not rounded up a multiple of the base alignment of a vec4.
54
55    let (align, span) = match types[ty].inner {
56        // 1. If the member is a scalar consuming N basic machine units,
57        // the base alignment is N.
58        TypeInner::Scalar(Scalar { width, .. }) => (Alignment::from_width(width), width as u32),
59        // 2. If the member is a two- or four-component vector with components
60        // consuming N basic machine units, the base alignment is 2N or 4N, respectively.
61        // 3. If the member is a three-component vector with components consuming N
62        // basic machine units, the base alignment is 4N.
63        TypeInner::Vector {
64            size,
65            scalar: Scalar { width, .. },
66        } => (
67            Alignment::from(size) * Alignment::from_width(width),
68            size as u32 * width as u32,
69        ),
70        // 4. If the member is an array of scalars or vectors, the base alignment and array
71        // stride are set to match the base alignment of a single array element, according
72        // to rules (1), (2), and (3), and rounded up to the base alignment of a vec4.
73        // TODO: Matrices array
74        TypeInner::Array { base, size, .. } => {
75            let info = calculate_offset(base, meta, layout, types, errors);
76
77            let name = types[ty].name.clone();
78
79            // See comment at the beginning of the function
80            let (align, stride) = if StructLayout::Std430 == layout {
81                (info.align, info.align.round_up(info.span))
82            } else {
83                let align = info.align.max(Alignment::MIN_UNIFORM);
84                (align, align.round_up(info.span))
85            };
86
87            let span = match size {
88                crate::ArraySize::Constant(size) => size.get() * stride,
89                crate::ArraySize::Pending(_) => unreachable!(),
90                crate::ArraySize::Dynamic => stride,
91            };
92
93            let ty_span = types.get_span(ty);
94            ty = types.insert(
95                Type {
96                    name,
97                    inner: TypeInner::Array {
98                        base: info.ty,
99                        size,
100                        stride,
101                    },
102                },
103                ty_span,
104            );
105
106            (align, span)
107        }
108        // 5. If the member is a column-major matrix with C columns and R rows, the
109        // matrix is stored identically to an array of C column vectors with R
110        // components each, according to rule (4)
111        // TODO: Row major matrices
112        TypeInner::Matrix {
113            columns,
114            rows,
115            scalar,
116        } => {
117            let mut align = Alignment::from(rows) * Alignment::from_width(scalar.width);
118
119            // See comment at the beginning of the function
120            if StructLayout::Std430 != layout {
121                align = align.max(Alignment::MIN_UNIFORM);
122            }
123
124            // See comment on the error kind
125            if StructLayout::Std140 == layout {
126                // Do the f16 test first, as it's more specific
127                if scalar == Scalar::F16 {
128                    errors.push(Error {
129                        kind: ErrorKind::UnsupportedF16MatrixInStd140 {
130                            columns: columns as u8,
131                            rows: rows as u8,
132                        },
133                        meta,
134                    });
135                }
136                if rows == crate::VectorSize::Bi {
137                    errors.push(Error {
138                        kind: ErrorKind::UnsupportedMatrixWithTwoRowsInStd140 {
139                            columns: columns as u8,
140                        },
141                        meta,
142                    });
143                }
144            }
145
146            (align, align * columns as u32)
147        }
148        TypeInner::Struct { ref members, .. } => {
149            let mut span = 0;
150            let mut align = Alignment::ONE;
151            let mut members = members.clone();
152            let name = types[ty].name.clone();
153
154            for member in members.iter_mut() {
155                let info = calculate_offset(member.ty, meta, layout, types, errors);
156
157                let member_alignment = info.align;
158                span = member_alignment.round_up(span);
159                align = member_alignment.max(align);
160
161                member.ty = info.ty;
162                member.offset = span;
163
164                span += info.span;
165            }
166
167            span = align.round_up(span);
168
169            let ty_span = types.get_span(ty);
170            ty = types.insert(
171                Type {
172                    name,
173                    inner: TypeInner::Struct { members, span },
174                },
175                ty_span,
176            );
177
178            (align, span)
179        }
180        _ => {
181            errors.push(Error {
182                kind: ErrorKind::SemanticError("Invalid struct member type".into()),
183                meta,
184            });
185            (Alignment::ONE, 0)
186        }
187    };
188
189    TypeAlignSpan { ty, align, span }
190}