aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/sksl/ir/SkSLType.h
blob: afd00d8b2af41d9a54c7b61047558dcb871d34f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
 
#ifndef SKIASL_TYPE
#define SKIASL_TYPE

#include "SkSLModifiers.h"
#include "SkSLSymbol.h"
#include "../SkSLPosition.h"
#include "../SkSLUtil.h"
#include "../spirv.h"
#include <vector>
#include <memory>

namespace SkSL {

class Context;

/**
 * Represents a type, such as int or vec4.
 */
class Type : public Symbol {
public:
    struct Field {
        Field(Modifiers modifiers, std::string name, const Type* type)
        : fModifiers(modifiers)
        , fName(std::move(name))
        , fType(std::move(type)) {}

        const std::string description() const {
            return fType->description() + " " + fName + ";";
        }

        Modifiers fModifiers;
        std::string fName;
        const Type* fType;
    };

    enum Kind {
        kScalar_Kind,
        kVector_Kind,
        kMatrix_Kind,
        kArray_Kind,
        kStruct_Kind,
        kGeneric_Kind,
        kSampler_Kind,
        kOther_Kind
    };

    // Create an "other" (special) type with the given name. These types cannot be directly 
    // referenced from user code.
    Type(std::string name)
    : INHERITED(Position(), kType_Kind, std::move(name))
    , fTypeKind(kOther_Kind) {}

    // Create a generic type which maps to the listed types. As currently implemented, there are 
    // always exactly four coercion targets, mapping to the scalar, vec2, vec3, and vec4 versions of 
    // a type.
    Type(std::string name, std::vector<const Type*> types)
    : INHERITED(Position(), kType_Kind, std::move(name))
    , fTypeKind(kGeneric_Kind)
    , fCoercibleTypes(std::move(types)) {
        ASSERT(fCoercibleTypes.size() == 4);
    }

    // Create a struct type with the given fields.
    Type(std::string name, std::vector<Field> fields)
    : INHERITED(Position(), kType_Kind, std::move(name))
    , fTypeKind(kStruct_Kind)
    , fFields(std::move(fields)) {}

    // Create a scalar type.
    Type(std::string name, bool isNumber)
    : INHERITED(Position(), kType_Kind, std::move(name))
    , fTypeKind(kScalar_Kind)
    , fIsNumber(isNumber)
    , fColumns(1)
    , fRows(1) {}

    // Create a scalar type which can be coerced to the listed types.
    Type(std::string name, bool isNumber, std::vector<const Type*> coercibleTypes)
    : INHERITED(Position(), kType_Kind, std::move(name))
    , fTypeKind(kScalar_Kind)
    , fIsNumber(isNumber)
    , fCoercibleTypes(std::move(coercibleTypes))
    , fColumns(1)
    , fRows(1) {}

    // Create a vector type.
    Type(std::string name, const Type& componentType, int columns)
    : Type(name, kVector_Kind, componentType, columns) {}

    // Create a vector or array type.
    Type(std::string name, Kind kind, const Type& componentType, int columns)
    : INHERITED(Position(), kType_Kind, std::move(name))
    , fTypeKind(kind)
    , fComponentType(&componentType)
    , fColumns(columns)
    , fRows(1)    
    , fDimensions(SpvDim1D) {}

    // Create a matrix type.
    Type(std::string name, const Type& componentType, int columns, int rows)
    : INHERITED(Position(), kType_Kind, std::move(name))
    , fTypeKind(kMatrix_Kind)
    , fComponentType(&componentType)
    , fColumns(columns)
    , fRows(rows)    
    , fDimensions(SpvDim1D) {}

    // Create a sampler type.
    Type(std::string name, SpvDim_ dimensions, bool isDepth, bool isArrayed, bool isMultisampled, 
         bool isSampled) 
    : INHERITED(Position(), kType_Kind, std::move(name))
    , fTypeKind(kSampler_Kind)
    , fDimensions(dimensions)
    , fIsDepth(isDepth)
    , fIsArrayed(isArrayed)
    , fIsMultisampled(isMultisampled)
    , fIsSampled(isSampled) {}

    std::string name() const {
        return fName;
    }

    std::string description() const override {
        return fName;
    }

    bool operator==(const Type& other) const {
        return fName == other.fName;
    }

    bool operator!=(const Type& other) const {
        return fName != other.fName;
    }

    /**
     * Returns the category (scalar, vector, matrix, etc.) of this type.
     */
    Kind kind() const {
        return fTypeKind;
    }

    /**
     * Returns true if this is a numeric scalar type.
     */
    bool isNumber() const {
        return fIsNumber;
    }

    /**
     * Returns true if an instance of this type can be freely coerced (implicitly converted) to 
     * another type.
     */
    bool canCoerceTo(const Type& other) const {
        int cost;
        return determineCoercionCost(other, &cost);
    }

    /**
     * Determines the "cost" of coercing (implicitly converting) this type to another type. The cost
     * is a number with no particular meaning other than that lower costs are preferable to higher 
     * costs. Returns true if a conversion is possible, false otherwise. The value of the out 
     * parameter is undefined if false is returned.
     */
    bool determineCoercionCost(const Type& other, int* outCost) const;

