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 "ir/SkSLType.h" 12 13 namespace SkSL { 14 15 class MemoryLayout { 16 public: 17 enum Standard { 18 k140_Standard, 19 k430_Standard, 20 kMetal_Standard 21 }; 22 23 MemoryLayout(Standard std) 24 : fStd(std) {} 25 26 static size_t vector_alignment(size_t componentSize, int columns) { 27 return componentSize * (columns + columns % 2); 28 } 29 30 /** 31 * Rounds up to the nearest multiple of 16 if in std140, otherwise returns the parameter 32 * unchanged (std140 requires various things to be rounded up to the nearest multiple of 16, 33 * std430 does not). 34 */ 35 size_t roundUpIfNeeded(size_t raw) const { 36 switch (fStd) { 37 case k140_Standard: return (raw + 15) & ~15; 38 case k430_Standard: return raw; 39 case kMetal_Standard: return raw; 40 } 41 ABORT("unreachable"); 42 } 43 44 /** 45 * Returns a type's required alignment when used as a standalone variable. 46 */ 47 size_t alignment(const Type& type) const { 48 // See OpenGL Spec 7.6.2.2 Standard Uniform Block Layout 49 switch (type.kind()) { 50 case Type::kScalar_Kind: 51 return this->size(type); 52 case Type::kVector_Kind: 53 return vector_alignment(this->size(type.componentType()), type.columns()); 54 case Type::kMatrix_Kind: 55 return this->roundUpIfNeeded(vector_alignment(this->size(type.componentType()), 56 type.rows())); 57 case Type::kArray_Kind: 58 return this->roundUpIfNeeded(this->alignment(type.componentType())); 59 case Type::kStruct_Kind: { 60 size_t result = 0; 61 for (const auto& f : type.fields()) { 62 size_t alignment = this->alignment(*f.fType); 63 if (alignment > result) { 64 result = alignment; 65 } 66 } 67 return this->roundUpIfNeeded(result); 68 } 69 default: 70 ABORT("cannot determine size of type %s", type.name().c_str()); 71 } 72 } 73 74 /** 75 * For matrices and arrays, returns the number of bytes from the start of one entry (row, in 76 * the case of matrices) to the start of the next. 77 */ 78 size_t stride(const Type& type) const { 79 switch (type.kind()) { 80 case Type::kMatrix_Kind: { 81 size_t base = vector_alignment(this->size(type.componentType()), type.rows()); 82 return this->roundUpIfNeeded(base); 83 } 84 case Type::kArray_Kind: { 85 int align = this->alignment(type.componentType()); 86 int stride = this->size(type.componentType()) + align - 1; 87 stride -= stride % align; 88 return this->roundUpIfNeeded(stride); 89 } 90 default: 91 ABORT("type does not have a stride"); 92 } 93 } 94 95 /** 96 * Returns the size of a type in bytes. 97 */ 98 size_t size(const Type& type) const { 99 switch (type.kind()) { 100 case Type::kScalar_Kind: 101 if (type.name() == "bool") { 102 return 1; 103 } 104 // FIXME need to take precision into account, once we figure out how we want to 105 // handle it... 106 return 4; 107 case Type::kVector_Kind: 108 if (fStd == kMetal_Standard && type.columns() == 3) { 109 return 4 * this->size(type.componentType()); 110 } 111 return type.columns() * this->size(type.componentType()); 112 case Type::kMatrix_Kind: // fall through 113 case Type::kArray_Kind: 114 return type.columns() * this->stride(type); 115 case Type::kStruct_Kind: { 116 size_t total = 0; 117 for (const auto& f : type.fields()) { 118 size_t alignment = this->alignment(*f.fType); 119 if (total % alignment != 0) { 120 total += alignment - total % alignment; 121 } 122 SkASSERT(total % alignment == 0); 123 total += this->size(*f.fType); 124 } 125 size_t alignment = this->alignment(type); 126 SkASSERT(!type.fields().size() || 127 (0 == alignment % this->alignment(*type.fields()[0].fType))); 128 return (total + alignment - 1) & ~(alignment - 1); 129 } 130 default: 131 ABORT("cannot determine size of type %s", type.name().c_str()); 132 } 133 } 134 135 const Standard fStd; 136 }; 137 138 } // namespace 139 140 #endif 141