Commit 4cdac9eb by jchen10 Committed by Commit Bot

ES31: Add atomic counter for GLSL parsing

This makes shader compiler support the new basic type 'atomic_uint' and validate its layout qualifiers properly. BUG=angleproject:1729 TEST=angle_unittests:AtomicCounterTest angle_deqp_gles31_tests:dEQP-GLES31.functional.atomic_counter.layout.invalid* angle_deqp_gles31_tests:dEQP-GLES31.functional.debug.negative_coverage.*.atomic* Change-Id: Ia237eadf6ea72314f436a0abbb93a05598e71eba Reviewed-on: https://chromium-review.googlesource.com/500088 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 2cd45629
......@@ -25,7 +25,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 175
#define ANGLE_SH_VERSION 176
enum ShShaderSpec
{
......
......@@ -121,6 +121,7 @@ struct Uniform : public VariableWithLocation
}
int binding;
int offset;
// Decide whether two uniforms are the same at shader link time,
// assuming one from vertex shader and the other from fragment shader.
......
......@@ -157,6 +157,7 @@ GLenum VariableComponentType(GLenum type)
case GL_IMAGE_CUBE:
case GL_INT_IMAGE_CUBE:
case GL_UNSIGNED_INT_IMAGE_CUBE:
case GL_UNSIGNED_INT_ATOMIC_COUNTER:
return GL_INT;
case GL_UNSIGNED_INT:
case GL_UNSIGNED_INT_VEC2:
......@@ -277,6 +278,7 @@ int VariableRowCount(GLenum type)
case GL_IMAGE_CUBE:
case GL_INT_IMAGE_CUBE:
case GL_UNSIGNED_INT_IMAGE_CUBE:
case GL_UNSIGNED_INT_ATOMIC_COUNTER:
return 1;
case GL_FLOAT_MAT2:
case GL_FLOAT_MAT3x2:
......@@ -340,6 +342,7 @@ int VariableColumnCount(GLenum type)
case GL_IMAGE_CUBE:
case GL_INT_IMAGE_CUBE:
case GL_UNSIGNED_INT_IMAGE_CUBE:
case GL_UNSIGNED_INT_ATOMIC_COUNTER:
return 1;
case GL_BOOL_VEC2:
case GL_FLOAT_VEC2:
......@@ -422,11 +425,15 @@ bool IsImageType(GLenum type)
return false;
}
bool IsAtomicCounterType(GLenum type)
{
return type == GL_UNSIGNED_INT_ATOMIC_COUNTER;
}
bool IsOpaqueType(GLenum type)
{
// ESSL 3.10 section 4.1.7 defines opaque types as: samplers, images and atomic counters.
// TODO(oetuaho): add atomic types
return IsImageType(type) || IsSamplerType(type);
return IsImageType(type) || IsSamplerType(type) || IsAtomicCounterType(type);
}
GLenum SamplerTypeToTextureType(GLenum samplerType)
......
......@@ -31,6 +31,7 @@ int VariableRowCount(GLenum type);
int VariableColumnCount(GLenum type);
bool IsSamplerType(GLenum type);
bool IsImageType(GLenum type);
bool IsAtomicCounterType(GLenum type);
bool IsOpaqueType(GLenum type);
GLenum SamplerTypeToTextureType(GLenum samplerType);
bool IsMatrixType(GLenum type);
......
......@@ -123,6 +123,8 @@ enum TBasicType
EbtInterfaceBlock,
EbtAddress, // should be deprecated??
EbtAtomicCounter,
// end of list
EbtLast
};
......@@ -198,10 +200,14 @@ inline bool IsGImage(TBasicType type)
return type > EbtGuardGImageBegin && type < EbtGuardGImageEnd;
}
inline bool IsAtomicCounter(TBasicType type)
{
return type == EbtAtomicCounter;
}
inline bool IsOpaqueType(TBasicType type)
{
// TODO (mradev): add atomic types as opaque.
return IsSampler(type) || IsImage(type);
return IsSampler(type) || IsImage(type) || IsAtomicCounter(type);
}
inline bool IsIntegerSampler(TBasicType type)
......@@ -626,6 +632,7 @@ struct TLayoutQualifier
sh::WorkGroupSize localSize;
int binding;
int offset;
// Image format layout qualifier
TLayoutImageInternalFormat imageInternalFormat;
......@@ -647,6 +654,7 @@ struct TLayoutQualifier
layoutQualifier.localSize.fill(-1);
layoutQualifier.binding = -1;
layoutQualifier.offset = -1;
layoutQualifier.numViews = -1;
layoutQualifier.yuv = false;
......@@ -656,7 +664,7 @@ struct TLayoutQualifier
bool isEmpty() const
{
return location == -1 && binding == -1 && numViews == -1 && yuv == false &&
return location == -1 && binding == -1 && offset == -1 && numViews == -1 && yuv == false &&
matrixPacking == EmpUnspecified && blockStorage == EbsUnspecified &&
!localSize.isAnyValueSet() && imageInternalFormat == EiifUnspecified;
}
......
......@@ -600,6 +600,10 @@ bool TCompiler::InitBuiltInSymbolTable(const ShBuiltInResources &resources)
// It isn't specified whether Sampler2DRect has default precision.
initSamplerDefaultPrecision(EbtSampler2DRect);
TPublicType atomicCounter;
atomicCounter.initializeBasicType(EbtAtomicCounter);
symbolTable.setDefaultPrecision(atomicCounter, EbpHigh);
InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable);
IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable);
......
......@@ -630,6 +630,11 @@ void InsertBuiltInFunctions(sh::GLenum type,
symbolTable.insertBuiltIn(ESSL3_BUILTINS, float1, "textureProjGradOffset", sampler2DShadow,
float4, float2, float2, int2);
const TType *atomicCounter = TCache::getType(EbtAtomicCounter);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCounter", atomicCounter);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCounterIncrement", atomicCounter);
symbolTable.insertBuiltIn(ESSL3_1_BUILTINS, uint1, "atomicCounterDecrement", atomicCounter);
const TType *gimage2D = TCache::getType(EbtGImage2D);
const TType *gimage3D = TCache::getType(EbtGImage3D);
const TType *gimage2DArray = TCache::getType(EbtGImage2DArray);
......
......@@ -9,11 +9,12 @@
#include <stdarg.h>
#include <stdio.h>
#include "common/mathutil.h"
#include "compiler/preprocessor/SourceLocation.h"
#include "compiler/translator/Cache.h"
#include "compiler/translator/glslang.h"
#include "compiler/translator/ValidateSwitch.h"
#include "compiler/translator/ValidateGlobalInitializer.h"
#include "compiler/translator/ValidateSwitch.h"
#include "compiler/translator/glslang.h"
#include "compiler/translator/util.h"
namespace sh
......@@ -68,6 +69,38 @@ const char *GetImageArgumentToken(TIntermTyped *imageNode)
} // namespace
// This tracks each binding point's current default offset for inheritance of subsequent
// variables using the same binding, and keeps offsets unique and non overlapping.
// See GLSL ES 3.1, section 4.4.6.
class TParseContext::AtomicCounterBindingState
{
public:
AtomicCounterBindingState() : mDefaultOffset(0) {}
// Inserts a new span and returns -1 if overlapping, else returns the starting offset of
// newly inserted span.
int insertSpan(int start, size_t length)
{
gl::RangeI newSpan(start, start + static_cast<int>(length));
for (const auto &span : mSpans)
{
if (newSpan.intersects(span))
{
return -1;
}
}
mSpans.push_back(newSpan);
mDefaultOffset = newSpan.high();
return start;
}
// Inserts a new span starting from the default offset.
int appendSpan(size_t length) { return insertSpan(mDefaultOffset, length); }
void setDefaultOffset(int offset) { mDefaultOffset = offset; }
private:
int mDefaultOffset;
std::vector<gl::RangeI> mSpans;
};
TParseContext::TParseContext(TSymbolTable &symt,
TExtensionBehavior &ext,
sh::GLenum type,
......@@ -114,11 +147,16 @@ TParseContext::TParseContext(TSymbolTable &symt,
mMaxCombinedTextureImageUnits(resources.MaxCombinedTextureImageUnits),
mMaxUniformLocations(resources.MaxUniformLocations),
mMaxUniformBufferBindings(resources.MaxUniformBufferBindings),
mMaxAtomicCounterBindings(resources.MaxAtomicCounterBindings),
mDeclaringFunction(false)
{
mComputeShaderLocalSize.fill(-1);
}
TParseContext::~TParseContext()
{
}
//
// Look at a '.' field selector string and change it into offsets
// for a vector.
......@@ -1107,6 +1145,24 @@ void TParseContext::declarationQualifierErrorCheck(const sh::TQualifier qualifie
}
}
void TParseContext::atomicCounterQualifierErrorCheck(const TPublicType &publicType,
const TSourceLoc &location)
{
if (publicType.precision != EbpHigh)
{
error(location, "Can only be highp", "atomic counter");
}
// dEQP enforces compile error if location is specified. See uniform_location.test.
if (publicType.layoutQualifier.location != -1)
{
error(location, "location must not be set for atomic_uint", "layout");
}
if (publicType.layoutQualifier.binding == -1)
{
error(location, "no binding specified", "atomic counter");
}
}
void TParseContext::emptyDeclarationErrorCheck(const TPublicType &publicType,
const TSourceLoc &location)
{
......@@ -1244,9 +1300,17 @@ void TParseContext::nonEmptyDeclarationErrorCheck(const TPublicType &publicType,
else
{
checkInternalFormatIsNotSpecified(identifierLocation, layoutQualifier.imageInternalFormat);
checkMemoryQualifierIsNotSpecified(publicType.memoryQualifier, identifierLocation);
}
if (IsAtomicCounter(publicType.getBasicType()))
{
atomicCounterQualifierErrorCheck(publicType, identifierLocation);
}
else
{
checkOffsetIsNotSpecified(identifierLocation, layoutQualifier.offset);
}
}
void TParseContext::checkBindingIsValid(const TSourceLoc &identifierLocation, const TType &type)
......@@ -1261,6 +1325,10 @@ void TParseContext::checkBindingIsValid(const TSourceLoc &identifierLocation, co
{
checkSamplerBindingIsValid(identifierLocation, layoutQualifier.binding, arraySize);
}
else if (IsAtomicCounter(type.getBasicType()))
{
checkAtomicCounterBindingIsValid(identifierLocation, layoutQualifier.binding);
}
else
{
ASSERT(!IsOpaqueType(type.getBasicType()));
......@@ -1318,6 +1386,15 @@ void TParseContext::checkBindingIsNotSpecified(const TSourceLoc &location, int b
}
}
void TParseContext::checkOffsetIsNotSpecified(const TSourceLoc &location, int offset)
{
if (offset != -1)
{
error(location, "invalid layout qualifier: only valid when used with atomic counters",
"offset");
}
}
void TParseContext::checkImageBindingIsValid(const TSourceLoc &location, int binding, int arraySize)
{
// Expects arraySize to be 1 when setting binding for only a single variable.
......@@ -1347,6 +1424,14 @@ void TParseContext::checkBlockBindingIsValid(const TSourceLoc &location, int bin
"binding");
}
}
void TParseContext::checkAtomicCounterBindingIsValid(const TSourceLoc &location, int binding)
{
if (binding >= mMaxAtomicCounterBindings)
{
error(location, "atomic counter binding greater than gl_MaxAtomicCounterBindings",
"binding");
}
}
void TParseContext::checkUniformLocationInRange(const TSourceLoc &location,
int objectLocationCount,
......@@ -1909,6 +1994,34 @@ void TParseContext::checkMemoryQualifierIsNotSpecified(const TMemoryQualifier &m
}
}
// Make sure there is no offset overlapping, and store the newly assigned offset to "type" in
// intermediate tree.
void TParseContext::checkAtomicCounterOffsetIsNotOverlapped(TPublicType &publicType,
size_t size,
bool forceAppend,
const TSourceLoc &loc,
TType &type)
{
auto &bindingState = mAtomicCounterBindingStates[publicType.layoutQualifier.binding];
int offset;
if (publicType.layoutQualifier.offset == -1 || forceAppend)
{
offset = bindingState.appendSpan(size);
}
else
{
offset = bindingState.insertSpan(publicType.layoutQualifier.offset, size);
}
if (offset == -1)
{
error(loc, "Offset overlapping", "atomic counter");
return;
}
TLayoutQualifier qualifier = type.getLayoutQualifier();
qualifier.offset = offset;
type.setLayoutQualifier(qualifier);
}
TIntermDeclaration *TParseContext::parseSingleDeclaration(
TPublicType &publicType,
const TSourceLoc &identifierOrTypeLocation,
......@@ -1958,6 +2071,10 @@ TIntermDeclaration *TParseContext::parseSingleDeclaration(
{
symbol = intermediate.addSymbol(0, "", type, identifierOrTypeLocation);
}
else if (IsAtomicCounter(publicType.getBasicType()))
{
setAtomicCounterBindingDefaultOffset(publicType, identifierOrTypeLocation);
}
}
else
{
......@@ -1965,6 +2082,13 @@ TIntermDeclaration *TParseContext::parseSingleDeclaration(
checkCanBeDeclaredWithoutInitializer(identifierOrTypeLocation, identifier, &publicType);
if (IsAtomicCounter(publicType.getBasicType()))
{
checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterSize, false,
identifierOrTypeLocation, type);
}
TVariable *variable = nullptr;
declareVariable(identifierOrTypeLocation, identifier, type, &variable);
......@@ -2008,6 +2132,12 @@ TIntermDeclaration *TParseContext::parseSingleArrayDeclaration(TPublicType &publ
// This ensures useless error messages regarding the variable's non-arrayness won't follow.
arrayType.setArraySize(size);
if (IsAtomicCounter(publicType.getBasicType()))
{
checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterArrayStride * size, false,
identifierLocation, arrayType);
}
TVariable *variable = nullptr;
declareVariable(identifierLocation, identifier, arrayType, &variable);
......@@ -2170,6 +2300,11 @@ void TParseContext::parseDeclarator(TPublicType &publicType,
TVariable *variable = nullptr;
TType type(publicType);
if (IsAtomicCounter(publicType.getBasicType()))
{
checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterSize, true,
identifierLocation, type);
}
declareVariable(identifierLocation, identifier, type, &variable);
TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, type, identifierLocation);
......@@ -2205,6 +2340,12 @@ void TParseContext::parseArrayDeclarator(TPublicType &publicType,
unsigned int size = checkIsValidArraySize(arrayLocation, indexExpression);
arrayType.setArraySize(size);
if (IsAtomicCounter(publicType.getBasicType()))
{
checkAtomicCounterOffsetIsNotOverlapped(publicType, kAtomicCounterArrayStride * size,
true, identifierLocation, arrayType);
}
TVariable *variable = nullptr;
declareVariable(identifierLocation, identifier, arrayType, &variable);
......@@ -2292,6 +2433,19 @@ void TParseContext::parseArrayInitDeclarator(const TPublicType &publicType,
}
}
void TParseContext::setAtomicCounterBindingDefaultOffset(const TPublicType &publicType,
const TSourceLoc &location)
{
const TLayoutQualifier &layoutQualifier = publicType.layoutQualifier;
checkAtomicCounterBindingIsValid(location, layoutQualifier.binding);
if (layoutQualifier.binding == -1 || layoutQualifier.offset == -1)
{
error(location, "Requires both binding and offset", "layout");
return;
}
mAtomicCounterBindingStates[layoutQualifier.binding].setDefaultOffset(layoutQualifier.offset);
}
void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder)
{
TTypeQualifier typeQualifier = typeQualifierBuilder.getVariableTypeQualifier(mDiagnostics);
......@@ -2321,6 +2475,8 @@ void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &type
checkYuvIsNotSpecified(typeQualifier.line, layoutQualifier.yuv);
checkOffsetIsNotSpecified(typeQualifier.line, layoutQualifier.offset);
if (typeQualifier.qualifier == EvqComputeIn)
{
if (mComputeShaderLocalSizeDeclared &&
......@@ -3423,6 +3579,19 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const TString &qualifierTyp
qualifier.binding = intValue;
}
}
else if (qualifierType == "offset")
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
if (intValue < 0)
{
error(intValueLine, "out of range: offset must be non-negative",
intValueString.c_str());
}
else
{
qualifier.offset = intValue;
}
}
else if (qualifierType == "local_size_x")
{
parseLocalSize(qualifierType, qualifierTypeLine, intValue, intValueLine, intValueString, 0u,
......@@ -3586,7 +3755,8 @@ TTypeSpecifierNonArray TParseContext::addStructure(const TSourceLoc &structLine,
{
error(field.line(), "invalid qualifier on struct member", "invariant");
}
if (IsImage(field.type()->getBasicType()))
// ESSL 3.10 section 4.1.8 -- atomic_uint or images are not allowed as structure member.
if (IsImage(field.type()->getBasicType()) || IsAtomicCounter(field.type()->getBasicType()))
{
error(field.line(), "disallowed type in struct", field.type()->getBasicString());
}
......
......@@ -40,6 +40,7 @@ class TParseContext : angle::NonCopyable
bool checksPrecErrors,
TDiagnostics *diagnostics,
const ShBuiltInResources &resources);
~TParseContext();
const pp::Preprocessor &getPreprocessor() const { return mPreprocessor; }
pp::Preprocessor &getPreprocessor() { return mPreprocessor; }
......@@ -363,6 +364,16 @@ class TParseContext : angle::NonCopyable
TSymbolTable &symbolTable; // symbol table that goes with the language currently being parsed
private:
class AtomicCounterBindingState;
constexpr static size_t kAtomicCounterSize = 4;
// UNIFORM_ARRAY_STRIDE for atomic counter arrays is an implementation-dependent value which
// can be queried after a program is linked according to ES 3.10 section 7.7.1. This is
// controversial with the offset inheritance as described in ESSL 3.10 section 4.4.6. Currently
// we treat it as always 4 in favour of the original interpretation in
// "ARB_shader_atomic_counters".
// TODO(jie.a.chen@intel.com): Double check this once the spec vagueness is resolved.
constexpr static size_t kAtomicCounterArrayStride = 4;
// Returns a clamped index. If it prints out an error message, the token is "[]".
int checkIndexOutOfRange(bool outOfRangeIndexIsError,
const TSourceLoc &location,
......@@ -381,6 +392,9 @@ class TParseContext : angle::NonCopyable
bool checkIsValidTypeAndQualifierForArray(const TSourceLoc &indexLocation,
const TPublicType &elementType);
// Done for all atomic counter declarations, whether empty or not.
void atomicCounterQualifierErrorCheck(const TPublicType &publicType,
const TSourceLoc &location);
// Assumes that multiplication op has already been set based on the types.
bool isMultiplicationTypeCombinationValid(TOperator op, const TType &left, const TType &right);
......@@ -393,11 +407,18 @@ class TParseContext : angle::NonCopyable
TLayoutImageInternalFormat internalFormat);
void checkMemoryQualifierIsNotSpecified(const TMemoryQualifier &memoryQualifier,
const TSourceLoc &location);
void checkAtomicCounterOffsetIsNotOverlapped(TPublicType &publicType,
size_t size,
bool forceAppend,
const TSourceLoc &loc,
TType &type);
void checkBindingIsValid(const TSourceLoc &identifierLocation, const TType &type);
void checkBindingIsNotSpecified(const TSourceLoc &location, int binding);
void checkOffsetIsNotSpecified(const TSourceLoc &location, int offset);
void checkImageBindingIsValid(const TSourceLoc &location, int binding, int arraySize);
void checkSamplerBindingIsValid(const TSourceLoc &location, int binding, int arraySize);
void checkBlockBindingIsValid(const TSourceLoc &location, int binding, int arraySize);
void checkAtomicCounterBindingIsValid(const TSourceLoc &location, int binding);
void checkUniformLocationInRange(const TSourceLoc &location,
int objectLocationCount,
......@@ -436,6 +457,9 @@ class TParseContext : angle::NonCopyable
const TSourceLoc &location,
bool insertParametersToSymbolTable);
void setAtomicCounterBindingDefaultOffset(const TPublicType &declaration,
const TSourceLoc &location);
// 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.
......@@ -482,8 +506,13 @@ class TParseContext : angle::NonCopyable
int mMaxCombinedTextureImageUnits;
int mMaxUniformLocations;
int mMaxUniformBufferBindings;
int mMaxAtomicCounterBindings;
// keeps track whether we are declaring / defining a function
bool mDeclaringFunction;
// Track the state of each atomic counter binding.
std::map<int, AtomicCounterBindingState> mAtomicCounterBindingStates;
};
int PaParseStrings(size_t count,
......
......@@ -568,6 +568,10 @@ TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier,
{
joinedQualifier.binding = rightQualifier.binding;
}
if (rightQualifier.offset != -1)
{
joinedQualifier.offset = rightQualifier.offset;
}
if (rightQualifier.matrixPacking != EmpUnspecified)
{
joinedQualifier.matrixPacking = rightQualifier.matrixPacking;
......
......@@ -181,7 +181,7 @@ bool ShaderVariable::isSameVariableAtLinkTime(const ShaderVariable &other,
return true;
}
Uniform::Uniform() : binding(-1)
Uniform::Uniform() : binding(-1), offset(-1)
{
}
......@@ -189,7 +189,8 @@ Uniform::~Uniform()
{
}
Uniform::Uniform(const Uniform &other) : VariableWithLocation(other), binding(other.binding)
Uniform::Uniform(const Uniform &other)
: VariableWithLocation(other), binding(other.binding), offset(other.offset)
{
}
......@@ -197,17 +198,19 @@ Uniform &Uniform::operator=(const Uniform &other)
{
VariableWithLocation::operator=(other);
binding = other.binding;
offset = other.offset;
return *this;
}
bool Uniform::operator==(const Uniform &other) const
{
return VariableWithLocation::operator==(other) && binding == other.binding;
return VariableWithLocation::operator==(other) && binding == other.binding &&
offset == other.offset;
}
bool Uniform::isSameUniformAtLinkTime(const Uniform &other) const
{
if (binding != -1 && other.binding != -1 && binding != other.binding)
if (binding != other.binding)
{
return false;
}
......@@ -215,6 +218,11 @@ bool Uniform::isSameUniformAtLinkTime(const Uniform &other) const
{
return false;
}
// TODO(jie.a.chen@intel.com): Add a test case to cover this.
if (offset != other.offset)
{
return false;
}
return VariableWithLocation::isSameVariableAtLinkTime(other, true);
}
......
......@@ -105,6 +105,8 @@ const char *getBasicString(TBasicType t)
return "iimageCube";
case EbtUImageCube:
return "uimageCube";
case EbtAtomicCounter:
return "atomic_uint";
default:
UNREACHABLE();
return "unknown type";
......
......@@ -329,6 +329,8 @@ TString TypeString(const TType &type)
return "samplerCUBE";
case EbtSamplerExternalOES:
return "sampler2D";
case EbtAtomicCounter:
return "atomic_uint";
default:
break;
}
......
......@@ -572,6 +572,7 @@ Uniform CollectVariables::recordUniform(const TIntermSymbol &variable) const
setCommonVariableProperties(variable.getType(), variable.getSymbol(), &uniform);
uniform.binding = variable.getType().getLayoutQualifier().binding;
uniform.location = variable.getType().getLayoutQualifier().location;
uniform.offset = variable.getType().getLayoutQualifier().offset;
return uniform;
}
......
File mode changed from 100644 to 100755
......@@ -225,10 +225,10 @@ O [0-7]
"coherent" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, COHERENT); }
"restrict" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, RESTRICT); }
"volatile" { return ES2_and_ES3_reserved_ES3_1_keyword(context, VOLATILE); }
"atomic_uint" { return ES2_ident_ES3_reserved_ES3_1_keyword(context, ATOMICUINT); }
/* Reserved keywords for GLSL ES 3.00 that are not reserved for GLSL ES 1.00 */
"resource" |
"atomic_uint" |
"noperspective" |
"patch" |
"sample" |
......
......@@ -187,6 +187,7 @@ extern void yyerror(YYLTYPE* yylloc, TParseContext* context, void *scanner, cons
%token <lex> SAMPLEREXTERNAL2DY2YEXT
%token <lex> IMAGE2D IIMAGE2D UIMAGE2D IMAGE3D IIMAGE3D UIMAGE3D IMAGE2DARRAY IIMAGE2DARRAY UIMAGE2DARRAY
%token <lex> IMAGECUBE IIMAGECUBE UIMAGECUBE
%token <lex> ATOMICUINT
%token <lex> LAYOUT
%token <lex> YUVCSCSTANDARDEXT YUVCSCSTANDARDEXTCONSTANT
......@@ -1264,6 +1265,9 @@ type_specifier_nonarray
| UIMAGECUBE {
$$.initialize(EbtUImageCube, @1);
}
| ATOMICUINT {
$$.initialize(EbtAtomicCounter, @1);
}
| TYPE_NAME {
//
// This is for user defined type names. The lexical phase looked up the
......
......@@ -588,7 +588,7 @@ static const flex_int16_t yy_accept[886] =
183, 183, 183, 183, 8, 183, 183, 9, 183, 183,
183, 183, 183, 183, 20, 102, 11, 156, 116, 89,
96, 183, 183, 183, 183, 183, 183, 183, 183, 183,
183, 183, 183, 183, 152, 183, 183, 183, 100, 105,
183, 183, 183, 183, 152, 183, 183, 183, 100, 106,
103, 183, 183, 183, 183, 183, 183, 183, 148, 117,
90, 95, 183, 183, 163, 183, 104, 183, 183, 183,
......@@ -600,7 +600,7 @@ static const flex_int16_t yy_accept[886] =
183, 183, 183, 130, 68, 69, 183, 183, 183, 183,
183, 183, 183, 183, 183, 183, 183, 183, 183, 183,
110, 183, 183, 183, 98, 132, 73, 74, 183, 183,
183, 183, 106, 183, 183, 183, 183, 183, 183, 183,
183, 183, 105, 183, 183, 183, 183, 183, 183, 183,
125, 183, 183, 183, 183, 183, 183, 183, 183, 183,
183, 183, 67, 183, 183, 183, 183, 61, 183, 183,
......@@ -2107,8 +2107,11 @@ case 104:
YY_RULE_SETUP
{ return ES2_and_ES3_reserved_ES3_1_keyword(context, VOLATILE); }
YY_BREAK
/* Reserved keywords for GLSL ES 3.00 that are not reserved for GLSL ES 1.00 */
case 105:
YY_RULE_SETUP
{ return ES2_ident_ES3_reserved_ES3_1_keyword(context, ATOMICUINT); }
YY_BREAK
/* Reserved keywords for GLSL ES 3.00 that are not reserved for GLSL ES 1.00 */
case 106:
case 107:
case 108:
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -50,158 +50,159 @@ extern int yydebug;
/* Token type. */
#ifndef YYTOKENTYPE
# define YYTOKENTYPE
enum yytokentype
{
INVARIANT = 258,
HIGH_PRECISION = 259,
MEDIUM_PRECISION = 260,
LOW_PRECISION = 261,
PRECISION = 262,
ATTRIBUTE = 263,
CONST_QUAL = 264,
BOOL_TYPE = 265,
FLOAT_TYPE = 266,
INT_TYPE = 267,
UINT_TYPE = 268,
BREAK = 269,
CONTINUE = 270,
DO = 271,
ELSE = 272,
FOR = 273,
IF = 274,
DISCARD = 275,
RETURN = 276,
SWITCH = 277,
CASE = 278,
DEFAULT = 279,
BVEC2 = 280,
BVEC3 = 281,
BVEC4 = 282,
IVEC2 = 283,
IVEC3 = 284,
IVEC4 = 285,
VEC2 = 286,
VEC3 = 287,
VEC4 = 288,
UVEC2 = 289,
UVEC3 = 290,
UVEC4 = 291,
MATRIX2 = 292,
MATRIX3 = 293,
MATRIX4 = 294,
IN_QUAL = 295,
OUT_QUAL = 296,
INOUT_QUAL = 297,
UNIFORM = 298,
VARYING = 299,
MATRIX2x3 = 300,
MATRIX3x2 = 301,
MATRIX2x4 = 302,
MATRIX4x2 = 303,
MATRIX3x4 = 304,
MATRIX4x3 = 305,
CENTROID = 306,
FLAT = 307,
SMOOTH = 308,
READONLY = 309,
WRITEONLY = 310,
COHERENT = 311,
RESTRICT = 312,
VOLATILE = 313,
SHARED = 314,
STRUCT = 315,
VOID_TYPE = 316,
WHILE = 317,
SAMPLER2D = 318,
SAMPLERCUBE = 319,
SAMPLER_EXTERNAL_OES = 320,
SAMPLER2DRECT = 321,
SAMPLER2DARRAY = 322,
ISAMPLER2D = 323,
ISAMPLER3D = 324,
ISAMPLERCUBE = 325,
ISAMPLER2DARRAY = 326,
USAMPLER2D = 327,
USAMPLER3D = 328,
USAMPLERCUBE = 329,
USAMPLER2DARRAY = 330,
SAMPLER2DMS = 331,
ISAMPLER2DMS = 332,
USAMPLER2DMS = 333,
SAMPLER3D = 334,
SAMPLER3DRECT = 335,
SAMPLER2DSHADOW = 336,
SAMPLERCUBESHADOW = 337,
SAMPLER2DARRAYSHADOW = 338,
SAMPLEREXTERNAL2DY2YEXT = 339,
IMAGE2D = 340,
IIMAGE2D = 341,
UIMAGE2D = 342,
IMAGE3D = 343,
IIMAGE3D = 344,
UIMAGE3D = 345,
IMAGE2DARRAY = 346,
IIMAGE2DARRAY = 347,
UIMAGE2DARRAY = 348,
IMAGECUBE = 349,
IIMAGECUBE = 350,
UIMAGECUBE = 351,
LAYOUT = 352,
YUVCSCSTANDARDEXT = 353,
YUVCSCSTANDARDEXTCONSTANT = 354,
IDENTIFIER = 355,
TYPE_NAME = 356,
FLOATCONSTANT = 357,
INTCONSTANT = 358,
UINTCONSTANT = 359,
BOOLCONSTANT = 360,
FIELD_SELECTION = 361,
LEFT_OP = 362,
RIGHT_OP = 363,
INC_OP = 364,
DEC_OP = 365,
LE_OP = 366,
GE_OP = 367,
EQ_OP = 368,
NE_OP = 369,
AND_OP = 370,
OR_OP = 371,
XOR_OP = 372,
MUL_ASSIGN = 373,
DIV_ASSIGN = 374,
ADD_ASSIGN = 375,
MOD_ASSIGN = 376,
LEFT_ASSIGN = 377,
RIGHT_ASSIGN = 378,
AND_ASSIGN = 379,
XOR_ASSIGN = 380,
OR_ASSIGN = 381,
SUB_ASSIGN = 382,
LEFT_PAREN = 383,
RIGHT_PAREN = 384,
LEFT_BRACKET = 385,
RIGHT_BRACKET = 386,
LEFT_BRACE = 387,
RIGHT_BRACE = 388,
DOT = 389,
COMMA = 390,
COLON = 391,
EQUAL = 392,
SEMICOLON = 393,
BANG = 394,
DASH = 395,
TILDE = 396,
PLUS = 397,
STAR = 398,
SLASH = 399,
PERCENT = 400,
LEFT_ANGLE = 401,
RIGHT_ANGLE = 402,
VERTICAL_BAR = 403,
CARET = 404,
AMPERSAND = 405,
QUESTION = 406
};
enum yytokentype
{
INVARIANT = 258,
HIGH_PRECISION = 259,
MEDIUM_PRECISION = 260,
LOW_PRECISION = 261,
PRECISION = 262,
ATTRIBUTE = 263,
CONST_QUAL = 264,
BOOL_TYPE = 265,
FLOAT_TYPE = 266,
INT_TYPE = 267,
UINT_TYPE = 268,
BREAK = 269,
CONTINUE = 270,
DO = 271,
ELSE = 272,
FOR = 273,
IF = 274,
DISCARD = 275,
RETURN = 276,
SWITCH = 277,
CASE = 278,
DEFAULT = 279,
BVEC2 = 280,
BVEC3 = 281,
BVEC4 = 282,
IVEC2 = 283,
IVEC3 = 284,
IVEC4 = 285,
VEC2 = 286,
VEC3 = 287,
VEC4 = 288,
UVEC2 = 289,
UVEC3 = 290,
UVEC4 = 291,
MATRIX2 = 292,
MATRIX3 = 293,
MATRIX4 = 294,
IN_QUAL = 295,
OUT_QUAL = 296,
INOUT_QUAL = 297,
UNIFORM = 298,
VARYING = 299,
MATRIX2x3 = 300,
MATRIX3x2 = 301,
MATRIX2x4 = 302,
MATRIX4x2 = 303,
MATRIX3x4 = 304,
MATRIX4x3 = 305,
CENTROID = 306,
FLAT = 307,
SMOOTH = 308,
READONLY = 309,
WRITEONLY = 310,
COHERENT = 311,
RESTRICT = 312,
VOLATILE = 313,
SHARED = 314,
STRUCT = 315,
VOID_TYPE = 316,
WHILE = 317,
SAMPLER2D = 318,
SAMPLERCUBE = 319,
SAMPLER_EXTERNAL_OES = 320,
SAMPLER2DRECT = 321,
SAMPLER2DARRAY = 322,
ISAMPLER2D = 323,
ISAMPLER3D = 324,
ISAMPLERCUBE = 325,
ISAMPLER2DARRAY = 326,
USAMPLER2D = 327,
USAMPLER3D = 328,
USAMPLERCUBE = 329,
USAMPLER2DARRAY = 330,
SAMPLER2DMS = 331,
ISAMPLER2DMS = 332,
USAMPLER2DMS = 333,
SAMPLER3D = 334,
SAMPLER3DRECT = 335,
SAMPLER2DSHADOW = 336,
SAMPLERCUBESHADOW = 337,
SAMPLER2DARRAYSHADOW = 338,
SAMPLEREXTERNAL2DY2YEXT = 339,
IMAGE2D = 340,
IIMAGE2D = 341,
UIMAGE2D = 342,
IMAGE3D = 343,
IIMAGE3D = 344,
UIMAGE3D = 345,
IMAGE2DARRAY = 346,
IIMAGE2DARRAY = 347,
UIMAGE2DARRAY = 348,
IMAGECUBE = 349,
IIMAGECUBE = 350,
UIMAGECUBE = 351,
ATOMICUINT = 352,
LAYOUT = 353,
YUVCSCSTANDARDEXT = 354,
YUVCSCSTANDARDEXTCONSTANT = 355,
IDENTIFIER = 356,
TYPE_NAME = 357,
FLOATCONSTANT = 358,
INTCONSTANT = 359,
UINTCONSTANT = 360,
BOOLCONSTANT = 361,
FIELD_SELECTION = 362,
LEFT_OP = 363,
RIGHT_OP = 364,
INC_OP = 365,
DEC_OP = 366,
LE_OP = 367,
GE_OP = 368,
EQ_OP = 369,
NE_OP = 370,
AND_OP = 371,
OR_OP = 372,
XOR_OP = 373,
MUL_ASSIGN = 374,
DIV_ASSIGN = 375,
ADD_ASSIGN = 376,
MOD_ASSIGN = 377,
LEFT_ASSIGN = 378,
RIGHT_ASSIGN = 379,
AND_ASSIGN = 380,
XOR_ASSIGN = 381,
OR_ASSIGN = 382,
SUB_ASSIGN = 383,
LEFT_PAREN = 384,
RIGHT_PAREN = 385,
LEFT_BRACKET = 386,
RIGHT_BRACKET = 387,
LEFT_BRACE = 388,
RIGHT_BRACE = 389,
DOT = 390,
COMMA = 391,
COLON = 392,
EQUAL = 393,
SEMICOLON = 394,
BANG = 395,
DASH = 396,
TILDE = 397,
PLUS = 398,
STAR = 399,
SLASH = 400,
PERCENT = 401,
LEFT_ANGLE = 402,
RIGHT_ANGLE = 403,
VERTICAL_BAR = 404,
CARET = 405,
AMPERSAND = 406,
QUESTION = 407
};
#endif
/* Value type. */
......
......@@ -392,6 +392,8 @@ GLenum GLVariableType(const TType &type)
return GL_INT_IMAGE_CUBE;
case EbtUImageCube:
return GL_UNSIGNED_INT_IMAGE_CUBE;
case EbtAtomicCounter:
return GL_UNSIGNED_INT_ATOMIC_COUNTER;
default:
UNREACHABLE();
}
......
......@@ -48,6 +48,7 @@
'<(angle_path)/src/tests/angle_unittests_utils.h',
'<(angle_path)/src/tests/compiler_tests/API_test.cpp',
'<(angle_path)/src/tests/compiler_tests/AppendixALimitations_test.cpp',
'<(angle_path)/src/tests/compiler_tests/AtomicCounter_test.cpp',
'<(angle_path)/src/tests/compiler_tests/CollectVariables_test.cpp',
'<(angle_path)/src/tests/compiler_tests/ConstantFolding_test.cpp',
'<(angle_path)/src/tests/compiler_tests/ConstantFoldingNaN_test.cpp',
......
//
// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// AtomicCounter_test.cpp:
// Tests for validating ESSL 3.10 section 4.4.6.
//
#include "gtest/gtest.h"
#include "GLSLANG/ShaderLang.h"
#include "angle_gl.h"
#include "gtest/gtest.h"
#include "tests/test_utils/ShaderCompileTreeTest.h"
using namespace sh;
class AtomicCounterTest : public ShaderCompileTreeTest
{
public:
AtomicCounterTest() {}
protected:
::GLenum getShaderType() const override { return GL_VERTEX_SHADER; }
ShShaderSpec getShaderSpec() const override { return SH_GLES3_1_SPEC; }
void initResources(ShBuiltInResources *resources) override
{
resources->MaxAtomicCounterBindings = 8;
}
};
// Test that layout qualifiers described in ESSL 3.10 section 4.4.6 can be successfully compiled,
// and the values of offset are properly assigned to counter variables.
TEST_F(AtomicCounterTest, BasicAtomicCounterDeclaration)
{
mExtraCompileOptions |= SH_VARIABLES;
const std::string &source =
"#version 310 es\n"
"layout(binding = 2, offset = 4) uniform atomic_uint a;\n"
"layout(binding = 2) uniform atomic_uint b;\n"
"layout(binding = 2, offset = 12) uniform atomic_uint c, d;\n"
"layout(binding = 1, offset = 4) uniform atomic_uint e;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(source))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
std::vector<sh::Uniform> counters = getUniforms();
EXPECT_EQ(std::string("a"), counters[0].name);
EXPECT_EQ(2, counters[0].binding);
EXPECT_EQ(4, counters[0].offset);
EXPECT_EQ(std::string("b"), counters[1].name);
EXPECT_EQ(2, counters[1].binding);
EXPECT_EQ(8, counters[1].offset);
EXPECT_EQ(std::string("c"), counters[2].name);
EXPECT_EQ(2, counters[2].binding);
EXPECT_EQ(12, counters[2].offset);
EXPECT_EQ(std::string("d"), counters[3].name);
EXPECT_EQ(2, counters[3].binding);
EXPECT_EQ(16, counters[3].offset);
EXPECT_EQ(std::string("e"), counters[4].name);
EXPECT_EQ(1, counters[4].binding);
EXPECT_EQ(4, counters[4].offset);
}
// Test that ESSL 3.00 doesn't support atomic_uint.
TEST_F(AtomicCounterTest, InvalidShaderVersion)
{
const std::string &source =
"#version 300 es\n"
"layout(binding = 2, offset = 4) uniform atomic_uint a;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(source))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that any qualifier other than uniform leads to compile-time error.
TEST_F(AtomicCounterTest, InvalidQualifier)
{
const std::string &source =
"#version 310 es\n"
"layout(binding = 2, offset = 4) in atomic_uint a;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(source))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that uniform must be specified for declaration.
TEST_F(AtomicCounterTest, UniformMustSpecifiedForDeclaration)
{
const std::string &source =
"#version 310 es\n"
"atomic_uint a;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(source))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that offset overlapping leads to compile-time error(ESSL 3.10 section 4.4.6).
TEST_F(AtomicCounterTest, BindingOffsetOverlapping)
{
const std::string &source =
"#version 310 es\n"
"layout(binding = 2, offset = 4) uniform atomic_uint a;\n"
"layout(binding = 2, offset = 6) uniform atomic_uint b;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(source))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test offset inheritance for multiple variables in one same declaration.
TEST_F(AtomicCounterTest, MultipleVariablesDeclaration)
{
const std::string &source =
"#version 310 es\n"
"layout(binding = 2, offset = 4) uniform atomic_uint a, b;\n"
"layout(binding = 2, offset = 8) uniform atomic_uint c;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(source))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that subsequent declarations inherit the globally specified offset.
TEST_F(AtomicCounterTest, GlobalBindingOffsetOverlapping)
{
const std::string &source =
"#version 310 es\n"
"layout(binding = 2, offset = 4) uniform atomic_uint;\n"
"layout(binding = 2) uniform atomic_uint b;\n"
"layout(binding = 2, offset = 4) uniform atomic_uint c;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(source))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// The spec only demands offset unique and non-overlapping. So this should be allowed.
TEST_F(AtomicCounterTest, DeclarationSequenceWithDecrementalOffsetsSpecified)
{
const std::string &source =
"#version 310 es\n"
"layout(binding = 2, offset = 4) uniform atomic_uint a;\n"
"layout(binding = 2, offset = 0) uniform atomic_uint b;\n"
"void main()\n"
"{\n"
"}\n";
if (!compile(source))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that image format qualifiers are not allowed for atomic counters.
TEST_F(AtomicCounterTest, ImageFormatMustNotSpecified)
{
const std::string &source =
"#version 310 es\n"
"layout(binding = 2, offset = 4, rgba32f) uniform atomic_uint a;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(source))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
// Test that global layout qualifiers must not use 'offset'.
TEST_F(AtomicCounterTest, OffsetMustNotSpecifiedForGlobalLayoutQualifier)
{
const std::string &source =
"#version 310 es\n"
"layout(offset = 4) in;\n"
"void main()\n"
"{\n"
"}\n";
if (compile(source))
{
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment