Commit ec93b1d0 by Alexis Hetu Committed by Alexis Hétu

Imported a few ES3 fixes from Angle

Imported some of the more trivial bug fixes: - Added a few missing interface block cases - Added checks for varying structs - Added checks for unsized arrays - Added first-class array check for pre ES3 compilation - Added check that ES3 functions do not use builtin names (ES3 only) - Added more binary operator checks Change-Id: I3d75453f17e1123478ef7da0998e869970a7fb7d Reviewed-on: https://swiftshader-review.googlesource.com/8289Tested-by: 's avatarAlexis Hétu <sugoi@google.com> Reviewed-by: 's avatarNicolas Capens <capn@google.com>
parent 15060bb0
......@@ -62,6 +62,134 @@ static bool ValidateMultiplication(TOperator op, const TType &left, const TType
}
}
TOperator TypeToConstructorOperator(const TType &type)
{
switch(type.getBasicType())
{
case EbtFloat:
if(type.isMatrix())
{
switch(type.getNominalSize())
{
case 2:
switch(type.getSecondarySize())
{
case 2:
return EOpConstructMat2;
case 3:
return EOpConstructMat2x3;
case 4:
return EOpConstructMat2x4;
default:
break;
}
break;
case 3:
switch(type.getSecondarySize())
{
case 2:
return EOpConstructMat3x2;
case 3:
return EOpConstructMat3;
case 4:
return EOpConstructMat3x4;
default:
break;
}
break;
case 4:
switch(type.getSecondarySize())
{
case 2:
return EOpConstructMat4x2;
case 3:
return EOpConstructMat4x3;
case 4:
return EOpConstructMat4;
default:
break;
}
break;
}
}
else
{
switch(type.getNominalSize())
{
case 1:
return EOpConstructFloat;
case 2:
return EOpConstructVec2;
case 3:
return EOpConstructVec3;
case 4:
return EOpConstructVec4;
default:
break;
}
}
break;
case EbtInt:
switch(type.getNominalSize())
{
case 1:
return EOpConstructInt;
case 2:
return EOpConstructIVec2;
case 3:
return EOpConstructIVec3;
case 4:
return EOpConstructIVec4;
default:
break;
}
break;
case EbtUInt:
switch(type.getNominalSize())
{
case 1:
return EOpConstructUInt;
case 2:
return EOpConstructUVec2;
case 3:
return EOpConstructUVec3;
case 4:
return EOpConstructUVec4;
default:
break;
}
break;
case EbtBool:
switch(type.getNominalSize())
{
case 1:
return EOpConstructBool;
case 2:
return EOpConstructBVec2;
case 3:
return EOpConstructBVec3;
case 4:
return EOpConstructBVec4;
default:
break;
}
break;
case EbtStruct:
return EOpConstructStruct;
default:
break;
}
return EOpNull;
}
const char* getOperatorString(TOperator op) {
switch (op) {
case EOpInitialize: return "=";
......@@ -657,6 +785,17 @@ bool TIntermediate::postProcess(TIntermNode* root)
//
////////////////////////////////////////////////////////////////
// static
TIntermTyped *TIntermTyped::CreateIndexNode(int index)
{
ConstantUnion *u = new ConstantUnion[1];
u[0].setIConst(index);
TType type(EbtInt, EbpUndefined, EvqConstExpr, 1);
TIntermConstantUnion *node = new TIntermConstantUnion(u, type);
return node;
}
//
// Say whether or not an operation node changes the value of a variable.
//
......
......@@ -28,6 +28,77 @@
//
////////////////////////////////////////////////////////////////////////
namespace
{
bool IsVaryingOut(TQualifier qualifier)
{
switch(qualifier)
{
case EvqVaryingOut:
case EvqSmoothOut:
case EvqFlatOut:
case EvqCentroidOut:
case EvqVertexOut:
return true;
default: break;
}
return false;
}
bool IsVaryingIn(TQualifier qualifier)
{
switch(qualifier)
{
case EvqVaryingIn:
case EvqSmoothIn:
case EvqFlatIn:
case EvqCentroidIn:
case EvqFragmentIn:
return true;
default: break;
}
return false;
}
bool IsVarying(TQualifier qualifier)
{
return IsVaryingIn(qualifier) || IsVaryingOut(qualifier);
}
bool IsAssignment(TOperator op)
{
switch(op)
{
case EOpPostIncrement:
case EOpPostDecrement:
case EOpPreIncrement:
case EOpPreDecrement:
case EOpAssign:
case EOpAddAssign:
case EOpSubAssign:
case EOpMulAssign:
case EOpVectorTimesMatrixAssign:
case EOpVectorTimesScalarAssign:
case EOpMatrixTimesScalarAssign:
case EOpMatrixTimesMatrixAssign:
case EOpDivAssign:
case EOpIModAssign:
case EOpBitShiftLeftAssign:
case EOpBitShiftRightAssign:
case EOpBitwiseAndAssign:
case EOpBitwiseXorAssign:
case EOpBitwiseOrAssign:
return true;
default:
return false;
}
}
}
//
// Look at a '.' field selector string and change it into offsets
// for a vector.
......@@ -282,6 +353,7 @@ bool TParseContext::lValueErrorCheck(const TSourceLoc &line, const char* op, TIn
case EOpIndexDirect:
case EOpIndexIndirect:
case EOpIndexDirectStruct:
case EOpIndexDirectInterfaceBlock:
return lValueErrorCheck(line, op, binaryNode->getLeft());
case EOpVectorSwizzle:
errorReturn = lValueErrorCheck(line, op, binaryNode->getLeft());
......@@ -756,7 +828,7 @@ bool TParseContext::containsSampler(TType& type)
if (IsSampler(type.getBasicType()))
return true;
if (type.getBasicType() == EbtStruct) {
if (type.getBasicType() == EbtStruct || type.isInterfaceBlock()) {
const TFieldList& fields = type.getStruct()->fields();
for(unsigned int i = 0; i < fields.size(); ++i) {
if (containsSampler(*fields[i]->type()))
......@@ -776,7 +848,7 @@ bool TParseContext::arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped* ex
{
TIntermConstantUnion* constant = expr->getAsConstantUnion();
if (constant == 0 || !constant->isScalarInt())
if (expr->getQualifier() != EvqConstExpr || constant == 0 || !constant->isScalarInt())
{
error(line, "array size must be a constant integer expression", "");
return true;
......@@ -798,14 +870,20 @@ bool TParseContext::arraySizeErrorCheck(const TSourceLoc &line, TIntermTyped* ex
{
size = constant->getIConst(0);
if (size <= 0)
if (size < 0)
{
error(line, "array size must be a positive integer", "");
error(line, "array size must be non-negative", "");
size = 1;
return true;
}
}
if(size == 0)
{
error(line, "array size must be greater than zero", "");
return true;
}
return false;
}
......@@ -839,6 +917,15 @@ bool TParseContext::arrayTypeErrorCheck(const TSourceLoc &line, TPublicType type
return true;
}
// In ESSL1.00 shaders, structs cannot be varying (section 4.3.5). This is checked elsewhere.
// In ESSL3.00 shaders, struct inputs/outputs are allowed but not arrays of structs (section 4.3.4).
if(mShaderVersion >= 300 && type.type == EbtStruct && IsVarying(type.qualifier))
{
error(line, "cannot declare arrays of structs of this qualifier",
TType(type).getCompleteString().c_str());
return true;
}
return false;
}
......@@ -1191,9 +1278,19 @@ bool TParseContext::executeInitializer(const TSourceLoc& line, const TString& id
ASSERT(intermNode != nullptr);
TType type = TType(pType);
if(type.isArray() && (type.getArraySize() == 0))
if(type.isUnsizedArray())
{
type.setArraySize(initializer->getArraySize());
// We have not checked yet whether the initializer actually is an array or not.
if(initializer->isArray())
{
type.setArraySize(initializer->getArraySize());
}
else
{
// Having a non-array initializer for an unsized array will result in an error later,
// so we don't generate an error message here.
type.setArraySize(1u);
}
}
TVariable *variable = nullptr;
......@@ -1285,6 +1382,12 @@ TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier, bool inva
if(mShaderVersion < 300)
{
if(typeSpecifier.array)
{
error(typeSpecifier.line, "not supported", "first-class array");
returnType.clearArrayness();
}
if(qualifier == EvqAttribute && (typeSpecifier.type == EbtBool || typeSpecifier.type == EbtInt))
{
error(typeSpecifier.line, "cannot be bool or int", getQualifierString(qualifier));
......@@ -1300,44 +1403,87 @@ TPublicType TParseContext::addFullySpecifiedType(TQualifier qualifier, bool inva
}
else
{
switch(qualifier)
if(!returnType.layoutQualifier.isEmpty())
{
case EvqSmoothIn:
case EvqSmoothOut:
case EvqVertexOut:
case EvqFragmentIn:
case EvqCentroidOut:
case EvqCentroidIn:
if(typeSpecifier.type == EbtBool)
{
error(typeSpecifier.line, "cannot be bool", getQualifierString(qualifier));
recover();
}
if(typeSpecifier.type == EbtInt || typeSpecifier.type == EbtUInt)
{
error(typeSpecifier.line, "must use 'flat' interpolation here", getQualifierString(qualifier));
recover();
}
break;
case EvqVertexIn:
case EvqFragmentOut:
case EvqFlatIn:
case EvqFlatOut:
if(typeSpecifier.type == EbtBool)
{
error(typeSpecifier.line, "cannot be bool", getQualifierString(qualifier));
recover();
}
break;
globalErrorCheck(typeSpecifier.line, symbolTable.atGlobalLevel(), "layout");
}
default: break;
if(IsVarying(returnType.qualifier) || returnType.qualifier == EvqVertexIn || returnType.qualifier == EvqFragmentOut)
{
checkInputOutputTypeIsValidES3(returnType.qualifier, typeSpecifier, typeSpecifier.line);
}
}
return returnType;
}
void TParseContext::checkInputOutputTypeIsValidES3(const TQualifier qualifier,
const TPublicType &type,
const TSourceLoc &qualifierLocation)
{
// An input/output variable can never be bool or a sampler. Samplers are checked elsewhere.
if(type.type == EbtBool)
{
error(qualifierLocation, "cannot be bool", getQualifierString(qualifier));
}
// Specific restrictions apply for vertex shader inputs and fragment shader outputs.
switch(qualifier)
{
case EvqVertexIn:
// ESSL 3.00 section 4.3.4
if(type.array)
{
error(qualifierLocation, "cannot be array", getQualifierString(qualifier));
}
// Vertex inputs with a struct type are disallowed in singleDeclarationErrorCheck
return;
case EvqFragmentOut:
// ESSL 3.00 section 4.3.6
if(type.isMatrix())
{
error(qualifierLocation, "cannot be matrix", getQualifierString(qualifier));
}
// Fragment outputs with a struct type are disallowed in singleDeclarationErrorCheck
return;
default:
break;
}
// Vertex shader outputs / fragment shader inputs have a different, slightly more lenient set of
// restrictions.
bool typeContainsIntegers = (type.type == EbtInt || type.type == EbtUInt ||
type.isStructureContainingType(EbtInt) ||
type.isStructureContainingType(EbtUInt));
if(typeContainsIntegers && qualifier != EvqFlatIn && qualifier != EvqFlatOut)
{
error(qualifierLocation, "must use 'flat' interpolation here", getQualifierString(qualifier));
}
if(type.type == EbtStruct)
{
// ESSL 3.00 sections 4.3.4 and 4.3.6.
// These restrictions are only implied by the ESSL 3.00 spec, but
// the ESSL 3.10 spec lists these restrictions explicitly.
if(type.array)
{
error(qualifierLocation, "cannot be an array of structures", getQualifierString(qualifier));
}
if(type.isStructureContainingArrays())
{
error(qualifierLocation, "cannot be a structure containing an array", getQualifierString(qualifier));
}
if(type.isStructureContainingType(EbtStruct))
{
error(qualifierLocation, "cannot be a structure containing a structure", getQualifierString(qualifier));
}
if(type.isStructureContainingType(EbtBool))
{
error(qualifierLocation, "cannot be a structure containing a bool", getQualifierString(qualifier));
}
}
}
TIntermAggregate *TParseContext::parseSingleDeclaration(TPublicType &publicType,
const TSourceLoc &identifierOrTypeLocation,
const TString &identifier)
......@@ -1915,7 +2061,13 @@ TFunction *TParseContext::parseFunctionDeclarator(const TSourceLoc &location, TF
// here.
//
TFunction *prevDec = static_cast<TFunction *>(symbolTable.find(function->getMangledName(), getShaderVersion()));
if(prevDec)
if(getShaderVersion() >= 300 && symbolTable.hasUnmangledBuiltIn(function->getName().c_str()))
{
// With ESSL 3.00, names of built-in functions cannot be redeclared as functions.
// Therefore overloading or redefining builtin functions is an error.
error(location, "Name of a built-in function cannot be redeclared as function", function->getName().c_str());
}
else if(prevDec)
{
if(prevDec->getReturnType() != function->getReturnType())
{
......@@ -1969,84 +2121,7 @@ TFunction *TParseContext::addConstructorFunc(const TPublicType &publicTypeIn)
}
else
{
switch(publicType.type)
{
case EbtFloat:
if(publicType.isMatrix())
{
switch(publicType.getCols())
{
case 2:
switch(publicType.getRows())
{
case 2: op = EOpConstructMat2; break;
case 3: op = EOpConstructMat2x3; break;
case 4: op = EOpConstructMat2x4; break;
}
break;
case 3:
switch(publicType.getRows())
{
case 2: op = EOpConstructMat3x2; break;
case 3: op = EOpConstructMat3; break;
case 4: op = EOpConstructMat3x4; break;
}
break;
case 4:
switch(publicType.getRows())
{
case 2: op = EOpConstructMat4x2; break;
case 3: op = EOpConstructMat4x3; break;
case 4: op = EOpConstructMat4; break;
}
break;
}
}
else
{
switch(publicType.getNominalSize())
{
case 1: op = EOpConstructFloat; break;
case 2: op = EOpConstructVec2; break;
case 3: op = EOpConstructVec3; break;
case 4: op = EOpConstructVec4; break;
}
}
break;
case EbtInt:
switch(publicType.getNominalSize())
{
case 1: op = EOpConstructInt; break;
case 2: op = EOpConstructIVec2; break;
case 3: op = EOpConstructIVec3; break;
case 4: op = EOpConstructIVec4; break;
}
break;
case EbtUInt:
switch(publicType.getNominalSize())
{
case 1: op = EOpConstructUInt; break;
case 2: op = EOpConstructUVec2; break;
case 3: op = EOpConstructUVec3; break;
case 4: op = EOpConstructUVec4; break;
}
break;
case EbtBool:
switch(publicType.getNominalSize())
{
case 1: op = EOpConstructBool; break;
case 2: op = EOpConstructBVec2; break;
case 3: op = EOpConstructBVec3; break;
case 4: op = EOpConstructBVec4; break;
}
break;
default: break;
}
op = TypeToConstructorOperator(TType(publicType));
if(op == EOpNull)
{
error(publicType.line, "cannot construct this type", getBasicString(publicType.type));
......@@ -2717,9 +2792,8 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre
}
else
{
ConstantUnion *unionArray = new ConstantUnion[1];
unionArray->setIConst(i);
TIntermTyped *index = intermediate.addConstantUnion(unionArray, *fields[i]->type(), fieldLocation);
TIntermTyped *index = TIntermTyped::CreateIndexNode(i);
index->setLine(fieldLocation);
indexedExpression = intermediate.addIndex(EOpIndexDirectStruct, baseExpression, index, dotLocation);
indexedExpression->setType(*fields[i]->type());
}
......@@ -3237,6 +3311,46 @@ bool TParseContext::binaryOpCommonCheck(TOperator op, TIntermTyped *left, TInter
{
return false;
}
break;
case EOpAdd:
case EOpSub:
case EOpDiv:
case EOpIMod:
case EOpBitShiftLeft:
case EOpBitShiftRight:
case EOpBitwiseAnd:
case EOpBitwiseXor:
case EOpBitwiseOr:
case EOpAddAssign:
case EOpSubAssign:
case EOpDivAssign:
case EOpIModAssign:
case EOpBitShiftLeftAssign:
case EOpBitShiftRightAssign:
case EOpBitwiseAndAssign:
case EOpBitwiseXorAssign:
case EOpBitwiseOrAssign:
if((left->isMatrix() && right->isVector()) || (left->isVector() && right->isMatrix()))
{
return false;
}
// Are the sizes compatible?
if(left->getNominalSize() != right->getNominalSize() || left->getSecondarySize() != right->getSecondarySize())
{
// If the nominal sizes of operands do not match:
// One of them must be a scalar.
if(!left->isScalar() && !right->isScalar())
return false;
// In the case of compound assignment other than multiply-assign,
// the right side needs to be a scalar. Otherwise a vector/matrix
// would be assigned to a scalar. A scalar can't be shifted by a
// vector either.
if(!right->isScalar() && (IsAssignment(op) || op == EOpBitShiftLeft || op == EOpBitShiftRight))
return false;
}
break;
default:
break;
}
......
......@@ -142,6 +142,7 @@ public:
bool layoutLocationErrorCheck(const TSourceLoc& location, const TLayoutQualifier &layoutQualifier);
bool functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *);
void es3InvariantErrorCheck(const TQualifier qualifier, const TSourceLoc &invariantLocation);
void checkInputOutputTypeIsValidES3(const TQualifier qualifier, const TPublicType &type, const TSourceLoc &qualifierLocation);
const TExtensionBehavior& extensionBehavior() const { return mDirectiveHandler.extensionBehavior(); }
bool supportsExtension(const char* extension);
......
......@@ -121,6 +121,17 @@ bool TStructure::containsArrays() const
return false;
}
bool TStructure::containsType(TBasicType type) const
{
for(size_t i = 0; i < mFields->size(); ++i)
{
const TType *fieldType = (*mFields)[i]->type();
if(fieldType->getBasicType() == type || fieldType->isStructureContainingType(type))
return true;
}
return false;
}
bool TStructure::containsSamplers() const
{
for(size_t i = 0; i < mFields->size(); ++i)
......
......@@ -369,6 +369,7 @@ public:
{
if(ptype1->getBasicType() == EbtGSampler2D)
{
insertUnmangledBuiltIn(name);
bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
insertBuiltIn(level, gvec4 ? new TType(EbtFloat, 4) : rvalue, name, new TType(EbtSampler2D), ptype2, ptype3, ptype4, ptype5);
insertBuiltIn(level, gvec4 ? new TType(EbtInt, 4) : rvalue, name, new TType(EbtISampler2D), ptype2, ptype3, ptype4, ptype5);
......@@ -376,6 +377,7 @@ public:
}
else if(ptype1->getBasicType() == EbtGSampler3D)
{
insertUnmangledBuiltIn(name);
bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
insertBuiltIn(level, gvec4 ? new TType(EbtFloat, 4) : rvalue, name, new TType(EbtSampler3D), ptype2, ptype3, ptype4, ptype5);
insertBuiltIn(level, gvec4 ? new TType(EbtInt, 4) : rvalue, name, new TType(EbtISampler3D), ptype2, ptype3, ptype4, ptype5);
......@@ -383,6 +385,7 @@ public:
}
else if(ptype1->getBasicType() == EbtGSamplerCube)
{
insertUnmangledBuiltIn(name);
bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
insertBuiltIn(level, gvec4 ? new TType(EbtFloat, 4) : rvalue, name, new TType(EbtSamplerCube), ptype2, ptype3, ptype4, ptype5);
insertBuiltIn(level, gvec4 ? new TType(EbtInt, 4) : rvalue, name, new TType(EbtISamplerCube), ptype2, ptype3, ptype4, ptype5);
......@@ -390,6 +393,7 @@ public:
}
else if(ptype1->getBasicType() == EbtGSampler2DArray)
{
insertUnmangledBuiltIn(name);
bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
insertBuiltIn(level, gvec4 ? new TType(EbtFloat, 4) : rvalue, name, new TType(EbtSampler2DArray), ptype2, ptype3, ptype4, ptype5);
insertBuiltIn(level, gvec4 ? new TType(EbtInt, 4) : rvalue, name, new TType(EbtISampler2DArray), ptype2, ptype3, ptype4, ptype5);
......@@ -398,6 +402,7 @@ public:
else if(IsGenType(rvalue) || IsGenType(ptype1) || IsGenType(ptype2) || IsGenType(ptype3))
{
ASSERT(!ptype4);
insertUnmangledBuiltIn(name);
insertBuiltIn(level, op, ext, GenType(rvalue, 1), name, GenType(ptype1, 1), GenType(ptype2, 1), GenType(ptype3, 1));
insertBuiltIn(level, op, ext, GenType(rvalue, 2), name, GenType(ptype1, 2), GenType(ptype2, 2), GenType(ptype3, 2));
insertBuiltIn(level, op, ext, GenType(rvalue, 3), name, GenType(ptype1, 3), GenType(ptype2, 3), GenType(ptype3, 3));
......@@ -406,6 +411,7 @@ public:
else if(IsVecType(rvalue) || IsVecType(ptype1) || IsVecType(ptype2) || IsVecType(ptype3))
{
ASSERT(!ptype4);
insertUnmangledBuiltIn(name);
insertBuiltIn(level, op, ext, VecType(rvalue, 2), name, VecType(ptype1, 2), VecType(ptype2, 2), VecType(ptype3, 2));
insertBuiltIn(level, op, ext, VecType(rvalue, 3), name, VecType(ptype1, 3), VecType(ptype2, 3), VecType(ptype3, 3));
insertBuiltIn(level, op, ext, VecType(rvalue, 4), name, VecType(ptype1, 4), VecType(ptype2, 4), VecType(ptype3, 4));
......@@ -441,17 +447,20 @@ public:
function->addParameter(param5);
}
ASSERT(hasUnmangledBuiltIn(name));
insert(level, *function);
}
}
void insertBuiltIn(ESymbolLevel level, TOperator op, TType *rvalue, const char *name, TType *ptype1, TType *ptype2 = 0, TType *ptype3 = 0, TType *ptype4 = 0, TType *ptype5 = 0)
{
insertUnmangledBuiltIn(name);
insertBuiltIn(level, op, "", rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5);
}
void insertBuiltIn(ESymbolLevel level, TType *rvalue, const char *name, TType *ptype1, TType *ptype2 = 0, TType *ptype3 = 0, TType *ptype4 = 0, TType *ptype5 = 0)
{
insertUnmangledBuiltIn(name);
insertBuiltIn(level, EOpNull, rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5);
}
......@@ -518,6 +527,12 @@ public:
void setGlobalInvariant() { mGlobalInvariant = true; }
bool getGlobalInvariant() const { return mGlobalInvariant; }
bool hasUnmangledBuiltIn(const char *name) { return mUnmangledBuiltinNames.count(std::string(name)) > 0; }
private:
// Used to insert unmangled functions to check redeclaration of built-ins in ESSL 3.00.
void insertUnmangledBuiltIn(const char *name) { mUnmangledBuiltinNames.insert(std::string(name)); }
protected:
ESymbolLevel currentLevel() const { return static_cast<ESymbolLevel>(table.size() - 1); }
......@@ -525,6 +540,8 @@ protected:
typedef std::map< TBasicType, TPrecision > PrecisionStackLevel;
std::vector< PrecisionStackLevel > precisionStack;
std::set<std::string> mUnmangledBuiltinNames;
std::set<std::string> mInvariantVaryings;
bool mGlobalInvariant;
};
......
......@@ -132,6 +132,7 @@ public:
return mDeepestNesting;
}
bool containsArrays() const;
bool containsType(TBasicType type) const;
bool containsSamplers() const;
bool equals(const TStructure &other) const;
......@@ -381,6 +382,7 @@ public:
int getSecondarySize() const { return secondarySize; }
bool isArray() const { return array ? true : false; }
bool isUnsizedArray() const { return array && arraySize == 0; }
int getArraySize() const { return arraySize; }
void setArraySize(int s) { array = true; arraySize = s; }
int getMaxArraySize () const { return maxArraySize; }
......@@ -468,6 +470,11 @@ public:
return structure ? structure->containsArrays() : false;
}
bool isStructureContainingType(TBasicType t) const
{
return structure ? structure->containsType(t) : false;
}
bool isStructureContainingSamplers() const
{
return structure ? structure->containsSamplers() : false;
......@@ -576,6 +583,16 @@ struct TPublicType
return userDef->isStructureContainingArrays();
}
bool isStructureContainingType(TBasicType t) const
{
if(!userDef)
{
return false;
}
return userDef->isStructureContainingType(t);
}
bool isMatrix() const
{
return primarySize > 1 && secondarySize > 1;
......
......@@ -238,6 +238,7 @@ enum TOperator {
EOpBitwiseOrAssign
};
extern TOperator TypeToConstructorOperator(const TType &type);
extern const char* getOperatorString(TOperator op);
class TIntermTraverser;
......@@ -334,6 +335,7 @@ public:
int registerSize() const { return type.registerSize(); }
int getArraySize() const { return type.getArraySize(); }
static TIntermTyped *CreateIndexNode(int index);
protected:
TType type;
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment