Commit b1edc4f5 by Olli Etuaho

Accept const array initialization in shader parsing

Array constructors are not folded, unlike all other constant expressions. Change initializer parsing path so that it accepts constant initializers whether they are folded or not. Some parts need to be adapted to work with expressions that are qualified as constant but that are not necessarily folded: 1. Identifier parsing 2. Indexing parsing 3. Field selection parsing 4. HLSL output for variable declarations 5. Determining unary operator result type 6. Determining binary operator result type 7. Determining built-in function call result type 8. Determining ternary operator result type Corner cases that are not supported yet: 1. Using array constructors inside case labels 2. Using array constructors inside array size expressions 3. Detecting when a negative constant expression containing an array constructor is used to index an array In these cases being able to constant fold the expression is essential to validating that the code is correct, so they require a more sophisticated solution. For now we keep the old code that rejects the shader if ANGLE hasn't been able to constant fold the case label or array size. In case of indexing an array with a negative constant expression containing an array constructor, ANGLE will simply treat it as a non-constant expression. BUG=541551 BUG=angleproject:1094 TEST=dEQP-GLES3.functional.shaders.constant_expressions.* (all pass), angle_unittests Change-Id: I0cbc47afd1651a4dece3d68acf7ec72a01fdf047 Reviewed-on: https://chromium-review.googlesource.com/310231Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 80dfe958
...@@ -320,6 +320,19 @@ bool TIntermAggregate::insertChildNodes(TIntermSequence::size_type position, TIn ...@@ -320,6 +320,19 @@ bool TIntermAggregate::insertChildNodes(TIntermSequence::size_type position, TIn
return true; return true;
} }
bool TIntermAggregate::areChildrenConstQualified()
{
for (TIntermNode *&child : mSequence)
{
TIntermTyped *typed = child->getAsTyped();
if (typed && typed->getQualifier() != EvqConst)
{
return false;
}
}
return true;
}
void TIntermAggregate::setPrecisionFromChildren() void TIntermAggregate::setPrecisionFromChildren()
{ {
mGotPrecisionFromChildren = true; mGotPrecisionFromChildren = true;
...@@ -587,7 +600,10 @@ void TIntermUnary::promote(const TType *funcReturnType) ...@@ -587,7 +600,10 @@ void TIntermUnary::promote(const TType *funcReturnType)
} }
} }
mType.setQualifier(EvqTemporary); if (mOperand->getQualifier() == EvqConst)
mType.setQualifier(EvqConst);
else
mType.setQualifier(EvqTemporary);
} }
// //
...@@ -612,10 +628,12 @@ bool TIntermBinary::promote(TInfoSink &infoSink) ...@@ -612,10 +628,12 @@ bool TIntermBinary::promote(TInfoSink &infoSink)
mLeft->getPrecision(), mRight->getPrecision()); mLeft->getPrecision(), mRight->getPrecision());
getTypePointer()->setPrecision(higherPrecision); getTypePointer()->setPrecision(higherPrecision);
TQualifier resultQualifier = EvqConst;
// Binary operations results in temporary variables unless both // Binary operations results in temporary variables unless both
// operands are const. // operands are const.
if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst) if (mLeft->getQualifier() != EvqConst || mRight->getQualifier() != EvqConst)
{ {
resultQualifier = EvqTemporary;
getTypePointer()->setQualifier(EvqTemporary); getTypePointer()->setQualifier(EvqTemporary);
} }
...@@ -670,14 +688,15 @@ bool TIntermBinary::promote(TInfoSink &infoSink) ...@@ -670,14 +688,15 @@ bool TIntermBinary::promote(TInfoSink &infoSink)
if (mLeft->isVector()) if (mLeft->isVector())
{ {
mOp = EOpVectorTimesMatrix; mOp = EOpVectorTimesMatrix;
setType(TType(basicType, higherPrecision, EvqTemporary, setType(TType(basicType, higherPrecision, resultQualifier,
static_cast<unsigned char>(mRight->getCols()), 1)); static_cast<unsigned char>(mRight->getCols()), 1));
} }
else else
{ {
mOp = EOpMatrixTimesScalar; mOp = EOpMatrixTimesScalar;
setType(TType(basicType, higherPrecision, EvqTemporary, setType(TType(basicType, higherPrecision, resultQualifier,
static_cast<unsigned char>(mRight->getCols()), static_cast<unsigned char>(mRight->getRows()))); static_cast<unsigned char>(mRight->getCols()),
static_cast<unsigned char>(mRight->getRows())));
} }
} }
else if (mLeft->isMatrix() && !mRight->isMatrix()) else if (mLeft->isMatrix() && !mRight->isMatrix())
...@@ -685,7 +704,7 @@ bool TIntermBinary::promote(TInfoSink &infoSink) ...@@ -685,7 +704,7 @@ bool TIntermBinary::promote(TInfoSink &infoSink)
if (mRight->isVector()) if (mRight->isVector())
{ {
mOp = EOpMatrixTimesVector; mOp = EOpMatrixTimesVector;
setType(TType(basicType, higherPrecision, EvqTemporary, setType(TType(basicType, higherPrecision, resultQualifier,
static_cast<unsigned char>(mLeft->getRows()), 1)); static_cast<unsigned char>(mLeft->getRows()), 1));
} }
else else
...@@ -696,8 +715,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink) ...@@ -696,8 +715,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink)
else if (mLeft->isMatrix() && mRight->isMatrix()) else if (mLeft->isMatrix() && mRight->isMatrix())
{ {
mOp = EOpMatrixTimesMatrix; mOp = EOpMatrixTimesMatrix;
setType(TType(basicType, higherPrecision, EvqTemporary, setType(TType(basicType, higherPrecision, resultQualifier,
static_cast<unsigned char>(mRight->getCols()), static_cast<unsigned char>(mLeft->getRows()))); static_cast<unsigned char>(mRight->getCols()),
static_cast<unsigned char>(mLeft->getRows())));
} }
else if (!mLeft->isMatrix() && !mRight->isMatrix()) else if (!mLeft->isMatrix() && !mRight->isMatrix())
{ {
...@@ -708,7 +728,7 @@ bool TIntermBinary::promote(TInfoSink &infoSink) ...@@ -708,7 +728,7 @@ bool TIntermBinary::promote(TInfoSink &infoSink)
else if (mLeft->isVector() || mRight->isVector()) else if (mLeft->isVector() || mRight->isVector())
{ {
mOp = EOpVectorTimesScalar; mOp = EOpVectorTimesScalar;
setType(TType(basicType, higherPrecision, EvqTemporary, setType(TType(basicType, higherPrecision, resultQualifier,
static_cast<unsigned char>(nominalSize), 1)); static_cast<unsigned char>(nominalSize), 1));
} }
} }
...@@ -751,8 +771,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink) ...@@ -751,8 +771,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink)
else if (mLeft->isMatrix() && mRight->isMatrix()) else if (mLeft->isMatrix() && mRight->isMatrix())
{ {
mOp = EOpMatrixTimesMatrixAssign; mOp = EOpMatrixTimesMatrixAssign;
setType(TType(basicType, higherPrecision, EvqTemporary, setType(TType(basicType, higherPrecision, resultQualifier,
static_cast<unsigned char>(mRight->getCols()), static_cast<unsigned char>(mLeft->getRows()))); static_cast<unsigned char>(mRight->getCols()),
static_cast<unsigned char>(mLeft->getRows())));
} }
else if (!mLeft->isMatrix() && !mRight->isMatrix()) else if (!mLeft->isMatrix() && !mRight->isMatrix())
{ {
...@@ -765,7 +786,7 @@ bool TIntermBinary::promote(TInfoSink &infoSink) ...@@ -765,7 +786,7 @@ bool TIntermBinary::promote(TInfoSink &infoSink)
if (!mLeft->isVector()) if (!mLeft->isVector())
return false; return false;
mOp = EOpVectorTimesScalarAssign; mOp = EOpVectorTimesScalarAssign;
setType(TType(basicType, higherPrecision, EvqTemporary, setType(TType(basicType, higherPrecision, resultQualifier,
static_cast<unsigned char>(mLeft->getNominalSize()), 1)); static_cast<unsigned char>(mLeft->getNominalSize()), 1));
} }
} }
...@@ -835,8 +856,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink) ...@@ -835,8 +856,9 @@ bool TIntermBinary::promote(TInfoSink &infoSink)
{ {
const int secondarySize = std::max( const int secondarySize = std::max(
mLeft->getSecondarySize(), mRight->getSecondarySize()); mLeft->getSecondarySize(), mRight->getSecondarySize());
setType(TType(basicType, higherPrecision, EvqTemporary, setType(TType(basicType, higherPrecision, resultQualifier,
static_cast<unsigned char>(nominalSize), static_cast<unsigned char>(secondarySize))); static_cast<unsigned char>(nominalSize),
static_cast<unsigned char>(secondarySize)));
if (mLeft->isArray()) if (mLeft->isArray())
{ {
ASSERT(mLeft->getArraySize() == mRight->getArraySize()); ASSERT(mLeft->getArraySize() == mRight->getArraySize());
......
...@@ -516,6 +516,7 @@ class TIntermAggregate : public TIntermOperator ...@@ -516,6 +516,7 @@ class TIntermAggregate : public TIntermOperator
void setUseEmulatedFunction() { mUseEmulatedFunction = true; } void setUseEmulatedFunction() { mUseEmulatedFunction = true; }
bool getUseEmulatedFunction() { return mUseEmulatedFunction; } bool getUseEmulatedFunction() { return mUseEmulatedFunction; }
bool areChildrenConstQualified();
void setPrecisionFromChildren(); void setPrecisionFromChildren();
void setBuiltInFunctionPrecision(); void setBuiltInFunctionPrecision();
......
...@@ -309,15 +309,20 @@ TIntermTyped *TIntermediate::addComma( ...@@ -309,15 +309,20 @@ TIntermTyped *TIntermediate::addComma(
TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock,
const TSourceLoc &line) const TSourceLoc &line)
{ {
TQualifier resultQualifier = EvqTemporary;
if (cond->getQualifier() == EvqConst && trueBlock->getQualifier() == EvqConst &&
falseBlock->getQualifier() == EvqConst)
{
resultQualifier = EvqConst;
}
// Right now it's safe to fold ternary operators only when all operands // Right now it's safe to fold ternary operators only when all operands
// are constant. If only the condition is constant, it's theoretically // are constant. If only the condition is constant, it's theoretically
// possible to fold the ternary operator, but that requires making sure // possible to fold the ternary operator, but that requires making sure
// that the node returned from here won't be treated as a constant // that the node returned from here won't be treated as a constant
// expression in case the node that gets eliminated was not a constant // expression in case the node that gets eliminated was not a constant
// expression. // expression.
if (cond->getAsConstantUnion() && if (resultQualifier == EvqConst && cond->getAsConstantUnion() &&
trueBlock->getAsConstantUnion() && trueBlock->getAsConstantUnion() && falseBlock->getAsConstantUnion())
falseBlock->getAsConstantUnion())
{ {
if (cond->getAsConstantUnion()->getBConst(0)) if (cond->getAsConstantUnion()->getBConst(0))
return trueBlock; return trueBlock;
...@@ -329,7 +334,7 @@ TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *true ...@@ -329,7 +334,7 @@ TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *true
// Make a selection node. // Make a selection node.
// //
TIntermSelection *node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType()); TIntermSelection *node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType());
node->getTypePointer()->setQualifier(EvqTemporary); node->getTypePointer()->setQualifier(resultQualifier);
node->setLine(line); node->setLine(line);
return node; return node;
......
...@@ -1869,7 +1869,9 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node) ...@@ -1869,7 +1869,9 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
TIntermTyped *variable = (*sequence)[0]->getAsTyped(); TIntermTyped *variable = (*sequence)[0]->getAsTyped();
ASSERT(sequence->size() == 1); ASSERT(sequence->size() == 1);
if (variable && (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal)) if (variable &&
(variable->getQualifier() == EvqTemporary ||
variable->getQualifier() == EvqGlobal || variable->getQualifier() == EvqConst))
{ {
ensureStructDefined(variable->getType()); ensureStructDefined(variable->getType());
......
...@@ -1188,7 +1188,7 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location, ...@@ -1188,7 +1188,7 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location,
{ {
const TVariable *variable = getNamedVariable(location, name, symbol); const TVariable *variable = getNamedVariable(location, name, symbol);
if (variable->getType().getQualifier() == EvqConst) if (variable->getType().getQualifier() == EvqConst && variable->getConstPointer())
{ {
TConstantUnion *constArray = variable->getConstPointer(); TConstantUnion *constArray = variable->getConstPointer();
TType t(variable->getType()); TType t(variable->getType());
...@@ -1310,9 +1310,17 @@ bool TParseContext::executeInitializer(const TSourceLoc &line, ...@@ -1310,9 +1310,17 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
variable->getType().setQualifier(EvqTemporary); variable->getType().setQualifier(EvqTemporary);
return true; return true;
} }
// Save the constant folded value to the variable if possible. For example array
// initializers are not folded, since that way copying the array literal to multiple places
// in the shader is avoided.
// TODO(oetuaho@nvidia.com): Consider constant folding array initialization in cases where
// it would be beneficial.
if (initializer->getAsConstantUnion()) if (initializer->getAsConstantUnion())
{ {
variable->shareConstPointer(initializer->getAsConstantUnion()->getUnionArrayPointer()); variable->shareConstPointer(initializer->getAsConstantUnion()->getUnionArrayPointer());
*intermNode = nullptr;
return false;
} }
else if (initializer->getAsSymbolNode()) else if (initializer->getAsSymbolNode())
{ {
...@@ -1321,34 +1329,22 @@ bool TParseContext::executeInitializer(const TSourceLoc &line, ...@@ -1321,34 +1329,22 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
const TVariable *tVar = static_cast<const TVariable *>(symbol); const TVariable *tVar = static_cast<const TVariable *>(symbol);
TConstantUnion *constArray = tVar->getConstPointer(); TConstantUnion *constArray = tVar->getConstPointer();
variable->shareConstPointer(constArray); if (constArray)
} {
else variable->shareConstPointer(constArray);
{ *intermNode = nullptr;
std::stringstream extraInfoStream; return false;
extraInfoStream << "'" << variable->getType().getCompleteString() << "'"; }
std::string extraInfo = extraInfoStream.str();
error(line, " cannot assign to", "=", extraInfo.c_str());
variable->getType().setQualifier(EvqTemporary);
return true;
} }
} }
if (qualifier != EvqConst) TIntermSymbol *intermSymbol = intermediate.addSymbol(
variable->getUniqueId(), variable->getName(), variable->getType(), line);
*intermNode = createAssign(EOpInitialize, intermSymbol, initializer, line);
if (*intermNode == nullptr)
{ {
TIntermSymbol *intermSymbol = intermediate.addSymbol( assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
variable->getUniqueId(), variable->getName(), variable->getType(), line); return true;
*intermNode = createAssign(EOpInitialize, intermSymbol, initializer, line);
if (*intermNode == nullptr)
{
assignError(line, "=", intermSymbol->getCompleteString(),
initializer->getCompleteString());
return true;
}
}
else
{
*intermNode = nullptr;
} }
return false; return false;
...@@ -2791,16 +2787,17 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, ...@@ -2791,16 +2787,17 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
recover(); recover();
index = 0; index = 0;
} }
if (baseExpression->getType().getQualifier() == EvqConst) if (baseExpression->getType().getQualifier() == EvqConst &&
baseExpression->getAsConstantUnion())
{ {
if (baseExpression->isArray()) if (baseExpression->isArray())
{ {
// constant folding for arrays // constant folding for array indexing
indexedExpression = addConstArrayNode(index, baseExpression, location); indexedExpression = addConstArrayNode(index, baseExpression, location);
} }
else if (baseExpression->isVector()) else if (baseExpression->isVector())
{ {
// constant folding for vectors // constant folding for vector indexing
TVectorFields fields; TVectorFields fields;
fields.num = 1; fields.num = 1;
fields.offsets[0] = fields.offsets[0] =
...@@ -2809,7 +2806,7 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, ...@@ -2809,7 +2806,7 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
} }
else if (baseExpression->isMatrix()) else if (baseExpression->isMatrix())
{ {
// constant folding for matrices // constant folding for matrix indexing
indexedExpression = addConstMatrixNode(index, baseExpression, location); indexedExpression = addConstMatrixNode(index, baseExpression, location);
} }
} }
...@@ -2948,7 +2945,8 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre ...@@ -2948,7 +2945,8 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre
recover(); recover();
} }
if (baseExpression->getType().getQualifier() == EvqConst) if (baseExpression->getType().getQualifier() == EvqConst &&
baseExpression->getAsConstantUnion())
{ {
// constant folding for vector fields // constant folding for vector fields
indexedExpression = addConstVectorNode(fields, baseExpression, fieldLocation); indexedExpression = addConstVectorNode(fields, baseExpression, fieldLocation);
...@@ -2998,7 +2996,8 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre ...@@ -2998,7 +2996,8 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre
} }
if (fieldFound) if (fieldFound)
{ {
if (baseExpression->getType().getQualifier() == EvqConst) if (baseExpression->getType().getQualifier() == EvqConst &&
baseExpression->getAsConstantUnion())
{ {
indexedExpression = addConstStruct(fieldString, baseExpression, dotLocation); indexedExpression = addConstStruct(fieldString, baseExpression, dotLocation);
if (indexedExpression == 0) if (indexedExpression == 0)
...@@ -3090,6 +3089,11 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre ...@@ -3090,6 +3089,11 @@ TIntermTyped *TParseContext::addFieldSelectionExpression(TIntermTyped *baseExpre
indexedExpression = baseExpression; indexedExpression = baseExpression;
} }
if (baseExpression->getQualifier() == EvqConst)
{
indexedExpression->getTypePointer()->setQualifier(EvqConst);
}
return indexedExpression; return indexedExpression;
} }
...@@ -3919,6 +3923,10 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall, ...@@ -3919,6 +3923,10 @@ TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall,
intermediate.setAggregateOperator(paramNode, op, loc); intermediate.setAggregateOperator(paramNode, op, loc);
aggregate->setType(fnCandidate->getReturnType()); aggregate->setType(fnCandidate->getReturnType());
aggregate->setPrecisionFromChildren(); aggregate->setPrecisionFromChildren();
if (aggregate->areChildrenConstQualified())
{
aggregate->getTypePointer()->setQualifier(EvqConst);
}
// Some built-in functions have out parameters too. // Some built-in functions have out parameters too.
functionCallLValueErrorCheck(fnCandidate, aggregate); functionCallLValueErrorCheck(fnCandidate, aggregate);
......
...@@ -9,6 +9,10 @@ ...@@ -9,6 +9,10 @@
// will effectively become // will effectively become
// type[n] a; // type[n] a;
// a = initializer; // a = initializer;
//
// Note that if the array is declared as const, the initialization is still split, making the AST
// technically invalid. Because of that this transformation should only be used when subsequent
// stages don't care about const qualifiers.
#include "compiler/translator/SeparateArrayInitialization.h" #include "compiler/translator/SeparateArrayInitialization.h"
......
...@@ -9,6 +9,10 @@ ...@@ -9,6 +9,10 @@
// will effectively become // will effectively become
// type[n] a; // type[n] a;
// a = initializer; // a = initializer;
//
// Note that if the array is declared as const, the initialization is still split, making the AST
// technically invalid. Because of that this transformation should only be used when subsequent
// stages don't care about const qualifiers.
#ifndef COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_ #ifndef COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_
#define COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_ #define COMPILER_TRANSLATOR_SEPARATEARRAYINITIALIZATION_H_
......
...@@ -933,3 +933,137 @@ TEST_F(MalformedShaderTest, OutOfRangeIntegerLiteral) ...@@ -933,3 +933,137 @@ TEST_F(MalformedShaderTest, OutOfRangeIntegerLiteral)
FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog; FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
} }
} }
// Test that vector field selection from a value taken from an array constructor is accepted as a
// constant expression.
TEST_F(MalformedShaderTest, FieldSelectionFromVectorArrayConstructorIsConst)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"void main()\n"
"{\n"
" const float f = vec2[1](vec2(0.0, 1.0))[0].x;\n"
" my_FragColor = vec4(f);\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
}
}
// Test that structure field selection from a value taken from an array constructor is accepted as a
// constant expression.
TEST_F(MalformedShaderTest, FieldSelectionFromStructArrayConstructorIsConst)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"struct S { float member; };\n"
"void main()\n"
"{\n"
" const float f = S[1](S(0.0))[0].member;\n"
" my_FragColor = vec4(f);\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
}
}
// Test that a reference to a const array is accepted as a constant expression.
TEST_F(MalformedShaderTest, ArraySymbolIsConst)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"void main()\n"
"{\n"
" const float[2] arr = float[2](0.0, 1.0);\n"
" const float f = arr[0];\n"
" my_FragColor = vec4(f);\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
}
}
// Test that using an array constructor in a parameter to a built-in function is accepted as a
// constant expression.
TEST_F(MalformedShaderTest, BuiltInFunctionAppliedToArrayConstructorIsConst)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"void main()\n"
"{\n"
" const float f = sin(float[2](0.0, 1.0)[0]);\n"
" my_FragColor = vec4(f);\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
}
}
// Test that using an array constructor in a parameter to a built-in function is accepted as a
// constant expression.
TEST_F(MalformedShaderTest, BuiltInFunctionWithMultipleParametersAppliedToArrayConstructorIsConst)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"void main()\n"
"{\n"
" const float f = pow(1.0, float[2](0.0, 1.0)[0]);\n"
" my_FragColor = vec4(f);\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
}
}
// Test that using an array constructor in a parameter to a constructor is accepted as a constant
// expression.
TEST_F(MalformedShaderTest, ConstructorWithMultipleParametersAppliedToArrayConstructorIsConst)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"void main()\n"
"{\n"
" const vec2 f = vec2(1.0, float[2](0.0, 1.0)[0]);\n"
" my_FragColor = vec4(f.x);\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
}
}
// Test that using an array constructor in an operand of the ternary selection operator is accepted
// as a constant expression.
TEST_F(MalformedShaderTest, TernaryOperatorAppliedToArrayConstructorIsConst)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"void main()\n"
"{\n"
" const float f = true ? float[2](0.0, 1.0)[0] : 1.0;\n"
" my_FragColor = vec4(f);\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
}
}
...@@ -257,12 +257,6 @@ ...@@ -257,12 +257,6 @@
1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.modf.highp_fragment = FAIL 1093 WIN : dEQP-GLES3.functional.shaders.builtin_functions.precision.modf.highp_fragment = FAIL
1094 WIN : dEQP-GLES3.functional.shaders.invariance.highp.loop_2 = FAIL 1094 WIN : dEQP-GLES3.functional.shaders.invariance.highp.loop_2 = FAIL
1094 WIN : dEQP-GLES3.functional.shaders.invariance.mediump.loop_2 = FAIL 1094 WIN : dEQP-GLES3.functional.shaders.invariance.mediump.loop_2 = FAIL
1094 WIN : dEQP-GLES3.functional.shaders.constant_expressions.complex_types.array_size_vertex = FAIL
1094 WIN : dEQP-GLES3.functional.shaders.constant_expressions.complex_types.array_size_fragment = FAIL
1094 WIN : dEQP-GLES3.functional.shaders.constant_expressions.complex_types.array_length_vertex = FAIL
1094 WIN : dEQP-GLES3.functional.shaders.constant_expressions.complex_types.array_length_fragment = FAIL
1094 WIN : dEQP-GLES3.functional.shaders.constant_expressions.complex_types.array_vertex = FAIL
1094 WIN : dEQP-GLES3.functional.shaders.constant_expressions.complex_types.array_fragment = FAIL
1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.projected.nearest_nearest_clamp = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.projected.nearest_nearest_clamp = FAIL
1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.projected.nearest_nearest_repeat = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.projected.nearest_nearest_repeat = FAIL
1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.projected.nearest_nearest_mirror = FAIL 1095 WIN : dEQP-GLES3.functional.texture.mipmap.2d.projected.nearest_nearest_mirror = FAIL
......
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