Add compute shader compilation support in the glsl compiler
Support is added for compute shader compilation. There is a small
extension to the parser so that 'local_size_x = ', 'local_size_y = '
and 'local_size_z = ' are supported as layout qualifiers.
A few shader compilation tests are added and one which checks the AST
whether the layout qualifiers are properly parsed.
BUG=angleproject:1442
TEST=angle_unittests
TEST=angle_end2end_tests
Change-Id: I67283797d1cf13fa4ac47faa2a6e66d93a2db867
Reviewed-on: https://chromium-review.googlesource.com/362300
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/BaseTypes.h b/src/compiler/translator/BaseTypes.h
index cccde03..198ca36 100644
--- a/src/compiler/translator/BaseTypes.h
+++ b/src/compiler/translator/BaseTypes.h
@@ -7,6 +7,9 @@
#ifndef COMPILER_TRANSLATOR_BASETYPES_H_
#define COMPILER_TRANSLATOR_BASETYPES_H_
+#include <algorithm>
+#include <array>
+
#include "common/debug.h"
//
@@ -346,6 +349,9 @@
EvqFlatIn,
EvqCentroidIn, // Implies smooth
+ // GLSL ES 3.1 compute shader special variables
+ EvqComputeIn,
+
// end of list
EvqLast
};
@@ -365,12 +371,18 @@
EbsStd140
};
+using TLocalSize = std::array<int, 3>;
+
struct TLayoutQualifier
{
int location;
TLayoutMatrixPacking matrixPacking;
TLayoutBlockStorage blockStorage;
+ // Compute shader layout qualifiers.
+ // -1 means unspecified.
+ TLocalSize localSize;
+
static TLayoutQualifier create()
{
TLayoutQualifier layoutQualifier;
@@ -379,15 +391,66 @@
layoutQualifier.matrixPacking = EmpUnspecified;
layoutQualifier.blockStorage = EbsUnspecified;
+ layoutQualifier.localSize.fill(-1);
+
return layoutQualifier;
}
bool isEmpty() const
{
- return location == -1 && matrixPacking == EmpUnspecified && blockStorage == EbsUnspecified;
+ return location == -1 && matrixPacking == EmpUnspecified &&
+ blockStorage == EbsUnspecified && localSize[0] == -1 && localSize[1] == -1 &&
+ localSize[2] == -1;
+ }
+
+ bool isGroupSizeSpecified() const
+ {
+ return std::any_of(localSize.begin(), localSize.end(),
+ [](int value) { return value != -1; });
+ }
+
+ bool isCombinationValid() const
+ {
+ bool workSizeSpecified = isGroupSizeSpecified();
+ bool otherLayoutQualifiersSpecified =
+ (location != -1 || matrixPacking != EmpUnspecified || blockStorage != EbsUnspecified);
+
+ // we can have either the work group size specified, or the other layout qualifiers
+ return !(workSizeSpecified && otherLayoutQualifiersSpecified);
+ }
+
+ bool isLocalSizeEqual(const TLocalSize &localSizeIn) const
+ {
+ for (size_t i = 0u; i < localSize.size(); ++i)
+ {
+ bool result =
+ (localSize[i] == localSizeIn[i] || (localSize[i] == 1 && localSizeIn[i] == -1) ||
+ (localSize[i] == -1 && localSizeIn[i] == 1));
+ if (!result)
+ {
+ return false;
+ }
+ }
+ return true;
}
};
+inline const char *getLocalSizeString(size_t dimension)
+{
+ switch (dimension)
+ {
+ case 0u:
+ return "local_size_x";
+ case 1u:
+ return "local_size_y";
+ case 2u:
+ return "local_size_z";
+ default:
+ UNREACHABLE();
+ return "dimension out of bounds";
+ }
+}
+
//
// This is just for debug print out, carried along with the definitions above.
//
@@ -432,6 +495,7 @@
case EvqSmoothIn: return "smooth in";
case EvqFlatIn: return "flat in";
case EvqCentroidIn: return "smooth centroid in";
+ case EvqComputeIn: return "in";
default: UNREACHABLE(); return "unknown qualifier";
}
// clang-format on
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index f586c7f..1469a33 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -150,8 +150,10 @@
clampingStrategy(SH_CLAMP_WITH_CLAMP_INTRINSIC),
builtInFunctionEmulator(),
mSourcePath(NULL),
+ mComputeShaderLocalSizeDeclared(false),
mTemporaryIndex(0)
{
+ mComputeShaderLocalSize.fill(1);
}
TCompiler::~TCompiler()
@@ -250,6 +252,9 @@
mPragma = parseContext.pragma();
symbolTable.setGlobalInvariant(mPragma.stdgl.invariantAll);
+ mComputeShaderLocalSizeDeclared = parseContext.isComputeShaderLocalSizeDeclared();
+ mComputeShaderLocalSize = parseContext.getComputeShaderLocalSize();
+
root = parseContext.getTreeRoot();
root = intermediate.postProcess(root);
@@ -450,6 +455,10 @@
symbolTable.setDefaultPrecision(integer, EbpHigh);
symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
break;
+ case GL_COMPUTE_SHADER:
+ symbolTable.setDefaultPrecision(integer, EbpHigh);
+ symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
+ break;
default:
assert(false && "Language not supported");
}
diff --git a/src/compiler/translator/Compiler.h b/src/compiler/translator/Compiler.h
index 4657daa..b982556 100644
--- a/src/compiler/translator/Compiler.h
+++ b/src/compiler/translator/Compiler.h
@@ -85,6 +85,9 @@
int getShaderVersion() const { return shaderVersion; }
TInfoSink& getInfoSink() { return infoSink; }
+ bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
+ const TLocalSize &getComputeShaderLocalSize() { return mComputeShaderLocalSize; }
+
// Clears the results from the previous compilation.
void clearResults();
@@ -227,6 +230,10 @@
TInfoSink infoSink; // Output sink.
const char *mSourcePath; // Path of source file or NULL
+ // compute shader local group size
+ bool mComputeShaderLocalSizeDeclared;
+ TLocalSize mComputeShaderLocalSize;
+
// name hashing.
ShHashFunction64 hashFunction;
NameMap nameMap;
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index 87dc8d6..76bed47 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -682,7 +682,9 @@
symbolTable.insert(ESSL3_BUILTINS, new TVariable(NewPoolTString("gl_VertexID"),
TType(EbtInt, EbpHigh, EvqVertexID, 1)));
break;
-
+ case GL_COMPUTE_SHADER:
+ // TODO (mradev): add compute shader built-ins
+ break;
default:
assert(false && "Language not supported");
}
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index a96c4b6..33ec8c0 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -340,6 +340,9 @@
case EvqPointCoord:
message = "can't modify gl_PointCoord";
break;
+ case EvqComputeIn:
+ message = "can't modify work group size variable";
+ break;
default:
//
// Type that can't be written to?
@@ -1010,6 +1013,7 @@
case EvqAttribute:
case EvqVertexIn:
case EvqFragmentOut:
+ case EvqComputeIn:
if (publicType.type == EbtStruct)
{
error(identifierLocation, "cannot be used with a structure",
@@ -1068,6 +1072,35 @@
return false;
}
+void TParseContext::layoutSupportedErrorCheck(const TSourceLoc &location,
+ const TString &layoutQualifierName,
+ int versionRequired)
+{
+
+ if (mShaderVersion < versionRequired)
+ {
+ error(location, "invalid layout qualifier:", layoutQualifierName.c_str(), "not supported");
+ recover();
+ }
+}
+
+bool TParseContext::layoutWorkGroupSizeErrorCheck(const TSourceLoc &location,
+ const TLayoutQualifier &layoutQualifier)
+{
+ const TLocalSize &localSize = layoutQualifier.localSize;
+ for (size_t i = 0u; i < localSize.size(); ++i)
+ {
+ if (localSize[i] != -1)
+ {
+ error(location, "invalid layout qualifier:", getLocalSizeString(i),
+ "only valid when used with 'in' in a compute shader global layout declaration");
+ return true;
+ }
+ }
+
+ return false;
+}
+
bool TParseContext::functionCallLValueErrorCheck(const TFunction *fnCandidate,
TIntermAggregate *aggregate)
{
@@ -1132,6 +1165,23 @@
mDirectiveHandler.handlePragma(srcLoc, name, value, stdgl);
}
+TLocalSize TParseContext::getComputeShaderLocalSize() const
+{
+ TLocalSize result;
+ for (size_t i = 0u; i < result.size(); ++i)
+ {
+ if (mComputeShaderLocalSizeDeclared && mComputeShaderLocalSize[i] == -1)
+ {
+ result[i] = 1;
+ }
+ else
+ {
+ result[i] = mComputeShaderLocalSize[i];
+ }
+ }
+ return result;
+}
+
/////////////////////////////////////////////////////////////////////////////////
//
// Non-Errors.
@@ -1386,6 +1436,11 @@
returnType.invariant = invariant;
returnType.layoutQualifier = layoutQualifier;
+ if (layoutWorkGroupSizeErrorCheck(typeSpecifier.line, layoutQualifier))
+ {
+ recover();
+ }
+
if (mShaderVersion < 300)
{
if (typeSpecifier.array)
@@ -1422,6 +1477,12 @@
{
es3InputOutputTypeCheck(qualifier, typeSpecifier, typeSpecifier.line);
}
+ if (qualifier == EvqComputeIn)
+ {
+ error(typeSpecifier.line, "'in' can be only used to specify the local group size",
+ "in");
+ recover();
+ }
}
return returnType;
@@ -1890,13 +1951,6 @@
void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier)
{
- if (typeQualifier.qualifier != EvqUniform)
- {
- error(typeQualifier.line, "invalid qualifier:", getQualifierString(typeQualifier.qualifier),
- "global layout must be uniform");
- recover();
- return;
- }
const TLayoutQualifier layoutQualifier = typeQualifier.layoutQualifier;
@@ -1908,27 +1962,108 @@
return;
}
- if (mShaderVersion < 300)
+ if (!layoutQualifier.isCombinationValid())
{
- error(typeQualifier.line, "layout qualifiers supported in GLSL ES 3.00 only", "layout");
+ error(typeQualifier.line, "invalid combination:", "layout");
recover();
return;
}
- if (layoutLocationErrorCheck(typeQualifier.line, typeQualifier.layoutQualifier))
+ if (typeQualifier.qualifier == EvqComputeIn)
{
- recover();
- return;
- }
+ if (mComputeShaderLocalSizeDeclared &&
+ !layoutQualifier.isLocalSizeEqual(mComputeShaderLocalSize))
+ {
+ error(typeQualifier.line, "Work group size does not match the previous declaration",
+ "layout");
+ recover();
+ return;
+ }
- if (layoutQualifier.matrixPacking != EmpUnspecified)
- {
- mDefaultMatrixPacking = layoutQualifier.matrixPacking;
- }
+ if (mShaderVersion < 310)
+ {
+ error(typeQualifier.line, "in type qualifier supported in GLSL ES 3.10 only", "layout");
+ recover();
+ return;
+ }
- if (layoutQualifier.blockStorage != EbsUnspecified)
+ if (!layoutQualifier.isGroupSizeSpecified())
+ {
+ error(typeQualifier.line, "No local work group size specified", "layout");
+ recover();
+ return;
+ }
+
+ const TVariable *maxComputeWorkGroupSize = static_cast<const TVariable *>(
+ symbolTable.findBuiltIn("gl_MaxComputeWorkGroupSize", mShaderVersion));
+
+ const TConstantUnion *maxComputeWorkGroupSizeData =
+ maxComputeWorkGroupSize->getConstPointer();
+
+ for (size_t i = 0u; i < layoutQualifier.localSize.size(); ++i)
+ {
+ if (layoutQualifier.localSize[i] != -1)
+ {
+ mComputeShaderLocalSize[i] = layoutQualifier.localSize[i];
+ const int maxComputeWorkGroupSizeValue = maxComputeWorkGroupSizeData[i].getIConst();
+ if (mComputeShaderLocalSize[i] < 1 ||
+ mComputeShaderLocalSize[i] > maxComputeWorkGroupSizeValue)
+ {
+ std::stringstream errorMessageStream;
+ errorMessageStream << "Value must be at least 1 and no greater than "
+ << maxComputeWorkGroupSizeValue;
+ const std::string &errorMessage = errorMessageStream.str();
+
+ error(typeQualifier.line, "invalid value:", getLocalSizeString(i),
+ errorMessage.c_str());
+ recover();
+ return;
+ }
+ }
+ }
+
+ mComputeShaderLocalSizeDeclared = true;
+ }
+ else
{
- mDefaultBlockStorage = layoutQualifier.blockStorage;
+
+ if (layoutWorkGroupSizeErrorCheck(typeQualifier.line, typeQualifier.layoutQualifier))
+ {
+ recover();
+ return;
+ }
+
+ if (typeQualifier.qualifier != EvqUniform)
+ {
+ error(typeQualifier.line, "invalid qualifier:",
+ getQualifierString(typeQualifier.qualifier), "global layout must be uniform");
+ recover();
+ return;
+ }
+
+ if (mShaderVersion < 300)
+ {
+ error(typeQualifier.line, "layout qualifiers supported in GLSL ES 3.00 and above",
+ "layout");
+ recover();
+ return;
+ }
+
+ if (layoutLocationErrorCheck(typeQualifier.line, typeQualifier.layoutQualifier))
+ {
+ recover();
+ return;
+ }
+
+ if (layoutQualifier.matrixPacking != EmpUnspecified)
+ {
+ mDefaultMatrixPacking = layoutQualifier.matrixPacking;
+ }
+
+ if (layoutQualifier.blockStorage != EbsUnspecified)
+ {
+ mDefaultBlockStorage = layoutQualifier.blockStorage;
+ }
}
}
@@ -2463,6 +2598,11 @@
blockLayoutQualifier.blockStorage = mDefaultBlockStorage;
}
+ if (layoutWorkGroupSizeErrorCheck(nameLine, blockLayoutQualifier))
+ {
+ recover();
+ }
+
TSymbol *blockNameSymbol = new TInterfaceBlockName(&blockName);
if (!symbolTable.declare(blockNameSymbol))
{
@@ -3046,11 +3186,7 @@
TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
const TSourceLoc &qualifierTypeLine)
{
- TLayoutQualifier qualifier;
-
- qualifier.location = -1;
- qualifier.matrixPacking = EmpUnspecified;
- qualifier.blockStorage = EbsUnspecified;
+ TLayoutQualifier qualifier = TLayoutQualifier::create();
if (qualifierType == "shared")
{
@@ -3087,25 +3223,34 @@
return qualifier;
}
+void TParseContext::parseLocalSize(const TString &qualifierType,
+ const TSourceLoc &qualifierTypeLine,
+ int intValue,
+ const TSourceLoc &intValueLine,
+ const std::string &intValueString,
+ size_t index,
+ TLocalSize *localSize)
+{
+ layoutSupportedErrorCheck(qualifierTypeLine, qualifierType, 310);
+ if (intValue < 1)
+ {
+ std::string errorMessage = std::string(getLocalSizeString(index)) + " must be positive";
+ error(intValueLine, "out of range:", intValueString.c_str(), errorMessage.c_str());
+ recover();
+ }
+ (*localSize)[index] = intValue;
+}
+
TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierType,
const TSourceLoc &qualifierTypeLine,
- const TString &intValueString,
int intValue,
const TSourceLoc &intValueLine)
{
- TLayoutQualifier qualifier;
+ TLayoutQualifier qualifier = TLayoutQualifier::create();
- qualifier.location = -1;
- qualifier.matrixPacking = EmpUnspecified;
- qualifier.blockStorage = EbsUnspecified;
+ std::string intValueString = Str(intValue);
- if (qualifierType != "location")
- {
- error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str(),
- "only location may have arguments");
- recover();
- }
- else
+ if (qualifierType == "location")
{
// must check that location is non-negative
if (intValue < 0)
@@ -3119,12 +3264,33 @@
qualifier.location = intValue;
}
}
+ else if (qualifierType == "local_size_x")
+ {
+ parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 0u,
+ &qualifier.localSize);
+ }
+ else if (qualifierType == "local_size_y")
+ {
+ parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 1u,
+ &qualifier.localSize);
+ }
+ else if (qualifierType == "local_size_z")
+ {
+ parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 2u,
+ &qualifier.localSize);
+ }
+ else
+ {
+ error(qualifierTypeLine, "invalid layout qualifier", qualifierType.c_str());
+ recover();
+ }
return qualifier;
}
TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualifier,
- TLayoutQualifier rightQualifier)
+ TLayoutQualifier rightQualifier,
+ const TSourceLoc &rightQualifierLocation)
{
TLayoutQualifier joinedQualifier = leftQualifier;
@@ -3141,6 +3307,22 @@
joinedQualifier.blockStorage = rightQualifier.blockStorage;
}
+ for (size_t i = 0u; i < rightQualifier.localSize.size(); ++i)
+ {
+ if (rightQualifier.localSize[i] != -1)
+ {
+ if (joinedQualifier.localSize[i] != -1 &&
+ joinedQualifier.localSize[i] != rightQualifier.localSize[i])
+ {
+ error(rightQualifierLocation,
+ "Cannot have multiple different work group size specifiers",
+ getLocalSizeString(i));
+ recover();
+ }
+ joinedQualifier.localSize[i] = rightQualifier.localSize[i];
+ }
+ }
+
return joinedQualifier;
}
@@ -3210,6 +3392,11 @@
recover();
}
+ if (layoutWorkGroupSizeErrorCheck(typeSpecifier.line, typeSpecifier.layoutQualifier))
+ {
+ recover();
+ }
+
for (unsigned int i = 0; i < fieldList->size(); ++i)
{
//
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 6973a2c..ce71488 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -65,8 +65,10 @@
mUsesFragColor(false),
mUsesSecondaryOutputs(false),
mMinProgramTexelOffset(resources.MinProgramTexelOffset),
- mMaxProgramTexelOffset(resources.MaxProgramTexelOffset)
+ mMaxProgramTexelOffset(resources.MaxProgramTexelOffset),
+ mComputeShaderLocalSizeDeclared(false)
{
+ mComputeShaderLocalSize.fill(-1);
}
const pp::Preprocessor &getPreprocessor() const { return mPreprocessor; }
@@ -114,6 +116,9 @@
void incrSwitchNestingLevel() { ++mSwitchNestingLevel; }
void decrSwitchNestingLevel() { --mSwitchNestingLevel; }
+ bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
+ TLocalSize getComputeShaderLocalSize() const;
+
// This method is guaranteed to succeed, even if no variable with 'name' exists.
const TVariable *getNamedVariable(const TSourceLoc &location, const TString *name, const TSymbol *symbol);
TIntermTyped *parseVariableIdentifier(const TSourceLoc &location,
@@ -149,6 +154,11 @@
bool extensionErrorCheck(const TSourceLoc &line, const TString&);
bool singleDeclarationErrorCheck(const TPublicType &publicType, const TSourceLoc &identifierLocation);
bool layoutLocationErrorCheck(const TSourceLoc &location, const TLayoutQualifier &layoutQualifier);
+ void layoutSupportedErrorCheck(const TSourceLoc &location,
+ const TString &layoutQualifierName,
+ int versionRequired);
+ bool layoutWorkGroupSizeErrorCheck(const TSourceLoc &location,
+ const TLayoutQualifier &layoutQualifier);
bool functionCallLValueErrorCheck(const TFunction *fnCandidate, TIntermAggregate *);
void es3InvariantErrorCheck(const TQualifier qualifier, const TSourceLoc &invariantLocation);
void es3InputOutputTypeCheck(const TQualifier qualifier,
@@ -279,14 +289,22 @@
TIntermTyped *arrayIndex,
const TSourceLoc& arrayIndexLine);
+ void parseLocalSize(const TString &qualifierType,
+ const TSourceLoc &qualifierTypeLine,
+ int intValue,
+ const TSourceLoc &intValueLine,
+ const std::string &intValueString,
+ size_t index,
+ TLocalSize *localSize);
TLayoutQualifier parseLayoutQualifier(
const TString &qualifierType, const TSourceLoc &qualifierTypeLine);
TLayoutQualifier parseLayoutQualifier(const TString &qualifierType,
const TSourceLoc &qualifierTypeLine,
- const TString &intValueString,
int intValue,
const TSourceLoc &intValueLine);
- TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier, TLayoutQualifier rightQualifier);
+ TLayoutQualifier joinLayoutQualifiers(TLayoutQualifier leftQualifier,
+ TLayoutQualifier rightQualifier,
+ const TSourceLoc &rightQualifierLocation);
TPublicType joinInterpolationQualifiers(const TSourceLoc &interpolationLoc, TQualifier interpolationQualifier,
const TSourceLoc &storageLoc, TQualifier storageQualifier);
@@ -396,6 +414,10 @@
// gl_Secondary FragColor or both.
int mMinProgramTexelOffset;
int mMaxProgramTexelOffset;
+
+ // keep track of local group size declared in layout. It should be declared only once.
+ bool mComputeShaderLocalSizeDeclared;
+ TLocalSize mComputeShaderLocalSize;
};
int PaParseStrings(
diff --git a/src/compiler/translator/ShaderLang.cpp b/src/compiler/translator/ShaderLang.cpp
index c000598..4cbf2e5 100644
--- a/src/compiler/translator/ShaderLang.cpp
+++ b/src/compiler/translator/ShaderLang.cpp
@@ -358,6 +358,17 @@
return GetShaderVariables<sh::InterfaceBlock>(handle);
}
+std::array<int, 3> ShGetComputeShaderLocalGroupSize(const ShHandle handle)
+{
+ ASSERT(handle);
+
+ TShHandleBase *base = static_cast<TShHandleBase *>(handle);
+ TCompiler *compiler = base->getAsCompiler();
+ ASSERT(compiler);
+
+ return compiler->getComputeShaderLocalSize();
+}
+
bool ShCheckVariablesWithinPackingLimits(int maxVectors,
const std::vector<sh::ShaderVariable> &variables)
{
diff --git a/src/compiler/translator/TranslatorESSL.cpp b/src/compiler/translator/TranslatorESSL.cpp
index f3615a4..c04b13d 100644
--- a/src/compiler/translator/TranslatorESSL.cpp
+++ b/src/compiler/translator/TranslatorESSL.cpp
@@ -77,6 +77,13 @@
// Write array bounds clamping emulation if needed.
getArrayBoundsClamper().OutputClampingFunctionDefinition(sink);
+ if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared())
+ {
+ const TLocalSize &localSize = getComputeShaderLocalSize();
+ sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
+ << ", local_size_z=" << localSize[2] << ") in;\n";
+ }
+
// Write translated shader.
TOutputESSL outputESSL(sink, getArrayIndexClampingStrategy(), getHashFunction(), getNameMap(),
getSymbolTable(), shaderVer, precisionEmulation);
diff --git a/src/compiler/translator/TranslatorGLSL.cpp b/src/compiler/translator/TranslatorGLSL.cpp
index d9d6cdc..7174233 100644
--- a/src/compiler/translator/TranslatorGLSL.cpp
+++ b/src/compiler/translator/TranslatorGLSL.cpp
@@ -134,6 +134,13 @@
}
}
+ if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared())
+ {
+ const TLocalSize &localSize = getComputeShaderLocalSize();
+ sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
+ << ", local_size_z=" << localSize[2] << ") in;\n";
+ }
+
// Write translated shader.
TOutputGLSL outputGLSL(sink,
getArrayIndexClampingStrategy(),
diff --git a/src/compiler/translator/glslang.y b/src/compiler/translator/glslang.y
index 18aea4e..102e373 100644
--- a/src/compiler/translator/glslang.y
+++ b/src/compiler/translator/glslang.y
@@ -126,6 +126,20 @@
} \
}
+#define COMPUTE_ONLY(S, L) { \
+ if (context->getShaderType() != GL_COMPUTE_SHADER) { \
+ context->error(L, " supported in compute shaders only ", S); \
+ context->recover(); \
+ } \
+}
+
+#define NON_COMPUTE_ONLY(S, L) { \
+ if (context->getShaderType() != GL_VERTEX_SHADER && context->getShaderType() != GL_FRAGMENT_SHADER) { \
+ context->error(L, " supported in vertex and fragment shaders only ", S); \
+ context->recover(); \
+ } \
+}
+
#define ES2_ONLY(S, L) { \
if (context->getShaderVersion() != 100) { \
context->error(L, " supported in GLSL ES 1.00 only ", S); \
@@ -933,37 +947,35 @@
$$.qualifier = EvqConst;
}
| IN_QUAL {
- ES3_OR_NEWER("in", @1, "storage qualifier");
if (context->getShaderType() == GL_FRAGMENT_SHADER)
{
+ ES3_OR_NEWER("in", @1, "storage qualifier");
$$.qualifier = EvqFragmentIn;
}
+ else if (context->getShaderType() == GL_VERTEX_SHADER)
+ {
+ ES3_OR_NEWER("in", @1, "storage qualifier");
+ $$.qualifier = EvqVertexIn;
+ }
else
{
- $$.qualifier = EvqVertexIn;
+ $$.qualifier = EvqComputeIn;
}
}
| OUT_QUAL {
ES3_OR_NEWER("out", @1, "storage qualifier");
+ NON_COMPUTE_ONLY("out", @1);
$$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqVertexOut;
}
| CENTROID IN_QUAL {
ES3_OR_NEWER("centroid in", @1, "storage qualifier");
- if (context->getShaderType() == GL_VERTEX_SHADER)
- {
- context->error(@1, "invalid storage qualifier", "it is an error to use 'centroid in' in the vertex shader");
- context->recover();
- }
- $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqCentroidIn : EvqVertexIn;
+ FRAG_ONLY("centroid in", @1);
+ $$.qualifier = EvqCentroidIn;
}
| CENTROID OUT_QUAL {
ES3_OR_NEWER("centroid out", @1, "storage qualifier");
- if (context->getShaderType() == GL_FRAGMENT_SHADER)
- {
- context->error(@1, "invalid storage qualifier", "it is an error to use 'centroid out' in the fragment shader");
- context->recover();
- }
- $$.qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqCentroidOut;
+ VERTEX_ONLY("centroid out", @1);
+ $$.qualifier = EvqCentroidOut;
}
| UNIFORM {
if (context->globalErrorCheck(@1, context->symbolTable.atGlobalLevel(), "uniform"))
@@ -1018,7 +1030,7 @@
$$ = $1;
}
| layout_qualifier_id_list COMMA layout_qualifier_id {
- $$ = context->joinLayoutQualifiers($1, $3);
+ $$ = context->joinLayoutQualifiers($1, $3, @3);
}
;
@@ -1027,10 +1039,10 @@
$$ = context->parseLayoutQualifier(*$1.string, @1);
}
| IDENTIFIER EQUAL INTCONSTANT {
- $$ = context->parseLayoutQualifier(*$1.string, @1, *$3.string, $3.i, @3);
+ $$ = context->parseLayoutQualifier(*$1.string, @1, $3.i, @3);
}
| IDENTIFIER EQUAL UINTCONSTANT {
- $$ = context->parseLayoutQualifier(*$1.string, @1, *$3.string, $3.i, @3);
+ $$ = context->parseLayoutQualifier(*$1.string, @1, $3.i, @3);
}
;
diff --git a/src/compiler/translator/glslang_tab.cpp b/src/compiler/translator/glslang_tab.cpp
index 9db956c..101bcd3 100644
--- a/src/compiler/translator/glslang_tab.cpp
+++ b/src/compiler/translator/glslang_tab.cpp
@@ -370,6 +370,20 @@
} \
}
+#define COMPUTE_ONLY(S, L) { \
+ if (context->getShaderType() != GL_COMPUTE_SHADER) { \
+ context->error(L, " supported in compute shaders only ", S); \
+ context->recover(); \
+ } \
+}
+
+#define DRAW_ONLY(S, L) { \
+ if (context->getShaderType() != GL_VERTEX_SHADER && context->getShaderType() != GL_FRAGMENT_SHADER) { \
+ context->error(L, " supported in vertex and fragment shaders only ", S); \
+ context->recover(); \
+ } \
+}
+
#define ES2_ONLY(S, L) { \
if (context->getShaderVersion() != 100) { \
context->error(L, " supported in GLSL ES 1.00 only ", S); \
@@ -703,34 +717,34 @@
/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */
static const yytype_uint16 yyrline[] =
{
- 0, 221, 221, 222, 225, 235, 238, 243, 248, 253,
- 258, 264, 267, 270, 273, 276, 279, 285, 293, 304,
- 308, 316, 319, 325, 329, 336, 342, 351, 359, 365,
- 372, 382, 385, 388, 391, 401, 402, 403, 404, 412,
- 413, 416, 419, 426, 427, 430, 436, 437, 441, 448,
- 449, 452, 455, 458, 464, 465, 468, 474, 475, 482,
- 483, 490, 491, 498, 499, 505, 506, 512, 513, 519,
- 520, 526, 527, 535, 536, 537, 538, 542, 543, 544,
- 548, 552, 556, 560, 567, 570, 576, 584, 592, 595,
- 601, 612, 616, 620, 624, 631, 637, 640, 647, 655,
- 676, 685, 695, 723, 728, 738, 743, 753, 756, 759,
- 762, 768, 775, 778, 782, 786, 791, 796, 803, 807,
- 811, 815, 820, 825, 829, 836, 846, 852, 855, 861,
- 867, 874, 883, 893, 901, 904, 911, 915, 919, 924,
- 932, 935, 946, 950, 959, 968, 976, 986, 998, 1001,
- 1004, 1010, 1017, 1020, 1026, 1029, 1032, 1038, 1041, 1046,
- 1061, 1065, 1069, 1073, 1077, 1081, 1086, 1091, 1096, 1101,
- 1106, 1111, 1116, 1121, 1126, 1131, 1136, 1141, 1146, 1151,
- 1156, 1161, 1166, 1171, 1176, 1181, 1186, 1190, 1194, 1198,
- 1202, 1206, 1210, 1214, 1218, 1222, 1226, 1230, 1234, 1238,
- 1242, 1246, 1255, 1263, 1267, 1280, 1280, 1283, 1283, 1289,
- 1292, 1308, 1311, 1320, 1324, 1330, 1337, 1352, 1356, 1360,
- 1361, 1367, 1368, 1369, 1370, 1371, 1372, 1373, 1377, 1378,
- 1378, 1378, 1388, 1389, 1393, 1393, 1394, 1394, 1399, 1402,
- 1412, 1415, 1421, 1422, 1426, 1434, 1438, 1445, 1445, 1452,
- 1455, 1462, 1467, 1482, 1482, 1487, 1487, 1494, 1494, 1502,
- 1505, 1511, 1514, 1520, 1524, 1531, 1534, 1537, 1540, 1543,
- 1552, 1556, 1563, 1566, 1572, 1572
+ 0, 235, 235, 236, 239, 249, 252, 257, 262, 267,
+ 272, 278, 281, 284, 287, 290, 293, 299, 307, 318,
+ 322, 330, 333, 339, 343, 350, 356, 365, 373, 379,
+ 386, 396, 399, 402, 405, 415, 416, 417, 418, 426,
+ 427, 430, 433, 440, 441, 444, 450, 451, 455, 462,
+ 463, 466, 469, 472, 478, 479, 482, 488, 489, 496,
+ 497, 504, 505, 512, 513, 519, 520, 526, 527, 533,
+ 534, 540, 541, 549, 550, 551, 552, 556, 557, 558,
+ 562, 566, 570, 574, 581, 584, 590, 598, 606, 609,
+ 615, 626, 630, 634, 638, 645, 651, 654, 661, 669,
+ 690, 699, 709, 737, 742, 752, 757, 767, 770, 773,
+ 776, 782, 789, 792, 796, 800, 805, 810, 817, 821,
+ 825, 829, 834, 839, 843, 850, 860, 866, 869, 875,
+ 881, 888, 897, 907, 915, 918, 925, 929, 933, 938,
+ 946, 949, 965, 970, 975, 980, 988, 998, 1010, 1013,
+ 1016, 1022, 1029, 1032, 1038, 1041, 1044, 1050, 1053, 1058,
+ 1073, 1077, 1081, 1085, 1089, 1093, 1098, 1103, 1108, 1113,
+ 1118, 1123, 1128, 1133, 1138, 1143, 1148, 1153, 1158, 1163,
+ 1168, 1173, 1178, 1183, 1188, 1193, 1198, 1202, 1206, 1210,
+ 1214, 1218, 1222, 1226, 1230, 1234, 1238, 1242, 1246, 1250,
+ 1254, 1258, 1267, 1275, 1279, 1292, 1292, 1295, 1295, 1301,
+ 1304, 1320, 1323, 1332, 1336, 1342, 1349, 1364, 1368, 1372,
+ 1373, 1379, 1380, 1381, 1382, 1383, 1384, 1385, 1389, 1390,
+ 1390, 1390, 1400, 1401, 1405, 1405, 1406, 1406, 1411, 1414,
+ 1424, 1427, 1433, 1434, 1438, 1446, 1450, 1457, 1457, 1464,
+ 1467, 1474, 1479, 1494, 1494, 1499, 1499, 1506, 1506, 1514,
+ 1517, 1523, 1526, 1532, 1536, 1543, 1546, 1549, 1552, 1555,
+ 1564, 1568, 1575, 1578, 1584, 1584
};
#endif
@@ -3607,14 +3621,19 @@
case 141:
{
- ES3_OR_NEWER("in", (yylsp[0]), "storage qualifier");
if (context->getShaderType() == GL_FRAGMENT_SHADER)
{
+ ES3_OR_NEWER("in", (yylsp[0]), "storage qualifier");
(yyval.interm.type).qualifier = EvqFragmentIn;
}
+ else if (context->getShaderType() == GL_VERTEX_SHADER)
+ {
+ ES3_OR_NEWER("in", (yylsp[0]), "storage qualifier");
+ (yyval.interm.type).qualifier = EvqVertexIn;
+ }
else
{
- (yyval.interm.type).qualifier = EvqVertexIn;
+ (yyval.interm.type).qualifier = EvqComputeIn;
}
}
@@ -3624,6 +3643,7 @@
{
ES3_OR_NEWER("out", (yylsp[0]), "storage qualifier");
+ DRAW_ONLY("out", (yylsp[0]));
(yyval.interm.type).qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqVertexOut;
}
@@ -3633,12 +3653,8 @@
{
ES3_OR_NEWER("centroid in", (yylsp[-1]), "storage qualifier");
- if (context->getShaderType() == GL_VERTEX_SHADER)
- {
- context->error((yylsp[-1]), "invalid storage qualifier", "it is an error to use 'centroid in' in the vertex shader");
- context->recover();
- }
- (yyval.interm.type).qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqCentroidIn : EvqVertexIn;
+ FRAG_ONLY("centroid in", (yylsp[-1]));
+ (yyval.interm.type).qualifier = EvqCentroidIn;
}
break;
@@ -3647,12 +3663,8 @@
{
ES3_OR_NEWER("centroid out", (yylsp[-1]), "storage qualifier");
- if (context->getShaderType() == GL_FRAGMENT_SHADER)
- {
- context->error((yylsp[-1]), "invalid storage qualifier", "it is an error to use 'centroid out' in the fragment shader");
- context->recover();
- }
- (yyval.interm.type).qualifier = (context->getShaderType() == GL_FRAGMENT_SHADER) ? EvqFragmentOut : EvqCentroidOut;
+ VERTEX_ONLY("centroid out", (yylsp[-1]));
+ (yyval.interm.type).qualifier = EvqCentroidOut;
}
break;
@@ -3740,7 +3752,7 @@
case 153:
{
- (yyval.interm.layoutQualifier) = context->joinLayoutQualifiers((yyvsp[-2].interm.layoutQualifier), (yyvsp[0].interm.layoutQualifier));
+ (yyval.interm.layoutQualifier) = context->joinLayoutQualifiers((yyvsp[-2].interm.layoutQualifier), (yyvsp[0].interm.layoutQualifier), (yylsp[0]));
}
break;
@@ -3756,7 +3768,7 @@
case 155:
{
- (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), *(yyvsp[0].lex).string, (yyvsp[0].lex).i, (yylsp[0]));
+ (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), (yyvsp[0].lex).i, (yylsp[0]));
}
break;
@@ -3764,7 +3776,7 @@
case 156:
{
- (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), *(yyvsp[0].lex).string, (yyvsp[0].lex).i, (yylsp[0]));
+ (yyval.interm.layoutQualifier) = context->parseLayoutQualifier(*(yyvsp[-2].lex).string, (yylsp[-2]), (yyvsp[0].lex).i, (yylsp[0]));
}
break;