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