Commit d8105a03 by Jiawei Shao Committed by Commit Bot

ES31: Implement gl_in in Geometry Shader

This patch intends to implement geometry shader built-in interface block instance gl_in defined in GL_OES_geometry_shader. 1. Add the definition of gl_in and its interface block gl_PerVertex into the symbol table. 2. Support gl_Position as a member of gl_in. 3. Set the array size of gl_in when a valid input primitive type is known. 4. Add check that it should be a compile error to index gl_in or call length() on gl_in without a valid input primitive declaration. This patch also adds unit tests to cover all these new features. BUG=angleproject:1941 TEST=angle_unittests Change-Id: I8da20c943b29c9ce904834625b396aab6302e1e1 Reviewed-on: https://chromium-review.googlesource.com/605059 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarOlli Etuaho <oetuaho@nvidia.com>
parent 401345e4
...@@ -45,7 +45,11 @@ enum BlockLayoutType ...@@ -45,7 +45,11 @@ enum BlockLayoutType
enum class BlockType enum class BlockType
{ {
BLOCK_UNIFORM, BLOCK_UNIFORM,
BLOCK_BUFFER BLOCK_BUFFER,
// Required in OpenGL ES 3.1 extension GL_OES_shader_io_blocks.
// TODO(jiawei.shao@intel.com): add BLOCK_OUT.
BLOCK_IN
}; };
// Base class for all variables defined in shaders, including Varyings, Uniforms, etc // Base class for all variables defined in shaders, including Varyings, Uniforms, etc
...@@ -218,6 +222,8 @@ struct InterfaceBlock ...@@ -218,6 +222,8 @@ struct InterfaceBlock
// Decide whether two interface blocks are the same at shader link time. // Decide whether two interface blocks are the same at shader link time.
bool isSameInterfaceBlockAtLinkTime(const InterfaceBlock &other) const; bool isSameInterfaceBlockAtLinkTime(const InterfaceBlock &other) const;
bool isBuiltIn() const { return name.compare(0, 3, "gl_") == 0; }
std::string name; std::string name;
std::string mappedName; std::string mappedName;
std::string instanceName; std::string instanceName;
......
...@@ -576,6 +576,7 @@ enum TQualifier ...@@ -576,6 +576,7 @@ enum TQualifier
// GLSL ES 3.1 extension OES_geometry_shader qualifiers // GLSL ES 3.1 extension OES_geometry_shader qualifiers
EvqGeometryIn, EvqGeometryIn,
EvqGeometryOut, EvqGeometryOut,
EvqPerVertexIn,
EvqLayer, // gl_Layer EvqLayer, // gl_Layer
// end of list // end of list
...@@ -832,6 +833,7 @@ inline const char *getQualifierString(TQualifier q) ...@@ -832,6 +833,7 @@ inline const char *getQualifierString(TQualifier q)
case EvqWriteOnly: return "writeonly"; case EvqWriteOnly: return "writeonly";
case EvqGeometryIn: return "in"; case EvqGeometryIn: return "in";
case EvqGeometryOut: return "out"; case EvqGeometryOut: return "out";
case EvqPerVertexIn: return "gl_in";
default: UNREACHABLE(); return "unknown qualifier"; default: UNREACHABLE(); return "unknown qualifier";
} }
// clang-format on // clang-format on
......
...@@ -26,6 +26,7 @@ void CollectVariables(TIntermBlock *root, ...@@ -26,6 +26,7 @@ void CollectVariables(TIntermBlock *root,
std::vector<Varying> *outputVaryings, std::vector<Varying> *outputVaryings,
std::vector<InterfaceBlock> *uniformBlocks, std::vector<InterfaceBlock> *uniformBlocks,
std::vector<InterfaceBlock> *shaderStorageBlocks, std::vector<InterfaceBlock> *shaderStorageBlocks,
std::vector<InterfaceBlock> *inBlocks,
ShHashFunction64 hashFunction, ShHashFunction64 hashFunction,
TSymbolTable *symbolTable, TSymbolTable *symbolTable,
int shaderVersion, int shaderVersion,
......
...@@ -475,8 +475,8 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[], ...@@ -475,8 +475,8 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
{ {
ASSERT(!variablesCollected); ASSERT(!variablesCollected);
CollectVariables(root, &attributes, &outputVariables, &uniforms, &inputVaryings, CollectVariables(root, &attributes, &outputVariables, &uniforms, &inputVaryings,
&outputVaryings, &uniformBlocks, &shaderStorageBlocks, hashFunction, &outputVaryings, &uniformBlocks, &shaderStorageBlocks, &inBlocks,
&symbolTable, shaderVersion, extensionBehavior); hashFunction, &symbolTable, shaderVersion, extensionBehavior);
collectInterfaceBlocks(); collectInterfaceBlocks();
variablesCollected = true; variablesCollected = true;
if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS) if (compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS)
...@@ -751,10 +751,11 @@ void TCompiler::setResourceString() ...@@ -751,10 +751,11 @@ void TCompiler::setResourceString()
void TCompiler::collectInterfaceBlocks() void TCompiler::collectInterfaceBlocks()
{ {
ASSERT(interfaceBlocks.empty()); ASSERT(interfaceBlocks.empty());
interfaceBlocks.reserve(uniformBlocks.size() + shaderStorageBlocks.size()); interfaceBlocks.reserve(uniformBlocks.size() + shaderStorageBlocks.size() + inBlocks.size());
interfaceBlocks.insert(interfaceBlocks.end(), uniformBlocks.begin(), uniformBlocks.end()); interfaceBlocks.insert(interfaceBlocks.end(), uniformBlocks.begin(), uniformBlocks.end());
interfaceBlocks.insert(interfaceBlocks.end(), shaderStorageBlocks.begin(), interfaceBlocks.insert(interfaceBlocks.end(), shaderStorageBlocks.begin(),
shaderStorageBlocks.end()); shaderStorageBlocks.end());
interfaceBlocks.insert(interfaceBlocks.end(), inBlocks.begin(), inBlocks.end());
} }
void TCompiler::clearResults() void TCompiler::clearResults()
...@@ -773,6 +774,7 @@ void TCompiler::clearResults() ...@@ -773,6 +774,7 @@ void TCompiler::clearResults()
interfaceBlocks.clear(); interfaceBlocks.clear();
uniformBlocks.clear(); uniformBlocks.clear();
shaderStorageBlocks.clear(); shaderStorageBlocks.clear();
inBlocks.clear();
variablesCollected = false; variablesCollected = false;
mGLPositionInitialized = false; mGLPositionInitialized = false;
......
...@@ -119,6 +119,7 @@ class TCompiler : public TShHandleBase ...@@ -119,6 +119,7 @@ class TCompiler : public TShHandleBase
{ {
return shaderStorageBlocks; return shaderStorageBlocks;
} }
const std::vector<sh::InterfaceBlock> &getInBlocks() const { return inBlocks; }
ShHashFunction64 getHashFunction() const { return hashFunction; } ShHashFunction64 getHashFunction() const { return hashFunction; }
NameMap &getNameMap() { return nameMap; } NameMap &getNameMap() { return nameMap; }
...@@ -196,6 +197,7 @@ class TCompiler : public TShHandleBase ...@@ -196,6 +197,7 @@ class TCompiler : public TShHandleBase
std::vector<sh::InterfaceBlock> interfaceBlocks; std::vector<sh::InterfaceBlock> interfaceBlocks;
std::vector<sh::InterfaceBlock> uniformBlocks; std::vector<sh::InterfaceBlock> uniformBlocks;
std::vector<sh::InterfaceBlock> shaderStorageBlocks; std::vector<sh::InterfaceBlock> shaderStorageBlocks;
std::vector<sh::InterfaceBlock> inBlocks;
private: private:
// Creates the function call DAG for further analysis, returning false if there is a recursion // Creates the function call DAG for further analysis, returning false if there is a recursion
......
...@@ -908,9 +908,32 @@ void IdentifyBuiltIns(sh::GLenum type, ...@@ -908,9 +908,32 @@ void IdentifyBuiltIns(sh::GLenum type,
} }
case GL_GEOMETRY_SHADER_OES: case GL_GEOMETRY_SHADER_OES:
// TODO(jiawei.shao@intel.com): add Geometry Shader built-in variables. {
break; // TODO(jiawei.shao@intel.com): add all Geometry Shader built-in variables.
const char *extension = "GL_OES_geometry_shader";
// Add built-in interface block gl_PerVertex and the built-in array gl_in.
// TODO(jiawei.shao@intel.com): implement GL_OES_geometry_point_size.
const TString *glPerVertexString = NewPoolTString("gl_PerVertex");
symbolTable.insertInterfaceBlockNameExt(ESSL3_1_BUILTINS, extension, glPerVertexString);
TFieldList *fieldList = NewPoolTFieldList();
TSourceLoc zeroSourceLoc = {0, 0, 0, 0};
TField *glPositionField = new TField(new TType(EbtFloat, EbpHigh, EvqPosition, 4),
NewPoolTString("gl_Position"), zeroSourceLoc);
fieldList->push_back(glPositionField);
TInterfaceBlock *glInBlock = new TInterfaceBlock(
glPerVertexString, fieldList, NewPoolTString("gl_in"), TLayoutQualifier::create());
// The array size of gl_in is undefined until we get a valid input primitive
// declaration.
TType glInType(glInBlock, EvqPerVertexIn, TLayoutQualifier::create(), 0);
glInType.setArrayUnsized();
symbolTable.insertVariableExt(ESSL3_1_BUILTINS, extension, "gl_in", glInType);
break;
}
default: default:
UNREACHABLE(); UNREACHABLE();
} }
......
...@@ -622,7 +622,8 @@ bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary *node) ...@@ -622,7 +622,8 @@ bool TOutputGLSLBase::visitBinary(Visit visit, TIntermBinary *node)
const TField *field = interfaceBlock->fields()[index->getIConst(0)]; const TField *field = interfaceBlock->fields()[index->getIConst(0)];
TString fieldName = field->name(); TString fieldName = field->name();
ASSERT(!mSymbolTable->findBuiltIn(interfaceBlock->name(), mShaderVersion)); ASSERT(!mSymbolTable->findBuiltIn(interfaceBlock->name(), mShaderVersion) ||
interfaceBlock->name() == "gl_PerVertex");
fieldName = hashName(TName(fieldName)); fieldName = hashName(TName(fieldName));
out << fieldName; out << fieldName;
......
...@@ -95,6 +95,27 @@ bool CanSetDefaultPrecisionOnType(const TPublicType &type) ...@@ -95,6 +95,27 @@ bool CanSetDefaultPrecisionOnType(const TPublicType &type)
return true; return true;
} }
// Map input primitive types to input array sizes in a geometry shader.
GLuint GetGeometryShaderInputArraySize(TLayoutPrimitiveType primitiveType)
{
switch (primitiveType)
{
case EptPoints:
return 1u;
case EptLines:
return 2u;
case EptTriangles:
return 3u;
case EptLinesAdjacency:
return 4u;
case EptTrianglesAdjacency:
return 6u;
default:
UNREACHABLE();
return 0u;
}
}
} // namespace } // namespace
// This tracks each binding point's current default offset for inheritance of subsequent // This tracks each binding point's current default offset for inheritance of subsequent
...@@ -184,7 +205,8 @@ TParseContext::TParseContext(TSymbolTable &symt, ...@@ -184,7 +205,8 @@ TParseContext::TParseContext(TSymbolTable &symt,
mGeometryShaderInvocations(0), mGeometryShaderInvocations(0),
mGeometryShaderMaxVertices(-1), mGeometryShaderMaxVertices(-1),
mMaxGeometryShaderInvocations(resources.MaxGeometryShaderInvocations), mMaxGeometryShaderInvocations(resources.MaxGeometryShaderInvocations),
mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices) mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices),
mGeometryShaderInputArraySize(0)
{ {
mComputeShaderLocalSize.fill(-1); mComputeShaderLocalSize.fill(-1);
} }
...@@ -496,6 +518,9 @@ bool TParseContext::checkCanBeLValue(const TSourceLoc &line, const char *op, TIn ...@@ -496,6 +518,9 @@ bool TParseContext::checkCanBeLValue(const TSourceLoc &line, const char *op, TIn
case EvqComputeIn: case EvqComputeIn:
message = "can't modify work group size variable"; message = "can't modify work group size variable";
break; break;
case EvqPerVertexIn:
message = "can't modify any member in gl_in";
break;
default: default:
// //
// Type that can't be written to? // Type that can't be written to?
...@@ -1748,6 +1773,13 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location, ...@@ -1748,6 +1773,13 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location,
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 (variable->getType().getQualifier() == EvqPerVertexIn)
{
TType type(variable->getType());
type.setArraySize(mGeometryShaderInputArraySize);
node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), type);
}
else else
{ {
node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variable->getType()); node = new TIntermSymbol(variable->getUniqueId(), variable->getName(), variable->getType());
...@@ -2683,6 +2715,15 @@ bool TParseContext::checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier ...@@ -2683,6 +2715,15 @@ bool TParseContext::checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier
} }
} }
void TParseContext::setGeometryShaderInputArraySizes()
{
// TODO(jiawei.shao@intel.com): check former input array sizes match the input primitive
// declaration.
ASSERT(mGeometryShaderInputArraySize == 0);
mGeometryShaderInputArraySize =
GetGeometryShaderInputArraySize(mGeometryShaderInputPrimitiveType);
}
bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier) bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier)
{ {
ASSERT(typeQualifier.qualifier == EvqGeometryIn); ASSERT(typeQualifier.qualifier == EvqGeometryIn);
...@@ -2708,6 +2749,7 @@ bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier ...@@ -2708,6 +2749,7 @@ bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier
if (mGeometryShaderInputPrimitiveType == EptUndefined) if (mGeometryShaderInputPrimitiveType == EptUndefined)
{ {
mGeometryShaderInputPrimitiveType = layoutQualifier.primitiveType; mGeometryShaderInputPrimitiveType = layoutQualifier.primitiveType;
setGeometryShaderInputArraySizes();
} }
else if (mGeometryShaderInputPrimitiveType != layoutQualifier.primitiveType) else if (mGeometryShaderInputPrimitiveType != layoutQualifier.primitiveType)
{ {
...@@ -3337,6 +3379,7 @@ TIntermTyped *TParseContext::addConstructor(TIntermSequence *arguments, ...@@ -3337,6 +3379,7 @@ TIntermTyped *TParseContext::addConstructor(TIntermSequence *arguments,
// //
// Interface/uniform blocks // Interface/uniform blocks
// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
// //
TIntermDeclaration *TParseContext::addInterfaceBlock( TIntermDeclaration *TParseContext::addInterfaceBlock(
const TTypeQualifierBuilder &typeQualifierBuilder, const TTypeQualifierBuilder &typeQualifierBuilder,
...@@ -3641,6 +3684,16 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, ...@@ -3641,6 +3684,16 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
return CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst)); return CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
} }
if (baseExpression->getQualifier() == EvqPerVertexIn)
{
ASSERT(mShaderType == GL_GEOMETRY_SHADER_OES);
if (mGeometryShaderInputPrimitiveType == EptUndefined)
{
error(location, "missing input primitive declaration before indexing gl_in.", "[");
return CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
}
}
TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion(); TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion();
// TODO(oetuaho@nvidia.com): Get rid of indexConstantUnion == nullptr below once ANGLE is able // TODO(oetuaho@nvidia.com): Get rid of indexConstantUnion == nullptr below once ANGLE is able
...@@ -3651,9 +3704,21 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, ...@@ -3651,9 +3704,21 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
{ {
if (baseExpression->isInterfaceBlock()) if (baseExpression->isInterfaceBlock())
{ {
error(location, // TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
"array indexes for interface blocks arrays must be constant integral expressions", switch (baseExpression->getQualifier())
"["); {
case EvqPerVertexIn:
break;
case EvqUniform:
case EvqBuffer:
error(location,
"array indexes for uniform block arrays and shader storage block arrays "
"must be constant integral expressions",
"[");
break;
default:
UNREACHABLE();
}
} }
else if (baseExpression->getQualifier() == EvqFragmentOut) else if (baseExpression->getQualifier() == EvqFragmentOut)
{ {
...@@ -5326,6 +5391,11 @@ TIntermTyped *TParseContext::addMethod(TFunction *fnCall, ...@@ -5326,6 +5391,11 @@ TIntermTyped *TParseContext::addMethod(TFunction *fnCall,
{ {
error(loc, "length can only be called on arrays", "length"); error(loc, "length can only be called on arrays", "length");
} }
else if (typedThis->getQualifier() == EvqPerVertexIn &&
mGeometryShaderInputPrimitiveType == EptUndefined)
{
error(loc, "missing input primitive declaration before calling length on gl_in", "length");
}
else else
{ {
arraySize = typedThis->getArraySize(); arraySize = typedThis->getArraySize();
......
...@@ -535,6 +535,7 @@ class TParseContext : angle::NonCopyable ...@@ -535,6 +535,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();
// 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
...@@ -600,6 +601,8 @@ class TParseContext : angle::NonCopyable ...@@ -600,6 +601,8 @@ 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.
}; };
int PaParseStrings(size_t count, int PaParseStrings(size_t count,
......
...@@ -299,6 +299,18 @@ TInterfaceBlockName *TSymbolTable::declareInterfaceBlockName(const TString *name ...@@ -299,6 +299,18 @@ TInterfaceBlockName *TSymbolTable::declareInterfaceBlockName(const TString *name
return nullptr; return nullptr;
} }
TInterfaceBlockName *TSymbolTable::insertInterfaceBlockNameExt(ESymbolLevel level,
const char *ext,
const TString *name)
{
TInterfaceBlockName *blockNameSymbol = new TInterfaceBlockName(this, name);
if (insert(level, ext, blockNameSymbol))
{
return blockNameSymbol;
}
return nullptr;
}
TVariable *TSymbolTable::insertVariable(ESymbolLevel level, const char *name, const TType &type) TVariable *TSymbolTable::insertVariable(ESymbolLevel level, const char *name, const TType &type)
{ {
return insertVariable(level, NewPoolTString(name), type); return insertVariable(level, NewPoolTString(name), type);
......
...@@ -347,6 +347,9 @@ class TSymbolTable : angle::NonCopyable ...@@ -347,6 +347,9 @@ class TSymbolTable : angle::NonCopyable
const char *name, const char *name,
const TType &type); const TType &type);
TVariable *insertStructType(ESymbolLevel level, TStructure *str); TVariable *insertStructType(ESymbolLevel level, TStructure *str);
TInterfaceBlockName *insertInterfaceBlockNameExt(ESymbolLevel level,
const char *ext,
const TString *name);
bool insertConstInt(ESymbolLevel level, const char *name, int value, TPrecision precision) bool insertConstInt(ESymbolLevel level, const char *name, int value, TPrecision precision)
{ {
......
...@@ -253,7 +253,7 @@ class TType ...@@ -253,7 +253,7 @@ class TType
TType(TInterfaceBlock *interfaceBlockIn, TType(TInterfaceBlock *interfaceBlockIn,
TQualifier qualifierIn, TQualifier qualifierIn,
TLayoutQualifier layoutQualifierIn, TLayoutQualifier layoutQualifierIn,
int arraySizeIn) unsigned int arraySizeIn)
: type(EbtInterfaceBlock), : type(EbtInterfaceBlock),
precision(EbpUndefined), precision(EbpUndefined),
qualifier(qualifierIn), qualifier(qualifierIn),
...@@ -349,6 +349,7 @@ class TType ...@@ -349,6 +349,7 @@ class TType
invalidateMangledName(); invalidateMangledName();
} }
} }
void setArrayUnsized() { setArraySize(0u); }
void clearArrayness() void clearArrayness()
{ {
if (array) if (array)
......
...@@ -131,6 +131,43 @@ class CollectFragmentVariablesTest : public CollectVariablesTest ...@@ -131,6 +131,43 @@ class CollectFragmentVariablesTest : public CollectVariablesTest
CollectFragmentVariablesTest() : CollectVariablesTest(GL_FRAGMENT_SHADER) {} CollectFragmentVariablesTest() : CollectVariablesTest(GL_FRAGMENT_SHADER) {}
}; };
class CollectGeometryVariablesTest : public CollectVariablesTest
{
public:
CollectGeometryVariablesTest() : CollectVariablesTest(GL_GEOMETRY_SHADER_OES) {}
protected:
void SetUp() override
{
ShBuiltInResources resources;
InitBuiltInResources(&resources);
resources.OES_geometry_shader = 1;
initTranslator(resources);
}
void initTranslator(const ShBuiltInResources &resources)
{
mTranslator.reset(
new TranslatorGLSL(mShaderType, SH_GLES3_1_SPEC, SH_GLSL_COMPATIBILITY_OUTPUT));
ASSERT_TRUE(mTranslator->Init(resources));
}
void compileGeometryShaderWithInputPrimitive(const std::string &inputPrimitive)
{
std::ostringstream sstream;
sstream << "#version 310 es\n"
<< "#extension GL_OES_geometry_shader : require\n"
<< "layout (" << inputPrimitive << ") in;\n"
<< "layout (points, max_vertices = 2) out;\n"
<< "void main()\n"
<< "{\n"
<< " vec4 value = gl_in[0].gl_Position;\n"
<< "}\n";
compile(sstream.str());
}
};
TEST_F(CollectFragmentVariablesTest, SimpleOutputVar) TEST_F(CollectFragmentVariablesTest, SimpleOutputVar)
{ {
const std::string &shaderString = const std::string &shaderString =
...@@ -830,3 +867,63 @@ TEST_F(CollectVertexVariablesTest, ViewID_OVR) ...@@ -830,3 +867,63 @@ TEST_F(CollectVertexVariablesTest, ViewID_OVR)
const Varying *varying = &varyings[0]; const Varying *varying = &varyings[0];
EXPECT_EQ("gl_Position", varying->name); EXPECT_EQ("gl_Position", varying->name);
} }
// Test all the fields of gl_in can be collected correctly in a geometry shader.
TEST_F(CollectGeometryVariablesTest, CollectGLInFields)
{
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"
"void main()\n"
"{\n"
" vec4 value = gl_in[0].gl_Position;\n"
" vec4 value2 = gl_in[0].gl_Position;\n"
"}\n";
compile(shaderString);
EXPECT_TRUE(mTranslator->getOutputVaryings().empty());
EXPECT_TRUE(mTranslator->getInputVaryings().empty());
const auto &inBlocks = mTranslator->getInBlocks();
ASSERT_EQ(1u, inBlocks.size());
const InterfaceBlock *inBlock = &inBlocks[0];
EXPECT_EQ("gl_PerVertex", inBlock->name);
EXPECT_EQ("gl_in", inBlock->instanceName);
EXPECT_TRUE(inBlock->staticUse);
EXPECT_TRUE(inBlock->isBuiltIn());
ASSERT_EQ(1u, inBlock->fields.size());
const ShaderVariable &glPositionField = inBlock->fields[0];
EXPECT_EQ("gl_Position", glPositionField.name);
EXPECT_FALSE(glPositionField.isArray());
EXPECT_FALSE(glPositionField.isStruct());
EXPECT_TRUE(glPositionField.staticUse);
EXPECT_TRUE(glPositionField.isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, glPositionField.precision);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, glPositionField.type);
}
// Test the collected array size of gl_in matches the input primitive declaration.
TEST_F(CollectGeometryVariablesTest, GLInArraySize)
{
const std::array<std::string, 5> kInputPrimitives = {
{"points", "lines", "lines_adjacency", "triangles", "triangles_adjacency"}};
constexpr GLuint kArraySizeForInputPrimitives[] = {1u, 2u, 4u, 3u, 6u};
for (size_t i = 0; i < kInputPrimitives.size(); ++i)
{
compileGeometryShaderWithInputPrimitive(kInputPrimitives[i]);
const auto &inBlocks = mTranslator->getInBlocks();
ASSERT_EQ(1u, inBlocks.size());
const InterfaceBlock *inBlock = &inBlocks[0];
ASSERT_EQ("gl_in", inBlock->instanceName);
EXPECT_EQ(kArraySizeForInputPrimitives[i], inBlock->arraySize);
}
}
...@@ -869,3 +869,120 @@ TEST_F(GeometryShaderTest, invalidLayoutQualifiers) ...@@ -869,3 +869,120 @@ TEST_F(GeometryShaderTest, invalidLayoutQualifiers)
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog; FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
} }
} }
// Verify that indexing an array with a constant integer on gl_in is legal.
TEST_F(GeometryShaderTest, IndexGLInByConstantInteger)
{
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"
"void main()\n"
"{\n"
" vec4 position;\n"
" position = gl_in[0].gl_Position;\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Verify that indexing an array with an integer variable on gl_in is legal.
TEST_F(GeometryShaderTest, IndexGLInByVariable)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (lines) in;\n"
"layout (points, max_vertices = 2) out;\n"
"void main()\n"
"{\n"
" vec4 position;\n"
" for (int i = 0; i < 2; i++)\n"
" {\n"
" position = gl_in[i].gl_Position;\n"
" }\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Verify that indexing an array on gl_in without input primitive declaration causes a compile
// error.
TEST_F(GeometryShaderTest, IndexGLInWithoutInputPrimitive)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, max_vertices = 2) out;\n"
"void main()\n"
"{\n"
" vec4 position = gl_in[0].gl_Position;\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Verify that using gl_in.length() without input primitive declaration causes a compile error.
TEST_F(GeometryShaderTest, UseGLInLengthWithoutInputPrimitive)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, max_vertices = 2) out;\n"
"void main()\n"
"{\n"
" int length = gl_in.length();\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Verify that using gl_in.length() with input primitive declaration can compile.
TEST_F(GeometryShaderTest, UseGLInLengthWithInputPrimitive)
{
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"
"void main()\n"
"{\n"
" int length = gl_in.length();\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Verify that gl_in[].gl_Position cannot be l-value.
TEST_F(GeometryShaderTest, AssignValueToGLIn)
{
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"
"void main()\n"
"{\n"
" gl_in[0].gl_Position = vec4(0, 0, 0, 1);\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