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}