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