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