From 657acc0c4077b57a950d04bcf9dab509e1f50525 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 30 Jan 2019 14:18:43 +0000 Subject: [PATCH] Add option to reflect buffer blocks & variables separately to uniforms * Also note the uniform indices of atomic counter buffers --- StandAlone/StandAlone.cpp | 3 + Test/baseResults/hlsl.automap.frag.out | 4 ++ .../hlsl.reflection.binding.frag.out | 4 ++ Test/baseResults/hlsl.reflection.vert.out | 4 ++ Test/baseResults/hlsl.shift.per-set.frag.out | 4 ++ Test/baseResults/reflection.frag.out | 4 ++ Test/baseResults/reflection.options.frag.out | 4 ++ Test/baseResults/reflection.options.vert.out | 10 +++- Test/baseResults/reflection.vert.out | 4 ++ Test/reflection.options.vert | 7 +++ Test/runtests | 4 +- glslang/MachineIndependent/ShaderLang.cpp | 22 ++++--- glslang/MachineIndependent/reflection.cpp | 59 +++++++++++++------ glslang/MachineIndependent/reflection.h | 47 +++++++++++++++ glslang/Public/ShaderLang.h | 7 +++ 15 files changed, 158 insertions(+), 29 deletions(-) diff --git a/StandAlone/StandAlone.cpp b/StandAlone/StandAlone.cpp index 7588320f..52fd80c6 100644 --- a/StandAlone/StandAlone.cpp +++ b/StandAlone/StandAlone.cpp @@ -530,6 +530,8 @@ void ProcessArguments(std::vector>& workItem ReflectOptions |= EShReflectionBasicArraySuffix; } else if (lowerword == "reflect-intermediate-io") { ReflectOptions |= EShReflectionIntermediateIO; + } else if (lowerword == "reflect-separate-buffers") { + ReflectOptions |= EShReflectionSeparateBuffers; } else if (lowerword == "resource-set-bindings" || // synonyms lowerword == "resource-set-binding" || lowerword == "rsb") { @@ -1529,6 +1531,7 @@ void usage() " --reflect-basic-array-suffix arrays of basic types will have trailing [0]\n" " --reflect-intermediate-io reflection includes inputs/outputs of linked shaders\n" " rather than just vertex/fragment\n" + " --reflect-separate-buffers reflect buffer variables and blocks separately to uniforms\n" " --resource-set-binding [stage] name set binding\n" " set descriptor set and binding for\n" " individual resources\n" diff --git a/Test/baseResults/hlsl.automap.frag.out b/Test/baseResults/hlsl.automap.frag.out index 51a15db4..fb914ece 100644 --- a/Test/baseResults/hlsl.automap.frag.out +++ b/Test/baseResults/hlsl.automap.frag.out @@ -25,6 +25,10 @@ u6: offset -1, type ffffffff, size 0, index -1, binding 46, stages 0, numMembers cb: offset -1, type ffffffff, size 4, index -1, binding 51, stages 0, numMembers 1 tb: offset -1, type ffffffff, size 4, index -1, binding 17, stages 0, numMembers 1 +Buffer variable reflection: + +Buffer block reflection: + Pipeline input reflection: Pipeline output reflection: diff --git a/Test/baseResults/hlsl.reflection.binding.frag.out b/Test/baseResults/hlsl.reflection.binding.frag.out index da452cf3..8d5743ea 100644 --- a/Test/baseResults/hlsl.reflection.binding.frag.out +++ b/Test/baseResults/hlsl.reflection.binding.frag.out @@ -15,6 +15,10 @@ Uniform block reflection: cbuff1: offset -1, type ffffffff, size 24, index -1, binding 2, stages 0, numMembers 3 cbuff2: offset -1, type ffffffff, size 24, index -1, binding 3, stages 0, numMembers 3 +Buffer variable reflection: + +Buffer block reflection: + Pipeline input reflection: Pipeline output reflection: diff --git a/Test/baseResults/hlsl.reflection.vert.out b/Test/baseResults/hlsl.reflection.vert.out index 97c8466a..c9367480 100644 --- a/Test/baseResults/hlsl.reflection.vert.out +++ b/Test/baseResults/hlsl.reflection.vert.out @@ -67,6 +67,10 @@ nested: offset -1, type ffffffff, size 32, index -1, binding -1, stages 0, numMe abl: offset -1, type ffffffff, size 4, index -1, binding -1, stages 0, numMembers 1 abl2: offset -1, type ffffffff, size 4, index -1, binding -1, stages 0, numMembers 1 +Buffer variable reflection: + +Buffer block reflection: + Pipeline input reflection: attributeFloat: offset 0, type 1406, size 0, index 0, binding -1, stages 0 attributeFloat2: offset 0, type 8b50, size 0, index 0, binding -1, stages 0 diff --git a/Test/baseResults/hlsl.shift.per-set.frag.out b/Test/baseResults/hlsl.shift.per-set.frag.out index a6aa7a01..a0eb574b 100644 --- a/Test/baseResults/hlsl.shift.per-set.frag.out +++ b/Test/baseResults/hlsl.shift.per-set.frag.out @@ -226,6 +226,10 @@ u6: offset -1, type ffffffff, size 0, index -1, binding 34, stages 0, numMembers cb: offset -1, type ffffffff, size 4, index -1, binding 51, stages 0, numMembers 1 tb: offset -1, type ffffffff, size 4, index -1, binding 27, stages 0, numMembers 1 +Buffer variable reflection: + +Buffer block reflection: + Pipeline input reflection: Pipeline output reflection: diff --git a/Test/baseResults/reflection.frag.out b/Test/baseResults/reflection.frag.out index d3d90a27..65a8786d 100644 --- a/Test/baseResults/reflection.frag.out +++ b/Test/baseResults/reflection.frag.out @@ -3,6 +3,10 @@ Uniform reflection: Uniform block reflection: +Buffer variable reflection: + +Buffer block reflection: + Pipeline input reflection: Pipeline output reflection: diff --git a/Test/baseResults/reflection.options.frag.out b/Test/baseResults/reflection.options.frag.out index d0fe56e3..a0cecef6 100644 --- a/Test/baseResults/reflection.options.frag.out +++ b/Test/baseResults/reflection.options.frag.out @@ -3,6 +3,10 @@ Uniform reflection: Uniform block reflection: +Buffer variable reflection: + +Buffer block reflection: + Pipeline input reflection: inval: offset 0, type 1406, size 0, index 0, binding -1, stages 0 diff --git a/Test/baseResults/reflection.options.vert.out b/Test/baseResults/reflection.options.vert.out index b9247a0a..add73961 100644 --- a/Test/baseResults/reflection.options.vert.out +++ b/Test/baseResults/reflection.options.vert.out @@ -1,5 +1,13 @@ reflection.options.vert Uniform reflection: +UBO.verts[0].position[0]: offset 0, type 1406, size 1, index 0, binding -1, stages 1, arrayStride 4, topLevelArrayStride 24 +UBO.verts[1].position[0]: offset 24, type 1406, size 1, index 0, binding -1, stages 1, arrayStride 4, topLevelArrayStride 24 +UBO.flt[0]: offset 48, type 1406, size 8, index 0, binding -1, stages 1, arrayStride 4, topLevelArrayStride 4 + +Uniform block reflection: +UBO: offset -1, type ffffffff, size 80, index -1, binding -1, stages 0, numMembers 5 + +Buffer variable reflection: t[0].v[0].position[0]: offset 0, type 1406, size 3, index 0, binding -1, stages 1, arrayStride 4, topLevelArrayStride 72 t[0].v[1].position[0]: offset 24, type 1406, size 3, index 0, binding -1, stages 1, arrayStride 4, topLevelArrayStride 72 t[0].v[2].position[0]: offset 48, type 1406, size 3, index 0, binding -1, stages 1, arrayStride 4, topLevelArrayStride 72 @@ -10,7 +18,7 @@ MultipleArrays.tri[0].v[0].position[0]: offset 0, type 1406, size 1, index 1, bi MultipleArrays.vert[0].position[0]: offset 360, type 1406, size 1, index 1, binding -1, stages 1, arrayStride 4, topLevelArrayStride 24 MultipleArrays.f[0]: offset 480, type 1406, size 5, index 1, binding -1, stages 1, arrayStride 4, topLevelArrayStride 4 -Uniform block reflection: +Buffer block reflection: VertexCollection: offset -1, type ffffffff, size 360, index -1, binding -1, stages 0, numMembers 6 MultipleArrays: offset -1, type ffffffff, size 500, index -1, binding -1, stages 0, numMembers 9 diff --git a/Test/baseResults/reflection.vert.out b/Test/baseResults/reflection.vert.out index a7793c71..6baa8ac0 100644 --- a/Test/baseResults/reflection.vert.out +++ b/Test/baseResults/reflection.vert.out @@ -146,6 +146,10 @@ buf4: offset -1, type ffffffff, size 4, index -1, binding -1, stages 0, numMembe nested2: offset -1, type ffffffff, size 208, index -1, binding -1, stages 0, numMembers 15 VertexCollection: offset -1, type ffffffff, size 360, index -1, binding -1, stages 0, numMembers 30 +Buffer variable reflection: + +Buffer block reflection: + Pipeline input reflection: attributeFloat: offset 0, type 1406, size 0, index 0, binding -1, stages 0 attributeFloat2: offset 0, type 8b50, size 0, index 0, binding -1, stages 0 diff --git a/Test/reflection.options.vert b/Test/reflection.options.vert index cfb2b0d4..001537e0 100644 --- a/Test/reflection.options.vert +++ b/Test/reflection.options.vert @@ -19,6 +19,11 @@ buffer MultipleArrays { float f[5]; } multiarray; +uniform UBO { + VertexInfo verts[2]; + float flt[8]; +} ubo; + out float outval; void main() @@ -30,6 +35,8 @@ void main() f += multiarray.tri[gl_InstanceID].v[0].position[0]; f += multiarray.vert[gl_InstanceID].position[0]; f += multiarray.f[gl_InstanceID]; + f += ubo.verts[gl_InstanceID].position[0]; + f += ubo.flt[gl_InstanceID]; TriangleInfo tlocal[5] = t; outval = f; } diff --git a/Test/runtests b/Test/runtests index 44d3dc35..d3705034 100755 --- a/Test/runtests +++ b/Test/runtests @@ -32,11 +32,11 @@ diff -b $BASEDIR/badMacroArgs.frag.out $TARGETDIR/badMacroArgs.frag.out || HASER echo Running reflection... $EXE -l -q -C reflection.vert > $TARGETDIR/reflection.vert.out diff -b $BASEDIR/reflection.vert.out $TARGETDIR/reflection.vert.out || HASERROR=1 -$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io reflection.options.vert > $TARGETDIR/reflection.options.vert.out +$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers reflection.options.vert > $TARGETDIR/reflection.options.vert.out diff -b $BASEDIR/reflection.options.vert.out $TARGETDIR/reflection.options.vert.out || HASERROR=1 $EXE -l -q -C reflection.frag > $TARGETDIR/reflection.frag.out diff -b $BASEDIR/reflection.frag.out $TARGETDIR/reflection.frag.out || HASERROR=1 -$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io reflection.frag > $TARGETDIR/reflection.options.frag.out +$EXE -l -q -C --reflect-strict-array-suffix --reflect-basic-array-suffix --reflect-intermediate-io --reflect-separate-buffers reflection.frag > $TARGETDIR/reflection.options.frag.out diff -b $BASEDIR/reflection.options.frag.out $TARGETDIR/reflection.options.frag.out || HASERROR=1 $EXE -D -Od -e flizv -l -q -C -V -Od hlsl.reflection.vert > $TARGETDIR/hlsl.reflection.vert.out diff -b $BASEDIR/hlsl.reflection.vert.out $TARGETDIR/hlsl.reflection.vert.out || HASERROR=1 diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index a32d6053..63d3fc46 100755 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -2001,14 +2001,20 @@ bool TProgram::buildReflection(int opts) unsigned TProgram::getLocalSize(int dim) const { return reflection->getLocalSize(dim); } int TProgram::getReflectionIndex(const char* name) const { return reflection->getIndex(name); } -int TProgram::getNumUniformVariables() const { return reflection->getNumUniforms(); } -const TObjectReflection& TProgram::getUniform(int index) const { return reflection->getUniform(index); } -int TProgram::getNumUniformBlocks() const { return reflection->getNumUniformBlocks(); } -const TObjectReflection& TProgram::getUniformBlock(int index) const { return reflection->getUniformBlock(index); } -int TProgram::getNumPipeInputs() const { return reflection->getNumPipeInputs(); } -const TObjectReflection& TProgram::getPipeInput(int index) const { return reflection->getPipeInput(index); } -int TProgram::getNumPipeOutputs() const { return reflection->getNumPipeOutputs(); } -const TObjectReflection& TProgram::getPipeOutput(int index) const { return reflection->getPipeOutput(index); } +int TProgram::getNumUniformVariables() const { return reflection->getNumUniforms(); } +const TObjectReflection& TProgram::getUniform(int index) const { return reflection->getUniform(index); } +int TProgram::getNumUniformBlocks() const { return reflection->getNumUniformBlocks(); } +const TObjectReflection& TProgram::getUniformBlock(int index) const { return reflection->getUniformBlock(index); } +int TProgram::getNumPipeInputs() const { return reflection->getNumPipeInputs(); } +const TObjectReflection& TProgram::getPipeInput(int index) const { return reflection->getPipeInput(index); } +int TProgram::getNumPipeOutputs() const { return reflection->getNumPipeOutputs(); } +const TObjectReflection& TProgram::getPipeOutput(int index) const { return reflection->getPipeOutput(index); } +int TProgram::getNumBufferVariables() const { return reflection->getNumBufferVariables(); } +const TObjectReflection& TProgram::getBufferVariable(int index) const { return reflection->getBufferVariable(index); } +int TProgram::getNumBufferBlocks() const { return reflection->getNumStorageBuffers(); } +const TObjectReflection& TProgram::getBufferBlock(int index) const { return reflection->getStorageBufferBlock(index); } +int TProgram::getNumAtomicCounters() const { return reflection->getNumAtomicCounters(); } +const TObjectReflection& TProgram::getAtomicCounter(int index) const { return reflection->getAtomicCounter(index); } void TProgram::dumpReflection() { reflection->dump(); } diff --git a/glslang/MachineIndependent/reflection.cpp b/glslang/MachineIndependent/reflection.cpp index b235b465..a7d9b13a 100644 --- a/glslang/MachineIndependent/reflection.cpp +++ b/glslang/MachineIndependent/reflection.cpp @@ -93,7 +93,8 @@ public: // Use a degenerate (empty) set of dereferences to immediately put as at the end of // the dereference change expected by blowUpActiveAggregate. TList derefs; - blowUpActiveAggregate(base.getType(), base.getName(), derefs, derefs.end(), -1, -1, 0, 0); + blowUpActiveAggregate(base.getType(), base.getName(), derefs, derefs.end(), -1, -1, 0, 0, + base.getQualifier().storage); } } @@ -268,7 +269,7 @@ 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 topLevelArrayStride) + int topLevelArrayStride, TStorageQualifier baseStorage) { // when strictArraySuffix is enabled, we closely follow the rules from ARB_program_interface_query. // Broadly: @@ -305,7 +306,7 @@ public: ++nextDeref; TType derefType(*terminalType, 0); blowUpActiveAggregate(derefType, newBaseName, derefs, nextDeref, offset, blockIndex, arraySize, - topLevelArrayStride); + topLevelArrayStride, baseStorage); if (offset >= 0) offset += stride; @@ -376,7 +377,7 @@ public: offset = baseOffset + stride * i; blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0, - topLevelArrayStride); + topLevelArrayStride, baseStorage); } } else { // Visit all members of this aggregate, and for each one, @@ -404,7 +405,7 @@ public: } blowUpActiveAggregate(derefType, newBaseName, derefs, derefs.end(), offset, blockIndex, 0, - arrayStride); + arrayStride, baseStorage); } } @@ -423,22 +424,26 @@ public: if (arraySize == 0) arraySize = mapToGlArraySize(*terminalType); + TReflection::TMapIndexToReflection& variables = reflection.GetVariableMapForStorage(baseStorage); + TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); if (it == reflection.nameToIndex.end()) { - reflection.nameToIndex[name.c_str()] = (int)reflection.indexToUniform.size(); - - reflection.indexToUniform.push_back(TObjectReflection(name.c_str(), *terminalType, offset, - mapToGlType(*terminalType), - arraySize, blockIndex)); + int uniformIndex = (int)variables.size(); + reflection.nameToIndex[name.c_str()] = uniformIndex; + variables.push_back(TObjectReflection(name.c_str(), *terminalType, offset, mapToGlType(*terminalType), + arraySize, blockIndex)); if (terminalType->isArray()) { - reflection.indexToUniform.back().arrayStride = getArrayStride(baseType, *terminalType); + variables.back().arrayStride = getArrayStride(baseType, *terminalType); if (topLevelArrayStride == 0) - topLevelArrayStride = reflection.indexToUniform.back().arrayStride; + topLevelArrayStride = variables.back().arrayStride; } - reflection.indexToUniform.back().topLevelArrayStride = topLevelArrayStride; + if ((reflection.options & EShReflectionSeparateBuffers) && terminalType->getBasicType() == EbtAtomicUint) + reflection.atomicCounterUniformIndices.push_back(uniformIndex); + + variables.back().topLevelArrayStride = topLevelArrayStride; } else if (arraySize > 1) { - int& reflectedArraySize = reflection.indexToUniform[it->second].size; + int& reflectedArraySize = variables[it->second].size; reflectedArraySize = std::max(arraySize, reflectedArraySize); } } @@ -528,19 +533,22 @@ public: else baseName = base->getName(); } - blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.begin(), offset, blockIndex, arraySize, 0); + blowUpActiveAggregate(base->getType(), baseName, derefs, derefs.begin(), offset, blockIndex, arraySize, 0, + base->getQualifier().storage); } int addBlockName(const TString& name, const TType& type, int size) { + TReflection::TMapIndexToReflection& blocks = reflection.GetBlockMapForStorage(type.getQualifier().storage); + int blockIndex; TReflection::TNameToIndex::const_iterator it = reflection.nameToIndex.find(name.c_str()); if (reflection.nameToIndex.find(name.c_str()) == reflection.nameToIndex.end()) { - blockIndex = (int)reflection.indexToUniformBlock.size(); + blockIndex = (int)blocks.size(); reflection.nameToIndex[name.c_str()] = blockIndex; - reflection.indexToUniformBlock.push_back(TObjectReflection(name.c_str(), type, -1, -1, size, -1)); + blocks.push_back(TObjectReflection(name.c_str(), type, -1, -1, size, -1)); - reflection.indexToUniformBlock.back().numMembers = countAggregateMembers(type); + blocks.back().numMembers = countAggregateMembers(type); } else blockIndex = it->second; @@ -1013,6 +1021,11 @@ void TReflection::buildUniformStageMask(const TIntermediate& intermediate) for (int i = 0; i < int(indexToUniform.size()); ++i) { indexToUniform[i].stages = static_cast(indexToUniform[i].stages | 1 << intermediate.getStage()); } + + for (int i = 0; i < int(indexToBufferVariable.size()); ++i) { + indexToBufferVariable[i].stages = + static_cast(indexToBufferVariable[i].stages | 1 << intermediate.getStage()); + } } // Merge live symbols from 'intermediate' into the existing reflection database. @@ -1057,6 +1070,16 @@ void TReflection::dump() indexToUniformBlock[i].dump(); printf("\n"); + printf("Buffer variable reflection:\n"); + for (size_t i = 0; i < indexToBufferVariable.size(); ++i) + indexToBufferVariable[i].dump(); + printf("\n"); + + printf("Buffer block reflection:\n"); + for (size_t i = 0; i < indexToBufferBlock.size(); ++i) + indexToBufferBlock[i].dump(); + printf("\n"); + printf("Pipeline input reflection:\n"); for (size_t i = 0; i < indexToPipeInput.size(); ++i) indexToPipeInput[i].dump(); diff --git a/glslang/MachineIndependent/reflection.h b/glslang/MachineIndependent/reflection.h index ccd87f1a..44b17a05 100644 --- a/glslang/MachineIndependent/reflection.h +++ b/glslang/MachineIndependent/reflection.h @@ -107,6 +107,36 @@ public: return badReflection; } + // for mapping from an atomic counter to the uniform index + int getNumAtomicCounters() const { return (int)atomicCounterUniformIndices.size(); } + const TObjectReflection& getAtomicCounter(int i) const + { + if (i >= 0 && i < (int)atomicCounterUniformIndices.size()) + return getUniform(atomicCounterUniformIndices[i]); + else + return badReflection; + } + + // for mapping a buffer variable index to a buffer variable object's description + int getNumBufferVariables() { return (int)indexToBufferVariable.size(); } + const TObjectReflection& getBufferVariable(int i) const + { + if (i >= 0 && i < (int)indexToBufferVariable.size()) + return indexToBufferVariable[i]; + else + return badReflection; + } + + // for mapping a storage block index to the storage block's description + int getNumStorageBuffers() const { return (int)indexToBufferBlock.size(); } + const TObjectReflection& getStorageBufferBlock(int i) const + { + if (i >= 0 && i < (int)indexToBufferBlock.size()) + return indexToBufferBlock[i]; + else + return badReflection; + } + // for mapping any name to its index (block names, uniform names and input/output names) int getIndex(const char* name) const { @@ -135,6 +165,20 @@ protected: // Need a TString hash: typedef std::unordered_map TNameToIndex; typedef std::map TNameToIndex; typedef std::vector TMapIndexToReflection; + typedef std::vector TIndices; + + TMapIndexToReflection& GetBlockMapForStorage(TStorageQualifier storage) + { + if ((options & EShReflectionSeparateBuffers) && storage == EvqBuffer) + return indexToBufferBlock; + return indexToUniformBlock; + } + TMapIndexToReflection& GetVariableMapForStorage(TStorageQualifier storage) + { + if ((options & EShReflectionSeparateBuffers) && storage == EvqBuffer) + return indexToBufferVariable; + return indexToUniform; + } EShReflectionOptions options; @@ -145,8 +189,11 @@ protected: TNameToIndex nameToIndex; // maps names to indexes; can hold all types of data: uniform/buffer and which function names have been processed TMapIndexToReflection indexToUniform; TMapIndexToReflection indexToUniformBlock; + TMapIndexToReflection indexToBufferVariable; + TMapIndexToReflection indexToBufferBlock; TMapIndexToReflection indexToPipeInput; TMapIndexToReflection indexToPipeOutput; + TIndices atomicCounterUniformIndices; unsigned int localSize[3]; }; diff --git a/glslang/Public/ShaderLang.h b/glslang/Public/ShaderLang.h index 827de4eb..7aaaaf3a 100755 --- a/glslang/Public/ShaderLang.h +++ b/glslang/Public/ShaderLang.h @@ -247,6 +247,7 @@ typedef enum { EShReflectionStrictArraySuffix = (1 << 0), // reflection will follow stricter rules for array-of-structs suffixes EShReflectionBasicArraySuffix = (1 << 1), // arrays of basic types will be appended with [0] as in GL reflection EShReflectionIntermediateIO = (1 << 2), // reflect inputs and outputs to program, even with no vertex shader + EShReflectionSeparateBuffers = (1 << 3), // buffer variables and buffer blocks are reflected separately } EShReflectionOptions; // @@ -748,6 +749,12 @@ public: const TObjectReflection& getPipeInput(int index) const; int getNumPipeOutputs() const; const TObjectReflection& getPipeOutput(int index) const; + int getNumBufferVariables() const; + const TObjectReflection& getBufferVariable(int index) const; + int getNumBufferBlocks() const; + const TObjectReflection& getBufferBlock(int index) const; + int getNumAtomicCounters() const; + const TObjectReflection& getAtomicCounter(int index) const; // Legacy Reflection Interface - expressed in terms of above interface int getNumLiveUniformVariables() const // can be used for glGetProgramiv(GL_ACTIVE_UNIFORMS)