    /**
     * For matrices and vectors, returns the type of individual cells (e.g. mat2 has a component
     * type of kFloat_Type). For all other types, causes an assertion failure.
     */
    const Type& componentType() const {
        ASSERT(fComponentType);
        return *fComponentType;
    }

    /**
     * For matrices and vectors, returns the number of columns (e.g. both mat3 and vec3 return 3).
     * For scalars, returns 1. For arrays, returns either the size of the array (if known) or -1. 
     * For all other types, causes an assertion failure.
     */
    int columns() const {
        ASSERT(fTypeKind == kScalar_Kind || fTypeKind == kVector_Kind || 
               fTypeKind == kMatrix_Kind || fTypeKind == kArray_Kind);
        return fColumns;
    }

    /**
     * For matrices, returns the number of rows (e.g. mat2x4 returns 4). For vectors and scalars,
     * returns 1. For all other types, causes an assertion failure.
     */
    int rows() const {
        ASSERT(fRows > 0);
        return fRows;
    }

    const std::vector<Field>& fields() const {
        ASSERT(fTypeKind == kStruct_Kind);
        return fFields;
    }

    /**
     * For generic types, returns the types that this generic type can substitute for. For other
     * types, returns a list of other types that this type can be coerced into.
     */
    const std::vector<const Type*>& coercibleTypes() const {
        ASSERT(fCoercibleTypes.size() > 0);
        return fCoercibleTypes;
    }

    int dimensions() const {
        ASSERT(fTypeKind == kSampler_Kind);
        return fDimensions;
    }

    bool isDepth() const {
        ASSERT(fTypeKind == kSampler_Kind);
        return fIsDepth;
    }

    bool isArrayed() const {
        ASSERT(fTypeKind == kSampler_Kind);
        return fIsArrayed;
    }

    bool isMultisampled() const {
        ASSERT(fTypeKind == kSampler_Kind);
        return fIsMultisampled;
    }

    bool isSampled() const {
        ASSERT(fTypeKind == kSampler_Kind);
        return fIsSampled;
    }

    static size_t vector_alignment(size_t componentSize, int columns) {
        return componentSize * (columns + columns % 2);
    }

    /**
     * Returns the type's required alignment (when putting this type into a struct, the offset must
     * be a multiple of the alignment).
     */
    size_t alignment() const {
        // See OpenGL Spec 7.6.2.2 Standard Uniform Block Layout
        switch (fTypeKind) {
            case kScalar_Kind:
                return this->size();
            case kVector_Kind:
                return vector_alignment(fComponentType->size(), fColumns);
            case kMatrix_Kind:
                return (vector_alignment(fComponentType->size(), fRows) + 15) & ~15;
            case kArray_Kind:
                // round up to next multiple of 16
                return (fComponentType->alignment() + 15) & ~15;
            case kStruct_Kind: {
                size_t result = 16;
                for (size_t i = 0; i < fFields.size(); i++) {
                    size_t alignment = fFields[i].fType->alignment();
                    if (alignment > result) {
                        result = alignment;
                    }
                }
            }
            default:
                ABORT(("cannot determine size of type " + fName).c_str());
        }
    }

    /**
     * For matrices and arrays, returns the number of bytes from the start of one entry (row, in
     * the case of matrices) to the start of the next.
     */
    size_t stride() const {
        switch (fTypeKind) {
            case kMatrix_Kind: // fall through
            case kArray_Kind:
                return this->alignment();
            default:
                ABORT("type does not have a stride");
        }
    }

    /**
     * Returns the size of this type in bytes.
     */
    size_t size() const {
        switch (fTypeKind) {
            case kScalar_Kind:
                // FIXME need to take precision into account, once we figure out how we want to
                // handle it...
                return 4;
            case kVector_Kind:
                return fColumns * fComponentType->size();
            case kMatrix_Kind:
                return vector_alignment(fComponentType->size(), fRows) * fColumns;
            case kArray_Kind:
                return fColumns * this->stride();
            case kStruct_Kind: {
                size_t total = 0;
                for (size_t i = 0; i < fFields.size(); i++) {
                    size_t alignment = fFields[i].fType->alignment();
                    if (total % alignment != 0) {
                        total += alignment - total % alignment;
                    }
                    ASSERT(false);
                    ASSERT(total % alignment == 0);
                    total += fFields[i].fType->size();
                }
                return total;
            }
            default:
                ABORT(("cannot determine size of type " + fName).c_str());
        }
    }

    /**
     * Returns the corresponding vector or matrix type with the specified number of columns and 
     * rows.
     */
    const Type& toCompound(const Context& context, int columns, int rows) const;

private:
    typedef Symbol INHERITED;

    const Kind fTypeKind;
    const bool fIsNumber = false;
    const Type* fComponentType = nullptr;
    const std::vector<const Type*> fCoercibleTypes;
    const int fColumns = -1;
    const int fRows = -1;
    const std::vector<Field> fFields;
    const SpvDim_ fDimensions = SpvDim1D;
    const bool fIsDepth = false;
    const bool fIsArrayed = false;
    const bool fIsMultisampled = false;
    const bool fIsSampled = false;
};

} // namespace

#endif