Flutter Engine
The Flutter Engine
SkSLMemoryLayout.h
Go to the documentation of this file.
1/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef SKIASL_MEMORYLAYOUT
9#define SKIASL_MEMORYLAYOUT
10
11#include <algorithm>
12
14
15namespace SkSL {
16
18public:
19 enum class Standard {
20 // GLSL std140 layout as described in OpenGL Spec v4.5, 7.6.2.2.
21 k140,
22
23 // GLSL std430 layout. This layout is like std140 but with optimizations. This layout can
24 // ONLY be used with shader storage blocks.
25 k430,
26
27 // MSL memory layout.
28 kMetal,
29
30 // WebGPU Shading Language buffer layout constraints for the uniform address space.
31 kWGSLUniform_Base, // treats f16 as a full 32-bit float
32 kWGSLUniform_EnableF16, // treats f16 as a 16-bit half float
33
34 // WebGPU Shading Language buffer layout constraints for the storage address space.
35 kWGSLStorage_Base,
36 kWGSLStorage_EnableF16,
37 };
38
40
41 bool isWGSL_Base() const {
42 return fStd == Standard::kWGSLUniform_Base ||
44 }
45
46 bool isWGSL_F16() const {
47 return fStd == Standard::kWGSLUniform_EnableF16 ||
49 }
50
51 bool isWGSL_Uniform() const {
52 return fStd == Standard::kWGSLUniform_Base ||
54 }
55
56 bool isWGSL() const {
57 return fStd == Standard::kWGSLUniform_Base ||
61 }
62
63 bool isMetal() const {
64 return fStd == Standard::kMetal;
65 }
66
67 /**
68 * WGSL and std140 require various types of variables (structs, arrays, and matrices) in the
69 * uniform address space to be rounded up to the nearest multiple of 16. This function performs
70 * the rounding depending on the given `type` and the current memory layout standard.
71 *
72 * (For WGSL, see https://www.w3.org/TR/WGSL/#address-space-layout-constraints).
73 */
74 size_t roundUpIfNeeded(size_t raw, Type::TypeKind type) const {
75 if (fStd == Standard::k140) {
76 return roundUp16(raw);
77 }
78 // WGSL uniform matrix layout is simply the alignment of the matrix columns and
79 // doesn't have a 16-byte multiple alignment constraint.
80 if (this->isWGSL_Uniform() && type != Type::TypeKind::kMatrix) {
81 return roundUp16(raw);
82 }
83 return raw;
84 }
85
86 /**
87 * Rounds up the integer `n` to the smallest multiple of 16 greater than `n`.
88 */
89 size_t roundUp16(size_t n) const { return (n + 15) & ~15; }
90
91 /**
92 * Returns a type's required alignment when used as a standalone variable.
93 */
94 size_t alignment(const Type& type) const {
95 // See OpenGL Spec 7.6.2.2 Standard Uniform Block Layout
96 switch (type.typeKind()) {
99 return this->size(type);
100
102 return GetVectorAlignment(this->size(type.componentType()), type.columns());
103
105 return this->roundUpIfNeeded(
106 GetVectorAlignment(this->size(type.componentType()), type.rows()),
107 type.typeKind());
108
110 return this->roundUpIfNeeded(this->alignment(type.componentType()),
111 type.typeKind());
112
114 size_t result = 0;
115 for (const auto& f : type.fields()) {
116 size_t alignment = this->alignment(*f.fType);
117 if (alignment > result) {
119 }
120 }
121 return this->roundUpIfNeeded(result, type.typeKind());
122 }
123 default:
124 SK_ABORT("cannot determine alignment of type '%s'", type.displayName().c_str());
125 }
126 }
127
128 /**
129 * For matrices and arrays, returns the number of bytes from the start of one entry (row, in
130 * the case of matrices) to the start of the next.
131 */
132 size_t stride(const Type& type) const {
133 switch (type.typeKind()) {
135 return this->alignment(type);
136
138 int stride = this->size(type.componentType());
139 if (stride > 0) {
140 int align = this->alignment(type.componentType());
141 stride += align - 1;
142 stride -= stride % align;
143 stride = this->roundUpIfNeeded(stride, type.typeKind());
144 }
145 return stride;
146 }
147 default:
148 SK_ABORT("type '%s' does not have a stride", type.displayName().c_str());
149 }
150 }
151
152 /**
153 * Returns the size of a type in bytes. Returns 0 if the given type is not supported.
154 */
155 size_t size(const Type& type) const {
156 switch (type.typeKind()) {
158 if (type.isBoolean()) {
159 return this->isWGSL() ? 0 : 1;
160 }
161 if (this->isMetal() && !type.highPrecision() && type.isNumber()) {
162 return 2;
163 }
164 if (this->isWGSL_F16() && !type.highPrecision() && type.isFloat()) {
165 return 2;
166 }
167 return 4;
168
170 // Our atomic types (currently atomicUint) always occupy 4 bytes.
171 return 4;
172
174 if (this->isMetal() && type.columns() == 3) {
175 return 4 * this->size(type.componentType());
176 }
177 return type.columns() * this->size(type.componentType());
178
179 case Type::TypeKind::kMatrix: // fall through
181 return type.isUnsizedArray() ? 0 : (type.columns() * this->stride(type));
182
184 size_t total = 0;
185 for (const auto& f : type.fields()) {
186 size_t alignment = this->alignment(*f.fType);
187 if (total % alignment != 0) {
188 total += alignment - total % alignment;
189 }
190 SkASSERT(total % alignment == 0);
191 total += this->size(*f.fType);
192 }
193 size_t alignment = this->alignment(type);
194 SkASSERT(!type.fields().size() ||
195 (0 == alignment % this->alignment(*type.fields()[0].fType)));
196 return (total + alignment - 1) & ~(alignment - 1);
197 }
198 default:
199 SK_ABORT("cannot determine size of type '%s'", type.displayName().c_str());
200 }
201 }
202
203 /**
204 * Not all types are compatible with memory layout.
205 */
206 size_t isSupported(const Type& type) const {
207 switch (type.typeKind()) {
209 return true;
210
212 // bool is not host-shareable in WGSL.
213 return this->isWGSL() ? !type.isBoolean() : true;
214
218 return this->isSupported(type.componentType());
219
221 return std::all_of(
222 type.fields().begin(), type.fields().end(), [this](const Field& f) {
223 return this->isSupported(*f.fType);
224 });
225
226 default:
227 return false;
228 }
229 }
230
231private:
232 static size_t GetVectorAlignment(size_t componentSize, int columns) {
233 return componentSize * (columns + columns % 2);
234 }
235
236 const Standard fStd;
237};
238
239} // namespace SkSL
240
241#endif
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
#define SkASSERT(cond)
Definition: SkAssert.h:116
GLenum type
bool isMetal() const
size_t size(const Type &type) const
size_t stride(const Type &type) const
MemoryLayout(Standard std)
size_t roundUpIfNeeded(size_t raw, Type::TypeKind type) const
bool isWGSL_Uniform() const
size_t roundUp16(size_t n) const
size_t isSupported(const Type &type) const
bool isWGSL_F16() const
bool isWGSL_Base() const
size_t alignment(const Type &type) const
bool highPrecision() const
Definition: SkSLType.h:570
bool isBoolean() const
Definition: SkSLType.h:297
virtual const Type & componentType() const
Definition: SkSLType.h:404
virtual int columns() const
Definition: SkSLType.h:429
@ kMetal
Definition: embedder.h:85
GAsyncResult * result
Definition: ref_ptr.h:256