diff --git a/src/libraries/glslang/glslang/MachineIndependent/reflection.cpp b/src/libraries/glslang/glslang/MachineIndependent/reflection.cpp index 9ea48c452..c76d9c6e2 100644 --- a/src/libraries/glslang/glslang/MachineIndependent/reflection.cpp +++ b/src/libraries/glslang/glslang/MachineIndependent/reflection.cpp @@ -113,7 +113,7 @@ public: // Use a degenerate (empty) set of dereferences to immediately put as at the end of // the dereference change expected by blowUpActiveAggregate. blowUpActiveAggregate(base.getType(), baseName, derefs, derefs.end(), offset, blockIndex, 0, -1, 0, - base.getQualifier().storage, updateStageMasks); + base.getQualifier().storage, updateStageMasks, &base.getConstArray()); } } @@ -250,7 +250,8 @@ public: // A value of 0 for arraySize will mean to use the full array's size. void blowUpActiveAggregate(const TType& baseType, const TString& baseName, const TList& derefs, TList::const_iterator deref, int offset, int blockIndex, int arraySize, - int topLevelArraySize, int topLevelArrayStride, TStorageQualifier baseStorage, bool active) + int topLevelArraySize, int topLevelArrayStride, TStorageQualifier baseStorage, bool active, + const TConstUnionArray* constArray = nullptr) { // when strictArraySuffix is enabled, we closely follow the rules from ARB_program_interface_query. // Broadly: @@ -265,9 +266,18 @@ public: // process the part of the dereference chain that was explicit in the shader TString name = baseName; const TType* terminalType = &baseType; + const TConstUnionArray* terminalConstArray = constArray; for (; deref != derefs.end(); ++deref) { TIntermBinary* visitNode = *deref; terminalType = &visitNode->getType(); + if (visitNode->getAsSymbolNode()) + terminalConstArray = &visitNode->getAsSymbolNode()->getConstArray(); + else if (visitNode->getAsConstantUnion()) + terminalConstArray = &visitNode->getAsConstantUnion()->getConstArray(); + else if (visitNode->getLeft() != nullptr && visitNode->getLeft()->getAsSymbolNode()) + terminalConstArray = &visitNode->getLeft()->getAsSymbolNode()->getConstArray(); + else + terminalConstArray = nullptr; int index; switch (visitNode->getOp()) { case EOpIndexIndirect: { @@ -450,7 +460,7 @@ public: int uniformIndex = (int)variables.size(); reflection.nameToIndex[name.c_str()] = uniformIndex; variables.push_back(TObjectReflection(name.c_str(), *terminalType, offset, mapToGlType(*terminalType), - arraySize, blockIndex)); + arraySize, blockIndex, terminalConstArray)); if (terminalType->isArray()) { variables.back().arrayStride = getArrayStride(baseType, *terminalType); if (topLevelArrayStride == 0) @@ -602,7 +612,7 @@ public: // otherwise - if we're not using strict array suffix rules, or this isn't a block so we are // expanding root arrays anyway, just start the iteration from the base block type. blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.end(), 0, blockIndex, 0, -1, 0, - base->getQualifier().storage, false); + base->getQualifier().storage, false, &base->getConstArray()); } } @@ -634,7 +644,7 @@ public: baseName = base->getName(); } blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.begin(), offset, blockIndex, arraySize, -1, 0, - base->getQualifier().storage, true); + base->getQualifier().storage, true, &base->getConstArray()); } int addBlockName(const TString& name, const TType& type, int size) @@ -1078,9 +1088,9 @@ void TReflectionTraverser::visitSymbol(TIntermSymbol* base) // TObjectReflection::TObjectReflection(const std::string &pName, const TType &pType, int pOffset, int pGLDefineType, - int pSize, int pIndex) + int pSize, int pIndex, const TConstUnionArray* pConstArray) : name(pName), offset(pOffset), glDefineType(pGLDefineType), size(pSize), index(pIndex), counterIndex(-1), - numMembers(-1), arrayStride(0), topLevelArrayStride(0), stages(EShLanguageMask(0)), type(pType.clone()) + numMembers(-1), arrayStride(0), topLevelArrayStride(0), stages(EShLanguageMask(0)), type(pType.clone()), constArray(pConstArray) { } diff --git a/src/libraries/glslang/glslang/Public/ShaderLang.h b/src/libraries/glslang/glslang/Public/ShaderLang.h index 7e5d2cd3e..1fd692c7c 100644 --- a/src/libraries/glslang/glslang/Public/ShaderLang.h +++ b/src/libraries/glslang/glslang/Public/ShaderLang.h @@ -140,6 +140,7 @@ typedef enum : unsigned { namespace glslang { class TType; +class TConstUnionArray; typedef enum { EShSourceNone, @@ -723,9 +724,10 @@ private: // Data needed for just a single object at the granularity exchanged by the reflection API class TObjectReflection { public: - GLSLANG_EXPORT TObjectReflection(const std::string& pName, const TType& pType, int pOffset, int pGLDefineType, int pSize, int pIndex); + GLSLANG_EXPORT TObjectReflection(const std::string& pName, const TType& pType, int pOffset, int pGLDefineType, int pSize, int pIndex, const TConstUnionArray* pConstArray = nullptr); const TType* getType() const { return type; } + const TConstUnionArray* getConstArray() const { return constArray; } GLSLANG_EXPORT int getBinding() const; GLSLANG_EXPORT void dump() const; static TObjectReflection badReflection() { return TObjectReflection(); } @@ -745,11 +747,12 @@ public: protected: TObjectReflection() : offset(-1), glDefineType(-1), size(-1), index(-1), counterIndex(-1), numMembers(-1), arrayStride(0), - topLevelArrayStride(0), stages(EShLanguageMask(0)), type(nullptr) + topLevelArrayStride(0), stages(EShLanguageMask(0)), type(nullptr), constArray(nullptr) { } const TType* type; + const TConstUnionArray* constArray; }; class TReflection; diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index 2a85ca27c..bb26ca57c 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -792,6 +792,24 @@ static PixelFormat getPixelFormat(glslang::TLayoutFormat format) } } +template +static T convertData(const glslang::TConstUnion &data) +{ + switch (data.getType()) + { + case glslang::EbtInt: return (T) data.getIConst(); + case glslang::EbtUint: return (T) data.getUConst(); + case glslang::EbtDouble: return (T) data.getDConst(); + case glslang::EbtInt8: return (T) data.getI8Const(); + case glslang::EbtInt16: return (T) data.getI16Const(); + case glslang::EbtInt64: return (T) data.getI64Const(); + case glslang::EbtUint8: return (T) data.getU8Const(); + case glslang::EbtUint16: return (T) data.getU16Const(); + case glslang::EbtUint64: return (T) data.getU64Const(); + default: return 0; + } +} + bool Shader::validateInternal(StrongRef stages[], std::string &err, ValidationReflection &reflection) { glslang::TProgram program; @@ -871,6 +889,50 @@ bool Shader::validateInternal(StrongRef stages[], std::string &err, reflection.storageTextures[info.name] = texreflection; } + else if (!type->isOpaque()) + { + LocalUniform u = {}; + auto &values = u.initializerValues; + const glslang::TConstUnionArray *constarray = info.getConstArray(); + + // Store initializer values for local uniforms. Some love graphics + // backends strip these out of the shader so we need to be able to + // access them (to re-send them) by getting them here. + switch (type->getBasicType()) + { + case glslang::EbtFloat: + u.dataType = DATA_BASETYPE_FLOAT; + if (constarray != nullptr) + { + values.resize(constarray->size()); + for (int i = 0; i < constarray->size(); i++) + values[i].f = convertData((*constarray)[i]); + } + break; + case glslang::EbtUint: + u.dataType = DATA_BASETYPE_UINT; + if (constarray != nullptr) + { + values.resize(constarray->size()); + for (int i = 0; i < constarray->size(); i++) + values[i].u = convertData((*constarray)[i]); + } + break; + case glslang::EbtInt: + case glslang::EbtBool: + default: + u.dataType = DATA_BASETYPE_INT; + if (constarray != nullptr) + { + values.resize(constarray->size()); + for (int i = 0; i < constarray->size(); i++) + values[i].i = convertData((*constarray)[i]); + } + break; + } + + reflection.localUniforms[info.name] = u; + } } for (int i = 0; i < program.getNumBufferBlocks(); i++) diff --git a/src/modules/graphics/Shader.h b/src/modules/graphics/Shader.h index 21f8d7466..a5a455de4 100644 --- a/src/modules/graphics/Shader.h +++ b/src/modules/graphics/Shader.h @@ -158,6 +158,13 @@ public: }; }; + union LocalUniformValue + { + float f; + int32 i; + uint32 u; + }; + // The members in here must respect uniform buffer alignment/padding rules. struct BuiltinUniformData { @@ -259,10 +266,17 @@ protected: Access access; }; + struct LocalUniform + { + DataBaseType dataType; + std::vector initializerValues; + }; + struct ValidationReflection { std::map storageBuffers; std::map storageTextures; + std::map localUniforms; int localThreadgroupSize[3]; bool usesPointSize; }; diff --git a/src/modules/graphics/metal/Shader.mm b/src/modules/graphics/metal/Shader.mm index 1564b7329..33bdc1dd5 100644 --- a/src/modules/graphics/metal/Shader.mm +++ b/src/modules/graphics/metal/Shader.mm @@ -565,6 +565,16 @@ void Shader::compileFromGLSLang(id device, const glslang::TProgram &p u.matrix.rows = membertype.vecsize; u.matrix.columns = membertype.columns; } + if (validationReflection.localUniforms.find(u.name) != validationReflection.localUniforms.end()) + { + const auto &ru = validationReflection.localUniforms.find(u.name); + const auto &values = ru->second.initializerValues; + if (!values.empty()) + { + memcpy(u.data, values.data(), std::min(u.dataSize, values.size() * sizeof(LocalUniformValue))); + } + } + updateUniform(&u, u.count); break; case SPIRType::Struct: // TODO @@ -582,6 +592,8 @@ void Shader::compileFromGLSLang(id device, const glslang::TProgram &p builtinUniformDataOffset = offset; builtinUniformInfo[builtin] = &uniforms[u.name]; } + + } } else