Commit b5cc1198 by Shao Committed by Commit Bot

ES31: Add Geometry Shader layout qualifiers in GLSL compiler

This patch intends to implement Geometry Shader layout qualifiers required in OpenGL ES 3.1 extension GL_OES_geometry_shader in ANGLE GLSL compiler. 1. Add support to the shader type GL_GEOMETRY_SHADER_OES. 2. Implement Geometry Shader layout qualifiers in the GLSL compiler: (1) Add support to OpenGL ES 3.1 extension "GL_OES_geometry_shader". (2) Add validations of the input and output primitive declarations in the Geometry Shader layout declarations. (3) Add 'invocations' and 'max_vertices' support in the Geometry Shader layout declarations 3. Add unit tests to cover all the new features added in this patch. BUG=angleproject:1941 TEST=angle_unittests Change-Id: Ie693e11f8a00dab3552626ed63e9336c7fbd3cb8 Reviewed-on: https://chromium-review.googlesource.com/560647 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent 4c19a8a8
......@@ -25,7 +25,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 178
#define ANGLE_SH_VERSION 179
enum ShShaderSpec
{
......@@ -291,6 +291,7 @@ struct ShBuiltInResources
int ARM_shader_framebuffer_fetch;
int OVR_multiview;
int EXT_YUV_target;
int OES_geometry_shader;
// Set to 1 to enable replacing GL_EXT_draw_buffers #extension directives
// with GL_NV_draw_buffers in ESSL output. This flag can be used to emulate
......@@ -411,6 +412,12 @@ struct ShBuiltInResources
// maximum point size (higher limit from ALIASED_POINT_SIZE_RANGE)
float MaxPointSize;
// OES_geometry_shader constants
// TODO(jiawei.shao@intel.com): add complete geometry shader constants.
int MaxGeometryUniformComponents;
int MaxGeometryOutputVertices;
int MaxGeometryShaderInvocations;
};
//
......
......@@ -63,6 +63,7 @@ void GenerateResources(ShBuiltInResources *resources)
resources->OES_standard_derivatives = 0;
resources->OES_EGL_image_external = 0;
resources->OES_geometry_shader = 1;
}
int main(int argc, char *argv[])
......@@ -74,6 +75,7 @@ int main(int argc, char *argv[])
ShHandle vertexCompiler = 0;
ShHandle fragmentCompiler = 0;
ShHandle computeCompiler = 0;
ShHandle geometryCompiler = 0;
ShShaderSpec spec = SH_GLES2_SPEC;
ShShaderOutput output = SH_ESSL_OUTPUT;
......@@ -268,7 +270,14 @@ int main(int argc, char *argv[])
}
compiler = computeCompiler;
break;
case GL_GEOMETRY_SHADER_OES:
if (geometryCompiler == 0)
{
geometryCompiler =
sh::ConstructCompiler(GL_GEOMETRY_SHADER_OES, spec, output, &resources);
}
compiler = geometryCompiler;
break;
default: break;
}
if (compiler)
......@@ -307,7 +316,8 @@ int main(int argc, char *argv[])
}
}
if ((vertexCompiler == 0) && (fragmentCompiler == 0) && (computeCompiler == 0))
if ((vertexCompiler == 0) && (fragmentCompiler == 0) && (computeCompiler == 0) &&
(geometryCompiler == 0))
failCode = EFailUsage;
if (failCode == EFailUsage)
usage();
......@@ -318,6 +328,8 @@ int main(int argc, char *argv[])
sh::Destruct(fragmentCompiler);
if (computeCompiler)
sh::Destruct(computeCompiler);
if (geometryCompiler)
sh::Destruct(geometryCompiler);
sh::Finalize();
......@@ -389,6 +401,8 @@ sh::GLenum FindShaderType(const char *fileName)
return GL_VERTEX_SHADER;
if (strncmp(ext, ".comp", 5) == 0)
return GL_COMPUTE_SHADER;
if (strncmp(ext, ".geom", 5) == 0)
return GL_GEOMETRY_SHADER_OES;
}
return GL_FRAGMENT_SHADER;
......
......@@ -573,6 +573,10 @@ enum TQualifier
EvqRestrict,
EvqVolatile,
// GLSL ES 3.1 extension OES_geometry_shader qualifiers
EvqGeometryIn,
EvqGeometryOut,
// end of list
EvqLast
};
......@@ -623,6 +627,18 @@ enum TYuvCscStandardEXT
EycsItu709
};
enum TLayoutPrimitiveType
{
EptUndefined,
EptPoints,
EptLines,
EptLinesAdjacency,
EptTriangles,
EptTrianglesAdjacency,
EptLineStrip,
EptTriangleStrip
};
struct TLayoutQualifier
{
int location;
......@@ -645,6 +661,11 @@ struct TLayoutQualifier
// EXT_YUV_target yuv layout qualifier.
bool yuv;
// OES_geometry_shader layout qualifiers.
TLayoutPrimitiveType primitiveType;
int invocations;
int maxVertices;
static TLayoutQualifier create()
{
TLayoutQualifier layoutQualifier;
......@@ -661,6 +682,11 @@ struct TLayoutQualifier
layoutQualifier.yuv = false;
layoutQualifier.imageInternalFormat = EiifUnspecified;
layoutQualifier.primitiveType = EptUndefined;
layoutQualifier.invocations = 0;
layoutQualifier.maxVertices = -1;
return layoutQualifier;
}
......@@ -668,13 +694,16 @@ struct TLayoutQualifier
{
return location == -1 && binding == -1 && offset == -1 && numViews == -1 && yuv == false &&
matrixPacking == EmpUnspecified && blockStorage == EbsUnspecified &&
!localSize.isAnyValueSet() && imageInternalFormat == EiifUnspecified;
!localSize.isAnyValueSet() && imageInternalFormat == EiifUnspecified &&
primitiveType == EptUndefined && invocations == 0 && maxVertices == -1;
}
bool isCombinationValid() const
{
bool workSizeSpecified = localSize.isAnyValueSet();
bool numViewsSet = (numViews != -1);
bool geometryShaderSpecified =
(primitiveType != EptUndefined) || (invocations != 0) || (maxVertices != -1);
bool otherLayoutQualifiersSpecified =
(location != -1 || binding != -1 || matrixPacking != EmpUnspecified ||
blockStorage != EbsUnspecified || imageInternalFormat != EiifUnspecified);
......@@ -682,7 +711,7 @@ struct TLayoutQualifier
// we can have either the work group size specified, or number of views,
// or yuv layout qualifier, or the other layout qualifiers.
return (workSizeSpecified ? 1 : 0) + (numViewsSet ? 1 : 0) + (yuv ? 1 : 0) +
(otherLayoutQualifiersSpecified ? 1 : 0) <=
(otherLayoutQualifiersSpecified ? 1 : 0) + (geometryShaderSpecified ? 1 : 0) <=
1;
}
......@@ -799,6 +828,8 @@ inline const char *getQualifierString(TQualifier q)
case EvqLocalInvocationIndex: return "LocalInvocationIndex";
case EvqReadOnly: return "readonly";
case EvqWriteOnly: return "writeonly";
case EvqGeometryIn: return "in";
case EvqGeometryOut: return "out";
default: UNREACHABLE(); return "unknown qualifier";
}
// clang-format on
......@@ -901,6 +932,30 @@ inline const char *getYuvCscStandardEXTString(TYuvCscStandardEXT ycsq)
}
}
inline const char *getGeometryShaderPrimitiveTypeString(TLayoutPrimitiveType primitiveType)
{
switch (primitiveType)
{
case EptPoints:
return "points";
case EptLines:
return "lines";
case EptTriangles:
return "triangles";
case EptLinesAdjacency:
return "lines_adjacency";
case EptTrianglesAdjacency:
return "triangles_adjacency";
case EptLineStrip:
return "line_strip";
case EptTriangleStrip:
return "triangle_strip";
default:
UNREACHABLE();
return "unknown geometry shader primitive type";
}
}
} // namespace sh
#endif // COMPILER_TRANSLATOR_BASETYPES_H_
......@@ -146,11 +146,14 @@ int GetMaxUniformVectorsForShaderType(GLenum shaderType, const ShBuiltInResource
return resources.MaxVertexUniformVectors;
case GL_FRAGMENT_SHADER:
return resources.MaxFragmentUniformVectors;
// TODO (jiawei.shao@intel.com): check if we need finer-grained component counting
case GL_COMPUTE_SHADER:
// TODO (jiawei.shao@intel.com): check if we need finer-grained component counting
return resources.MaxComputeUniformComponents / 4;
case GL_GEOMETRY_SHADER_OES:
return resources.MaxGeometryUniformComponents / 4;
default:
UNIMPLEMENTED();
UNREACHABLE();
return -1;
}
}
......@@ -242,7 +245,11 @@ TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
builtInFunctionEmulator(),
mDiagnostics(infoSink.info),
mSourcePath(nullptr),
mComputeShaderLocalSizeDeclared(false)
mComputeShaderLocalSizeDeclared(false),
mGeometryShaderMaxVertices(-1),
mGeometryShaderInvocations(0),
mGeometryShaderInputPrimitiveType(EptUndefined),
mGeometryShaderOutputPrimitiveType(EptUndefined)
{
mComputeShaderLocalSize.fill(1);
}
......@@ -360,6 +367,15 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
success = false;
}
if (success && shaderType == GL_GEOMETRY_SHADER_OES)
{
mGeometryShaderInputPrimitiveType = parseContext.getGeometryShaderInputPrimitiveType();
mGeometryShaderOutputPrimitiveType =
parseContext.getGeometryShaderOutputPrimitiveType();
mGeometryShaderMaxVertices = parseContext.getGeometryShaderMaxVertices();
mGeometryShaderInvocations = parseContext.getGeometryShaderInvocations();
}
// Disallow expressions deemed too complex.
if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY))
success = limitExpressionComplexity(root);
......@@ -626,15 +642,13 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources)
symbolTable.setDefaultPrecision(EbtInt, EbpMedium);
break;
case GL_VERTEX_SHADER:
symbolTable.setDefaultPrecision(EbtInt, EbpHigh);
symbolTable.setDefaultPrecision(EbtFloat, EbpHigh);
break;
case GL_COMPUTE_SHADER:
case GL_GEOMETRY_SHADER_OES:
symbolTable.setDefaultPrecision(EbtInt, EbpHigh);
symbolTable.setDefaultPrecision(EbtFloat, EbpHigh);
break;
default:
assert(false && "Language not supported");
UNREACHABLE();
}
// Set defaults for sampler types that have default precision, even those that are
// only available if an extension exists.
......@@ -694,6 +708,7 @@ void TCompiler::setResourceString()
<< ":ARM_shader_framebuffer_fetch:" << compileResources.ARM_shader_framebuffer_fetch
<< ":OVR_multiview:" << compileResources.OVR_multiview
<< ":EXT_YUV_target:" << compileResources.EXT_YUV_target
<< ":OES_geometry_shader:" << compileResources.OES_geometry_shader
<< ":MaxVertexOutputVectors:" << compileResources.MaxVertexOutputVectors
<< ":MaxFragmentInputVectors:" << compileResources.MaxFragmentInputVectors
<< ":MinProgramTexelOffset:" << compileResources.MinProgramTexelOffset
......@@ -725,7 +740,10 @@ void TCompiler::setResourceString()
<< ":MaxVertexAtomicCounterBuffers:" << compileResources.MaxVertexAtomicCounterBuffers
<< ":MaxFragmentAtomicCounterBuffers:" << compileResources.MaxFragmentAtomicCounterBuffers
<< ":MaxCombinedAtomicCounterBuffers:" << compileResources.MaxCombinedAtomicCounterBuffers
<< ":MaxAtomicCounterBufferSize:" << compileResources.MaxAtomicCounterBufferSize;
<< ":MaxAtomicCounterBufferSize:" << compileResources.MaxAtomicCounterBufferSize
<< ":MaxGeometryOutputVertices:" << compileResources.MaxGeometryOutputVertices
<< ":MaxGeometryUniformComponents:" << compileResources.MaxGeometryUniformComponents
<< ":MaxGeometryShaderInvocations:" << compileResources.MaxGeometryShaderInvocations;
// clang-format on
builtInResourcesString = strstream.str();
......@@ -749,6 +767,11 @@ void TCompiler::clearResults()
mNumViews = -1;
mGeometryShaderInputPrimitiveType = EptUndefined;
mGeometryShaderOutputPrimitiveType = EptUndefined;
mGeometryShaderInvocations = 0;
mGeometryShaderMaxVertices = -1;
builtInFunctionEmulator.cleanup();
nameMap.clear();
......
......@@ -125,8 +125,20 @@ class TCompiler : public TShHandleBase
// Get the resources set by InitBuiltInSymbolTable
const ShBuiltInResources &getResources() const;
protected:
int getGeometryShaderMaxVertices() const { return mGeometryShaderMaxVertices; }
int getGeometryShaderInvocations() const { return mGeometryShaderInvocations; }
TLayoutPrimitiveType getGeometryShaderInputPrimitiveType() const
{
return mGeometryShaderInputPrimitiveType;
}
TLayoutPrimitiveType getGeometryShaderOutputPrimitiveType() const
{
return mGeometryShaderOutputPrimitiveType;
}
sh::GLenum getShaderType() const { return shaderType; }
protected:
// Initialize symbol-table with built-in symbols.
bool InitBuiltInSymbolTable(const ShBuiltInResources &resources);
// Compute the string representation of the built-in resources
......@@ -241,6 +253,12 @@ class TCompiler : public TShHandleBase
// GL_OVR_multiview num_views.
int mNumViews;
// geometry shader parameters.
int mGeometryShaderMaxVertices;
int mGeometryShaderInvocations;
TLayoutPrimitiveType mGeometryShaderInputPrimitiveType;
TLayoutPrimitiveType mGeometryShaderOutputPrimitiveType;
// name hashing.
ShHashFunction64 hashFunction;
NameMap nameMap;
......
......@@ -902,6 +902,10 @@ void IdentifyBuiltIns(sh::GLenum type,
}
break;
case GL_GEOMETRY_SHADER_OES:
// TODO(jiawei.shao@intel.com): add Geometry Shader built-in variables.
break;
default:
assert(false && "Language not supported");
}
......@@ -942,6 +946,10 @@ void InitExtensionBehavior(const ShBuiltInResources &resources, TExtensionBehavi
{
extBehavior["GL_EXT_YUV_target"] = EBhUndefined;
}
if (resources.OES_geometry_shader)
{
extBehavior["GL_OES_geometry_shader"] = EBhUndefined;
}
}
void ResetExtensionBehavior(TExtensionBehavior &extBehavior)
......
......@@ -6,6 +6,7 @@
#include "compiler/translator/OutputGLSLBase.h"
#include "angle_gl.h"
#include "common/debug.h"
#include "common/mathutil.h"
#include "compiler/translator/Compiler.h"
......@@ -1293,4 +1294,52 @@ void TOutputGLSLBase::declareInterfaceBlock(const TInterfaceBlock *interfaceBloc
out << "}";
}
void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out,
sh::TLayoutPrimitiveType inputPrimitive,
int invocations,
sh::TLayoutPrimitiveType outputPrimitive,
int maxVertices)
{
// Omit 'invocations = 1'
if (inputPrimitive != EptUndefined || invocations > 1)
{
out << "layout (";
if (inputPrimitive != EptUndefined)
{
out << getGeometryShaderPrimitiveTypeString(inputPrimitive);
}
if (invocations > 1)
{
if (inputPrimitive != EptUndefined)
{
out << ", ";
}
out << "invocations = " << invocations;
}
out << ") in;\n";
}
if (outputPrimitive != EptUndefined || maxVertices != -1)
{
out << "layout (";
if (outputPrimitive != EptUndefined)
{
out << getGeometryShaderPrimitiveTypeString(outputPrimitive);
}
if (maxVertices != -1)
{
if (outputPrimitive != EptUndefined)
{
out << ", ";
}
out << "max_vertices = " << maxVertices;
}
out << ") out;\n";
}
}
} // namespace sh
......@@ -109,6 +109,12 @@ class TOutputGLSLBase : public TIntermTraverser
ShCompileOptions mCompileOptions;
};
void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out,
sh::TLayoutPrimitiveType inputPrimitive,
int invocations,
sh::TLayoutPrimitiveType outputPrimitive,
int maxVertices);
} // namespace sh
#endif // COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_
......@@ -178,7 +178,13 @@ TParseContext::TParseContext(TSymbolTable &symt,
mMaxUniformBufferBindings(resources.MaxUniformBufferBindings),
mMaxAtomicCounterBindings(resources.MaxAtomicCounterBindings),
mMaxShaderStorageBufferBindings(resources.MaxShaderStorageBufferBindings),
mDeclaringFunction(false)
mDeclaringFunction(false),
mGeometryShaderInputPrimitiveType(EptUndefined),
mGeometryShaderOutputPrimitiveType(EptUndefined),
mGeometryShaderInvocations(0),
mGeometryShaderMaxVertices(-1),
mMaxGeometryShaderInvocations(resources.MaxGeometryShaderInvocations),
mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices)
{
mComputeShaderLocalSize.fill(-1);
}
......@@ -2654,6 +2660,133 @@ void TParseContext::parseDefaultPrecisionQualifier(const TPrecision precision,
symbolTable.setDefaultPrecision(type.getBasicType(), precision);
}
bool TParseContext::checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier &typeQualifier)
{
switch (typeQualifier.layoutQualifier.primitiveType)
{
case EptLines:
case EptLinesAdjacency:
case EptTriangles:
case EptTrianglesAdjacency:
return typeQualifier.qualifier == EvqGeometryIn;
case EptLineStrip:
case EptTriangleStrip:
return typeQualifier.qualifier == EvqGeometryOut;
case EptPoints:
return true;
default:
UNREACHABLE();
return false;
}
}
bool TParseContext::parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier)
{
ASSERT(typeQualifier.qualifier == EvqGeometryIn);
const TLayoutQualifier &layoutQualifier = typeQualifier.layoutQualifier;
if (layoutQualifier.maxVertices != -1)
{
error(typeQualifier.line,
"max_vertices can only be declared in 'out' layout in a geometry shader", "layout");
return false;
}
// Set mGeometryInputPrimitiveType if exists
if (layoutQualifier.primitiveType != EptUndefined)
{
if (!checkPrimitiveTypeMatchesTypeQualifier(typeQualifier))
{
error(typeQualifier.line, "invalid primitive type for 'in' layout", "layout");
return false;
}
if (mGeometryShaderInputPrimitiveType == EptUndefined)
{
mGeometryShaderInputPrimitiveType = layoutQualifier.primitiveType;
}
else if (mGeometryShaderInputPrimitiveType != layoutQualifier.primitiveType)
{
error(typeQualifier.line, "primitive doesn't match earlier input primitive declaration",
"layout");
return false;
}
}
// Set mGeometryInvocations if exists
if (layoutQualifier.invocations > 0)
{
if (mGeometryShaderInvocations == 0)
{
mGeometryShaderInvocations = layoutQualifier.invocations;
}
else if (mGeometryShaderInvocations != layoutQualifier.invocations)
{
error(typeQualifier.line, "invocations contradicts to the earlier declaration",
"layout");
return false;
}
}
return true;
}
bool TParseContext::parseGeometryShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier)
{
ASSERT(typeQualifier.qualifier == EvqGeometryOut);
const TLayoutQualifier &layoutQualifier = typeQualifier.layoutQualifier;
if (layoutQualifier.invocations > 0)
{
error(typeQualifier.line,
"invocations can only be declared in 'in' layout in a geometry shader", "layout");
return false;
}
// Set mGeometryOutputPrimitiveType if exists
if (layoutQualifier.primitiveType != EptUndefined)
{
if (!checkPrimitiveTypeMatchesTypeQualifier(typeQualifier))
{
error(typeQualifier.line, "invalid primitive type for 'out' layout", "layout");
return false;
}
if (mGeometryShaderOutputPrimitiveType == EptUndefined)
{
mGeometryShaderOutputPrimitiveType = layoutQualifier.primitiveType;
}
else if (mGeometryShaderOutputPrimitiveType != layoutQualifier.primitiveType)
{
error(typeQualifier.line,
"primitive doesn't match earlier output primitive declaration", "layout");
return false;
}
}
// Set mGeometryMaxVertices if exists
if (layoutQualifier.maxVertices > -1)
{
if (mGeometryShaderMaxVertices == -1)
{
mGeometryShaderMaxVertices = layoutQualifier.maxVertices;
}
else if (mGeometryShaderMaxVertices != layoutQualifier.maxVertices)
{
error(typeQualifier.line, "max_vertices contradicts to the earlier declaration",
"layout");
return false;
}
}
return true;
}
void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder)
{
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(mDiagnostics);
......@@ -2735,6 +2868,33 @@ void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &type
mComputeShaderLocalSizeDeclared = true;
}
else if (typeQualifier.qualifier == EvqGeometryIn)
{
if (mShaderVersion < 310)
{
error(typeQualifier.line, "in type qualifier supported in GLSL ES 3.10 only", "layout");
return;
}
if (!parseGeometryShaderInputLayoutQualifier(typeQualifier))
{
return;
}
}
else if (typeQualifier.qualifier == EvqGeometryOut)
{
if (mShaderVersion < 310)
{
error(typeQualifier.line, "out type qualifier supported in GLSL ES 3.10 only",
"layout");
return;
}
if (!parseGeometryShaderOutputLayoutQualifier(typeQualifier))
{
return;
}
}
else if (isMultiviewExtensionEnabled() && typeQualifier.qualifier == EvqVertexIn)
{
// This error is only specified in WebGL, but tightens unspecified behavior in the native
......@@ -3833,6 +3993,48 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierTyp
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.imageInternalFormat = EiifR32UI;
}
else if (qualifierType == "points" && isExtensionEnabled("GL_OES_geometry_shader") &&
mShaderType == GL_GEOMETRY_SHADER_OES)
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptPoints;
}
else if (qualifierType == "lines" && isExtensionEnabled("GL_OES_geometry_shader") &&
mShaderType == GL_GEOMETRY_SHADER_OES)
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptLines;
}
else if (qualifierType == "lines_adjacency" && isExtensionEnabled("GL_OES_geometry_shader") &&
mShaderType == GL_GEOMETRY_SHADER_OES)
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptLinesAdjacency;
}
else if (qualifierType == "triangles" && isExtensionEnabled("GL_OES_geometry_shader") &&
mShaderType == GL_GEOMETRY_SHADER_OES)
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptTriangles;
}
else if (qualifierType == "triangles_adjacency" &&
isExtensionEnabled("GL_OES_geometry_shader") && mShaderType == GL_GEOMETRY_SHADER_OES)
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptTrianglesAdjacency;
}
else if (qualifierType == "line_strip" && isExtensionEnabled("GL_OES_geometry_shader") &&
mShaderType == GL_GEOMETRY_SHADER_OES)
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptLineStrip;
}
else if (qualifierType == "triangle_strip" && isExtensionEnabled("GL_OES_geometry_shader") &&
mShaderType == GL_GEOMETRY_SHADER_OES)
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptTriangleStrip;
}
else
{
......@@ -3875,6 +4077,46 @@ void TParseContext::parseNumViews(int intValue,
*numViews = intValue;
}
void TParseContext::parseInvocations(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *numInvocations)
{
// Although SPEC isn't clear whether invocations can be less than 1, we add this limit because
// it doesn't make sense to accept invocations <= 0.
if (intValue < 1 || intValue > mMaxGeometryShaderInvocations)
{
error(intValueLine,
"out of range: invocations must be in the range of [1, "
"MAX_GEOMETRY_SHADER_INVOCATIONS_OES]",
intValueString.c_str());
}
else
{
*numInvocations = intValue;
}
}
void TParseContext::parseMaxVertices(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *maxVertices)
{
// Although SPEC isn't clear whether max_vertices can be less than 0, we add this limit because
// it doesn't make sense to accept max_vertices < 0.
if (intValue < 0 || intValue > mMaxGeometryShaderMaxVertices)
{
error(
intValueLine,
"out of range: max_vertices must be in the range of [0, gl_MaxGeometryOutputVertices]",
intValueString.c_str());
}
else
{
*maxVertices = intValue;
}
}
TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
const TSourceLoc &qualifierTypeLine,
int intValue,
......@@ -3944,6 +4186,17 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierTyp
{
parseNumViews(intValue, intValueLine, intValueString, &qualifier.numViews);
}
else if (qualifierType == "invocations" && isExtensionEnabled("GL_OES_geometry_shader") &&
mShaderType == GL_GEOMETRY_SHADER_OES)
{
parseInvocations(intValue, intValueLine, intValueString, &qualifier.invocations);
}
else if (qualifierType == "max_vertices" && isExtensionEnabled("GL_OES_geometry_shader") &&
mShaderType == GL_GEOMETRY_SHADER_OES)
{
parseMaxVertices(intValue, intValueLine, intValueString, &qualifier.maxVertices);
}
else
{
error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str());
......@@ -3981,23 +4234,39 @@ TStorageQualifierWrapper *TParseContext::parseInQualifier(const TSourceLoc &loc)
{
return new TStorageQualifierWrapper(EvqIn, loc);
}
if (getShaderType() == GL_FRAGMENT_SHADER)
switch (getShaderType())
{
if (mShaderVersion < 300)
case GL_VERTEX_SHADER:
{
error(loc, "storage qualifier supported in GLSL ES 3.00 and above only", "in");
if (mShaderVersion < 300 && !isMultiviewExtensionEnabled())
{
error(loc, "storage qualifier supported in GLSL ES 3.00 and above only", "in");
}
return new TStorageQualifierWrapper(EvqVertexIn, loc);
}
return new TStorageQualifierWrapper(EvqFragmentIn, loc);
}
if (getShaderType() == GL_VERTEX_SHADER)
{
if (mShaderVersion < 300 && !isMultiviewExtensionEnabled())
case GL_FRAGMENT_SHADER:
{
if (mShaderVersion < 300)
{
error(loc, "storage qualifier supported in GLSL ES 3.00 and above only", "in");
}
return new TStorageQualifierWrapper(EvqFragmentIn, loc);
}
case GL_COMPUTE_SHADER:
{
error(loc, "storage qualifier supported in GLSL ES 3.00 and above only", "in");
return new TStorageQualifierWrapper(EvqComputeIn, loc);
}
case GL_GEOMETRY_SHADER_OES:
{
return new TStorageQualifierWrapper(EvqGeometryIn, loc);
}
default:
{
UNREACHABLE();
return new TStorageQualifierWrapper(EvqLast, loc);
}
return new TStorageQualifierWrapper(EvqVertexIn, loc);
}
return new TStorageQualifierWrapper(EvqComputeIn, loc);
}
TStorageQualifierWrapper *TParseContext::parseOutQualifier(const TSourceLoc &loc)
......@@ -4006,19 +4275,39 @@ TStorageQualifierWrapper *TParseContext::parseOutQualifier(const TSourceLoc &loc
{
return new TStorageQualifierWrapper(EvqOut, loc);
}
if (mShaderVersion < 300)
{
error(loc, "storage qualifier supported in GLSL ES 3.00 and above only", "out");
}
if (getShaderType() != GL_VERTEX_SHADER && getShaderType() != GL_FRAGMENT_SHADER)
{
error(loc, "storage qualifier supported in vertex and fragment shaders only", "out");
}
if (getShaderType() == GL_VERTEX_SHADER)
switch (getShaderType())
{
return new TStorageQualifierWrapper(EvqVertexOut, loc);
case GL_VERTEX_SHADER:
{
if (mShaderVersion < 300)
{
error(loc, "storage qualifier supported in GLSL ES 3.00 and above only", "out");
}
return new TStorageQualifierWrapper(EvqVertexOut, loc);
}
case GL_FRAGMENT_SHADER:
{
if (mShaderVersion < 300)
{
error(loc, "storage qualifier supported in GLSL ES 3.00 and above only", "out");
}
return new TStorageQualifierWrapper(EvqFragmentOut, loc);
}
case GL_COMPUTE_SHADER:
{
error(loc, "storage qualifier isn't supported in compute shaders", "out");
return new TStorageQualifierWrapper(EvqLast, loc);
}
case GL_GEOMETRY_SHADER_OES:
{
return new TStorageQualifierWrapper(EvqGeometryOut, loc);
}
default:
{
UNREACHABLE();
return new TStorageQualifierWrapper(EvqLast, loc);
}
}
return new TStorageQualifierWrapper(EvqFragmentOut, loc);
}
TStorageQualifierWrapper *TParseContext::parseInOutQualifier(const TSourceLoc &loc)
......
......@@ -340,6 +340,14 @@ class TParseContext : angle::NonCopyable
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *numViews);
void parseInvocations(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *numInvocations);
void parseMaxVertices(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *numMaxVertices);
TLayoutQualifier parseLayoutQualifier(const TString &qualifierType,
const TSourceLoc &qualifierTypeLine);
TLayoutQualifier parseLayoutQualifier(const TString &qualifierType,
......@@ -407,6 +415,20 @@ class TParseContext : angle::NonCopyable
TIntermTyped *falseExpression,
const TSourceLoc &line);
int getGeometryShaderMaxVertices() const { return mGeometryShaderMaxVertices; }
int getGeometryShaderInvocations() const
{
return (mGeometryShaderInvocations > 0) ? mGeometryShaderInvocations : 1;
}
TLayoutPrimitiveType getGeometryShaderInputPrimitiveType() const
{
return mGeometryShaderInputPrimitiveType;
}
TLayoutPrimitiveType getGeometryShaderOutputPrimitiveType() const
{
return mGeometryShaderOutputPrimitiveType;
}
// TODO(jmadill): make this private
TSymbolTable &symbolTable; // symbol table that goes with the language currently being parsed
......@@ -510,6 +532,10 @@ class TParseContext : angle::NonCopyable
void setAtomicCounterBindingDefaultOffset(const TPublicType &declaration,
const TSourceLoc &location);
bool checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier &typeQualifier);
bool parseGeometryShaderInputLayoutQualifier(const TTypeQualifier &typeQualifier);
bool parseGeometryShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier);
// 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
// followed by a declarator.
......@@ -566,6 +592,14 @@ class TParseContext : angle::NonCopyable
// Track the state of each atomic counter binding.
std::map<int, AtomicCounterBindingState> mAtomicCounterBindingStates;
// Track the geometry shader global parameters declared in layout.
TLayoutPrimitiveType mGeometryShaderInputPrimitiveType;
TLayoutPrimitiveType mGeometryShaderOutputPrimitiveType;
int mGeometryShaderInvocations;
int mGeometryShaderMaxVertices;
int mMaxGeometryShaderInvocations;
int mMaxGeometryShaderMaxVertices;
};
int PaParseStrings(size_t count,
......
......@@ -606,6 +606,42 @@ TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier,
joinedQualifier.imageInternalFormat = rightQualifier.imageInternalFormat;
}
if (rightQualifier.primitiveType != EptUndefined)
{
if (joinedQualifier.primitiveType != EptUndefined &&
joinedQualifier.primitiveType != rightQualifier.primitiveType)
{
diagnostics->error(rightQualifierLocation,
"Cannot have multiple different primitive specifiers",
getGeometryShaderPrimitiveTypeString(rightQualifier.primitiveType));
}
joinedQualifier.primitiveType = rightQualifier.primitiveType;
}
if (rightQualifier.invocations != 0)
{
if (joinedQualifier.invocations != 0 &&
joinedQualifier.invocations != rightQualifier.invocations)
{
diagnostics->error(rightQualifierLocation,
"Cannot have multiple different invocations specifiers",
"invocations");
}
joinedQualifier.invocations = rightQualifier.invocations;
}
if (rightQualifier.maxVertices != -1)
{
if (joinedQualifier.maxVertices != -1 &&
joinedQualifier.maxVertices != rightQualifier.maxVertices)
{
diagnostics->error(rightQualifierLocation,
"Cannot have multiple different max_vertices specifiers",
"max_vertices");
}
joinedQualifier.maxVertices = rightQualifier.maxVertices;
}
return joinedQualifier;
}
......
......@@ -164,6 +164,7 @@ void InitBuiltInResources(ShBuiltInResources *resources)
resources->ARM_shader_framebuffer_fetch = 0;
resources->OVR_multiview = 0;
resources->EXT_YUV_target = 0;
resources->OES_geometry_shader = 0;
resources->NV_draw_buffers = 0;
......@@ -224,6 +225,11 @@ void InitBuiltInResources(ShBuiltInResources *resources)
resources->MaxAtomicCounterBufferSize = 32;
resources->MaxUniformBufferBindings = 32;
// TODO(jiawei.shao@intel.com): add complete geometry shader constants.
resources->MaxGeometryUniformComponents = 1024;
resources->MaxGeometryOutputVertices = 256;
resources->MaxGeometryShaderInvocations = 32;
}
//
......
......@@ -95,6 +95,13 @@ void TranslatorESSL::translate(TIntermBlock *root, ShCompileOptions compileOptio
<< ", local_size_z=" << localSize[2] << ") in;\n";
}
if (getShaderType() == GL_GEOMETRY_SHADER_OES)
{
WriteGeometryShaderLayoutQualifiers(
sink, getGeometryShaderInputPrimitiveType(), getGeometryShaderInvocations(),
getGeometryShaderOutputPrimitiveType(), getGeometryShaderMaxVertices());
}
// Write translated shader.
TOutputESSL outputESSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(),
&getSymbolTable(), getShaderType(), shaderVer, precisionEmulation,
......@@ -153,6 +160,22 @@ void TranslatorESSL::writeExtensionBehavior(ShCompileOptions compileOptions)
sink << "#extension GL_NV_viewport_array2 : require\n";
}
}
else if (iter->first == "GL_OES_geometry_shader")
{
sink << "#ifdef GL_OES_geometry_shader\n"
<< "#extension GL_OES_geometry_shader : " << getBehaviorString(iter->second)
<< "\n"
<< "#elif defined GL_EXT_geometry_shader\n"
<< "#extension GL_EXT_geometry_shader : " << getBehaviorString(iter->second)
<< "\n";
if (iter->second == EBhRequire)
{
sink << "#else\n"
<< "#error \"No geometry shader extensions available.\" // Only generate "
"this if the extension is \"required\"\n";
}
sink << "#endif\n";
}
else
{
sink << "#extension " << iter->first << " : " << getBehaviorString(iter->second)
......
......@@ -200,6 +200,13 @@ void TranslatorGLSL::translate(TIntermBlock *root, ShCompileOptions compileOptio
<< ", local_size_z=" << localSize[2] << ") in;\n";
}
if (getShaderType() == GL_GEOMETRY_SHADER_OES)
{
WriteGeometryShaderLayoutQualifiers(
sink, getGeometryShaderInputPrimitiveType(), getGeometryShaderInvocations(),
getGeometryShaderOutputPrimitiveType(), getGeometryShaderMaxVertices());
}
// Write translated shader.
TOutputGLSL outputGLSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(),
&getSymbolTable(), getShaderType(), getShaderVersion(), getOutputType(),
......@@ -268,6 +275,12 @@ void TranslatorGLSL::writeExtensionBehavior(TIntermNode *root, ShCompileOptions
sink << "#extension GL_ARB_draw_buffers : " << getBehaviorString(iter.second)
<< "\n";
}
if (iter.first == "GL_OES_geometry_shader")
{
sink << "#extension GL_ARB_geometry_shader4 : " << getBehaviorString(iter.second)
<< "\n";
}
}
const bool isMultiview =
......
......@@ -64,6 +64,7 @@
'<(angle_path)/src/tests/compiler_tests/FloatLex_test.cpp',
'<(angle_path)/src/tests/compiler_tests/FragDepth_test.cpp',
'<(angle_path)/src/tests/compiler_tests/GLSLCompatibilityOutput_test.cpp',
'<(angle_path)/src/tests/compiler_tests/GeometryShader_test.cpp',
'<(angle_path)/src/tests/compiler_tests/InitOutputVariables_test.cpp',
'<(angle_path)/src/tests/compiler_tests/IntermNode_test.cpp',
'<(angle_path)/src/tests/compiler_tests/NV_draw_buffers_test.cpp',
......
//
// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// GeometryShader_test.cpp:
// tests for compiling a Geometry Shader
//
#include "GLSLANG/ShaderLang.h"
#include "angle_gl.h"
#include "compiler/translator/BaseTypes.h"
#include "compiler/translator/TranslatorESSL.h"
#include "gtest/gtest.h"
#include "tests/test_utils/compiler_test.h"
using namespace sh;
class GeometryShaderTest : public testing::Test
{
public:
GeometryShaderTest() {}
protected:
void SetUp() override
{
ShBuiltInResources resources;
InitBuiltInResources(&resources);
resources.OES_geometry_shader = 1;
mTranslator = new TranslatorESSL(GL_GEOMETRY_SHADER_OES, SH_GLES3_1_SPEC);
ASSERT_TRUE(mTranslator->Init(resources));
}
void TearDown() override { SafeDelete(mTranslator); }
// Return true when compilation succeeds
bool compile(const std::string &shaderString)
{
const char *shaderStrings[] = {shaderString.c_str()};
bool status = mTranslator->compile(shaderStrings, 1, SH_OBJECT_CODE | SH_VARIABLES);
TInfoSink &infoSink = mTranslator->getInfoSink();
mInfoLog = infoSink.info.c_str();
return status;
}
bool compileGeometryShader(const std::string &statement1, const std::string &statement2)
{
std::ostringstream sstream;
sstream << kHeader << statement1 << statement2 << kEmptyBody;
return compile(sstream.str());
}
bool compileGeometryShader(const std::string &statement1,
const std::string &statement2,
const std::string &statement3,
const std::string &statement4)
{
std::ostringstream sstream;
sstream << kHeader << statement1 << statement2 << statement3 << statement4 << kEmptyBody;
return compile(sstream.str());
}
static std::string GetGeometryShaderLayout(const std::string &layoutType,
const std::string &primitive,
int invocations,
int maxVertices)
{
std::ostringstream sstream;
sstream << "layout (";
if (!primitive.empty())
{
sstream << primitive;
}
if (invocations > 0)
{
sstream << ", invocations = " << invocations;
}
if (maxVertices >= 0)
{
sstream << ", max_vertices = " << maxVertices;
}
sstream << ") " << layoutType << ";" << std::endl;
return sstream.str();
}
const std::string kHeader =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n";
const std::string kEmptyBody =
"void main()\n"
"{\n"
"}\n";
const std::string kInputLayout = "layout (points) in;\n";
const std::string kOutputLayout = "layout (points, max_vertices = 1) out;\n";
std::string mInfoLog;
TranslatorESSL *mTranslator = nullptr;
};
// Geometry Shaders are not supported in GLSL ES shaders version lower than 310.
TEST_F(GeometryShaderTest, Version300)
{
const std::string &shaderString =
"#version 300 es\n"
"layout(points) in;\n"
"layout(points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders are not supported in GLSL ES shaders version 310 without extension
// OES_geometry_shader enabled.
TEST_F(GeometryShaderTest, Version310WithoutExtension)
{
const std::string &shaderString =
"#version 310 es\n"
"layout(points) in;\n"
"layout(points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders are supported in GLSL ES shaders version 310 with OES_geometry_shader enabled.
TEST_F(GeometryShaderTest, Version310WithOESExtension)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout(points) in;\n"
"layout(points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Missing the declaration of input primitive in a geometry shader should be a link error instead of
// a compile error.
TEST_F(GeometryShaderTest, NoInputPrimitives)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout(points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders can only support 5 kinds of input primitives, which cannot be used as output
// primitives except 'points'.
// Skip testing "points" as it can be used as both input and output primitives.
TEST_F(GeometryShaderTest, ValidInputPrimitives)
{
const std::array<std::string, 4> kInputPrimitives = {
{"lines", "lines_adjacency", "triangles", "triangles_adjacency"}};
for (const std::string &inputPrimitive : kInputPrimitives)
{
if (!compileGeometryShader(GetGeometryShaderLayout("in", inputPrimitive, -1, -1),
kOutputLayout))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
if (compileGeometryShader(kInputLayout,
GetGeometryShaderLayout("out", inputPrimitive, -1, 6)))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
}
// Geometry Shaders allow duplicated declaration of input primitive, but all of them must be same.
TEST_F(GeometryShaderTest, RedeclareInputPrimitives)
{
const std::array<std::string, 5> kInputPrimitives = {
{"points", "lines", "lines_adjacency", "triangles", "triangles_adjacency"}};
for (GLuint i = 0; i < kInputPrimitives.size(); ++i)
{
const std::string &inputLayoutStr1 =
GetGeometryShaderLayout("in", kInputPrimitives[i], -1, -1);
if (!compileGeometryShader(inputLayoutStr1, inputLayoutStr1, kOutputLayout, ""))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
for (GLuint j = i + 1; j < kInputPrimitives.size(); ++j)
{
const std::string &inputLayoutStr2 =
GetGeometryShaderLayout("in", kInputPrimitives[j], -1, -1);
if (compileGeometryShader(inputLayoutStr1, inputLayoutStr2, kOutputLayout, ""))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
}
}
// Geometry Shaders don't allow declaring different input primitives in one layout.
TEST_F(GeometryShaderTest, DeclareDifferentInputPrimitivesInOneLayout)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, triangles) in;\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders don't allow 'invocations' < 1.
TEST_F(GeometryShaderTest, InvocationsLessThanOne)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, invocations = 0) in;\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders allow declaring 'invocations' == 1 together with input primitive declaration in
// one layout.
TEST_F(GeometryShaderTest, InvocationsEqualsOne)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, invocations = 1) in;\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders allow declaring 'invocations' == 1 in an individual layout.
TEST_F(GeometryShaderTest, InvocationsEqualsOneInSeparatedLayout)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (invocations = 1) in;\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders don't allow 'invocations' larger than the implementation-dependent maximum value
// (32 in this test).
TEST_F(GeometryShaderTest, TooLargeInvocations)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, invocations = 9989899) in;\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders allow 'invocations' declared together with input primitives in one layout.
TEST_F(GeometryShaderTest, InvocationsDeclaredWithInputPrimitives)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, invocations = 3) in;\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders allow 'invocations' declared before input primitives in one input layout.
TEST_F(GeometryShaderTest, InvocationsBeforeInputPrimitives)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (invocations = 3, points) in;\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders allow 'invocations' declared in an individual input layout.
TEST_F(GeometryShaderTest, InvocationsInIndividualLayout)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (invocations = 3) in;\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders allow duplicated 'invocations' declarations.
TEST_F(GeometryShaderTest, DuplicatedInvocations)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, invocations = 3) in;\n"
"layout (invocations = 3) in;\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders don't allow multiple different 'invocations' declarations in different
// layouts.
TEST_F(GeometryShaderTest, RedeclareDifferentInvocations)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, invocations = 3) in;\n"
"layout (invocations = 5) in;\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders don't allow multiple different 'invocations' declarations in different
// layouts.
TEST_F(GeometryShaderTest, RedeclareDifferentInvocationsAfterInvocationEqualsOne)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, invocations = 1) in;\n"
"layout (invocations = 5) in;\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders don't allow multiple different 'invocations' declarations in one layout.
TEST_F(GeometryShaderTest, RedeclareDifferentInvocationsInOneLayout)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, invocations = 3, invocations = 5) in;\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders don't allow 'invocations' in out layouts.
TEST_F(GeometryShaderTest, DeclareInvocationsInOutLayout)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, invocations = 3, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders don't allow 'invocations' in layouts without 'in' qualifier.
TEST_F(GeometryShaderTest, DeclareInvocationsInLayoutNoQualifier)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (invocations = 3);\n"
"layout (points, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders allow declaring output primitive before input primitive declaration.
TEST_F(GeometryShaderTest, DeclareOutputPrimitiveBeforeInputPrimitiveDeclare)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, max_vertices = 1) out;\n"
"layout (points) in;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders allow declaring 'max_vertices' before output primitive in one output layout.
TEST_F(GeometryShaderTest, DeclareMaxVerticesBeforeOutputPrimitive)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (max_vertices = 1, points) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Missing the declaration of output primitive should be a link error instead of a compile error in
// a geometry shader.
TEST_F(GeometryShaderTest, NoOutputPrimitives)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders can only support 3 kinds of output primitives, which cannot be used as input
// primitives except 'points'.
// Skip testing "points" as it can be used as both input and output primitives.
TEST_F(GeometryShaderTest, ValidateOutputPrimitives)
{
const std::string outputPrimitives[] = {"line_strip", "triangle_strip"};
for (const std::string &outPrimitive : outputPrimitives)
{
if (!compileGeometryShader(kInputLayout,
GetGeometryShaderLayout("out", outPrimitive, -1, 6)))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
if (compileGeometryShader(GetGeometryShaderLayout("in", outPrimitive, -1, -1),
kOutputLayout))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
}
// Geometry Shaders allow duplicated output primitive declarations, but all of them must be same.
TEST_F(GeometryShaderTest, RedeclareOutputPrimitives)
{
const std::array<std::string, 3> outPrimitives = {{"points", "line_strip", "triangle_strip"}};
for (GLuint i = 0; i < outPrimitives.size(); i++)
{
constexpr int maxVertices = 1;
const std::string &outputLayoutStr1 =
GetGeometryShaderLayout("out", outPrimitives[i], -1, maxVertices);
const std::string &outputLayoutStr2 =
GetGeometryShaderLayout("out", outPrimitives[i], -1, -1);
if (!compileGeometryShader(kInputLayout, outputLayoutStr1, outputLayoutStr2, ""))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
for (GLuint j = i + 1; j < outPrimitives.size(); j++)
{
const std::string &outputLayoutStr3 =
GetGeometryShaderLayout("out", outPrimitives[j], -1, -1);
if (compileGeometryShader(kInputLayout, outputLayoutStr1, outputLayoutStr3, ""))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
}
}
// Geometry Shaders don't allow declaring different output primitives in one layout.
TEST_F(GeometryShaderTest, RedeclareDifferentOutputPrimitivesInOneLayout)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, max_vertices = 3, line_strip) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Missing the declarations of output primitives and 'max_vertices' in a geometry shader should
// be a link error instead of a compile error.
TEST_F(GeometryShaderTest, NoOutLayouts)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Missing the declarations of 'max_vertices' in a geometry shader should be a link error
// instead of a compile error.
TEST_F(GeometryShaderTest, NoMaxVertices)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders cannot declare a negative 'max_vertices'.
TEST_F(GeometryShaderTest, NegativeMaxVertices)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, max_vertices = -1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders allow max_vertices == 0.
TEST_F(GeometryShaderTest, ZeroMaxVertices)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, max_vertices = 0) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders cannot declare a 'max_vertices' that is greater than
// MAX_GEOMETRY_OUTPUT_VERTICES_EXT (256 in this test).
TEST_F(GeometryShaderTest, TooLargeMaxVertices)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, max_vertices = 257) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders can declare 'max_vertices' in an individual out layout.
TEST_F(GeometryShaderTest, MaxVerticesInIndividualLayout)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points) out;\n"
"layout (max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders allow duplicated 'max_vertices' declarations.
TEST_F(GeometryShaderTest, DuplicatedMaxVertices)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, max_vertices = 1) out;\n"
"layout (max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Geometry Shaders don't allow declaring different 'max_vertices'.
TEST_F(GeometryShaderTest, RedeclareDifferentMaxVertices)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, max_vertices = 1) out;\n"
"layout (max_vertices = 2) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders don't allow declaring different 'max_vertices'.
TEST_F(GeometryShaderTest, RedeclareDifferentMaxVerticesInOneLayout)
{
const std::string &shaderString =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, max_vertices = 2, max_vertices = 1) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders don't allow 'location' declared with input/output primitives in one layout.
TEST_F(GeometryShaderTest, invalidLocation)
{
const std::string &shaderString1 =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, location = 1) in;\n"
"layout (points, max_vertices = 2) out;\n"
"void main()\n"
"{\n"
"}\n";
const std::string &shaderString2 =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (invocations = 2, location = 1) in;\n"
"layout (points, max_vertices = 2) out;\n"
"void main()\n"
"{\n"
"}\n";
const std::string &shaderString3 =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, location = 3, max_vertices = 2) out;\n"
"void main()\n"
"{\n"
"}\n";
const std::string &shaderString4 =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points) out;\n"
"layout (max_vertices = 2, location = 3) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString1) || compile(shaderString2) || compile(shaderString3) ||
compile(shaderString4))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Geometry Shaders don't allow invalid layout qualifier declarations.
TEST_F(GeometryShaderTest, invalidLayoutQualifiers)
{
const std::string &shaderString1 =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, abc) in;\n"
"layout (points, max_vertices = 2) out;\n"
"void main()\n"
"{\n"
"}\n";
const std::string &shaderString2 =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, abc, max_vertices = 2) out;\n"
"void main()\n"
"{\n"
"}\n";
const std::string &shaderString3 =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, xyz = 2) in;\n"
"layout (points, max_vertices = 2) out;\n"
"void main()\n"
"{\n"
"}\n";
const std::string &shaderString4 =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points) out;\n"
"layout (max_vertices = 2, xyz = 3) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString1) || compile(shaderString2) || compile(shaderString3) ||
compile(shaderString4))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
......@@ -93,6 +93,20 @@ class ComputeShaderEnforcePackingValidationTest : public ComputeShaderValidation
static constexpr GLint kMaxComputeUniformComponents = 128;
};
class GeometryShaderValidationTest : public ShaderCompileTreeTest
{
public:
GeometryShaderValidationTest() {}
protected:
void initResources(ShBuiltInResources *resources) override
{
resources->OES_geometry_shader = 1;
}
::GLenum getShaderType() const override { return GL_GEOMETRY_SHADER_OES; }
ShShaderSpec getShaderSpec() const override { return SH_GLES3_1_SPEC; }
};
// This is a test for a bug that used to exist in ANGLE:
// Calling a function with all parameters missing should not succeed.
TEST_F(FragmentShaderValidationTest, FunctionParameterMismatch)
......@@ -2189,6 +2203,53 @@ TEST_F(FragmentShaderValidationTest, InvalidUseOfLocalSizeX)
}
}
// The local_size layout qualifier is only available in compute shaders.
TEST_F(GeometryShaderValidationTest, InvalidUseOfLocalSizeX)
{
const std::string &shaderString1 =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points, local_size_x = 15) in;\n"
"layout (points, max_vertices = 2) out;\n"
"void main()\n"
"{\n"
"}\n";
const std::string &shaderString2 =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (invocations = 2, local_size_x = 15) in;\n"
"layout (points, max_vertices = 2) out;\n"
"void main()\n"
"{\n"
"}\n";
const std::string &shaderString3 =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points, local_size_x = 15, max_vertices = 2) out;\n"
"void main()\n"
"{\n"
"}\n";
const std::string &shaderString4 =
"#version 310 es\n"
"#extension GL_OES_geometry_shader : require\n"
"layout (points) in;\n"
"layout (points) out;\n"
"layout (max_vertices = 2, local_size_x = 15) out;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(shaderString1) || compile(shaderString2) || compile(shaderString3) ||
compile(shaderString4))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// It is a compile time error to use the gl_WorkGroupSize constant if
// the local size has not been declared yet.
// GLSL ES 3.10 Revision 4, 7.1.3 Compute Shader Special Variables
......@@ -4086,3 +4147,180 @@ TEST_F(FragmentShaderValidationTest, ArrayAsArraySize)
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// The input primitive layout qualifier is only available in geometry shaders.
TEST_F(VertexShaderValidationTest, InvalidUseOfInputPrimitives)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"layout(points) in vec4 myInput;\n"
"out vec4 myOutput;\n"
"void main() {\n"
" myOutput = myInput;\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// The input primitive layout qualifier is only available in geometry shaders.
TEST_F(FragmentShaderValidationTest, InvalidUseOfInputPrimitives)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"layout(points) in vec4 myInput;\n"
"out vec4 myOutput;\n"
"void main() {\n"
" myOutput = myInput;\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// The input primitive layout qualifier is only available in geometry shaders.
TEST_F(ComputeShaderValidationTest, InvalidUseOfInputPrimitives)
{
const std::string &shaderString =
"#version 310 es\n"
"layout(points, local_size_x = 12) in;\n"
"void main()\n"
"{\n"
" uvec3 WorkGroupSize = gl_WorkGroupSize;\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// The output primitive layout qualifier is only available in geometry shaders.
TEST_F(VertexShaderValidationTest, InvalidUseOfOutputPrimitives)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"in vec4 myInput;\n"
"layout(points) out vec4 myOutput;\n"
"void main() {\n"
" myOutput = myInput;\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// The output primitive layout qualifier is only available in geometry shaders.
TEST_F(FragmentShaderValidationTest, InvalidUseOfOutputPrimitives)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"in vec4 myInput;\n"
"layout(points) out vec4 myOutput;\n"
"void main() {\n"
" myOutput = myInput;\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// The 'invocations' layout qualifier is only available in geometry shaders.
TEST_F(VertexShaderValidationTest, InvalidUseOfInvocations)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"layout (invocations = 3) in vec4 myInput;\n"
"out vec4 myOutput;\n"
"void main() {\n"
" myOutput = myInput;\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// The 'invocations' layout qualifier is only available in geometry shaders.
TEST_F(FragmentShaderValidationTest, InvalidUseOfInvocations)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"layout (invocations = 3) in vec4 myInput;\n"
"out vec4 myOutput;\n"
"void main() {\n"
" myOutput = myInput;\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// The 'invocations' layout qualifier is only available in geometry shaders.
TEST_F(ComputeShaderValidationTest, InvalidUseOfInvocations)
{
const std::string &shaderString =
"#version 310 es\n"
"layout(invocations = 3, local_size_x = 12) in;\n"
"void main()\n"
"{\n"
" uvec3 WorkGroupSize = gl_WorkGroupSize;\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// The 'max_vertices' layout qualifier is only available in geometry shaders.
TEST_F(VertexShaderValidationTest, InvalidUseOfMaxVertices)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"in vec4 myInput;\n"
"layout(max_vertices = 3) out vec4 myOutput;\n"
"void main() {\n"
" myOutput = myInput;\n"
"}\n";
if (compile(shaderString))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// The 'max_vertices' layout qualifier is only available in geometry shaders.
TEST_F(FragmentShaderValidationTest, InvalidUseOfMaxVertices)
{
const std::string &shaderString =
"#version 310 es\n"
"precision mediump float;\n"
"in vec4 myInput;\n"
"layout(max_vertices = 3) out vec4 myOutput;\n"
"void main() {\n"
" myOutput = myInput;\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