Home | History | Annotate | Download | only in sksl
      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