Uniform blocks implementation - Added support for uniform blocks in OutputASM::declareUniform - Added basic implementation of Program::applyUniformBuffers() to be completed later on when uniform buffers are implemented Change-Id: I919b59d4557bb10bb302e6b6bd0ada79553ca8bb Reviewed-on: https://swiftshader-review.googlesource.com/3651 Tested-by: Alexis Hétu <sugoi@google.com> Reviewed-by: Nicolas Capens <capn@google.com>
diff --git a/src/OpenGL/compiler/OutputASM.cpp b/src/OpenGL/compiler/OutputASM.cpp index 782d2b0..da58281 100644 --- a/src/OpenGL/compiler/OutputASM.cpp +++ b/src/OpenGL/compiler/OutputASM.cpp
@@ -75,13 +75,16 @@ ConstantUnion constants[4]; }; - Uniform::Uniform(GLenum type, GLenum precision, const std::string &name, int arraySize, int registerIndex) + Uniform::Uniform(GLenum type, GLenum precision, const std::string &name, int arraySize, int registerIndex, int blockId) : + type(type), precision(precision), name(name), arraySize(arraySize), registerIndex(registerIndex), blockId(blockId) { - this->type = type; - this->precision = precision; - this->name = name; - this->arraySize = arraySize; - this->registerIndex = registerIndex; + } + + UniformBlock::UniformBlock(const std::string& name, const std::string& instanceName, unsigned int dataSize, unsigned int arraySize, + TLayoutBlockStorage layout, bool isRowMajorLayout, int registerIndex, int blockId) : + name(name), instanceName(instanceName), dataSize(dataSize), arraySize(arraySize), layout(layout), + isRowMajorLayout(isRowMajorLayout), registerIndex(registerIndex), blockId(blockId) + { } Attribute::Attribute() @@ -2399,55 +2402,73 @@ } } - void OutputASM::declareUniform(const TType &type, const TString &name, int index) + void OutputASM::declareUniform(const TType &type, const TString &name, int offset, int blockId) { const TStructure *structure = type.getStruct(); + const TInterfaceBlock *block = (type.isInterfaceBlock() || (blockId == -1)) ? type.getInterfaceBlock() : nullptr; ActiveUniforms &activeUniforms = shaderObject->activeUniforms; - if(!structure) + if(block) { - activeUniforms.push_back(Uniform(glVariableType(type), glVariablePrecision(type), name.c_str(), type.getArraySize(), index)); + ActiveUniformBlocks &activeUniformBlocks = shaderObject->activeUniformBlocks; + blockId = activeUniformBlocks.size(); + unsigned int dataSize = block->objectSize() * 4; // FIXME: assuming 4 bytes per element + activeUniformBlocks.push_back(UniformBlock(block->name().c_str(), block->hasInstanceName() ? block->instanceName().c_str() : std::string(), dataSize, + block->arraySize(), block->blockStorage(), block->matrixPacking() == EmpRowMajor, offset, blockId)); + } + + if(!structure && !block) + { + if(blockId >= 0) + { + shaderObject->activeUniformBlocks[blockId].fields.push_back(activeUniforms.size()); + } + activeUniforms.push_back(Uniform(glVariableType(type), glVariablePrecision(type), name.c_str(), type.getArraySize(), offset, blockId)); if(isSamplerRegister(type)) { for(int i = 0; i < type.totalRegisterCount(); i++) { - shader->declareSampler(index + i); + shader->declareSampler(offset + i); } } } else { - const TFieldList& fields = structure->fields(); + const TFieldList& fields = structure ? structure->fields() : block->fields(); + const bool containerHasName = structure || block->hasInstanceName(); + const TString &containerName = structure ? name : (containerHasName ? block->instanceName() : TString()); if(type.isArray()) { - int elementIndex = index; + int elementOffset = offset; for(int i = 0; i < type.getArraySize(); i++) { + int fieldOffset = (blockId == -1) ? elementOffset : 0; for(size_t j = 0; j < fields.size(); j++) { const TType &fieldType = *(fields[j]->type()); const TString &fieldName = fields[j]->name(); - const TString uniformName = name + "[" + str(i) + "]." + fieldName; - declareUniform(fieldType, uniformName, elementIndex); - elementIndex += fieldType.totalRegisterCount(); + const TString uniformName = containerHasName ? containerName + "[" + str(i) + "]." + fieldName : fieldName; + declareUniform(fieldType, uniformName, fieldOffset, blockId); + fieldOffset += fieldType.totalRegisterCount(); } + elementOffset = fieldOffset; } } else { - int fieldIndex = index; + int fieldOffset = (blockId == -1) ? offset : 0; for(size_t i = 0; i < fields.size(); i++) { const TType &fieldType = *(fields[i]->type()); const TString &fieldName = fields[i]->name(); - const TString uniformName = name + "." + fieldName; - declareUniform(fieldType, uniformName, fieldIndex); - fieldIndex += fieldType.totalRegisterCount(); + const TString uniformName = containerHasName ? containerName + "." + fieldName : fieldName; + declareUniform(fieldType, uniformName, fieldOffset, blockId); + fieldOffset += fieldType.totalRegisterCount(); } } }
diff --git a/src/OpenGL/compiler/OutputASM.h b/src/OpenGL/compiler/OutputASM.h index 43b0bd2..156fda5 100644 --- a/src/OpenGL/compiler/OutputASM.h +++ b/src/OpenGL/compiler/OutputASM.h
@@ -32,7 +32,7 @@ { struct Uniform { - Uniform(GLenum type, GLenum precision, const std::string &name, int arraySize, int registerIndex); + Uniform(GLenum type, GLenum precision, const std::string &name, int arraySize, int registerIndex, int blockId); GLenum type; GLenum precision; @@ -40,10 +40,34 @@ int arraySize; int registerIndex; + + int blockId; }; typedef std::vector<Uniform> ActiveUniforms; + struct UniformBlock + { + UniformBlock(const std::string& name, const std::string& instanceName, unsigned int dataSize, unsigned int arraySize, + TLayoutBlockStorage layout, bool isRowMajorLayout, int registerIndex, int blockId); + + const std::string& getName() const { return (instanceName.length() > 0) ? instanceName : name; } + + std::string name; + std::string instanceName; + unsigned int dataSize; + unsigned int arraySize; + TLayoutBlockStorage layout; + bool isRowMajorLayout; + std::vector<int> fields; + + int registerIndex; + + int blockId; + }; + + typedef std::vector<UniformBlock> ActiveUniformBlocks; + struct Attribute { Attribute(); @@ -99,6 +123,7 @@ VaryingList varyings; ActiveUniforms activeUniforms; ActiveAttributes activeAttributes; + ActiveUniformBlocks activeUniformBlocks; }; struct Function @@ -180,7 +205,7 @@ int allocate(VariableArray &list, TIntermTyped *variable); void free(VariableArray &list, TIntermTyped *variable); - void declareUniform(const TType &type, const TString &name, int index); + void declareUniform(const TType &type, const TString &name, int offset, int blockId = -1); GLenum glVariableType(const TType &type); GLenum glVariablePrecision(const TType &type);
diff --git a/src/OpenGL/libGLESv2/Context.cpp b/src/OpenGL/libGLESv2/Context.cpp index 6f33d6c..ae733d0 100644 --- a/src/OpenGL/libGLESv2/Context.cpp +++ b/src/OpenGL/libGLESv2/Context.cpp
@@ -3031,6 +3031,7 @@ } programObject->applyUniforms(); + programObject->applyUniformBuffers(); } void Context::applyTextures()
diff --git a/src/OpenGL/libGLESv2/Program.cpp b/src/OpenGL/libGLESv2/Program.cpp index 1cb028a..4f17718 100644 --- a/src/OpenGL/libGLESv2/Program.cpp +++ b/src/OpenGL/libGLESv2/Program.cpp
@@ -35,9 +35,30 @@ return buffer; } + Uniform::BlockInfo::BlockInfo(const glsl::Uniform& uniform, int blockIndex, bool rowMajorLayout) + { + if(blockIndex >= 0) + { + index = blockIndex; + offset = uniform.registerIndex; + arrayStride = UniformTypeSize(uniform.type) * uniform.arraySize; + isRowMajorMatrix = rowMajorLayout; + int rowCount = VariableRowCount(uniform.type); + matrixStride = (rowCount > 1) ? (isRowMajorMatrix ? rowCount : VariableColumnCount(uniform.type)) * UniformTypeSize(UniformComponentType(uniform.type)) : 0; + } + else + { + index = -1; + offset = -1; + arrayStride = -1; + matrixStride = -1; + isRowMajorMatrix = false; + } + } + Uniform::Uniform(GLenum type, GLenum precision, const std::string &name, unsigned int arraySize, - const int blockIndex, const BlockMemberInfo &blockInfo) - : type(type), precision(precision), name(name), arraySize(arraySize), blockIndex(blockIndex), blockInfo(blockInfo) + const BlockInfo &blockInfo) + : type(type), precision(precision), name(name), arraySize(arraySize), blockInfo(blockInfo) { int bytes = UniformTypeSize(type) * size(); data = new unsigned char[bytes]; @@ -68,11 +89,26 @@ return size() * VariableRegisterCount(type); } - UniformBlock::UniformBlock(const std::string &name, unsigned int elementIndex, unsigned int dataSize) : - name(name), elementIndex(elementIndex), dataSize(dataSize), psRegisterIndex(GL_INVALID_INDEX), vsRegisterIndex(GL_INVALID_INDEX) + UniformBlock::UniformBlock(const std::string &name, unsigned int elementIndex, unsigned int dataSize, std::vector<unsigned int> memberUniformIndexes) : + name(name), elementIndex(elementIndex), dataSize(dataSize), memberUniformIndexes(memberUniformIndexes), psRegisterIndex(GL_INVALID_INDEX), vsRegisterIndex(GL_INVALID_INDEX) { } + void UniformBlock::setRegisterIndex(GLenum shader, unsigned int registerIndex) + { + switch(shader) + { + case GL_VERTEX_SHADER: + vsRegisterIndex = registerIndex; + break; + case GL_FRAGMENT_SHADER: + psRegisterIndex = registerIndex; + break; + default: + UNREACHABLE(shader); + } + } + bool UniformBlock::isArrayElement() const { return elementIndex != GL_INVALID_INDEX; @@ -348,7 +384,7 @@ { ASSERT(uniformBlockIndex < getActiveUniformBlockCount()); - const UniformBlock &uniformBlock = *uniformBlocks[uniformBlockIndex]; + const UniformBlock &uniformBlock = uniformBlocks[uniformBlockIndex]; switch(pname) { @@ -387,7 +423,7 @@ unsigned int numUniformBlocks = getActiveUniformBlockCount(); for(unsigned int blockIndex = 0; blockIndex < numUniformBlocks; blockIndex++) { - const UniformBlock &uniformBlock = *uniformBlocks[blockIndex]; + const UniformBlock &uniformBlock = uniformBlocks[blockIndex]; if(uniformBlock.name == baseName) { const bool arrayElementZero = (subscript == GL_INVALID_INDEX && uniformBlock.elementIndex == 0); @@ -1087,6 +1123,48 @@ } } + void Program::applyUniformBuffers() + { + GLint vertexUniformBuffers[IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS]; + GLint fragmentUniformBuffers[IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS]; + + for(unsigned int registerIndex = 0; registerIndex < IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS; ++registerIndex) + { + vertexUniformBuffers[registerIndex] = -1; + } + + for(unsigned int registerIndex = 0; registerIndex < IMPLEMENTATION_MAX_UNIFORM_BUFFER_BINDINGS; ++registerIndex) + { + fragmentUniformBuffers[registerIndex] = -1; + } + + for(unsigned int uniformBlockIndex = 0; uniformBlockIndex < uniformBlocks.size(); uniformBlockIndex++) + { + UniformBlock &uniformBlock = uniformBlocks[uniformBlockIndex]; + GLuint blockBinding = uniformBlockBindings[uniformBlockIndex]; + + // Unnecessary to apply an unreferenced standard or shared UBO + if(!uniformBlock.isReferencedByVertexShader() && !uniformBlock.isReferencedByFragmentShader()) + { + continue; + } + + if(uniformBlock.isReferencedByVertexShader()) + { + unsigned int registerIndex = uniformBlock.vsRegisterIndex; + ASSERT(vertexUniformBuffers[registerIndex] == -1); + vertexUniformBuffers[registerIndex] = blockBinding; + } + + if(uniformBlock.isReferencedByFragmentShader()) + { + unsigned int registerIndex = uniformBlock.psRegisterIndex; + ASSERT(fragmentUniformBuffers[registerIndex] == -1); + fragmentUniformBuffers[registerIndex] = blockBinding; + } + } + } + bool Program::linkVaryings() { for(glsl::VaryingList::iterator input = fragmentShader->varyings.begin(); input != fragmentShader->varyings.end(); ++input) @@ -1233,6 +1311,12 @@ return; } + // Link uniform blocks before uniforms to make it easy to assign block indices to fields + if(!linkUniformBlocks(vertexShader, fragmentShader)) + { + return; + } + if(!linkUniforms(fragmentShader)) { return; @@ -1345,7 +1429,18 @@ { const glsl::Uniform &uniform = activeUniforms[uniformIndex]; - if(!defineUniform(shader->getType(), uniform.type, uniform.precision, uniform.name, uniform.arraySize, uniform.registerIndex)) + int blockIndex = -1; + bool isRowMajorMatrix = false; + if(uniform.blockId >= 0) + { + const glsl::ActiveUniformBlocks &activeUniformBlocks = shader->activeUniformBlocks; + ASSERT(static_cast<size_t>(uniform.blockId) < activeUniformBlocks.size()); + UniformBlockArray::iterator it = std::find(uniformBlocks.begin(), uniformBlocks.end(), UniformBlock(activeUniformBlocks[uniform.blockId].getName(), GL_INVALID_INDEX, 0, std::vector<unsigned int>())); + ASSERT(it != uniformBlocks.end()); + blockIndex = (it - uniformBlocks.begin()) / sizeof(UniformBlockArray::iterator); + isRowMajorMatrix = activeUniformBlocks[uniform.blockId].isRowMajorLayout; + } + if(!defineUniform(shader->getType(), uniform.type, uniform.precision, uniform.name, uniform.arraySize, uniform.registerIndex, Uniform::BlockInfo(uniform, blockIndex, isRowMajorMatrix))) { return false; } @@ -1354,7 +1449,7 @@ return true; } - bool Program::defineUniform(GLenum shader, GLenum type, GLenum precision, const std::string &name, unsigned int arraySize, int registerIndex) + bool Program::defineUniform(GLenum shader, GLenum type, GLenum precision, const std::string &name, unsigned int arraySize, int registerIndex, const Uniform::BlockInfo& blockInfo) { if(IsSamplerUniform(type)) { @@ -1460,7 +1555,7 @@ } else { - uniform = new Uniform(type, precision, name, arraySize, -1, Uniform::BlockMemberInfo::getDefaultBlockInfo()); + uniform = new Uniform(type, precision, name, arraySize, blockInfo); } if(!uniform) @@ -1510,6 +1605,117 @@ return true; } + bool Program::areMatchingUniformBlocks(const glsl::UniformBlock &block1, const glsl::UniformBlock &block2, const Shader *shader1, const Shader *shader2) + { + // validate blocks for the same member types + if(block1.fields.size() != block2.fields.size()) + { + return false; + } + if(block1.arraySize != block2.arraySize) + { + return false; + } + if(block1.layout != block2.layout || block1.isRowMajorLayout != block2.isRowMajorLayout) + { + return false; + } + const unsigned int numBlockMembers = block1.fields.size(); + for(unsigned int blockMemberIndex = 0; blockMemberIndex < numBlockMembers; blockMemberIndex++) + { + const glsl::Uniform& member1 = shader1->activeUniforms[block1.fields[blockMemberIndex]]; + const glsl::Uniform& member2 = shader2->activeUniforms[block2.fields[blockMemberIndex]]; + if(member1.name != member2.name || + member1.arraySize != member2.arraySize || + member1.precision != member2.precision || + member1.type != member2.type) + { + return false; + } + } + return true; + } + + bool Program::linkUniformBlocks(const Shader *vertexShader, const Shader *fragmentShader) + { + const glsl::ActiveUniformBlocks &vertexUniformBlocks = vertexShader->activeUniformBlocks; + const glsl::ActiveUniformBlocks &fragmentUniformBlocks = fragmentShader->activeUniformBlocks; + // Check that interface blocks defined in the vertex and fragment shaders are identical + typedef std::map<std::string, const glsl::UniformBlock*> UniformBlockMap; + UniformBlockMap linkedUniformBlocks; + for(unsigned int blockIndex = 0; blockIndex < vertexUniformBlocks.size(); blockIndex++) + { + const glsl::UniformBlock &vertexUniformBlock = vertexUniformBlocks[blockIndex]; + linkedUniformBlocks[vertexUniformBlock.name] = &vertexUniformBlock; + } + for(unsigned int blockIndex = 0; blockIndex < fragmentUniformBlocks.size(); blockIndex++) + { + const glsl::UniformBlock &fragmentUniformBlock = fragmentUniformBlocks[blockIndex]; + UniformBlockMap::const_iterator entry = linkedUniformBlocks.find(fragmentUniformBlock.name); + if(entry != linkedUniformBlocks.end()) + { + const glsl::UniformBlock &vertexUniformBlock = *entry->second; + if(!areMatchingUniformBlocks(vertexUniformBlock, fragmentUniformBlock, vertexShader, fragmentShader)) + { + return false; + } + } + } + for(unsigned int blockIndex = 0; blockIndex < vertexUniformBlocks.size(); blockIndex++) + { + const glsl::UniformBlock &uniformBlock = vertexUniformBlocks[blockIndex]; + if(!defineUniformBlock(vertexShader, uniformBlock)) + { + return false; + } + } + for(unsigned int blockIndex = 0; blockIndex < fragmentUniformBlocks.size(); blockIndex++) + { + const glsl::UniformBlock &uniformBlock = fragmentUniformBlocks[blockIndex]; + if(!defineUniformBlock(fragmentShader, uniformBlock)) + { + return false; + } + } + return true; + } + + bool Program::defineUniformBlock(const Shader *shader, const glsl::UniformBlock &block) + { + UniformBlockArray::iterator it = std::find(uniformBlocks.begin(), uniformBlocks.end(), UniformBlock(block.getName(), 0, 0, std::vector<unsigned int>())); + + if(it == uniformBlocks.end()) + { + const glsl::ActiveUniforms &activeUniforms = shader->activeUniforms; + const std::vector<int>& fields = block.fields; + std::vector<unsigned int> memberUniformIndexes; + for(size_t i = 0; i < fields.size(); ++i) + { + memberUniformIndexes.push_back(activeUniforms[fields[i]].registerIndex); + } + + if(block.arraySize > 0) + { + for(unsigned int i = 0; i < block.arraySize; ++i) + { + uniformBlocks.push_back(UniformBlock(block.getName(), i, block.dataSize, memberUniformIndexes)); + uniformBlocks[uniformBlocks.size() - 1].setRegisterIndex(shader->getType(), block.registerIndex); + } + } + else + { + uniformBlocks.push_back(UniformBlock(block.getName(), GL_INVALID_INDEX, block.dataSize, memberUniformIndexes)); + uniformBlocks[uniformBlocks.size() - 1].setRegisterIndex(shader->getType(), block.registerIndex); + } + } + else + { + it->setRegisterIndex(shader->getType(), block.registerIndex); + } + + return true; + } + bool Program::applyUniform1bv(GLint location, GLsizei count, const GLboolean *v) { int vector[MAX_UNIFORM_VECTORS][4]; @@ -2336,6 +2542,7 @@ uniforms.pop_back(); } + uniformBlocks.clear(); uniformIndex.clear(); transformFeedbackLinkedVaryings.clear(); @@ -2571,7 +2778,7 @@ case GL_UNIFORM_TYPE: return static_cast<GLint>(uniform.type); case GL_UNIFORM_SIZE: return static_cast<GLint>(uniform.size()); case GL_UNIFORM_NAME_LENGTH: return static_cast<GLint>(uniform.name.size() + 1 + (uniform.isArray() ? 3 : 0)); - case GL_UNIFORM_BLOCK_INDEX: return uniform.blockIndex; + case GL_UNIFORM_BLOCK_INDEX: return uniform.blockInfo.index; case GL_UNIFORM_OFFSET: return uniform.blockInfo.offset; case GL_UNIFORM_ARRAY_STRIDE: return uniform.blockInfo.arrayStride; case GL_UNIFORM_MATRIX_STRIDE: return uniform.blockInfo.matrixStride; @@ -2587,7 +2794,7 @@ { ASSERT(index < getActiveUniformBlockCount()); - const UniformBlock &uniformBlock = *uniformBlocks[index]; + const UniformBlock &uniformBlock = uniformBlocks[index]; if(bufSize > 0) { @@ -2624,7 +2831,7 @@ unsigned int numUniformBlocks = getActiveUniformBlockCount(); for(unsigned int uniformBlockIndex = 0; uniformBlockIndex < numUniformBlocks; uniformBlockIndex++) { - const UniformBlock &uniformBlock = *uniformBlocks[uniformBlockIndex]; + const UniformBlock &uniformBlock = uniformBlocks[uniformBlockIndex]; if(!uniformBlock.name.empty()) { const int length = uniformBlock.name.length() + 1;
diff --git a/src/OpenGL/libGLESv2/Program.h b/src/OpenGL/libGLESv2/Program.h index 48d691d..20ee05b 100644 --- a/src/OpenGL/libGLESv2/Program.h +++ b/src/OpenGL/libGLESv2/Program.h
@@ -34,17 +34,11 @@ // Helper struct representing a single shader uniform struct Uniform { - struct BlockMemberInfo + struct BlockInfo { - BlockMemberInfo(int offset, int arrayStride, int matrixStride, bool isRowMajorMatrix) - : offset(offset), arrayStride(arrayStride), matrixStride(matrixStride), isRowMajorMatrix(isRowMajorMatrix) - {} + BlockInfo(const glsl::Uniform& uniform, int blockIndex, bool rowMajorLayout); - static BlockMemberInfo getDefaultBlockInfo() - { - return BlockMemberInfo(-1, -1, -1, false); - } - + int index; int offset; int arrayStride; int matrixStride; @@ -52,7 +46,7 @@ }; Uniform(GLenum type, GLenum precision, const std::string &name, unsigned int arraySize, - const int blockIndex, const BlockMemberInfo &blockInfo); + const BlockInfo &blockInfo); ~Uniform(); @@ -64,8 +58,7 @@ const GLenum precision; const std::string name; const unsigned int arraySize; - const int blockIndex; - const BlockMemberInfo blockInfo; + const BlockInfo blockInfo; unsigned char *data; bool dirty; @@ -78,7 +71,12 @@ struct UniformBlock { // use GL_INVALID_INDEX for non-array elements - UniformBlock(const std::string &name, unsigned int elementIndex, unsigned int dataSize); + UniformBlock(const std::string &name, unsigned int elementIndex, unsigned int dataSize, std::vector<unsigned int> memberUniformIndexes); + + void setRegisterIndex(GLenum shader, unsigned int registerIndex); + + // For std::find on UniformBlockArray + inline bool operator==(const UniformBlock& other) const { return name == other.name; } bool isArrayElement() const; bool isReferencedByVertexShader() const; @@ -172,6 +170,7 @@ void dirtyAllUniforms(); void applyUniforms(); + void applyUniformBuffers(); void link(); bool isLinked() const; @@ -225,7 +224,10 @@ int getAttributeBinding(const glsl::Attribute &attribute); bool linkUniforms(const Shader *shader); - bool defineUniform(GLenum shader, GLenum type, GLenum precision, const std::string &_name, unsigned int arraySize, int registerIndex); + bool linkUniformBlocks(const Shader *vertexShader, const Shader *fragmentShader); + bool areMatchingUniformBlocks(const glsl::UniformBlock &block1, const glsl::UniformBlock &block2, const Shader *shader1, const Shader *shader2); + bool defineUniform(GLenum shader, GLenum type, GLenum precision, const std::string &_name, unsigned int arraySize, int registerIndex, const Uniform::BlockInfo& blockInfo); + bool defineUniformBlock(const Shader *shader, const glsl::UniformBlock &block); bool applyUniform1bv(GLint location, GLsizei count, const GLboolean *v); bool applyUniform2bv(GLint location, GLsizei count, const GLboolean *v); bool applyUniform3bv(GLint location, GLsizei count, const GLboolean *v); @@ -293,7 +295,7 @@ UniformArray uniforms; typedef std::vector<UniformLocation> UniformIndex; UniformIndex uniformIndex; - typedef std::vector<UniformBlock*> UniformBlockArray; + typedef std::vector<UniformBlock> UniformBlockArray; UniformBlockArray uniformBlocks; typedef std::vector<LinkedVarying> LinkedVaryingArray; LinkedVaryingArray transformFeedbackLinkedVaryings;