diff --git a/3rdparty/glslang/SPIRV/GlslangToSpv.cpp b/3rdparty/glslang/SPIRV/GlslangToSpv.cpp index 8eb06b7cd..6d9c7cfec 100644 --- a/3rdparty/glslang/SPIRV/GlslangToSpv.cpp +++ b/3rdparty/glslang/SPIRV/GlslangToSpv.cpp @@ -2784,6 +2784,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt break; case glslang::EOpAtomicAdd: + case glslang::EOpAtomicSubtract: case glslang::EOpAtomicMin: case glslang::EOpAtomicMax: case glslang::EOpAtomicAnd: @@ -2955,6 +2956,7 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt break; case glslang::EOpAtomicAdd: + case glslang::EOpAtomicSubtract: case glslang::EOpAtomicMin: case glslang::EOpAtomicMax: case glslang::EOpAtomicAnd: @@ -6894,6 +6896,7 @@ spv::Id TGlslangToSpvTraverser::createAtomicOperation(glslang::TOperator op, spv builder.addCapability(spv::CapabilityAtomicFloat64AddEXT); } break; + case glslang::EOpAtomicSubtract: case glslang::EOpAtomicCounterSubtract: opCode = spv::OpAtomicISub; break; diff --git a/3rdparty/glslang/StandAlone/StandAlone.cpp b/3rdparty/glslang/StandAlone/StandAlone.cpp index fdbf02799..923ded305 100644 --- a/3rdparty/glslang/StandAlone/StandAlone.cpp +++ b/3rdparty/glslang/StandAlone/StandAlone.cpp @@ -110,6 +110,7 @@ bool SpvToolsValidate = false; bool NaNClamp = false; bool stripDebugInfo = false; bool beQuiet = false; +bool VulkanRulesRelaxed = false; // // Return codes from main/exit(). @@ -195,6 +196,17 @@ std::array, glslang::EResCount> baseBindi std::array, glslang::EResCount> baseBindingForSet; std::array, EShLangCount> baseResourceSetBinding; +std::vector> blockStorageOverrides; + +bool setGlobalUniformBlock = false; +std::string globalUniformName; +unsigned int globalUniformBinding; +unsigned int globalUniformSet; + +bool setGlobalBufferBlock = false; +std::string atomicCounterBlockName; +unsigned int atomicCounterBlockSet; + // Add things like "#define ..." to a preamble to use in the beginning of the shader. class TPreamble { public: @@ -396,6 +408,115 @@ void ProcessResourceSetBindingBase(int& argc, char**& argv, std::array>& storage) +{ + if (argc < 3) + usage(); + + glslang::TBlockStorageClass blockStorage = glslang::EbsNone; + + std::string strBacking(argv[2]); + if (strBacking == "uniform") + blockStorage = glslang::EbsUniform; + else if (strBacking == "buffer") + blockStorage = glslang::EbsStorageBuffer; + else if (strBacking == "push_constant") + blockStorage = glslang::EbsPushConstant; + else { + printf("%s: invalid block storage\n", strBacking.c_str()); + usage(); + } + + storage.push_back(std::make_pair(std::string(argv[1]), blockStorage)); + + argc -= 2; + argv += 2; +} + +inline bool isNonDigit(char c) { + // a non-digit character valid in a glsl identifier + return (c == '_') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +// whether string isa valid identifier to be used in glsl +bool isValidIdentifier(const char* str) { + std::string idn(str); + + if (idn.length() == 0) { + return false; + } + + if (idn.length() >= 3 && idn.substr(0, 3) == "gl_") { + // identifiers startin with "gl_" are reserved + return false; + } + + if (!isNonDigit(idn[0])) { + return false; + } + + for (unsigned int i = 1; i < idn.length(); ++i) { + if (!(isdigit(idn[i]) || isNonDigit(idn[i]))) { + return false; + } + } + + return true; +} + +// Process settings for either the global buffer block or global unfirom block +// of the form: +// --argname name set binding +void ProcessGlobalBlockSettings(int& argc, char**& argv, std::string* name, unsigned int* set, unsigned int* binding) +{ + if (argc < 4) + usage(); + + unsigned int curArg = 1; + + assert(name || set || binding); + + if (name) { + if (!isValidIdentifier(argv[curArg])) { + printf("%s: invalid identifier\n", argv[curArg]); + usage(); + } + *name = argv[curArg]; + + curArg++; + } + + if (set) { + errno = 0; + int setVal = ::strtol(argv[curArg], NULL, 10); + if (errno || setVal < 0) { + printf("%s: invalid set\n", argv[curArg]); + usage(); + } + *set = setVal; + + curArg++; + } + + if (binding) { + errno = 0; + int bindingVal = ::strtol(argv[curArg], NULL, 10); + if (errno || bindingVal < 0) { + printf("%s: invalid binding\n", argv[curArg]); + usage(); + } + *binding = bindingVal; + + curArg++; + } + + argc -= (curArg - 1); + argv += (curArg - 1); +} + // // Do all command-line argument parsing. This includes building up the work-items // to be processed later, and saving all the command-line options. @@ -569,6 +690,17 @@ void ProcessArguments(std::vector>& workItem lowerword == "resource-set-binding" || lowerword == "rsb") { ProcessResourceSetBindingBase(argc, argv, baseResourceSetBinding); + } else if (lowerword == "set-block-storage" || + lowerword == "sbs") { + ProcessBlockStorage(argc, argv, blockStorageOverrides); + } else if (lowerword == "set-atomic-counter-block" || + lowerword == "sacb") { + ProcessGlobalBlockSettings(argc, argv, &atomicCounterBlockName, &atomicCounterBlockSet, nullptr); + setGlobalBufferBlock = true; + } else if (lowerword == "set-default-uniform-block" || + lowerword == "sdub") { + ProcessGlobalBlockSettings(argc, argv, &globalUniformName, &globalUniformSet, &globalUniformBinding); + setGlobalUniformBlock = true; } else if (lowerword == "shift-image-bindings" || // synonyms lowerword == "shift-image-binding" || lowerword == "sib") { @@ -721,6 +853,9 @@ void ProcessArguments(std::vector>& workItem else Error("unknown -O option"); break; + case 'R': + VulkanRulesRelaxed = true; + break; case 'S': if (argc <= 1) Error("no specified for -S"); @@ -1068,6 +1203,24 @@ void CompileAndLinkShaderUnits(std::vector compUnits) shader->setUniformLocationBase(uniformBase); #endif + if (VulkanRulesRelaxed) { + for (auto& storageOverride : blockStorageOverrides) { + shader->addBlockStorageOverride(storageOverride.first.c_str(), + storageOverride.second); + } + + if (setGlobalBufferBlock) { + shader->setAtomicCounterBlockName(atomicCounterBlockName.c_str()); + shader->setAtomicCounterBlockSet(atomicCounterBlockSet); + } + + if (setGlobalUniformBlock) { + shader->setGlobalUniformBlockName(globalUniformName.c_str()); + shader->setGlobalUniformSet(globalUniformSet); + shader->setGlobalUniformBinding(globalUniformBinding); + } + } + shader->setNanMinMaxClamp(NaNClamp); #ifdef ENABLE_HLSL @@ -1091,6 +1244,8 @@ void CompileAndLinkShaderUnits(std::vector compUnits) if (targetHlslFunctionality1) shader->setEnvTargetHlslFunctionality1(); #endif + if (VulkanRulesRelaxed) + shader->setEnvInputVulkanRulesRelaxed(); } shaders.push_back(shader); @@ -1572,6 +1727,9 @@ void usage() " is searched first, followed by left-to-right order of -I\n" " -Od disables optimization; may cause illegal SPIR-V for HLSL\n" " -Os optimizes SPIR-V to minimize size\n" + " -R use relaxed verification rules for generating Vulkan SPIR-V,\n" + " allowing the use of default uniforms, atomic_uints, and\n" + " gl_VertexID and gl_InstanceID keywords.\n" " -S uses specified stage rather than parsing the file extension\n" " choices for are vert, tesc, tese, geom, frag, or comp\n" " -U | --undef-macro | --U \n" @@ -1649,6 +1807,22 @@ void usage() " --resource-set-binding [stage] set\n" " set descriptor set for all resources\n" " --rsb synonym for --resource-set-binding\n" + " --set-block-backing name {uniform|buffer|push_constant}\n" + " changes the backing type of a uniform, buffer,\n" + " or push_constant block declared in\n" + " in the program, when using -R option.\n" + " This can be used to change the backing\n" + " for existing blocks as well as implicit ones\n" + " such as 'gl_DefaultUniformBlock'.\n" + " --sbs synonym for set-block-storage\n" + " --set-atomic-counter-block name set\n" + " set name, and descriptor set for\n" + " atomic counter blocks, with -R opt\n" + " --sacb synonym for set-atomic-counter-block\n" + " --set-default-uniform-block name set binding\n" + " set name, descriptor set, and binding for\n" + " global default-uniform-block, with -R opt\n" + " --sdub synonym for set-default-uniform-block\n" " --shift-image-binding [stage] num\n" " base binding number for images (uav)\n" " --shift-image-binding [stage] [num set]...\n" diff --git a/3rdparty/glslang/glslang/Include/Types.h b/3rdparty/glslang/glslang/Include/Types.h index 603203db9..04250843a 100644 --- a/3rdparty/glslang/glslang/Include/Types.h +++ b/3rdparty/glslang/glslang/Include/Types.h @@ -501,6 +501,7 @@ public: noContraction = false; nullInit = false; #endif + defaultBlock = false; } // drop qualifiers that don't belong in a temporary variable @@ -514,6 +515,7 @@ public: specConstant = false; nonUniform = false; nullInit = false; + defaultBlock = false; clearLayout(); } @@ -572,6 +574,7 @@ public: bool specConstant : 1; bool nonUniform : 1; bool explicitOffset : 1; + bool defaultBlock : 1; // default blocks with matching names have structures merged when linking #ifdef GLSLANG_WEB bool isWriteOnly() const { return false; } @@ -756,6 +759,46 @@ public: } } + TBlockStorageClass getBlockStorage() const { + if (storage == EvqUniform && !isPushConstant()) { + return EbsUniform; + } + else if (storage == EvqUniform) { + return EbsPushConstant; + } + else if (storage == EvqBuffer) { + return EbsStorageBuffer; + } + return EbsNone; + } + + void setBlockStorage(TBlockStorageClass newBacking) { +#ifndef GLSLANG_WEB + layoutPushConstant = (newBacking == EbsPushConstant); +#endif + switch (newBacking) { + case EbsUniform : + if (layoutPacking == ElpStd430) { + // std430 would not be valid + layoutPacking = ElpStd140; + } + storage = EvqUniform; + break; + case EbsStorageBuffer : + storage = EvqBuffer; + break; +#ifndef GLSLANG_WEB + case EbsPushConstant : + storage = EvqUniform; + layoutSet = TQualifier::layoutSetEnd; + layoutBinding = TQualifier::layoutBindingEnd; + break; +#endif + default: + break; + } + } + #ifdef GLSLANG_WEB bool isPerView() const { return false; } bool isTaskMemory() const { return false; } @@ -852,6 +895,7 @@ public: return hasNonXfbLayout() || hasXfb(); } + TLayoutMatrix layoutMatrix : 3; TLayoutPacking layoutPacking : 4; int layoutOffset; @@ -2292,6 +2336,17 @@ public: name += ';' ; } + // These variables are inconsistently declared inside and outside of gl_PerVertex in glslang right now. + // They are declared inside of 'in gl_PerVertex', but sitting as standalone when they are 'out'puts. + bool isInconsistentGLPerVertexMember(const TString& name) const + { + if (name == "gl_SecondaryPositionNV" || + name == "gl_PositionPerViewNV") + return true; + return false; + } + + // Do two structure types match? They could be declared independently, // in different places, but still might satisfy the definition of matching. // From the spec: @@ -2307,22 +2362,48 @@ public: (isStruct() && right.isStruct() && structure == right.structure)) return true; - // Both being nullptr was caught above, now they both have to be structures of the same number of elements - if (!isStruct() || !right.isStruct() || - structure->size() != right.structure->size()) - return false; - // Structure names have to match if (*typeName != *right.typeName) return false; - // Compare the names and types of all the members, which have to match - for (unsigned int i = 0; i < structure->size(); ++i) { - if ((*structure)[i].type->getFieldName() != (*right.structure)[i].type->getFieldName()) - return false; + // There are inconsistencies with how gl_PerVertex is setup. For now ignore those as errors if they + // are known inconsistencies. + bool isGLPerVertex = *typeName == "gl_PerVertex"; - if (*(*structure)[i].type != *(*right.structure)[i].type) - return false; + // Both being nullptr was caught above, now they both have to be structures of the same number of elements + if (!isStruct() || !right.isStruct() || + (structure->size() != right.structure->size() && !isGLPerVertex)) + return false; + + // Compare the names and types of all the members, which have to match + for (int li = 0, ri = 0; li < structure->size() || ri < right.structure->size(); ++li, ++ri) { + if (li < structure->size() && ri < right.structure->size()) { + if ((*structure)[li].type->getFieldName() == (*right.structure)[ri].type->getFieldName()) { + if (*(*structure)[li].type != *(*right.structure)[ri].type) + return false; + } else { + // If one of the members is something that's inconsistently declared, skip over it + // for now. + if (isGLPerVertex) { + if (isInconsistentGLPerVertexMember((*structure)[li].type->getFieldName())) { + ri--; + continue; + } else if (isInconsistentGLPerVertexMember((*right.structure)[ri].type->getFieldName())) { + li--; + continue; + } + } else { + return false; + } + } + // If we get here, then there should only be inconsistently declared members left + } else if (li < structure->size()) { + if (!isInconsistentGLPerVertexMember((*structure)[li].type->getFieldName())) + return false; + } else { + if (!isInconsistentGLPerVertexMember((*right.structure)[ri].type->getFieldName())) + return false; + } } return true; diff --git a/3rdparty/glslang/glslang/Include/intermediate.h b/3rdparty/glslang/glslang/Include/intermediate.h index 85edd6358..1440c205e 100644 --- a/3rdparty/glslang/glslang/Include/intermediate.h +++ b/3rdparty/glslang/glslang/Include/intermediate.h @@ -593,6 +593,7 @@ enum TOperator { EOpTime, EOpAtomicAdd, + EOpAtomicSubtract, EOpAtomicMin, EOpAtomicMax, EOpAtomicAnd, @@ -1135,6 +1136,8 @@ public: virtual TBasicType getBasicType() const { return type.getBasicType(); } virtual TQualifier& getQualifier() { return type.getQualifier(); } virtual const TQualifier& getQualifier() const { return type.getQualifier(); } + virtual TArraySizes* getArraySizes() { return type.getArraySizes(); } + virtual const TArraySizes* getArraySizes() const { return type.getArraySizes(); } virtual void propagatePrecision(TPrecisionQualifier); virtual int getVectorSize() const { return type.getVectorSize(); } virtual int getMatrixCols() const { return type.getMatrixCols(); } diff --git a/3rdparty/glslang/glslang/MachineIndependent/Initialize.cpp b/3rdparty/glslang/glslang/MachineIndependent/Initialize.cpp index c94a8a4b5..a9e5af456 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/Initialize.cpp +++ b/3rdparty/glslang/glslang/MachineIndependent/Initialize.cpp @@ -1627,6 +1627,36 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV "uint atomicCounterExchange(atomic_uint, uint);" "uint atomicCounterCompSwap(atomic_uint, uint, uint);" + "\n"); + } + } + else if (spvVersion.vulkanRelaxed) { + // + // Atomic counter functions act as aliases to normal atomic functions. + // replace definitions to take 'volatile coherent uint' instead of 'atomic_uint' + // and map to equivalent non-counter atomic op + // + if ((profile != EEsProfile && version >= 300) || + (profile == EEsProfile && version >= 310)) { + commonBuiltins.append( + "uint atomicCounterIncrement(volatile coherent uint);" + "uint atomicCounterDecrement(volatile coherent uint);" + "uint atomicCounter(volatile coherent uint);" + + "\n"); + } + if (profile != EEsProfile && version >= 460) { + commonBuiltins.append( + "uint atomicCounterAdd(volatile coherent uint, uint);" + "uint atomicCounterSubtract(volatile coherent uint, uint);" + "uint atomicCounterMin(volatile coherent uint, uint);" + "uint atomicCounterMax(volatile coherent uint, uint);" + "uint atomicCounterAnd(volatile coherent uint, uint);" + "uint atomicCounterOr(volatile coherent uint, uint);" + "uint atomicCounterXor(volatile coherent uint, uint);" + "uint atomicCounterExchange(volatile coherent uint, uint);" + "uint atomicCounterCompSwap(volatile coherent uint, uint, uint);" + "\n"); } } @@ -4124,7 +4154,7 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV } #ifndef GLSLANG_WEB if ((profile != EEsProfile && version >= 420) || esBarrier) { - if (spvVersion.vulkan == 0) { + if (spvVersion.vulkan == 0 || spvVersion.vulkanRelaxed) { commonBuiltins.append("void memoryBarrierAtomicCounter();"); } commonBuiltins.append("void memoryBarrierImage();"); @@ -4848,6 +4878,13 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV "in int gl_VertexIndex;" "in int gl_InstanceIndex;" ); + + if (spvVersion.vulkan > 0 && version >= 140 && spvVersion.vulkanRelaxed) + stageBuiltins[EShLangVertex].append( + "in int gl_VertexID;" // declare with 'in' qualifier + "in int gl_InstanceID;" + ); + if (version >= 440) { stageBuiltins[EShLangVertex].append( "in int gl_BaseVertexARB;" @@ -4885,7 +4922,7 @@ void TBuiltIns::initialize(int version, EProfile profile, const SpvVersion& spvV "mediump float gl_PointSize;" // needs qualifier fixed later ); } else { - if (spvVersion.vulkan == 0) + if (spvVersion.vulkan == 0 || spvVersion.vulkanRelaxed) stageBuiltins[EShLangVertex].append( "in highp int gl_VertexID;" // needs qualifier fixed later "in highp int gl_InstanceID;" // needs qualifier fixed later @@ -7436,6 +7473,12 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion SpecialQualifier("gl_InstanceID", EvqInstanceId, EbvInstanceId, symbolTable); } + if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) { + // treat these built-ins as aliases of VertexIndex and InstanceIndex + BuiltInVariable("gl_VertexID", EbvVertexIndex, symbolTable); + BuiltInVariable("gl_InstanceID", EbvInstanceIndex, symbolTable); + } + if (profile != EEsProfile) { if (version >= 440) { symbolTable.setVariableExtensions("gl_BaseVertexARB", 1, &E_GL_ARB_shader_draw_parameters); @@ -8912,6 +8955,14 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion symbolTable.relateToOperator("memoryBarrierAtomicCounter", EOpMemoryBarrierAtomicCounter); symbolTable.relateToOperator("memoryBarrierImage", EOpMemoryBarrierImage); + if (spvVersion.vulkanRelaxed) { + // + // functions signature have been replaced to take uint operations on buffer variables + // remap atomic counter functions to atomic operations + // + symbolTable.relateToOperator("memoryBarrierAtomicCounter", EOpMemoryBarrierBuffer); + } + symbolTable.relateToOperator("atomicLoad", EOpAtomicLoad); symbolTable.relateToOperator("atomicStore", EOpAtomicStore); @@ -8919,6 +8970,20 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion symbolTable.relateToOperator("atomicCounterDecrement", EOpAtomicCounterDecrement); symbolTable.relateToOperator("atomicCounter", EOpAtomicCounter); + if (spvVersion.vulkanRelaxed) { + // + // functions signature have been replaced to take uint operations + // remap atomic counter functions to atomic operations + // + // these atomic counter functions do not match signatures of glsl + // atomic functions, so they will be remapped to semantically + // equivalent functions in the parser + // + symbolTable.relateToOperator("atomicCounterIncrement", EOpNull); + symbolTable.relateToOperator("atomicCounterDecrement", EOpNull); + symbolTable.relateToOperator("atomicCounter", EOpNull); + } + symbolTable.relateToOperator("clockARB", EOpReadClockSubgroupKHR); symbolTable.relateToOperator("clock2x32ARB", EOpReadClockSubgroupKHR); @@ -8937,6 +9002,23 @@ void TBuiltIns::identifyBuiltIns(int version, EProfile profile, const SpvVersion symbolTable.relateToOperator("atomicCounterCompSwap", EOpAtomicCounterCompSwap); } + if (spvVersion.vulkanRelaxed) { + // + // functions signature have been replaced to take 'uint' instead of 'atomic_uint' + // remap atomic counter functions to non-counter atomic ops so + // functions act as aliases to non-counter atomic ops + // + symbolTable.relateToOperator("atomicCounterAdd", EOpAtomicAdd); + symbolTable.relateToOperator("atomicCounterSubtract", EOpAtomicSubtract); + symbolTable.relateToOperator("atomicCounterMin", EOpAtomicMin); + symbolTable.relateToOperator("atomicCounterMax", EOpAtomicMax); + symbolTable.relateToOperator("atomicCounterAnd", EOpAtomicAnd); + symbolTable.relateToOperator("atomicCounterOr", EOpAtomicOr); + symbolTable.relateToOperator("atomicCounterXor", EOpAtomicXor); + symbolTable.relateToOperator("atomicCounterExchange", EOpAtomicExchange); + symbolTable.relateToOperator("atomicCounterCompSwap", EOpAtomicCompSwap); + } + symbolTable.relateToOperator("fma", EOpFma); symbolTable.relateToOperator("frexp", EOpFrexp); symbolTable.relateToOperator("ldexp", EOpLdexp); diff --git a/3rdparty/glslang/glslang/MachineIndependent/ParseContextBase.cpp b/3rdparty/glslang/glslang/MachineIndependent/ParseContextBase.cpp index 3efa27aca..02cca409e 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/ParseContextBase.cpp +++ b/3rdparty/glslang/glslang/MachineIndependent/ParseContextBase.cpp @@ -601,7 +601,6 @@ void TParseContextBase::parseSwizzleSelector(const TSourceLoc& loc, const TStrin selector.push_back(0); } -#ifdef ENABLE_HLSL // // Make the passed-in variable information become a member of the // global uniform block. If this doesn't exist yet, make it. @@ -646,7 +645,67 @@ void TParseContextBase::growGlobalUniformBlock(const TSourceLoc& loc, TType& mem ++firstNewMember; } -#endif + +void TParseContextBase::growAtomicCounterBlock(int binding, const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) { + // Make the atomic counter block, if not yet made. + const auto &at = atomicCounterBuffers.find(binding); + if (at == atomicCounterBuffers.end()) { + atomicCounterBuffers.insert({binding, (TVariable*)nullptr }); + atomicCounterBlockFirstNewMember.insert({binding, 0}); + } + + TVariable*& atomicCounterBuffer = atomicCounterBuffers[binding]; + int& bufferNewMember = atomicCounterBlockFirstNewMember[binding]; + + if (atomicCounterBuffer == nullptr) { + TQualifier blockQualifier; + blockQualifier.clear(); + blockQualifier.storage = EvqBuffer; + + char charBuffer[512]; + if (binding != TQualifier::layoutBindingEnd) { + snprintf(charBuffer, 512, "%s_%d", getAtomicCounterBlockName(), binding); + } else { + snprintf(charBuffer, 512, "%s_0", getAtomicCounterBlockName()); + } + + TType blockType(new TTypeList, *NewPoolTString(charBuffer), blockQualifier); + setUniformBlockDefaults(blockType); + blockType.getQualifier().layoutPacking = ElpStd430; + atomicCounterBuffer = new TVariable(NewPoolTString(""), blockType, true); + // If we arn't auto mapping bindings then set the block to use the same + // binding as what the atomic was set to use + if (!intermediate.getAutoMapBindings()) { + atomicCounterBuffer->getWritableType().getQualifier().layoutBinding = binding; + } + bufferNewMember = 0; + + atomicCounterBuffer->getWritableType().getQualifier().layoutSet = atomicCounterBlockSet; + } + + // Add the requested member as a member to the global block. + TType* type = new TType; + type->shallowCopy(memberType); + type->setFieldName(memberName); + if (typeList) + type->setStruct(typeList); + TTypeLoc typeLoc = {type, loc}; + atomicCounterBuffer->getType().getWritableStruct()->push_back(typeLoc); + + // Insert into the symbol table. + if (bufferNewMember == 0) { + // This is the first request; we need a normal symbol table insert + if (symbolTable.insert(*atomicCounterBuffer)) + trackLinkage(*atomicCounterBuffer); + else + error(loc, "failed to insert the global constant buffer", "buffer", ""); + } else { + // This is a follow-on request; we need to amend the first insert + symbolTable.amend(*atomicCounterBuffer, bufferNewMember); + } + + ++bufferNewMember; +} void TParseContextBase::finish() { diff --git a/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp b/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp index 12d68a3e6..550fce679 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp +++ b/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.cpp @@ -225,6 +225,108 @@ void TParseContext::parserError(const char* s) error(getCurrentLoc(), "compilation terminated", "", ""); } +void TParseContext::growGlobalUniformBlock(const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) +{ + bool createBlock = globalUniformBlock == nullptr; + + if (createBlock) { + globalUniformBinding = intermediate.getGlobalUniformBinding(); + globalUniformSet = intermediate.getGlobalUniformSet(); + } + + // use base class function to create/expand block + TParseContextBase::growGlobalUniformBlock(loc, memberType, memberName, typeList); + + if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) { + // check for a block storage override + TBlockStorageClass storageOverride = intermediate.getBlockStorageOverride(getGlobalUniformBlockName()); + TQualifier& qualifier = globalUniformBlock->getWritableType().getQualifier(); + qualifier.defaultBlock = true; + + if (storageOverride != EbsNone) { + if (createBlock) { + // Remap block storage + qualifier.setBlockStorage(storageOverride); + + // check that the change didn't create errors + blockQualifierCheck(loc, qualifier, false); + } + + // remap meber storage as well + memberType.getQualifier().setBlockStorage(storageOverride); + } + } +} + +void TParseContext::growAtomicCounterBlock(int binding, const TSourceLoc& loc, TType& memberType, const TString& memberName, TTypeList* typeList) +{ + bool createBlock = atomicCounterBuffers.find(binding) == atomicCounterBuffers.end(); + + if (createBlock) { + atomicCounterBlockSet = intermediate.getAtomicCounterBlockSet(); + } + + // use base class function to create/expand block + TParseContextBase::growAtomicCounterBlock(binding, loc, memberType, memberName, typeList); + TQualifier& qualifier = atomicCounterBuffers[binding]->getWritableType().getQualifier(); + qualifier.defaultBlock = true; + + if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) { + // check for a Block storage override + TBlockStorageClass storageOverride = intermediate.getBlockStorageOverride(getAtomicCounterBlockName()); + + if (storageOverride != EbsNone) { + if (createBlock) { + // Remap block storage + + qualifier.setBlockStorage(storageOverride); + + // check that the change didn't create errors + blockQualifierCheck(loc, qualifier, false); + } + + // remap meber storage as well + memberType.getQualifier().setBlockStorage(storageOverride); + } + } +} + +const char* TParseContext::getGlobalUniformBlockName() const +{ + const char* name = intermediate.getGlobalUniformBlockName(); + if (std::string(name) == "") + return "gl_DefaultUniformBlock"; + else + return name; +} +void TParseContext::finalizeGlobalUniformBlockLayout(TVariable&) +{ +} +void TParseContext::setUniformBlockDefaults(TType& block) const +{ + block.getQualifier().layoutPacking = ElpStd140; + block.getQualifier().layoutMatrix = ElmColumnMajor; +} + + +const char* TParseContext::getAtomicCounterBlockName() const +{ + const char* name = intermediate.getAtomicCounterBlockName(); + if (std::string(name) == "") + return "gl_AtomicCounterBlock"; + else + return name; +} +void TParseContext::finalizeAtomicCounterBlockLayout(TVariable&) +{ +} + +void TParseContext::setAtomicCounterBlockDefaults(TType& block) const +{ + block.getQualifier().layoutPacking = ElpStd430; + block.getQualifier().layoutMatrix = ElmRowMajor; +} + void TParseContext::handlePragma(const TSourceLoc& loc, const TVector& tokens) { #ifndef GLSLANG_WEB @@ -1135,6 +1237,14 @@ TIntermTyped* TParseContext::handleFunctionCall(const TSourceLoc& loc, TFunction { TIntermTyped* result = nullptr; + if (spvVersion.vulkan != 0 && spvVersion.vulkanRelaxed) { + // allow calls that are invalid in Vulkan Semantics to be invisibily + // remapped to equivalent valid functions + result = vkRelaxedRemapFunctionCall(loc, function, arguments); + if (result) + return result; + } + if (function->getBuiltInOp() == EOpArrayLength) result = handleLengthMethod(loc, function, arguments); else if (function->getBuiltInOp() != EOpNull) { @@ -1727,6 +1837,7 @@ void TParseContext::memorySemanticsCheck(const TSourceLoc& loc, const TFunction& // Grab the semantics and storage class semantics from the operands, based on opcode switch (callNode.getOp()) { case EOpAtomicAdd: + case EOpAtomicSubtract: case EOpAtomicMin: case EOpAtomicMax: case EOpAtomicAnd: @@ -2176,6 +2287,7 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan } case EOpAtomicAdd: + case EOpAtomicSubtract: case EOpAtomicMin: case EOpAtomicMax: case EOpAtomicAnd: @@ -3388,7 +3500,7 @@ void TParseContext::transparentOpaqueCheck(const TSourceLoc& loc, const TType& t if (type.containsNonOpaque()) { // Vulkan doesn't allow transparent uniforms outside of blocks - if (spvVersion.vulkan > 0) + if (spvVersion.vulkan > 0 && !spvVersion.vulkanRelaxed) vulkanRemoved(loc, "non-opaque uniforms outside a block"); // OpenGL wants locations on these (unless they are getting automapped) if (spvVersion.openGl > 0 && !type.getQualifier().hasLocation() && !intermediate.getAutoMapLocations()) @@ -5019,14 +5131,22 @@ void TParseContext::setLayoutQualifier(const TSourceLoc& loc, TPublicType& publi return; } if (id == TQualifier::getLayoutPackingString(ElpPacked)) { - if (spvVersion.spv != 0) - spvRemoved(loc, "packed"); + if (spvVersion.spv != 0) { + if (spvVersion.vulkanRelaxed) + return; // silently ignore qualifier + else + spvRemoved(loc, "packed"); + } publicType.qualifier.layoutPacking = ElpPacked; return; } if (id == TQualifier::getLayoutPackingString(ElpShared)) { - if (spvVersion.spv != 0) - spvRemoved(loc, "shared"); + if (spvVersion.spv != 0) { + if (spvVersion.vulkanRelaxed) + return; // silently ignore qualifier + else + spvRemoved(loc, "shared"); + } publicType.qualifier.layoutPacking = ElpShared; return; } @@ -5928,7 +6048,7 @@ void TParseContext::layoutTypeCheck(const TSourceLoc& loc, const TType& type) error(loc, "sampler binding not less than gl_MaxCombinedTextureImageUnits", "binding", type.isArray() ? "(using array)" : ""); #endif } - if (type.isAtomic()) { + if (type.isAtomic() && !spvVersion.vulkanRelaxed) { if (qualifier.layoutBinding >= (unsigned int)resources.maxAtomicCounterBindings) { error(loc, "atomic_uint binding is too large; see gl_MaxAtomicCounterBindings", "binding", ""); return; @@ -6598,6 +6718,68 @@ const TFunction* TParseContext::findFunctionExplicitTypes(const TSourceLoc& loc, return bestMatch; } +// +// Adjust function calls that aren't declared in Vulkan to a +// calls with equivalent effects +// +TIntermTyped* TParseContext::vkRelaxedRemapFunctionCall(const TSourceLoc& loc, TFunction* function, TIntermNode* arguments) +{ + TIntermTyped* result = nullptr; + +#ifndef GLSLANG_WEB + if (function->getBuiltInOp() != EOpNull) { + return nullptr; + } + + if (function->getName() == "atomicCounterIncrement") { + // change atomicCounterIncrement into an atomicAdd of 1 + TString name("atomicAdd"); + TType uintType(EbtUint); + + TFunction realFunc(&name, function->getType()); + + for (int i = 0; i < function->getParamCount(); ++i) { + realFunc.addParameter((*function)[i]); + } + + TParameter tmpP = { 0, &uintType }; + realFunc.addParameter(tmpP); + arguments = intermediate.growAggregate(arguments, intermediate.addConstantUnion(1, loc, true)); + + result = handleFunctionCall(loc, &realFunc, arguments); + } else if (function->getName() == "atomicCounterDecrement") { + // change atomicCounterDecrement into an atomicAdd with -1 + // and subtract 1 from result, to return post-decrement value + TString name("atomicAdd"); + TType uintType(EbtUint); + + TFunction realFunc(&name, function->getType()); + + for (int i = 0; i < function->getParamCount(); ++i) { + realFunc.addParameter((*function)[i]); + } + + TParameter tmpP = { 0, &uintType }; + realFunc.addParameter(tmpP); + arguments = intermediate.growAggregate(arguments, intermediate.addConstantUnion(-1, loc, true)); + + result = handleFunctionCall(loc, &realFunc, arguments); + + // post decrement, so that it matches AtomicCounterDecrement semantics + if (result) { + result = handleBinaryMath(loc, "-", EOpSub, result, intermediate.addConstantUnion(1, loc, true)); + } + } else if (function->getName() == "atomicCounter") { + // change atomicCounter into a direct read of the variable + if (arguments->getAsTyped()) { + result = arguments->getAsTyped(); + } + } +#endif + + return result; +} + // 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) @@ -6622,6 +6804,91 @@ void TParseContext::declareTypeDefaults(const TSourceLoc& loc, const TPublicType #endif } +bool TParseContext::vkRelaxedRemapUniformVariable(const TSourceLoc& loc, TString& identifier, const TPublicType&, + TArraySizes*, TIntermTyped* initializer, TType& type) +{ + if (parsingBuiltins || symbolTable.atBuiltInLevel() || !symbolTable.atGlobalLevel() || + type.getQualifier().storage != EvqUniform || + !(type.containsNonOpaque() +#ifndef GLSLANG_WEB + || type.getBasicType() == EbtAtomicUint +#endif + )) { + return false; + } + + if (type.getQualifier().hasLocation()) { + warn(loc, "ignoring layout qualifier for uniform", identifier.c_str(), "location"); + type.getQualifier().layoutLocation = TQualifier::layoutLocationEnd; + } + + if (initializer) { + warn(loc, "Ignoring initializer for uniform", identifier.c_str(), ""); + initializer = nullptr; + } + + if (type.isArray()) { + // do array size checks here + arraySizesCheck(loc, type.getQualifier(), type.getArraySizes(), initializer, false); + + if (arrayQualifierError(loc, type.getQualifier()) || arrayError(loc, type)) { + error(loc, "array param error", identifier.c_str(), ""); + } + } + + // do some checking on the type as it was declared + layoutTypeCheck(loc, type); + + int bufferBinding = TQualifier::layoutBindingEnd; + TVariable* updatedBlock = nullptr; + +#ifndef GLSLANG_WEB + // Convert atomic_uint into members of a buffer block + if (type.isAtomic()) { + type.setBasicType(EbtUint); + type.getQualifier().storage = EvqBuffer; + + type.getQualifier().volatil = true; + type.getQualifier().coherent = true; + + // xxTODO: use logic from fixOffset() to apply explicit member offset + bufferBinding = type.getQualifier().layoutBinding; + type.getQualifier().layoutBinding = TQualifier::layoutBindingEnd; + type.getQualifier().explicitOffset = false; + growAtomicCounterBlock(bufferBinding, loc, type, identifier, nullptr); + updatedBlock = atomicCounterBuffers[bufferBinding]; + } +#endif + + if (!updatedBlock) { + growGlobalUniformBlock(loc, type, identifier, nullptr); + updatedBlock = globalUniformBlock; + } + + // + // don't assign explicit member offsets here + // if any are assigned, need to be updated here and in the merge/link step + // fixBlockUniformOffsets(updatedBlock->getWritableType().getQualifier(), *updatedBlock->getWritableType().getWritableStruct()); + + // checks on update buffer object + layoutObjectCheck(loc, *updatedBlock); + + TSymbol* symbol = symbolTable.find(identifier); + + if (!symbol) { + if (updatedBlock == globalUniformBlock) + error(loc, "error adding uniform to default uniform block", identifier.c_str(), ""); + else + error(loc, "error adding atomic counter to atomic counter block", identifier.c_str(), ""); + return false; + } + + // merge qualifiers + mergeObjectLayoutQualifiers(updatedBlock->getWritableType().getQualifier(), type.getQualifier(), true); + + return true; +} + // // Do everything necessary to handle a variable (non-block) declaration. // Either redeclaring a variable, or making a new one, updating the symbol @@ -6733,6 +7000,14 @@ TIntermNode* TParseContext::declareVariable(const TSourceLoc& loc, TString& iden if (symbol == nullptr) reservedErrorCheck(loc, identifier); + if (symbol == nullptr && spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) { + bool remapped = vkRelaxedRemapUniformVariable(loc, identifier, publicType, arraySizes, initializer, type); + + if (remapped) { + return nullptr; + } + } + inheritGlobalDefaults(type.getQualifier()); // Declare the variable @@ -7625,6 +7900,8 @@ void TParseContext::inheritMemoryQualifiers(const TQualifier& from, TQualifier& void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, const TString* instanceName, TArraySizes* arraySizes) { + if (spvVersion.vulkan > 0 && spvVersion.vulkanRelaxed) + blockStorageRemap(loc, blockName, currentBlockQualifier); blockStageIoCheck(loc, currentBlockQualifier); blockQualifierCheck(loc, currentBlockQualifier, instanceName != nullptr); if (arraySizes != nullptr) { @@ -7914,6 +8191,17 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con trackLinkage(variable); } +// +// allow storage type of block to be remapped at compile time +// +void TParseContext::blockStorageRemap(const TSourceLoc&, const TString* instanceName, TQualifier& qualifier) +{ + TBlockStorageClass type = intermediate.getBlockStorageOverride(instanceName->c_str()); + if (type != EbsNone) { + qualifier.setBlockStorage(type); + } +} + // Do all block-declaration checking regarding the combination of in/out/uniform/buffer // with a particular stage. void TParseContext::blockStageIoCheck(const TSourceLoc& loc, const TQualifier& qualifier) diff --git a/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.h b/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.h index ad9b8d6ac..6f00621af 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.h +++ b/3rdparty/glslang/glslang/MachineIndependent/ParseHelper.h @@ -92,7 +92,8 @@ public: limits(resources.limits), globalUniformBlock(nullptr), globalUniformBinding(TQualifier::layoutBindingEnd), - globalUniformSet(TQualifier::layoutSetEnd) + globalUniformSet(TQualifier::layoutSetEnd), + atomicCounterBlockSet(TQualifier::layoutSetEnd) { if (entryPoint != nullptr) sourceEntryPointName = *entryPoint; @@ -154,10 +155,11 @@ public: extensionCallback(line, extension, behavior); } -#ifdef ENABLE_HLSL // Manage the global uniform block (default uniforms in GLSL, $Global in HLSL) virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr); -#endif + + // Manage global buffer (used for backing atomic counters in GLSL when using relaxed Vulkan semantics) + virtual void growAtomicCounterBlock(int binding, const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr); // Potentially rename shader entry point function void renameShaderFunction(TString*& name) const @@ -230,7 +232,24 @@ protected: // override this to set the language-specific name virtual const char* getGlobalUniformBlockName() const { return ""; } virtual void setUniformBlockDefaults(TType&) const { } - virtual void finalizeGlobalUniformBlockLayout(TVariable&) { } + virtual void finalizeGlobalUniformBlockLayout(TVariable&) {} + + // Manage the atomic counter block (used for atomic_uints with Vulkan-Relaxed) + TMap atomicCounterBuffers; + unsigned int atomicCounterBlockSet; + TMap atomicCounterBlockFirstNewMember; + // override this to set the language-specific name + virtual const char* getAtomicCounterBlockName() const { return ""; } + virtual void setAtomicCounterBlockDefaults(TType&) const {} + virtual void finalizeAtomicCounterBlockLayout(TVariable&) {} + bool isAtomicCounterBlock(const TSymbol& symbol) { + const TVariable* var = symbol.getAsVariable(); + if (!var) + return false; + const auto& at = atomicCounterBuffers.find(var->getType().getQualifier().layoutBinding); + return (at != atomicCounterBuffers.end() && (*at).second->getType() == var->getType()); + } + virtual void outputMessage(const TSourceLoc&, const char* szReason, const char* szToken, const char* szExtraInfoFormat, TPrefixType prefix, va_list args); @@ -293,6 +312,9 @@ public: bool parseShaderStrings(TPpContext&, TInputScanner& input, bool versionWillBeError = false) override; void parserError(const char* s); // for bison's yyerror + virtual void growGlobalUniformBlock(const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr) override; + virtual void growAtomicCounterBlock(int binding, const TSourceLoc&, TType&, const TString& memberName, TTypeList* typeList = nullptr) override; + void reservedErrorCheck(const TSourceLoc&, const TString&); void reservedPpErrorCheck(const TSourceLoc&, const char* name, const char* op) override; bool lineContinuationCheck(const TSourceLoc&, bool endOfComment) override; @@ -340,6 +362,10 @@ public: void checkPrecisionQualifier(const TSourceLoc&, TPrecisionQualifier); void memorySemanticsCheck(const TSourceLoc&, const TFunction&, const TIntermOperator& callNode); + TIntermTyped* vkRelaxedRemapFunctionCall(const TSourceLoc&, TFunction*, TIntermNode*); + // returns true if the variable was remapped to something else + bool vkRelaxedRemapUniformVariable(const TSourceLoc&, TString&, const TPublicType&, TArraySizes*, TIntermTyped*, TType&); + void assignError(const TSourceLoc&, const char* op, TString left, TString right); void unaryOpError(const TSourceLoc&, const char* op, TString operand); void binaryOpError(const TSourceLoc&, const char* op, TString left, TString right); @@ -417,6 +443,7 @@ public: TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermTyped*, const TSourceLoc&, bool subset); void inheritMemoryQualifiers(const TQualifier& from, TQualifier& to); void declareBlock(const TSourceLoc&, TTypeList& typeList, const TString* instanceName = 0, TArraySizes* arraySizes = 0); + void blockStorageRemap(const TSourceLoc&, const TString*, TQualifier&); void blockStageIoCheck(const TSourceLoc&, const TQualifier&); void blockQualifierCheck(const TSourceLoc&, const TQualifier&, bool instanceName); void fixBlockLocations(const TSourceLoc&, TQualifier&, TTypeList&, bool memberWithLocation, bool memberWithoutLocation); @@ -461,6 +488,14 @@ protected: void finish() override; #endif + virtual const char* getGlobalUniformBlockName() const override; + virtual void finalizeGlobalUniformBlockLayout(TVariable&) override; + virtual void setUniformBlockDefaults(TType& block) const override; + + virtual const char* getAtomicCounterBlockName() const override; + virtual void finalizeAtomicCounterBlockLayout(TVariable&) override; + virtual void setAtomicCounterBlockDefaults(TType& block) const override; + public: // // Generally, bison productions, the scanner, and the PP need read/write access to these; just give them direct access diff --git a/3rdparty/glslang/glslang/MachineIndependent/ShaderLang.cpp b/3rdparty/glslang/glslang/MachineIndependent/ShaderLang.cpp index 3f793a743..d02eae6fc 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/ShaderLang.cpp +++ b/3rdparty/glslang/glslang/MachineIndependent/ShaderLang.cpp @@ -159,7 +159,7 @@ int MapVersionToIndex(int version) return index; } -const int SpvVersionCount = 3; // index range in MapSpvVersionToIndex +const int SpvVersionCount = 4; // index range in MapSpvVersionToIndex int MapSpvVersionToIndex(const SpvVersion& spvVersion) { @@ -167,8 +167,12 @@ int MapSpvVersionToIndex(const SpvVersion& spvVersion) if (spvVersion.openGl > 0) index = 1; - else if (spvVersion.vulkan > 0) - index = 2; + else if (spvVersion.vulkan > 0) { + if (!spvVersion.vulkanRelaxed) + index = 2; + else + index = 3; + } assert(index < SpvVersionCount); @@ -723,6 +727,7 @@ void TranslateEnvironment(const TEnvironment* environment, EShMessages& messages break; case EShClientVulkan: spvVersion.vulkanGlsl = environment->input.dialectVersion; + spvVersion.vulkanRelaxed = environment->input.vulkanRulesRelaxed; break; case EShClientOpenGL: spvVersion.openGl = environment->input.dialectVersion; @@ -1762,6 +1767,7 @@ TShader::TShader(EShLanguage s) // clear environment (avoid constructors in them for use in a C interface) environment.input.languageFamily = EShSourceNone; environment.input.dialect = EShClientNone; + environment.input.vulkanRulesRelaxed = false; environment.client.client = EShClientNone; environment.target.language = EShTargetNone; environment.target.hlslFunctionality1 = false; @@ -1868,6 +1874,15 @@ void TShader::setResourceSetBinding(const std::vector& base) { in void TShader::setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode) { intermediate->setTextureSamplerTransformMode(mode); } #endif +void TShader::addBlockStorageOverride(const char* nameStr, TBlockStorageClass backing) { intermediate->addBlockStorageOverride(nameStr, backing); } + +void TShader::setGlobalUniformBlockName(const char* name) { intermediate->setGlobalUniformBlockName(name); } +void TShader::setGlobalUniformSet(unsigned int set) { intermediate->setGlobalUniformSet(set); } +void TShader::setGlobalUniformBinding(unsigned int binding) { intermediate->setGlobalUniformBinding(binding); } + +void TShader::setAtomicCounterBlockName(const char* name) { intermediate->setAtomicCounterBlockName(name); } +void TShader::setAtomicCounterBlockSet(unsigned int set) { intermediate->setAtomicCounterBlockSet(set); } + #ifdef ENABLE_HLSL // See comment above TDefaultHlslIoMapper in iomapper.cpp: void TShader::setHlslIoMapping(bool hlslIoMap) { intermediate->setHlslIoMapping(hlslIoMap); } @@ -1983,7 +1998,10 @@ bool TProgram::link(EShMessages messages) error = true; } - // TODO: Link: cross-stage error checking + if (!error) { + if (! crossStageCheck(messages)) + error = true; + } return ! error; } @@ -2060,6 +2078,69 @@ bool TProgram::linkStage(EShLanguage stage, EShMessages messages) return intermediate[stage]->getNumErrors() == 0; } +// +// Check that there are no errors in linker objects accross stages +// +// Return true if no errors. +// +bool TProgram::crossStageCheck(EShMessages) { + + // make temporary intermediates to hold the linkage symbols for each linking interface + // while we do the checks + // Independent interfaces are: + // all uniform variables and blocks + // all buffer blocks + // all in/out on a stage boundary + + TVector activeStages; + for (int s = 0; s < EShLangCount; ++s) { + if (intermediate[s]) + activeStages.push_back(intermediate[s]); + } + + // no extra linking if there is only one stage + if (! (activeStages.size() > 1)) + return true; + + // setup temporary tree to hold unfirom objects from different stages + TIntermediate* firstIntermediate = activeStages.front(); + TIntermediate uniforms(EShLangCount, + firstIntermediate->getVersion(), + firstIntermediate->getProfile()); + uniforms.setSpv(firstIntermediate->getSpv()); + + TIntermAggregate uniformObjects(EOpLinkerObjects); + TIntermAggregate root(EOpSequence); + root.getSequence().push_back(&uniformObjects); + uniforms.setTreeRoot(&root); + + bool error = false; + + // merge uniforms from all stages into a single intermediate + for (unsigned int i = 0; i < activeStages.size(); ++i) { + uniforms.mergeUniformObjects(*infoSink, *activeStages[i]); + } + error |= uniforms.getNumErrors() != 0; + + // copy final definition of global block back into each stage + for (unsigned int i = 0; i < activeStages.size(); ++i) { + // We only want to merge into already existing global uniform blocks. + // A stage that doesn't already know about the global doesn't care about it's content. + // Otherwise we end up pointing to the same object between different stages + // and that will break binding/set remappings + bool mergeExistingOnly = true; + activeStages[i]->mergeGlobalUniformBlocks(*infoSink, uniforms, mergeExistingOnly); + } + + // compare cross stage symbols for each stage boundary + for (unsigned int i = 1; i < activeStages.size(); ++i) { + activeStages[i - 1]->checkStageIO(*infoSink, *activeStages[i]); + error |= (activeStages[i - 1]->getNumErrors() != 0); + } + + return !error; +} + const char* TProgram::getInfoLog() { return infoSink->info.c_str(); diff --git a/3rdparty/glslang/glslang/MachineIndependent/Versions.cpp b/3rdparty/glslang/glslang/MachineIndependent/Versions.cpp index 488c98c5c..7554bfd76 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/Versions.cpp +++ b/3rdparty/glslang/glslang/MachineIndependent/Versions.cpp @@ -1273,7 +1273,7 @@ void TParseVersions::spvRemoved(const TSourceLoc& loc, const char* op) // Call for any operation removed because Vulkan SPIR-V is being generated. void TParseVersions::vulkanRemoved(const TSourceLoc& loc, const char* op) { - if (spvVersion.vulkan > 0) + if (spvVersion.vulkan > 0 && !spvVersion.vulkanRelaxed) error(loc, "not allowed when using GLSL for Vulkan", op, ""); } diff --git a/3rdparty/glslang/glslang/MachineIndependent/Versions.h b/3rdparty/glslang/glslang/MachineIndependent/Versions.h index f377973a8..25feb0b7b 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/Versions.h +++ b/3rdparty/glslang/glslang/MachineIndependent/Versions.h @@ -87,11 +87,12 @@ inline const char* ProfileName(EProfile profile) // The union of all requested rule sets will be applied. // struct SpvVersion { - SpvVersion() : spv(0), vulkanGlsl(0), vulkan(0), openGl(0) {} + SpvVersion() : spv(0), vulkanGlsl(0), vulkan(0), openGl(0), vulkanRelaxed(false) {} unsigned int spv; // the version of SPIR-V to target, as defined by "word 1" of the SPIR-V binary header int vulkanGlsl; // the version of GLSL semantics for Vulkan, from GL_KHR_vulkan_glsl, for "#define VULKAN XXX" int vulkan; // the version of Vulkan, for which SPIR-V execution environment rules to use int openGl; // the version of GLSL semantics for OpenGL, from GL_ARB_gl_spirv, for "#define GL_SPIRV XXX" + bool vulkanRelaxed; // relax changes to GLSL for Vulkan, allowing some GL-specific to be compiled to Vulkan SPIR-V target }; // diff --git a/3rdparty/glslang/glslang/MachineIndependent/gl_types.h b/3rdparty/glslang/glslang/MachineIndependent/gl_types.h index b9372d4bb..d6c939374 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/gl_types.h +++ b/3rdparty/glslang/glslang/MachineIndependent/gl_types.h @@ -49,9 +49,9 @@ #define GL_INT64_VEC4_ARB 0x8FEB #define GL_UNSIGNED_INT64_ARB 0x140F -#define GL_UNSIGNED_INT64_VEC2_ARB 0x8FE5 -#define GL_UNSIGNED_INT64_VEC3_ARB 0x8FE6 -#define GL_UNSIGNED_INT64_VEC4_ARB 0x8FE7 +#define GL_UNSIGNED_INT64_VEC2_ARB 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_ARB 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_ARB 0x8FF7 #define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 #define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 #define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 diff --git a/3rdparty/glslang/glslang/MachineIndependent/intermOut.cpp b/3rdparty/glslang/glslang/MachineIndependent/intermOut.cpp index 5ce3e4728..c718f949a 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/intermOut.cpp +++ b/3rdparty/glslang/glslang/MachineIndependent/intermOut.cpp @@ -886,6 +886,7 @@ bool TOutputTraverser::visitAggregate(TVisit /* visit */, TIntermAggregate* node case EOpTime: out.debug << "time"; break; case EOpAtomicAdd: out.debug << "AtomicAdd"; break; + case EOpAtomicSubtract: out.debug << "AtomicSubtract"; break; case EOpAtomicMin: out.debug << "AtomicMin"; break; case EOpAtomicMax: out.debug << "AtomicMax"; break; case EOpAtomicAnd: out.debug << "AtomicAnd"; break; diff --git a/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp b/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp index c42e74fa5..7e12864f3 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp +++ b/3rdparty/glslang/glslang/MachineIndependent/iomapper.cpp @@ -210,8 +210,8 @@ struct TResolverUniformAdaptor { ent.newIndex = -1; const bool isValid = resolver.validateBinding(stage, ent); if (isValid) { - resolver.resolveBinding(ent.stage, ent); resolver.resolveSet(ent.stage, ent); + resolver.resolveBinding(ent.stage, ent); resolver.resolveUniformLocation(ent.stage, ent); if (ent.newBinding != -1) { @@ -317,15 +317,13 @@ private: }; // The class is used for reserving explicit uniform locations and ubo/ssbo/opaque bindings +// xxTODO: maybe this logic should be moved into the resolver's "validateInOut" and "validateUniform" struct TSymbolValidater { TSymbolValidater(TIoMapResolver& r, TInfoSink& i, TVarLiveMap* in[EShLangCount], TVarLiveMap* out[EShLangCount], TVarLiveMap* uniform[EShLangCount], bool& hadError, EProfile profile, int version) - : preStage(EShLangCount) - , currentStage(EShLangCount) - , nextStage(EShLangCount) - , resolver(r) + : resolver(r) , infoSink(i) , hadError(hadError) , profile(profile) @@ -438,17 +436,23 @@ struct TSymbolValidater TIntermSymbol* base = ent1.symbol; const TType& type = ent1.symbol->getType(); const TString& name = entKey.first; - EShLanguage stage = ent1.stage; TString mangleName1, mangleName2; - if (currentStage != stage) { - preStage = currentStage; - currentStage = stage; - nextStage = EShLangCount; - for (int i = currentStage + 1; i < EShLangCount; i++) { - if (inVarMaps[i] != nullptr) { - nextStage = static_cast(i); - break; - } + EShLanguage stage = ent1.stage; + EShLanguage preStage, currentStage, nextStage; + + preStage = EShLangCount; + for (int i = stage - 1; i >= 0; i--) { + if (inVarMaps[i] != nullptr) { + preStage = static_cast(i); + break; + } + } + currentStage = stage; + nextStage = EShLangCount; + for (int i = stage + 1; i < EShLangCount; i++) { + if (inVarMaps[i] != nullptr) { + nextStage = static_cast(i); + break; } } @@ -459,6 +463,9 @@ struct TSymbolValidater type.appendMangledName(mangleName1); } + + // basic checking that symbols match + // more extensive checking in the link stage if (base->getQualifier().storage == EvqVaryingIn) { // validate stage in; if (preStage == EShLangCount) @@ -484,8 +491,7 @@ struct TSymbolValidater if (ent2->second.symbol->getType().getQualifier().isArrayedIo(preStage)) { TType subType(ent2->second.symbol->getType(), 0); subType.appendMangledName(mangleName2); - } - else { + } else { ent2->second.symbol->getType().appendMangledName(mangleName2); } @@ -536,8 +542,7 @@ struct TSymbolValidater if (ent2->second.symbol->getType().getQualifier().isArrayedIo(nextStage)) { TType subType(ent2->second.symbol->getType(), 0); subType.appendMangledName(mangleName2); - } - else { + } else { ent2->second.symbol->getType().appendMangledName(mangleName2); } if (mangleName1 == mangleName2) @@ -550,7 +555,7 @@ struct TSymbolValidater } return; } - } else if (base->getQualifier().isUniformOrBuffer() && ! base->getQualifier().isPushConstant()) { + } else if (base->getQualifier().isUniformOrBuffer() && !base->getQualifier().isPushConstant()) { // validate uniform type; for (int i = 0; i < EShLangCount; i++) { if (i != currentStage && outVarMaps[i] != nullptr) { @@ -558,6 +563,7 @@ struct TSymbolValidater if (ent2 != uniformVarMap[i]->end()) { ent2->second.symbol->getType().appendMangledName(mangleName2); if (mangleName1 != mangleName2) { + ent2->second.symbol->getType().sameElementType(type); TString err = "Invalid Uniform variable type : " + entKey.first; infoSink.info.message(EPrefixInternalError, err.c_str()); hadError = true; @@ -608,8 +614,7 @@ struct TSymbolValidater } TVarLiveMap *inVarMaps[EShLangCount], *outVarMaps[EShLangCount], *uniformVarMap[EShLangCount]; - // Use for mark pre stage, to get more interface symbol information. - EShLanguage preStage, currentStage, nextStage; + // Use for mark current shader stage for resolver TIoMapResolver& resolver; TInfoSink& infoSink; @@ -749,14 +754,18 @@ TDefaultIoResolverBase::TDefaultIoResolverBase(const TIntermediate& intermediate , nextOutputLocation(0) { memset(stageMask, false, sizeof(bool) * (EShLangCount + 1)); + memset(stageIntermediates, 0, sizeof(TIntermediate*) * (EShLangCount)); + stageIntermediates[intermediate.getStage()] = &intermediate; } -int TDefaultIoResolverBase::getBaseBinding(TResourceType res, unsigned int set) const { - return selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set)); +int TDefaultIoResolverBase::getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const { + return stageIntermediates[stage] ? selectBaseBinding(stageIntermediates[stage]->getShiftBinding(res), stageIntermediates[stage]->getShiftBindingForSet(res, set)) + : selectBaseBinding(intermediate.getShiftBinding(res), intermediate.getShiftBindingForSet(res, set)); } -const std::vector& TDefaultIoResolverBase::getResourceSetBinding() const { - return intermediate.getResourceSetBinding(); +const std::vector& TDefaultIoResolverBase::getResourceSetBinding(EShLanguage stage) const { + return stageIntermediates[stage] ? stageIntermediates[stage]->getResourceSetBinding() + : intermediate.getResourceSetBinding(); } bool TDefaultIoResolverBase::doAutoBindingMapping() const { return intermediate.getAutoMapBindings(); } @@ -797,14 +806,14 @@ int TDefaultIoResolverBase::getFreeSlot(int set, int base, int size) { return reserveSlot(set, base, size); } -int TDefaultIoResolverBase::resolveSet(EShLanguage /*stage*/, TVarEntryInfo& ent) { +int TDefaultIoResolverBase::resolveSet(EShLanguage stage, TVarEntryInfo& ent) { const TType& type = ent.symbol->getType(); if (type.getQualifier().hasSet()) { return ent.newSet = type.getQualifier().layoutSet; } // If a command line or API option requested a single descriptor set, use that (if not overrided by spaceN) - if (getResourceSetBinding().size() == 1) { - return ent.newSet = atoi(getResourceSetBinding()[0].c_str()); + if (getResourceSetBinding(stage).size() == 1) { + return ent.newSet = atoi(getResourceSetBinding(stage)[0].c_str()); } return ent.newSet = 0; } @@ -925,7 +934,7 @@ int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInf preStage = currentStage; currentStage = stage; } - // kick out of not doing this + // kick out if not doing this if (! doAutoLocationMapping()) { return ent.newLocation = -1; } @@ -1073,7 +1082,7 @@ int TDefaultGlslIoResolver::resolveUniformLocation(EShLanguage /*stage*/, TVarEn return ent.newLocation = location; } -int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) { +int TDefaultGlslIoResolver::resolveBinding(EShLanguage stage, TVarEntryInfo& ent) { const TType& type = ent.symbol->getType(); const TString& name = ent.symbol->getAccessName(); // On OpenGL arrays of opaque types take a separate binding for each element @@ -1086,30 +1095,32 @@ int TDefaultGlslIoResolver::resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& // There is no 'set' qualifier in OpenGL shading language, each resource has its own // binding name space, so remap the 'set' to resource type which make each resource // binding is valid from 0 to MAX_XXRESOURCE_BINDINGS - int set = resource; + int set = intermediate.getSpv().openGl != 0 ? resource : ent.newSet; + int resourceKey = set; if (resource < EResCount) { if (type.getQualifier().hasBinding()) { - ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings); - return ent.newBinding; - } else if (ent.live && doAutoBindingMapping()) { + int newBinding = reserveSlot(resourceKey, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings); + return ent.newBinding = newBinding; + + } else { // The resource in current stage is not declared with binding, but it is possible declared // with explicit binding in other stages, find the resourceSlotMap firstly to check whether // the resource has binding, don't need to allocate if it already has a binding bool hasBinding = false; - if (! resourceSlotMap[resource].empty()) { - TVarSlotMap::iterator iter = resourceSlotMap[resource].find(name); - if (iter != resourceSlotMap[resource].end()) { + ent.newBinding = -1; // leave as -1 if it isn't set below + + if (! resourceSlotMap[resourceKey].empty()) { + TVarSlotMap::iterator iter = resourceSlotMap[resourceKey].find(name); + if (iter != resourceSlotMap[resourceKey].end()) { hasBinding = true; ent.newBinding = iter->second; } } - if (! hasBinding) { - TVarSlotMap varSlotMap; + if (!hasBinding && (ent.live && doAutoBindingMapping())) { // find free slot, the caller did make sure it passes all vars with binding // first and now all are passed that do not have a binding and needs one - int binding = getFreeSlot(resource, getBaseBinding(resource, set), numBindings); - varSlotMap[name] = binding; - resourceSlotMap[resource] = varSlotMap; + int binding = getFreeSlot(resourceKey, getBaseBinding(stage, resource, set), numBindings); + resourceSlotMap[resourceKey][name] = binding; ent.newBinding = binding; } return ent.newBinding; @@ -1211,16 +1222,20 @@ void TDefaultGlslIoResolver::reserverStorageSlot(TVarEntryInfo& ent, TInfoSink& void TDefaultGlslIoResolver::reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) { const TType& type = ent.symbol->getType(); const TString& name = ent.symbol->getAccessName(); - int resource = getResourceType(type); + TResourceType resource = getResourceType(type); + int set = intermediate.getSpv().openGl != 0 ? resource : resolveSet(ent.stage, ent); + int resourceKey = set; + if (type.getQualifier().hasBinding()) { - TVarSlotMap& varSlotMap = resourceSlotMap[resource]; + TVarSlotMap& varSlotMap = resourceSlotMap[resourceKey]; TVarSlotMap::iterator iter = varSlotMap.find(name); - int binding = type.getQualifier().layoutBinding; + int binding = type.getQualifier().layoutBinding + getBaseBinding(ent.stage, resource, set); + if (iter == varSlotMap.end()) { // Reserve the slots for the ubo, ssbo and opaques who has explicit binding - int numBindings = type.isSizedArray() ? type.getCumulativeArraySize() : 1; + int numBindings = intermediate.getSpv().openGl != 0 && type.isSizedArray() ? type.getCumulativeArraySize() : 1; varSlotMap[name] = binding; - reserveSlot(resource, binding, numBindings); + reserveSlot(resourceKey, binding, numBindings); } else { // Allocate binding by name for OpenGL driver, so the resource in different // stages should be declared with the same binding @@ -1269,7 +1284,7 @@ struct TDefaultIoResolver : public TDefaultIoResolverBase { return EResCount; } - int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override { + int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override { const TType& type = ent.symbol->getType(); const int set = getLayoutSet(type); // On OpenGL arrays of opaque types take a seperate binding for each element @@ -1278,11 +1293,11 @@ struct TDefaultIoResolver : public TDefaultIoResolverBase { if (resource < EResCount) { if (type.getQualifier().hasBinding()) { return ent.newBinding = reserveSlot( - set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding, numBindings); + set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding, numBindings); } else if (ent.live && doAutoBindingMapping()) { // find free slot, the caller did make sure it passes all vars with binding // first and now all are passed that do not have a binding and needs one - return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set), numBindings); + return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set), numBindings); } } return ent.newBinding = -1; @@ -1354,17 +1369,17 @@ struct TDefaultHlslIoResolver : public TDefaultIoResolverBase { return EResCount; } - int resolveBinding(EShLanguage /*stage*/, TVarEntryInfo& ent) override { + int resolveBinding(EShLanguage stage, TVarEntryInfo& ent) override { const TType& type = ent.symbol->getType(); const int set = getLayoutSet(type); TResourceType resource = getResourceType(type); if (resource < EResCount) { if (type.getQualifier().hasBinding()) { - return ent.newBinding = reserveSlot(set, getBaseBinding(resource, set) + type.getQualifier().layoutBinding); + return ent.newBinding = reserveSlot(set, getBaseBinding(stage, resource, set) + type.getQualifier().layoutBinding); } else if (ent.live && doAutoBindingMapping()) { // find free slot, the caller did make sure it passes all vars with binding // first and now all are passed that do not have a binding and needs one - return ent.newBinding = getFreeSlot(set, getBaseBinding(resource, set)); + return ent.newBinding = getFreeSlot(set, getBaseBinding(stage, resource, set)); } } return ent.newBinding = -1; @@ -1403,10 +1418,10 @@ bool TIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TInfoSi else resolver = &defaultResolver; } - resolver->addStage(stage); #else resolver = &defaultResolver; #endif + resolver->addStage(stage, intermediate); TVarLiveMap inVarMap, outVarMap, uniformVarMap; TVarLiveVector inVector, outVector, uniformVector; @@ -1502,10 +1517,21 @@ bool TGlslIoMapper::addStage(EShLanguage stage, TIntermediate& intermediate, TIn } // if no resolver is provided, use the default resolver with the given shifts and auto map settings TDefaultGlslIoResolver defaultResolver(intermediate); +#ifdef ENABLE_HLSL + TDefaultHlslIoResolver defaultHlslResolver(intermediate); + if (resolver == nullptr) { + // TODO: use a passed in IO mapper for this + if (intermediate.usingHlslIoMapping()) + resolver = &defaultHlslResolver; + else + resolver = &defaultResolver; + } +#else if (resolver == nullptr) { resolver = &defaultResolver; } - resolver->addStage(stage); +#endif + resolver->addStage(stage, intermediate); inVarMaps[stage] = new TVarLiveMap(); outVarMaps[stage] = new TVarLiveMap(); uniformVarMap[stage] = new TVarLiveMap(); TVarGatherTraverser iter_binding_all(intermediate, true, *inVarMaps[stage], *outVarMaps[stage], *uniformVarMap[stage]); @@ -1547,15 +1573,51 @@ bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) { TResolverInOutAdaptor inOutResolve(EShLangCount, *resolver, infoSink, hadError); TSymbolValidater symbolValidater(*resolver, infoSink, inVarMaps, outVarMaps, uniformVarMap, hadError, profile, version); + + TVarLiveVector inVectors[EShLangCount]; + TVarLiveVector outVectors[EShLangCount]; TVarLiveVector uniformVector; + resolver->beginResolve(EShLangCount); for (int stage = EShLangVertex; stage < EShLangCount; stage++) { if (inVarMaps[stage] != nullptr) { inOutResolve.setStage(EShLanguage(stage)); - for (auto& var : *(inVarMaps[stage])) { symbolValidater(var); } - for (auto& var : *(inVarMaps[stage])) { inOutResolve(var); } - for (auto& var : *(outVarMaps[stage])) { symbolValidater(var); } - for (auto& var : *(outVarMaps[stage])) { inOutResolve(var); } + + // copy vars into a sorted list + std::for_each(inVarMaps[stage]->begin(), inVarMaps[stage]->end(), + [&inVectors, stage](TVarLivePair p) { inVectors[stage].push_back(p); }); + std::sort(inVectors[stage].begin(), inVectors[stage].end(), + [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + + std::for_each(outVarMaps[stage]->begin(), outVarMaps[stage]->end(), + [&outVectors, stage](TVarLivePair p) { outVectors[stage].push_back(p); }); + std::sort(outVectors[stage].begin(), outVectors[stage].end(), + [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { + return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + }); + + for (auto& var : inVectors[stage]) { symbolValidater(var); } + for (auto& var : inVectors[stage]) { inOutResolve(var); } + for (auto& var : outVectors[stage]) { symbolValidater(var); } + for (auto& var : outVectors[stage]) { inOutResolve(var); } + + // copy results back into maps + std::for_each(inVectors[stage].begin(), inVectors[stage].end(), + [this, stage](TVarLivePair p) { + auto at = inVarMaps[stage]->find(p.first); + if (at != inVarMaps[stage]->end()) + at->second = p.second; + }); + + std::for_each(outVectors[stage].begin(), outVectors[stage].end(), + [this, stage](TVarLivePair p) { + auto at = outVarMaps[stage]->find(p.first); + if (at != outVarMaps[stage]->end()) + at->second = p.second; + }); + } if (uniformVarMap[stage] != nullptr) { uniformResolve.setStage(EShLanguage(stage)); @@ -1563,7 +1625,7 @@ bool TGlslIoMapper::doMap(TIoMapResolver* resolver, TInfoSink& infoSink) { } } std::sort(uniformVector.begin(), uniformVector.end(), [](const TVarLivePair& p1, const TVarLivePair& p2) -> bool { - return TVarEntryInfo::TOrderByPriority()(p1.second, p2.second); + return TVarEntryInfo::TOrderByPriorityAndLive()(p1.second, p2.second); }); for (auto& var : uniformVector) { symbolValidater(var); } for (auto& var : uniformVector) { uniformResolve(var); } diff --git a/3rdparty/glslang/glslang/MachineIndependent/iomapper.h b/3rdparty/glslang/glslang/MachineIndependent/iomapper.h index 1dce8ff54..07357c2ef 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/iomapper.h +++ b/3rdparty/glslang/glslang/MachineIndependent/iomapper.h @@ -87,6 +87,35 @@ struct TVarEntryInfo { return lPoints > rPoints; } }; + + struct TOrderByPriorityAndLive { + // ordering: + // 1) do live variables first + // 2) has both binding and set + // 3) has binding but no set + // 4) has no binding but set + // 5) has no binding and no set + inline bool operator()(const TVarEntryInfo& l, const TVarEntryInfo& r) { + + const TQualifier& lq = l.symbol->getQualifier(); + const TQualifier& rq = r.symbol->getQualifier(); + + // simple rules: + // has binding gives 2 points + // has set gives 1 point + // who has the most points is more important. + int lPoints = (lq.hasBinding() ? 2 : 0) + (lq.hasSet() ? 1 : 0); + int rPoints = (rq.hasBinding() ? 2 : 0) + (rq.hasSet() ? 1 : 0); + + if (l.live != r.live) + return l.live > r.live; + + if (lPoints != rPoints) + return lPoints > rPoints; + + return l.id < r.id; + } + }; }; // Base class for shared TIoMapResolver services, used by several derivations. @@ -107,8 +136,8 @@ public: void endCollect(EShLanguage) override {} void reserverResourceSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {} void reserverStorageSlot(TVarEntryInfo& /*ent*/, TInfoSink& /*infoSink*/) override {} - int getBaseBinding(TResourceType res, unsigned int set) const; - const std::vector& getResourceSetBinding() const; + int getBaseBinding(EShLanguage stage, TResourceType res, unsigned int set) const; + const std::vector& getResourceSetBinding(EShLanguage stage) const; virtual TResourceType getResourceType(const glslang::TType& type) = 0; bool doAutoBindingMapping() const; bool doAutoLocationMapping() const; @@ -122,9 +151,11 @@ public: int resolveInOutLocation(EShLanguage stage, TVarEntryInfo& ent) override; int resolveInOutComponent(EShLanguage /*stage*/, TVarEntryInfo& ent) override; int resolveInOutIndex(EShLanguage /*stage*/, TVarEntryInfo& ent) override; - void addStage(EShLanguage stage) override { - if (stage < EShLangCount) + void addStage(EShLanguage stage, TIntermediate& stageIntermediate) override { + if (stage < EShLangCount) { stageMask[stage] = true; + stageIntermediates[stage] = &stageIntermediate; + } } uint32_t computeTypeLocationSize(const TType& type, EShLanguage stage); @@ -139,6 +170,8 @@ protected: int nextInputLocation; int nextOutputLocation; bool stageMask[EShLangCount + 1]; + const TIntermediate* stageIntermediates[EShLangCount]; + // Return descriptor set specific base if there is one, and the generic base otherwise. int selectBaseBinding(int base, int descriptorSetBase) const { return descriptorSetBase != -1 ? descriptorSetBase : base; diff --git a/3rdparty/glslang/glslang/MachineIndependent/linkValidate.cpp b/3rdparty/glslang/glslang/MachineIndependent/linkValidate.cpp index 3f3a3127c..33af7bbe0 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/linkValidate.cpp +++ b/3rdparty/glslang/glslang/MachineIndependent/linkValidate.cpp @@ -90,6 +90,56 @@ void TIntermediate::merge(TInfoSink& infoSink, TIntermediate& unit) #endif } +// +// check that link objects between stages +// +void TIntermediate::mergeUniformObjects(TInfoSink& infoSink, TIntermediate& unit) { + if (unit.treeRoot == nullptr || treeRoot == nullptr) + return; + + // Get the linker-object lists + TIntermSequence& linkerObjects = findLinkerObjects()->getSequence(); + TIntermSequence unitLinkerObjects = unit.findLinkerObjects()->getSequence(); + + // filter unitLinkerObjects to only contain uniforms + auto end = std::remove_if(unitLinkerObjects.begin(), unitLinkerObjects.end(), + [](TIntermNode* node) {return node->getAsSymbolNode()->getQualifier().storage != EvqUniform && + node->getAsSymbolNode()->getQualifier().storage != EvqBuffer; }); + unitLinkerObjects.resize(end - unitLinkerObjects.begin()); + + // merge uniforms and do error checking + bool mergeExistingOnly = false; + mergeGlobalUniformBlocks(infoSink, unit, mergeExistingOnly); + mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects, unit.getStage()); +} + +// +// do error checking on the shader boundary in / out vars +// +void TIntermediate::checkStageIO(TInfoSink& infoSink, TIntermediate& unit) { + if (unit.treeRoot == nullptr || treeRoot == nullptr) + return; + + // Get copies of the linker-object lists + TIntermSequence linkerObjects = findLinkerObjects()->getSequence(); + TIntermSequence unitLinkerObjects = unit.findLinkerObjects()->getSequence(); + + // filter linkerObjects to only contain out variables + auto end = std::remove_if(linkerObjects.begin(), linkerObjects.end(), + [](TIntermNode* node) {return node->getAsSymbolNode()->getQualifier().storage != EvqVaryingOut; }); + linkerObjects.resize(end - linkerObjects.begin()); + + // filter unitLinkerObjects to only contain in variables + auto unitEnd = std::remove_if(unitLinkerObjects.begin(), unitLinkerObjects.end(), + [](TIntermNode* node) {return node->getAsSymbolNode()->getQualifier().storage != EvqVaryingIn; }); + unitLinkerObjects.resize(unitEnd - unitLinkerObjects.begin()); + + // do matching and error checking + mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects, unit.getStage()); + + // TODO: final check; make sure that any statically used `in` have matching `out` written to +} + void TIntermediate::mergeCallGraphs(TInfoSink& infoSink, TIntermediate& unit) { if (unit.getNumEntryPoints() > 0) { @@ -137,6 +187,7 @@ void TIntermediate::mergeModes(TInfoSink& infoSink, TIntermediate& unit) MERGE_MAX(spvVersion.vulkanGlsl); MERGE_MAX(spvVersion.vulkan); MERGE_MAX(spvVersion.openGl); + MERGE_TRUE(spvVersion.vulkanRelaxed); numErrors += unit.getNumErrors(); // Only one push_constant is allowed, mergeLinkerObjects() will ensure the push_constant @@ -312,7 +363,9 @@ void TIntermediate::mergeTrees(TInfoSink& infoSink, TIntermediate& unit) remapIds(idMaps, idShift + 1, unit); mergeBodies(infoSink, globals, unitGlobals); - mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects); + bool mergeExistingOnly = false; + mergeGlobalUniformBlocks(infoSink, unit, mergeExistingOnly); + mergeLinkerObjects(infoSink, linkerObjects, unitLinkerObjects, unit.getStage()); ioAccessed.insert(unit.ioAccessed.begin(), unit.ioAccessed.end()); } @@ -456,11 +509,193 @@ void TIntermediate::mergeBodies(TInfoSink& infoSink, TIntermSequence& globals, c globals.insert(globals.end() - 1, unitGlobals.begin(), unitGlobals.end() - 1); } +static inline bool isSameInterface(TIntermSymbol* symbol, EShLanguage stage, TIntermSymbol* unitSymbol, EShLanguage unitStage) { + return // 1) same stage and same shader interface + (stage == unitStage && symbol->getType().getShaderInterface() == unitSymbol->getType().getShaderInterface()) || + // 2) accross stages and both are uniform or buffer + (symbol->getQualifier().storage == EvqUniform && unitSymbol->getQualifier().storage == EvqUniform) || + (symbol->getQualifier().storage == EvqBuffer && unitSymbol->getQualifier().storage == EvqBuffer) || + // 3) in/out matched across stage boundary + (stage < unitStage && symbol->getQualifier().storage == EvqVaryingOut && unitSymbol->getQualifier().storage == EvqVaryingIn) || + (unitStage < stage && symbol->getQualifier().storage == EvqVaryingIn && unitSymbol->getQualifier().storage == EvqVaryingOut); +} + +// +// Global Unfiform block stores any default uniforms (i.e. uniforms without a block) +// If two linked stages declare the same member, they are meant to be the same uniform +// and need to be in the same block +// merge the members of different stages to allow them to be linked properly +// as a single block +// +void TIntermediate::mergeGlobalUniformBlocks(TInfoSink& infoSink, TIntermediate& unit, bool mergeExistingOnly) +{ + TIntermSequence& linkerObjects = findLinkerObjects()->getSequence(); + TIntermSequence& unitLinkerObjects = unit.findLinkerObjects()->getSequence(); + + // build lists of default blocks from the intermediates + TIntermSequence defaultBlocks; + TIntermSequence unitDefaultBlocks; + + auto filter = [](TIntermSequence& list, TIntermNode* node) { + if (node->getAsSymbolNode()->getQualifier().defaultBlock) { + list.push_back(node); + } + }; + + std::for_each(linkerObjects.begin(), linkerObjects.end(), + [&defaultBlocks, &filter](TIntermNode* node) { + filter(defaultBlocks, node); + }); + std::for_each(unitLinkerObjects.begin(), unitLinkerObjects.end(), + [&unitDefaultBlocks, &filter](TIntermNode* node) { + filter(unitDefaultBlocks, node); + }); + + auto itUnitBlock = unitDefaultBlocks.begin(); + for (; itUnitBlock != unitDefaultBlocks.end(); itUnitBlock++) { + + bool add = !mergeExistingOnly; + auto itBlock = defaultBlocks.begin(); + + for (; itBlock != defaultBlocks.end(); itBlock++) { + TIntermSymbol* block = (*itBlock)->getAsSymbolNode(); + TIntermSymbol* unitBlock = (*itUnitBlock)->getAsSymbolNode(); + + assert(block && unitBlock); + + // if the two default blocks match, then merge their definitions + if (block->getType().getTypeName() == unitBlock->getType().getTypeName() && + block->getQualifier().storage == unitBlock->getQualifier().storage) { + add = false; + mergeBlockDefinitions(infoSink, block, unitBlock, &unit); + } + } + if (add) { + // push back on original list; won't change the size of the list we're iterating over + linkerObjects.push_back(*itUnitBlock); + } + } +} + +void TIntermediate::mergeBlockDefinitions(TInfoSink& infoSink, TIntermSymbol* block, TIntermSymbol* unitBlock, TIntermediate* unit) { + if (block->getType() == unitBlock->getType()) { + return; + } + + if (block->getType().getTypeName() != unitBlock->getType().getTypeName() || + block->getType().getBasicType() != unitBlock->getType().getBasicType() || + block->getQualifier().storage != unitBlock->getQualifier().storage || + block->getQualifier().layoutSet != unitBlock->getQualifier().layoutSet) { + // different block names likely means different blocks + return; + } + + // merge the struct + // order of declarations doesn't matter and they matched based on member name + TTypeList* memberList = block->getType().getWritableStruct(); + TTypeList* unitMemberList = unitBlock->getType().getWritableStruct(); + + // keep track of which members have changed position + // so we don't have to search the array again + std::map memberIndexUpdates; + + size_t memberListStartSize = memberList->size(); + for (unsigned int i = 0; i < unitMemberList->size(); ++i) { + bool merge = true; + for (unsigned int j = 0; j < memberListStartSize; ++j) { + if ((*memberList)[j].type->getFieldName() == (*unitMemberList)[i].type->getFieldName()) { + merge = false; + const TType* memberType = (*memberList)[j].type; + const TType* unitMemberType = (*unitMemberList)[i].type; + + // compare types + // don't need as many checks as when merging symbols, since + // initializers and most qualifiers are stripped when the member is moved into the block + if ((*memberType) != (*unitMemberType)) { + error(infoSink, "Types must match:"); + infoSink.info << " " << memberType->getFieldName() << ": "; + infoSink.info << "\"" << memberType->getCompleteString() << "\" versus "; + infoSink.info << "\"" << unitMemberType->getCompleteString() << "\"\n"; + } + + memberIndexUpdates[i] = j; + } + } + if (merge) { + memberList->push_back((*unitMemberList)[i]); + memberIndexUpdates[i] = (unsigned int)memberList->size() - 1; + } + } + + TType unitType; + unitType.shallowCopy(unitBlock->getType()); + + // update symbol node in unit tree, + // and other nodes that may reference it + class TMergeBlockTraverser : public TIntermTraverser { + public: + TMergeBlockTraverser(const glslang::TType &type, const glslang::TType& unitType, + glslang::TIntermediate& unit, + const std::map& memberIdxUpdates) : + newType(type), unitType(unitType), unit(unit), memberIndexUpdates(memberIdxUpdates) + { } + virtual ~TMergeBlockTraverser() { } + + const glslang::TType& newType; // type with modifications + const glslang::TType& unitType; // copy of original type + glslang::TIntermediate& unit; // intermediate that is being updated + const std::map& memberIndexUpdates; + + virtual void visitSymbol(TIntermSymbol* symbol) + { + glslang::TType& symType = symbol->getWritableType(); + + if (symType == unitType) { + // each symbol node has a local copy of the unitType + // if merging involves changing properties that aren't shared objects + // they should be updated in all instances + + // e.g. the struct list is a ptr to an object, so it can be updated + // once, outside the traverser + //*symType.getWritableStruct() = *newType.getStruct(); + } + + } + + virtual bool visitBinary(TVisit, glslang::TIntermBinary* node) + { + if (node->getOp() == EOpIndexDirectStruct && node->getLeft()->getType() == unitType) { + // this is a dereference to a member of the block since the + // member list changed, need to update this to point to the + // right index + assert(node->getRight()->getAsConstantUnion()); + + glslang::TIntermConstantUnion* constNode = node->getRight()->getAsConstantUnion(); + unsigned int memberIdx = constNode->getConstArray()[0].getUConst(); + unsigned int newIdx = memberIndexUpdates.at(memberIdx); + TIntermTyped* newConstNode = unit.addConstantUnion(newIdx, node->getRight()->getLoc()); + + node->setRight(newConstNode); + delete constNode; + + return true; + } + return true; + } + } finalLinkTraverser(block->getType(), unitType, *unit, memberIndexUpdates); + + // update the tree to use the new type + unit->getTreeRoot()->traverse(&finalLinkTraverser); + + // update the member list + (*unitMemberList) = (*memberList); +} + // // Merge the linker objects from unitLinkerObjects into linkerObjects. // Duplication is expected and filtered out, but contradictions are an error. // -void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects) +void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects, EShLanguage unitStage) { // Error check and merge the linker objects (duplicates should not be created) std::size_t initialNumLinkerObjects = linkerObjects.size(); @@ -475,7 +710,7 @@ void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& lin // If they are both blocks in the same shader interface, // match by the block-name, not the identifier name. if (symbol->getType().getBasicType() == EbtBlock && unitSymbol->getType().getBasicType() == EbtBlock) { - if (symbol->getType().getShaderInterface() == unitSymbol->getType().getShaderInterface()) { + if (isSameInterface(symbol, getStage(), unitSymbol, unitStage)) { isSameSymbol = symbol->getType().getTypeName() == unitSymbol->getType().getTypeName(); } } @@ -495,18 +730,54 @@ void TIntermediate::mergeLinkerObjects(TInfoSink& infoSink, TIntermSequence& lin if (! symbol->getQualifier().hasBinding() && unitSymbol->getQualifier().hasBinding()) symbol->getQualifier().layoutBinding = unitSymbol->getQualifier().layoutBinding; + // Similarly for location + if (!symbol->getQualifier().hasLocation() && unitSymbol->getQualifier().hasLocation()) { + symbol->getQualifier().layoutLocation = unitSymbol->getQualifier().layoutLocation; + } + // Update implicit array sizes mergeImplicitArraySizes(symbol->getWritableType(), unitSymbol->getType()); // Check for consistent types/qualification/initializers etc. - mergeErrorCheck(infoSink, *symbol, *unitSymbol, false); + mergeErrorCheck(infoSink, *symbol, *unitSymbol, unitStage); } // If different symbols, verify they arn't push_constant since there can only be one per stage - else if (symbol->getQualifier().isPushConstant() && unitSymbol->getQualifier().isPushConstant()) + else if (symbol->getQualifier().isPushConstant() && unitSymbol->getQualifier().isPushConstant() && getStage() == unitStage) error(infoSink, "Only one push_constant block is allowed per stage"); } - if (merge) + if (merge) { linkerObjects.push_back(unitLinkerObjects[unitLinkObj]); + + // for anonymous blocks, check that their members don't conflict with other names + if (unitLinkerObjects[unitLinkObj]->getAsSymbolNode()->getBasicType() == EbtBlock && + IsAnonymous(unitLinkerObjects[unitLinkObj]->getAsSymbolNode()->getName())) { + for (std::size_t linkObj = 0; linkObj < initialNumLinkerObjects; ++linkObj) { + TIntermSymbol* symbol = linkerObjects[linkObj]->getAsSymbolNode(); + TIntermSymbol* unitSymbol = unitLinkerObjects[unitLinkObj]->getAsSymbolNode(); + assert(symbol && unitSymbol); + + auto checkName = [this, unitSymbol, &infoSink](const TString& name) { + for (unsigned int i = 0; i < unitSymbol->getType().getStruct()->size(); ++i) { + if (name == (*unitSymbol->getType().getStruct())[i].type->getFieldName()) { + error(infoSink, "Anonymous member name used for global variable or other anonymous member: "); + infoSink.info << (*unitSymbol->getType().getStruct())[i].type->getCompleteString() << "\n"; + } + } + }; + + if (isSameInterface(symbol, getStage(), unitSymbol, unitStage)) { + checkName(symbol->getName()); + + // check members of other anonymous blocks + if (symbol->getBasicType() == EbtBlock && IsAnonymous(symbol->getName())) { + for (unsigned int i = 0; i < symbol->getType().getStruct()->size(); ++i) { + checkName((*symbol->getType().getStruct())[i].type->getFieldName()); + } + } + } + } + } + } } } @@ -538,26 +809,74 @@ void TIntermediate::mergeImplicitArraySizes(TType& type, const TType& unitType) // // This function only does one of intra- or cross-stage matching per call. // -void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& symbol, const TIntermSymbol& unitSymbol, bool crossStage) +void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& symbol, const TIntermSymbol& unitSymbol, EShLanguage unitStage) { #if !defined(GLSLANG_WEB) && !defined(GLSLANG_ANGLE) + bool crossStage = getStage() != unitStage; bool writeTypeComparison = false; // Types have to match - if (symbol.getType() != unitSymbol.getType()) { + { // but, we make an exception if one is an implicit array and the other is sized - if (! (symbol.getType().isArray() && unitSymbol.getType().isArray() && - symbol.getType().sameElementType(unitSymbol.getType()) && - (symbol.getType().isUnsizedArray() || unitSymbol.getType().isUnsizedArray()))) { - error(infoSink, "Types must match:"); + // or if the array sizes differ because of the extra array dimension on some in/out boundaries + bool arraysMatch = false; + if (isIoResizeArray(symbol.getType(), getStage()) || isIoResizeArray(unitSymbol.getType(), unitStage)) { + // if the arrays have an extra dimension because of the stage. + // compare dimensions while ignoring the outer dimension + unsigned int firstDim = isIoResizeArray(symbol.getType(), getStage()) ? 1 : 0; + unsigned int numDim = symbol.getArraySizes() + ? symbol.getArraySizes()->getNumDims() : 0; + unsigned int unitFirstDim = isIoResizeArray(unitSymbol.getType(), unitStage) ? 1 : 0; + unsigned int unitNumDim = unitSymbol.getArraySizes() + ? unitSymbol.getArraySizes()->getNumDims() : 0; + arraysMatch = (numDim - firstDim) == (unitNumDim - unitFirstDim); + // check that array sizes match as well + for (unsigned int i = 0; i < (numDim - firstDim) && arraysMatch; i++) { + if (symbol.getArraySizes()->getDimSize(firstDim + i) != + unitSymbol.getArraySizes()->getDimSize(unitFirstDim + i)) { + arraysMatch = false; + break; + } + } + } + else { + arraysMatch = symbol.getType().sameArrayness(unitSymbol.getType()) || + (symbol.getType().isArray() && unitSymbol.getType().isArray() && + (symbol.getType().isUnsizedArray() || unitSymbol.getType().isUnsizedArray())); + } + + if (!symbol.getType().sameElementType(unitSymbol.getType()) || + !symbol.getType().sameTypeParameters(unitSymbol.getType()) || + !arraysMatch ) { writeTypeComparison = true; + error(infoSink, "Types must match:"); + } + } + + // Interface block member-wise layout qualifiers have to match + if (symbol.getType().getBasicType() == EbtBlock && unitSymbol.getType().getBasicType() == EbtBlock && + symbol.getType().getStruct() && unitSymbol.getType().getStruct() && + symbol.getType().sameStructType(unitSymbol.getType())) { + for (unsigned int i = 0; i < symbol.getType().getStruct()->size(); ++i) { + const TQualifier& qualifier = (*symbol.getType().getStruct())[i].type->getQualifier(); + const TQualifier& unitQualifier = (*unitSymbol.getType().getStruct())[i].type->getQualifier(); + if (qualifier.layoutMatrix != unitQualifier.layoutMatrix || + qualifier.layoutOffset != unitQualifier.layoutOffset || + qualifier.layoutAlign != unitQualifier.layoutAlign || + qualifier.layoutLocation != unitQualifier.layoutLocation || + qualifier.layoutComponent != unitQualifier.layoutComponent) { + error(infoSink, "Interface block member layout qualifiers must match:"); + writeTypeComparison = true; + } } } // Qualifiers have to (almost) match // Storage... - if (symbol.getQualifier().storage != unitSymbol.getQualifier().storage) { + if (symbol.getQualifier().storage != unitSymbol.getQualifier().storage && + !((crossStage && symbol.getQualifier().storage == EvqVaryingIn && unitSymbol.getQualifier().storage == EvqVaryingOut) || + (crossStage && symbol.getQualifier().storage == EvqVaryingOut && unitSymbol.getQualifier().storage == EvqVaryingIn))) { error(infoSink, "Storage qualifiers must match:"); writeTypeComparison = true; } @@ -597,12 +916,16 @@ void TIntermediate::mergeErrorCheck(TInfoSink& infoSink, const TIntermSymbol& sy } // Auxiliary and interpolation... - if (symbol.getQualifier().centroid != unitSymbol.getQualifier().centroid || + // "interpolation qualification (e.g., flat) and auxiliary qualification (e.g. centroid) may differ. + // These mismatches are allowed between any pair of stages ... + // those provided in the fragment shader supersede those provided in previous stages." + if (!crossStage && + (symbol.getQualifier().centroid != unitSymbol.getQualifier().centroid || symbol.getQualifier().smooth != unitSymbol.getQualifier().smooth || symbol.getQualifier().flat != unitSymbol.getQualifier().flat || symbol.getQualifier().isSample()!= unitSymbol.getQualifier().isSample() || symbol.getQualifier().isPatch() != unitSymbol.getQualifier().isPatch() || - symbol.getQualifier().isNonPerspective() != unitSymbol.getQualifier().isNonPerspective()) { + symbol.getQualifier().isNonPerspective() != unitSymbol.getQualifier().isNonPerspective())) { error(infoSink, "Interpolation and auxiliary storage qualifiers must match:"); writeTypeComparison = true; } @@ -1830,4 +2153,17 @@ int TIntermediate::computeBufferReferenceTypeSize(const TType& type) return size; } +#ifndef GLSLANG_WEB +bool TIntermediate::isIoResizeArray(const TType& type, EShLanguage language) { + return type.isArray() && + ((language == EShLangGeometry && type.getQualifier().storage == EvqVaryingIn) || + (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)); +} +#endif // not GLSLANG_WEB + } // end namespace glslang diff --git a/3rdparty/glslang/glslang/MachineIndependent/localintermediate.h b/3rdparty/glslang/glslang/MachineIndependent/localintermediate.h index 9fe684a33..c9a1d8114 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/localintermediate.h +++ b/3rdparty/glslang/glslang/MachineIndependent/localintermediate.h @@ -293,7 +293,12 @@ public: useStorageBuffer(false), nanMinMaxClamp(false), depthReplacing(false), - uniqueId(0) + uniqueId(0), + globalUniformBlockName(""), + atomicCounterBlockName(""), + globalUniformBlockSet(TQualifier::layoutSetEnd), + globalUniformBlockBinding(TQualifier::layoutBindingEnd), + atomicCounterBlockSet(TQualifier::layoutSetEnd) #ifndef GLSLANG_WEB , implicitThisName("@this"), implicitCounterName("@count"), @@ -537,6 +542,19 @@ public: void addSymbolLinkageNode(TIntermAggregate*& linkage, const TSymbol&); TIntermAggregate* findLinkerObjects() const; + void setGlobalUniformBlockName(const char* name) { globalUniformBlockName = std::string(name); } + const char* getGlobalUniformBlockName() const { return globalUniformBlockName.c_str(); } + void setGlobalUniformSet(unsigned int set) { globalUniformBlockSet = set; } + unsigned int getGlobalUniformSet() const { return globalUniformBlockSet; } + void setGlobalUniformBinding(unsigned int binding) { globalUniformBlockBinding = binding; } + unsigned int getGlobalUniformBinding() const { return globalUniformBlockBinding; } + + void setAtomicCounterBlockName(const char* name) { atomicCounterBlockName = std::string(name); } + const char* getAtomicCounterBlockName() const { return atomicCounterBlockName.c_str(); } + void setAtomicCounterBlockSet(unsigned int set) { atomicCounterBlockSet = set; } + unsigned int getAtomicCounterBlockSet() const { return atomicCounterBlockSet; } + + void setUseStorageBuffer() { useStorageBuffer = true; } bool usingStorageBuffer() const { return useStorageBuffer; } void setDepthReplacing() { depthReplacing = true; } @@ -848,6 +866,20 @@ public: bool getBinaryDoubleOutput() { return binaryDoubleOutput; } #endif // GLSLANG_WEB + void addBlockStorageOverride(const char* nameStr, TBlockStorageClass backing) + { + std::string name(nameStr); + blockBackingOverrides[name] = backing; + } + TBlockStorageClass getBlockStorageOverride(const char* nameStr) const + { + std::string name = nameStr; + auto pos = blockBackingOverrides.find(name); + if (pos == blockBackingOverrides.end()) + return EbsNone; + else + return pos->second; + } #ifdef ENABLE_HLSL void setHlslFunctionality1() { hlslFunctionality1 = true; } bool getHlslFunctionality1() const { return hlslFunctionality1; } @@ -883,6 +915,10 @@ public: void merge(TInfoSink&, TIntermediate&); void finalCheck(TInfoSink&, bool keepUncalled); + void mergeGlobalUniformBlocks(TInfoSink& infoSink, TIntermediate& unit, bool mergeExistingOnly); + void mergeUniformObjects(TInfoSink& infoSink, TIntermediate& unit); + void checkStageIO(TInfoSink&, TIntermediate&); + bool buildConvertOp(TBasicType dst, TBasicType src, TOperator& convertOp) const; TIntermTyped* createConversion(TBasicType convertTo, TIntermTyped* node) const; @@ -906,6 +942,8 @@ public: static int getOffset(const TType& type, int index); static int getBlockSize(const TType& blockType); static int computeBufferReferenceTypeSize(const TType&); + static bool isIoResizeArray(const TType& type, EShLanguage language); + bool promote(TIntermOperator*); void setNanMinMaxClamp(bool setting) { nanMinMaxClamp = setting; } bool getNanMinMaxClamp() const { return nanMinMaxClamp; } @@ -963,9 +1001,10 @@ protected: void seedIdMap(TIdMaps& idMaps, long long& IdShift); void remapIds(const TIdMaps& idMaps, long long idShift, TIntermediate&); void mergeBodies(TInfoSink&, TIntermSequence& globals, const TIntermSequence& unitGlobals); - void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects); + void mergeLinkerObjects(TInfoSink&, TIntermSequence& linkerObjects, const TIntermSequence& unitLinkerObjects, EShLanguage); + void mergeBlockDefinitions(TInfoSink&, TIntermSymbol* block, TIntermSymbol* unitBlock, TIntermediate* unitRoot); void mergeImplicitArraySizes(TType&, const TType&); - void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, bool crossStage); + void mergeErrorCheck(TInfoSink&, const TIntermSymbol&, const TIntermSymbol&, EShLanguage); void checkCallGraphCycles(TInfoSink&); void checkCallGraphBodies(TInfoSink&, bool keepUncalled); void inOutLocationCheck(TInfoSink&); @@ -1015,6 +1054,13 @@ protected: bool localSizeNotDefault[3]; int localSizeSpecId[3]; unsigned long long uniqueId; + + std::string globalUniformBlockName; + std::string atomicCounterBlockName; + unsigned int globalUniformBlockSet; + unsigned int globalUniformBlockBinding; + unsigned int atomicCounterBlockSet; + #ifndef GLSLANG_WEB public: const char* const implicitThisName; @@ -1075,6 +1121,7 @@ protected: int uniformLocationBase; TNumericFeatures numericFeatures; #endif + std::unordered_map blockBackingOverrides; std::unordered_set usedConstantId; // specialization constant ids used std::vector usedAtomics; // sets of bindings used by atomic counters diff --git a/3rdparty/glslang/glslang/MachineIndependent/reflection.cpp b/3rdparty/glslang/glslang/MachineIndependent/reflection.cpp index 9870a400b..9ea48c452 100644 --- a/3rdparty/glslang/glslang/MachineIndependent/reflection.cpp +++ b/3rdparty/glslang/glslang/MachineIndependent/reflection.cpp @@ -907,8 +907,8 @@ public: case EbtFloat16: return GL_FLOAT16_VEC2_NV + offset; case EbtInt: return GL_INT_VEC2 + offset; case EbtUint: return GL_UNSIGNED_INT_VEC2 + offset; - case EbtInt64: return GL_INT64_ARB + offset; - case EbtUint64: return GL_UNSIGNED_INT64_ARB + offset; + case EbtInt64: return GL_INT64_VEC2_ARB + offset; + case EbtUint64: return GL_UNSIGNED_INT64_VEC2_ARB + offset; case EbtBool: return GL_BOOL_VEC2 + offset; case EbtAtomicUint: return GL_UNSIGNED_INT_ATOMIC_COUNTER + offset; default: return 0; diff --git a/3rdparty/glslang/glslang/Public/ShaderLang.h b/3rdparty/glslang/glslang/Public/ShaderLang.h index e147b0dc8..14683daf1 100644 --- a/3rdparty/glslang/glslang/Public/ShaderLang.h +++ b/3rdparty/glslang/glslang/Public/ShaderLang.h @@ -187,6 +187,7 @@ struct TInputLanguage { EShLanguage stage; // redundant information with other input, this one overrides when not EShSourceNone EShClient dialect; int dialectVersion; // version of client's language definition, not the client (when not EShClientNone) + bool vulkanRulesRelaxed; }; struct TClient { @@ -427,6 +428,14 @@ enum TResourceType { EResCount }; +enum TBlockStorageClass +{ + EbsUniform = 0, + EbsStorageBuffer, + EbsPushConstant, + EbsNone, // not a uniform or buffer variable + EbsCount, +}; // Make one TShader per shader that you will link into a program. Then // - provide the shader through setStrings() or setStringsWithLengths() @@ -483,6 +492,14 @@ public: GLSLANG_EXPORT void setNoStorageFormat(bool useUnknownFormat); GLSLANG_EXPORT void setNanMinMaxClamp(bool nanMinMaxClamp); GLSLANG_EXPORT void setTextureSamplerTransformMode(EShTextureSamplerTransformMode mode); + GLSLANG_EXPORT void addBlockStorageOverride(const char* nameStr, glslang::TBlockStorageClass backing); + + GLSLANG_EXPORT void setGlobalUniformBlockName(const char* name); + GLSLANG_EXPORT void setAtomicCounterBlockName(const char* name); + GLSLANG_EXPORT void setGlobalUniformSet(unsigned int set); + GLSLANG_EXPORT void setGlobalUniformBinding(unsigned int binding); + GLSLANG_EXPORT void setAtomicCounterBlockSet(unsigned int set); + GLSLANG_EXPORT void setAtomicCounterBlockBinding(unsigned int binding); // For setting up the environment (cleared to nothingness in the constructor). // These must be called so that parsing is done for the right source language and @@ -539,6 +556,9 @@ public: bool getEnvTargetHlslFunctionality1() const { return false; } #endif + void setEnvInputVulkanRulesRelaxed() { environment.input.vulkanRulesRelaxed = true; } + bool getEnvInputVulkanRulesRelaxed() const { return environment.input.vulkanRulesRelaxed; } + // Interface to #include handlers. // // To support #include, a client of Glslang does the following: @@ -806,7 +826,7 @@ public: // Called by TSlotCollector to resolve resource locations or bindings virtual void reserverResourceSlot(TVarEntryInfo& ent, TInfoSink& infoSink) = 0; // Called by mapIO.addStage to set shader stage mask to mark a stage be added to this pipeline - virtual void addStage(EShLanguage stage) = 0; + virtual void addStage(EShLanguage stage, TIntermediate& stageIntermediate) = 0; }; #endif // !GLSLANG_WEB && !GLSLANG_ANGLE @@ -928,6 +948,7 @@ public: protected: GLSLANG_EXPORT bool linkStage(EShLanguage, EShMessages); + GLSLANG_EXPORT bool crossStageCheck(EShMessages); TPoolAllocator* pool; std::list stages[EShLangCount];