blob: c863d9830be1b6650b349a7f6733f214ad22b362 [file] [log] [blame]
/*
* 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 "src/sksl/SkSLPosition.h"
#include "src/sksl/SkSLUtil.h"
#include "src/sksl/ir/SkSLModifiers.h"
#include "src/sksl/ir/SkSLSymbol.h"
#include "src/sksl/spirv.h"
#include <algorithm>
#include <climits>
#include <vector>
#include <memory>
namespace SkSL {
class BuiltinTypes;
class Context;
struct CoercionCost {
static CoercionCost Free() { return { 0, 0, false }; }
static CoercionCost Normal(int cost) { return { cost, 0, false }; }
static CoercionCost Narrowing(int cost) { return { 0, cost, false }; }
static CoercionCost Impossible() { return { 0, 0, true }; }
bool isPossible(bool allowNarrowing) const {
return !fImpossible && (fNarrowingCost == 0 || allowNarrowing);
}
// Addition of two costs. Saturates at Impossible().
CoercionCost operator+(CoercionCost rhs) const {
if (fImpossible || rhs.fImpossible) {
return Impossible();
}
return { fNormalCost + rhs.fNormalCost, fNarrowingCost + rhs.fNarrowingCost, false };
}
bool operator<(CoercionCost rhs) const {
return std::tie( fImpossible, fNarrowingCost, fNormalCost) <
std::tie(rhs.fImpossible, rhs.fNarrowingCost, rhs.fNormalCost);
}
int fNormalCost;
int fNarrowingCost;
bool fImpossible;
};
/**
* Represents a type, such as int or float4.
*/
class Type final : public Symbol {
public:
static constexpr Kind kSymbolKind = Kind::kType;
struct Field {
Field(Modifiers modifiers, StringFragment name, const Type* type)
: fModifiers(modifiers)
, fName(name)
, fType(std::move(type)) {}
String description() const {
return fType->displayName() + " " + fName + ";";
}
Modifiers fModifiers;
StringFragment fName;
const Type* fType;
};
enum class TypeKind {
kArray,
kEnum,
kGeneric,
kMatrix,
kOther,
kSampler,
kSeparateSampler,
kScalar,
kStruct,
kTexture,
kVector
};
enum class NumberKind {
kFloat,
kSigned,
kUnsigned,
kBoolean,
kNonnumeric
};
Type(const Type& other) = delete;
/** Creates an enum type. */
static std::unique_ptr<Type> MakeEnumType(String name) {
return std::unique_ptr<Type>(new Type(std::move(name), TypeKind::kEnum));
}
/** Creates a struct type with the given fields. */
static std::unique_ptr<Type> MakeStructType(int offset, String name,
std::vector<Field> fields) {
return std::unique_ptr<Type>(new Type(offset, std::move(name), std::move(fields)));
}
/** Creates an array type. */
static constexpr int kUnsizedArray = -1;
static std::unique_ptr<Type> MakeArrayType(String name, const Type& componentType,
int columns) {
return std::unique_ptr<Type>(new Type(std::move(name), TypeKind::kArray, componentType,
columns));
}
/** Creates a clone of this Type, if needed, and inserts it into a different symbol table. */
const Type* clone(SymbolTable* symbolTable) const;
/**
* Returns true if this type is known to come from BuiltinTypes. If this returns true, the Type
* will always be available in the root SymbolTable and never needs to be copied to migrate an
* Expression from one location to another. If it returns false, the Type might not exist in a
* separate SymbolTable and you'll need to consider copying it.
*/
bool isInBuiltinTypes() const {
return !(this->isArray() || this->isStruct() || this->isEnum());
}
String displayName() const {
return this->scalarTypeForLiteral().name();
}
String description() const override {
return this->displayName();
}
bool isPrivate() const {
return this->name().startsWith("$");
}
bool operator==(const Type& other) const {
return this->name() == other.name();
}
bool operator!=(const Type& other) const {
return this->name() != other.name();
}
/**
* Returns the category (scalar, vector, matrix, etc.) of this type.
*/
TypeKind typeKind() const {
return fTypeKind;
}
NumberKind numberKind() const {
return fNumberKind;
}
/**
* Returns true if this type is a bool.
*/
bool isBoolean() const {
return fNumberKind == NumberKind::kBoolean;
}
/**
* Returns true if this is a numeric scalar type.
*/
bool isNumber() const {
return this->isFloat() || this->isInteger();
}
/**
* Returns true if this is a floating-point scalar type (float or half).
*/
bool isFloat() const {
return fNumberKind == NumberKind::kFloat;
}
/**
* Returns true if this is a signed scalar type (int or short).
*/
bool isSigned() const {
return fNumberKind == NumberKind::kSigned;
}
/**
* Returns true if this is an unsigned scalar type (uint or ushort).
*/
bool isUnsigned() const {
return fNumberKind == NumberKind::kUnsigned;
}
/**
* Returns true if this is a signed or unsigned integer.
*/
bool isInteger() const {
return this->isSigned() || this->isUnsigned();
}
/**
* Returns true if this is an "opaque type" (an external object which the shader references in
* some fashion). https://www.khronos.org/opengl/wiki/Data_Type_(GLSL)#Opaque_types
*/
bool isOpaque() const {
switch (fTypeKind) {
case TypeKind::kOther:
case TypeKind::kSampler:
case TypeKind::kSeparateSampler:
case TypeKind::kTexture:
return true;
default:
return false;
}
}
/**
* Returns the "priority" of a number type, in order of float > half > int > short.
* When operating on two number types, the result is the higher-priority type.
*/
int priority() const {
return fPriority;
}
/**
* Returns true if an instance of this type can be freely coerced (implicitly converted) to
* another type.
*/
bool canCoerceTo(const Type& other, bool allowNarrowing) const {
return this->coercionCost(other).isPossible(allowNarrowing);
}
/**
* 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 INT_MAX if the coercion is not possible.
*/
CoercionCost coercionCost(const Type& other) const;
/**
* For matrices and vectors, returns the type of individual cells (e.g. mat2 has a component
* type of Float). For arrays, returns the base type. For all other types, returns the type
* itself.
*/
const Type& componentType() const {
if (fComponentType) {
return *fComponentType;
}
return *this;
}
/**
* For texturesamplers, returns the type of texture it samples (e.g., sampler2D has
* a texture type of texture2D).
*/
const Type& textureType() const {
SkASSERT(fTextureType);
return *fTextureType;
}
/**
* For matrices and vectors, returns the number of columns (e.g. both mat3 and float3 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 SkASSERTion failure.
*/
int columns() const {
SkASSERT(this->isScalar() || this->isVector() || this->isMatrix() || this->isArray());
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 SkASSERTion failure.
*/
int rows() const {
SkASSERT(fRows > 0);
return fRows;
}
const std::vector<Field>& fields() const {
SkASSERT(this->isStruct());
return fFields;
}
/**
* For generic types, returns the types that this generic type can substitute for.
*/
const std::vector<const Type*>& coercibleTypes() const {
SkASSERT(fCoercibleTypes.size() > 0);
return fCoercibleTypes;
}
SpvDim_ dimensions() const {
SkASSERT(TypeKind::kSampler == fTypeKind || TypeKind::kTexture == fTypeKind);
return fDimensions;
}
bool isDepth() const {
SkASSERT(TypeKind::kSampler == fTypeKind || TypeKind::kTexture == fTypeKind);
return fIsDepth;
}
bool isArrayedTexture() const {
SkASSERT(TypeKind::kSampler == fTypeKind || TypeKind::kTexture == fTypeKind);
return fIsArrayed;
}
bool isScalar() const {
return fTypeKind == TypeKind::kScalar;
}
bool isLiteral() const {
return fScalarTypeForLiteral != nullptr;
}
const Type& scalarTypeForLiteral() const {
return fScalarTypeForLiteral ? *fScalarTypeForLiteral : *this;
}
bool isVector() const {
return fTypeKind == TypeKind::kVector;
}
bool isMatrix() const {
return fTypeKind == TypeKind::kMatrix;
}
bool isArray() const {
return fTypeKind == TypeKind::kArray;
}
bool isStruct() const {
return fTypeKind == TypeKind::kStruct;
}
bool isEnum() const {
return fTypeKind == TypeKind::kEnum;
}
bool isMultisampled() const {
SkASSERT(TypeKind::kSampler == fTypeKind || TypeKind::kTexture == fTypeKind);
return fIsMultisampled;
}
bool isSampled() const {
SkASSERT(TypeKind::kSampler == fTypeKind || TypeKind::kTexture == fTypeKind);
return fIsSampled;
}
bool highPrecision() const {
if (fComponentType) {
return fComponentType->highPrecision();
}
return fHighPrecision;
}
/**
* 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;
/**
* Coerces the passed-in expression to this type. If the types are incompatible, reports an
* error and returns null.
*/
std::unique_ptr<Expression> coerceExpression(std::unique_ptr<Expression> expr,
const Context& context) const;
private:
friend class BuiltinTypes;
using INHERITED = Symbol;
// Constructor for MakeOtherType.
Type(const char* name)
: INHERITED(-1, kSymbolKind, name)
, fTypeKind(TypeKind::kOther)
, fNumberKind(NumberKind::kNonnumeric) {}
// Constructor for MakeEnumType and MakeSeparateSamplerType.
Type(String name, TypeKind kind)
: INHERITED(-1, kSymbolKind, "")
, fNameString(std::move(name))
, fTypeKind(kind)
, fNumberKind(NumberKind::kNonnumeric) {
fName = StringFragment(fNameString.c_str(), fNameString.length());
}
// Constructor for MakeGenericType.
Type(const char* name, std::vector<const Type*> types)
: INHERITED(-1, kSymbolKind, name)
, fTypeKind(TypeKind::kGeneric)
, fNumberKind(NumberKind::kNonnumeric)
, fCoercibleTypes(std::move(types)) {}
// Constructor for MakeScalarType.
Type(const char* name, NumberKind numberKind, int priority, bool highPrecision)
: INHERITED(-1, kSymbolKind, name)
, fTypeKind(TypeKind::kScalar)
, fNumberKind(numberKind)
, fPriority(priority)
, fColumns(1)
, fRows(1)
, fHighPrecision(highPrecision) {}
// Constructor for MakeLiteralType.
Type(const char* name, const Type& scalarType, int priority)
: INHERITED(-1, kSymbolKind, name)
, fTypeKind(TypeKind::kScalar)
, fNumberKind(scalarType.numberKind())
, fPriority(priority)
, fColumns(1)
, fRows(1)
, fHighPrecision(scalarType.highPrecision())
, fScalarTypeForLiteral(&scalarType) {}
// Constructor shared by MakeVectorType and MakeArrayType.
Type(String name, TypeKind kind, const Type& componentType, int columns)
: INHERITED(-1, kSymbolKind, "")
, fNameString(std::move(name))
, fTypeKind(kind)
, fNumberKind(NumberKind::kNonnumeric)
, fComponentType(&componentType)
, fColumns(columns)
, fRows(1)
, fDimensions(SpvDim1D) {
if (this->isArray()) {
// Allow either explicitly-sized or unsized arrays.
SkASSERT(this->columns() > 0 || this->columns() == kUnsizedArray);
// Disallow multi-dimensional arrays.
SkASSERT(!this->componentType().isArray());
} else {
SkASSERT(this->columns() > 0);
}
fName = StringFragment(fNameString.c_str(), fNameString.length());
}
// Constructor for MakeMatrixType.
Type(const char* name, const Type& componentType, int columns, int rows)
: INHERITED(-1, kSymbolKind, name)
, fTypeKind(TypeKind::kMatrix)
, fNumberKind(NumberKind::kNonnumeric)
, fComponentType(&componentType)
, fColumns(columns)
, fRows(rows)
, fDimensions(SpvDim1D) {}
// Constructor for MakeStructType.
Type(int offset, String name, std::vector<Field> fields)
: INHERITED(offset, kSymbolKind, "")
, fNameString(std::move(name))
, fTypeKind(TypeKind::kStruct)
, fNumberKind(NumberKind::kNonnumeric)
, fFields(std::move(fields)) {
fName = StringFragment(fNameString.c_str(), fNameString.length());
}
// Constructor for MakeTextureType.
Type(const char* name, SpvDim_ dimensions, bool isDepth, bool isArrayedTexture,
bool isMultisampled, bool isSampled)
: INHERITED(-1, kSymbolKind, name)
, fTypeKind(TypeKind::kTexture)
, fNumberKind(NumberKind::kNonnumeric)
, fDimensions(dimensions)
, fIsDepth(isDepth)
, fIsArrayed(isArrayedTexture)
, fIsMultisampled(isMultisampled)
, fIsSampled(isSampled) {}
// Constructor for MakeSamplerType.
Type(const char* name, const Type& textureType)
: INHERITED(-1, kSymbolKind, name)
, fTypeKind(TypeKind::kSampler)
, fNumberKind(NumberKind::kNonnumeric)
, fDimensions(textureType.dimensions())
, fIsDepth(textureType.isDepth())
, fIsArrayed(textureType.isArrayedTexture())
, fIsMultisampled(textureType.isMultisampled())
, fIsSampled(textureType.isSampled())
, fTextureType(&textureType) {}
String fNameString;
TypeKind fTypeKind;
// always kNonnumeric_NumberKind for non-scalar values
NumberKind fNumberKind;
int fPriority = -1;
const Type* fComponentType = nullptr;
std::vector<const Type*> fCoercibleTypes;
int fColumns = -1;
int fRows = -1;
std::vector<Field> fFields;
SpvDim_ fDimensions = SpvDim1D;
bool fIsDepth = false;
bool fIsArrayed = false;
bool fIsMultisampled = false;
bool fIsSampled = false;
bool fHighPrecision = false;
const Type* fTextureType = nullptr;
const Type* fScalarTypeForLiteral = nullptr;
};
} // namespace SkSL
#endif