Commit 8e4b355b by Jiawei Shao Committed by Commit Bot

ES31: Implement Geometry Shader inputs and outputs

This patch intends to implement Geometry Shader inputs and outputs in ANGLE GLSL compiler. 1. Only accept arrays as the inputs of a Geometry Shader. 2. Allow unsized arrays as the inputs of a Geometry Shader after a valid input primitive declaration and assign size to them. 3. Implement Geometry Shader outputs. 4. Allow Geometry Shader inputs and outputs using interpolation qualifiers ('flat', 'smooth', 'centroid'). 5. Allow using 'location' layout qualifier on Geometry Shader inputs and outputs. BUG=angleproject:1941 TEST=angle_unittests Change-Id: Ia7e250277c61f45c8479437b567c2831ff26b433 Reviewed-on: https://chromium-review.googlesource.com/650211 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 73e4fde6
...@@ -611,6 +611,7 @@ Varying CollectVariablesTraverser::recordVarying(const TIntermSymbol &variable) ...@@ -611,6 +611,7 @@ Varying CollectVariablesTraverser::recordVarying(const TIntermSymbol &variable)
case EvqSmoothOut: case EvqSmoothOut:
case EvqFlatOut: case EvqFlatOut:
case EvqCentroidOut: case EvqCentroidOut:
case EvqGeometryOut:
if (mSymbolTable->isVaryingInvariant(std::string(variable.getSymbol().c_str())) || if (mSymbolTable->isVaryingInvariant(std::string(variable.getSymbol().c_str())) ||
type.isInvariant()) type.isInvariant())
{ {
......
...@@ -409,7 +409,7 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[], ...@@ -409,7 +409,7 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
if (success && shaderVersion >= 310) if (success && shaderVersion >= 310)
{ {
success = ValidateVaryingLocations(root, &mDiagnostics); success = ValidateVaryingLocations(root, &mDiagnostics, shaderType);
} }
if (success && shaderVersion >= 300 && shaderType == GL_FRAGMENT_SHADER) if (success && shaderVersion >= 300 && shaderType == GL_FRAGMENT_SHADER)
......
...@@ -207,7 +207,7 @@ TParseContext::TParseContext(TSymbolTable &symt, ...@@ -207,7 +207,7 @@ TParseContext::TParseContext(TSymbolTable &symt,
mGeometryShaderMaxVertices(-1), mGeometryShaderMaxVertices(-1),
mMaxGeometryShaderInvocations(resources.MaxGeometryShaderInvocations), mMaxGeometryShaderInvocations(resources.MaxGeometryShaderInvocations),
mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices), mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices),
mGeometryShaderInputArraySize(0) mGeometryShaderInputArraySize(0u)
{ {
mComputeShaderLocalSize.fill(-1); mComputeShaderLocalSize.fill(-1);
} }
...@@ -476,6 +476,7 @@ bool TParseContext::checkCanBeLValue(const TSourceLoc &line, const char *op, TIn ...@@ -476,6 +476,7 @@ bool TParseContext::checkCanBeLValue(const TSourceLoc &line, const char *op, TIn
break; break;
case EvqFragmentIn: case EvqFragmentIn:
case EvqVertexIn: case EvqVertexIn:
case EvqGeometryIn:
case EvqFlatIn: case EvqFlatIn:
case EvqSmoothIn: case EvqSmoothIn:
case EvqCentroidIn: case EvqCentroidIn:
...@@ -1772,15 +1773,15 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location, ...@@ -1772,15 +1773,15 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location,
return node; return node;
} }
const TType &variableType = variable->getType();
TIntermTyped *node = nullptr; TIntermTyped *node = nullptr;
if (variable->getConstPointer()) if (variable->getConstPointer())
{ {
const TConstantUnion *constArray = variable->getConstPointer(); const TConstantUnion *constArray = variable->getConstPointer();
node = new TIntermConstantUnion(constArray, variable->getType()); node = new TIntermConstantUnion(constArray, variableType);
} }
else if (variable->getType().getQualifier() == EvqWorkGroupSize && else if (variableType.getQualifier() == EvqWorkGroupSize && mComputeShaderLocalSizeDeclared)
mComputeShaderLocalSizeDeclared)
{ {
// gl_WorkGroupSize can be used to size arrays according to the ESSL 3.10.4 spec, so it // gl_WorkGroupSize can be used to size arrays according to the ESSL 3.10.4 spec, so it
// needs to be added to the AST as a constant and not as a symbol. // needs to be added to the AST as a constant and not as a symbol.
...@@ -1791,23 +1792,24 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location, ...@@ -1791,23 +1792,24 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location,
constArray[i].setUConst(static_cast<unsigned int>(workGroupSize[i])); constArray[i].setUConst(static_cast<unsigned int>(workGroupSize[i]));
} }
ASSERT(variable->getType().getBasicType() == EbtUInt); ASSERT(variableType.getBasicType() == EbtUInt);
ASSERT(variable->getType().getObjectSize() == 3); ASSERT(variableType.getObjectSize() == 3);
TType type(variable->getType()); TType type(variableType);
type.setQualifier(EvqConst); type.setQualifier(EvqConst);
node = new TIntermConstantUnion(constArray, type); node = new TIntermConstantUnion(constArray, type);
} }
// TODO(jiawei.shao@intel.com): set array sizes for user-defined geometry shader inputs. else if ((mGeometryShaderInputPrimitiveType != EptUndefined) &&
else if (variable->getType().getQualifier() == EvqPerVertexIn) (variableType.getQualifier() == EvqPerVertexIn))
{ {
TType type(variable->getType()); ASSERT(mGeometryShaderInputArraySize > 0u);
type.setArraySize(0, mGeometryShaderInputArraySize);
node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), type); node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variableType);
node->getTypePointer()->setArraySize(0, mGeometryShaderInputArraySize);
} }
else else
{ {
node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variable->getType()); node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variableType);
} }
ASSERT(node != nullptr); ASSERT(node != nullptr);
node->setLine(location); node->setLine(location);
...@@ -2297,6 +2299,13 @@ TIntermDeclaration *TParseContext::parseSingleDeclaration( ...@@ -2297,6 +2299,13 @@ TIntermDeclaration *TParseContext::parseSingleDeclaration(
} }
} }
if (IsGeometryShaderInput(mShaderType, type.getQualifier()))
{
error(identifierOrTypeLocation,
"Geometry shader input varying variable must be declared as an array",
identifier.c_str());
}
declarationQualifierErrorCheck(publicType.qualifier, publicType.layoutQualifier, declarationQualifierErrorCheck(publicType.qualifier, publicType.layoutQualifier,
identifierOrTypeLocation); identifierOrTypeLocation);
...@@ -2369,15 +2378,51 @@ TIntermDeclaration *TParseContext::parseSingleArrayDeclaration(TPublicType &publ ...@@ -2369,15 +2378,51 @@ TIntermDeclaration *TParseContext::parseSingleArrayDeclaration(TPublicType &publ
TType arrayType(publicType); TType arrayType(publicType);
unsigned int size = checkIsValidArraySize(identifierLocation, indexExpression); if (indexExpression == nullptr)
// Make the type an array even if size check failed.
// This ensures useless error messages regarding the variable's non-arrayness won't follow.
arrayType.makeArray(size);
if (IsAtomicCounter(publicType.getBasicType()))
{ {
checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterArrayStride * size, false, if (IsGeometryShaderInput(mShaderType, publicType.qualifier))
identifierLocation, arrayType); {
// Set size for the unsized geometry shader inputs if they are declared after a valid
// input primitive declaration.
if (mGeometryShaderInputPrimitiveType != EptUndefined)
{
ASSERT(mGeometryShaderInputArraySize > 0u);
arrayType.makeArray(mGeometryShaderInputArraySize);
}
else
{
// [GLSL ES 3.2 SPEC Chapter 4.4.1.2]
// An input can be declared without an array size if there is a previous layout
// which specifies the size.
error(indexLocation,
"Missing a valid input primitive declaration before declaring an unsized "
"array input",
"");
}
}
else
{
// Unsized array declarations are only allowed in declaring geometry shader inputs.
error(indexLocation, "Invalid unsized array declaration", "");
}
}
else
{
unsigned int size = checkIsValidArraySize(identifierLocation, indexExpression);
if (IsGeometryShaderInput(mShaderType, publicType.qualifier))
{
setGeometryShaderInputArraySize(size, indexLocation);
}
// Make the type an array even if size check failed.
// This ensures useless error messages regarding the variable's non-arrayness won't follow.
arrayType.makeArray(size);
if (IsAtomicCounter(publicType.getBasicType()))
{
checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterArrayStride * size,
false, identifierLocation, arrayType);
}
} }
TVariable *variable = nullptr; TVariable *variable = nullptr;
...@@ -2734,13 +2779,20 @@ bool TParseContext::checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier ...@@ -2734,13 +2779,20 @@ bool TParseContext::checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier
} }
} }
void TParseContext::setGeometryShaderInputArraySizes() void TParseContext::setGeometryShaderInputArraySize(unsigned int inputArraySize,
const TSourceLoc &line)
{ {
// TODO(jiawei.shao@intel.com): check former input array sizes match the input primitive if (mGeometryShaderInputArraySize == 0u)
// declaration. {
ASSERT(mGeometryShaderInputArraySize == 0); mGeometryShaderInputArraySize = inputArraySize;
mGeometryShaderInputArraySize = }
GetGeometryShaderInputArraySize(mGeometryShaderInputPrimitiveType); else if (mGeometryShaderInputArraySize != inputArraySize)
{
error(line,
"Array size or input primitive declaration doesn't match the size of earlier sized "
"array inputs.",
"layout");
}
} }
bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier) bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier)
...@@ -2768,7 +2820,9 @@ bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier ...@@ -2768,7 +2820,9 @@ bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier
if (mGeometryShaderInputPrimitiveType == EptUndefined) if (mGeometryShaderInputPrimitiveType == EptUndefined)
{ {
mGeometryShaderInputPrimitiveType = layoutQualifier.primitiveType; mGeometryShaderInputPrimitiveType = layoutQualifier.primitiveType;
setGeometryShaderInputArraySizes(); setGeometryShaderInputArraySize(
GetGeometryShaderInputArraySize(mGeometryShaderInputPrimitiveType),
typeQualifier.line);
} }
else if (mGeometryShaderInputPrimitiveType != layoutQualifier.primitiveType) else if (mGeometryShaderInputPrimitiveType != layoutQualifier.primitiveType)
{ {
...@@ -5562,6 +5616,7 @@ TIntermTyped *TParseContext::addMethod(TFunction *fnCall, ...@@ -5562,6 +5616,7 @@ TIntermTyped *TParseContext::addMethod(TFunction *fnCall,
else if (typedThis->getQualifier() == EvqPerVertexIn && else if (typedThis->getQualifier() == EvqPerVertexIn &&
mGeometryShaderInputPrimitiveType == EptUndefined) mGeometryShaderInputPrimitiveType == EptUndefined)
{ {
ASSERT(mShaderType == GL_GEOMETRY_SHADER_OES);
error(loc, "missing input primitive declaration before calling length on gl_in", "length"); error(loc, "missing input primitive declaration before calling length on gl_in", "length");
} }
else else
......
...@@ -539,7 +539,7 @@ class TParseContext : angle::NonCopyable ...@@ -539,7 +539,7 @@ class TParseContext : angle::NonCopyable
bool checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier &typeQualifier); bool checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier &typeQualifier);
bool parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier); bool parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier);
bool parseGeometryShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier); bool parseGeometryShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier);
void setGeometryShaderInputArraySizes(); void setGeometryShaderInputArraySize(unsigned int inputArraySize, const TSourceLoc &line);
// Set to true when the last/current declarator list was started with an empty declaration. The // Set to true when the last/current declarator list was started with an empty declaration. The
// non-empty declaration error check will need to be performed if the empty declaration is // non-empty declaration error check will need to be performed if the empty declaration is
...@@ -606,8 +606,9 @@ class TParseContext : angle::NonCopyable ...@@ -606,8 +606,9 @@ class TParseContext : angle::NonCopyable
int mGeometryShaderMaxVertices; int mGeometryShaderMaxVertices;
int mMaxGeometryShaderInvocations; int mMaxGeometryShaderInvocations;
int mMaxGeometryShaderMaxVertices; int mMaxGeometryShaderMaxVertices;
int mGeometryShaderInputArraySize; // Track if all input array sizes are same and matches the
// latter input primitive declaration. // Track if all input array sizes are same and matches the latter input primitive declaration.
unsigned int mGeometryShaderInputArraySize;
}; };
int PaParseStrings(size_t count, int PaParseStrings(size_t count,
......
...@@ -308,9 +308,11 @@ bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storag ...@@ -308,9 +308,11 @@ bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storag
*joinedQualifier = EvqCentroid; *joinedQualifier = EvqCentroid;
break; break;
case EvqVertexOut: case EvqVertexOut:
case EvqGeometryOut:
*joinedQualifier = EvqSmoothOut; *joinedQualifier = EvqSmoothOut;
break; break;
case EvqFragmentIn: case EvqFragmentIn:
case EvqGeometryIn:
*joinedQualifier = EvqSmoothIn; *joinedQualifier = EvqSmoothIn;
break; break;
default: default:
...@@ -326,9 +328,11 @@ bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storag ...@@ -326,9 +328,11 @@ bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storag
*joinedQualifier = EvqFlat; *joinedQualifier = EvqFlat;
break; break;
case EvqVertexOut: case EvqVertexOut:
case EvqGeometryOut:
*joinedQualifier = EvqFlatOut; *joinedQualifier = EvqFlatOut;
break; break;
case EvqFragmentIn: case EvqFragmentIn:
case EvqGeometryIn:
*joinedQualifier = EvqFlatIn; *joinedQualifier = EvqFlatIn;
break; break;
default: default:
...@@ -341,9 +345,11 @@ bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storag ...@@ -341,9 +345,11 @@ bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storag
switch (storageQualifier) switch (storageQualifier)
{ {
case EvqVertexOut: case EvqVertexOut:
case EvqGeometryOut:
*joinedQualifier = EvqCentroidOut; *joinedQualifier = EvqCentroidOut;
break; break;
case EvqFragmentIn: case EvqFragmentIn:
case EvqGeometryIn:
*joinedQualifier = EvqCentroidIn; *joinedQualifier = EvqCentroidIn;
break; break;
default: default:
......
...@@ -24,7 +24,7 @@ void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagno ...@@ -24,7 +24,7 @@ void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagno
diagnostics->error(symbol.getLine(), reason, symbol.getSymbol().c_str()); diagnostics->error(symbol.getLine(), reason, symbol.getSymbol().c_str());
} }
int GetLocationCount(const TIntermSymbol *varying) int GetLocationCount(const TIntermSymbol *varying, bool ignoreVaryingArraySize)
{ {
const auto &varyingType = varying->getType(); const auto &varyingType = varying->getType();
if (varyingType.getStruct() != nullptr) if (varyingType.getStruct() != nullptr)
...@@ -40,6 +40,18 @@ int GetLocationCount(const TIntermSymbol *varying) ...@@ -40,6 +40,18 @@ int GetLocationCount(const TIntermSymbol *varying)
} }
return totalLocation; return totalLocation;
} }
// [GL_OES_shader_io_blocks SPEC Chapter 4.4.1]
// Geometry shader inputs, tessellation control shader inputs and outputs, and tessellation
// evaluation inputs all have an additional level of arrayness relative to other shader inputs
// and outputs. This outer array level is removed from the type before considering how many
// locations the type consumes.
else if (ignoreVaryingArraySize)
{
// Array-of-arrays cannot be inputs or outputs of a geometry shader.
// (GL_OES_geometry_shader SPEC issues(5))
ASSERT(!varyingType.isArrayOfArrays());
return varyingType.getSecondarySize();
}
else else
{ {
return varyingType.getSecondarySize() * static_cast<int>(varyingType.getArraySizeProduct()); return varyingType.getSecondarySize() * static_cast<int>(varyingType.getArraySizeProduct());
...@@ -48,7 +60,9 @@ int GetLocationCount(const TIntermSymbol *varying) ...@@ -48,7 +60,9 @@ int GetLocationCount(const TIntermSymbol *varying)
using VaryingVector = std::vector<const TIntermSymbol *>; using VaryingVector = std::vector<const TIntermSymbol *>;
void ValidateShaderInterface(TDiagnostics *diagnostics, VaryingVector &varyingVector) void ValidateShaderInterface(TDiagnostics *diagnostics,
VaryingVector &varyingVector,
bool ignoreVaryingArraySize)
{ {
// Location conflicts can only happen when there are two or more varyings in varyingVector. // Location conflicts can only happen when there are two or more varyings in varyingVector.
if (varyingVector.size() <= 1) if (varyingVector.size() <= 1)
...@@ -62,7 +76,7 @@ void ValidateShaderInterface(TDiagnostics *diagnostics, VaryingVector &varyingVe ...@@ -62,7 +76,7 @@ void ValidateShaderInterface(TDiagnostics *diagnostics, VaryingVector &varyingVe
const int location = varying->getType().getLayoutQualifier().location; const int location = varying->getType().getLayoutQualifier().location;
ASSERT(location >= 0); ASSERT(location >= 0);
const int elementCount = GetLocationCount(varying); const int elementCount = GetLocationCount(varying, ignoreVaryingArraySize);
for (int elementIndex = 0; elementIndex < elementCount; ++elementIndex) for (int elementIndex = 0; elementIndex < elementCount; ++elementIndex)
{ {
const int offsetLocation = location + elementIndex; const int offsetLocation = location + elementIndex;
...@@ -85,7 +99,7 @@ void ValidateShaderInterface(TDiagnostics *diagnostics, VaryingVector &varyingVe ...@@ -85,7 +99,7 @@ void ValidateShaderInterface(TDiagnostics *diagnostics, VaryingVector &varyingVe
class ValidateVaryingLocationsTraverser : public TIntermTraverser class ValidateVaryingLocationsTraverser : public TIntermTraverser
{ {
public: public:
ValidateVaryingLocationsTraverser(); ValidateVaryingLocationsTraverser(GLenum shaderType);
void validate(TDiagnostics *diagnostics); void validate(TDiagnostics *diagnostics);
private: private:
...@@ -94,10 +108,11 @@ class ValidateVaryingLocationsTraverser : public TIntermTraverser ...@@ -94,10 +108,11 @@ class ValidateVaryingLocationsTraverser : public TIntermTraverser
VaryingVector mInputVaryingsWithLocation; VaryingVector mInputVaryingsWithLocation;
VaryingVector mOutputVaryingsWithLocation; VaryingVector mOutputVaryingsWithLocation;
GLenum mShaderType;
}; };
ValidateVaryingLocationsTraverser::ValidateVaryingLocationsTraverser() ValidateVaryingLocationsTraverser::ValidateVaryingLocationsTraverser(GLenum shaderType)
: TIntermTraverser(true, false, false) : TIntermTraverser(true, false, false), mShaderType(shaderType)
{ {
} }
...@@ -140,15 +155,16 @@ void ValidateVaryingLocationsTraverser::validate(TDiagnostics *diagnostics) ...@@ -140,15 +155,16 @@ void ValidateVaryingLocationsTraverser::validate(TDiagnostics *diagnostics)
{ {
ASSERT(diagnostics); ASSERT(diagnostics);
ValidateShaderInterface(diagnostics, mInputVaryingsWithLocation); ValidateShaderInterface(diagnostics, mInputVaryingsWithLocation,
ValidateShaderInterface(diagnostics, mOutputVaryingsWithLocation); mShaderType == GL_GEOMETRY_SHADER_OES);
ValidateShaderInterface(diagnostics, mOutputVaryingsWithLocation, false);
} }
} // anonymous namespace } // anonymous namespace
bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics) bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics, GLenum shaderType)
{ {
ValidateVaryingLocationsTraverser varyingValidator; ValidateVaryingLocationsTraverser varyingValidator(shaderType);
root->traverse(&varyingValidator); root->traverse(&varyingValidator);
int numErrorsBefore = diagnostics->numErrors(); int numErrorsBefore = diagnostics->numErrors();
varyingValidator.validate(diagnostics); varyingValidator.validate(diagnostics);
......
...@@ -10,13 +10,15 @@ ...@@ -10,13 +10,15 @@
#ifndef COMPILER_TRANSLATOR_VALIDATEVARYINGLOCATIONS_H_ #ifndef COMPILER_TRANSLATOR_VALIDATEVARYINGLOCATIONS_H_
#define COMPILER_TRANSLATOR_VALIDATEVARYINGLOCATIONS_H_ #define COMPILER_TRANSLATOR_VALIDATEVARYINGLOCATIONS_H_
#include "GLSLANG/ShaderVars.h"
namespace sh namespace sh
{ {
class TIntermBlock; class TIntermBlock;
class TDiagnostics; class TDiagnostics;
bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics); bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics, GLenum shaderType);
} // namespace sh } // namespace sh
......
...@@ -749,6 +749,11 @@ single_declaration ...@@ -749,6 +749,11 @@ single_declaration
$$.type = $1; $$.type = $1;
$$.intermDeclaration = context->parseSingleDeclaration($$.type, @2, *$2.string); $$.intermDeclaration = context->parseSingleDeclaration($$.type, @2, *$2.string);
} }
| fully_specified_type identifier LEFT_BRACKET RIGHT_BRACKET {
ES3_1_ONLY("[]", @3, "implicitly sized array declaration");
$$.type = $1;
$$.intermDeclaration = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, nullptr);
}
| fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET { | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
$$.type = $1; $$.type = $1;
$$.intermDeclaration = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, $4); $$.intermDeclaration = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, $4);
......
...@@ -23,6 +23,24 @@ bool atoi_clamp(const char *str, unsigned int *value) ...@@ -23,6 +23,24 @@ bool atoi_clamp(const char *str, unsigned int *value)
namespace sh namespace sh
{ {
namespace
{
bool IsInterpolationIn(TQualifier qualifier)
{
switch (qualifier)
{
case EvqSmoothIn:
case EvqFlatIn:
case EvqCentroidIn:
return true;
default:
return false;
}
}
} // anonymous namespace
float NumericLexFloat32OutOfRangeToInfinity(const std::string &str) float NumericLexFloat32OutOfRangeToInfinity(const std::string &str)
{ {
// Parses a decimal string using scientific notation into a floating point number. // Parses a decimal string using scientific notation into a floating point number.
...@@ -442,7 +460,12 @@ TString ArrayString(const TType &type) ...@@ -442,7 +460,12 @@ TString ArrayString(const TType &type)
for (auto arraySizeIter = arraySizes.rbegin(); arraySizeIter != arraySizes.rend(); for (auto arraySizeIter = arraySizes.rbegin(); arraySizeIter != arraySizes.rend();
++arraySizeIter) ++arraySizeIter)
{ {
arrayString << "[" << (*arraySizeIter) << "]"; arrayString << "[";
if (*arraySizeIter > 0)
{
arrayString << (*arraySizeIter);
}
arrayString << "]";
} }
return arrayString.str(); return arrayString.str();
} }
...@@ -464,6 +487,7 @@ bool IsVaryingOut(TQualifier qualifier) ...@@ -464,6 +487,7 @@ bool IsVaryingOut(TQualifier qualifier)
case EvqFlatOut: case EvqFlatOut:
case EvqCentroidOut: case EvqCentroidOut:
case EvqVertexOut: case EvqVertexOut:
case EvqGeometryOut:
return true; return true;
default: default:
...@@ -482,6 +506,7 @@ bool IsVaryingIn(TQualifier qualifier) ...@@ -482,6 +506,7 @@ bool IsVaryingIn(TQualifier qualifier)
case EvqFlatIn: case EvqFlatIn:
case EvqCentroidIn: case EvqCentroidIn:
case EvqFragmentIn: case EvqFragmentIn:
case EvqGeometryIn:
return true; return true;
default: default:
...@@ -496,6 +521,12 @@ bool IsVarying(TQualifier qualifier) ...@@ -496,6 +521,12 @@ bool IsVarying(TQualifier qualifier)
return IsVaryingIn(qualifier) || IsVaryingOut(qualifier); return IsVaryingIn(qualifier) || IsVaryingOut(qualifier);
} }
bool IsGeometryShaderInput(GLenum shaderType, TQualifier qualifier)
{
return (qualifier == EvqGeometryIn) ||
((shaderType == GL_GEOMETRY_SHADER_OES) && IsInterpolationIn(qualifier));
}
InterpolationType GetInterpolationType(TQualifier qualifier) InterpolationType GetInterpolationType(TQualifier qualifier)
{ {
switch (qualifier) switch (qualifier)
...@@ -510,6 +541,8 @@ InterpolationType GetInterpolationType(TQualifier qualifier) ...@@ -510,6 +541,8 @@ InterpolationType GetInterpolationType(TQualifier qualifier)
case EvqFragmentIn: case EvqFragmentIn:
case EvqVaryingIn: case EvqVaryingIn:
case EvqVaryingOut: case EvqVaryingOut:
case EvqGeometryIn:
case EvqGeometryOut:
return INTERPOLATION_SMOOTH; return INTERPOLATION_SMOOTH;
case EvqCentroidIn: case EvqCentroidIn:
......
...@@ -39,6 +39,7 @@ GLenum GLVariablePrecision(const TType &type); ...@@ -39,6 +39,7 @@ GLenum GLVariablePrecision(const TType &type);
bool IsVaryingIn(TQualifier qualifier); bool IsVaryingIn(TQualifier qualifier);
bool IsVaryingOut(TQualifier qualifier); bool IsVaryingOut(TQualifier qualifier);
bool IsVarying(TQualifier qualifier); bool IsVarying(TQualifier qualifier);
bool IsGeometryShaderInput(GLenum shaderType, TQualifier qualifier);
InterpolationType GetInterpolationType(TQualifier qualifier); InterpolationType GetInterpolationType(TQualifier qualifier);
// Returns array brackets including size with outermost array size first, as specified in GLSL ES // Returns array brackets including size with outermost array size first, as specified in GLSL ES
......
...@@ -182,17 +182,17 @@ class CollectGeometryVariablesTest : public CollectVariablesOESGeometryShaderTes ...@@ -182,17 +182,17 @@ class CollectGeometryVariablesTest : public CollectVariablesOESGeometryShaderTes
} }
protected: protected:
void compileGeometryShaderWithInputPrimitive(const std::string &inputPrimitive) void compileGeometryShaderWithInputPrimitive(const std::string &inputPrimitive,
const std::string &inputVarying,
const std::string &functionBody)
{ {
std::ostringstream sstream; std::ostringstream sstream;
sstream << "#version 310 es\n" sstream << "#version 310 es\n"
<< "#extension GL_OES_geometry_shader : require\n" << "#extension GL_OES_geometry_shader : require\n"
<< "layout (" << inputPrimitive << ") in;\n" << "layout (" << inputPrimitive << ") in;\n"
<< "layout (points, max_vertices = 2) out;\n" << "layout (points, max_vertices = 2) out;\n"
<< "void main()\n" << inputVarying << functionBody;
<< "{\n"
<< " vec4 value = gl_in[0].gl_Position;\n"
<< "}\n";
compile(sstream.str()); compile(sstream.str());
} }
}; };
...@@ -981,11 +981,18 @@ TEST_F(CollectGeometryVariablesTest, GLInArraySize) ...@@ -981,11 +981,18 @@ TEST_F(CollectGeometryVariablesTest, GLInArraySize)
{ {
const std::array<std::string, 5> kInputPrimitives = { const std::array<std::string, 5> kInputPrimitives = {
{"points", "lines", "lines_adjacency", "triangles", "triangles_adjacency"}}; {"points", "lines", "lines_adjacency", "triangles", "triangles_adjacency"}};
constexpr GLuint kArraySizeForInputPrimitives[] = {1u, 2u, 4u, 3u, 6u};
const GLuint kArraySizeForInputPrimitives[] = {1u, 2u, 4u, 3u, 6u};
const std::string &functionBody =
"void main()\n"
"{\n"
" vec4 value = gl_in[0].gl_Position;\n"
"}\n";
for (size_t i = 0; i < kInputPrimitives.size(); ++i) for (size_t i = 0; i < kInputPrimitives.size(); ++i)
{ {
compileGeometryShaderWithInputPrimitive(kInputPrimitives[i]); compileGeometryShaderWithInputPrimitive(kInputPrimitives[i], "", functionBody);
const auto &inBlocks = mTranslator->getInBlocks(); const auto &inBlocks = mTranslator->getInBlocks();
ASSERT_EQ(1u, inBlocks.size()); ASSERT_EQ(1u, inBlocks.size());
...@@ -1288,3 +1295,172 @@ TEST_F(CollectFragmentVariablesES31Test, CollectInputWithLocation) ...@@ -1288,3 +1295,172 @@ TEST_F(CollectFragmentVariablesES31Test, CollectInputWithLocation)
EXPECT_EQ("f_input2", varying2->name); EXPECT_EQ("f_input2", varying2->name);
EXPECT_EQ(1, varying2->location); EXPECT_EQ(1, varying2->location);
} }
// Test collecting the inputs of a geometry shader.
TEST_F(CollectGeometryVariablesTest, CollectInputs)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, max_vertices = 2) out;\n"
"in vec4 texcoord1[];\n"
"in vec4 texcoord2[1];\n"
"void main()\n"
"{\n"
" vec4 coord1 = texcoord1[0];\n"
" vec4 coord2 = texcoord2[0];\n"
"}\n";
compile(shaderString);
ASSERT_TRUE(mTranslator->getOutputVaryings().empty());
const auto &inputVaryings = mTranslator->getInputVaryings();
ASSERT_EQ(2u, inputVaryings.size());
const std::string kVaryingName[] = {"texcoord1", "texcoord2"};
for (size_t i = 0; i < inputVaryings.size(); ++i)
{
const Varying &varying = inputVaryings[i];
EXPECT_EQ(kVaryingName[i], varying.name);
EXPECT_TRUE(varying.isArray());
EXPECT_FALSE(varying.isStruct());
EXPECT_TRUE(varying.staticUse);
EXPECT_FALSE(varying.isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, varying.precision);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, varying.type);
EXPECT_FALSE(varying.isInvariant);
EXPECT_EQ(1u, varying.arraySize);
}
}
// Test that the unsized input of a geometry shader can be correctly collected.
TEST_F(CollectGeometryVariablesTest, CollectInputArraySizeForUnsizedInput)
{
const std::array<std::string, 5> kInputPrimitives = {
{"points", "lines", "lines_adjacency", "triangles", "triangles_adjacency"}};
const GLuint kArraySizeForInputPrimitives[] = {1u, 2u, 4u, 3u, 6u};
const std::string &kVariableDeclaration = "in vec4 texcoord[];\n";
const std::string &kFunctionBody =
"void main()\n"
"{\n"
" vec4 value = texcoord[0];\n"
"}\n";
for (size_t i = 0; i < kInputPrimitives.size(); ++i)
{
compileGeometryShaderWithInputPrimitive(kInputPrimitives[i], kVariableDeclaration,
kFunctionBody);
const auto &inputVaryings = mTranslator->getInputVaryings();
ASSERT_EQ(1u, inputVaryings.size());
const Varying *varying = &inputVaryings[0];
EXPECT_EQ("texcoord", varying->name);
EXPECT_EQ(kArraySizeForInputPrimitives[i], varying->arraySize);
}
}
// Test collecting inputs using interpolation qualifiers in a geometry shader.
TEST_F(CollectGeometryVariablesTest, CollectInputsWithInterpolationQualifiers)
{
const std::string &kHeader =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n";
const std::string &kLayout =
"layout (points) in;\n"
"layout (points, max_vertices = 2) out;\n";
const std::array<std::string, 3> kInterpolationQualifiers = {{"flat", "smooth", "centroid"}};
const std::array<InterpolationType, 3> kInterpolationType = {
{INTERPOLATION_FLAT, INTERPOLATION_SMOOTH, INTERPOLATION_CENTROID}};
const std::string &kFunctionBody =
"void main()\n"
"{\n"
" vec4 value = texcoord[0];\n"
"}\n";
for (size_t i = 0; i < kInterpolationQualifiers.size(); ++i)
{
const std::string &qualifier = kInterpolationQualifiers[i];
std::ostringstream stream1;
stream1 << kHeader << kLayout << qualifier << " in vec4 texcoord[];\n" << kFunctionBody;
compile(stream1.str());
const auto &inputVaryings = mTranslator->getInputVaryings();
ASSERT_EQ(1u, inputVaryings.size());
const Varying *varying = &inputVaryings[0];
EXPECT_EQ("texcoord", varying->name);
EXPECT_EQ(kInterpolationType[i], varying->interpolation);
}
}
// Test collecting outputs using interpolation qualifiers in a geometry shader.
TEST_F(CollectGeometryVariablesTest, CollectOutputsWithInterpolationQualifiers)
{
const std::string &kHeader =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, max_vertices = 2) out;\n";
const std::array<std::string, 4> kInterpolationQualifiers = {
{"", "flat", "smooth", "centroid"}};
const std::array<InterpolationType, 4> kInterpolationType = {
{INTERPOLATION_SMOOTH, INTERPOLATION_FLAT, INTERPOLATION_SMOOTH, INTERPOLATION_CENTROID}};
const std::string &kFunctionBody =
"void main()\n"
"{\n"
" texcoord = vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n";
for (size_t i = 0; i < kInterpolationQualifiers.size(); ++i)
{
const std::string &qualifier = kInterpolationQualifiers[i];
std::ostringstream stream;
stream << kHeader << qualifier << " out vec4 texcoord;\n" << kFunctionBody;
compile(stream.str());
const auto &outputVaryings = mTranslator->getOutputVaryings();
ASSERT_EQ(1u, outputVaryings.size());
const Varying *varying = &outputVaryings[0];
EXPECT_EQ("texcoord", varying->name);
EXPECT_EQ(kInterpolationType[i], varying->interpolation);
EXPECT_FALSE(varying->isInvariant);
}
}
// Test collecting outputs using 'invariant' qualifier in a geometry shader.
TEST_F(CollectGeometryVariablesTest, CollectOutputsWithInvariant)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, max_vertices = 2) out;\n"
"invariant out vec4 texcoord;\n"
"void main()\n"
"{\n"
" texcoord = vec4(1.0, 0.0, 0.0, 1.0);\n"
"}\n";
compile(shaderString);
const auto &outputVaryings = mTranslator->getOutputVaryings();
ASSERT_EQ(1u, outputVaryings.size());
const Varying *varying = &outputVaryings[0];
EXPECT_EQ("texcoord", varying->name);
EXPECT_TRUE(varying->isInvariant);
}
...@@ -5087,3 +5087,20 @@ TEST_F(FragmentShaderValidationTest, SwitchFinalCaseEmptyESSL310) ...@@ -5087,3 +5087,20 @@ TEST_F(FragmentShaderValidationTest, SwitchFinalCaseEmptyESSL310)
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog; FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
} }
} }
// Test that fragment shader cannot declare unsized inputs.
TEST_F(FragmentShaderValidationTest, UnsizedInputs)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"in float i_value[];\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
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