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