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