Commit e670fc72 by Jamie Madill Committed by Commit Bot

Implement shader compiler changes for Tessellation.

Numerous rule changes to support validating Tessellation Control and Evaluation shaders. New per-patch inputs and output variable support. Includes a new traverser step that validates barrier function calls. Functionality changes upcoming in http://crrev.com/c/2568234 Bug: angleproject:3572 Change-Id: If8da1c21d30efa12c60ed0d6c3f8cf0b27e4c86f Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2633936 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarMohan Maiya <m.maiya@samsung.com>
parent 20f8828c
......@@ -26,7 +26,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 250
#define ANGLE_SH_VERSION 251
enum ShShaderSpec
{
......@@ -789,11 +789,20 @@ const std::set<std::string> *GetUsedImage2DFunctionNames(const ShHandle handle);
bool HasValidGeometryShaderInputPrimitiveType(const ShHandle handle);
bool HasValidGeometryShaderOutputPrimitiveType(const ShHandle handle);
bool HasValidGeometryShaderMaxVertices(const ShHandle handle);
bool HasValidTessGenMode(const ShHandle handle);
bool HasValidTessGenSpacing(const ShHandle handle);
bool HasValidTessGenVertexOrder(const ShHandle handle);
bool HasValidTessGenPointMode(const ShHandle handle);
GLenum GetGeometryShaderInputPrimitiveType(const ShHandle handle);
GLenum GetGeometryShaderOutputPrimitiveType(const ShHandle handle);
int GetGeometryShaderInvocations(const ShHandle handle);
int GetGeometryShaderMaxVertices(const ShHandle handle);
unsigned int GetShaderSharedMemorySize(const ShHandle handle);
int GetTessControlShaderVertices(const ShHandle handle);
GLenum GetTessGenMode(const ShHandle handle);
GLenum GetTessGenSpacing(const ShHandle handle);
GLenum GetTessGenVertexOrder(const ShHandle handle);
GLenum GetTessGenPointMode(const ShHandle handle);
//
// Helper function to identify specs that are based on the WebGL spec.
......
......@@ -201,6 +201,8 @@ struct ShaderVariable
InterpolationType interpolation;
bool isInvariant;
bool isShaderIOBlock;
bool isPatch;
// Decide whether two varyings are the same at shader link time,
// assuming they are from consecutive shader stages.
// Invariance needs to match only in ESSL1. Relevant spec sections:
......
......@@ -6,11 +6,11 @@
"src/compiler/translator/glslang.l":
"a04170178bdc8511bff69ec4ff144576",
"src/compiler/translator/glslang.y":
"1c7a2f4fa0f62ebc42f855bb8a8deeaf",
"d5e97733e569b408728a1645b6763890",
"src/compiler/translator/glslang_lex_autogen.cpp":
"5d6469e58f9855f93987b5ddbd663920",
"src/compiler/translator/glslang_tab_autogen.cpp":
"821a6cccd98a7d89666bfb3fabee3585",
"f4d714cad4bd92c4b9ecfebc81349dc7",
"src/compiler/translator/glslang_tab_autogen.h":
"840c12db9f7824afd05b5306c8816d95",
"tools/flex-bison/linux/bison.sha1":
......
......@@ -92,6 +92,8 @@ angle_translator_sources = [
"src/compiler/translator/Types.h",
"src/compiler/translator/ValidateAST.cpp",
"src/compiler/translator/ValidateAST.h",
"src/compiler/translator/ValidateBarrierFunctionCall.cpp",
"src/compiler/translator/ValidateBarrierFunctionCall.h",
"src/compiler/translator/ValidateClipCullDistance.cpp",
"src/compiler/translator/ValidateClipCullDistance.h",
"src/compiler/translator/ValidateGlobalInitializer.cpp",
......
......@@ -1060,6 +1060,8 @@ inline bool IsShaderIn(TQualifier qualifier)
switch (qualifier)
{
case EvqVertexIn:
case EvqTessControlIn:
case EvqTessEvaluationIn:
case EvqGeometryIn:
case EvqFragmentIn:
case EvqAttribute:
......@@ -1069,6 +1071,7 @@ inline bool IsShaderIn(TQualifier qualifier)
case EvqNoPerspectiveIn:
case EvqCentroidIn:
case EvqSampleIn:
case EvqPatchIn:
return true;
default:
return false;
......@@ -1080,6 +1083,8 @@ inline bool IsShaderOut(TQualifier qualifier)
switch (qualifier)
{
case EvqVertexOut:
case EvqTessControlOut:
case EvqTessEvaluationOut:
case EvqGeometryOut:
case EvqFragmentOut:
case EvqVaryingOut:
......@@ -1088,6 +1093,7 @@ inline bool IsShaderOut(TQualifier qualifier)
case EvqNoPerspectiveOut:
case EvqCentroidOut:
case EvqSampleOut:
case EvqPatchOut:
return true;
default:
return false;
......@@ -1168,6 +1174,20 @@ enum TLayoutPrimitiveType
EptTriangleStrip
};
enum TLayoutTessEvaluationType
{
EtetUndefined,
EtetTriangles,
EtetQuads,
EtetIsolines,
EtetEqualSpacing,
EtetFractionalEvenSpacing,
EtetFractionalOddSpacing,
EtetCw,
EtetCcw,
EtetPointMode
};
struct TLayoutQualifier
{
// Must have a trivial default constructor since it is used in YYSTYPE.
......@@ -1181,7 +1201,9 @@ struct TLayoutQualifier
earlyFragmentTests == false && matrixPacking == EmpUnspecified &&
blockStorage == EbsUnspecified && !localSize.isAnyValueSet() &&
imageInternalFormat == EiifUnspecified && primitiveType == EptUndefined &&
invocations == 0 && maxVertices == -1 && index == -1;
invocations == 0 && maxVertices == -1 && vertices == 0 &&
tesPrimitiveType == EtetUndefined && tesVertexSpacingType == EtetUndefined &&
tesOrderingType == EtetUndefined && tesPointType == EtetUndefined && index == -1;
}
bool isCombinationValid() const
......@@ -1236,6 +1258,13 @@ struct TLayoutQualifier
int invocations;
int maxVertices;
// EXT_tessellation_control shader layout qualifiers
int vertices;
TLayoutTessEvaluationType tesPrimitiveType;
TLayoutTessEvaluationType tesVertexSpacingType;
TLayoutTessEvaluationType tesOrderingType;
TLayoutTessEvaluationType tesPointType;
// EXT_blend_func_extended fragment output layout qualifier
int index;
......@@ -1255,6 +1284,11 @@ struct TLayoutQualifier
primitiveType(EptUndefined),
invocations(0),
maxVertices(-1),
vertices(0),
tesPrimitiveType(EtetUndefined),
tesVertexSpacingType(EtetUndefined),
tesOrderingType(EtetUndefined),
tesPointType(EtetUndefined),
index(-1)
{}
};
......@@ -1324,6 +1358,7 @@ inline const char *getQualifierString(TQualifier q)
case EvqVaryingOut: return "varying";
case EvqUniform: return "uniform";
case EvqBuffer: return "buffer";
case EvqPatch: return "patch";
case EvqVertexIn: return "in";
case EvqFragmentOut: return "out";
case EvqVertexOut: return "out";
......@@ -1389,6 +1424,17 @@ inline const char *getQualifierString(TQualifier q)
case EvqSampleMaskIn: return "SampleMaskIn";
case EvqSampleMask: return "SampleMask";
case EvqNumSamples: return "NumSamples";
case EvqPatchIn: return "patch in";
case EvqPatchOut: return "patch out";
case EvqTessControlIn: return "in";
case EvqTessControlOut: return "out";
case EvqPerVertexOut: return "gl_out";
case EvqPatchVerticesIn: return "PatchVerticesIn";
case EvqTessLevelOuter: return "TessLevelOuter";
case EvqTessLevelInner: return "TessLevelInner";
case EvqTessEvaluationIn: return "in";
case EvqTessEvaluationOut: return "out";
case EvqTessCoord: return "TessCoord";
default: UNREACHABLE(); return "unknown qualifier";
}
// clang-format on
......@@ -1517,6 +1563,34 @@ inline const char *getGeometryShaderPrimitiveTypeString(TLayoutPrimitiveType pri
}
}
inline const char *getTessEvaluationShaderTypeString(TLayoutTessEvaluationType type)
{
switch (type)
{
case EtetTriangles:
return "triangles";
case EtetQuads:
return "quads";
case EtetIsolines:
return "isolines";
case EtetEqualSpacing:
return "equal_spacing";
case EtetFractionalEvenSpacing:
return "fractional_even_spacing";
case EtetFractionalOddSpacing:
return "fractional_odd_spacing";
case EtetCw:
return "cw";
case EtetCcw:
return "ccw";
case EtetPointMode:
return "point_mode";
default:
UNREACHABLE();
return "unknown tessellation evaluation shader variable type";
}
}
} // namespace sh
#endif // COMPILER_TRANSLATOR_BASETYPES_H_
......@@ -124,7 +124,9 @@ class CollectVariablesTraverser : public TIntermTraverser
ShHashFunction64 hashFunction,
TSymbolTable *symbolTable,
GLenum shaderType,
const TExtensionBehavior &extensionBehavior);
const TExtensionBehavior &extensionBehavior,
const ShBuiltInResources &resources,
int tessControlShaderOutputVertices);
bool visitGlobalQualifierDeclaration(Visit visit,
TIntermGlobalQualifierDeclaration *node) override;
......@@ -138,11 +140,13 @@ class CollectVariablesTraverser : public TIntermTraverser
void setFieldOrVariableProperties(const TType &type,
bool staticUse,
bool isShaderIOBlock,
bool isPatch,
ShaderVariable *variableOut) const;
void setFieldProperties(const TType &type,
const ImmutableString &name,
bool staticUse,
bool isShaderIOBlock,
bool isPatch,
ShaderVariable *variableOut) const;
void setCommonVariableProperties(const TType &type,
const TVariable &variable,
......@@ -217,8 +221,11 @@ class CollectVariablesTraverser : public TIntermTraverser
bool mSampleMaskAdded;
bool mSampleMaskInAdded;
// Geometry Shader builtins
// Geometry and Tessellation Shader builtins
bool mPerVertexInAdded;
bool mPerVertexOutAdded;
// Geometry Shader builtins
bool mPrimitiveIDInAdded;
bool mInvocationIDAdded;
......@@ -229,10 +236,18 @@ class CollectVariablesTraverser : public TIntermTraverser
// Shared memory variables
bool mSharedVariableAdded;
// Tessellation Shader builtins
bool mPatchVerticesInAdded;
bool mTessLevelOuterAdded;
bool mTessLevelInnerAdded;
bool mTessCoordAdded;
const int mTessControlShaderOutputVertices;
ShHashFunction64 mHashFunction;
GLenum mShaderType;
const TExtensionBehavior &mExtensionBehavior;
const ShBuiltInResources &mResources;
};
CollectVariablesTraverser::CollectVariablesTraverser(
......@@ -247,7 +262,9 @@ CollectVariablesTraverser::CollectVariablesTraverser(
ShHashFunction64 hashFunction,
TSymbolTable *symbolTable,
GLenum shaderType,
const TExtensionBehavior &extensionBehavior)
const TExtensionBehavior &extensionBehavior,
const ShBuiltInResources &resources,
int tessControlShaderOutputVertices)
: TIntermTraverser(true, false, false, symbolTable),
mAttribs(attribs),
mOutputVariables(outputVariables),
......@@ -289,14 +306,21 @@ CollectVariablesTraverser::CollectVariablesTraverser(
mSampleMaskAdded(false),
mSampleMaskInAdded(false),
mPerVertexInAdded(false),
mPerVertexOutAdded(false),
mPrimitiveIDInAdded(false),
mInvocationIDAdded(false),
mPrimitiveIDAdded(false),
mLayerAdded(false),
mSharedVariableAdded(false),
mPatchVerticesInAdded(false),
mTessLevelOuterAdded(false),
mTessLevelInnerAdded(false),
mTessCoordAdded(false),
mTessControlShaderOutputVertices(tessControlShaderOutputVertices),
mHashFunction(hashFunction),
mShaderType(shaderType),
mExtensionBehavior(extensionBehavior)
mExtensionBehavior(extensionBehavior),
mResources(resources)
{}
std::string CollectVariablesTraverser::getMappedName(const TSymbol *symbol) const
......@@ -314,8 +338,10 @@ void CollectVariablesTraverser::setBuiltInInfoFromSymbol(const TVariable &variab
bool isShaderIOBlock =
IsShaderIoBlock(type.getQualifier()) && type.getInterfaceBlock() != nullptr;
bool isPatch =
type.getQualifier() == EvqTessLevelInner || type.getQualifier() == EvqTessLevelOuter;
setFieldOrVariableProperties(type, true, isShaderIOBlock, info);
setFieldOrVariableProperties(type, true, isShaderIOBlock, isPatch, info);
}
void CollectVariablesTraverser::recordBuiltInVaryingUsed(const TVariable &variable,
......@@ -618,7 +644,9 @@ void CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol)
}
else
{
ASSERT(mShaderType == GL_FRAGMENT_SHADER);
ASSERT(mShaderType == GL_FRAGMENT_SHADER ||
mShaderType == GL_TESS_CONTROL_SHADER ||
mShaderType == GL_TESS_EVALUATION_SHADER);
recordBuiltInVaryingUsed(symbol->variable(), &mPrimitiveIDAdded,
mInputVaryings);
}
......@@ -664,6 +692,39 @@ void CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol)
case EvqCullDistance:
recordBuiltInVaryingUsed(symbol->variable(), &mCullDistanceAdded, mOutputVaryings);
return;
case EvqPatchVerticesIn:
recordBuiltInVaryingUsed(symbol->variable(), &mPatchVerticesInAdded,
mInputVaryings);
break;
case EvqTessCoord:
recordBuiltInVaryingUsed(symbol->variable(), &mTessCoordAdded, mInputVaryings);
break;
case EvqTessLevelOuter:
if (mShaderType == GL_TESS_CONTROL_SHADER)
{
recordBuiltInVaryingUsed(symbol->variable(), &mTessLevelOuterAdded,
mOutputVaryings);
}
else
{
ASSERT(mShaderType == GL_TESS_EVALUATION_SHADER);
recordBuiltInVaryingUsed(symbol->variable(), &mTessLevelOuterAdded,
mInputVaryings);
}
break;
case EvqTessLevelInner:
if (mShaderType == GL_TESS_CONTROL_SHADER)
{
recordBuiltInVaryingUsed(symbol->variable(), &mTessLevelInnerAdded,
mOutputVaryings);
}
else
{
ASSERT(mShaderType == GL_TESS_EVALUATION_SHADER);
recordBuiltInVaryingUsed(symbol->variable(), &mTessLevelInnerAdded,
mInputVaryings);
}
break;
default:
break;
}
......@@ -677,12 +738,14 @@ void CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol)
void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
bool staticUse,
bool isShaderIOBlock,
bool isPatch,
ShaderVariable *variableOut) const
{
ASSERT(variableOut);
variableOut->staticUse = staticUse;
variableOut->isShaderIOBlock = isShaderIOBlock;
variableOut->isPatch = isPatch;
const TStructure *structure = type.getStruct();
const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
......@@ -702,7 +765,7 @@ void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
// Regardless of the variable type (uniform, in/out etc.) its fields are always plain
// ShaderVariable objects.
ShaderVariable fieldVariable;
setFieldProperties(*field->type(), field->name(), staticUse, isShaderIOBlock,
setFieldProperties(*field->type(), field->name(), staticUse, isShaderIOBlock, isPatch,
&fieldVariable);
variableOut->fields.push_back(fieldVariable);
}
......@@ -720,7 +783,8 @@ void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
for (const TField *field : fields)
{
ShaderVariable fieldVariable;
setFieldProperties(*field->type(), field->name(), staticUse, true, &fieldVariable);
setFieldProperties(*field->type(), field->name(), staticUse, true, isPatch,
&fieldVariable);
fieldVariable.isShaderIOBlock = true;
variableOut->fields.push_back(fieldVariable);
}
......@@ -735,6 +799,27 @@ void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
if (!arraySizes.empty())
{
variableOut->arraySizes.assign(arraySizes.begin(), arraySizes.end());
if (arraySizes[0] == 0)
{
// Tessellation Control & Evaluation shader inputs:
// Declaring an array size is optional. If no size is specified, it will be taken from
// the implementation-dependent maximum patch size (gl_MaxPatchVertices).
if (type.getQualifier() == EvqTessControlIn ||
type.getQualifier() == EvqTessEvaluationIn)
{
variableOut->arraySizes[0] = mResources.MaxPatchVertices;
}
// Tessellation Control shader outputs:
// Declaring an array size is optional. If no size is specified, it will be taken from
// output patch size declared in the shader.
if (type.getQualifier() == EvqTessControlOut)
{
ASSERT(mTessControlShaderOutputVertices > 0);
variableOut->arraySizes[0] = mTessControlShaderOutputVertices;
}
}
}
}
......@@ -742,10 +827,11 @@ void CollectVariablesTraverser::setFieldProperties(const TType &type,
const ImmutableString &name,
bool staticUse,
bool isShaderIOBlock,
bool isPatch,
ShaderVariable *variableOut) const
{
ASSERT(variableOut);
setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, variableOut);
setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, isPatch, variableOut);
variableOut->name.assign(name.data(), name.length());
variableOut->mappedName = HashName(name, mHashFunction, nullptr).data();
}
......@@ -755,12 +841,14 @@ void CollectVariablesTraverser::setCommonVariableProperties(const TType &type,
ShaderVariable *variableOut) const
{
ASSERT(variableOut);
ASSERT(type.getInterfaceBlock() == nullptr || IsShaderIoBlock(type.getQualifier()) ||
type.getQualifier() == EvqPatchIn || type.getQualifier() == EvqPatchOut);
const bool staticUse = mSymbolTable->isStaticallyUsed(variable);
const bool isShaderIOBlock =
IsShaderIoBlock(type.getQualifier()) && type.getInterfaceBlock() != nullptr;
const bool staticUse = mSymbolTable->isStaticallyUsed(variable);
const bool isShaderIOBlock = type.getInterfaceBlock() != nullptr;
const bool isPatch = type.getQualifier() == EvqPatchIn || type.getQualifier() == EvqPatchOut;
setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, variableOut);
setFieldOrVariableProperties(type, staticUse, isShaderIOBlock, isPatch, variableOut);
const bool isNamed = variable.symbolType() != SymbolType::Empty;
......@@ -836,6 +924,10 @@ ShaderVariable CollectVariablesTraverser::recordVarying(const TIntermSymbol &var
varying.isInvariant = true;
}
break;
case EvqPatchIn:
case EvqPatchOut:
varying.isPatch = true;
break;
default:
break;
}
......@@ -865,6 +957,7 @@ ShaderVariable CollectVariablesTraverser::recordVarying(const TIntermSymbol &var
const TType &fieldType = *blockField->type();
fieldVariable.hasImplicitLocation = isBlockImplicitLocation;
fieldVariable.isPatch = varying.isPatch;
int fieldLocation = fieldType.getLayoutQualifier().location;
if (fieldLocation >= 0)
......@@ -957,7 +1050,7 @@ void CollectVariablesTraverser::recordInterfaceBlock(const char *instanceName,
}
ShaderVariable fieldVariable;
setFieldProperties(fieldType, field->name(), staticUse, false, &fieldVariable);
setFieldProperties(fieldType, field->name(), staticUse, false, false, &fieldVariable);
fieldVariable.isRowMajorLayout =
(fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
interfaceBlock->fields.push_back(fieldVariable);
......@@ -1016,7 +1109,8 @@ bool CollectVariablesTraverser::visitDeclaration(Visit, TIntermDeclaration *node
// (named or unnamed) structure as ShaderVariable. at link between two shaders, validation
// between of named and unnamed, needs the same structure, its members, and members order
// except instance name.
if (typedNode.getBasicType() == EbtInterfaceBlock && !IsShaderIoBlock(qualifier))
if (typedNode.getBasicType() == EbtInterfaceBlock && !IsShaderIoBlock(qualifier) &&
qualifier != EvqPatchIn && qualifier != EvqPatchOut)
{
InterfaceBlock interfaceBlock;
bool isUnnamed = variable.variable().symbolType() == SymbolType::Empty;
......@@ -1041,7 +1135,8 @@ bool CollectVariablesTraverser::visitDeclaration(Visit, TIntermDeclaration *node
else
{
ASSERT(variable.variable().symbolType() != SymbolType::Empty ||
IsShaderIoBlock(qualifier));
IsShaderIoBlock(qualifier) || qualifier == EvqPatchIn ||
qualifier == EvqPatchOut);
switch (qualifier)
{
case EvqAttribute:
......@@ -1125,6 +1220,13 @@ bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode)
{
ioBlockVar = FindShaderIOBlockVariable(interfaceBlock->name(), mInputVaryings);
}
else if (qualifier == EvqPerVertexOut)
{
TIntermSymbol *symbolNode = blockNode->getAsSymbolNode();
ASSERT(symbolNode);
recordBuiltInVaryingUsed(symbolNode->variable(), &mPerVertexOutAdded, mOutputVaryings);
ioBlockVar = FindShaderIOBlockVariable(interfaceBlock->name(), mOutputVaryings);
}
else if (IsVaryingOut(qualifier))
{
ioBlockVar = FindShaderIOBlockVariable(interfaceBlock->name(), mOutputVaryings);
......@@ -1177,12 +1279,14 @@ void CollectVariables(TIntermBlock *root,
ShHashFunction64 hashFunction,
TSymbolTable *symbolTable,
GLenum shaderType,
const TExtensionBehavior &extensionBehavior)
const TExtensionBehavior &extensionBehavior,
const ShBuiltInResources &resources,
int tessControlShaderOutputVertices)
{
CollectVariablesTraverser collect(attributes, outputVariables, uniforms, inputVaryings,
outputVaryings, sharedVariables, uniformBlocks,
shaderStorageBlocks, hashFunction, symbolTable, shaderType,
extensionBehavior);
CollectVariablesTraverser collect(
attributes, outputVariables, uniforms, inputVaryings, outputVaryings, sharedVariables,
uniformBlocks, shaderStorageBlocks, hashFunction, symbolTable, shaderType,
extensionBehavior, resources, tessControlShaderOutputVertices);
root->traverse(&collect);
}
......
......@@ -30,7 +30,9 @@ void CollectVariables(TIntermBlock *root,
ShHashFunction64 hashFunction,
TSymbolTable *symbolTable,
GLenum shaderType,
const TExtensionBehavior &extensionBehavior);
const TExtensionBehavior &extensionBehavior,
const ShBuiltInResources &resources,
int tessControlShaderOutputVertices);
} // namespace sh
#endif // COMPILER_TRANSLATOR_COLLECTVARIABLES_H_
......@@ -16,6 +16,7 @@
#include "compiler/translator/IsASTDepthBelowLimit.h"
#include "compiler/translator/OutputTree.h"
#include "compiler/translator/ParseContext.h"
#include "compiler/translator/ValidateBarrierFunctionCall.h"
#include "compiler/translator/ValidateClipCullDistance.h"
#include "compiler/translator/ValidateLimitations.h"
#include "compiler/translator/ValidateMaxParameters.h"
......@@ -305,6 +306,11 @@ TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
mGeometryShaderInvocations(0),
mGeometryShaderInputPrimitiveType(EptUndefined),
mGeometryShaderOutputPrimitiveType(EptUndefined),
mTessControlShaderOutputVertices(0),
mTessEvaluationShaderInputPrimitiveType(EtetUndefined),
mTessEvaluationShaderInputVertexSpacingType(EtetUndefined),
mTessEvaluationShaderInputOrderingType(EtetUndefined),
mTessEvaluationShaderInputPointType(EtetUndefined),
mCompileOptions(0)
{}
......@@ -492,6 +498,20 @@ void TCompiler::setASTMetadata(const TParseContext &parseContext)
mGeometryShaderMaxVertices = parseContext.getGeometryShaderMaxVertices();
mGeometryShaderInvocations = parseContext.getGeometryShaderInvocations();
}
if (mShaderType == GL_TESS_CONTROL_SHADER_EXT)
{
mTessControlShaderOutputVertices = parseContext.getTessControlShaderOutputVertices();
}
if (mShaderType == GL_TESS_EVALUATION_SHADER_EXT)
{
mTessEvaluationShaderInputPrimitiveType =
parseContext.getTessEvaluationShaderInputPrimitiveType();
mTessEvaluationShaderInputVertexSpacingType =
parseContext.getTessEvaluationShaderInputVertexSpacingType();
mTessEvaluationShaderInputOrderingType =
parseContext.getTessEvaluationShaderInputOrderingType();
mTessEvaluationShaderInputPointType = parseContext.getTessEvaluationShaderInputPointType();
}
}
unsigned int TCompiler::getSharedMemorySize() const
......@@ -620,6 +640,11 @@ bool TCompiler::checkAndSimplifyAST(TIntermBlock *root,
return false;
}
if (mShaderType == GL_TESS_CONTROL_SHADER && !ValidateBarrierFunctionCall(root, &mDiagnostics))
{
return false;
}
// Fail compilation if precision emulation not supported.
if (getResources().WEBGL_debug_shader_precision && getPragma().debugShaderPrecision &&
!EmulatePrecision::SupportedInLanguage(mOutputType))
......@@ -816,7 +841,7 @@ bool TCompiler::checkAndSimplifyAST(TIntermBlock *root,
CollectVariables(root, &mAttributes, &mOutputVariables, &mUniforms, &mInputVaryings,
&mOutputVaryings, &mSharedVariables, &mUniformBlocks,
&mShaderStorageBlocks, mResources.HashFunction, &mSymbolTable, mShaderType,
mExtensionBehavior);
mExtensionBehavior, mResources, mTessControlShaderOutputVertices);
collectInterfaceBlocks();
mVariablesCollected = true;
if ((compileOptions & SH_USE_UNUSED_STANDARD_SHARED_BLOCKS) != 0)
......@@ -1115,6 +1140,7 @@ void TCompiler::setResourceString()
<< ":EXT_shadow_samplers:" << mResources.EXT_shadow_samplers
<< ":OES_shader_multisample_interpolation:" << mResources.OES_shader_multisample_interpolation
<< ":OES_shader_image_atomic:" << mResources.OES_shader_image_atomic
<< ":EXT_tessellation_shader:" << mResources.EXT_tessellation_shader
<< ":OES_texture_buffer:" << mResources.OES_texture_buffer
<< ":EXT_texture_buffer:" << mResources.EXT_texture_buffer
<< ":OES_sample_variables:" << mResources.OES_sample_variables
......@@ -1160,7 +1186,25 @@ void TCompiler::setResourceString()
<< ":MaxGeometryImageUniforms:" << mResources.MaxGeometryImageUniforms
<< ":MaxClipDistances" << mResources.MaxClipDistances
<< ":MaxCullDistances" << mResources.MaxCullDistances
<< ":MaxCombinedClipAndCullDistances" << mResources.MaxCombinedClipAndCullDistances;
<< ":MaxCombinedClipAndCullDistances" << mResources.MaxCombinedClipAndCullDistances
<< ":MaxTessControlInputComponents:" << mResources.MaxTessControlInputComponents
<< ":MaxTessControlOutputComponents:" << mResources.MaxTessControlOutputComponents
<< ":MaxTessControlTextureImageUnits:" << mResources.MaxTessControlTextureImageUnits
<< ":MaxTessControlUniformComponents:" << mResources.MaxTessControlUniformComponents
<< ":MaxTessControlTotalOutputComponents:" << mResources.MaxTessControlTotalOutputComponents
<< ":MaxTessControlImageUniforms:" << mResources.MaxTessControlImageUniforms
<< ":MaxTessControlAtomicCounters:" << mResources.MaxTessControlAtomicCounters
<< ":MaxTessControlAtomicCounterBuffers:" << mResources.MaxTessControlAtomicCounterBuffers
<< ":MaxTessPatchComponents:" << mResources.MaxTessPatchComponents
<< ":MaxPatchVertices:" << mResources.MaxPatchVertices
<< ":MaxTessGenLevel:" << mResources.MaxTessGenLevel
<< ":MaxTessEvaluationInputComponents:" << mResources.MaxTessEvaluationInputComponents
<< ":MaxTessEvaluationOutputComponents:" << mResources.MaxTessEvaluationOutputComponents
<< ":MaxTessEvaluationTextureImageUnits:" << mResources.MaxTessEvaluationTextureImageUnits
<< ":MaxTessEvaluationUniformComponents:" << mResources.MaxTessEvaluationUniformComponents
<< ":MaxTessEvaluationImageUniforms:" << mResources.MaxTessEvaluationImageUniforms
<< ":MaxTessEvaluationAtomicCounters:" << mResources.MaxTessEvaluationAtomicCounters
<< ":MaxTessEvaluationAtomicCounterBuffers:" << mResources.MaxTessEvaluationAtomicCounterBuffers;
// clang-format on
mBuiltInResourcesString = strstream.str();
......@@ -1222,6 +1266,12 @@ void TCompiler::clearResults()
mGeometryShaderInvocations = 0;
mGeometryShaderMaxVertices = -1;
mTessControlShaderOutputVertices = 0;
mTessEvaluationShaderInputPrimitiveType = EtetUndefined;
mTessEvaluationShaderInputVertexSpacingType = EtetUndefined;
mTessEvaluationShaderInputOrderingType = EtetUndefined;
mTessEvaluationShaderInputPointType = EtetUndefined;
mBuiltInFunctionEmulator.cleanup();
mNameMap.clear();
......
......@@ -148,6 +148,25 @@ class TCompiler : public TShHandleBase
}
unsigned int getStructSize(const ShaderVariable &var) const;
int getTessControlShaderOutputVertices() const { return mTessControlShaderOutputVertices; }
TLayoutTessEvaluationType getTessEvaluationShaderInputPrimitiveType() const
{
return mTessEvaluationShaderInputPrimitiveType;
}
TLayoutTessEvaluationType getTessEvaluationShaderInputVertexSpacingType() const
{
return mTessEvaluationShaderInputVertexSpacingType;
}
TLayoutTessEvaluationType getTessEvaluationShaderInputOrderingType() const
{
return mTessEvaluationShaderInputOrderingType;
}
TLayoutTessEvaluationType getTessEvaluationShaderInputPointType() const
{
return mTessEvaluationShaderInputPointType;
}
unsigned int getSharedMemorySize() const;
sh::GLenum getShaderType() const { return mShaderType; }
......@@ -299,6 +318,13 @@ class TCompiler : public TShHandleBase
TLayoutPrimitiveType mGeometryShaderInputPrimitiveType;
TLayoutPrimitiveType mGeometryShaderOutputPrimitiveType;
// tesssellation shader parameters
int mTessControlShaderOutputVertices;
TLayoutTessEvaluationType mTessEvaluationShaderInputPrimitiveType;
TLayoutTessEvaluationType mTessEvaluationShaderInputVertexSpacingType;
TLayoutTessEvaluationType mTessEvaluationShaderInputOrderingType;
TLayoutTessEvaluationType mTessEvaluationShaderInputPointType;
// name hashing.
NameMap mNameMap;
......
......@@ -1404,7 +1404,8 @@ void TOutputGLSLBase::declareInterfaceBlock(const TType &type)
const TFieldList &fields = interfaceBlock->fields();
for (const TField *field : fields)
{
if (!IsShaderIoBlock(type.getQualifier()))
if (!IsShaderIoBlock(type.getQualifier()) && type.getQualifier() != EvqPatchIn &&
type.getQualifier() != EvqPatchOut)
{
writeFieldLayoutQualifier(field);
}
......@@ -1473,6 +1474,40 @@ void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out,
}
}
void WriteTessControlShaderLayoutQualifiers(TInfoSinkBase &out, int inputVertices)
{
if (inputVertices != 0)
{
out << "layout (vertices = " << inputVertices << ") out;\n";
}
}
void WriteTessEvaluationShaderLayoutQualifiers(TInfoSinkBase &out,
sh::TLayoutTessEvaluationType inputPrimitive,
sh::TLayoutTessEvaluationType inputVertexSpacing,
sh::TLayoutTessEvaluationType inputOrdering,
sh::TLayoutTessEvaluationType inputPoint)
{
if (inputPrimitive != EtetUndefined)
{
out << "layout (";
out << getTessEvaluationShaderTypeString(inputPrimitive);
if (inputVertexSpacing != EtetUndefined)
{
out << ", " << getTessEvaluationShaderTypeString(inputVertexSpacing);
}
if (inputOrdering != EtetUndefined)
{
out << ", " << getTessEvaluationShaderTypeString(inputOrdering);
}
if (inputPoint != EtetUndefined)
{
out << ", " << getTessEvaluationShaderTypeString(inputPoint);
}
out << ") in;\n";
}
}
// If SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS is enabled, layout qualifiers are spilled whenever
// variables with specified layout qualifiers are copied. Additional checks are needed against the
// type and storage qualifier of the variable to verify that layout qualifiers have to be outputted.
......
......@@ -129,6 +129,14 @@ void WriteGeometryShaderLayoutQualifiers(TInfoSinkBase &out,
sh::TLayoutPrimitiveType outputPrimitive,
int maxVertices);
void WriteTessControlShaderLayoutQualifiers(TInfoSinkBase &out, int inputVertices);
void WriteTessEvaluationShaderLayoutQualifiers(TInfoSinkBase &out,
sh::TLayoutTessEvaluationType inputPrimitive,
sh::TLayoutTessEvaluationType inputVertexSpacing,
sh::TLayoutTessEvaluationType inputOrdering,
sh::TLayoutTessEvaluationType inputPoint);
bool NeedsToWriteLayoutQualifier(const TType &type);
} // namespace sh
......
......@@ -241,6 +241,12 @@ TParseContext::TParseContext(TSymbolTable &symt,
mMaxGeometryShaderInvocations(resources.MaxGeometryShaderInvocations),
mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices),
mGeometryInputArraySize(0),
mMaxPatchVertices(resources.MaxPatchVertices),
mTessControlShaderOutputVertices(0),
mTessEvaluationShaderInputPrimitiveType(EtetUndefined),
mTessEvaluationShaderInputVertexSpacingType(EtetUndefined),
mTessEvaluationShaderInputOrderingType(EtetUndefined),
mTessEvaluationShaderInputPointType(EtetUndefined),
mFunctionBodyNewScope(false),
mOutputType(outputType)
{}
......@@ -551,6 +557,8 @@ bool TParseContext::checkCanBeLValue(const TSourceLoc &line, const char *op, TIn
case EvqFragmentIn:
case EvqVertexIn:
case EvqGeometryIn:
case EvqTessControlIn:
case EvqTessEvaluationIn:
case EvqFlatIn:
case EvqNoPerspectiveIn:
case EvqSmoothIn:
......@@ -1163,7 +1171,10 @@ bool TParseContext::checkIsValidTypeAndQualifierForArray(const TSourceLoc &index
// as arrays (GL_EXT_geometry_shader section 7.4.1).
if (mShaderVersion >= 300 && elementType.getBasicType() == EbtStruct &&
sh::IsVarying(elementType.qualifier) &&
!IsGeometryShaderInput(mShaderType, elementType.qualifier))
!IsGeometryShaderInput(mShaderType, elementType.qualifier) &&
!IsTessellationControlShaderInput(mShaderType, elementType.qualifier) &&
!IsTessellationEvaluationShaderInput(mShaderType, elementType.qualifier) &&
!IsTessellationControlShaderOutput(mShaderType, elementType.qualifier))
{
TInfoSinkBase typeString;
typeString << TType(elementType);
......@@ -1199,7 +1210,6 @@ void TParseContext::checkCanBeDeclaredWithoutInitializer(const TSourceLoc &line,
error(line, "variables with qualifier 'const' must be initialized", identifier);
}
}
// This will make the type sized if it isn't sized yet.
checkIsNotUnsizedArray(line, "implicitly sized arrays need to be initialized", identifier,
type);
}
......@@ -1744,7 +1754,7 @@ void TParseContext::checkBindingIsValid(const TSourceLoc &identifierLocation, co
}
}
void TParseContext::checkLayoutQualifierSupported(const TSourceLoc &location,
bool TParseContext::checkLayoutQualifierSupported(const TSourceLoc &location,
const ImmutableString &layoutQualifierName,
int versionRequired)
{
......@@ -1752,7 +1762,9 @@ void TParseContext::checkLayoutQualifierSupported(const TSourceLoc &location,
if (mShaderVersion < versionRequired)
{
error(location, "invalid layout qualifier: not supported", layoutQualifierName);
return false;
}
return true;
}
bool TParseContext::checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location,
......@@ -2487,8 +2499,9 @@ void TParseContext::checkInputOutputTypeIsValidES3(const TQualifier qualifier,
bool typeContainsIntegers =
(type.getBasicType() == EbtInt || type.getBasicType() == EbtUInt ||
type.isStructureContainingType(EbtInt) || type.isStructureContainingType(EbtUInt));
bool extendedShaderTypes =
mShaderVersion == 320 || isExtensionEnabled(TExtension::EXT_geometry_shader);
bool extendedShaderTypes = mShaderVersion == 320 ||
isExtensionEnabled(TExtension::EXT_geometry_shader) ||
isExtensionEnabled(TExtension::EXT_tessellation_shader);
if (typeContainsIntegers && qualifier != EvqFlatIn && qualifier != EvqFlatOut &&
(!extendedShaderTypes || mShaderType == GL_FRAGMENT_SHADER))
{
......@@ -2645,6 +2658,85 @@ void TParseContext::checkGeometryShaderInputAndSetArraySize(const TSourceLoc &lo
}
}
void TParseContext::checkTessellationShaderUnsizedArraysAndSetSize(const TSourceLoc &location,
const ImmutableString &token,
TType *type)
{
TQualifier qualifier = type->getQualifier();
if (!IsTessellationControlShaderOutput(mShaderType, qualifier) &&
!IsTessellationControlShaderInput(mShaderType, qualifier) &&
!IsTessellationEvaluationShaderInput(mShaderType, qualifier))
{
return;
}
// Such variables must be declared as arrays or inside output blocks declared as arrays.
if (!type->isArray())
{
error(location, "Tessellation interface variables must be declared as an array", token);
return;
}
// If a size is specified, it must match the maximum patch size.
unsigned int outermostSize = type->getOutermostArraySize();
if (outermostSize == 0u)
{
switch (qualifier)
{
case EvqTessControlIn:
case EvqTessEvaluationIn:
case EvqFlatIn:
case EvqCentroidIn:
case EvqSmoothIn:
// Declaring an array size is optional. If no size is specified, it will be taken
// from the implementation-dependent maximum patch size (gl_MaxPatchVertices).
ASSERT(mMaxPatchVertices > 0);
type->sizeOutermostUnsizedArray(mMaxPatchVertices);
break;
case EvqTessControlOut:
case EvqFlatOut:
case EvqCentroidOut:
case EvqSmoothOut:
// Declaring an array size is optional. If no size is specified, it will be taken
// from output patch size declared in the shader.
type->sizeOutermostUnsizedArray(mTessControlShaderOutputVertices);
break;
default:
UNREACHABLE();
break;
}
}
else
{
if (IsTessellationControlShaderInput(mShaderType, qualifier) ||
IsTessellationEvaluationShaderInput(mShaderType, qualifier))
{
if (outermostSize != static_cast<unsigned int>(mMaxPatchVertices))
{
error(
location,
"If a size is specified for a tessellation control or evaluation user-defined "
"input variable, it must match the maximum patch size (gl_MaxPatchVertices).",
token);
}
}
else if (IsTessellationControlShaderOutput(mShaderType, qualifier))
{
if (outermostSize != static_cast<unsigned int>(mTessControlShaderOutputVertices) &&
mTessControlShaderOutputVertices != 0)
{
error(location,
"If a size is specified for a tessellation control user-defined per-vertex "
"output variable, it must match the the number of vertices in the output "
"patch.",
token);
}
}
return;
}
}
TIntermDeclaration *TParseContext::parseSingleDeclaration(
TPublicType &publicType,
const TSourceLoc &identifierOrTypeLocation,
......@@ -2675,6 +2767,7 @@ TIntermDeclaration *TParseContext::parseSingleDeclaration(
}
checkGeometryShaderInputAndSetArraySize(identifierOrTypeLocation, identifier, type);
checkTessellationShaderUnsizedArraysAndSetSize(identifierOrTypeLocation, identifier, type);
declarationQualifierErrorCheck(publicType.qualifier, publicType.layoutQualifier,
identifierOrTypeLocation);
......@@ -2751,6 +2844,7 @@ TIntermDeclaration *TParseContext::parseSingleArrayDeclaration(
checkArrayOfArraysInOut(indexLocation, elementType, *arrayType);
checkGeometryShaderInputAndSetArraySize(indexLocation, identifier, arrayType);
checkTessellationShaderUnsizedArraysAndSetSize(indexLocation, identifier, arrayType);
checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, arrayType);
......@@ -2915,6 +3009,7 @@ void TParseContext::parseDeclarator(TPublicType &publicType,
TType *type = new TType(publicType);
checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier, type);
checkTessellationShaderUnsizedArraysAndSetSize(identifierLocation, identifier, type);
checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, type);
......@@ -2957,6 +3052,7 @@ void TParseContext::parseArrayDeclarator(TPublicType &elementType,
arrayType->makeArrays(arraySizes);
checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier, arrayType);
checkTessellationShaderUnsizedArraysAndSetSize(identifierLocation, identifier, arrayType);
checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, arrayType);
......@@ -3227,6 +3323,89 @@ bool TParseContext::parseGeometryShaderOutputLayoutQualifier(const TTypeQualifie
return true;
}
bool TParseContext::parseTessControlShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier)
{
ASSERT(typeQualifier.qualifier == EvqTessControlOut);
const TLayoutQualifier &layoutQualifier = typeQualifier.layoutQualifier;
if (layoutQualifier.vertices == 0)
{
error(typeQualifier.line, "No vertices specified", "layout");
return false;
}
// Set mTessControlShaderOutputVertices if exists
if (mTessControlShaderOutputVertices == 0)
{
mTessControlShaderOutputVertices = layoutQualifier.vertices;
}
else
{
error(typeQualifier.line, "Duplicated vertices specified", "layout");
}
return true;
}
bool TParseContext::parseTessEvaluationShaderInputLayoutQualifier(
const TTypeQualifier &typeQualifier)
{
ASSERT(typeQualifier.qualifier == EvqTessEvaluationIn);
const TLayoutQualifier &layoutQualifier = typeQualifier.layoutQualifier;
// Set mTessEvaluationShaderInputPrimitiveType if exists
if (layoutQualifier.tesPrimitiveType != EtetUndefined)
{
if (mTessEvaluationShaderInputPrimitiveType == EtetUndefined)
{
mTessEvaluationShaderInputPrimitiveType = layoutQualifier.tesPrimitiveType;
}
else
{
error(typeQualifier.line, "Duplicated primitive type declaration", "layout");
}
}
// Set mTessEvaluationShaderVertexSpacingType if exists
if (layoutQualifier.tesVertexSpacingType != EtetUndefined)
{
if (mTessEvaluationShaderInputVertexSpacingType == EtetUndefined)
{
mTessEvaluationShaderInputVertexSpacingType = layoutQualifier.tesVertexSpacingType;
}
else
{
error(typeQualifier.line, "Duplicated vertex spacing declaration", "layout");
}
}
// Set mTessEvaluationShaderInputOrderingType if exists
if (layoutQualifier.tesOrderingType != EtetUndefined)
{
if (mTessEvaluationShaderInputOrderingType == EtetUndefined)
{
mTessEvaluationShaderInputOrderingType = layoutQualifier.tesOrderingType;
}
else
{
error(typeQualifier.line, "Duplicated ordering declaration", "layout");
}
}
// Set mTessEvaluationShaderInputPointType if exists
if (layoutQualifier.tesPointType != EtetUndefined)
{
if (mTessEvaluationShaderInputPointType == EtetUndefined)
{
mTessEvaluationShaderInputPointType = layoutQualifier.tesPointType;
}
else
{
error(typeQualifier.line, "Duplicated point type declaration", "layout");
}
}
return true;
}
void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder)
{
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(mDiagnostics);
......@@ -3377,7 +3556,8 @@ void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &type
if (mShaderVersion < 310)
{
error(typeQualifier.line,
"in type qualifier without variable declaration supported in GLSL ES 3.10 only",
"in type qualifier without variable declaration supported in GLSL ES 3.10 and "
"after",
"layout");
return;
}
......@@ -3393,6 +3573,34 @@ void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &type
mEarlyFragmentTestsSpecified = true;
}
else if (typeQualifier.qualifier == EvqTessControlOut)
{
if (mShaderVersion < 310)
{
error(typeQualifier.line, "out type qualifier supported in GLSL ES 3.10 and after",
"layout");
return;
}
if (!parseTessControlShaderOutputLayoutQualifier(typeQualifier))
{
return;
}
}
else if (typeQualifier.qualifier == EvqTessEvaluationIn)
{
if (mShaderVersion < 310)
{
error(typeQualifier.line, "in type qualifier supported in GLSL ES 3.10 and after",
"layout");
return;
}
if (!parseTessEvaluationShaderInputLayoutQualifier(typeQualifier))
{
return;
}
}
else
{
if (!checkWorkGroupSizeIsNotSpecified(typeQualifier.line, layoutQualifier))
......@@ -3409,7 +3617,7 @@ void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &type
if (mShaderVersion < 300)
{
error(typeQualifier.line, "layout qualifiers supported in GLSL ES 3.00 and above",
error(typeQualifier.line, "layout qualifiers supported in GLSL ES 3.00 and after",
"layout");
return;
}
......@@ -3902,6 +4110,26 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
"3.10",
getQualifierString(typeQualifier.qualifier));
}
else if (typeQualifier.qualifier == EvqPatchOut)
{
if ((!isExtensionEnabled(TExtension::EXT_tessellation_shader) && mShaderVersion < 320) ||
mShaderType != GL_TESS_CONTROL_SHADER)
{
error(typeQualifier.line,
"invalid qualifier: 'patch out' requires a tessellation control shader",
getQualifierString(typeQualifier.qualifier));
}
}
else if (typeQualifier.qualifier == EvqPatchIn)
{
if ((!isExtensionEnabled(TExtension::EXT_tessellation_shader) && mShaderVersion < 320) ||
mShaderType != GL_TESS_EVALUATION_SHADER)
{
error(typeQualifier.line,
"invalid qualifier: 'patch in' requires a tessellation evaluation shader",
getQualifierString(typeQualifier.qualifier));
}
}
else if (typeQualifier.qualifier != EvqUniform && typeQualifier.qualifier != EvqBuffer)
{
if (isShaderIoBlock)
......@@ -3910,9 +4138,14 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
!isExtensionEnabled(TExtension::EXT_shader_io_blocks))
{
error(typeQualifier.line,
"invalid qualifier: interface blocks need shader io block extension",
"invalid qualifier: shader IO blocks need shader io block extension",
getQualifierString(typeQualifier.qualifier));
}
if (mShaderType == GL_TESS_CONTROL_SHADER && arraySizes == nullptr)
{
error(typeQualifier.line, "type must be an array", blockName);
}
}
else
{
......@@ -3999,7 +4232,8 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
typeQualifier.layoutQualifier.earlyFragmentTests);
TLayoutQualifier blockLayoutQualifier = typeQualifier.layoutQualifier;
if (!IsShaderIoBlock(typeQualifier.qualifier))
if (!IsShaderIoBlock(typeQualifier.qualifier) && typeQualifier.qualifier != EvqPatchIn &&
typeQualifier.qualifier != EvqPatchOut)
{
checkLocationIsNotSpecified(typeQualifier.line, blockLayoutQualifier);
}
......@@ -4082,7 +4316,8 @@ TIntermDeclaration *TParseContext::addInterfaceBlock(
case EvqFlat:
case EvqNoPerspective:
case EvqCentroid:
if (!IsShaderIoBlock(typeQualifier.qualifier))
if (!IsShaderIoBlock(typeQualifier.qualifier) &&
typeQualifier.qualifier != EvqPatchIn && typeQualifier.qualifier != EvqPatchOut)
{
error(field->line(), "invalid qualifier on interface block member",
getQualifierString(qualifier));
......@@ -4291,14 +4526,28 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
if (baseExpression->getQualifier() == EvqPerVertexIn)
{
ASSERT(mShaderType == GL_GEOMETRY_SHADER_EXT);
if (mGeometryShaderInputPrimitiveType == EptUndefined)
if (mGeometryShaderInputPrimitiveType == EptUndefined &&
mShaderType == GL_GEOMETRY_SHADER_EXT)
{
error(location, "missing input primitive declaration before indexing gl_in.", "[");
return CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
}
}
if (mShaderType == GL_TESS_CONTROL_SHADER &&
IsTessellationControlShaderOutput(mShaderType, baseExpression->getQualifier()))
{
const TIntermSymbol *intermSymbol = indexExpression->getAsSymbolNode();
if (!intermSymbol || intermSymbol->getName() != "gl_InvocationID")
{
error(location,
"tessellation-control per-vertex output l-value must be indexed with "
"gl_InvocationID",
"[");
return CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
}
}
TIntermConstantUnion *indexConstantUnion = indexExpression->getAsConstantUnion();
// ES3.2 or ES3.1's EXT_gpu_shader5 allow dynamically uniform expressions to be used as indices
......@@ -4335,7 +4584,9 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
break;
default:
// It's ok for shader I/O blocks to be dynamically indexed
if (!IsShaderIoBlock(baseExpression->getQualifier()))
if (!IsShaderIoBlock(baseExpression->getQualifier()) &&
baseExpression->getQualifier() != EvqPatchIn &&
baseExpression->getQualifier() != EvqPatchOut)
{
// We can reach here only in error cases.
ASSERT(mDiagnostics->numErrors() > 0);
......@@ -4729,49 +4980,88 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const ImmutableString &qual
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.imageInternalFormat = EiifR32UI;
}
else if (qualifierType == "points" && mShaderType == GL_GEOMETRY_SHADER_EXT &&
checkCanUseExtension(qualifierTypeLine, TExtension::EXT_geometry_shader))
else if (mShaderType == GL_GEOMETRY_SHADER_EXT &&
checkCanUseExtension(qualifierTypeLine, TExtension::EXT_geometry_shader) &&
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310))
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptPoints;
}
else if (qualifierType == "lines" && mShaderType == GL_GEOMETRY_SHADER_EXT &&
checkCanUseExtension(qualifierTypeLine, TExtension::EXT_geometry_shader))
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptLines;
}
else if (qualifierType == "lines_adjacency" && mShaderType == GL_GEOMETRY_SHADER_EXT &&
checkCanUseExtension(qualifierTypeLine, TExtension::EXT_geometry_shader))
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptLinesAdjacency;
}
else if (qualifierType == "triangles" && mShaderType == GL_GEOMETRY_SHADER_EXT &&
checkCanUseExtension(qualifierTypeLine, TExtension::EXT_geometry_shader))
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptTriangles;
}
else if (qualifierType == "triangles_adjacency" && mShaderType == GL_GEOMETRY_SHADER_EXT &&
checkCanUseExtension(qualifierTypeLine, TExtension::EXT_geometry_shader))
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptTrianglesAdjacency;
}
else if (qualifierType == "line_strip" && mShaderType == GL_GEOMETRY_SHADER_EXT &&
checkCanUseExtension(qualifierTypeLine, TExtension::EXT_geometry_shader))
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptLineStrip;
if (qualifierType == "points")
{
qualifier.primitiveType = EptPoints;
}
else if (qualifierType == "lines")
{
qualifier.primitiveType = EptLines;
}
else if (qualifierType == "lines_adjacency")
{
qualifier.primitiveType = EptLinesAdjacency;
}
else if (qualifierType == "triangles")
{
qualifier.primitiveType = EptTriangles;
}
else if (qualifierType == "triangles_adjacency")
{
qualifier.primitiveType = EptTrianglesAdjacency;
}
else if (qualifierType == "line_strip")
{
qualifier.primitiveType = EptLineStrip;
}
else if (qualifierType == "triangle_strip")
{
qualifier.primitiveType = EptTriangleStrip;
}
else
{
error(qualifierTypeLine, "invalid layout qualifier", qualifierType);
}
}
else if (qualifierType == "triangle_strip" && mShaderType == GL_GEOMETRY_SHADER_EXT &&
checkCanUseExtension(qualifierTypeLine, TExtension::EXT_geometry_shader))
else if (mShaderType == GL_TESS_EVALUATION_SHADER_EXT &&
checkCanUseExtension(qualifierTypeLine, TExtension::EXT_tessellation_shader) &&
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310))
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
qualifier.primitiveType = EptTriangleStrip;
if (qualifierType == "triangles")
{
qualifier.tesPrimitiveType = EtetTriangles;
}
else if (qualifierType == "quads")
{
qualifier.tesPrimitiveType = EtetQuads;
}
else if (qualifierType == "isolines")
{
qualifier.tesPrimitiveType = EtetIsolines;
}
else if (qualifierType == "equal_spacing")
{
qualifier.tesVertexSpacingType = EtetEqualSpacing;
}
else if (qualifierType == "fractional_even_spacing")
{
qualifier.tesVertexSpacingType = EtetFractionalEvenSpacing;
}
else if (qualifierType == "fractional_odd_spacing")
{
qualifier.tesVertexSpacingType = EtetFractionalOddSpacing;
}
else if (qualifierType == "cw")
{
qualifier.tesOrderingType = EtetCw;
}
else if (qualifierType == "ccw")
{
qualifier.tesOrderingType = EtetCcw;
}
else if (qualifierType == "point_mode")
{
qualifier.tesPointType = EtetPointMode;
}
else
{
error(qualifierTypeLine, "invalid layout qualifier", qualifierType);
}
}
else
{
error(qualifierTypeLine, "invalid layout qualifier", qualifierType);
......@@ -4853,6 +5143,23 @@ void TParseContext::parseMaxVertices(int intValue,
}
}
void TParseContext::parseVertices(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *vertices)
{
if (intValue < 1 || intValue > mMaxPatchVertices)
{
error(intValueLine,
"out of range : vertices must be in the range of [1, gl_MaxPatchVertices]",
intValueString.c_str());
}
else
{
*vertices = intValue;
}
}
void TParseContext::parseIndexLayoutQualifier(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
......@@ -4961,6 +5268,11 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const ImmutableString &qual
{
parseIndexLayoutQualifier(intValue, intValueLine, intValueString, &qualifier.index);
}
else if (qualifierType == "vertices" && mShaderType == GL_TESS_CONTROL_SHADER_EXT &&
checkCanUseExtension(qualifierTypeLine, TExtension::EXT_tessellation_shader))
{
parseVertices(intValue, intValueLine, intValueString, &qualifier.vertices);
}
else
{
error(qualifierTypeLine, "invalid layout qualifier", qualifierType);
......@@ -5022,10 +5334,18 @@ TStorageQualifierWrapper *TParseContext::parseInQualifier(const TSourceLoc &loc)
{
return new TStorageQualifierWrapper(EvqComputeIn, loc);
}
case GL_GEOMETRY_SHADER_EXT:
case GL_GEOMETRY_SHADER:
{
return new TStorageQualifierWrapper(EvqGeometryIn, loc);
}
case GL_TESS_CONTROL_SHADER:
{
return new TStorageQualifierWrapper(EvqTessControlIn, loc);
}
case GL_TESS_EVALUATION_SHADER:
{
return new TStorageQualifierWrapper(EvqTessEvaluationIn, loc);
}
default:
{
UNREACHABLE();
......@@ -5067,6 +5387,14 @@ TStorageQualifierWrapper *TParseContext::parseOutQualifier(const TSourceLoc &loc
{
return new TStorageQualifierWrapper(EvqGeometryOut, loc);
}
case GL_TESS_CONTROL_SHADER_EXT:
{
return new TStorageQualifierWrapper(EvqTessControlOut, loc);
}
case GL_TESS_EVALUATION_SHADER_EXT:
{
return new TStorageQualifierWrapper(EvqTessEvaluationOut, loc);
}
default:
{
UNREACHABLE();
......
......@@ -177,7 +177,7 @@ class TParseContext : angle::NonCopyable
// Done only for empty declarations.
void emptyDeclarationErrorCheck(const TType &type, const TSourceLoc &location);
void checkLayoutQualifierSupported(const TSourceLoc &location,
bool checkLayoutQualifierSupported(const TSourceLoc &location,
const ImmutableString &layoutQualifierName,
int versionRequired);
bool checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location,
......@@ -380,6 +380,10 @@ class TParseContext : angle::NonCopyable
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *numMaxVertices);
void parseVertices(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
int *numVertices);
void parseIndexLayoutQualifier(int intValue,
const TSourceLoc &intValueLine,
const std::string &intValueString,
......@@ -465,6 +469,23 @@ class TParseContext : angle::NonCopyable
{
return mGeometryShaderOutputPrimitiveType;
}
int getTessControlShaderOutputVertices() const { return mTessControlShaderOutputVertices; }
TLayoutTessEvaluationType getTessEvaluationShaderInputPrimitiveType() const
{
return mTessEvaluationShaderInputPrimitiveType;
}
TLayoutTessEvaluationType getTessEvaluationShaderInputVertexSpacingType() const
{
return mTessEvaluationShaderInputVertexSpacingType;
}
TLayoutTessEvaluationType getTessEvaluationShaderInputOrderingType() const
{
return mTessEvaluationShaderInputOrderingType;
}
TLayoutTessEvaluationType getTessEvaluationShaderInputPointType() const
{
return mTessEvaluationShaderInputPointType;
}
ShShaderOutput getOutputType() const { return mOutputType; }
......@@ -574,6 +595,11 @@ class TParseContext : angle::NonCopyable
const ImmutableString &token,
TType *type);
// Similar, for tessellation shaders.
void checkTessellationShaderUnsizedArraysAndSetSize(const TSourceLoc &location,
const ImmutableString &token,
TType *type);
// Will size any unsized array type so unsized arrays won't need to be taken into account
// further along the line in parsing.
void checkIsNotUnsizedArray(const TSourceLoc &line,
......@@ -616,6 +642,9 @@ class TParseContext : angle::NonCopyable
bool parseGeometryShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier);
void setGeometryShaderInputArraySize(unsigned int inputArraySize, const TSourceLoc &line);
bool parseTessControlShaderOutputLayoutQualifier(const TTypeQualifier &typeQualifier);
bool parseTessEvaluationShaderInputLayoutQualifier(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.
......@@ -681,6 +710,13 @@ class TParseContext : angle::NonCopyable
int mMaxGeometryShaderMaxVertices;
unsigned int mGeometryInputArraySize;
int mMaxPatchVertices;
int mTessControlShaderOutputVertices;
TLayoutTessEvaluationType mTessEvaluationShaderInputPrimitiveType;
TLayoutTessEvaluationType mTessEvaluationShaderInputVertexSpacingType;
TLayoutTessEvaluationType mTessEvaluationShaderInputOrderingType;
TLayoutTessEvaluationType mTessEvaluationShaderInputPointType;
// Track when we add new scope for func body in ESSL 1.00 spec
bool mFunctionBodyNewScope;
......
......@@ -372,10 +372,14 @@ bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storag
break;
case EvqVertexOut:
case EvqGeometryOut:
case EvqTessControlOut:
case EvqTessEvaluationOut:
*joinedQualifier = EvqSmoothOut;
break;
case EvqFragmentIn:
case EvqGeometryIn:
case EvqTessControlIn:
case EvqTessEvaluationIn:
*joinedQualifier = EvqSmoothIn;
break;
default:
......@@ -392,10 +396,14 @@ bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storag
break;
case EvqVertexOut:
case EvqGeometryOut:
case EvqTessControlOut:
case EvqTessEvaluationOut:
*joinedQualifier = EvqFlatOut;
break;
case EvqFragmentIn:
case EvqGeometryIn:
case EvqTessControlIn:
case EvqTessEvaluationIn:
*joinedQualifier = EvqFlatIn;
break;
default:
......@@ -412,10 +420,14 @@ bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storag
break;
case EvqVertexOut:
case EvqGeometryOut:
case EvqTessControlOut:
case EvqTessEvaluationOut:
*joinedQualifier = EvqNoPerspectiveOut;
break;
case EvqFragmentIn:
case EvqGeometryIn:
case EvqTessControlIn:
case EvqTessEvaluationIn:
*joinedQualifier = EvqNoPerspectiveIn;
break;
default:
......@@ -429,10 +441,14 @@ bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storag
{
case EvqVertexOut:
case EvqGeometryOut:
case EvqTessControlOut:
case EvqTessEvaluationOut:
*joinedQualifier = EvqCentroidOut;
break;
case EvqFragmentIn:
case EvqGeometryIn:
case EvqTessControlIn:
case EvqTessEvaluationIn:
*joinedQualifier = EvqCentroidIn;
break;
default:
......@@ -446,10 +462,14 @@ bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storag
{
case EvqVertexOut:
case EvqGeometryOut:
case EvqTessControlOut:
case EvqTessEvaluationOut:
*joinedQualifier = EvqSampleOut;
break;
case EvqFragmentIn:
case EvqGeometryIn:
case EvqTessControlIn:
case EvqTessEvaluationIn:
*joinedQualifier = EvqSampleIn;
break;
default:
......@@ -457,6 +477,21 @@ bool JoinVariableStorageQualifier(TQualifier *joinedQualifier, TQualifier storag
}
break;
}
case EvqPatch:
{
switch (storageQualifier)
{
case EvqTessControlOut:
*joinedQualifier = EvqPatchOut;
break;
case EvqTessEvaluationIn:
*joinedQualifier = EvqPatchIn;
break;
default:
return false;
}
break;
}
default:
return false;
}
......@@ -757,6 +792,48 @@ TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier,
joinedQualifier.maxVertices = rightQualifier.maxVertices;
}
if (rightQualifier.tesPrimitiveType != EtetUndefined)
{
if (joinedQualifier.tesPrimitiveType == EtetUndefined)
{
joinedQualifier.tesPrimitiveType = rightQualifier.tesPrimitiveType;
}
}
if (rightQualifier.tesVertexSpacingType != EtetUndefined)
{
if (joinedQualifier.tesVertexSpacingType == EtetUndefined)
{
joinedQualifier.tesVertexSpacingType = rightQualifier.tesVertexSpacingType;
}
}
if (rightQualifier.tesOrderingType != EtetUndefined)
{
if (joinedQualifier.tesOrderingType == EtetUndefined)
{
joinedQualifier.tesOrderingType = rightQualifier.tesOrderingType;
}
}
if (rightQualifier.tesPointType != EtetUndefined)
{
if (joinedQualifier.tesPointType == EtetUndefined)
{
joinedQualifier.tesPointType = rightQualifier.tesPointType;
}
}
if (rightQualifier.vertices != 0)
{
if (joinedQualifier.vertices != 0 && joinedQualifier.vertices != rightQualifier.vertices)
{
diagnostics->error(rightQualifierLocation,
"Cannot have multiple different vertices specifiers", "vertices");
}
joinedQualifier.vertices = rightQualifier.vertices;
}
if (rightQualifier.index != -1)
{
if (joinedQualifier.index != -1)
......
......@@ -102,6 +102,36 @@ GLenum GetGeometryShaderPrimitiveTypeEnum(sh::TLayoutPrimitiveType primitiveType
}
}
GLenum GetTessellationShaderTypeEnum(sh::TLayoutTessEvaluationType type)
{
switch (type)
{
case EtetTriangles:
return GL_TRIANGLES;
case EtetQuads:
return GL_QUADS;
case EtetIsolines:
return GL_ISOLINES;
case EtetEqualSpacing:
return GL_EQUAL;
case EtetFractionalEvenSpacing:
return GL_FRACTIONAL_EVEN;
case EtetFractionalOddSpacing:
return GL_FRACTIONAL_ODD;
case EtetCw:
return GL_CW;
case EtetCcw:
return GL_CCW;
case EtetPointMode:
return GL_TESS_GEN_POINT_MODE;
case EtetUndefined:
default:
UNREACHABLE();
return GL_INVALID_VALUE;
}
}
} // anonymous namespace
//
......@@ -713,6 +743,50 @@ bool HasValidGeometryShaderMaxVertices(const ShHandle handle)
return compiler->getGeometryShaderMaxVertices() >= 0;
}
bool HasValidTessGenMode(const ShHandle handle)
{
ASSERT(handle);
TShHandleBase *base = static_cast<TShHandleBase *>(handle);
TCompiler *compiler = base->getAsCompiler();
ASSERT(compiler);
return compiler->getTessEvaluationShaderInputPrimitiveType() != EtetUndefined;
}
bool HasValidTessGenSpacing(const ShHandle handle)
{
ASSERT(handle);
TShHandleBase *base = static_cast<TShHandleBase *>(handle);
TCompiler *compiler = base->getAsCompiler();
ASSERT(compiler);
return compiler->getTessEvaluationShaderInputVertexSpacingType() != EtetUndefined;
}
bool HasValidTessGenVertexOrder(const ShHandle handle)
{
ASSERT(handle);
TShHandleBase *base = static_cast<TShHandleBase *>(handle);
TCompiler *compiler = base->getAsCompiler();
ASSERT(compiler);
return compiler->getTessEvaluationShaderInputOrderingType() != EtetUndefined;
}
bool HasValidTessGenPointMode(const ShHandle handle)
{
ASSERT(handle);
TShHandleBase *base = static_cast<TShHandleBase *>(handle);
TCompiler *compiler = base->getAsCompiler();
ASSERT(compiler);
return compiler->getTessEvaluationShaderInputPointType() != EtetUndefined;
}
GLenum GetGeometryShaderInputPrimitiveType(const ShHandle handle)
{
ASSERT(handle);
......@@ -759,6 +833,62 @@ int GetGeometryShaderMaxVertices(const ShHandle handle)
return maxVertices;
}
int GetTessControlShaderVertices(const ShHandle handle)
{
ASSERT(handle);
TShHandleBase *base = static_cast<TShHandleBase *>(handle);
TCompiler *compiler = base->getAsCompiler();
ASSERT(compiler);
int vertices = compiler->getTessControlShaderOutputVertices();
return vertices;
}
GLenum GetTessGenMode(const ShHandle handle)
{
ASSERT(handle);
TShHandleBase *base = static_cast<TShHandleBase *>(handle);
TCompiler *compiler = base->getAsCompiler();
ASSERT(compiler);
return GetTessellationShaderTypeEnum(compiler->getTessEvaluationShaderInputPrimitiveType());
}
GLenum GetTessGenSpacing(const ShHandle handle)
{
ASSERT(handle);
TShHandleBase *base = static_cast<TShHandleBase *>(handle);
TCompiler *compiler = base->getAsCompiler();
ASSERT(compiler);
return GetTessellationShaderTypeEnum(compiler->getTessEvaluationShaderInputVertexSpacingType());
}
GLenum GetTessGenVertexOrder(const ShHandle handle)
{
ASSERT(handle);
TShHandleBase *base = static_cast<TShHandleBase *>(handle);
TCompiler *compiler = base->getAsCompiler();
ASSERT(compiler);
return GetTessellationShaderTypeEnum(compiler->getTessEvaluationShaderInputOrderingType());
}
GLenum GetTessGenPointMode(const ShHandle handle)
{
ASSERT(handle);
TShHandleBase *base = static_cast<TShHandleBase *>(handle);
TCompiler *compiler = base->getAsCompiler();
ASSERT(compiler);
return GetTessellationShaderTypeEnum(compiler->getTessEvaluationShaderInputPointType());
}
unsigned int GetShaderSharedMemorySize(const ShHandle handle)
{
ASSERT(handle);
......
......@@ -51,6 +51,7 @@ ShaderVariable::ShaderVariable(GLenum typeIn)
interpolation(INTERPOLATION_SMOOTH),
isInvariant(false),
isShaderIOBlock(false),
isPatch(false),
texelFetchStaticUse(false),
flattenedOffsetInParentArrays(-1)
{}
......@@ -87,6 +88,7 @@ ShaderVariable::ShaderVariable(const ShaderVariable &other)
interpolation(other.interpolation),
isInvariant(other.isInvariant),
isShaderIOBlock(other.isShaderIOBlock),
isPatch(other.isPatch),
texelFetchStaticUse(other.texelFetchStaticUse),
flattenedOffsetInParentArrays(other.flattenedOffsetInParentArrays)
{}
......@@ -117,6 +119,7 @@ ShaderVariable &ShaderVariable::operator=(const ShaderVariable &other)
interpolation = other.interpolation;
isInvariant = other.isInvariant;
isShaderIOBlock = other.isShaderIOBlock;
isPatch = other.isPatch;
texelFetchStaticUse = other.texelFetchStaticUse;
return *this;
}
......@@ -133,7 +136,7 @@ bool ShaderVariable::operator==(const ShaderVariable &other) const
offset != other.offset || readonly != other.readonly || writeonly != other.writeonly ||
index != other.index || yuv != other.yuv || interpolation != other.interpolation ||
isInvariant != other.isInvariant || isShaderIOBlock != other.isShaderIOBlock ||
texelFetchStaticUse != other.texelFetchStaticUse)
isPatch != other.isPatch || texelFetchStaticUse != other.texelFetchStaticUse)
{
return false;
}
......@@ -444,7 +447,7 @@ bool ShaderVariable::isSameVaryingAtLinkTime(const ShaderVariable &other, int sh
return ShaderVariable::isSameVariableAtLinkTime(other, false, false) &&
InterpolationTypesMatch(interpolation, other.interpolation) &&
(shaderVersion >= 300 || isInvariant == other.isInvariant) &&
location == other.location &&
(isPatch == other.isPatch) && location == other.location &&
(isSameNameAtLinkTime(other) || (shaderVersion >= 310 && location >= 0));
}
......
......@@ -39,6 +39,10 @@ bool CheckShaderType(Shader expected, GLenum actual)
return actual == GL_GEOMETRY_SHADER;
case Shader::GEOMETRY_EXT:
return actual == GL_GEOMETRY_SHADER_EXT;
case Shader::TESS_CONTROL_EXT:
return actual == GL_TESS_CONTROL_SHADER_EXT;
case Shader::TESS_EVALUATION_EXT:
return actual == GL_TESS_EVALUATION_SHADER_EXT;
case Shader::NOT_COMPUTE:
return actual != GL_COMPUTE_SHADER;
default:
......@@ -411,6 +415,8 @@ void TSymbolTable::initializeBuiltIns(sh::GLenum type,
case GL_VERTEX_SHADER:
case GL_COMPUTE_SHADER:
case GL_GEOMETRY_SHADER_EXT:
case GL_TESS_CONTROL_SHADER_EXT:
case GL_TESS_EVALUATION_SHADER_EXT:
setDefaultPrecision(EbtInt, EbpHigh);
setDefaultPrecision(EbtFloat, EbpHigh);
break;
......
......@@ -171,6 +171,8 @@ constexpr ImmutableString kFlippedFragCoordName = ImmutableString("flippedFragC
constexpr gl::ShaderMap<const char *> kDefaultUniformNames = {
{gl::ShaderType::Vertex, vk::kDefaultUniformsNameVS},
{gl::ShaderType::TessControl, vk::kDefaultUniformsNameTCS},
{gl::ShaderType::TessEvaluation, vk::kDefaultUniformsNameTES},
{gl::ShaderType::Geometry, vk::kDefaultUniformsNameGS},
{gl::ShaderType::Fragment, vk::kDefaultUniformsNameFS},
{gl::ShaderType::Compute, vk::kDefaultUniformsNameCS},
......@@ -1156,6 +1158,22 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
break;
}
case gl::ShaderType::TessControl:
{
WriteTessControlShaderLayoutQualifiers(sink, getTessControlShaderOutputVertices());
break;
}
case gl::ShaderType::TessEvaluation:
{
WriteTessEvaluationShaderLayoutQualifiers(
sink, getTessEvaluationShaderInputPrimitiveType(),
getTessEvaluationShaderInputVertexSpacingType(),
getTessEvaluationShaderInputOrderingType(),
getTessEvaluationShaderInputPointType());
break;
}
case gl::ShaderType::Compute:
{
EmitWorkGroupSizeGLSL(*this, sink);
......
//
// Copyright 2020 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.
//
// ValidateBarrierFunctionCalls:
// Runs compilation checks related to the "barrier built-in function.
#include "compiler/translator/ValidateBarrierFunctionCall.h"
#include "compiler/translator/Diagnostics.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
namespace sh
{
namespace
{
class Traverser : public TIntermTraverser
{
public:
Traverser(TDiagnostics *diagnostics)
: TIntermTraverser(true, false, true), mDiagnostics(diagnostics)
{}
bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
{
if (!node->getFunction()->isMain())
{
return false;
}
mInMain = visit == PreVisit;
return true;
}
bool visitBranch(Visit visit, TIntermBranch *branch) override
{
if (branch->getFlowOp() == EOpReturn)
{
mSeenReturn = true;
}
return true;
}
bool visitAggregate(Visit visit, TIntermAggregate *node) override
{
if (node->getOp() != EOpBarrier)
{
return true;
}
if (mSeenReturn)
{
mDiagnostics->error(node->getLine(),
"barrier() may not be called at any point after a return statement "
"in the function main().",
"barrier");
mValid = false;
return false;
}
// TODO(anglebug.com/5557): Determine if we should check loops as well.
if (mBranchCount > 0)
{
mDiagnostics->error(
node->getLine(),
"barrier() may not be called in potentially divergent flow control.", "barrier");
mValid = false;
return false;
}
return true;
}
bool visitIfElse(Visit visit, TIntermIfElse *node) override
{
mBranchCount += ((visit == PreVisit) ? 1 : -1);
return true;
}
bool valid() const { return mValid; }
private:
TDiagnostics *mDiagnostics = nullptr;
bool mInMain = false;
bool mSeenReturn = false;
bool mValid = true;
uint32_t mBranchCount = 0;
};
} // anonymous namespace
bool ValidateBarrierFunctionCall(TIntermBlock *root, TDiagnostics *diagnostics)
{
Traverser traverser(diagnostics);
root->traverse(&traverser);
return traverser.valid();
}
} // namespace sh
//
// Copyright 2020 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.
//
// ValidateBarrierFunctionCalls:
// Runs compilation checks related to the "barrier built-in function.
#ifndef COMPILER_TRANSLATOR_VALIDATEBARRIERFUNCTIONCALL_H_
#define COMPILER_TRANSLATOR_VALIDATEBARRIERFUNCTIONCALL_H_
#include "common/angleutils.h"
namespace sh
{
class TDiagnostics;
class TIntermBlock;
ANGLE_NO_DISCARD bool ValidateBarrierFunctionCall(TIntermBlock *root, TDiagnostics *diagnostics);
} // namespace sh
#endif // COMPILER_TRANSLATOR_VALIDATEBARRIERFUNCTIONCALL_H_
......@@ -116,6 +116,22 @@ int GetLocationCount(const TIntermSymbol *varying, bool ignoreVaryingArraySize)
return elementLocationCount * varyingType.getArraySizeProduct();
}
bool ShouldIgnoreVaryingArraySize(TQualifier qualifier, GLenum shaderType)
{
bool isVaryingIn = IsShaderIn(qualifier) && qualifier != EvqPatchIn;
switch (shaderType)
{
case GL_GEOMETRY_SHADER:
case GL_TESS_EVALUATION_SHADER:
return isVaryingIn;
case GL_TESS_CONTROL_SHADER:
return (IsShaderOut(qualifier) && qualifier != EvqPatchOut) || isVaryingIn;
default:
return false;
}
}
struct SymbolAndField
{
const TIntermSymbol *symbol;
......@@ -159,9 +175,9 @@ void MarkVaryingLocations(TDiagnostics *diagnostics,
using VaryingVector = std::vector<const TIntermSymbol *>;
void ValidateShaderInterface(TDiagnostics *diagnostics,
VaryingVector &varyingVector,
bool ignoreVaryingArraySize)
void ValidateShaderInterfaceAndAssignLocations(TDiagnostics *diagnostics,
const VaryingVector &varyingVector,
GLenum shaderType)
{
// Location conflicts can only happen when there are two or more varyings in varyingVector.
if (varyingVector.size() <= 1)
......@@ -176,6 +192,9 @@ void ValidateShaderInterface(TDiagnostics *diagnostics,
const int location = varyingType.getLayoutQualifier().location;
ASSERT(location >= 0);
bool ignoreVaryingArraySize =
ShouldIgnoreVaryingArraySize(varying->getQualifier(), shaderType);
// A varying is either:
//
// - A vector or matrix, which can take a number of contiguous locations
......@@ -318,9 +337,9 @@ void ValidateVaryingLocationsTraverser::validate(TDiagnostics *diagnostics)
{
ASSERT(diagnostics);
ValidateShaderInterface(diagnostics, mInputVaryingsWithLocation,
mShaderType == GL_GEOMETRY_SHADER_EXT);
ValidateShaderInterface(diagnostics, mOutputVaryingsWithLocation, false);
ValidateShaderInterfaceAndAssignLocations(diagnostics, mInputVaryingsWithLocation, mShaderType);
ValidateShaderInterfaceAndAssignLocations(diagnostics, mOutputVaryingsWithLocation,
mShaderType);
}
} // anonymous namespace
......@@ -329,8 +348,7 @@ unsigned int CalculateVaryingLocationCount(TIntermSymbol *varying, GLenum shader
{
const TType &varyingType = varying->getType();
const TQualifier qualifier = varyingType.getQualifier();
const bool isShaderIn = IsShaderIn(qualifier);
const bool ignoreVaryingArraySize = isShaderIn && shaderType == GL_GEOMETRY_SHADER_EXT;
const bool ignoreVaryingArraySize = ShouldIgnoreVaryingArraySize(qualifier, shaderType);
if (varyingType.isInterfaceBlock())
{
......
......@@ -868,7 +868,11 @@ storage_qualifier
$$ = new TStorageQualifierWrapper(EvqCentroid, @1);
}
| PATCH {
ES3_1_OR_NEWER("patch", @1, "storage qualifier");
if (context->getShaderVersion() < 320 &&
!context->checkCanUseExtension(@1, TExtension::EXT_tessellation_shader))
{
context->error(@1, "unsupported storage qualifier", "patch");
}
$$ = new TStorageQualifierWrapper(EvqPatch, @1);
}
| UNIFORM {
......
......@@ -752,19 +752,19 @@ static const yytype_uint16 yyrline[] = {
557, 561, 565, 569, 573, 580, 583, 589, 596, 603, 606, 609, 613, 617, 621, 625,
629, 636, 643, 646, 653, 661, 678, 688, 691, 697, 701, 705, 709, 716, 723, 726,
730, 734, 739, 746, 750, 754, 758, 763, 770, 774, 780, 783, 786, 796, 800, 807,
813, 819, 823, 827, 830, 833, 837, 845, 850, 854, 857, 860, 863, 866, 870, 874,
877, 881, 884, 887, 890, 893, 896, 900, 907, 914, 917, 920, 926, 933, 936, 942,
945, 948, 951, 957, 960, 967, 972, 979, 984, 995, 998, 1001, 1004, 1007, 1010, 1014,
1018, 1022, 1026, 1030, 1034, 1038, 1042, 1046, 1050, 1054, 1058, 1062, 1066, 1070, 1074, 1078,
1082, 1086, 1090, 1094, 1101, 1104, 1107, 1110, 1113, 1116, 1119, 1127, 1135, 1145, 1148, 1151,
1154, 1157, 1160, 1163, 1171, 1179, 1189, 1192, 1195, 1198, 1201, 1204, 1207, 1215, 1223, 1233,
1236, 1239, 1242, 1250, 1258, 1265, 1275, 1282, 1289, 1292, 1295, 1298, 1301, 1304, 1307, 1310,
1313, 1316, 1319, 1322, 1325, 1333, 1341, 1349, 1357, 1365, 1373, 1383, 1393, 1403, 1406, 1409,
1417, 1417, 1420, 1420, 1426, 1429, 1435, 1438, 1445, 1449, 1455, 1458, 1464, 1468, 1472, 1473,
1479, 1480, 1481, 1482, 1483, 1484, 1485, 1489, 1493, 1493, 1493, 1500, 1501, 1505, 1505, 1506,
1506, 1511, 1515, 1522, 1526, 1533, 1534, 1538, 1544, 1548, 1557, 1557, 1564, 1567, 1573, 1577,
1583, 1583, 1588, 1588, 1592, 1592, 1600, 1603, 1609, 1612, 1618, 1622, 1629, 1632, 1635, 1638,
1641, 1649, 1655, 1661, 1664, 1670, 1670};
813, 819, 823, 827, 830, 833, 837, 845, 850, 854, 857, 860, 863, 866, 870, 878,
881, 885, 888, 891, 894, 897, 900, 904, 911, 918, 921, 924, 930, 937, 940, 946,
949, 952, 955, 961, 964, 971, 976, 983, 988, 999, 1002, 1005, 1008, 1011, 1014, 1018,
1022, 1026, 1030, 1034, 1038, 1042, 1046, 1050, 1054, 1058, 1062, 1066, 1070, 1074, 1078, 1082,
1086, 1090, 1094, 1098, 1105, 1108, 1111, 1114, 1117, 1120, 1123, 1131, 1139, 1149, 1152, 1155,
1158, 1161, 1164, 1167, 1175, 1183, 1193, 1196, 1199, 1202, 1205, 1208, 1211, 1219, 1227, 1237,
1240, 1243, 1246, 1254, 1262, 1269, 1279, 1286, 1293, 1296, 1299, 1302, 1305, 1308, 1311, 1314,
1317, 1320, 1323, 1326, 1329, 1337, 1345, 1353, 1361, 1369, 1377, 1387, 1397, 1407, 1410, 1413,
1421, 1421, 1424, 1424, 1430, 1433, 1439, 1442, 1449, 1453, 1459, 1462, 1468, 1472, 1476, 1477,
1483, 1484, 1485, 1486, 1487, 1488, 1489, 1493, 1497, 1497, 1497, 1504, 1505, 1509, 1509, 1510,
1510, 1515, 1519, 1526, 1530, 1537, 1538, 1542, 1548, 1552, 1561, 1561, 1568, 1571, 1577, 1581,
1587, 1587, 1592, 1592, 1596, 1596, 1604, 1607, 1613, 1616, 1622, 1626, 1633, 1636, 1639, 1642,
1645, 1653, 1659, 1665, 1668, 1674, 1674};
#endif
#if YYDEBUG || YYERROR_VERBOSE || 0
......@@ -3670,7 +3670,11 @@ yyreduce:
case 142:
{
ES3_1_OR_NEWER("patch", (yylsp[0]), "storage qualifier");
if (context->getShaderVersion() < 320 &&
!context->checkCanUseExtension((yylsp[0]), TExtension::EXT_tessellation_shader))
{
context->error((yylsp[0]), "unsupported storage qualifier", "patch");
}
(yyval.interm.qualifierWrapper) = new TStorageQualifierWrapper(EvqPatch, (yylsp[0]));
}
......
......@@ -102,7 +102,8 @@ bool RemoveInactiveInterfaceVariablesTraverser::visitDeclaration(Visit visit,
// When a member has an explicit location, interface block should not be removed.
// If the member or interface would be removed, GetProgramResource could not return the
// location.
if (!IsShaderIoBlock(type.getQualifier()))
if (!IsShaderIoBlock(type.getQualifier()) && type.getQualifier() != EvqPatchIn &&
type.getQualifier() != EvqPatchOut)
{
removeDeclaration =
!IsVariableActive(mInterfaceBlocks, type.getInterfaceBlock()->name());
......
......@@ -26,7 +26,6 @@ namespace sh
namespace
{
bool IsInterpolationIn(TQualifier qualifier)
{
switch (qualifier)
......@@ -42,6 +41,20 @@ bool IsInterpolationIn(TQualifier qualifier)
}
}
bool IsInterpolationOut(TQualifier qualifier)
{
switch (qualifier)
{
case EvqSmoothOut:
case EvqFlatOut:
case EvqNoPerspectiveOut:
case EvqCentroidOut:
case EvqSampleOut:
return true;
default:
return false;
}
}
} // anonymous namespace
float NumericLexFloat32OutOfRangeToInfinity(const std::string &str)
......@@ -554,6 +567,7 @@ bool IsVaryingOut(TQualifier qualifier)
case EvqTessControlOut:
case EvqTessEvaluationOut:
case EvqSampleOut:
case EvqPatchOut:
return true;
default:
......@@ -577,6 +591,7 @@ bool IsVaryingIn(TQualifier qualifier)
case EvqTessControlIn:
case EvqTessEvaluationIn:
case EvqSampleIn:
case EvqPatchIn:
return true;
default:
......@@ -597,12 +612,34 @@ bool IsGeometryShaderInput(GLenum shaderType, TQualifier qualifier)
((shaderType == GL_GEOMETRY_SHADER_EXT) && IsInterpolationIn(qualifier));
}
bool IsTessellationControlShaderInput(GLenum shaderType, TQualifier qualifier)
{
return qualifier == EvqTessControlIn ||
((shaderType == GL_TESS_CONTROL_SHADER) && IsInterpolationIn(qualifier));
}
bool IsTessellationControlShaderOutput(GLenum shaderType, TQualifier qualifier)
{
return qualifier == EvqTessControlOut ||
((shaderType == GL_TESS_CONTROL_SHADER) && IsInterpolationOut(qualifier));
}
bool IsTessellationEvaluationShaderInput(GLenum shaderType, TQualifier qualifier)
{
return qualifier == EvqTessEvaluationIn ||
((shaderType == GL_TESS_EVALUATION_SHADER) && IsInterpolationIn(qualifier));
}
InterpolationType GetInterpolationType(TQualifier qualifier)
{
switch (qualifier)
{
case EvqFlatIn:
case EvqFlatOut:
// The auxiliary storage qualifier patch is not used for interpolation
// it is a compile-time error to use interpolation qualifiers with patch
case EvqPatchIn:
case EvqPatchOut:
return INTERPOLATION_FLAT;
case EvqNoPerspectiveIn:
......@@ -617,6 +654,10 @@ InterpolationType GetInterpolationType(TQualifier qualifier)
case EvqVaryingOut:
case EvqGeometryIn:
case EvqGeometryOut:
case EvqTessControlIn:
case EvqTessControlOut:
case EvqTessEvaluationIn:
case EvqTessEvaluationOut:
return INTERPOLATION_SMOOTH;
case EvqCentroidIn:
......
......@@ -55,6 +55,9 @@ bool IsVaryingIn(TQualifier qualifier);
bool IsVaryingOut(TQualifier qualifier);
bool IsVarying(TQualifier qualifier);
bool IsGeometryShaderInput(GLenum shaderType, TQualifier qualifier);
bool IsTessellationControlShaderInput(GLenum shaderType, TQualifier qualifier);
bool IsTessellationControlShaderOutput(GLenum shaderType, TQualifier qualifier);
bool IsTessellationEvaluationShaderInput(GLenum shaderType, TQualifier qualifier);
InterpolationType GetInterpolationType(TQualifier qualifier);
InterpolationType GetFieldInterpolationType(TQualifier qualifier);
......
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