diff options
Diffstat (limited to 'thirdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp')
-rw-r--r-- | thirdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp | 1392 |
1 files changed, 828 insertions, 564 deletions
diff --git a/thirdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp b/thirdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp index 6a8d379b09..a2224e1609 100644 --- a/thirdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp +++ b/thirdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp @@ -2,7 +2,7 @@ // Copyright (C) 2002-2005 3Dlabs Inc. Ltd. // Copyright (C) 2012-2015 LunarG, Inc. // Copyright (C) 2015-2018 Google, Inc. -// Copyright (C) 2017 ARM Limited. +// Copyright (C) 2017, 2019 ARM Limited. // // All rights reserved. // @@ -56,13 +56,16 @@ TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, b infoSink, forwardCompatible, messages, entryPoint), inMain(false), blockName(nullptr), - limits(resources.limits), + limits(resources.limits) +#ifndef GLSLANG_WEB + , atomicUintOffsets(nullptr), anyIndexLimits(false) +#endif { // decide whether precision qualifiers should be ignored or respected - if (profile == EEsProfile || spvVersion.vulkan > 0) { + if (isEsProfile() || spvVersion.vulkan > 0) { precisionManager.respectPrecisionQualifiers(); - if (! parsingBuiltins && language == EShLangFragment && profile != EEsProfile && spvVersion.vulkan > 0) + if (! parsingBuiltins && language == EShLangFragment && !isEsProfile() && spvVersion.vulkan > 0) precisionManager.warnAboutDefaults(); } @@ -83,6 +86,7 @@ TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, b globalInputDefaults.clear(); globalOutputDefaults.clear(); +#ifndef GLSLANG_WEB // "Shaders in the transform // feedback capturing mode have an initial global default of // layout(xfb_buffer = 0) out;" @@ -94,6 +98,7 @@ TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, b if (language == EShLangGeometry) globalOutputDefaults.layoutStream = 0; +#endif if (entryPoint != nullptr && entryPoint->size() > 0 && *entryPoint != "main") infoSink.info.message(EPrefixError, "Source entry point must be \"main\""); @@ -101,7 +106,9 @@ TParseContext::TParseContext(TSymbolTable& symbolTable, TIntermediate& interm, b TParseContext::~TParseContext() { +#ifndef GLSLANG_WEB delete [] atomicUintOffsets; +#endif } // Set up all default precisions as needed by the current environment. @@ -121,7 +128,7 @@ void TParseContext::setPrecisionDefaults() // replace with real precision defaults for those that have them if (obeyPrecisionQualifiers()) { - if (profile == EEsProfile) { + if (isEsProfile()) { // Most don't have defaults, a few default to lowp. TSampler sampler; sampler.set(EbtFloat, Esd2D); @@ -129,7 +136,7 @@ void TParseContext::setPrecisionDefaults() sampler.set(EbtFloat, EsdCube); defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; sampler.set(EbtFloat, Esd2D); - sampler.external = true; + sampler.setExternal(true); defaultSamplerPrecision[computeSamplerTypeIndex(sampler)] = EpqLow; } @@ -138,7 +145,7 @@ void TParseContext::setPrecisionDefaults() // is used to resolve the precision from the supplied arguments/operands instead. // So, we don't actually want to replace EpqNone with a default precision for built-ins. if (! parsingBuiltins) { - if (profile == EEsProfile && language == EShLangFragment) { + if (isEsProfile() && language == EShLangFragment) { defaultPrecision[EbtInt] = EpqMedium; defaultPrecision[EbtUint] = EpqMedium; } else { @@ -147,7 +154,7 @@ void TParseContext::setPrecisionDefaults() defaultPrecision[EbtFloat] = EpqHigh; } - if (profile != EEsProfile) { + if (!isEsProfile()) { // Non-ES profile // All sampler precisions default to highp. for (int type = 0; type < maxSamplerIndex; ++type) @@ -163,7 +170,9 @@ void TParseContext::setPrecisionDefaults() void TParseContext::setLimits(const TBuiltInResource& r) { resources = r; + intermediate.setLimits(r); +#ifndef GLSLANG_WEB anyIndexLimits = ! limits.generalAttributeMatrixVectorIndexing || ! limits.generalConstantMatrixVectorIndexing || ! limits.generalSamplerIndexing || @@ -171,7 +180,6 @@ void TParseContext::setLimits(const TBuiltInResource& r) ! limits.generalVariableIndexing || ! limits.generalVaryingIndexing; - intermediate.setLimits(resources); // "Each binding point tracks its own current default offset for // inheritance of subsequent variables using the same binding. The initial state of compilation is that all @@ -179,6 +187,7 @@ void TParseContext::setLimits(const TBuiltInResource& r) atomicUintOffsets = new int[resources.maxAtomicCounterBindings]; for (int b = 0; b < resources.maxAtomicCounterBindings; ++b) atomicUintOffsets[b] = 0; +#endif } // @@ -213,6 +222,7 @@ void TParseContext::parserError(const char* s) void TParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& tokens) { +#ifndef GLSLANG_WEB if (pragmaCallback) pragmaCallback(loc.line, tokens); @@ -285,6 +295,7 @@ void TParseContext::handlePragma(const TSourceLoc& loc, const TVector<TString>& warn(loc, "not implemented", "#pragma once", ""); } else if (tokens[0].compare("glslang_binary_double_output") == 0) intermediate.setBinaryDoubleOutput(); +#endif } // @@ -298,6 +309,7 @@ TIntermTyped* TParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symb if (symbol && symbol->getNumExtensions()) requireExtensions(loc, symbol->getNumExtensions(), symbol->getExtensions(), symbol->getName().c_str()); +#ifndef GLSLANG_WEB if (symbol && symbol->isReadOnly()) { // All shared things containing an unsized array must be copied up // on first use, so that all future references will share its array structure, @@ -307,11 +319,17 @@ TIntermTyped* TParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symb // If this is a variable or a block, check it and all it contains, but if this // is a member of an anonymous block, check the whole block, as the whole block // will need to be copied up if it contains an unsized array. - if (symbol->getType().containsUnsizedArray() || - (symbol->getAsAnonMember() && - symbol->getAsAnonMember()->getAnonContainer().getType().containsUnsizedArray())) - makeEditable(symbol); + // + // This check is being done before the block-name check further down, so guard + // for that too. + if (!symbol->getType().isUnusableName()) { + if (symbol->getType().containsUnsizedArray() || + (symbol->getAsAnonMember() && + symbol->getAsAnonMember()->getAnonContainer().getType().containsUnsizedArray())) + makeEditable(symbol); + } } +#endif const TVariable* variable; const TAnonMember* anon = symbol ? symbol->getAsAnonMember() : nullptr; @@ -334,8 +352,7 @@ TIntermTyped* TParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symb // See if it was a variable. variable = symbol ? symbol->getAsVariable() : nullptr; if (variable) { - if ((variable->getType().getBasicType() == EbtBlock || - variable->getType().getBasicType() == EbtStruct) && variable->getType().getStruct() == nullptr) { + if (variable->getType().isUnusableName()) { error(loc, "cannot be used (maybe an instance name is needed)", string->c_str(), ""); variable = nullptr; } @@ -357,7 +374,7 @@ TIntermTyped* TParseContext::handleVariable(const TSourceLoc& loc, TSymbol* symb if (variable->getType().getQualifier().isIo()) intermediate.addIoAccessed(*string); - if (variable->getType().getBasicType() == EbtReference && + if (variable->getType().isReference() && variable->getType().getQualifier().bufferReferenceNeedsVulkanMemoryModel()) { intermediate.setUseVulkanMemoryModel(); } @@ -378,7 +395,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn variableCheck(base); if (! base->isArray() && ! base->isMatrix() && ! base->isVector() && ! base->getType().isCoopMat() && - base->getBasicType() != EbtReference) { + ! base->isReference()) { if (base->getAsSymbolNode()) error(loc, " left of '[' is not of type array, matrix, or vector ", base->getAsSymbolNode()->getName().c_str(), ""); else @@ -389,7 +406,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn } if (!base->isArray() && base->isVector()) { - if (base->getType().containsBasicType(EbtFloat16)) + if (base->getType().contains16BitFloat()) requireFloat16Arithmetic(loc, "[", "does not operate on types containing float16"); if (base->getType().contains16BitInt()) requireInt16Arithmetic(loc, "[", "does not operate on types containing (u)int16"); @@ -407,23 +424,24 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn // at least one of base and index is not a front-end constant variable... TIntermTyped* result = nullptr; - if (base->getBasicType() == EbtReference && ! base->isArray()) { +#ifndef GLSLANG_WEB + if (base->isReference() && ! base->isArray()) { requireExtensions(loc, 1, &E_GL_EXT_buffer_reference2, "buffer reference indexing"); result = intermediate.addBinaryMath(EOpAdd, base, index, loc); result->setType(base->getType()); return result; } + if (base->getAsSymbolNode() && isIoResizeArray(base->getType())) + handleIoResizeArrayAccess(loc, base); +#endif if (index->getQualifier().isFrontEndConstant()) checkIndex(loc, base->getType(), indexValue); - if (base->getAsSymbolNode() && isIoResizeArray(base->getType())) - handleIoResizeArrayAccess(loc, base); - if (index->getQualifier().isFrontEndConstant()) { +#ifndef GLSLANG_WEB if (base->getType().isUnsizedArray()) { base->getWritableType().updateImplicitArraySize(indexValue + 1); -#ifdef NV_EXTENSIONS // For 2D per-view builtin arrays, update the inner dimension size in parent type if (base->getQualifier().isPerView() && base->getQualifier().builtIn != EbvNone) { TIntermBinary* binaryNode = base->getAsBinaryNode(); @@ -434,11 +452,12 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn arraySizes.setDimSize(1, std::max(arraySizes.getDimSize(1), indexValue + 1)); } } -#endif } else +#endif checkIndex(loc, base->getType(), indexValue); result = intermediate.addIndex(EOpIndexDirect, base, index, loc); } else { +#ifndef GLSLANG_WEB if (base->getType().isUnsizedArray()) { // we have a variable index into an unsized array, which is okay, // depending on the situation @@ -450,6 +469,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn } base->getWritableType().setArrayVariablyIndexed(); } +#endif if (base->getBasicType() == EbtBlock) { if (base->getQualifier().storage == EvqBuffer) requireProfile(base->getLoc(), ~EEsProfile, "variable indexing buffer block array"); @@ -457,7 +477,7 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn profileRequires(base->getLoc(), EEsProfile, 320, Num_AEP_gpu_shader5, AEP_gpu_shader5, "variable indexing uniform block array"); else { - // input/output blocks either don't exist or can be variable indexed + // input/output blocks either don't exist or can't be variably indexed } } else if (language == EShLangFragment && base->getQualifier().isPipeOutput()) requireProfile(base->getLoc(), ~EEsProfile, "variable indexing fragment shader output array"); @@ -471,8 +491,8 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn result = intermediate.addIndex(EOpIndexIndirect, base, index, loc); } - // Insert valid dereferenced result - TType newType(base->getType(), 0); // dereferenced type + // Insert valid dereferenced result type + TType newType(base->getType(), 0); if (base->getType().getQualifier().isConstant() && index->getQualifier().isConstant()) { newType.getQualifier().storage = EvqConst; // If base or index is a specialization constant, the result should also be a specialization constant. @@ -480,20 +500,27 @@ TIntermTyped* TParseContext::handleBracketDereference(const TSourceLoc& loc, TIn newType.getQualifier().makeSpecConstant(); } } else { - newType.getQualifier().makePartialTemporary(); + newType.getQualifier().storage = EvqTemporary; + newType.getQualifier().specConstant = false; } result->setType(newType); +#ifndef GLSLANG_WEB + inheritMemoryQualifiers(base->getQualifier(), result->getWritableType().getQualifier()); + // Propagate nonuniform if (base->getQualifier().isNonUniform() || index->getQualifier().isNonUniform()) result->getWritableType().getQualifier().nonUniform = true; if (anyIndexLimits) handleIndexLimits(loc, base, index); +#endif return result; } +#ifndef GLSLANG_WEB + // for ES 2.0 (version 100) limitations for almost all index operations except vertex-shader uniforms void TParseContext::handleIndexLimits(const TSourceLoc& /*loc*/, TIntermTyped* base, TIntermTyped* index) { @@ -530,14 +557,12 @@ bool TParseContext::isIoResizeArray(const TType& type) const { return type.isArray() && ((language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn) || - (language == EShLangTessControl && type.getQualifier().storage == EvqVaryingOut && ! type.getQualifier().patch) -#ifdef NV_EXTENSIONS - || - (language == EShLangFragment && type.getQualifier().storage == EvqVaryingIn && type.getQualifier().pervertexNV) || - (language == EShLangMeshNV && type.getQualifier().storage == EvqVaryingOut && !type.getQualifier().perTaskNV) - -#endif - ); + (language == EShLangTessControl && type.getQualifier().storage == EvqVaryingOut && + ! type.getQualifier().patch) || + (language == EShLangFragment && type.getQualifier().storage == EvqVaryingIn && + type.getQualifier().pervertexNV) || + (language == EShLangMeshNV && type.getQualifier().storage == EvqVaryingOut && + !type.getQualifier().perTaskNV)); } // If an array is not isIoResizeArray() but is an io array, make sure it has the right size @@ -566,11 +591,7 @@ void TParseContext::fixIoArraySize(const TSourceLoc& loc, TType& type) void TParseContext::ioArrayCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) { if (! type.isArray() && ! symbolTable.atBuiltInLevel()) { - if (type.getQualifier().isArrayedIo(language) -#ifdef NV_EXTENSIONS - && !type.getQualifier().layoutPassthrough -#endif - ) + if (type.getQualifier().isArrayedIo(language) && !type.getQualifier().layoutPassthrough) error(loc, "type must be an array:", type.getStorageQualifierString(), identifier.c_str()); } } @@ -617,12 +638,7 @@ void TParseContext::checkIoArraysConsistency(const TSourceLoc &loc, bool tailOnl // As I/O array sizes don't change, fetch requiredSize only once, // except for mesh shaders which could have different I/O array sizes based on type qualifiers. - if (firstIteration -#ifdef NV_EXTENSIONS - || (language == EShLangMeshNV) -#endif - ) - { + if (firstIteration || (language == EShLangMeshNV)) { requiredSize = getIoArrayImplicitSize(type.getQualifier(), &featureString); if (requiredSize == 0) break; @@ -647,14 +663,11 @@ int TParseContext::getIoArrayImplicitSize(const TQualifier &qualifier, TString * else if (language == EShLangTessControl) { expectedSize = maxVertices; str = "vertices"; - } -#ifdef NV_EXTENSIONS - else if (language == EShLangFragment) { + } else if (language == EShLangFragment) { // Number of vertices for Fragment shader is always three. expectedSize = 3; str = "vertices"; - } - else if (language == EShLangMeshNV) { + } else if (language == EShLangMeshNV) { unsigned int maxPrimitives = intermediate.getPrimitives() != TQualifier::layoutNotSet ? intermediate.getPrimitives() : 0; if (qualifier.builtIn == EbvPrimitiveIndicesNV) { @@ -671,7 +684,6 @@ int TParseContext::getIoArrayImplicitSize(const TQualifier &qualifier, TString * str = "max_vertices"; } } -#endif if (featureString) *featureString = str; return expectedSize; @@ -686,19 +698,19 @@ void TParseContext::checkIoArrayConsistency(const TSourceLoc& loc, int requiredS error(loc, "inconsistent input primitive for array size of", feature, name.c_str()); else if (language == EShLangTessControl) error(loc, "inconsistent output number of vertices for array size of", feature, name.c_str()); -#ifdef NV_EXTENSIONS else if (language == EShLangFragment) { if (type.getOuterArraySize() > requiredSize) error(loc, " cannot be greater than 3 for pervertexNV", feature, name.c_str()); } else if (language == EShLangMeshNV) error(loc, "inconsistent output array size of", feature, name.c_str()); -#endif else assert(0); } } +#endif // GLSLANG_WEB + // Handle seeing a binary node with a math operation. // Returns nullptr if not semantically allowed. TIntermTyped* TParseContext::handleBinaryMath(const TSourceLoc& loc, const char* str, TOperator op, TIntermTyped* left, TIntermTyped* right) @@ -721,7 +733,7 @@ TIntermTyped* TParseContext::handleBinaryMath(const TSourceLoc& loc, const char* break; } - if (((left->getType().containsBasicType(EbtFloat16) || right->getType().containsBasicType(EbtFloat16)) && !float16Arithmetic()) || + if (((left->getType().contains16BitFloat() || right->getType().contains16BitFloat()) && !float16Arithmetic()) || ((left->getType().contains16BitInt() || right->getType().contains16BitInt()) && !int16Arithmetic()) || ((left->getType().contains8BitInt() || right->getType().contains8BitInt()) && !int8Arithmetic())) { allowed = false; @@ -743,14 +755,13 @@ TIntermTyped* TParseContext::handleUnaryMath(const TSourceLoc& loc, const char* rValueErrorCheck(loc, str, childNode); bool allowed = true; - if ((childNode->getType().containsBasicType(EbtFloat16) && !float16Arithmetic()) || + if ((childNode->getType().contains16BitFloat() && !float16Arithmetic()) || (childNode->getType().contains16BitInt() && !int16Arithmetic()) || (childNode->getType().contains8BitInt() && !int8Arithmetic())) { allowed = false; } TIntermTyped* result = nullptr; - if (allowed) result = intermediate.addUnaryMath(op, childNode, loc); @@ -819,7 +830,7 @@ TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TInterm TSwizzleSelectors<TVectorSelector> selectors; parseSwizzleSelector(loc, field, base->getVectorSize(), selectors); - if (base->isVector() && selectors.size() != 1 && base->getType().containsBasicType(EbtFloat16)) + if (base->isVector() && selectors.size() != 1 && base->getType().contains16BitFloat()) requireFloat16Arithmetic(loc, ".", "can't swizzle types containing float16"); if (base->isVector() && selectors.size() != 1 && base->getType().contains16BitInt()) requireInt16Arithmetic(loc, ".", "can't swizzle types containing (u)int16"); @@ -854,12 +865,10 @@ TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TInterm if (base->getType().getQualifier().isSpecConstant()) result->getWritableType().getQualifier().makeSpecConstant(); } - } else if (base->getBasicType() == EbtStruct || - base->getBasicType() == EbtBlock || - base->getBasicType() == EbtReference) { - const TTypeList* fields = base->getBasicType() == EbtReference ? - base->getType().getReferentType()->getStruct() : - base->getType().getStruct(); + } else if (base->isStruct() || base->isReference()) { + const TTypeList* fields = base->isReference() ? + base->getType().getReferentType()->getStruct() : + base->getType().getStruct(); bool fieldFound = false; int member; for (member = 0; member < (int)fields->size(); ++member) { @@ -879,14 +888,15 @@ TIntermTyped* TParseContext::handleDotDereference(const TSourceLoc& loc, TInterm if ((*fields)[member].type->getQualifier().isIo()) intermediate.addIoAccessed(field); } + inheritMemoryQualifiers(base->getQualifier(), result->getWritableType().getQualifier()); } else error(loc, "no such field in structure", field.c_str(), ""); } else error(loc, "does not apply to this type:", field.c_str(), base->getType().getCompleteString().c_str()); // Propagate noContraction up the dereference chain - if (base->getQualifier().noContraction) - result->getWritableType().getQualifier().noContraction = true; + if (base->getQualifier().isNoContraction()) + result->getWritableType().getQualifier().setNoContraction(); // Propagate nonuniform if (base->getQualifier().isNonUniform()) @@ -1126,7 +1136,7 @@ TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction if (builtIn && fnCandidate->getNumExtensions()) requireExtensions(loc, fnCandidate->getNumExtensions(), fnCandidate->getExtensions(), fnCandidate->getName().c_str()); - if (builtIn && fnCandidate->getType().containsBasicType(EbtFloat16)) + if (builtIn && fnCandidate->getType().contains16BitFloat()) requireFloat16Arithmetic(loc, "built-in function", "float16 types can only be in uniform block or buffer storage"); if (builtIn && fnCandidate->getType().contains16BitInt()) requireInt16Arithmetic(loc, "built-in function", "(u)int16 types can only be in uniform block or buffer storage"); @@ -1146,9 +1156,11 @@ TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction if (lValueErrorCheck(arguments->getLoc(), "assign", arg->getAsTyped())) error(arguments->getLoc(), "Non-L-value cannot be passed for 'out' or 'inout' parameters.", "out", ""); } - TQualifier& argQualifier = arg->getAsTyped()->getQualifier(); - if (argQualifier.isMemory()) { + const TType& argType = arg->getAsTyped()->getType(); + const TQualifier& argQualifier = argType.getQualifier(); + if (argQualifier.isMemory() && (argType.containsOpaque() || argType.isReference())) { const char* message = "argument cannot drop memory qualifier when passed to formal parameter"; +#ifndef GLSLANG_WEB if (argQualifier.volatil && ! formalQualifier.volatil) error(arguments->getLoc(), message, "volatile", ""); if (argQualifier.coherent && ! (formalQualifier.devicecoherent || formalQualifier.coherent)) @@ -1165,18 +1177,19 @@ TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction error(arguments->getLoc(), message, "readonly", ""); if (argQualifier.writeonly && ! formalQualifier.writeonly) error(arguments->getLoc(), message, "writeonly", ""); - if (!builtIn && argQualifier.restrict && ! formalQualifier.restrict) - error(arguments->getLoc(), message, "restrict", ""); + // Don't check 'restrict', it is different than the rest: + // "...but only restrict can be taken away from a calling argument, by a formal parameter that + // lacks the restrict qualifier..." +#endif } - if (!builtIn && argQualifier.layoutFormat != formalQualifier.layoutFormat) { + if (!builtIn && argQualifier.getFormat() != formalQualifier.getFormat()) { // we have mismatched formats, which should only be allowed if writeonly // and at least one format is unknown - if (!formalQualifier.writeonly || (formalQualifier.layoutFormat != ElfNone && - argQualifier.layoutFormat != ElfNone)) + if (!formalQualifier.isWriteOnly() || (formalQualifier.getFormat() != ElfNone && + argQualifier.getFormat() != ElfNone)) error(arguments->getLoc(), "image formats must match", "format", ""); } - - if (builtIn && arg->getAsTyped()->getType().containsBasicType(EbtFloat16)) + if (builtIn && arg->getAsTyped()->getType().contains16BitFloat()) requireFloat16Arithmetic(arguments->getLoc(), "built-in function", "float16 types can only be in uniform block or buffer storage"); if (builtIn && arg->getAsTyped()->getType().contains16BitInt()) requireInt16Arithmetic(arguments->getLoc(), "built-in function", "(u)int16 types can only be in uniform block or buffer storage"); @@ -1216,9 +1229,11 @@ TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction intermediate.addToCallGraph(infoSink, currentCaller, fnCandidate->getMangledName()); } +#ifndef GLSLANG_WEB if (builtIn) nonOpBuiltInCheck(loc, *fnCandidate, *call); else +#endif userFunctionCallCheck(loc, *call); } @@ -1347,13 +1362,9 @@ void TParseContext::computeBuiltinPrecisions(TIntermTyped& node, const TFunction operationPrecision = std::max(operationPrecision, function[arg].type->getQualifier().precision); } // compute the result precision -#ifdef AMD_EXTENSIONS if (agg->isSampling() || agg->getOp() == EOpImageLoad || agg->getOp() == EOpImageStore || agg->getOp() == EOpImageLoadLod || agg->getOp() == EOpImageStoreLod) -#else - if (agg->isSampling() || agg->getOp() == EOpImageLoad || agg->getOp() == EOpImageStore) -#endif resultPrecision = sequence[0]->getAsTyped()->getQualifier().precision; else if (function.getType().getBasicType() != EbtBool) resultPrecision = function.getType().getQualifier().precision == EpqNone ? @@ -1374,7 +1385,9 @@ void TParseContext::computeBuiltinPrecisions(TIntermTyped& node, const TFunction TIntermNode* TParseContext::handleReturnValue(const TSourceLoc& loc, TIntermTyped* value) { +#ifndef GLSLANG_WEB storage16BitAssignmentCheck(loc, value->getType(), "return"); +#endif functionReturnsValue = true; if (currentFunctionType->getBasicType() == EbtVoid) { @@ -1399,6 +1412,7 @@ TIntermNode* TParseContext::handleReturnValue(const TSourceLoc& loc, TIntermType // See if the operation is being done in an illegal location. void TParseContext::checkLocation(const TSourceLoc& loc, TOperator op) { +#ifndef GLSLANG_WEB switch (op) { case EOpBarrier: if (language == EShLangTessControl) { @@ -1410,9 +1424,48 @@ void TParseContext::checkLocation(const TSourceLoc& loc, TOperator op) error(loc, "tessellation control barrier() cannot be placed after a return from main()", "", ""); } break; + case EOpBeginInvocationInterlock: + if (language != EShLangFragment) + error(loc, "beginInvocationInterlockARB() must be in a fragment shader", "", ""); + if (! inMain) + error(loc, "beginInvocationInterlockARB() must be in main()", "", ""); + else if (postEntryPointReturn) + error(loc, "beginInvocationInterlockARB() cannot be placed after a return from main()", "", ""); + if (controlFlowNestingLevel > 0) + error(loc, "beginInvocationInterlockARB() cannot be placed within flow control", "", ""); + + if (beginInvocationInterlockCount > 0) + error(loc, "beginInvocationInterlockARB() must only be called once", "", ""); + if (endInvocationInterlockCount > 0) + error(loc, "beginInvocationInterlockARB() must be called before endInvocationInterlockARB()", "", ""); + + beginInvocationInterlockCount++; + + // default to pixel_interlock_ordered + if (intermediate.getInterlockOrdering() == EioNone) + intermediate.setInterlockOrdering(EioPixelInterlockOrdered); + break; + case EOpEndInvocationInterlock: + if (language != EShLangFragment) + error(loc, "endInvocationInterlockARB() must be in a fragment shader", "", ""); + if (! inMain) + error(loc, "endInvocationInterlockARB() must be in main()", "", ""); + else if (postEntryPointReturn) + error(loc, "endInvocationInterlockARB() cannot be placed after a return from main()", "", ""); + if (controlFlowNestingLevel > 0) + error(loc, "endInvocationInterlockARB() cannot be placed within flow control", "", ""); + + if (endInvocationInterlockCount > 0) + error(loc, "endInvocationInterlockARB() must only be called once", "", ""); + if (beginInvocationInterlockCount == 0) + error(loc, "beginInvocationInterlockARB() must be called before endInvocationInterlockARB()", "", ""); + + endInvocationInterlockCount++; + break; default: break; } +#endif } // Finish processing object.length(). This started earlier in handleDotDereference(), where @@ -1430,29 +1483,28 @@ TIntermTyped* TParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction const TType& type = intermNode->getAsTyped()->getType(); if (type.isArray()) { if (type.isUnsizedArray()) { +#ifndef GLSLANG_WEB if (intermNode->getAsSymbolNode() && isIoResizeArray(type)) { // We could be between a layout declaration that gives a built-in io array implicit size and // a user redeclaration of that array, meaning we have to substitute its implicit size here // without actually redeclaring the array. (It is an error to use a member before the // redeclaration, but not an error to use the array name itself.) const TString& name = intermNode->getAsSymbolNode()->getName(); - if (name == "gl_in" || name == "gl_out" -#ifdef NV_EXTENSIONS - || name == "gl_MeshVerticesNV" - || name == "gl_MeshPrimitivesNV" -#endif - ) - { + if (name == "gl_in" || name == "gl_out" || name == "gl_MeshVerticesNV" || + name == "gl_MeshPrimitivesNV") { length = getIoArrayImplicitSize(type.getQualifier()); } } +#endif if (length == 0) { +#ifndef GLSLANG_WEB if (intermNode->getAsSymbolNode() && isIoResizeArray(type)) error(loc, "", function->getName().c_str(), "array must first be sized by a redeclaration or layout qualifier"); else if (isRuntimeLength(*intermNode->getAsTyped())) { // Create a unary op and let the back end handle it return intermediate.addBuiltInFunctionCall(loc, EOpArrayLength, true, intermNode, TType(EbtInt)); } else +#endif error(loc, "", function->getName().c_str(), "array must be declared with a size before using this method"); } } else if (type.getOuterArrayNode()) { @@ -1485,6 +1537,7 @@ TIntermTyped* TParseContext::handleLengthMethod(const TSourceLoc& loc, TFunction // void TParseContext::addInputArgumentConversions(const TFunction& function, TIntermNode*& arguments) const { +#ifndef GLSLANG_WEB TIntermAggregate* aggregate = arguments->getAsAggregate(); // Process each argument's conversion @@ -1512,6 +1565,7 @@ void TParseContext::addInputArgumentConversions(const TFunction& function, TInte } } } +#endif } // @@ -1523,6 +1577,9 @@ void TParseContext::addInputArgumentConversions(const TFunction& function, TInte // TIntermTyped* TParseContext::addOutputArgumentConversions(const TFunction& function, TIntermAggregate& intermNode) const { +#ifdef GLSLANG_WEB + return &intermNode; +#else TIntermSequence& arguments = intermNode.getSequence(); // Will there be any output conversions? @@ -1590,6 +1647,7 @@ TIntermTyped* TParseContext::addOutputArgumentConversions(const TFunction& funct conversionTree = intermediate.setAggregateOperator(conversionTree, EOpComma, intermNode.getType(), intermNode.getLoc()); return conversionTree; +#endif } void TParseContext::memorySemanticsCheck(const TSourceLoc& loc, const TFunction& fnCandidate, const TIntermOperator& callNode) @@ -1602,6 +1660,7 @@ void TParseContext::memorySemanticsCheck(const TSourceLoc& loc, const TFunction& const int gl_SemanticsAcquireRelease = 0x8; const int gl_SemanticsMakeAvailable = 0x2000; const int gl_SemanticsMakeVisible = 0x4000; + const int gl_SemanticsVolatile = 0x8000; //const int gl_StorageSemanticsNone = 0x0; const int gl_StorageSemanticsBuffer = 0x40; @@ -1691,7 +1750,8 @@ void TParseContext::memorySemanticsCheck(const TSourceLoc& loc, const TFunction& gl_SemanticsRelease | gl_SemanticsAcquireRelease | gl_SemanticsMakeAvailable | - gl_SemanticsMakeVisible))) { + gl_SemanticsMakeVisible | + gl_SemanticsVolatile))) { error(loc, "Invalid semantics value", fnCandidate.getName().c_str(), ""); } if (((storageClassSemantics | storageClassSemantics2) & ~(gl_StorageSemanticsBuffer | @@ -1743,10 +1803,18 @@ void TParseContext::memorySemanticsCheck(const TSourceLoc& loc, const TFunction& error(loc, "gl_SemanticsMakeVisible requires gl_SemanticsAcquire or gl_SemanticsAcquireRelease", fnCandidate.getName().c_str(), ""); } - + if ((semantics & gl_SemanticsVolatile) && + (callNode.getOp() == EOpMemoryBarrier || callNode.getOp() == EOpBarrier)) { + error(loc, "gl_SemanticsVolatile must not be used with memoryBarrier or controlBarrier", + fnCandidate.getName().c_str(), ""); + } + if ((callNode.getOp() == EOpAtomicCompSwap || callNode.getOp() == EOpImageAtomicCompSwap) && + ((semantics ^ semantics2) & gl_SemanticsVolatile)) { + error(loc, "semEqual and semUnequal must either both include gl_SemanticsVolatile or neither", + fnCandidate.getName().c_str(), ""); + } } - // // Do additional checking of built-in function calls that is not caught // by normal semantic checks on argument type, extension tagging, etc. @@ -1774,6 +1842,7 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan TString featureString; const char* feature = nullptr; switch (callNode.getOp()) { +#ifndef GLSLANG_WEB case EOpTextureGather: case EOpTextureGatherOffset: case EOpTextureGatherOffsets: @@ -1830,7 +1899,6 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan error(loc, "must be a compile-time constant:", feature, "component argument"); } -#ifdef AMD_EXTENSIONS bool bias = false; if (callNode.getOp() == EOpTextureGather) bias = fnCandidate.getParamCount() > 3; @@ -1845,12 +1913,8 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan profileRequires(loc, ~EEsProfile, 450, nullptr, feature); requireExtensions(loc, 1, &E_GL_AMD_texture_gather_bias_lod, feature); } -#endif - break; } - -#ifdef AMD_EXTENSIONS case EOpSparseTextureGather: case EOpSparseTextureGatherOffset: case EOpSparseTextureGatherOffsets: @@ -1928,7 +1992,7 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan int arg = -1; switch (callNode.getOp()) { case EOpTextureOffset: arg = 2; break; - case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().dim != EsdRect) ? 3 : 2; break; + case EOpTextureFetchOffset: arg = (arg0->getType().getSampler().isRect()) ? 2 : 3; break; case EOpTextureProjOffset: arg = 2; break; case EOpTextureLodOffset: arg = 3; break; case EOpTextureProjLodOffset: arg = 3; break; @@ -1941,7 +2005,7 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan if (arg > 0) { -#ifdef AMD_EXTENSIONS +#ifndef GLSLANG_WEB bool f16ShadowCompare = (*argp)[1]->getAsTyped()->getBasicType() == EbtFloat16 && arg0->getType().getSampler().shadow; if (f16ShadowCompare) ++arg; @@ -1961,7 +2025,7 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan break; } -#ifdef NV_EXTENSIONS +#ifndef GLSLANG_WEB case EOpTraceNV: if (!(*argp)[10]->getAsConstantUnion()) error(loc, "argument must be compile-time constant", "payload number", ""); @@ -1970,7 +2034,6 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan if (!(*argp)[1]->getAsConstantUnion()) error(loc, "argument must be compile-time constant", "callable data number", ""); break; -#endif case EOpTextureQuerySamples: case EOpImageQuerySamples: @@ -1992,12 +2055,12 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan // Make sure the image types have the correct layout() format and correct argument types const TType& imageType = arg0->getType(); if (imageType.getSampler().type == EbtInt || imageType.getSampler().type == EbtUint) { - if (imageType.getQualifier().layoutFormat != ElfR32i && imageType.getQualifier().layoutFormat != ElfR32ui) + if (imageType.getQualifier().getFormat() != ElfR32i && imageType.getQualifier().getFormat() != ElfR32ui) error(loc, "only supported on image with format r32i or r32ui", fnCandidate.getName().c_str(), ""); } else { if (fnCandidate.getName().compare(0, 19, "imageAtomicExchange") != 0) error(loc, "only supported on integer images", fnCandidate.getName().c_str(), ""); - else if (imageType.getQualifier().layoutFormat != ElfR32f && profile == EEsProfile) + else if (imageType.getQualifier().getFormat() != ElfR32f && isEsProfile()) error(loc, "only supported on image with format r32f", fnCandidate.getName().c_str(), ""); } @@ -2025,13 +2088,9 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan requireExtensions(loc, 1, &E_GL_KHR_memory_scope_semantics, fnCandidate.getName().c_str()); memorySemanticsCheck(loc, fnCandidate, callNode); } else if (arg0->getType().getBasicType() == EbtInt64 || arg0->getType().getBasicType() == EbtUint64) { -#ifdef NV_EXTENSIONS const char* const extensions[2] = { E_GL_NV_shader_atomic_int64, E_GL_EXT_shader_atomic_int64 }; requireExtensions(loc, 2, extensions, fnCandidate.getName().c_str()); -#else - requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_int64, fnCandidate.getName().c_str()); -#endif } break; } @@ -2039,9 +2098,7 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan case EOpInterpolateAtCentroid: case EOpInterpolateAtSample: case EOpInterpolateAtOffset: -#ifdef AMD_EXTENSIONS case EOpInterpolateAtVertex: -#endif // Make sure the first argument is an interpolant, or an array element of an interpolant if (arg0->getType().getQualifier().storage != EvqVaryingIn) { // It might still be an array element. @@ -2051,13 +2108,12 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan // // ES and desktop 4.3 and earlier: swizzles may not be used // desktop 4.4 and later: swizzles may be used - bool swizzleOkay = (profile != EEsProfile) && (version >= 440); + bool swizzleOkay = (!isEsProfile()) && (version >= 440); const TIntermTyped* base = TIntermediate::findLValueBase(arg0, swizzleOkay); if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn) error(loc, "first argument must be an interpolant, or interpolant-array element", fnCandidate.getName().c_str(), ""); } -#ifdef AMD_EXTENSIONS if (callNode.getOp() == EOpInterpolateAtVertex) { if (!arg0->getType().getQualifier().isExplicitInterpolation()) error(loc, "argument must be qualified as __explicitInterpAMD in", "interpolant", ""); @@ -2071,8 +2127,6 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan } } } -#endif - break; case EOpEmitStreamVertex: @@ -2103,9 +2157,12 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan break; case EOpSubgroupBroadcast: - // <id> must be an integral constant expression. - if ((*argp)[1]->getAsConstantUnion() == nullptr) - error(loc, "argument must be compile-time constant", "id", ""); + case EOpSubgroupQuadBroadcast: + if (spvVersion.spv < EShTargetSpv_1_5) { + // <id> must be an integral constant expression. + if ((*argp)[1]->getAsConstantUnion() == nullptr) + error(loc, "argument must be compile-time constant", "id", ""); + } break; case EOpBarrier: @@ -2115,6 +2172,7 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan memorySemanticsCheck(loc, fnCandidate, callNode); } break; +#endif default: break; @@ -2132,7 +2190,7 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan const TSampler& sampler = fnCandidate[0].type->getSampler(); const bool isTexture = sampler.isTexture() && !sampler.isCombined(); - const bool isBuffer = sampler.dim == EsdBuffer; + const bool isBuffer = sampler.isBuffer(); const bool isFetch = callNode.getOp() == EOpTextureFetch || callNode.getOp() == EOpTextureFetchOffset; if (isTexture && (!isBuffer || !isFetch)) @@ -2145,13 +2203,39 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan break; } - if (callNode.getOp() > EOpSubgroupGuardStart && callNode.getOp() < EOpSubgroupGuardStop) { + if (callNode.isSubgroup()) { // these require SPIR-V 1.3 if (spvVersion.spv > 0 && spvVersion.spv < EShTargetSpv_1_3) error(loc, "requires SPIR-V 1.3", "subgroup op", ""); + + // Check that if extended types are being used that the correct extensions are enabled. + if (arg0 != nullptr) { + const TType& type = arg0->getType(); + switch (type.getBasicType()) { + default: + break; + case EbtInt8: + case EbtUint8: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_int8, type.getCompleteString().c_str()); + break; + case EbtInt16: + case EbtUint16: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_int16, type.getCompleteString().c_str()); + break; + case EbtInt64: + case EbtUint64: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_int64, type.getCompleteString().c_str()); + break; + case EbtFloat16: + requireExtensions(loc, 1, &E_GL_EXT_shader_subgroup_extended_types_float16, type.getCompleteString().c_str()); + break; + } + } } } +#ifndef GLSLANG_WEB + extern bool PureOperatorBuiltins; // Deprecated! Use PureOperatorBuiltins == true instead, in which case this @@ -2267,17 +2351,19 @@ void TParseContext::nonOpBuiltInCheck(const TSourceLoc& loc, const TFunction& fn if (fnCandidate.getName().compare(0, 11, "imageAtomic") == 0) { const TType& imageType = callNode.getSequence()[0]->getAsTyped()->getType(); if (imageType.getSampler().type == EbtInt || imageType.getSampler().type == EbtUint) { - if (imageType.getQualifier().layoutFormat != ElfR32i && imageType.getQualifier().layoutFormat != ElfR32ui) + if (imageType.getQualifier().getFormat() != ElfR32i && imageType.getQualifier().getFormat() != ElfR32ui) error(loc, "only supported on image with format r32i or r32ui", fnCandidate.getName().c_str(), ""); } else { if (fnCandidate.getName().compare(0, 19, "imageAtomicExchange") != 0) error(loc, "only supported on integer images", fnCandidate.getName().c_str(), ""); - else if (imageType.getQualifier().layoutFormat != ElfR32f && profile == EEsProfile) + else if (imageType.getQualifier().getFormat() != ElfR32f && isEsProfile()) error(loc, "only supported on image with format r32f", fnCandidate.getName().c_str(), ""); } } } +#endif + // // Do any extra checking for a user function call. // @@ -2425,6 +2511,7 @@ bool TParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TInt bool errorReturn = false; switch(binaryNode->getOp()) { +#ifndef GLSLANG_WEB case EOpIndexDirect: case EOpIndexIndirect: // ... tessellation control shader ... @@ -2440,10 +2527,8 @@ bool TParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TInt error(loc, "tessellation-control per-vertex output l-value must be indexed with gl_InvocationID", "[]", ""); } } - - break; // left node is checked by base class - case EOpIndexDirectStruct: break; // left node is checked by base class +#endif case EOpVectorSwizzle: errorReturn = lValueErrorCheck(loc, op, binaryNode->getLeft()); if (!errorReturn) { @@ -2475,8 +2560,7 @@ bool TParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TInt } } - if (binaryNode && binaryNode->getOp() == EOpIndexDirectStruct && - binaryNode->getLeft()->getBasicType() == EbtReference) + if (binaryNode && binaryNode->getOp() == EOpIndexDirectStruct && binaryNode->getLeft()->isReference()) return false; // Let the base class check errors @@ -2499,7 +2583,7 @@ bool TParseContext::lValueErrorCheck(const TSourceLoc& loc, const char* op, TInt case EvqFragDepth: intermediate.setDepthReplacing(); // "In addition, it is an error to statically write to gl_FragDepth in the fragment shader." - if (profile == EEsProfile && intermediate.getEarlyFragmentTests()) + if (isEsProfile() && intermediate.getEarlyFragmentTests()) message = "can't modify gl_FragDepth if using early_fragment_tests"; break; @@ -2536,12 +2620,10 @@ void TParseContext::rValueErrorCheck(const TSourceLoc& loc, const char* op, TInt // Let the base class check errors TParseContextBase::rValueErrorCheck(loc, op, node); -#ifdef AMD_EXTENSIONS TIntermSymbol* symNode = node->getAsSymbolNode(); - if (!(symNode && symNode->getQualifier().writeonly)) // base class checks - if (symNode && symNode->getQualifier().explicitInterp) + if (!(symNode && symNode->getQualifier().isWriteOnly())) // base class checks + if (symNode && symNode->getQualifier().isExplicitInterpolation()) error(loc, "can't read from explicitly-interpolated object: ", op, symNode->getName().c_str()); -#endif } // @@ -2587,14 +2669,14 @@ void TParseContext::reservedErrorCheck(const TSourceLoc& loc, const TString& ide if (builtInName(identifier)) error(loc, "identifiers starting with \"gl_\" are reserved", identifier.c_str(), ""); - // "__" are not supposed to be an error. ES 310 (and desktop) added the clarification: + // "__" are not supposed to be an error. ES 300 (and desktop) added the clarification: // "In addition, all identifiers containing two consecutive underscores (__) are // reserved; using such a name does not itself result in an error, but may result // in undefined behavior." // however, before that, ES tests required an error. if (identifier.find("__") != TString::npos) { - if (profile == EEsProfile && version <= 300) - error(loc, "identifiers containing consecutive underscores (\"__\") are reserved, and an error if version <= 300", identifier.c_str(), ""); + if (isEsProfile() && version < 300) + error(loc, "identifiers containing consecutive underscores (\"__\") are reserved, and an error if version < 300", identifier.c_str(), ""); else warn(loc, "identifiers containing consecutive underscores (\"__\") are reserved", identifier.c_str(), ""); } @@ -2606,7 +2688,7 @@ void TParseContext::reservedErrorCheck(const TSourceLoc& loc, const TString& ide // void TParseContext::reservedPpErrorCheck(const TSourceLoc& loc, const char* identifier, const char* op) { - // "__" are not supposed to be an error. ES 310 (and desktop) added the clarification: + // "__" are not supposed to be an error. ES 300 (and desktop) added the clarification: // "All macro names containing two consecutive underscores ( __ ) are reserved; // defining such a name does not itself result in an error, but may result in // undefined behavior. All macro names prefixed with "GL_" ("GL" followed by a @@ -2618,14 +2700,14 @@ void TParseContext::reservedPpErrorCheck(const TSourceLoc& loc, const char* iden else if (strncmp(identifier, "defined", 8) == 0) ppError(loc, "\"defined\" can't be (un)defined:", op, identifier); else if (strstr(identifier, "__") != 0) { - if (profile == EEsProfile && version >= 300 && + if (isEsProfile() && version >= 300 && (strcmp(identifier, "__LINE__") == 0 || strcmp(identifier, "__FILE__") == 0 || strcmp(identifier, "__VERSION__") == 0)) ppError(loc, "predefined names can't be (un)defined:", op, identifier); else { - if (profile == EEsProfile && version <= 300) - ppError(loc, "names containing consecutive underscores are reserved, and an error if version <= 300:", op, identifier); + if (isEsProfile() && version < 300) + ppError(loc, "names containing consecutive underscores are reserved, and an error if version < 300:", op, identifier); else ppWarn(loc, "names containing consecutive underscores are reserved:", op, identifier); } @@ -2639,10 +2721,14 @@ void TParseContext::reservedPpErrorCheck(const TSourceLoc& loc, const char* iden // bool TParseContext::lineContinuationCheck(const TSourceLoc& loc, bool endOfComment) { +#ifdef GLSLANG_WEB + return true; +#endif + const char* message = "line continuation"; - bool lineContinuationAllowed = (profile == EEsProfile && version >= 300) || - (profile != EEsProfile && (version >= 420 || extensionTurnedOn(E_GL_ARB_shading_language_420pack))); + bool lineContinuationAllowed = (isEsProfile() && version >= 300) || + (!isEsProfile() && (version >= 420 || extensionTurnedOn(E_GL_ARB_shading_language_420pack))); if (endOfComment) { if (lineContinuationAllowed) @@ -2691,10 +2777,27 @@ bool TParseContext::builtInName(const TString& identifier) // bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, TFunction& function, TOperator op, TType& type) { - type.shallowCopy(function.getType()); + // See if the constructor does not establish the main type, only requalifies + // it, in which case the type comes from the argument instead of from the + // constructor function. + switch (op) { +#ifndef GLSLANG_WEB + case EOpConstructNonuniform: + if (node != nullptr && node->getAsTyped() != nullptr) { + type.shallowCopy(node->getAsTyped()->getType()); + type.getQualifier().makeTemporary(); + type.getQualifier().nonUniform = true; + } + break; +#endif + default: + type.shallowCopy(function.getType()); + break; + } + // See if it's a matrix bool constructingMatrix = false; - switch(op) { + switch (op) { case EOpConstructTextureSampler: return constructorTextureSamplerError(loc, function); case EOpConstructMat2x2: @@ -2706,6 +2809,7 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T case EOpConstructMat4x2: case EOpConstructMat4x3: case EOpConstructMat4x4: +#ifndef GLSLANG_WEB case EOpConstructDMat2x2: case EOpConstructDMat2x3: case EOpConstructDMat2x4: @@ -2724,6 +2828,7 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T case EOpConstructF16Mat4x2: case EOpConstructF16Mat4x3: case EOpConstructF16Mat4x4: +#endif constructingMatrix = true; break; default: @@ -2773,20 +2878,21 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T if (function[arg].type->isFloatingDomain()) floatArgument = true; if (type.isStruct()) { - if (function[arg].type->containsBasicType(EbtFloat16)) { + if (function[arg].type->contains16BitFloat()) { requireFloat16Arithmetic(loc, "constructor", "can't construct structure containing 16-bit type"); } - if (function[arg].type->containsBasicType(EbtUint16) || - function[arg].type->containsBasicType(EbtInt16)) { + if (function[arg].type->contains16BitInt()) { requireInt16Arithmetic(loc, "constructor", "can't construct structure containing 16-bit type"); } - if (function[arg].type->containsBasicType(EbtUint8) || - function[arg].type->containsBasicType(EbtInt8)) { + if (function[arg].type->contains8BitInt()) { requireInt8Arithmetic(loc, "constructor", "can't construct structure containing 8-bit type"); } } } + if (op == EOpConstructNonuniform) + constType = false; +#ifndef GLSLANG_WEB switch (op) { case EOpConstructFloat16: case EOpConstructF16Vec2: @@ -2826,6 +2932,7 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T default: break; } +#endif // inherit constness from children if (constType) { @@ -2834,17 +2941,24 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T if (specConstType) { switch (op) { case EOpConstructInt8: - case EOpConstructUint8: - case EOpConstructInt16: - case EOpConstructUint16: case EOpConstructInt: case EOpConstructUint: - case EOpConstructInt64: - case EOpConstructUint64: case EOpConstructBool: case EOpConstructBVec2: case EOpConstructBVec3: case EOpConstructBVec4: + case EOpConstructIVec2: + case EOpConstructIVec3: + case EOpConstructIVec4: + case EOpConstructUVec2: + case EOpConstructUVec3: + case EOpConstructUVec4: +#ifndef GLSLANG_WEB + case EOpConstructUint8: + case EOpConstructInt16: + case EOpConstructUint16: + case EOpConstructInt64: + case EOpConstructUint64: case EOpConstructI8Vec2: case EOpConstructI8Vec3: case EOpConstructI8Vec4: @@ -2857,18 +2971,13 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T case EOpConstructU16Vec2: case EOpConstructU16Vec3: case EOpConstructU16Vec4: - case EOpConstructIVec2: - case EOpConstructIVec3: - case EOpConstructIVec4: - case EOpConstructUVec2: - case EOpConstructUVec3: - case EOpConstructUVec4: case EOpConstructI64Vec2: case EOpConstructI64Vec3: case EOpConstructI64Vec4: case EOpConstructU64Vec2: case EOpConstructU64Vec3: case EOpConstructU64Vec4: +#endif // This was the list of valid ones, if they aren't converting from float // and aren't making an array. makeSpecConst = ! floatArgument && ! type.isArray(); @@ -2978,7 +3087,7 @@ bool TParseContext::constructorError(const TSourceLoc& loc, TIntermNode* node, T error(loc, "cannot convert a sampler", "constructor", ""); return true; } - if (op != EOpConstructStruct && typed->getBasicType() == EbtAtomicUint) { + if (op != EOpConstructStruct && typed->isAtomic()) { error(loc, "cannot convert an atomic_uint", "constructor", ""); return true; } @@ -3024,7 +3133,7 @@ bool TParseContext::constructorTextureSamplerError(const TSourceLoc& loc, const } // simulate the first argument's impact on the result type, so it can be compared with the encapsulated operator!=() TSampler texture = function.getType().getSampler(); - texture.combined = false; + texture.setCombined(false); texture.shadow = false; if (texture != function[0].type->getSampler()) { error(loc, "sampler-constructor first argument must match type and dimensionality of constructor type", token, ""); @@ -3076,14 +3185,14 @@ void TParseContext::samplerCheck(const TSourceLoc& loc, const TType& type, const { // Check that the appropriate extension is enabled if external sampler is used. // There are two extensions. The correct one must be used based on GLSL version. - if (type.getBasicType() == EbtSampler && type.getSampler().external) { + if (type.getBasicType() == EbtSampler && type.getSampler().isExternal()) { if (version < 300) { requireExtensions(loc, 1, &E_GL_OES_EGL_image_external, "samplerExternalOES"); } else { requireExtensions(loc, 1, &E_GL_OES_EGL_image_external_essl3, "samplerExternalOES"); } } - if (type.getSampler().yuv) { + if (type.getSampler().isYuv()) { requireExtensions(loc, 1, &E_GL_EXT_YUV_target, "__samplerExternal2DY2YEXT"); } @@ -3100,6 +3209,8 @@ void TParseContext::samplerCheck(const TSourceLoc& loc, const TType& type, const } } +#ifndef GLSLANG_WEB + void TParseContext::atomicUintCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) { if (type.getQualifier().storage == EvqUniform) @@ -3110,7 +3221,7 @@ void TParseContext::atomicUintCheck(const TSourceLoc& loc, const TType& type, co else if (type.getBasicType() == EbtAtomicUint && type.getQualifier().storage != EvqUniform) error(loc, "atomic_uints can only be used in uniform variables or function parameters:", type.getBasicTypeString().c_str(), identifier.c_str()); } -#ifdef NV_EXTENSIONS + void TParseContext::accStructNVCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) { if (type.getQualifier().storage == EvqUniform) @@ -3123,7 +3234,8 @@ void TParseContext::accStructNVCheck(const TSourceLoc& loc, const TType& type, c type.getBasicTypeString().c_str(), identifier.c_str()); } -#endif + +#endif // GLSLANG_WEB void TParseContext::transparentOpaqueCheck(const TSourceLoc& loc, const TType& type, const TString& identifier) { @@ -3188,7 +3300,7 @@ void TParseContext::globalQualifierFixCheck(const TSourceLoc& loc, TQualifier& q break; } - if (!nonuniformOkay && qualifier.nonUniform) + if (!nonuniformOkay && qualifier.isNonUniform()) error(loc, "for non-parameter, can only apply to 'in' or no storage qualifier", "nonuniformEXT", ""); invariantCheck(loc, qualifier); @@ -3202,7 +3314,7 @@ void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQuali if (! symbolTable.atGlobalLevel()) return; - if (!(publicType.userDef && publicType.userDef->getBasicType() == EbtReference)) { + if (!(publicType.userDef && publicType.userDef->isReference())) { if (qualifier.isMemoryQualifierImageAndSSBOOnly() && ! publicType.isImage() && publicType.qualifier.storage != EvqBuffer) { error(loc, "memory qualifiers cannot be used on this type", "", ""); } else if (qualifier.isMemory() && (publicType.basicType != EbtSampler) && !publicType.qualifier.isUniformOrBuffer()) { @@ -3212,13 +3324,13 @@ void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQuali if (qualifier.storage == EvqBuffer && publicType.basicType != EbtBlock && - !qualifier.layoutBufferReference) + !qualifier.hasBufferReference()) error(loc, "buffers can be declared only as blocks", "buffer", ""); if (qualifier.storage != EvqVaryingIn && qualifier.storage != EvqVaryingOut) return; - if (publicType.shaderQualifiers.blendEquation) + if (publicType.shaderQualifiers.hasBlendEquation()) error(loc, "can only be applied to a standalone 'out'", "blend equation", ""); // now, knowing it is a shader in/out, do all the in/out semantic checks @@ -3231,25 +3343,15 @@ void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQuali if (isTypeInt(publicType.basicType) || publicType.basicType == EbtDouble) profileRequires(loc, EEsProfile, 300, nullptr, "shader input/output"); - if (!qualifier.flat -#ifdef AMD_EXTENSIONS - && !qualifier.explicitInterp -#endif -#ifdef NV_EXTENSIONS - && !qualifier.pervertexNV -#endif - ) { + if (!qualifier.flat && !qualifier.isExplicitInterpolation() && !qualifier.isPervertexNV()) { if (isTypeInt(publicType.basicType) || publicType.basicType == EbtDouble || - (publicType.userDef && (publicType.userDef->containsBasicType(EbtInt8) || - publicType.userDef->containsBasicType(EbtUint8) || - publicType.userDef->containsBasicType(EbtInt16) || - publicType.userDef->containsBasicType(EbtUint16) || - publicType.userDef->containsBasicType(EbtInt) || - publicType.userDef->containsBasicType(EbtUint) || - publicType.userDef->containsBasicType(EbtInt64) || - publicType.userDef->containsBasicType(EbtUint64) || - publicType.userDef->containsBasicType(EbtDouble)))) { + (publicType.userDef && ( publicType.userDef->containsBasicType(EbtInt) + || publicType.userDef->containsBasicType(EbtUint) + || publicType.userDef->contains16BitInt() + || publicType.userDef->contains8BitInt() + || publicType.userDef->contains64BitInt() + || publicType.userDef->containsDouble()))) { if (qualifier.storage == EvqVaryingIn && language == EShLangFragment) error(loc, "must be qualified as flat", TType::getBasicString(publicType.basicType), GetStorageQualifierString(qualifier.storage)); else if (qualifier.storage == EvqVaryingOut && language == EShLangVertex && version == 300) @@ -3257,13 +3359,11 @@ void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQuali } } - if (qualifier.patch && qualifier.isInterpolation()) + if (qualifier.isPatch() && qualifier.isInterpolation()) error(loc, "cannot use interpolation qualifiers with patch", "patch", ""); -#ifdef NV_EXTENSIONS - if (qualifier.perTaskNV && publicType.basicType != EbtBlock) + if (qualifier.isTaskMemory() && publicType.basicType != EbtBlock) error(loc, "taskNV variables can be declared only as blocks", "taskNV", ""); -#endif if (qualifier.storage == EvqVaryingIn) { switch (language) { @@ -3281,18 +3381,6 @@ void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQuali if (qualifier.isAuxiliary() || qualifier.isInterpolation() || qualifier.isMemory() || qualifier.invariant) error(loc, "vertex input cannot be further qualified", "", ""); break; - - case EShLangTessControl: - if (qualifier.patch) - error(loc, "can only use on output in tessellation-control shader", "patch", ""); - break; - - case EShLangTessEvaluation: - break; - - case EShLangGeometry: - break; - case EShLangFragment: if (publicType.userDef) { profileRequires(loc, EEsProfile, 300, nullptr, "fragment-shader struct input"); @@ -3303,12 +3391,16 @@ void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQuali requireProfile(loc, ~EEsProfile, "fragment-shader struct input containing an array"); } break; - - case EShLangCompute: + case EShLangCompute: if (! symbolTable.atBuiltInLevel()) error(loc, "global storage input qualifier cannot be used in a compute shader", "in", ""); break; - +#ifndef GLSLANG_WEB + case EShLangTessControl: + if (qualifier.patch) + error(loc, "can only use on output in tessellation-control shader", "patch", ""); + break; +#endif default: break; } @@ -3326,18 +3418,6 @@ void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQuali } break; - - case EShLangTessControl: - break; - - case EShLangTessEvaluation: - if (qualifier.patch) - error(loc, "can only use on input in tessellation-evaluation shader", "patch", ""); - break; - - case EShLangGeometry: - break; - case EShLangFragment: profileRequires(loc, EEsProfile, 300, nullptr, "fragment shader output"); if (publicType.basicType == EbtStruct) { @@ -3359,7 +3439,12 @@ void TParseContext::globalQualifierTypeCheck(const TSourceLoc& loc, const TQuali case EShLangCompute: error(loc, "global storage output qualifier cannot be used in a compute shader", "out", ""); break; - +#ifndef GLSLANG_WEB + case EShLangTessEvaluation: + if (qualifier.patch) + error(loc, "can only use on input in tessellation-evaluation shader", "patch", ""); + break; +#endif default: break; } @@ -3383,18 +3468,14 @@ void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, cons // Multiple interpolation qualifiers (mostly done later by 'individual qualifiers') if (src.isInterpolation() && dst.isInterpolation()) -#ifdef AMD_EXTENSIONS error(loc, "can only have one interpolation qualifier (flat, smooth, noperspective, __explicitInterpAMD)", "", ""); -#else - error(loc, "can only have one interpolation qualifier (flat, smooth, noperspective)", "", ""); -#endif // Ordering - if (! force && ((profile != EEsProfile && version < 420) || - (profile == EEsProfile && version < 310)) + if (! force && ((!isEsProfile() && version < 420) || + (isEsProfile() && version < 310)) && ! extensionTurnedOn(E_GL_ARB_shading_language_420pack)) { // non-function parameters - if (src.noContraction && (dst.invariant || dst.isInterpolation() || dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone)) + if (src.isNoContraction() && (dst.invariant || dst.isInterpolation() || dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone)) error(loc, "precise qualifier must appear first", "", ""); if (src.invariant && (dst.isInterpolation() || dst.isAuxiliary() || dst.storage != EvqTemporary || dst.precision != EpqNone)) error(loc, "invariant qualifier must appear before interpolation, storage, and precision qualifiers ", "", ""); @@ -3406,7 +3487,7 @@ void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, cons error(loc, "precision qualifier must appear as last qualifier", "", ""); // function parameters - if (src.noContraction && (dst.storage == EvqConst || dst.storage == EvqIn || dst.storage == EvqOut)) + if (src.isNoContraction() && (dst.storage == EvqConst || dst.storage == EvqIn || dst.storage == EvqOut)) error(loc, "precise qualifier must appear first", "", ""); if (src.storage == EvqConst && (dst.storage == EvqIn || dst.storage == EvqOut)) error(loc, "in/out must appear before const", "", ""); @@ -3431,6 +3512,7 @@ void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, cons if (dst.precision == EpqNone || (force && src.precision != EpqNone)) dst.precision = src.precision; +#ifndef GLSLANG_WEB if (!force && ((src.coherent && (dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent)) || (src.devicecoherent && (dst.coherent || dst.queuefamilycoherent || dst.workgroupcoherent || dst.subgroupcoherent)) || (src.queuefamilycoherent && (dst.coherent || dst.devicecoherent || dst.workgroupcoherent || dst.subgroupcoherent)) || @@ -3438,6 +3520,7 @@ void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, cons (src.subgroupcoherent && (dst.coherent || dst.devicecoherent || dst.queuefamilycoherent || dst.workgroupcoherent)))) { error(loc, "only one coherent/devicecoherent/queuefamilycoherent/workgroupcoherent/subgroupcoherent qualifier allowed", GetPrecisionQualifierString(src.precision), ""); } +#endif // Layout qualifiers mergeObjectLayoutQualifiers(dst, src, false); @@ -3445,19 +3528,17 @@ void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, cons bool repeated = false; #define MERGE_SINGLETON(field) repeated |= dst.field && src.field; dst.field |= src.field; MERGE_SINGLETON(invariant); - MERGE_SINGLETON(noContraction); MERGE_SINGLETON(centroid); MERGE_SINGLETON(smooth); MERGE_SINGLETON(flat); + MERGE_SINGLETON(specConstant); +#ifndef GLSLANG_WEB + MERGE_SINGLETON(noContraction); MERGE_SINGLETON(nopersp); -#ifdef AMD_EXTENSIONS MERGE_SINGLETON(explicitInterp); -#endif -#ifdef NV_EXTENSIONS MERGE_SINGLETON(perPrimitiveNV); MERGE_SINGLETON(perViewNV); MERGE_SINGLETON(perTaskNV); -#endif MERGE_SINGLETON(patch); MERGE_SINGLETON(sample); MERGE_SINGLETON(coherent); @@ -3470,8 +3551,8 @@ void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, cons MERGE_SINGLETON(restrict); MERGE_SINGLETON(readonly); MERGE_SINGLETON(writeonly); - MERGE_SINGLETON(specConstant); MERGE_SINGLETON(nonUniform); +#endif if (repeated) error(loc, "replicated qualifiers", "", ""); @@ -3514,11 +3595,11 @@ void TParseContext::setDefaultPrecision(const TSourceLoc& loc, TPublicType& publ // correlates with the declaration of defaultSamplerPrecision[] int TParseContext::computeSamplerTypeIndex(TSampler& sampler) { - int arrayIndex = sampler.arrayed ? 1 : 0; - int shadowIndex = sampler.shadow ? 1 : 0; - int externalIndex = sampler.external? 1 : 0; - int imageIndex = sampler.image ? 1 : 0; - int msIndex = sampler.ms ? 1 : 0; + int arrayIndex = sampler.arrayed ? 1 : 0; + int shadowIndex = sampler.shadow ? 1 : 0; + int externalIndex = sampler.isExternal() ? 1 : 0; + int imageIndex = sampler.isImageClass() ? 1 : 0; + int msIndex = sampler.isMultiSample() ? 1 : 0; int flattened = EsdNumDims * (EbtNumTypes * (2 * (2 * (2 * (2 * arrayIndex + msIndex) + imageIndex) + shadowIndex) + externalIndex) + sampler.type) + sampler.dim; @@ -3542,8 +3623,10 @@ void TParseContext::precisionQualifierCheck(const TSourceLoc& loc, TBasicType ba if (! obeyPrecisionQualifiers() || parsingBuiltins) return; +#ifndef GLSLANG_WEB if (baseType == EbtAtomicUint && qualifier.precision != EpqNone && qualifier.precision != EpqHigh) error(loc, "atomic counters can only be highp", "atomic_uint", ""); +#endif if (baseType == EbtFloat || baseType == EbtUint || baseType == EbtInt || baseType == EbtSampler || baseType == EbtAtomicUint) { if (qualifier.precision == EpqNone) { @@ -3562,8 +3645,7 @@ void TParseContext::parameterTypeCheck(const TSourceLoc& loc, TStorageQualifier { if ((qualifier == EvqOut || qualifier == EvqInOut) && type.isOpaque()) error(loc, "samplers and atomic_uints cannot be output parameters", type.getBasicTypeString().c_str(), ""); - - if (!parsingBuiltins && type.containsBasicType(EbtFloat16)) + if (!parsingBuiltins && type.contains16BitFloat()) requireFloat16Arithmetic(loc, type.getBasicTypeString().c_str(), "float16 types can only be in uniform block or buffer storage"); if (!parsingBuiltins && type.contains16BitInt()) requireInt16Arithmetic(loc, type.getBasicTypeString().c_str(), "(u)int16 types can only be in uniform block or buffer storage"); @@ -3726,51 +3808,53 @@ void TParseContext::arraySizesCheck(const TSourceLoc& loc, const TQualifier& qua (qualifier.storage != EvqTemporary && qualifier.storage != EvqGlobal && qualifier.storage != EvqShared && qualifier.storage != EvqConst)) error(loc, "only outermost dimension of an array of arrays can be a specialization constant", "[]", ""); +#ifndef GLSLANG_WEB + // desktop always allows outer-dimension-unsized variable arrays, - if (profile != EEsProfile) + if (!isEsProfile()) return; // for ES, if size isn't coming from an initializer, it has to be explicitly declared now, // with very few exceptions - // last member of ssbo block exception: - if (qualifier.storage == EvqBuffer && lastMember) - return; - // implicitly-sized io exceptions: switch (language) { case EShLangGeometry: if (qualifier.storage == EvqVaryingIn) - if ((profile == EEsProfile && version >= 320) || + if ((isEsProfile() && version >= 320) || extensionsTurnedOn(Num_AEP_geometry_shader, AEP_geometry_shader)) return; break; case EShLangTessControl: if ( qualifier.storage == EvqVaryingIn || - (qualifier.storage == EvqVaryingOut && ! qualifier.patch)) - if ((profile == EEsProfile && version >= 320) || + (qualifier.storage == EvqVaryingOut && ! qualifier.isPatch())) + if ((isEsProfile() && version >= 320) || extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader)) return; break; case EShLangTessEvaluation: - if ((qualifier.storage == EvqVaryingIn && ! qualifier.patch) || + if ((qualifier.storage == EvqVaryingIn && ! qualifier.isPatch()) || qualifier.storage == EvqVaryingOut) - if ((profile == EEsProfile && version >= 320) || + if ((isEsProfile() && version >= 320) || extensionsTurnedOn(Num_AEP_tessellation_shader, AEP_tessellation_shader)) return; break; -#ifdef NV_EXTENSIONS case EShLangMeshNV: if (qualifier.storage == EvqVaryingOut) - if ((profile == EEsProfile && version >= 320) || + if ((isEsProfile() && version >= 320) || extensionTurnedOn(E_GL_NV_mesh_shader)) return; break; -#endif default: break; } +#endif + + // last member of ssbo block exception: + if (qualifier.storage == EvqBuffer && lastMember) + return; + arraySizeRequiredCheck(loc, *arraySizes); } @@ -3811,6 +3895,7 @@ void TParseContext::declareArray(const TSourceLoc& loc, const TString& identifie if (symbolTable.atGlobalLevel()) trackLinkage(*symbol); +#ifndef GLSLANG_WEB if (! symbolTable.atBuiltInLevel()) { if (isIoResizeArray(type)) { ioArraySymbolResizeList.push_back(symbol); @@ -3818,6 +3903,7 @@ void TParseContext::declareArray(const TSourceLoc& loc, const TString& identifie } else fixIoArraySize(loc, symbol->getWritableType()); } +#endif return; } @@ -3855,6 +3941,7 @@ void TParseContext::declareArray(const TSourceLoc& loc, const TString& identifie return; } +#ifndef GLSLANG_WEB if (existingType.isSizedArray()) { // be more leniant for input arrays to geometry shaders and tessellation control outputs, where the redeclaration is the same size if (! (isIoResizeArray(type) && existingType.getOuterArraySize() == type.getOuterArraySize())) @@ -3868,8 +3955,11 @@ void TParseContext::declareArray(const TSourceLoc& loc, const TString& identifie if (isIoResizeArray(type)) checkIoArraysConsistency(loc); +#endif } +#ifndef GLSLANG_WEB + // Policy and error check for needing a runtime sized array. void TParseContext::checkRuntimeSizable(const TSourceLoc& loc, const TIntermTyped& base) { @@ -3883,7 +3973,7 @@ void TParseContext::checkRuntimeSizable(const TSourceLoc& loc, const TIntermType const TIntermBinary* binary = base.getAsBinaryNode(); if (binary != nullptr && binary->getOp() == EOpIndexDirectStruct && - binary->getLeft()->getBasicType() == EbtReference) { + binary->getLeft()->isReference()) { const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); const int memberCount = (int)binary->getLeft()->getType().getReferentType()->getStruct()->size(); @@ -3893,8 +3983,8 @@ void TParseContext::checkRuntimeSizable(const TSourceLoc& loc, const TIntermType } // check for additional things allowed by GL_EXT_nonuniform_qualifier - if (base.getBasicType() == EbtSampler || - (base.getBasicType() == EbtBlock && base.getType().getQualifier().isUniformOrBuffer())) + if (base.getBasicType() == EbtSampler || base.getBasicType() == EbtAccStructNV || + (base.getBasicType() == EbtBlock && base.getType().getQualifier().isUniformOrBuffer())) requireExtensions(loc, 1, &E_GL_EXT_nonuniform_qualifier, "variable index"); else error(loc, "", "[", "array must be redeclared with a size before being indexed with a variable"); @@ -3910,7 +4000,7 @@ bool TParseContext::isRuntimeLength(const TIntermTyped& base) const // is it the last member? const int index = binary->getRight()->getAsConstantUnion()->getConstArray()[0].getIConst(); - if (binary->getLeft()->getBasicType() == EbtReference) + if (binary->getLeft()->isReference()) return false; const int memberCount = (int)binary->getLeft()->getType().getStruct()->size(); @@ -3922,27 +4012,34 @@ bool TParseContext::isRuntimeLength(const TIntermTyped& base) const return false; } -#ifdef NV_EXTENSIONS -// Fix mesh view output array dimension -void TParseContext::resizeMeshViewDimension(const TSourceLoc& loc, TType& type) +// Check if mesh perviewNV attributes have a view dimension +// and resize it to gl_MaxMeshViewCountNV when implicitly sized. +void TParseContext::checkAndResizeMeshViewDim(const TSourceLoc& loc, TType& type, bool isBlockMember) { // see if member is a per-view attribute - if (type.getQualifier().isPerView()) { - // since we don't have the maxMeshViewCountNV set during parsing builtins, we hardcode the value + if (!type.getQualifier().isPerView()) + return; + + if ((isBlockMember && type.isArray()) || (!isBlockMember && type.isArrayOfArrays())) { + // since we don't have the maxMeshViewCountNV set during parsing builtins, we hardcode the value. int maxViewCount = parsingBuiltins ? 4 : resources.maxMeshViewCountNV; + // For block members, outermost array dimension is the view dimension. + // For non-block members, outermost array dimension is the vertex/primitive dimension + // and 2nd outermost is the view dimension. + int viewDim = isBlockMember ? 0 : 1; + int viewDimSize = type.getArraySizes()->getDimSize(viewDim); - if (! type.isArray()) { - error(loc, "requires an view array dimension", "perviewNV", ""); - } - else if (!type.isUnsizedArray() && type.getOuterArraySize() != maxViewCount) { + if (viewDimSize != UnsizedArraySize && viewDimSize != maxViewCount) error(loc, "mesh view output array size must be gl_MaxMeshViewCountNV or implicitly sized", "[]", ""); - } - else if (type.isUnsizedArray()) { - type.changeOuterArraySize(maxViewCount); - } + else if (viewDimSize == UnsizedArraySize) + type.getArraySizes()->setDimSize(viewDim, maxViewCount); + } + else { + error(loc, "requires a view array dimension", "perviewNV", ""); } } -#endif + +#endif // GLSLANG_WEB // Returns true if the first argument to the #line directive is the line number for the next line. // @@ -3955,7 +4052,7 @@ void TParseContext::resizeMeshViewDimension(const TSourceLoc& loc, TType& type) // source string number source-string-number. bool TParseContext::lineDirectiveShouldSetNextLine() const { - return profile == EEsProfile || version >= 330; + return isEsProfile() || version >= 330; } // @@ -3986,18 +4083,19 @@ void TParseContext::nonInitConstCheck(const TSourceLoc& loc, TString& identifier TSymbol* TParseContext::redeclareBuiltinVariable(const TSourceLoc& loc, const TString& identifier, const TQualifier& qualifier, const TShaderQualifiers& publicType) { +#ifndef GLSLANG_WEB if (! builtInName(identifier) || symbolTable.atBuiltInLevel() || ! symbolTable.atGlobalLevel()) return nullptr; - bool nonEsRedecls = (profile != EEsProfile && (version >= 130 || identifier == "gl_TexCoord")); - bool esRedecls = (profile == EEsProfile && + bool nonEsRedecls = (!isEsProfile() && (version >= 130 || identifier == "gl_TexCoord")); + bool esRedecls = (isEsProfile() && (version >= 320 || extensionsTurnedOn(Num_AEP_shader_io_blocks, AEP_shader_io_blocks))); if (! esRedecls && ! nonEsRedecls) return nullptr; // Special case when using GL_ARB_separate_shader_objects bool ssoPre150 = false; // means the only reason this variable is redeclared is due to this combination - if (profile != EEsProfile && version <= 140 && extensionTurnedOn(E_GL_ARB_separate_shader_objects)) { + if (!isEsProfile() && version <= 140 && extensionTurnedOn(E_GL_ARB_separate_shader_objects)) { if (identifier == "gl_Position" || identifier == "gl_PointSize" || identifier == "gl_ClipVertex" || @@ -4020,11 +4118,9 @@ TSymbol* TParseContext::redeclareBuiltinVariable(const TSourceLoc& loc, const TS (identifier == "gl_Color" && language == EShLangFragment) || (identifier == "gl_FragStencilRefARB" && (nonEsRedecls && version >= 140) && language == EShLangFragment) || -#ifdef NV_EXTENSIONS identifier == "gl_SampleMask" || identifier == "gl_Layer" || identifier == "gl_PrimitiveIndicesNV" || -#endif identifier == "gl_TexCoord") { // Find the existing symbol, if any. @@ -4104,16 +4200,13 @@ TSymbol* TParseContext::redeclareBuiltinVariable(const TSourceLoc& loc, const TS } } else if ( -#ifdef NV_EXTENSIONS identifier == "gl_PrimitiveIndicesNV" || -#endif identifier == "gl_FragStencilRefARB") { if (qualifier.hasLayout()) error(loc, "cannot apply layout qualifier to", "redeclaration", symbol->getName().c_str()); if (qualifier.storage != EvqVaryingOut) error(loc, "cannot change output storage qualification of", "redeclaration", symbol->getName().c_str()); } -#ifdef NV_EXTENSIONS else if (identifier == "gl_SampleMask") { if (!publicType.layoutOverrideCoverage) { error(loc, "redeclaration only allowed for override_coverage layout", "redeclaration", symbol->getName().c_str()); @@ -4126,12 +4219,12 @@ TSymbol* TParseContext::redeclareBuiltinVariable(const TSourceLoc& loc, const TS symbolQualifier.layoutViewportRelative = qualifier.layoutViewportRelative; symbolQualifier.layoutSecondaryViewportRelativeOffset = qualifier.layoutSecondaryViewportRelativeOffset; } -#endif // TODO: semantics quality: separate smooth from nothing declared, then use IsInterpolation for several tests above return symbol; } +#endif return nullptr; } @@ -4143,16 +4236,13 @@ TSymbol* TParseContext::redeclareBuiltinVariable(const TSourceLoc& loc, const TS void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newTypeList, const TString& blockName, const TString* instanceName, TArraySizes* arraySizes) { +#ifndef GLSLANG_WEB const char* feature = "built-in block redeclaration"; profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, feature); profileRequires(loc, ~EEsProfile, 410, E_GL_ARB_separate_shader_objects, feature); - if (blockName != "gl_PerVertex" && blockName != "gl_PerFragment" -#ifdef NV_EXTENSIONS - && blockName != "gl_MeshPerVertexNV" && blockName != "gl_MeshPerPrimitiveNV" -#endif - ) - { + if (blockName != "gl_PerVertex" && blockName != "gl_PerFragment" && + blockName != "gl_MeshPerVertexNV" && blockName != "gl_MeshPerPrimitiveNV") { error(loc, "cannot redeclare block: ", "block declaration", blockName.c_str()); return; } @@ -4211,7 +4301,6 @@ void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newT TType& type = block->getWritableType(); -#ifdef NV_EXTENSIONS // if gl_PerVertex is redeclared for the purpose of passing through "gl_Position" // for passthrough purpose, the redeclared block should have the same qualifers as // the current one @@ -4221,7 +4310,6 @@ void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newT type.getQualifier().layoutStream = currentBlockQualifier.layoutStream; type.getQualifier().layoutXfbBuffer = currentBlockQualifier.layoutXfbBuffer; } -#endif TTypeList::iterator member = type.getWritableStruct()->begin(); size_t numOriginalMembersFound = 0; @@ -4254,7 +4342,6 @@ void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newT error(memberLoc, "cannot change array size of redeclared block member", member->type->getFieldName().c_str(), ""); else if (! oldType.getQualifier().isPerView() && newType.isArray()) arrayLimitCheck(loc, member->type->getFieldName(), newType.getOuterArraySize()); -#ifdef NV_EXTENSIONS if (oldType.getQualifier().isPerView() && ! newType.getQualifier().isPerView()) error(memberLoc, "missing perviewNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); else if (! oldType.getQualifier().isPerView() && newType.getQualifier().isPerView()) @@ -4274,7 +4361,6 @@ void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newT error(memberLoc, "missing perprimitiveNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); else if (! oldType.getQualifier().isPerPrimitive() && newType.getQualifier().isPerPrimitive()) error(memberLoc, "cannot add perprimitiveNV qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); -#endif if (newType.getQualifier().isMemory()) error(memberLoc, "cannot add memory qualifier to redeclared block member", member->type->getFieldName().c_str(), ""); if (newType.getQualifier().hasNonXfbLayout()) @@ -4365,6 +4451,7 @@ void TParseContext::redeclareBuiltinBlock(const TSourceLoc& loc, TTypeList& newT // Save it in the AST for linker use. trackLinkage(*block); +#endif // GLSLANG_WEB } void TParseContext::paramCheckFixStorage(const TSourceLoc& loc, const TStorageQualifier& qualifier, TType& type) @@ -4392,6 +4479,7 @@ void TParseContext::paramCheckFixStorage(const TSourceLoc& loc, const TStorageQu void TParseContext::paramCheckFix(const TSourceLoc& loc, const TQualifier& qualifier, TType& type) { +#ifndef GLSLANG_WEB if (qualifier.isMemory()) { type.getQualifier().volatil = qualifier.volatil; type.getQualifier().coherent = qualifier.coherent; @@ -4404,6 +4492,7 @@ void TParseContext::paramCheckFix(const TSourceLoc& loc, const TQualifier& quali type.getQualifier().writeonly = qualifier.writeonly; type.getQualifier().restrict = qualifier.restrict; } +#endif if (qualifier.isAuxiliary() || qualifier.isInterpolation()) @@ -4412,9 +4501,9 @@ void TParseContext::paramCheckFix(const TSourceLoc& loc, const TQualifier& quali error(loc, "cannot use layout qualifiers on a function parameter", "", ""); if (qualifier.invariant) error(loc, "cannot use invariant qualifier on a function parameter", "", ""); - if (qualifier.noContraction) { + if (qualifier.isNoContraction()) { if (qualifier.isParamOutput()) - type.getQualifier().noContraction = true; + type.getQualifier().setNoContraction(); else warn(loc, "qualifier has no effect on non-output parameters", "precise", ""); } @@ -4455,12 +4544,15 @@ void TParseContext::opaqueCheck(const TSourceLoc& loc, const TType& type, const void TParseContext::referenceCheck(const TSourceLoc& loc, const TType& type, const char* op) { +#ifndef GLSLANG_WEB if (containsFieldWithBasicType(type, EbtReference)) error(loc, "can't use with reference types", op, ""); +#endif } void TParseContext::storage16BitAssignmentCheck(const TSourceLoc& loc, const TType& type, const char* op) { +#ifndef GLSLANG_WEB if (type.getBasicType() == EbtStruct && containsFieldWithBasicType(type, EbtFloat16)) requireFloat16Arithmetic(loc, op, "can't use with structs containing float16"); @@ -4490,6 +4582,7 @@ void TParseContext::storage16BitAssignmentCheck(const TSourceLoc& loc, const TTy if (type.isArray() && type.getBasicType() == EbtUint8) requireInt8Arithmetic(loc, op, "can't use with arrays containing uint8"); +#endif } void TParseContext::specializationCheck(const TSourceLoc& loc, const TType& type, const char* op) @@ -4541,6 +4634,7 @@ void TParseContext::structTypeCheck(const TSourceLoc& /*loc*/, TPublicType& publ // void TParseContext::inductiveLoopCheck(const TSourceLoc& loc, TIntermNode* init, TIntermLoop* loop) { +#ifndef GLSLANG_WEB // loop index init must exist and be a declaration, which shows up in the AST as an aggregate of size 1 of the declaration bool badInit = false; if (! init || ! init->getAsAggregate() || init->getAsAggregate()->getSequence().size() != 1) @@ -4636,8 +4730,10 @@ void TParseContext::inductiveLoopCheck(const TSourceLoc& loc, TIntermNode* init, // the body inductiveLoopBodyCheck(loop->getBody(), loopIndex, symbolTable); +#endif } +#ifndef GLSLANG_WEB // Do limit checks for built-in arrays. void TParseContext::arrayLimitCheck(const TSourceLoc& loc, const TString& identifier, int size) { @@ -4647,13 +4743,12 @@ void TParseContext::arrayLimitCheck(const TSourceLoc& loc, const TString& identi limitCheck(loc, size, "gl_MaxClipDistances", "gl_ClipDistance array size"); else if (identifier.compare("gl_CullDistance") == 0) limitCheck(loc, size, "gl_MaxCullDistances", "gl_CullDistance array size"); -#ifdef NV_EXTENSIONS else if (identifier.compare("gl_ClipDistancePerViewNV") == 0) limitCheck(loc, size, "gl_MaxClipDistances", "gl_ClipDistancePerViewNV array size"); else if (identifier.compare("gl_CullDistancePerViewNV") == 0) limitCheck(loc, size, "gl_MaxCullDistances", "gl_CullDistancePerViewNV array size"); -#endif } +#endif // GLSLANG_WEB // See if the provided value is less than or equal to the symbol indicated by limit, // which should be a constant in the symbol table. @@ -4667,6 +4762,8 @@ void TParseContext::limitCheck(const TSourceLoc& loc, int value, const char* lim error(loc, "must be less than or equal to", feature, "%s (%d)", limit, constArray[0].getIConst()); } +#ifndef GLSLANG_WEB + // // Do any additional error checking, etc., once we know the parsing is done. // @@ -4688,33 +4785,30 @@ void TParseContext::finish() // about the stage itself. switch (language) { case EShLangGeometry: - if (profile == EEsProfile && version == 310) + if (isEsProfile() && version == 310) requireExtensions(getCurrentLoc(), Num_AEP_geometry_shader, AEP_geometry_shader, "geometry shaders"); break; case EShLangTessControl: case EShLangTessEvaluation: - if (profile == EEsProfile && version == 310) + if (isEsProfile() && version == 310) requireExtensions(getCurrentLoc(), Num_AEP_tessellation_shader, AEP_tessellation_shader, "tessellation shaders"); - else if (profile != EEsProfile && version < 400) + else if (!isEsProfile() && version < 400) requireExtensions(getCurrentLoc(), 1, &E_GL_ARB_tessellation_shader, "tessellation shaders"); break; case EShLangCompute: - if (profile != EEsProfile && version < 430) + if (!isEsProfile() && version < 430) requireExtensions(getCurrentLoc(), 1, &E_GL_ARB_compute_shader, "compute shaders"); break; -#ifdef NV_EXTENSIONS case EShLangTaskNV: requireExtensions(getCurrentLoc(), 1, &E_GL_NV_mesh_shader, "task shaders"); break; case EShLangMeshNV: requireExtensions(getCurrentLoc(), 1, &E_GL_NV_mesh_shader, "mesh shaders"); break; -#endif default: break; } -#ifdef NV_EXTENSIONS // Set default outputs for GL_NV_geometry_shader_passthrough if (language == EShLangGeometry && extensionTurnedOn(E_SPV_NV_geometry_shader_passthrough)) { if (intermediate.getOutputPrimitive() == ElgNone) { @@ -4734,8 +4828,8 @@ void TParseContext::finish() } } } -#endif } +#endif // GLSLANG_WEB // // Layout qualifier stuff. @@ -4771,6 +4865,7 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi publicType.qualifier.layoutPacking = ElpStd140; return; } +#ifndef GLSLANG_WEB if (id == TQualifier::getLayoutPackingString(ElpStd430)) { requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, "std430"); profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, nullptr, "std430"); @@ -4810,20 +4905,12 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi intermediate.setUsePhysicalStorageBuffer(); return; } - if (language == EShLangGeometry || language == EShLangTessEvaluation -#ifdef NV_EXTENSIONS - || language == EShLangMeshNV -#endif - ) { + if (language == EShLangGeometry || language == EShLangTessEvaluation || language == EShLangMeshNV) { if (id == TQualifier::getGeometryString(ElgTriangles)) { publicType.shaderQualifiers.geometry = ElgTriangles; return; } - if (language == EShLangGeometry -#ifdef NV_EXTENSIONS - || language == EShLangMeshNV -#endif - ) { + if (language == EShLangGeometry || language == EShLangMeshNV) { if (id == TQualifier::getGeometryString(ElgPoints)) { publicType.shaderQualifiers.geometry = ElgPoints; return; @@ -4832,10 +4919,7 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi publicType.shaderQualifiers.geometry = ElgLines; return; } -#ifdef NV_EXTENSIONS - if (language == EShLangGeometry) -#endif - { + if (language == EShLangGeometry) { if (id == TQualifier::getGeometryString(ElgLineStrip)) { publicType.shaderQualifiers.geometry = ElgLineStrip; return; @@ -4852,14 +4936,12 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi publicType.shaderQualifiers.geometry = ElgTriangleStrip; return; } -#ifdef NV_EXTENSIONS if (id == "passthrough") { requireExtensions(loc, 1, &E_SPV_NV_geometry_shader_passthrough, "geometry shader passthrough"); publicType.qualifier.layoutPassthrough = true; intermediate.setGeoPassthroughEXT(); return; } -#endif } } else { assert(language == EShLangTessEvaluation); @@ -4942,6 +5024,17 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi return; } } + for (TInterlockOrdering order = (TInterlockOrdering)(EioNone + 1); order < EioCount; order = (TInterlockOrdering)(order+1)) { + if (id == TQualifier::getInterlockOrderingString(order)) { + requireProfile(loc, ECoreProfile | ECompatibilityProfile, "fragment shader interlock layout qualifier"); + profileRequires(loc, ECoreProfile | ECompatibilityProfile, 450, nullptr, "fragment shader interlock layout qualifier"); + requireExtensions(loc, 1, &E_GL_ARB_fragment_shader_interlock, TQualifier::getInterlockOrderingString(order)); + if (order == EioShadingRateInterlockOrdered || order == EioShadingRateInterlockUnordered) + requireExtensions(loc, 1, &E_GL_NV_shading_rate_image, TQualifier::getInterlockOrderingString(order)); + publicType.shaderQualifiers.interlockOrdering = order; + return; + } + } if (id.compare(0, 13, "blend_support") == 0) { bool found = false; for (TBlendEquationShift be = (TBlendEquationShift)0; be < EBlendCount; be = (TBlendEquationShift)(be + 1)) { @@ -4958,7 +5051,6 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi error(loc, "unknown blend equation", "blend_support", ""); return; } -#ifdef NV_EXTENSIONS if (id == "override_coverage") { requireExtensions(loc, 1, &E_GL_NV_sample_mask_override_coverage, "sample mask override coverage"); publicType.shaderQualifiers.layoutOverrideCoverage = true; @@ -4996,9 +5088,8 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi } } } -#else - } #endif + error(loc, "unrecognized layout identifier, or qualifier requires assignment (e.g., binding = 4)", id.c_str(), ""); } @@ -5063,7 +5154,8 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi return; } else if (id == "location") { profileRequires(loc, EEsProfile, 300, nullptr, "location"); - const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + const char* exts[2] = { E_GL_ARB_separate_shader_objects, E_GL_ARB_explicit_attrib_location }; + // GL_ARB_explicit_uniform_location requires 330 or GL_ARB_explicit_attrib_location we do not need to add it here profileRequires(loc, ~EEsProfile, 330, 2, exts, "location"); if ((unsigned int)value >= TQualifier::layoutLocationEnd) error(loc, "location is too large", id.c_str(), ""); @@ -5083,8 +5175,10 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi error(loc, "needs a literal integer", "set", ""); return; } else if (id == "binding") { +#ifndef GLSLANG_WEB profileRequires(loc, ~EEsProfile, 420, E_GL_ARB_shading_language_420pack, "binding"); profileRequires(loc, EEsProfile, 310, nullptr, "binding"); +#endif if ((unsigned int)value >= TQualifier::layoutBindingEnd) error(loc, "binding is too large", id.c_str(), ""); else @@ -5092,7 +5186,23 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi if (nonLiteral) error(loc, "needs a literal integer", "binding", ""); return; - } else if (id == "component") { + } + if (id == "constant_id") { + requireSpv(loc, "constant_id"); + if (value >= (int)TQualifier::layoutSpecConstantIdEnd) { + error(loc, "specialization-constant id is too large", id.c_str(), ""); + } else { + publicType.qualifier.layoutSpecConstantId = value; + publicType.qualifier.specConstant = true; + if (! intermediate.addUsedConstantId(value)) + error(loc, "specialization-constant id already used", id.c_str(), ""); + } + if (nonLiteral) + error(loc, "needs a literal integer", "constant_id", ""); + return; + } +#ifndef GLSLANG_WEB + if (id == "component") { requireProfile(loc, ECoreProfile | ECompatibilityProfile, "component"); profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, "component"); if ((unsigned)value >= TQualifier::layoutComponentEnd) @@ -5102,7 +5212,8 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi if (nonLiteral) error(loc, "needs a literal integer", "component", ""); return; - } else if (id.compare(0, 4, "xfb_") == 0) { + } + if (id.compare(0, 4, "xfb_") == 0) { // "Any shader making any static use (after preprocessing) of any of these // *xfb_* qualifiers will cause the shader to be in a transform feedback // capturing mode and hence responsible for describing the transform feedback @@ -5148,7 +5259,6 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi return; } } - if (id == "input_attachment_index") { requireVulkan(loc, "input_attachment_index"); if (value >= (int)TQualifier::layoutAttachmentEnd) @@ -5159,20 +5269,6 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi error(loc, "needs a literal integer", "input_attachment_index", ""); return; } - if (id == "constant_id") { - requireSpv(loc, "constant_id"); - if (value >= (int)TQualifier::layoutSpecConstantIdEnd) { - error(loc, "specialization-constant id is too large", id.c_str(), ""); - } else { - publicType.qualifier.layoutSpecConstantId = value; - publicType.qualifier.specConstant = true; - if (! intermediate.addUsedConstantId(value)) - error(loc, "specialization-constant id already used", id.c_str(), ""); - } - if (nonLiteral) - error(loc, "needs a literal integer", "constant_id", ""); - return; - } if (id == "num_views") { requireExtensions(loc, Num_OVR_multiview_EXTs, OVR_multiview_EXTs, "num_views"); publicType.shaderQualifiers.numViews = value; @@ -5180,8 +5276,6 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi error(loc, "needs a literal integer", "num_views", ""); return; } - -#if NV_EXTENSIONS if (language == EShLangVertex || language == EShLangTessControl || language == EShLangTessEvaluation || @@ -5194,7 +5288,6 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi return; } } -#endif if (id == "buffer_reference_align") { requireExtensions(loc, 1, &E_GL_EXT_buffer_reference, "buffer_reference_align"); @@ -5206,11 +5299,10 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi error(loc, "needs a literal integer", "buffer_reference_align", ""); return; } +#endif switch (language) { - case EShLangVertex: - break; - +#ifndef GLSLANG_WEB case EShLangTessControl: if (id == "vertices") { if (value == 0) @@ -5223,9 +5315,6 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi } break; - case EShLangTessEvaluation: - break; - case EShLangGeometry: if (id == "invocations") { profileRequires(loc, ECompatibilityProfile | ECoreProfile, 400, nullptr, "invocations"); @@ -5275,7 +5364,6 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi } break; -#ifdef NV_EXTENSIONS case EShLangMeshNV: if (id == "max_vertices") { requireExtensions(loc, 1, &E_GL_NV_mesh_shader, "max_vertices"); @@ -5302,16 +5390,14 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi #endif case EShLangCompute: if (id.compare(0, 11, "local_size_") == 0) { -#ifdef NV_EXTENSIONS +#ifndef GLSLANG_WEB if (language == EShLangMeshNV || language == EShLangTaskNV) { requireExtensions(loc, 1, &E_GL_NV_mesh_shader, "gl_WorkGroupSize"); - } - else -#endif - { + } else { profileRequires(loc, EEsProfile, 310, 0, "gl_WorkGroupSize"); profileRequires(loc, ~EEsProfile, 430, E_GL_ARB_compute_shader, "gl_WorkGroupSize"); } +#endif if (nonLiteral) error(loc, "needs a literal integer", "local_size", ""); if (id.size() == 12 && value == 0) { @@ -5320,14 +5406,17 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi } if (id == "local_size_x") { publicType.shaderQualifiers.localSize[0] = value; + publicType.shaderQualifiers.localSizeNotDefault[0] = true; return; } if (id == "local_size_y") { publicType.shaderQualifiers.localSize[1] = value; + publicType.shaderQualifiers.localSizeNotDefault[1] = true; return; } if (id == "local_size_z") { publicType.shaderQualifiers.localSize[2] = value; + publicType.shaderQualifiers.localSizeNotDefault[2] = true; return; } if (spvVersion.spv != 0) { @@ -5375,53 +5464,50 @@ void TParseContext::mergeObjectLayoutQualifiers(TQualifier& dst, const TQualifie if (src.hasPacking()) dst.layoutPacking = src.layoutPacking; +#ifndef GLSLANG_WEB if (src.hasStream()) dst.layoutStream = src.layoutStream; - if (src.hasFormat()) dst.layoutFormat = src.layoutFormat; - if (src.hasXfbBuffer()) dst.layoutXfbBuffer = src.layoutXfbBuffer; + if (src.hasBufferReferenceAlign()) + dst.layoutBufferReferenceAlign = src.layoutBufferReferenceAlign; +#endif if (src.hasAlign()) dst.layoutAlign = src.layoutAlign; - if (src.hasBufferReferenceAlign()) - dst.layoutBufferReferenceAlign = src.layoutBufferReferenceAlign; - if (! inheritOnly) { if (src.hasLocation()) dst.layoutLocation = src.layoutLocation; - if (src.hasComponent()) - dst.layoutComponent = src.layoutComponent; - if (src.hasIndex()) - dst.layoutIndex = src.layoutIndex; - if (src.hasOffset()) dst.layoutOffset = src.layoutOffset; - if (src.hasSet()) dst.layoutSet = src.layoutSet; if (src.layoutBinding != TQualifier::layoutBindingEnd) dst.layoutBinding = src.layoutBinding; + if (src.hasSpecConstantId()) + dst.layoutSpecConstantId = src.layoutSpecConstantId; + +#ifndef GLSLANG_WEB + if (src.hasComponent()) + dst.layoutComponent = src.layoutComponent; + if (src.hasIndex()) + dst.layoutIndex = src.layoutIndex; if (src.hasXfbStride()) dst.layoutXfbStride = src.layoutXfbStride; if (src.hasXfbOffset()) dst.layoutXfbOffset = src.layoutXfbOffset; if (src.hasAttachment()) dst.layoutAttachment = src.layoutAttachment; - if (src.hasSpecConstantId()) - dst.layoutSpecConstantId = src.layoutSpecConstantId; - if (src.layoutPushConstant) dst.layoutPushConstant = true; if (src.layoutBufferReference) dst.layoutBufferReference = true; -#ifdef NV_EXTENSIONS if (src.layoutPassthrough) dst.layoutPassthrough = true; if (src.layoutViewportRelative) @@ -5490,17 +5576,15 @@ void TParseContext::layoutObjectCheck(const TSourceLoc& loc, const TSymbol& symb if (qualifier.hasPacking()) error(loc, "cannot specify packing on a variable declaration", "layout", ""); // "The offset qualifier can only be used on block members of blocks..." - if (qualifier.hasOffset() && type.getBasicType() != EbtAtomicUint) + if (qualifier.hasOffset() && !type.isAtomic()) error(loc, "cannot specify on a variable declaration", "offset", ""); // "The align qualifier can only be used on blocks or block members..." if (qualifier.hasAlign()) error(loc, "cannot specify on a variable declaration", "align", ""); - if (qualifier.layoutPushConstant) + if (qualifier.isPushConstant()) error(loc, "can only specify on a uniform block", "push_constant", ""); -#ifdef NV_EXTENSIONS - if (qualifier.layoutShaderRecordNV) + if (qualifier.isShaderRecordNV()) error(loc, "can only specify on a buffer block", "shaderRecordNV", ""); -#endif } break; default: @@ -5564,17 +5648,15 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type) case EvqVaryingOut: if (type.getBasicType() == EbtBlock) profileRequires(loc, ECoreProfile | ECompatibilityProfile, 440, E_GL_ARB_enhanced_layouts, "location qualifier on in/out block"); -#ifdef NV_EXTENSIONS if (type.getQualifier().isTaskMemory()) error(loc, "cannot apply to taskNV in/out blocks", "location", ""); -#endif break; case EvqUniform: case EvqBuffer: if (type.getBasicType() == EbtBlock) error(loc, "cannot apply to uniform or buffer block", "location", ""); break; -#ifdef NV_EXTENSIONS +#ifndef GLSLANG_WEB case EvqPayloadNV: case EvqPayloadInNV: case EvqHitAttrNV: @@ -5597,6 +5679,7 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type) error(loc, "fragment outputs sharing the same location must be the same basic type", "location", "%d", repeated); } +#ifndef GLSLANG_WEB if (qualifier.hasXfbOffset() && qualifier.hasXfbBuffer()) { int repeated = intermediate.addXfbBufferOffset(type); if (repeated >= 0) @@ -5608,25 +5691,20 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type) if ((type.containsBasicType(EbtDouble) || type.containsBasicType(EbtInt64) || type.containsBasicType(EbtUint64)) && ! IsMultipleOfPow2(qualifier.layoutXfbOffset, 8)) error(loc, "type contains double or 64-bit integer; xfb_offset must be a multiple of 8", "xfb_offset", ""); -#ifdef AMD_EXTENSIONS else if ((type.containsBasicType(EbtBool) || type.containsBasicType(EbtFloat) || type.containsBasicType(EbtInt) || type.containsBasicType(EbtUint)) && ! IsMultipleOfPow2(qualifier.layoutXfbOffset, 4)) error(loc, "must be a multiple of size of first component", "xfb_offset", ""); // ..., if applied to an aggregate containing a half float or 16-bit integer, the offset must also be a multiple of 2..." - else if ((type.containsBasicType(EbtFloat16) || type.containsBasicType(EbtInt16) || type.containsBasicType(EbtUint16)) && + else if ((type.contains16BitFloat() || type.containsBasicType(EbtInt16) || type.containsBasicType(EbtUint16)) && !IsMultipleOfPow2(qualifier.layoutXfbOffset, 2)) error(loc, "type contains half float or 16-bit integer; xfb_offset must be a multiple of 2", "xfb_offset", ""); -#else - else if (! IsMultipleOfPow2(qualifier.layoutXfbOffset, 4)) - error(loc, "must be a multiple of size of first component", "xfb_offset", ""); -#endif } - if (qualifier.hasXfbStride() && qualifier.hasXfbBuffer()) { if (! intermediate.setXfbBufferStride(qualifier.layoutXfbBuffer, qualifier.layoutXfbStride)) error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer); } +#endif if (qualifier.hasBinding()) { // Binding checking, from the spec: @@ -5649,15 +5727,19 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type) lastBinding += type.getCumulativeArraySize(); else { lastBinding += 1; +#ifndef GLSLANG_WEB if (spvVersion.vulkan == 0) warn(loc, "assuming binding count of one for compile-time checking of binding numbers for unsized array", "[]", ""); +#endif } } } +#ifndef GLSLANG_WEB if (spvVersion.vulkan == 0 && lastBinding >= resources.maxCombinedTextureImageUnits) error(loc, "sampler binding not less than gl_MaxCombinedTextureImageUnits", "binding", type.isArray() ? "(using array)" : ""); +#endif } - if (type.getBasicType() == EbtAtomicUint) { + if (type.isAtomic()) { if (qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) { error(loc, "atomic_uint binding is too large; see gl_MaxAtomicCounterBindings", "binding", ""); return; @@ -5667,18 +5749,16 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type) // some types require bindings // atomic_uint - if (type.getBasicType() == EbtAtomicUint) + if (type.isAtomic()) error(loc, "layout(binding=X) is required", "atomic_uint", ""); // SPIR-V if (spvVersion.spv > 0) { if (qualifier.isUniformOrBuffer()) { - if (type.getBasicType() == EbtBlock && !qualifier.layoutPushConstant && -#ifdef NV_EXTENSIONS - !qualifier.layoutShaderRecordNV && -#endif - !qualifier.layoutAttachment && - !qualifier.layoutBufferReference) + if (type.getBasicType() == EbtBlock && !qualifier.isPushConstant() && + !qualifier.isShaderRecordNV() && + !qualifier.hasAttachment() && + !qualifier.hasBufferReference()) error(loc, "uniform/buffer blocks require layout(binding=X)", "binding", ""); else if (spvVersion.vulkan > 0 && type.getBasicType() == EbtSampler) error(loc, "sampler/texture/image requires layout(binding=X)", "binding", ""); @@ -5703,40 +5783,38 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type) // Image format if (qualifier.hasFormat()) { if (! type.isImage()) - error(loc, "only apply to images", TQualifier::getLayoutFormatString(qualifier.layoutFormat), ""); + error(loc, "only apply to images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); else { - if (type.getSampler().type == EbtFloat && qualifier.layoutFormat > ElfFloatGuard) - error(loc, "does not apply to floating point images", TQualifier::getLayoutFormatString(qualifier.layoutFormat), ""); - if (type.getSampler().type == EbtInt && (qualifier.layoutFormat < ElfFloatGuard || qualifier.layoutFormat > ElfIntGuard)) - error(loc, "does not apply to signed integer images", TQualifier::getLayoutFormatString(qualifier.layoutFormat), ""); - if (type.getSampler().type == EbtUint && qualifier.layoutFormat < ElfIntGuard) - error(loc, "does not apply to unsigned integer images", TQualifier::getLayoutFormatString(qualifier.layoutFormat), ""); - - if (profile == EEsProfile) { + if (type.getSampler().type == EbtFloat && qualifier.getFormat() > ElfFloatGuard) + error(loc, "does not apply to floating point images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + if (type.getSampler().type == EbtInt && (qualifier.getFormat() < ElfFloatGuard || qualifier.getFormat() > ElfIntGuard)) + error(loc, "does not apply to signed integer images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + if (type.getSampler().type == EbtUint && qualifier.getFormat() < ElfIntGuard) + error(loc, "does not apply to unsigned integer images", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); + + if (isEsProfile()) { // "Except for image variables qualified with the format qualifiers r32f, r32i, and r32ui, image variables must // specify either memory qualifier readonly or the memory qualifier writeonly." - if (! (qualifier.layoutFormat == ElfR32f || qualifier.layoutFormat == ElfR32i || qualifier.layoutFormat == ElfR32ui)) { - if (! qualifier.readonly && ! qualifier.writeonly) - error(loc, "format requires readonly or writeonly memory qualifier", TQualifier::getLayoutFormatString(qualifier.layoutFormat), ""); + if (! (qualifier.getFormat() == ElfR32f || qualifier.getFormat() == ElfR32i || qualifier.getFormat() == ElfR32ui)) { + if (! qualifier.isReadOnly() && ! qualifier.isWriteOnly()) + error(loc, "format requires readonly or writeonly memory qualifier", TQualifier::getLayoutFormatString(qualifier.getFormat()), ""); } } } - } else if (type.isImage() && ! qualifier.writeonly) { + } else if (type.isImage() && ! qualifier.isWriteOnly()) { const char *explanation = "image variables not declared 'writeonly' and without a format layout qualifier"; requireProfile(loc, ECoreProfile | ECompatibilityProfile, explanation); profileRequires(loc, ECoreProfile | ECompatibilityProfile, 0, E_GL_EXT_shader_image_load_formatted, explanation); } - if (qualifier.layoutPushConstant && type.getBasicType() != EbtBlock) + if (qualifier.isPushConstant() && type.getBasicType() != EbtBlock) error(loc, "can only be used with a block", "push_constant", ""); - if (qualifier.layoutBufferReference && type.getBasicType() != EbtBlock) + if (qualifier.hasBufferReference() && type.getBasicType() != EbtBlock) error(loc, "can only be used with a block", "buffer_reference", ""); -#ifdef NV_EXTENSIONS - if (qualifier.layoutShaderRecordNV && type.getBasicType() != EbtBlock) + if (qualifier.isShaderRecordNV() && type.getBasicType() != EbtBlock) error(loc, "can only be used with a block", "shaderRecordNV", ""); -#endif // input attachment if (type.isSubpass()) { @@ -5793,10 +5871,11 @@ void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier // output block declarations, and output block member declarations." switch (qualifier.storage) { +#ifndef GLSLANG_WEB case EvqVaryingIn: { const char* feature = "location qualifier on input"; - if (profile == EEsProfile && version < 310) + if (isEsProfile() && version < 310) requireStage(loc, EShLangVertex, feature); else requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature); @@ -5813,7 +5892,7 @@ void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier case EvqVaryingOut: { const char* feature = "location qualifier on output"; - if (profile == EEsProfile && version < 310) + if (isEsProfile() && version < 310) requireStage(loc, EShLangFragment, feature); else requireStage(loc, (EShLanguageMask)~EShLangComputeMask, feature); @@ -5827,12 +5906,14 @@ void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier } break; } +#endif case EvqUniform: case EvqBuffer: { const char* feature = "location qualifier on uniform or buffer"; - requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile, feature); - profileRequires(loc, ECoreProfile | ECompatibilityProfile, 430, nullptr, feature); + requireProfile(loc, EEsProfile | ECoreProfile | ECompatibilityProfile | ENoProfile, feature); + profileRequires(loc, ~EEsProfile, 330, E_GL_ARB_explicit_attrib_location, feature); + profileRequires(loc, ~EEsProfile, 430, E_GL_ARB_explicit_uniform_location, feature); profileRequires(loc, EEsProfile, 310, nullptr, feature); break; } @@ -5867,18 +5948,17 @@ void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier error(loc, "offset/align can only be used on a uniform or buffer", "layout", ""); } } - if (qualifier.layoutPushConstant) { + if (qualifier.isPushConstant()) { if (qualifier.storage != EvqUniform) error(loc, "can only be used with a uniform", "push_constant", ""); if (qualifier.hasSet()) error(loc, "cannot be used with push_constant", "set", ""); } - if (qualifier.layoutBufferReference) { + if (qualifier.hasBufferReference()) { if (qualifier.storage != EvqBuffer) error(loc, "can only be used with buffer", "buffer_reference", ""); } -#ifdef NV_EXTENSIONS - if (qualifier.layoutShaderRecordNV) { + if (qualifier.isShaderRecordNV()) { if (qualifier.storage != EvqBuffer) error(loc, "can only be used with a buffer", "shaderRecordNV", ""); if (qualifier.hasBinding()) @@ -5890,12 +5970,12 @@ void TParseContext::layoutQualifierCheck(const TSourceLoc& loc, const TQualifier if (qualifier.storage == EvqHitAttrNV && qualifier.hasLayout()) { error(loc, "cannot apply layout qualifiers to hitAttributeNV variable", "hitAttributeNV", ""); } -#endif } // For places that can't have shader-level layout qualifiers void TParseContext::checkNoShaderLayouts(const TSourceLoc& loc, const TShaderQualifiers& shaderQualifiers) { +#ifndef GLSLANG_WEB const char* message = "can only apply to a standalone qualifier"; if (shaderQualifiers.geometry != ElgNone) @@ -5908,10 +5988,6 @@ void TParseContext::checkNoShaderLayouts(const TSourceLoc& loc, const TShaderQua error(loc, message, "point_mode", ""); if (shaderQualifiers.invocations != TQualifier::layoutNotSet) error(loc, message, "invocations", ""); - if (shaderQualifiers.earlyFragmentTests) - error(loc, message, "early_fragment_tests", ""); - if (shaderQualifiers.postDepthCoverage) - error(loc, message, "post_depth_coverage", ""); for (int i = 0; i < 3; ++i) { if (shaderQualifiers.localSize[i] > 1) error(loc, message, "local_size", ""); @@ -5919,36 +5995,38 @@ void TParseContext::checkNoShaderLayouts(const TSourceLoc& loc, const TShaderQua error(loc, message, "local_size id", ""); } if (shaderQualifiers.vertices != TQualifier::layoutNotSet) { - if (language == EShLangGeometry -#ifdef NV_EXTENSIONS - || language == EShLangMeshNV -#endif - ) + if (language == EShLangGeometry || language == EShLangMeshNV) error(loc, message, "max_vertices", ""); else if (language == EShLangTessControl) error(loc, message, "vertices", ""); else assert(0); } -#ifdef NV_EXTENSIONS + if (shaderQualifiers.earlyFragmentTests) + error(loc, message, "early_fragment_tests", ""); + if (shaderQualifiers.postDepthCoverage) + error(loc, message, "post_depth_coverage", ""); if (shaderQualifiers.primitives != TQualifier::layoutNotSet) { if (language == EShLangMeshNV) error(loc, message, "max_primitives", ""); else assert(0); } -#endif - if (shaderQualifiers.blendEquation) + if (shaderQualifiers.hasBlendEquation()) error(loc, message, "blend equation", ""); if (shaderQualifiers.numViews != TQualifier::layoutNotSet) error(loc, message, "num_views", ""); + if (shaderQualifiers.interlockOrdering != EioNone) + error(loc, message, TQualifier::getInterlockOrderingString(shaderQualifiers.interlockOrdering), ""); +#endif } // Correct and/or advance an object's offset layout qualifier. void TParseContext::fixOffset(const TSourceLoc& loc, TSymbol& symbol) { const TQualifier& qualifier = symbol.getType().getQualifier(); - if (symbol.getType().getBasicType() == EbtAtomicUint) { +#ifndef GLSLANG_WEB + if (symbol.getType().isAtomic()) { if (qualifier.hasBinding() && (int)qualifier.layoutBinding < resources.maxAtomicCounterBindings) { // Set the offset @@ -5957,6 +6035,10 @@ void TParseContext::fixOffset(const TSourceLoc& loc, TSymbol& symbol) offset = qualifier.layoutOffset; else offset = atomicUintOffsets[qualifier.layoutBinding]; + + if (offset % 4 != 0) + error(loc, "atomic counters offset should align based on 4:", "offset", "%d", offset); + symbol.getWritableType().getQualifier().layoutOffset = offset; // Check for overlap @@ -5977,6 +6059,7 @@ void TParseContext::fixOffset(const TSourceLoc& loc, TSymbol& symbol) atomicUintOffsets[qualifier.layoutBinding] = offset + numOffsets; } } +#endif } // @@ -5986,13 +6069,16 @@ void TParseContext::fixOffset(const TSourceLoc& loc, TSymbol& symbol) // const TFunction* TParseContext::findFunction(const TSourceLoc& loc, const TFunction& call, bool& builtIn) { - const TFunction* function = nullptr; - if (symbolTable.isFunctionNameVariable(call.getName())) { error(loc, "can't use function syntax on variable", call.getName().c_str(), ""); return nullptr; } +#ifdef GLSLANG_WEB + return findFunctionExact(loc, call, builtIn); +#endif + + const TFunction* function = nullptr; bool explicitTypesEnabled = extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types) || extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int8) || extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_int16) || @@ -6002,10 +6088,10 @@ const TFunction* TParseContext::findFunction(const TSourceLoc& loc, const TFunct extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float32) || extensionTurnedOn(E_GL_EXT_shader_explicit_arithmetic_types_float64); - if (profile == EEsProfile || version < 120) + if (isEsProfile() || version < 120) function = findFunctionExact(loc, call, builtIn); else if (version < 400) - function = findFunction120(loc, call, builtIn); + function = extensionTurnedOn(E_GL_ARB_gpu_shader_fp64) ? findFunction400(loc, call, builtIn) : findFunction120(loc, call, builtIn); else if (explicitTypesEnabled) function = findFunctionExplicitTypes(loc, call, builtIn); else @@ -6164,6 +6250,8 @@ const TFunction* TParseContext::findFunction400(const TSourceLoc& loc, const TFu } if (from.isArray() || to.isArray() || ! from.sameElementShape(to)) return false; + if (from.isCoopMat() && to.isCoopMat()) + return from.sameCoopMatBaseType(to); return intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType()); }; @@ -6238,6 +6326,8 @@ const TFunction* TParseContext::findFunctionExplicitTypes(const TSourceLoc& loc, } if (from.isArray() || to.isArray() || ! from.sameElementShape(to)) return false; + if (from.isCoopMat() && to.isCoopMat()) + return from.sameCoopMatBaseType(to); return intermediate.canImplicitlyPromote(from.getBasicType(), to.getBasicType()); }; @@ -6289,21 +6379,26 @@ const TFunction* TParseContext::findFunctionExplicitTypes(const TSourceLoc& loc, return bestMatch; } -// When a declaration includes a type, but not a variable name, it can be +// When a declaration includes a type, but not a variable name, it can be used // to establish defaults. void TParseContext::declareTypeDefaults(const TSourceLoc& loc, const TPublicType& publicType) { - if (publicType.basicType == EbtAtomicUint && publicType.qualifier.hasBinding() && publicType.qualifier.hasOffset()) { +#ifndef GLSLANG_WEB + if (publicType.basicType == EbtAtomicUint && publicType.qualifier.hasBinding()) { if (publicType.qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) { error(loc, "atomic_uint binding is too large", "binding", ""); return; } - atomicUintOffsets[publicType.qualifier.layoutBinding] = publicType.qualifier.layoutOffset; + + if(publicType.qualifier.hasOffset()) { + atomicUintOffsets[publicType.qualifier.layoutBinding] = publicType.qualifier.layoutOffset; + } return; } - if (publicType.qualifier.hasLayout() && !publicType.qualifier.layoutBufferReference) + if (publicType.qualifier.hasLayout() && !publicType.qualifier.hasBufferReference()) warn(loc, "useless application of layout qualifier", "layout", ""); +#endif } // @@ -6334,12 +6429,20 @@ TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& iden if (!publicType.typeParameters || publicType.typeParameters->getNumDims() != 4) { error(loc, "expected four type parameters", identifier.c_str(), ""); } - if (publicType.typeParameters && - publicType.typeParameters->getDimSize(0) != 16 && - publicType.typeParameters->getDimSize(0) != 32 && - publicType.typeParameters->getDimSize(0) != 64) { - error(loc, "expected 16, 32, or 64 bits for first type parameter", identifier.c_str(), ""); + if (publicType.typeParameters) { + if (isTypeFloat(publicType.basicType) && + publicType.typeParameters->getDimSize(0) != 16 && + publicType.typeParameters->getDimSize(0) != 32 && + publicType.typeParameters->getDimSize(0) != 64) { + error(loc, "expected 16, 32, or 64 bits for first type parameter", identifier.c_str(), ""); + } + if (isTypeInt(publicType.basicType) && + publicType.typeParameters->getDimSize(0) != 8 && + publicType.typeParameters->getDimSize(0) != 32) { + error(loc, "expected 8 or 32 bits for first type parameter", identifier.c_str(), ""); + } } + } else { if (publicType.typeParameters && publicType.typeParameters->getNumDims() != 0) { error(loc, "unexpected type parameters", identifier.c_str(), ""); @@ -6355,17 +6458,18 @@ TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& iden nonInitConstCheck(loc, identifier, type); samplerCheck(loc, type, identifier, initializer); - atomicUintCheck(loc, type, identifier); transparentOpaqueCheck(loc, type, identifier); -#ifdef NV_EXTENSIONS +#ifndef GLSLANG_WEB + atomicUintCheck(loc, type, identifier); accStructNVCheck(loc, type, identifier); + checkAndResizeMeshViewDim(loc, type, /*isBlockMember*/ false); #endif - if (type.getQualifier().storage == EvqConst && type.containsBasicType(EbtReference)) { + if (type.getQualifier().storage == EvqConst && type.containsReference()) { error(loc, "variables with reference type can't have qualifier 'const'", "qualifier", ""); } if (type.getQualifier().storage != EvqUniform && type.getQualifier().storage != EvqBuffer) { - if (type.containsBasicType(EbtFloat16)) + if (type.contains16BitFloat()) requireFloat16Arithmetic(loc, "qualifier", "float16 types can only be in uniform block or buffer storage"); if (type.contains16BitInt()) requireInt16Arithmetic(loc, "qualifier", "(u)int16 types can only be in uniform block or buffer storage"); @@ -6373,13 +6477,12 @@ TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& iden requireInt8Arithmetic(loc, "qualifier", "(u)int8 types can only be in uniform block or buffer storage"); } - if (type.getQualifier().storage == EvqShared && - type.containsCoopMat()) + if (type.getQualifier().storage == EvqShared && type.containsCoopMat()) error(loc, "qualifier", "Cooperative matrix types must not be used in shared memory", ""); if (identifier != "gl_FragCoord" && (publicType.shaderQualifiers.originUpperLeft || publicType.shaderQualifiers.pixelCenterInteger)) error(loc, "can only apply origin_upper_left and pixel_center_origin to gl_FragCoord", "layout qualifier", ""); - if (identifier != "gl_FragDepth" && publicType.shaderQualifiers.layoutDepth != EldNone) + if (identifier != "gl_FragDepth" && publicType.shaderQualifiers.getDepth() != EldNone) error(loc, "can only apply depth layout to gl_FragDepth", "layout qualifier", ""); // Check for redeclaration of built-ins and/or attempting to declare a reserved name @@ -6435,12 +6538,14 @@ TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& iden // Pick up global defaults from the provide global defaults into dst. void TParseContext::inheritGlobalDefaults(TQualifier& dst) const { +#ifndef GLSLANG_WEB if (dst.storage == EvqVaryingOut) { if (! dst.hasStream() && language == EShLangGeometry) dst.layoutStream = globalOutputDefaults.layoutStream; if (! dst.hasXfbBuffer()) dst.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; } +#endif } // @@ -6469,7 +6574,9 @@ TVariable* TParseContext::declareNonArray(const TSourceLoc& loc, const TString& // make a new variable TVariable* variable = new TVariable(&identifier, type); +#ifndef GLSLANG_WEB ioArrayCheck(loc, type, identifier); +#endif // add variable to symbol table if (symbolTable.insert(*variable)) { @@ -6496,7 +6603,7 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp // TStorageQualifier qualifier = variable->getType().getQualifier().storage; if (! (qualifier == EvqTemporary || qualifier == EvqGlobal || qualifier == EvqConst || - (qualifier == EvqUniform && profile != EEsProfile && version >= 120))) { + (qualifier == EvqUniform && !isEsProfile() && version >= 120))) { error(loc, " cannot initialize this type of qualifier ", variable->getType().getStorageQualifierString(), ""); return nullptr; } @@ -6514,7 +6621,9 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp TType skeletalType; skeletalType.shallowCopy(variable->getType()); skeletalType.getQualifier().makeTemporary(); +#ifndef GLSLANG_WEB initializer = convertInitializerList(loc, skeletalType, initializer); +#endif if (! initializer) { // error recovery; don't leave const without constant values if (qualifier == EvqConst) @@ -6568,7 +6677,7 @@ TIntermNode* TParseContext::executeInitializer(const TSourceLoc& loc, TIntermTyp // qualifier any initializer must be a constant expression." if (symbolTable.atGlobalLevel() && ! initializer->getType().getQualifier().isConstant()) { const char* initFeature = "non-constant global initializer (needs GL_EXT_shader_non_constant_global_initializers)"; - if (profile == EEsProfile) { + if (isEsProfile()) { if (relaxedErrors() && ! extensionTurnedOn(E_GL_EXT_shader_non_constant_global_initializers)) warn(loc, "not allowed in this version", initFeature, ""); else @@ -6819,7 +6928,7 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T // This avoids requesting a matrix of a new type that is going to be discarded anyway. // TODO: This could be generalized to more type combinations, but that would require // more extensive testing and full algorithm rework. For now, the need to do two changes makes - // the recursive call work, and avoids the most aggregious case of creating integer matrices. + // the recursive call work, and avoids the most egregious case of creating integer matrices. if (node->getType().isMatrix() && (type.isScalar() || type.isVector()) && type.isFloatingDomain() != node->getType().isFloatingDomain()) { TType transitionType(node->getBasicType(), glslang::EvqTemporary, type.getVectorSize(), 0, 0, node->isVector()); @@ -6850,6 +6959,35 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T basicOp = EOpConstructFloat; break; + case EOpConstructIVec2: + case EOpConstructIVec3: + case EOpConstructIVec4: + case EOpConstructInt: + basicOp = EOpConstructInt; + break; + + case EOpConstructUVec2: + if (node->getType().getBasicType() == EbtReference) { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference_uvec2, "reference conversion to uvec2"); + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvPtrToUvec2, true, node, + type); + return newNode; + } + case EOpConstructUVec3: + case EOpConstructUVec4: + case EOpConstructUint: + basicOp = EOpConstructUint; + break; + + case EOpConstructBVec2: + case EOpConstructBVec3: + case EOpConstructBVec4: + case EOpConstructBool: + basicOp = EOpConstructBool; + break; + +#ifndef GLSLANG_WEB + case EOpConstructDVec2: case EOpConstructDVec3: case EOpConstructDVec4: @@ -6880,6 +7018,22 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T case EOpConstructF16Mat4x4: case EOpConstructFloat16: basicOp = EOpConstructFloat16; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticFloat16Enabled()) { + TType tempType(EbtFloat, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructFloat16) + aggregateOp = EOpConstructFloat; + else + aggregateOp = (TOperator)(EOpConstructVec2 + op - EOpConstructF16Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtFloat16, newNode); + return newNode; + } break; case EOpConstructI8Vec2: @@ -6887,6 +7041,22 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T case EOpConstructI8Vec4: case EOpConstructInt8: basicOp = EOpConstructInt8; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticInt8Enabled()) { + TType tempType(EbtInt, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructInt8) + aggregateOp = EOpConstructInt; + else + aggregateOp = (TOperator)(EOpConstructIVec2 + op - EOpConstructI8Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtInt8, newNode); + return newNode; + } break; case EOpConstructU8Vec2: @@ -6894,6 +7064,22 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T case EOpConstructU8Vec4: case EOpConstructUint8: basicOp = EOpConstructUint8; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticInt8Enabled()) { + TType tempType(EbtUint, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructUint8) + aggregateOp = EOpConstructUint; + else + aggregateOp = (TOperator)(EOpConstructUVec2 + op - EOpConstructU8Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtUint8, newNode); + return newNode; + } break; case EOpConstructI16Vec2: @@ -6901,6 +7087,22 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T case EOpConstructI16Vec4: case EOpConstructInt16: basicOp = EOpConstructInt16; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticInt16Enabled()) { + TType tempType(EbtInt, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructInt16) + aggregateOp = EOpConstructInt; + else + aggregateOp = (TOperator)(EOpConstructIVec2 + op - EOpConstructI16Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtInt16, newNode); + return newNode; + } break; case EOpConstructU16Vec2: @@ -6908,20 +7110,22 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T case EOpConstructU16Vec4: case EOpConstructUint16: basicOp = EOpConstructUint16; - break; - - case EOpConstructIVec2: - case EOpConstructIVec3: - case EOpConstructIVec4: - case EOpConstructInt: - basicOp = EOpConstructInt; - break; - - case EOpConstructUVec2: - case EOpConstructUVec3: - case EOpConstructUVec4: - case EOpConstructUint: - basicOp = EOpConstructUint; + // 8/16-bit storage extensions don't support constructing composites of 8/16-bit types, + // so construct a 32-bit type and convert + if (!intermediate.getArithemeticInt16Enabled()) { + TType tempType(EbtUint, EvqTemporary, type.getVectorSize()); + newNode = node; + if (tempType != newNode->getType()) { + TOperator aggregateOp; + if (op == EOpConstructUint16) + aggregateOp = EOpConstructUint; + else + aggregateOp = (TOperator)(EOpConstructUVec2 + op - EOpConstructU16Vec2); + newNode = intermediate.setAggregateOperator(newNode, aggregateOp, tempType, node->getLoc()); + } + newNode = intermediate.addConversion(EbtUint16, newNode); + return newNode; + } break; case EOpConstructI64Vec2: @@ -6932,7 +7136,7 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T break; case EOpConstructUint64: - if (type.isScalar() && node->getType().getBasicType() == EbtReference) { + if (type.isScalar() && node->getType().isReference()) { TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvPtrToUint64, true, node, type); return newNode; } @@ -6943,27 +7147,27 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T basicOp = EOpConstructUint64; break; - case EOpConstructBVec2: - case EOpConstructBVec3: - case EOpConstructBVec4: - case EOpConstructBool: - basicOp = EOpConstructBool; - break; - case EOpConstructNonuniform: // Make a nonuniform copy of node - newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpCopyObject, true, node, node->getType()); - newNode->getWritableType().getQualifier().nonUniform = true; + newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpCopyObject, true, node, type); return newNode; case EOpConstructReference: // construct reference from reference - if (node->getType().getBasicType() == EbtReference) { + if (node->getType().isReference()) { newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConstructReference, true, node, type); return newNode; // construct reference from uint64 } else if (node->getType().isScalar() && node->getType().getBasicType() == EbtUint64) { - TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvUint64ToPtr, true, node, type); + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvUint64ToPtr, true, node, + type); + return newNode; + // construct reference from uvec2 + } else if (node->getType().isVector() && node->getType().getBasicType() == EbtUint && + node->getVectorSize() == 2) { + requireExtensions(loc, 1, &E_GL_EXT_buffer_reference_uvec2, "uvec2 conversion to reference"); + TIntermTyped* newNode = intermediate.addBuiltInFunctionCall(node->getLoc(), EOpConvUvec2ToPtr, true, node, + type); return newNode; } else { return nullptr; @@ -6976,19 +7180,75 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T } node = intermediate.setAggregateOperator(node, EOpConstructCooperativeMatrix, type, node->getLoc()); } else { + TOperator op = EOpNull; switch (type.getBasicType()) { default: assert(0); break; + case EbtInt: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToInt; break; + case EbtFloat16: op = EOpConvFloat16ToInt; break; + case EbtUint8: op = EOpConvUint8ToInt; break; + case EbtInt8: op = EOpConvInt8ToInt; break; + case EbtUint: op = EOpConvUintToInt; break; + default: assert(0); + } + break; + case EbtUint: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToUint; break; + case EbtFloat16: op = EOpConvFloat16ToUint; break; + case EbtUint8: op = EOpConvUint8ToUint; break; + case EbtInt8: op = EOpConvInt8ToUint; break; + case EbtInt: op = EOpConvIntToUint; break; + case EbtUint: op = EOpConvUintToInt8; break; + default: assert(0); + } + break; + case EbtInt8: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToInt8; break; + case EbtFloat16: op = EOpConvFloat16ToInt8; break; + case EbtUint8: op = EOpConvUint8ToInt8; break; + case EbtInt: op = EOpConvIntToInt8; break; + case EbtUint: op = EOpConvUintToInt8; break; + default: assert(0); + } + break; + case EbtUint8: + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToUint8; break; + case EbtFloat16: op = EOpConvFloat16ToUint8; break; + case EbtInt8: op = EOpConvInt8ToUint8; break; + case EbtInt: op = EOpConvIntToUint8; break; + case EbtUint: op = EOpConvUintToUint8; break; + default: assert(0); + } + break; case EbtFloat: - assert(node->getType().getBasicType() == EbtFloat16); - node = intermediate.addUnaryNode(EOpConvFloat16ToFloat, node, node->getLoc(), type); + switch (node->getType().getBasicType()) { + case EbtFloat16: op = EOpConvFloat16ToFloat; break; + case EbtInt8: op = EOpConvInt8ToFloat; break; + case EbtUint8: op = EOpConvUint8ToFloat; break; + case EbtInt: op = EOpConvIntToFloat; break; + case EbtUint: op = EOpConvUintToFloat; break; + default: assert(0); + } break; case EbtFloat16: - assert(node->getType().getBasicType() == EbtFloat); - node = intermediate.addUnaryNode(EOpConvFloatToFloat16, node, node->getLoc(), type); + switch (node->getType().getBasicType()) { + case EbtFloat: op = EOpConvFloatToFloat16; break; + case EbtInt8: op = EOpConvInt8ToFloat16; break; + case EbtUint8: op = EOpConvUint8ToFloat16; break; + case EbtInt: op = EOpConvIntToFloat16; break; + case EbtUint: op = EOpConvUintToFloat16; break; + default: assert(0); + } break; } + + node = intermediate.addUnaryNode(op, node, node->getLoc(), type); // If it's a (non-specialization) constant, it must be folded. if (node->getAsUnaryNode()->getOperand()->getAsConstantUnion()) return node->getAsUnaryNode()->getOperand()->getAsConstantUnion()->fold(op, node->getType()); @@ -6996,6 +7256,8 @@ TIntermTyped* TParseContext::constructBuiltIn(const TType& type, TOperator op, T return node; +#endif // GLSLANG_WEB + default: error(loc, "unsupported construction", "", ""); @@ -7037,6 +7299,23 @@ TIntermTyped* TParseContext::constructAggregate(TIntermNode* node, const TType& return converted; } +// If a memory qualifier is present in 'to', also make it present in 'from'. +void TParseContext::inheritMemoryQualifiers(const TQualifier& from, TQualifier& to) +{ +#ifndef GLSLANG_WEB + if (from.isReadOnly()) + to.readonly = from.readonly; + if (from.isWriteOnly()) + to.writeonly = from.writeonly; + if (from.coherent) + to.coherent = from.coherent; + if (from.volatil) + to.volatil = from.volatil; + if (from.restrict) + to.restrict = from.restrict; +#endif +} + // // Do everything needed to add an interface block. // @@ -7052,7 +7331,7 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con requireProfile(loc, ~EEsProfile, "array-of-array of block"); } - // fix and check for member storage qualifiers and types that don't belong within a block + // Inherit and check member storage qualifiers WRT to the block-level qualifier. for (unsigned int member = 0; member < typeList.size(); ++member) { TType& memberType = *typeList[member].type; TQualifier& memberQualifier = memberType.getQualifier(); @@ -7061,7 +7340,8 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con if (memberQualifier.storage != EvqTemporary && memberQualifier.storage != EvqGlobal && memberQualifier.storage != currentBlockQualifier.storage) error(memberLoc, "member storage qualifier cannot contradict block storage qualifier", memberType.getFieldName().c_str(), ""); memberQualifier.storage = currentBlockQualifier.storage; -#ifdef NV_EXTENSIONS +#ifndef GLSLANG_WEB + inheritMemoryQualifiers(currentBlockQualifier, memberQualifier); if (currentBlockQualifier.perPrimitiveNV) memberQualifier.perPrimitiveNV = currentBlockQualifier.perPrimitiveNV; if (currentBlockQualifier.perViewNV) @@ -7114,18 +7394,13 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con // Special case for "push_constant uniform", which has a default of std430, // contrary to normal uniform defaults, and can't have a default tracked for it. - if ((currentBlockQualifier.layoutPushConstant && !currentBlockQualifier.hasPacking()) -#ifdef NV_EXTENSIONS - || (currentBlockQualifier.layoutShaderRecordNV && !currentBlockQualifier.hasPacking()) -#endif - ) + if ((currentBlockQualifier.isPushConstant() && !currentBlockQualifier.hasPacking()) || + (currentBlockQualifier.isShaderRecordNV() && !currentBlockQualifier.hasPacking())) currentBlockQualifier.layoutPacking = ElpStd430; -#ifdef NV_EXTENSIONS // Special case for "taskNV in/out", which has a default of std430, - if (currentBlockQualifier.perTaskNV && !currentBlockQualifier.hasPacking()) + if (currentBlockQualifier.isTaskMemory() && !currentBlockQualifier.hasPacking()) currentBlockQualifier.layoutPacking = ElpStd430; -#endif // fix and check for member layout qualifiers @@ -7143,12 +7418,11 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con bool memberWithLocation = false; bool memberWithoutLocation = false; -#ifdef NV_EXTENSIONS bool memberWithPerViewQualifier = false; -#endif for (unsigned int member = 0; member < typeList.size(); ++member) { TQualifier& memberQualifier = typeList[member].type->getQualifier(); const TSourceLoc& memberLoc = typeList[member].loc; +#ifndef GLSLANG_WEB if (memberQualifier.hasStream()) { if (defaultQualification.layoutStream != memberQualifier.layoutStream) error(memberLoc, "member cannot contradict block", "stream", ""); @@ -7162,12 +7436,14 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con if (defaultQualification.layoutXfbBuffer != memberQualifier.layoutXfbBuffer) error(memberLoc, "member cannot contradict block (or what block inherited from global)", "xfb_buffer", ""); } +#endif if (memberQualifier.hasPacking()) error(memberLoc, "member of block cannot have a packing layout qualifier", typeList[member].type->getFieldName().c_str(), ""); if (memberQualifier.hasLocation()) { const char* feature = "location on block member"; switch (currentBlockQualifier.storage) { +#ifndef GLSLANG_WEB case EvqVaryingIn: case EvqVaryingOut: requireProfile(memberLoc, ECoreProfile | ECompatibilityProfile | EEsProfile, feature); @@ -7175,6 +7451,7 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con profileRequires(memberLoc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, feature); memberWithLocation = true; break; +#endif default: error(memberLoc, "can only use in an in/out block", feature, ""); break; @@ -7191,11 +7468,9 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con error(memberLoc, "can only be used with std140, std430, or scalar layout packing", "offset/align", ""); } -#ifdef NV_EXTENSIONS if (memberQualifier.isPerView()) { memberWithPerViewQualifier = true; } -#endif TQualifier newMemberQualification = defaultQualification; mergeQualifiers(memberLoc, newMemberQualification, memberQualifier, false); @@ -7204,6 +7479,7 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con layoutMemberLocationArrayCheck(loc, memberWithLocation, arraySizes); +#ifndef GLSLANG_WEB // Ensure that the block has an XfbBuffer assigned. This is needed // because if the block has a XfbOffset assigned, then it is // assumed that it has implicitly assigned the current global @@ -7213,6 +7489,7 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con if (!currentBlockQualifier.hasXfbBuffer() && currentBlockQualifier.hasXfbOffset()) currentBlockQualifier.layoutXfbBuffer = globalOutputDefaults.layoutXfbBuffer; } +#endif // Process the members fixBlockLocations(loc, currentBlockQualifier, typeList, memberWithLocation, memberWithoutLocation); @@ -7221,10 +7498,10 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con for (unsigned int member = 0; member < typeList.size(); ++member) layoutTypeCheck(typeList[member].loc, *typeList[member].type); -#ifdef NV_EXTENSIONS +#ifndef GLSLANG_WEB if (memberWithPerViewQualifier) { for (unsigned int member = 0; member < typeList.size(); ++member) { - resizeMeshViewDimension(typeList[member].loc, *typeList[member].type); + checkAndResizeMeshViewDim(typeList[member].loc, *typeList[member].type, /*isBlockMember*/ true); } } #endif @@ -7240,10 +7517,11 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con TType blockType(&typeList, *blockName, currentBlockQualifier); if (arraySizes != nullptr) blockType.transferArraySizes(arraySizes); - else - ioArrayCheck(loc, blockType, instanceName ? *instanceName : *blockName); - if (currentBlockQualifier.layoutBufferReference) { +#ifndef GLSLANG_WEB + if (arraySizes == nullptr) + ioArrayCheck(loc, blockType, instanceName ? *instanceName : *blockName); + if (currentBlockQualifier.hasBufferReference()) { if (currentBlockQualifier.storage != EvqBuffer) error(loc, "can only be used with buffer", "buffer_reference", ""); @@ -7255,7 +7533,7 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con TVariable* blockNameVar = new TVariable(blockName, blockNameType, true); if (! symbolTable.insert(*blockNameVar)) { TSymbol* existingName = symbolTable.find(*blockName); - if (existingName->getType().getBasicType() == EbtReference && + if (existingName->getType().isReference() && existingName->getType().getReferentType()->getStruct() && existingName->getType().getReferentType()->getStruct()->size() == 0 && existingName->getType().getQualifier().storage == blockType.getQualifier().storage) { @@ -7267,7 +7545,9 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con if (!instanceName) { return; } - } else { + } else +#endif + { // // Don't make a user-defined type out of block name; that will cause an error // if the same block name gets reused in a different interface. @@ -7315,12 +7595,14 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con // Check for general layout qualifier errors layoutObjectCheck(loc, variable); +#ifndef GLSLANG_WEB // fix up if (isIoResizeArray(blockType)) { ioArraySymbolResizeList.push_back(&variable); checkIoArraysConsistency(loc, true); } else fixIoArraySize(loc, variable.getWritableType()); +#endif // Save it in the AST for linker use. trackLinkage(variable); @@ -7333,8 +7615,8 @@ void TParseContext::blockStageIoCheck(const TSourceLoc& loc, const TQualifier& q switch (qualifier.storage) { case EvqUniform: profileRequires(loc, EEsProfile, 300, nullptr, "uniform block"); - profileRequires(loc, ENoProfile, 140, nullptr, "uniform block"); - if (currentBlockQualifier.layoutPacking == ElpStd430 && ! currentBlockQualifier.layoutPushConstant) + profileRequires(loc, ENoProfile, 140, E_GL_ARB_uniform_buffer_object, "uniform block"); + if (currentBlockQualifier.layoutPacking == ElpStd430 && ! currentBlockQualifier.isPushConstant()) requireExtensions(loc, 1, &E_GL_EXT_scalar_block_layout, "std430 requires the buffer storage qualifier"); break; case EvqBuffer: @@ -7346,41 +7628,28 @@ void TParseContext::blockStageIoCheck(const TSourceLoc& loc, const TQualifier& q profileRequires(loc, ~EEsProfile, 150, E_GL_ARB_separate_shader_objects, "input block"); // It is a compile-time error to have an input block in a vertex shader or an output block in a fragment shader // "Compute shaders do not permit user-defined input variables..." - requireStage(loc, (EShLanguageMask)(EShLangTessControlMask|EShLangTessEvaluationMask|EShLangGeometryMask|EShLangFragmentMask -#ifdef NV_EXTENSIONS - |EShLangMeshNVMask -#endif - ), "input block"); + requireStage(loc, (EShLanguageMask)(EShLangTessControlMask|EShLangTessEvaluationMask|EShLangGeometryMask| + EShLangFragmentMask|EShLangMeshNVMask), "input block"); if (language == EShLangFragment) { profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, "fragment input block"); - } -#ifdef NV_EXTENSIONS - else if (language == EShLangMeshNV && ! qualifier.isTaskMemory()) { + } else if (language == EShLangMeshNV && ! qualifier.isTaskMemory()) { error(loc, "input blocks cannot be used in a mesh shader", "out", ""); } -#endif break; case EvqVaryingOut: profileRequires(loc, ~EEsProfile, 150, E_GL_ARB_separate_shader_objects, "output block"); - requireStage(loc, (EShLanguageMask)(EShLangVertexMask|EShLangTessControlMask|EShLangTessEvaluationMask|EShLangGeometryMask -#ifdef NV_EXTENSIONS - |EShLangMeshNVMask|EShLangTaskNVMask -#endif - ), "output block"); + requireStage(loc, (EShLanguageMask)(EShLangVertexMask|EShLangTessControlMask|EShLangTessEvaluationMask| + EShLangGeometryMask|EShLangMeshNVMask|EShLangTaskNVMask), "output block"); // ES 310 can have a block before shader_io is turned on, so skip this test for built-ins if (language == EShLangVertex && ! parsingBuiltins) { profileRequires(loc, EEsProfile, 320, Num_AEP_shader_io_blocks, AEP_shader_io_blocks, "vertex output block"); - } -#ifdef NV_EXTENSIONS - else if (language == EShLangMeshNV && qualifier.isTaskMemory()) { + } else if (language == EShLangMeshNV && qualifier.isTaskMemory()) { error(loc, "can only use on input blocks in mesh shader", "taskNV", ""); - } - else if (language == EShLangTaskNV && ! qualifier.isTaskMemory()) { + } else if (language == EShLangTaskNV && ! qualifier.isTaskMemory()) { error(loc, "output blocks cannot be used in a task shader", "out", ""); } -#endif break; -#ifdef NV_EXTENSIONS +#ifndef GLSLANG_WEB case EvqPayloadNV: profileRequires(loc, ~EEsProfile, 460, E_GL_NV_ray_tracing, "rayPayloadNV block"); requireStage(loc, (EShLanguageMask)(EShLangRayGenNVMask | EShLangAnyHitNVMask | EShLangClosestHitNVMask | EShLangMissNVMask), @@ -7435,18 +7704,16 @@ void TParseContext::blockQualifierCheck(const TSourceLoc& loc, const TQualifier& error(loc, "cannot use interpolation qualifiers on an interface block", "flat/smooth/noperspective", ""); if (qualifier.centroid) error(loc, "cannot use centroid qualifier on an interface block", "centroid", ""); - if (qualifier.sample) + if (qualifier.isSample()) error(loc, "cannot use sample qualifier on an interface block", "sample", ""); if (qualifier.invariant) error(loc, "cannot use invariant qualifier on an interface block", "invariant", ""); - if (qualifier.layoutPushConstant) + if (qualifier.isPushConstant()) intermediate.addPushConstantCount(); -#ifdef NV_EXTENSIONS - if (qualifier.layoutShaderRecordNV) + if (qualifier.isShaderRecordNV()) intermediate.addShaderRecordNVCount(); - if (qualifier.perTaskNV) + if (qualifier.isTaskMemory()) intermediate.addTaskNVCount(); -#endif } // @@ -7495,6 +7762,7 @@ void TParseContext::fixBlockLocations(const TSourceLoc& loc, TQualifier& qualifi void TParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList) { +#ifndef GLSLANG_WEB // "If a block is qualified with xfb_offset, all its // members are assigned transform feedback buffer offsets. If a block is not qualified with xfb_offset, any // members of that block not qualified with an xfb_offset will not be assigned transform feedback buffer @@ -7507,24 +7775,18 @@ void TParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList) for (unsigned int member = 0; member < typeList.size(); ++member) { TQualifier& memberQualifier = typeList[member].type->getQualifier(); bool contains64BitType = false; -#ifdef AMD_EXTENSIONS bool contains32BitType = false; bool contains16BitType = false; int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType, contains32BitType, contains16BitType); -#else - int memberSize = intermediate.computeTypeXfbSize(*typeList[member].type, contains64BitType); -#endif // see if we need to auto-assign an offset to this member if (! memberQualifier.hasXfbOffset()) { // "if applied to an aggregate containing a double or 64-bit integer, the offset must also be a multiple of 8" if (contains64BitType) RoundToPow2(nextOffset, 8); -#ifdef AMD_EXTENSIONS else if (contains32BitType) RoundToPow2(nextOffset, 4); else if (contains16BitType) RoundToPow2(nextOffset, 2); -#endif memberQualifier.layoutXfbOffset = nextOffset; } else nextOffset = memberQualifier.layoutXfbOffset; @@ -7534,6 +7796,7 @@ void TParseContext::fixXfbOffsets(TQualifier& qualifier, TTypeList& typeList) // The above gave all block members an offset, so we can take it off the block now, // which will avoid double counting the offset usage. qualifier.layoutXfbOffset = TQualifier::layoutXfbOffsetEnd; +#endif } // Calculate and save the offset of each block member, using the recursively @@ -7610,7 +7873,7 @@ void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qua // a qualifier to an existing symbol. Detect this and create the block reference // type with an empty type list, which will be filled in later in // TParseContext::declareBlock. - if (!symbol && qualifier.layoutBufferReference) { + if (!symbol && qualifier.hasBufferReference()) { TTypeList typeList; TType blockType(&typeList, identifier, qualifier);; TType blockNameType(EbtReference, blockType, identifier); @@ -7650,10 +7913,10 @@ void TParseContext::addQualifierToExisting(const TSourceLoc& loc, TQualifier qua error(loc, "cannot change qualification after use", "invariant", ""); symbol->getWritableType().getQualifier().invariant = true; invariantCheck(loc, symbol->getType().getQualifier()); - } else if (qualifier.noContraction) { + } else if (qualifier.isNoContraction()) { if (intermediate.inIoAccessed(identifier)) error(loc, "cannot change qualification after use", "precise", ""); - symbol->getWritableType().getQualifier().noContraction = true; + symbol->getWritableType().getQualifier().setNoContraction(); } else if (qualifier.specConstant) { symbol->getWritableType().getQualifier().makeSpecConstant(); if (qualifier.hasSpecConstantId()) @@ -7676,7 +7939,7 @@ void TParseContext::invariantCheck(const TSourceLoc& loc, const TQualifier& qual bool pipeOut = qualifier.isPipeOutput(); bool pipeIn = qualifier.isPipeInput(); - if (version >= 300 || (profile != EEsProfile && version >= 420)) { + if (version >= 300 || (!isEsProfile() && version >= 420)) { if (! pipeOut) error(loc, "can only apply to an output", "invariant", ""); } else { @@ -7691,12 +7954,9 @@ void TParseContext::invariantCheck(const TSourceLoc& loc, const TQualifier& qual // void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, const TPublicType& publicType) { +#ifndef GLSLANG_WEB if (publicType.shaderQualifiers.vertices != TQualifier::layoutNotSet) { -#ifdef NV_EXTENSIONS assert(language == EShLangTessControl || language == EShLangGeometry || language == EShLangMeshNV); -#else - assert(language == EShLangTessControl || language == EShLangGeometry); -#endif const char* id = (language == EShLangTessControl) ? "vertices" : "max_vertices"; if (publicType.qualifier.storage != EvqVaryingOut) @@ -7707,7 +7967,6 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con if (language == EShLangTessControl) checkIoArraysConsistency(loc); } -#ifdef NV_EXTENSIONS if (publicType.shaderQualifiers.primitives != TQualifier::layoutNotSet) { assert(language == EShLangMeshNV); const char* id = "max_primitives"; @@ -7717,7 +7976,6 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con if (! intermediate.setPrimitives(publicType.shaderQualifiers.primitives)) error(loc, "cannot change previously set layout value", id, ""); } -#endif if (publicType.shaderQualifiers.invocations != TQualifier::layoutNotSet) { if (publicType.qualifier.storage != EvqVaryingIn) error(loc, "can only apply to 'in'", "invocations", ""); @@ -7734,12 +7992,10 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con case ElgTrianglesAdjacency: case ElgQuads: case ElgIsolines: -#ifdef NV_EXTENSIONS if (language == EShLangMeshNV) { error(loc, "cannot apply to input", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); break; } -#endif if (intermediate.setInputPrimitive(publicType.shaderQualifiers.geometry)) { if (language == EShLangGeometry) checkIoArraysConsistency(loc); @@ -7751,14 +8007,12 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con } } else if (publicType.qualifier.storage == EvqVaryingOut) { switch (publicType.shaderQualifiers.geometry) { -#ifdef NV_EXTENSIONS case ElgLines: case ElgTriangles: if (language != EShLangMeshNV) { error(loc, "cannot apply to 'out'", TQualifier::getGeometryString(publicType.shaderQualifiers.geometry), ""); break; } -#endif // Fall through case ElgPoints: case ElgLineStrip: @@ -7792,8 +8046,9 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con else error(loc, "can only apply to 'in'", "point_mode", ""); } +#endif for (int i = 0; i < 3; ++i) { - if (publicType.shaderQualifiers.localSize[i] > 1) { + if (publicType.shaderQualifiers.localSizeNotDefault[i]) { if (publicType.qualifier.storage == EvqVaryingIn) { if (! intermediate.setLocalSize(i, publicType.shaderQualifiers.localSize[i])) error(loc, "cannot change previously set size", "local_size", ""); @@ -7809,7 +8064,7 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con if (intermediate.getLocalSize(i) > (unsigned int)max) error(loc, "too large; see gl_MaxComputeWorkGroupSize", "local_size", ""); } -#ifdef NV_EXTENSIONS +#ifndef GLSLANG_WEB else if (language == EShLangMeshNV) { switch (i) { case 0: max = resources.maxMeshWorkGroupSizeX_NV; break; @@ -7819,8 +8074,7 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con } if (intermediate.getLocalSize(i) > (unsigned int)max) error(loc, "too large; see gl_MaxMeshWorkGroupSizeNV", "local_size", ""); - } - else if (language == EShLangTaskNV) { + } else if (language == EShLangTaskNV) { switch (i) { case 0: max = resources.maxTaskWorkGroupSizeX_NV; break; case 1: max = resources.maxTaskWorkGroupSizeY_NV; break; @@ -7855,6 +8109,8 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con workGroupSize->getWritableType().getQualifier().specConstant = true; } } + +#ifndef GLSLANG_WEB if (publicType.shaderQualifiers.earlyFragmentTests) { if (publicType.qualifier.storage == EvqVaryingIn) intermediate.setEarlyFragmentTests(); @@ -7867,12 +8123,19 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con else error(loc, "can only apply to 'in'", "post_coverage_coverage", ""); } - if (publicType.shaderQualifiers.blendEquation) { + if (publicType.shaderQualifiers.hasBlendEquation()) { if (publicType.qualifier.storage != EvqVaryingOut) error(loc, "can only apply to 'out'", "blend equation", ""); } + if (publicType.shaderQualifiers.interlockOrdering) { + if (publicType.qualifier.storage == EvqVaryingIn) { + if (!intermediate.setInterlockOrdering(publicType.shaderQualifiers.interlockOrdering)) + error(loc, "cannot change previously set fragment shader interlock ordering", TQualifier::getInterlockOrderingString(publicType.shaderQualifiers.interlockOrdering), ""); + } + else + error(loc, "can only apply to 'in'", TQualifier::getInterlockOrderingString(publicType.shaderQualifiers.interlockOrdering), ""); + } -#ifdef NV_EXTENSIONS if (publicType.shaderQualifiers.layoutDerivativeGroupQuads && publicType.shaderQualifiers.layoutDerivativeGroupLinear) { error(loc, "cannot be both specified", "derivative_group_quadsNV and derivative_group_linearNV", ""); @@ -7917,6 +8180,7 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con qualifier.isInterpolation() || qualifier.precision != EpqNone) error(loc, "cannot use auxiliary, memory, interpolation, or precision qualifier in a default qualifier declaration (declaration with no type)", "qualifier", ""); + // "The offset qualifier can only be used on block members of blocks..." // "The align qualifier can only be used on blocks or block members..." if (qualifier.hasOffset() || @@ -7941,6 +8205,7 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con case EvqVaryingIn: break; case EvqVaryingOut: +#ifndef GLSLANG_WEB if (qualifier.hasStream()) globalOutputDefaults.layoutStream = qualifier.layoutStream; if (qualifier.hasXfbBuffer()) @@ -7949,6 +8214,7 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con if (! intermediate.setXfbBufferStride(globalOutputDefaults.layoutXfbBuffer, qualifier.layoutXfbStride)) error(loc, "all stride settings must match for xfb buffer", "xfb_stride", "%d", qualifier.layoutXfbBuffer); } +#endif break; default: error(loc, "default qualifier requires 'uniform', 'buffer', 'in', or 'out' storage qualification", "", ""); @@ -7961,16 +8227,14 @@ void TParseContext::updateStandaloneQualifierDefaults(const TSourceLoc& loc, con error(loc, "cannot declare a default, use a full declaration", "location/component/index", ""); if (qualifier.hasXfbOffset()) error(loc, "cannot declare a default, use a full declaration", "xfb_offset", ""); - if (qualifier.layoutPushConstant) + if (qualifier.isPushConstant()) error(loc, "cannot declare a default, can only be used on a block", "push_constant", ""); - if (qualifier.layoutBufferReference) + if (qualifier.hasBufferReference()) error(loc, "cannot declare a default, can only be used on a block", "buffer_reference", ""); if (qualifier.hasSpecConstantId()) error(loc, "cannot declare a default, can only be used on a scalar", "constant_id", ""); -#ifdef NV_EXTENSIONS - if (qualifier.layoutShaderRecordNV) + if (qualifier.isShaderRecordNV()) error(loc, "cannot declare a default, can only be used on a block", "shaderRecordNV", ""); -#endif } // @@ -8037,7 +8301,7 @@ TIntermNode* TParseContext::addSwitch(const TSourceLoc& loc, TIntermTyped* expre // "it is an error to have no statement between a label and the end of the switch statement." // The specifications were updated to remove this (being ill-defined what a "statement" was), // so, this became a warning. However, 3.0 tests still check for the error. - if (profile == EEsProfile && version <= 300 && ! relaxedErrors()) + if (isEsProfile() && version <= 300 && ! relaxedErrors()) error(loc, "last case/default label not followed by statements", "switch", ""); else warn(loc, "last case/default label not followed by statements", "switch", ""); |