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 
MemoryLayout(Standard std)23     MemoryLayout(Standard std)
24     : fStd(std) {}
25 
vector_alignment(size_t componentSize,int columns)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      */
roundUpIfNeeded(size_t raw)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      */
alignment(const Type & type)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      */
stride(const Type & type)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      */
size(const Type & type)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