/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES 3.1 Module * ------------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Program interface utilities *//*--------------------------------------------------------------------*/ #include "es31fProgramInterfaceDefinitionUtil.hpp" #include "es31fProgramInterfaceDefinition.hpp" #include "gluVarType.hpp" #include "gluVarTypeUtil.hpp" #include "gluShaderUtil.hpp" #include "deString.h" #include "deStringUtil.hpp" #include "glwEnums.hpp" #include <set> #include <map> #include <sstream> #include <vector> #include <algorithm> namespace deqp { namespace gles31 { namespace Functional { namespace ProgramInterfaceDefinition { VariableSearchFilter::VariableSearchFilter (void) : m_shaderTypeBits (0xFFFFFFFFul) , m_storageBits (0xFFFFFFFFul) { } VariableSearchFilter VariableSearchFilter::createShaderTypeFilter (glu::ShaderType type) { DE_ASSERT(type < glu::SHADERTYPE_LAST); VariableSearchFilter filter; filter.m_shaderTypeBits = (1u << type); return filter; } VariableSearchFilter VariableSearchFilter::createStorageFilter (glu::Storage storage) { DE_ASSERT(storage < glu::STORAGE_LAST); VariableSearchFilter filter; filter.m_storageBits = (1u << storage); return filter; } VariableSearchFilter VariableSearchFilter::createShaderTypeStorageFilter (glu::ShaderType type, glu::Storage storage) { return logicalAnd(createShaderTypeFilter(type), createStorageFilter(storage)); } VariableSearchFilter VariableSearchFilter::logicalOr (const VariableSearchFilter& a, const VariableSearchFilter& b) { VariableSearchFilter filter; filter.m_shaderTypeBits = a.m_shaderTypeBits | b.m_shaderTypeBits; filter.m_storageBits = a.m_storageBits | b.m_storageBits; return filter; } VariableSearchFilter VariableSearchFilter::logicalAnd (const VariableSearchFilter& a, const VariableSearchFilter& b) { VariableSearchFilter filter; filter.m_shaderTypeBits = a.m_shaderTypeBits & b.m_shaderTypeBits; filter.m_storageBits = a.m_storageBits & b.m_storageBits; return filter; } bool VariableSearchFilter::matchesFilter (const ProgramInterfaceDefinition::Shader* shader) const { DE_ASSERT(shader->getType() < glu::SHADERTYPE_LAST); return (m_shaderTypeBits & (1u << shader->getType())) != 0; } bool VariableSearchFilter::matchesFilter (const glu::VariableDeclaration& variable) const { DE_ASSERT(variable.storage < glu::STORAGE_LAST); return (m_storageBits & (1u << variable.storage)) != 0; } bool VariableSearchFilter::matchesFilter (const glu::InterfaceBlock& block) const { DE_ASSERT(block.storage < glu::STORAGE_LAST); return (m_storageBits & (1u << block.storage)) != 0; } } // ProgramInterfaceDefinition static bool incrementMultiDimensionIndex (std::vector<int>& index, const std::vector<int>& dimensions) { int incrementDimensionNdx = (int)(index.size() - 1); while (incrementDimensionNdx >= 0) { if (++index[incrementDimensionNdx] == dimensions[incrementDimensionNdx]) index[incrementDimensionNdx--] = 0; else break; } return (incrementDimensionNdx != -1); } bool programContainsIOBlocks (const ProgramInterfaceDefinition::Program* program) { for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx) { if (shaderContainsIOBlocks(program->getShaders()[shaderNdx])) return true; } return false; } bool shaderContainsIOBlocks (const ProgramInterfaceDefinition::Shader* shader) { for (int ndx = 0; ndx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++ndx) { const glu::Storage storage = shader->getDefaultBlock().interfaceBlocks[ndx].storage; if (storage == glu::STORAGE_IN || storage == glu::STORAGE_OUT || storage == glu::STORAGE_PATCH_IN || storage == glu::STORAGE_PATCH_OUT) { return true; } } return false; } glu::ShaderType getProgramTransformFeedbackStage (const ProgramInterfaceDefinition::Program* program) { if (program->hasStage(glu::SHADERTYPE_GEOMETRY)) return glu::SHADERTYPE_GEOMETRY; if (program->hasStage(glu::SHADERTYPE_TESSELLATION_EVALUATION)) return glu::SHADERTYPE_TESSELLATION_EVALUATION; if (program->hasStage(glu::SHADERTYPE_VERTEX)) return glu::SHADERTYPE_VERTEX; DE_ASSERT(false); return glu::SHADERTYPE_LAST; } void generateVariableTypeResourceNames (std::vector<std::string>& resources, const std::string& name, const glu::VarType& type, deUint32 resourceNameGenerationFlags) { DE_ASSERT((resourceNameGenerationFlags & (~RESOURCE_NAME_GENERATION_FLAG_MASK)) == 0); // remove top-level flag from children const deUint32 childFlags = resourceNameGenerationFlags & ~((deUint32)RESOURCE_NAME_GENERATION_FLAG_TOP_LEVEL_BUFFER_VARIABLE); if (type.isBasicType()) resources.push_back(name); else if (type.isStructType()) { const glu::StructType* structType = type.getStructPtr(); for (int ndx = 0; ndx < structType->getNumMembers(); ++ndx) generateVariableTypeResourceNames(resources, name + "." + structType->getMember(ndx).getName(), structType->getMember(ndx).getType(), childFlags); } else if (type.isArrayType()) { // Bottom-level arrays of basic types of a transform feedback variable will produce only the first // element but without the trailing "[0]" if (type.getElementType().isBasicType() && (resourceNameGenerationFlags & RESOURCE_NAME_GENERATION_FLAG_TRANSFORM_FEEDBACK_VARIABLE) != 0) { resources.push_back(name); } // Bottom-level arrays of basic types and SSBO top-level arrays of any type procude only first element else if (type.getElementType().isBasicType() || (resourceNameGenerationFlags & RESOURCE_NAME_GENERATION_FLAG_TOP_LEVEL_BUFFER_VARIABLE) != 0) { generateVariableTypeResourceNames(resources, name + "[0]", type.getElementType(), childFlags); } // Other arrays of aggregate types are expanded else { for (int ndx = 0; ndx < type.getArraySize(); ++ndx) generateVariableTypeResourceNames(resources, name + "[" + de::toString(ndx) + "]", type.getElementType(), childFlags); } } else DE_ASSERT(false); } // Program source generation namespace { using ProgramInterfaceDefinition::VariablePathComponent; using ProgramInterfaceDefinition::VariableSearchFilter; static std::string getShaderExtensionDeclarations (const ProgramInterfaceDefinition::Shader* shader) { std::vector<std::string> extensions; std::ostringstream buf; if (shader->getType() == glu::SHADERTYPE_GEOMETRY) { extensions.push_back("GL_EXT_geometry_shader"); } else if (shader->getType() == glu::SHADERTYPE_TESSELLATION_CONTROL || shader->getType() == glu::SHADERTYPE_TESSELLATION_EVALUATION) { extensions.push_back("GL_EXT_tessellation_shader"); } if (shaderContainsIOBlocks(shader)) extensions.push_back("GL_EXT_shader_io_blocks"); for (int ndx = 0; ndx < (int)extensions.size(); ++ndx) buf << "#extension " << extensions[ndx] << " : require\n"; return buf.str(); } static std::string getShaderTypeDeclarations (const ProgramInterfaceDefinition::Program* program, glu::ShaderType type) { switch (type) { case glu::SHADERTYPE_VERTEX: return ""; case glu::SHADERTYPE_FRAGMENT: return ""; case glu::SHADERTYPE_GEOMETRY: { std::ostringstream buf; buf << "layout(points) in;\n" "layout(points, max_vertices=" << program->getGeometryNumOutputVertices() << ") out;\n"; return buf.str(); } case glu::SHADERTYPE_TESSELLATION_CONTROL: { std::ostringstream buf; buf << "layout(vertices=" << program->getTessellationNumOutputPatchVertices() << ") out;\n"; return buf.str(); } case glu::SHADERTYPE_TESSELLATION_EVALUATION: return "layout(triangles, point_mode) in;\n"; case glu::SHADERTYPE_COMPUTE: return "layout(local_size_x=1) in;\n"; default: DE_ASSERT(false); return ""; } } class StructNameEqualPredicate { public: StructNameEqualPredicate (const char* name) : m_name(name) { } bool operator() (const glu::StructType* type) { return type->hasTypeName() && (deStringEqual(m_name, type->getTypeName()) == DE_TRUE); } private: const char* m_name; }; static void collectNamedStructureDefinitions (std::vector<const glu::StructType*>& dst, const glu::VarType& type) { if (type.isBasicType()) return; else if (type.isArrayType()) return collectNamedStructureDefinitions(dst, type.getElementType()); else if (type.isStructType()) { if (type.getStructPtr()->hasTypeName()) { // must be unique (may share the the same struct) std::vector<const glu::StructType*>::iterator where = std::find_if(dst.begin(), dst.end(), StructNameEqualPredicate(type.getStructPtr()->getTypeName())); if (where != dst.end()) { DE_ASSERT(**where == *type.getStructPtr()); // identical type has been added already, types of members must be added too return; } } // Add types of members first for (int ndx = 0; ndx < type.getStructPtr()->getNumMembers(); ++ndx) collectNamedStructureDefinitions(dst, type.getStructPtr()->getMember(ndx).getType()); dst.push_back(type.getStructPtr()); } else DE_ASSERT(false); } static void writeStructureDefinitions (std::ostringstream& buf, const ProgramInterfaceDefinition::DefaultBlock& defaultBlock) { std::vector<const glu::StructType*> namedStructs; // Collect all structs in post order for (int ndx = 0; ndx < (int)defaultBlock.variables.size(); ++ndx) collectNamedStructureDefinitions(namedStructs, defaultBlock.variables[ndx].varType); for (int blockNdx = 0; blockNdx < (int)defaultBlock.interfaceBlocks.size(); ++blockNdx) for (int ndx = 0; ndx < (int)defaultBlock.interfaceBlocks[blockNdx].variables.size(); ++ndx) collectNamedStructureDefinitions(namedStructs, defaultBlock.interfaceBlocks[blockNdx].variables[ndx].varType); // Write for (int structNdx = 0; structNdx < (int)namedStructs.size(); ++structNdx) { buf << "struct " << namedStructs[structNdx]->getTypeName() << "\n" "{\n"; for (int memberNdx = 0; memberNdx < namedStructs[structNdx]->getNumMembers(); ++memberNdx) buf << glu::indent(1) << glu::declare(namedStructs[structNdx]->getMember(memberNdx).getType(), namedStructs[structNdx]->getMember(memberNdx).getName(), 1) << ";\n"; buf << "};\n"; } if (!namedStructs.empty()) buf << "\n"; } static void writeInterfaceBlock (std::ostringstream& buf, const glu::InterfaceBlock& interfaceBlock) { buf << interfaceBlock.layout; if (interfaceBlock.layout != glu::Layout()) buf << " "; buf << glu::getStorageName(interfaceBlock.storage) << " " << interfaceBlock.interfaceName << "\n" << "{\n"; for (int ndx = 0; ndx < (int)interfaceBlock.variables.size(); ++ndx) buf << glu::indent(1) << interfaceBlock.variables[ndx] << ";\n"; buf << "}"; if (!interfaceBlock.instanceName.empty()) buf << " " << interfaceBlock.instanceName; for (int dimensionNdx = 0; dimensionNdx < (int)interfaceBlock.dimensions.size(); ++dimensionNdx) buf << "[" << interfaceBlock.dimensions[dimensionNdx] << "]"; buf << ";\n\n"; } static bool isReadableInterface (const glu::InterfaceBlock& interface) { return interface.storage == glu::STORAGE_UNIFORM || interface.storage == glu::STORAGE_IN || interface.storage == glu::STORAGE_PATCH_IN || (interface.storage == glu::STORAGE_BUFFER && (interface.memoryAccessQualifierFlags & glu::MEMORYACCESSQUALIFIER_WRITEONLY_BIT) == 0); } static bool isWritableInterface (const glu::InterfaceBlock& interface) { return interface.storage == glu::STORAGE_OUT || interface.storage == glu::STORAGE_PATCH_OUT || (interface.storage == glu::STORAGE_BUFFER && (interface.memoryAccessQualifierFlags & glu::MEMORYACCESSQUALIFIER_READONLY_BIT) == 0); } static void writeVariableReadAccumulateExpression (std::ostringstream& buf, const std::string& accumulatorName, const std::string& name, glu::ShaderType shaderType, glu::Storage storage, const ProgramInterfaceDefinition::Program* program, const glu::VarType& varType) { if (varType.isBasicType()) { buf << "\t" << accumulatorName << " += "; if (glu::isDataTypeScalar(varType.getBasicType())) buf << "vec4(float(" << name << "))"; else if (glu::isDataTypeVector(varType.getBasicType())) buf << "vec4(" << name << ".xyxy)"; else if (glu::isDataTypeMatrix(varType.getBasicType())) buf << "vec4(float(" << name << "[0][0]))"; else if (glu::isDataTypeSamplerMultisample(varType.getBasicType())) buf << "vec4(float(textureSize(" << name << ").x))"; else if (glu::isDataTypeSampler(varType.getBasicType())) buf << "vec4(float(textureSize(" << name << ", 0).x))"; else if (glu::isDataTypeImage(varType.getBasicType())) buf << "vec4(float(imageSize(" << name << ").x))"; else if (varType.getBasicType() == glu::TYPE_UINT_ATOMIC_COUNTER) buf << "vec4(float(atomicCounterIncrement(" << name << ")))"; else DE_ASSERT(false); buf << ";\n"; } else if (varType.isStructType()) { for (int ndx = 0; ndx < varType.getStructPtr()->getNumMembers(); ++ndx) writeVariableReadAccumulateExpression(buf, accumulatorName, name + "." + varType.getStructPtr()->getMember(ndx).getName(), shaderType, storage, program, varType.getStructPtr()->getMember(ndx).getType()); } else if (varType.isArrayType()) { if (varType.getArraySize() != glu::VarType::UNSIZED_ARRAY) { for (int ndx = 0; ndx < varType.getArraySize(); ++ndx) writeVariableReadAccumulateExpression(buf, accumulatorName, name + "[" + de::toString(ndx) + "]", shaderType, storage, program, varType.getElementType()); } else if (storage == glu::STORAGE_BUFFER) { // run-time sized array, read arbitrary writeVariableReadAccumulateExpression(buf, accumulatorName, name + "[8]", shaderType, storage, program, varType.getElementType()); } else { DE_ASSERT(storage == glu::STORAGE_IN); if (shaderType == glu::SHADERTYPE_GEOMETRY) { // implicit sized geometry input array, size = primitive size. Just reading first is enough writeVariableReadAccumulateExpression(buf, accumulatorName, name + "[0]", shaderType, storage, program, varType.getElementType()); } else if (shaderType == glu::SHADERTYPE_TESSELLATION_CONTROL) { // implicit sized tessellation input array, size = input patch max size. Just reading current is enough writeVariableReadAccumulateExpression(buf, accumulatorName, name + "[gl_InvocationID]", shaderType, storage, program, varType.getElementType()); } else if (shaderType == glu::SHADERTYPE_TESSELLATION_EVALUATION) { // implicit sized tessellation input array, size = output patch max size. Read all to prevent optimizations DE_ASSERT(program->getTessellationNumOutputPatchVertices() > 0); for (int ndx = 0; ndx < (int)program->getTessellationNumOutputPatchVertices(); ++ndx) { writeVariableReadAccumulateExpression(buf, accumulatorName, name + "[" + de::toString(ndx) + "]", shaderType, storage, program, varType.getElementType()); } } else DE_ASSERT(false); } } else DE_ASSERT(false); } static void writeInterfaceReadAccumulateExpression (std::ostringstream& buf, const std::string& accumulatorName, const glu::InterfaceBlock& block, glu::ShaderType shaderType, const ProgramInterfaceDefinition::Program* program) { if (block.dimensions.empty()) { const std::string prefix = (block.instanceName.empty()) ? ("") : (block.instanceName + "."); for (int ndx = 0; ndx < (int)block.variables.size(); ++ndx) { writeVariableReadAccumulateExpression(buf, accumulatorName, prefix + block.variables[ndx].name, shaderType, block.storage, program, block.variables[ndx].varType); } } else { std::vector<int> index(block.dimensions.size(), 0); for (;;) { // access element { std::ostringstream name; name << block.instanceName; for (int dimensionNdx = 0; dimensionNdx < (int)block.dimensions.size(); ++dimensionNdx) name << "[" << index[dimensionNdx] << "]"; for (int ndx = 0; ndx < (int)block.variables.size(); ++ndx) { writeVariableReadAccumulateExpression(buf, accumulatorName, name.str() + "." + block.variables[ndx].name, shaderType, block.storage, program, block.variables[ndx].varType); } } // increment index if (!incrementMultiDimensionIndex(index, block.dimensions)) break; } } } static void writeVariableWriteExpression (std::ostringstream& buf, const std::string& sourceVec4Name, const std::string& name, glu::ShaderType shaderType, glu::Storage storage, const ProgramInterfaceDefinition::Program* program, const glu::VarType& varType) { if (varType.isBasicType()) { buf << "\t" << name << " = "; if (glu::isDataTypeScalar(varType.getBasicType())) buf << glu::getDataTypeName(varType.getBasicType()) << "(" << sourceVec4Name << ".y)"; else if (glu::isDataTypeVector(varType.getBasicType()) || glu::isDataTypeMatrix(varType.getBasicType())) buf << glu::getDataTypeName(varType.getBasicType()) << "(" << glu::getDataTypeName(glu::getDataTypeScalarType(varType.getBasicType())) << "(" << sourceVec4Name << ".y))"; else DE_ASSERT(false); buf << ";\n"; } else if (varType.isStructType()) { for (int ndx = 0; ndx < varType.getStructPtr()->getNumMembers(); ++ndx) writeVariableWriteExpression(buf, sourceVec4Name, name + "." + varType.getStructPtr()->getMember(ndx).getName(), shaderType, storage, program, varType.getStructPtr()->getMember(ndx).getType()); } else if (varType.isArrayType()) { if (varType.getArraySize() != glu::VarType::UNSIZED_ARRAY) { for (int ndx = 0; ndx < varType.getArraySize(); ++ndx) writeVariableWriteExpression(buf, sourceVec4Name, name + "[" + de::toString(ndx) + "]", shaderType, storage, program, varType.getElementType()); } else if (storage == glu::STORAGE_BUFFER) { // run-time sized array, write arbitrary writeVariableWriteExpression(buf, sourceVec4Name, name + "[9]", shaderType, storage, program, varType.getElementType()); } else { DE_ASSERT(storage == glu::STORAGE_OUT); if (shaderType == glu::SHADERTYPE_TESSELLATION_CONTROL) { // implicit sized tessellation onput array, size = output patch max size. Can only write to gl_InvocationID writeVariableWriteExpression(buf, sourceVec4Name, name + "[gl_InvocationID]", shaderType, storage, program, varType.getElementType()); } else DE_ASSERT(false); } } else DE_ASSERT(false); } static void writeInterfaceWriteExpression (std::ostringstream& buf, const std::string& sourceVec4Name, const glu::InterfaceBlock& block, glu::ShaderType shaderType, const ProgramInterfaceDefinition::Program* program) { if (block.dimensions.empty()) { const std::string prefix = (block.instanceName.empty()) ? ("") : (block.instanceName + "."); for (int ndx = 0; ndx < (int)block.variables.size(); ++ndx) { writeVariableWriteExpression(buf, sourceVec4Name, prefix + block.variables[ndx].name, shaderType, block.storage, program, block.variables[ndx].varType); } } else { std::vector<int> index(block.dimensions.size(), 0); for (;;) { // access element { std::ostringstream name; name << block.instanceName; for (int dimensionNdx = 0; dimensionNdx < (int)block.dimensions.size(); ++dimensionNdx) name << "[" << index[dimensionNdx] << "]"; for (int ndx = 0; ndx < (int)block.variables.size(); ++ndx) { writeVariableWriteExpression(buf, sourceVec4Name, name.str() + "." + block.variables[ndx].name, shaderType, block.storage, program, block.variables[ndx].varType); } } // increment index if (!incrementMultiDimensionIndex(index, block.dimensions)) break; } } } static bool traverseVariablePath (std::vector<VariablePathComponent>& typePath, const char* subPath, const glu::VarType& type) { glu::VarTokenizer tokenizer(subPath); typePath.push_back(VariablePathComponent(&type)); if (tokenizer.getToken() == glu::VarTokenizer::TOKEN_END) return true; if (type.isStructType() && tokenizer.getToken() == glu::VarTokenizer::TOKEN_PERIOD) { tokenizer.advance(); // malformed path if (tokenizer.getToken() != glu::VarTokenizer::TOKEN_IDENTIFIER) return false; for (int memberNdx = 0; memberNdx < type.getStructPtr()->getNumMembers(); ++memberNdx) if (type.getStructPtr()->getMember(memberNdx).getName() == tokenizer.getIdentifier()) return traverseVariablePath(typePath, subPath + tokenizer.getCurrentTokenEndLocation(), type.getStructPtr()->getMember(memberNdx).getType()); // malformed path, no such member return false; } else if (type.isArrayType() && tokenizer.getToken() == glu::VarTokenizer::TOKEN_LEFT_BRACKET) { tokenizer.advance(); // malformed path if (tokenizer.getToken() != glu::VarTokenizer::TOKEN_NUMBER) return false; tokenizer.advance(); if (tokenizer.getToken() != glu::VarTokenizer::TOKEN_RIGHT_BRACKET) return false; return traverseVariablePath(typePath, subPath + tokenizer.getCurrentTokenEndLocation(), type.getElementType()); } return false; } static bool traverseVariablePath (std::vector<VariablePathComponent>& typePath, const std::string& path, const glu::VariableDeclaration& var) { if (glu::parseVariableName(path.c_str()) != var.name) return false; typePath.push_back(VariablePathComponent(&var)); return traverseVariablePath(typePath, path.c_str() + var.name.length(), var.varType); } static bool traverseShaderVariablePath (std::vector<VariablePathComponent>& typePath, const ProgramInterfaceDefinition::Shader* shader, const std::string& path, const VariableSearchFilter& filter) { // Default block variable? for (int varNdx = 0; varNdx < (int)shader->getDefaultBlock().variables.size(); ++varNdx) if (filter.matchesFilter(shader->getDefaultBlock().variables[varNdx])) if (traverseVariablePath(typePath, path, shader->getDefaultBlock().variables[varNdx])) return true; // is variable an interface block variable? { const std::string blockName = glu::parseVariableName(path.c_str()); for (int interfaceNdx = 0; interfaceNdx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++interfaceNdx) { if (!filter.matchesFilter(shader->getDefaultBlock().interfaceBlocks[interfaceNdx])) continue; if (shader->getDefaultBlock().interfaceBlocks[interfaceNdx].interfaceName == blockName) { // resource is a member of a named interface block // \note there is no array index specifier even if the interface is declared as an array of instances const std::string blockMemberPath = path.substr(blockName.size() + 1); const std::string blockMemeberName = glu::parseVariableName(blockMemberPath.c_str()); for (int varNdx = 0; varNdx < (int)shader->getDefaultBlock().interfaceBlocks[interfaceNdx].variables.size(); ++varNdx) { if (shader->getDefaultBlock().interfaceBlocks[interfaceNdx].variables[varNdx].name == blockMemeberName) { typePath.push_back(VariablePathComponent(&shader->getDefaultBlock().interfaceBlocks[interfaceNdx])); return traverseVariablePath(typePath, blockMemberPath, shader->getDefaultBlock().interfaceBlocks[interfaceNdx].variables[varNdx]); } } // terminate search return false; } else if (shader->getDefaultBlock().interfaceBlocks[interfaceNdx].instanceName.empty()) { const std::string blockMemeberName = glu::parseVariableName(path.c_str()); // unnamed block contains such variable? for (int varNdx = 0; varNdx < (int)shader->getDefaultBlock().interfaceBlocks[interfaceNdx].variables.size(); ++varNdx) { if (shader->getDefaultBlock().interfaceBlocks[interfaceNdx].variables[varNdx].name == blockMemeberName) { typePath.push_back(VariablePathComponent(&shader->getDefaultBlock().interfaceBlocks[interfaceNdx])); return traverseVariablePath(typePath, path, shader->getDefaultBlock().interfaceBlocks[interfaceNdx].variables[varNdx]); } } // continue search } } } return false; } static bool traverseProgramVariablePath (std::vector<VariablePathComponent>& typePath, const ProgramInterfaceDefinition::Program* program, const std::string& path, const VariableSearchFilter& filter) { for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx) { const ProgramInterfaceDefinition::Shader* shader = program->getShaders()[shaderNdx]; if (filter.matchesFilter(shader)) { // \note modifying output variable even when returning false typePath.clear(); if (traverseShaderVariablePath(typePath, shader, path, filter)) return true; } } return false; } static bool containsSubType (const glu::VarType& complexType, glu::DataType basicType) { if (complexType.isBasicType()) { return complexType.getBasicType() == basicType; } else if (complexType.isArrayType()) { return containsSubType(complexType.getElementType(), basicType); } else if (complexType.isStructType()) { for (int ndx = 0; ndx < complexType.getStructPtr()->getNumMembers(); ++ndx) if (containsSubType(complexType.getStructPtr()->getMember(ndx).getType(), basicType)) return true; return false; } else { DE_ASSERT(false); return false; } } static int getNumShaderBlocks (const ProgramInterfaceDefinition::Shader* shader, glu::Storage storage) { int retVal = 0; for (int ndx = 0; ndx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++ndx) { if (shader->getDefaultBlock().interfaceBlocks[ndx].storage == storage) { int numInstances = 1; for (int dimensionNdx = 0; dimensionNdx < (int)shader->getDefaultBlock().interfaceBlocks[ndx].dimensions.size(); ++dimensionNdx) numInstances *= shader->getDefaultBlock().interfaceBlocks[ndx].dimensions[dimensionNdx]; retVal += numInstances; } } return retVal; } static int getNumAtomicCounterBuffers (const ProgramInterfaceDefinition::Shader* shader) { std::set<int> buffers; for (int ndx = 0; ndx < (int)shader->getDefaultBlock().variables.size(); ++ndx) { if (containsSubType(shader->getDefaultBlock().variables[ndx].varType, glu::TYPE_UINT_ATOMIC_COUNTER)) { DE_ASSERT(shader->getDefaultBlock().variables[ndx].layout.binding != -1); buffers.insert(shader->getDefaultBlock().variables[ndx].layout.binding); } } return (int)buffers.size(); } template <typename DataTypeMap> static int accumulateComplexType (const glu::VarType& complexType, const DataTypeMap& dTypeMap) { if (complexType.isBasicType()) return dTypeMap(complexType.getBasicType()); else if (complexType.isArrayType()) { const int arraySize = (complexType.getArraySize() == glu::VarType::UNSIZED_ARRAY) ? (1) : (complexType.getArraySize()); return arraySize * accumulateComplexType(complexType.getElementType(), dTypeMap); } else if (complexType.isStructType()) { int sum = 0; for (int ndx = 0; ndx < complexType.getStructPtr()->getNumMembers(); ++ndx) sum += accumulateComplexType(complexType.getStructPtr()->getMember(ndx).getType(), dTypeMap); return sum; } else { DE_ASSERT(false); return false; } } template <typename InterfaceBlockFilter, typename VarDeclFilter, typename DataTypeMap> static int accumulateShader (const ProgramInterfaceDefinition::Shader* shader, const InterfaceBlockFilter& ibFilter, const VarDeclFilter& vdFilter, const DataTypeMap& dMap) { int retVal = 0; for (int ndx = 0; ndx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++ndx) { if (ibFilter(shader->getDefaultBlock().interfaceBlocks[ndx])) { int numInstances = 1; for (int dimensionNdx = 0; dimensionNdx < (int)shader->getDefaultBlock().interfaceBlocks[ndx].dimensions.size(); ++dimensionNdx) numInstances *= shader->getDefaultBlock().interfaceBlocks[ndx].dimensions[dimensionNdx]; for (int varNdx = 0; varNdx < (int)shader->getDefaultBlock().interfaceBlocks[ndx].variables.size(); ++varNdx) retVal += numInstances * accumulateComplexType(shader->getDefaultBlock().interfaceBlocks[ndx].variables[varNdx].varType, dMap); } } for (int varNdx = 0; varNdx < (int)shader->getDefaultBlock().variables.size(); ++varNdx) if (vdFilter(shader->getDefaultBlock().variables[varNdx])) retVal += accumulateComplexType(shader->getDefaultBlock().variables[varNdx].varType, dMap); return retVal; } static bool dummyTrueConstantTypeFilter (glu::DataType d) { DE_UNREF(d); return true; } class InstanceCounter { public: InstanceCounter (bool (*predicate)(glu::DataType)) : m_predicate(predicate) { } int operator() (glu::DataType t) const { return (m_predicate(t)) ? (1) : (0); } private: bool (*const m_predicate)(glu::DataType); }; class InterfaceBlockStorageFilter { public: InterfaceBlockStorageFilter (glu::Storage storage) : m_storage(storage) { } bool operator() (const glu::InterfaceBlock& b) const { return m_storage == b.storage; } private: const glu::Storage m_storage; }; class VariableDeclarationStorageFilter { public: VariableDeclarationStorageFilter (glu::Storage storage) : m_storage(storage) { } bool operator() (const glu::VariableDeclaration& d) const { return m_storage == d.storage; } private: const glu::Storage m_storage; }; static int getNumTypeInstances (const glu::VarType& complexType, bool (*predicate)(glu::DataType)) { return accumulateComplexType(complexType, InstanceCounter(predicate)); } static int getNumTypeInstances (const ProgramInterfaceDefinition::Shader* shader, glu::Storage storage, bool (*predicate)(glu::DataType)) { return accumulateShader(shader, InterfaceBlockStorageFilter(storage), VariableDeclarationStorageFilter(storage), InstanceCounter(predicate)); } static int getNumTypeInstances (const ProgramInterfaceDefinition::Shader* shader, glu::Storage storage) { return getNumTypeInstances(shader, storage, dummyTrueConstantTypeFilter); } static int accumulateShaderStorage (const ProgramInterfaceDefinition::Shader* shader, glu::Storage storage, int (*typeMap)(glu::DataType)) { return accumulateShader(shader, InterfaceBlockStorageFilter(storage), VariableDeclarationStorageFilter(storage), typeMap); } static int getNumDataTypeComponents (glu::DataType type) { if (glu::isDataTypeScalarOrVector(type) || glu::isDataTypeMatrix(type)) return glu::getDataTypeScalarSize(type); else return 0; } static int getNumDataTypeVectors (glu::DataType type) { if (glu::isDataTypeScalar(type)) return 1; else if (glu::isDataTypeVector(type)) return 1; else if (glu::isDataTypeMatrix(type)) return glu::getDataTypeMatrixNumColumns(type); else return 0; } static int getNumComponents (const ProgramInterfaceDefinition::Shader* shader, glu::Storage storage) { return accumulateShaderStorage(shader, storage, getNumDataTypeComponents); } static int getNumVectors (const ProgramInterfaceDefinition::Shader* shader, glu::Storage storage) { return accumulateShaderStorage(shader, storage, getNumDataTypeVectors); } static int getNumDefaultBlockComponents (const ProgramInterfaceDefinition::Shader* shader, glu::Storage storage) { int retVal = 0; for (int varNdx = 0; varNdx < (int)shader->getDefaultBlock().variables.size(); ++varNdx) if (shader->getDefaultBlock().variables[varNdx].storage == storage) retVal += accumulateComplexType(shader->getDefaultBlock().variables[varNdx].varType, getNumDataTypeComponents); return retVal; } static int getMaxBufferBinding (const ProgramInterfaceDefinition::Shader* shader, glu::Storage storage) { int maxBinding = -1; for (int ndx = 0; ndx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++ndx) { if (shader->getDefaultBlock().interfaceBlocks[ndx].storage == storage) { const int binding = (shader->getDefaultBlock().interfaceBlocks[ndx].layout.binding == -1) ? (0) : (shader->getDefaultBlock().interfaceBlocks[ndx].layout.binding); int numInstances = 1; for (int dimensionNdx = 0; dimensionNdx < (int)shader->getDefaultBlock().interfaceBlocks[ndx].dimensions.size(); ++dimensionNdx) numInstances *= shader->getDefaultBlock().interfaceBlocks[ndx].dimensions[dimensionNdx]; maxBinding = de::max(maxBinding, binding + numInstances - 1); } } return (int)maxBinding; } static int getBufferTypeSize (glu::DataType type, glu::MatrixOrder order) { // assume vec4 alignments, should produce values greater than or equal to the actual resource usage int numVectors = 0; if (glu::isDataTypeScalarOrVector(type)) numVectors = 1; else if (glu::isDataTypeMatrix(type) && order == glu::MATRIXORDER_ROW_MAJOR) numVectors = glu::getDataTypeMatrixNumRows(type); else if (glu::isDataTypeMatrix(type) && order != glu::MATRIXORDER_ROW_MAJOR) numVectors = glu::getDataTypeMatrixNumColumns(type); else DE_ASSERT(false); return 4 * numVectors; } static int getBufferVariableSize (const glu::VarType& type, glu::MatrixOrder order) { if (type.isBasicType()) return getBufferTypeSize(type.getBasicType(), order); else if (type.isArrayType()) { const int arraySize = (type.getArraySize() == glu::VarType::UNSIZED_ARRAY) ? (1) : (type.getArraySize()); return arraySize * getBufferVariableSize(type.getElementType(), order); } else if (type.isStructType()) { int sum = 0; for (int ndx = 0; ndx < type.getStructPtr()->getNumMembers(); ++ndx) sum += getBufferVariableSize(type.getStructPtr()->getMember(ndx).getType(), order); return sum; } else { DE_ASSERT(false); return false; } } static int getBufferSize (const glu::InterfaceBlock& block, glu::MatrixOrder blockOrder) { int size = 0; for (int ndx = 0; ndx < (int)block.variables.size(); ++ndx) size += getBufferVariableSize(block.variables[ndx].varType, (block.variables[ndx].layout.matrixOrder == glu::MATRIXORDER_LAST) ? (blockOrder) : (block.variables[ndx].layout.matrixOrder)); return size; } static int getBufferMaxSize (const ProgramInterfaceDefinition::Shader* shader, glu::Storage storage) { int maxSize = 0; for (int ndx = 0; ndx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++ndx) if (shader->getDefaultBlock().interfaceBlocks[ndx].storage == storage) maxSize = de::max(maxSize, getBufferSize(shader->getDefaultBlock().interfaceBlocks[ndx], shader->getDefaultBlock().interfaceBlocks[ndx].layout.matrixOrder)); return (int)maxSize; } static int getAtomicCounterMaxBinding (const ProgramInterfaceDefinition::Shader* shader) { int maxBinding = -1; for (int ndx = 0; ndx < (int)shader->getDefaultBlock().variables.size(); ++ndx) { if (containsSubType(shader->getDefaultBlock().variables[ndx].varType, glu::TYPE_UINT_ATOMIC_COUNTER)) { DE_ASSERT(shader->getDefaultBlock().variables[ndx].layout.binding != -1); maxBinding = de::max(maxBinding, shader->getDefaultBlock().variables[ndx].layout.binding); } } return (int)maxBinding; } static int getUniformMaxBinding (const ProgramInterfaceDefinition::Shader* shader, bool (*predicate)(glu::DataType)) { int maxBinding = -1; for (int ndx = 0; ndx < (int)shader->getDefaultBlock().variables.size(); ++ndx) { const int binding = (shader->getDefaultBlock().variables[ndx].layout.binding == -1) ? (0) : (shader->getDefaultBlock().variables[ndx].layout.binding); const int numInstances = getNumTypeInstances(shader->getDefaultBlock().variables[ndx].varType, predicate); maxBinding = de::max(maxBinding, binding + numInstances - 1); } return maxBinding; } static int getAtomicCounterMaxBufferSize (const ProgramInterfaceDefinition::Shader* shader) { std::map<int, int> bufferSizes; int maxSize = 0; for (int ndx = 0; ndx < (int)shader->getDefaultBlock().variables.size(); ++ndx) { if (containsSubType(shader->getDefaultBlock().variables[ndx].varType, glu::TYPE_UINT_ATOMIC_COUNTER)) { const int bufferBinding = shader->getDefaultBlock().variables[ndx].layout.binding; const int offset = (shader->getDefaultBlock().variables[ndx].layout.offset == -1) ? (0) : (shader->getDefaultBlock().variables[ndx].layout.offset); const int size = offset + 4 * getNumTypeInstances(shader->getDefaultBlock().variables[ndx].varType, glu::isDataTypeAtomicCounter); DE_ASSERT(shader->getDefaultBlock().variables[ndx].layout.binding != -1); if (bufferSizes.find(bufferBinding) == bufferSizes.end()) bufferSizes[bufferBinding] = size; else bufferSizes[bufferBinding] = de::max<int>(bufferSizes[bufferBinding], size); } } for (std::map<int, int>::iterator it = bufferSizes.begin(); it != bufferSizes.end(); ++it) maxSize = de::max<int>(maxSize, it->second); return maxSize; } static int getNumFeedbackVaryingComponents (const ProgramInterfaceDefinition::Program* program, const std::string& name) { std::vector<VariablePathComponent> path; if (name == "gl_Position") return 4; DE_ASSERT(deStringBeginsWith(name.c_str(), "gl_") == DE_FALSE); if (!traverseProgramVariablePath(path, program, name, VariableSearchFilter::createShaderTypeStorageFilter(getProgramTransformFeedbackStage(program), glu::STORAGE_OUT))) DE_ASSERT(false); // Program failed validate, invalid operation return accumulateComplexType(*path.back().getVariableType(), getNumDataTypeComponents); } static int getNumXFBComponents (const ProgramInterfaceDefinition::Program* program) { int numComponents = 0; for (int ndx = 0; ndx < (int)program->getTransformFeedbackVaryings().size(); ++ndx) numComponents += getNumFeedbackVaryingComponents(program, program->getTransformFeedbackVaryings()[ndx]); return numComponents; } static int getNumMaxXFBOutputComponents (const ProgramInterfaceDefinition::Program* program) { int numComponents = 0; for (int ndx = 0; ndx < (int)program->getTransformFeedbackVaryings().size(); ++ndx) numComponents = de::max(numComponents, getNumFeedbackVaryingComponents(program, program->getTransformFeedbackVaryings()[ndx])); return numComponents; } static int getFragmentOutputMaxLocation (const ProgramInterfaceDefinition::Shader* shader) { DE_ASSERT(shader->getType() == glu::SHADERTYPE_FRAGMENT); int maxOutputLocation = -1; for (int ndx = 0; ndx < (int)shader->getDefaultBlock().variables.size(); ++ndx) { if (shader->getDefaultBlock().variables[ndx].storage == glu::STORAGE_OUT) { // missing location qualifier means location == 0 const int outputLocation = (shader->getDefaultBlock().variables[ndx].layout.location == -1) ? (0) : (shader->getDefaultBlock().variables[ndx].layout.location); // only basic types or arrays of basic types possible DE_ASSERT(!shader->getDefaultBlock().variables[ndx].varType.isStructType()); const int locationSlotsTaken = (shader->getDefaultBlock().variables[ndx].varType.isArrayType()) ? (shader->getDefaultBlock().variables[ndx].varType.getArraySize()) : (1); maxOutputLocation = de::max(maxOutputLocation, outputLocation + locationSlotsTaken - 1); } } return maxOutputLocation; } } // anonymous std::vector<std::string> getProgramInterfaceBlockMemberResourceList (const glu::InterfaceBlock& interfaceBlock) { const std::string namePrefix = (!interfaceBlock.instanceName.empty()) ? (interfaceBlock.interfaceName + ".") : (""); const bool isTopLevelBufferVariable = (interfaceBlock.storage == glu::STORAGE_BUFFER); std::vector<std::string> resources; // \note this is defined in the GLSL spec, not in the GL spec for (int variableNdx = 0; variableNdx < (int)interfaceBlock.variables.size(); ++variableNdx) generateVariableTypeResourceNames(resources, namePrefix + interfaceBlock.variables[variableNdx].name, interfaceBlock.variables[variableNdx].varType, (isTopLevelBufferVariable) ? (RESOURCE_NAME_GENERATION_FLAG_TOP_LEVEL_BUFFER_VARIABLE) : (RESOURCE_NAME_GENERATION_FLAG_DEFAULT)); return resources; } std::vector<std::string> getProgramInterfaceResourceList (const ProgramInterfaceDefinition::Program* program, ProgramInterface interface) { // The same {uniform (block), buffer (variable)} can exist in multiple shaders, remove duplicates but keep order const bool removeDuplicated = (interface == PROGRAMINTERFACE_UNIFORM) || (interface == PROGRAMINTERFACE_UNIFORM_BLOCK) || (interface == PROGRAMINTERFACE_BUFFER_VARIABLE) || (interface == PROGRAMINTERFACE_SHADER_STORAGE_BLOCK); std::vector<std::string> resources; switch (interface) { case PROGRAMINTERFACE_UNIFORM: case PROGRAMINTERFACE_BUFFER_VARIABLE: { const glu::Storage storage = (interface == PROGRAMINTERFACE_UNIFORM) ? (glu::STORAGE_UNIFORM) : (glu::STORAGE_BUFFER); for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx) { const ProgramInterfaceDefinition::Shader* shader = program->getShaders()[shaderNdx]; for (int variableNdx = 0; variableNdx < (int)shader->getDefaultBlock().variables.size(); ++variableNdx) if (shader->getDefaultBlock().variables[variableNdx].storage == storage) generateVariableTypeResourceNames(resources, shader->getDefaultBlock().variables[variableNdx].name, shader->getDefaultBlock().variables[variableNdx].varType, RESOURCE_NAME_GENERATION_FLAG_DEFAULT); for (int interfaceNdx = 0; interfaceNdx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++interfaceNdx) { const glu::InterfaceBlock& interfaceBlock = shader->getDefaultBlock().interfaceBlocks[interfaceNdx]; if (interfaceBlock.storage == storage) { const std::vector<std::string> blockResources = getProgramInterfaceBlockMemberResourceList(interfaceBlock); resources.insert(resources.end(), blockResources.begin(), blockResources.end()); } } } break; } case PROGRAMINTERFACE_UNIFORM_BLOCK: case PROGRAMINTERFACE_SHADER_STORAGE_BLOCK: { const glu::Storage storage = (interface == PROGRAMINTERFACE_UNIFORM_BLOCK) ? (glu::STORAGE_UNIFORM) : (glu::STORAGE_BUFFER); for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx) { const ProgramInterfaceDefinition::Shader* shader = program->getShaders()[shaderNdx]; for (int interfaceNdx = 0; interfaceNdx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++interfaceNdx) { const glu::InterfaceBlock& interfaceBlock = shader->getDefaultBlock().interfaceBlocks[interfaceNdx]; if (interfaceBlock.storage == storage) { std::vector<int> index(interfaceBlock.dimensions.size(), 0); for (;;) { // add resource string for each element { std::ostringstream name; name << interfaceBlock.interfaceName; for (int dimensionNdx = 0; dimensionNdx < (int)interfaceBlock.dimensions.size(); ++dimensionNdx) name << "[" << index[dimensionNdx] << "]"; resources.push_back(name.str()); } // increment index if (!incrementMultiDimensionIndex(index, interfaceBlock.dimensions)) break; } } } } break; } case PROGRAMINTERFACE_PROGRAM_INPUT: case PROGRAMINTERFACE_PROGRAM_OUTPUT: { const glu::Storage queryStorage = (interface == PROGRAMINTERFACE_PROGRAM_INPUT) ? (glu::STORAGE_IN) : (glu::STORAGE_OUT); const glu::Storage queryPatchStorage = (interface == PROGRAMINTERFACE_PROGRAM_INPUT) ? (glu::STORAGE_PATCH_IN) : (glu::STORAGE_PATCH_OUT); const glu::ShaderType shaderType = (interface == PROGRAMINTERFACE_PROGRAM_INPUT) ? (program->getFirstStage()) : (program->getLastStage()); for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx) { const ProgramInterfaceDefinition::Shader* shader = program->getShaders()[shaderNdx]; if (shader->getType() != shaderType) continue; for (int variableNdx = 0; variableNdx < (int)shader->getDefaultBlock().variables.size(); ++variableNdx) { const glu::Storage variableStorage = shader->getDefaultBlock().variables[variableNdx].storage; if (variableStorage == queryStorage || variableStorage == queryPatchStorage) generateVariableTypeResourceNames(resources, shader->getDefaultBlock().variables[variableNdx].name, shader->getDefaultBlock().variables[variableNdx].varType, RESOURCE_NAME_GENERATION_FLAG_DEFAULT); } for (int interfaceNdx = 0; interfaceNdx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++interfaceNdx) { const glu::InterfaceBlock& interfaceBlock = shader->getDefaultBlock().interfaceBlocks[interfaceNdx]; if (interfaceBlock.storage == queryStorage || interfaceBlock.storage == queryPatchStorage) { const std::vector<std::string> blockResources = getProgramInterfaceBlockMemberResourceList(interfaceBlock); resources.insert(resources.end(), blockResources.begin(), blockResources.end()); } } } // built-ins if (interface == PROGRAMINTERFACE_PROGRAM_INPUT) { if (shaderType == glu::SHADERTYPE_VERTEX && resources.empty()) resources.push_back("gl_VertexID"); // only read from when there are no other inputs else if (shaderType == glu::SHADERTYPE_FRAGMENT && resources.empty()) resources.push_back("gl_FragCoord"); // only read from when there are no other inputs else if (shaderType == glu::SHADERTYPE_GEOMETRY) resources.push_back("gl_PerVertex.gl_Position"); else if (shaderType == glu::SHADERTYPE_TESSELLATION_CONTROL) { resources.push_back("gl_InvocationID"); resources.push_back("gl_PerVertex.gl_Position"); } else if (shaderType == glu::SHADERTYPE_TESSELLATION_EVALUATION) resources.push_back("gl_PerVertex.gl_Position"); else if (shaderType == glu::SHADERTYPE_COMPUTE && resources.empty()) resources.push_back("gl_NumWorkGroups"); // only read from when there are no other inputs } else if (interface == PROGRAMINTERFACE_PROGRAM_OUTPUT) { if (shaderType == glu::SHADERTYPE_VERTEX) resources.push_back("gl_Position"); else if (shaderType == glu::SHADERTYPE_FRAGMENT && resources.empty()) resources.push_back("gl_FragDepth"); // only written to when there are no other outputs else if (shaderType == glu::SHADERTYPE_GEOMETRY) resources.push_back("gl_Position"); else if (shaderType == glu::SHADERTYPE_TESSELLATION_CONTROL) { resources.push_back("gl_PerVertex.gl_Position"); resources.push_back("gl_TessLevelOuter[0]"); resources.push_back("gl_TessLevelInner[0]"); } else if (shaderType == glu::SHADERTYPE_TESSELLATION_EVALUATION) resources.push_back("gl_Position"); } break; } case PROGRAMINTERFACE_TRANSFORM_FEEDBACK_VARYING: { const glu::ShaderType xfbStage = getProgramTransformFeedbackStage(program); for (int varyingNdx = 0; varyingNdx < (int)program->getTransformFeedbackVaryings().size(); ++varyingNdx) { const std::string& varyingName = program->getTransformFeedbackVaryings()[varyingNdx]; if (deStringBeginsWith(varyingName.c_str(), "gl_")) resources.push_back(varyingName); // builtin else { std::vector<VariablePathComponent> path; if (!traverseProgramVariablePath(path, program, varyingName, VariableSearchFilter::createShaderTypeStorageFilter(xfbStage, glu::STORAGE_OUT))) DE_ASSERT(false); // Program failed validate, invalid operation generateVariableTypeResourceNames(resources, varyingName, *path.back().getVariableType(), RESOURCE_NAME_GENERATION_FLAG_TRANSFORM_FEEDBACK_VARIABLE); } } break; } default: DE_ASSERT(false); } if (removeDuplicated) { std::set<std::string> addedVariables; std::vector<std::string> uniqueResouces; for (int ndx = 0; ndx < (int)resources.size(); ++ndx) { if (addedVariables.find(resources[ndx]) == addedVariables.end()) { addedVariables.insert(resources[ndx]); uniqueResouces.push_back(resources[ndx]); } } uniqueResouces.swap(resources); } return resources; } /** * Name of the dummy uniform added by generateProgramInterfaceProgramSources * * A uniform named "dummyZero" is added by * generateProgramInterfaceProgramSources. It is used in expressions to * prevent various program resources from being eliminated by the GLSL * compiler's optimizer. * * \sa deqp::gles31::Functional::ProgramInterfaceDefinition::generateProgramInterfaceProgramSources */ const char* getDummyZeroUniformName() { return "dummyZero"; } glu::ProgramSources generateProgramInterfaceProgramSources (const ProgramInterfaceDefinition::Program* program) { glu::ProgramSources sources; DE_ASSERT(program->isValid()); for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx) { const ProgramInterfaceDefinition::Shader* shader = program->getShaders()[shaderNdx]; bool containsUserDefinedOutputs = false; bool containsUserDefinedInputs = false; std::ostringstream sourceBuf; std::ostringstream usageBuf; sourceBuf << glu::getGLSLVersionDeclaration(shader->getVersion()) << "\n" << getShaderExtensionDeclarations(shader) << getShaderTypeDeclarations(program, shader->getType()) << "\n"; // Struct definitions writeStructureDefinitions(sourceBuf, shader->getDefaultBlock()); // variables in the default scope for (int ndx = 0; ndx < (int)shader->getDefaultBlock().variables.size(); ++ndx) sourceBuf << shader->getDefaultBlock().variables[ndx] << ";\n"; if (!shader->getDefaultBlock().variables.empty()) sourceBuf << "\n"; // Interface blocks for (int ndx = 0; ndx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++ndx) writeInterfaceBlock(sourceBuf, shader->getDefaultBlock().interfaceBlocks[ndx]); // Use inputs and outputs so that they won't be removed by the optimizer usageBuf << "highp uniform vec4 " << getDummyZeroUniformName() << "; // Default value is vec4(0.0).\n" "highp vec4 readInputs()\n" "{\n" " highp vec4 retValue = " << getDummyZeroUniformName() << ";\n"; // User-defined inputs for (int ndx = 0; ndx < (int)shader->getDefaultBlock().variables.size(); ++ndx) { if (shader->getDefaultBlock().variables[ndx].storage == glu::STORAGE_IN || shader->getDefaultBlock().variables[ndx].storage == glu::STORAGE_PATCH_IN || shader->getDefaultBlock().variables[ndx].storage == glu::STORAGE_UNIFORM) { writeVariableReadAccumulateExpression(usageBuf, "retValue", shader->getDefaultBlock().variables[ndx].name, shader->getType(), shader->getDefaultBlock().variables[ndx].storage, program, shader->getDefaultBlock().variables[ndx].varType); containsUserDefinedInputs = true; } } for (int interfaceNdx = 0; interfaceNdx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++interfaceNdx) { const glu::InterfaceBlock& interface = shader->getDefaultBlock().interfaceBlocks[interfaceNdx]; if (isReadableInterface(interface)) { writeInterfaceReadAccumulateExpression(usageBuf, "retValue", interface, shader->getType(), program); containsUserDefinedInputs = true; } } // Built-in-inputs switch (shader->getType()) { case glu::SHADERTYPE_VERTEX: // make readInputs to never be compile time constant if (!containsUserDefinedInputs) usageBuf << " retValue += vec4(float(gl_VertexID));\n"; break; case glu::SHADERTYPE_FRAGMENT: // make readInputs to never be compile time constant if (!containsUserDefinedInputs) usageBuf << " retValue += gl_FragCoord;\n"; break; case glu::SHADERTYPE_GEOMETRY: // always use previous stage's output values so that previous stage won't be optimized out usageBuf << " retValue += gl_in[0].gl_Position;\n"; break; case glu::SHADERTYPE_TESSELLATION_CONTROL: // always use previous stage's output values so that previous stage won't be optimized out usageBuf << " retValue += gl_in[0].gl_Position;\n"; break; case glu::SHADERTYPE_TESSELLATION_EVALUATION: // always use previous stage's output values so that previous stage won't be optimized out usageBuf << " retValue += gl_in[0].gl_Position;\n"; break; case glu::SHADERTYPE_COMPUTE: // make readInputs to never be compile time constant if (!containsUserDefinedInputs) usageBuf << " retValue += vec4(float(gl_NumWorkGroups.x));\n"; break; default: DE_ASSERT(false); } usageBuf << " return retValue;\n" "}\n\n"; usageBuf << "void writeOutputs(in highp vec4 dummyValue)\n" "{\n"; // User-defined outputs for (int ndx = 0; ndx < (int)shader->getDefaultBlock().variables.size(); ++ndx) { if (shader->getDefaultBlock().variables[ndx].storage == glu::STORAGE_OUT || shader->getDefaultBlock().variables[ndx].storage == glu::STORAGE_PATCH_OUT) { writeVariableWriteExpression(usageBuf, "dummyValue", shader->getDefaultBlock().variables[ndx].name, shader->getType(), shader->getDefaultBlock().variables[ndx].storage, program, shader->getDefaultBlock().variables[ndx].varType); containsUserDefinedOutputs = true; } } for (int interfaceNdx = 0; interfaceNdx < (int)shader->getDefaultBlock().interfaceBlocks.size(); ++interfaceNdx) { const glu::InterfaceBlock& interface = shader->getDefaultBlock().interfaceBlocks[interfaceNdx]; if (isWritableInterface(interface)) { writeInterfaceWriteExpression(usageBuf, "dummyValue", interface, shader->getType(), program); containsUserDefinedOutputs = true; } } // Builtin-outputs that must be written to if (shader->getType() == glu::SHADERTYPE_VERTEX) usageBuf << " gl_Position = dummyValue;\n"; else if (shader->getType() == glu::SHADERTYPE_GEOMETRY) usageBuf << " gl_Position = dummyValue;\n" " EmitVertex();\n"; else if (shader->getType() == glu::SHADERTYPE_TESSELLATION_CONTROL) usageBuf << " gl_out[gl_InvocationID].gl_Position = dummyValue;\n" " gl_TessLevelOuter[0] = 2.8;\n" " gl_TessLevelOuter[1] = 2.8;\n" " gl_TessLevelOuter[2] = 2.8;\n" " gl_TessLevelOuter[3] = 2.8;\n" " gl_TessLevelInner[0] = 2.8;\n" " gl_TessLevelInner[1] = 2.8;\n"; else if (shader->getType() == glu::SHADERTYPE_TESSELLATION_EVALUATION) usageBuf << " gl_Position = dummyValue;\n"; // Output to sink input data to if (!containsUserDefinedOutputs) { if (shader->getType() == glu::SHADERTYPE_FRAGMENT) usageBuf << " gl_FragDepth = dot(dummyValue.xy, dummyValue.xw);\n"; else if (shader->getType() == glu::SHADERTYPE_COMPUTE) usageBuf << " dummyOutputBlock.dummyValue = dummyValue;\n"; } usageBuf << "}\n\n" "void main()\n" "{\n" " writeOutputs(readInputs());\n" "}\n"; // Interface for dummy output if (shader->getType() == glu::SHADERTYPE_COMPUTE && !containsUserDefinedOutputs) { sourceBuf << "writeonly buffer DummyOutputInterface\n" << "{\n" << " highp vec4 dummyValue;\n" << "} dummyOutputBlock;\n\n"; } sources << glu::ShaderSource(shader->getType(), sourceBuf.str() + usageBuf.str()); } if (program->isSeparable()) sources << glu::ProgramSeparable(true); for (int ndx = 0; ndx < (int)program->getTransformFeedbackVaryings().size(); ++ndx) sources << glu::TransformFeedbackVarying(program->getTransformFeedbackVaryings()[ndx]); if (program->getTransformFeedbackMode()) sources << glu::TransformFeedbackMode(program->getTransformFeedbackMode()); return sources; } bool findProgramVariablePathByPathName (std::vector<VariablePathComponent>& typePath, const ProgramInterfaceDefinition::Program* program, const std::string& pathName, const VariableSearchFilter& filter) { std::vector<VariablePathComponent> modifiedPath; if (!traverseProgramVariablePath(modifiedPath, program, pathName, filter)) return false; // modify param only on success typePath.swap(modifiedPath); return true; } ProgramInterfaceDefinition::ShaderResourceUsage getShaderResourceUsage (const ProgramInterfaceDefinition::Program* program, const ProgramInterfaceDefinition::Shader* shader) { ProgramInterfaceDefinition::ShaderResourceUsage retVal; retVal.numInputs = getNumTypeInstances(shader, glu::STORAGE_IN); retVal.numInputVectors = getNumVectors(shader, glu::STORAGE_IN); retVal.numInputComponents = getNumComponents(shader, glu::STORAGE_IN); retVal.numOutputs = getNumTypeInstances(shader, glu::STORAGE_OUT); retVal.numOutputVectors = getNumVectors(shader, glu::STORAGE_OUT); retVal.numOutputComponents = getNumComponents(shader, glu::STORAGE_OUT); retVal.numPatchInputComponents = getNumComponents(shader, glu::STORAGE_PATCH_IN); retVal.numPatchOutputComponents = getNumComponents(shader, glu::STORAGE_PATCH_OUT); retVal.numDefaultBlockUniformComponents = getNumDefaultBlockComponents(shader, glu::STORAGE_UNIFORM); retVal.numCombinedUniformComponents = getNumComponents(shader, glu::STORAGE_UNIFORM); retVal.numUniformVectors = getNumVectors(shader, glu::STORAGE_UNIFORM); retVal.numSamplers = getNumTypeInstances(shader, glu::STORAGE_UNIFORM, glu::isDataTypeSampler); retVal.numImages = getNumTypeInstances(shader, glu::STORAGE_UNIFORM, glu::isDataTypeImage); retVal.numAtomicCounterBuffers = getNumAtomicCounterBuffers(shader); retVal.numAtomicCounters = getNumTypeInstances(shader, glu::STORAGE_UNIFORM, glu::isDataTypeAtomicCounter); retVal.numUniformBlocks = getNumShaderBlocks(shader, glu::STORAGE_UNIFORM); retVal.numShaderStorageBlocks = getNumShaderBlocks(shader, glu::STORAGE_BUFFER); // add builtins switch (shader->getType()) { case glu::SHADERTYPE_VERTEX: // gl_Position is not counted break; case glu::SHADERTYPE_FRAGMENT: // nada break; case glu::SHADERTYPE_GEOMETRY: // gl_Position in (point mode => size 1) retVal.numInputs += 1; retVal.numInputVectors += 1; retVal.numInputComponents += 4; // gl_Position out retVal.numOutputs += 1; retVal.numOutputVectors += 1; retVal.numOutputComponents += 4; break; case glu::SHADERTYPE_TESSELLATION_CONTROL: // gl_Position in is read up to gl_InstanceID retVal.numInputs += 1 * program->getTessellationNumOutputPatchVertices(); retVal.numInputVectors += 1 * program->getTessellationNumOutputPatchVertices(); retVal.numInputComponents += 4 * program->getTessellationNumOutputPatchVertices(); // gl_Position out, size = num patch out vertices retVal.numOutputs += 1 * program->getTessellationNumOutputPatchVertices(); retVal.numOutputVectors += 1 * program->getTessellationNumOutputPatchVertices(); retVal.numOutputComponents += 4 * program->getTessellationNumOutputPatchVertices(); break; case glu::SHADERTYPE_TESSELLATION_EVALUATION: // gl_Position in is read up to gl_InstanceID retVal.numInputs += 1 * program->getTessellationNumOutputPatchVertices(); retVal.numInputVectors += 1 * program->getTessellationNumOutputPatchVertices(); retVal.numInputComponents += 4 * program->getTessellationNumOutputPatchVertices(); // gl_Position out retVal.numOutputs += 1; retVal.numOutputVectors += 1; retVal.numOutputComponents += 4; break; case glu::SHADERTYPE_COMPUTE: // nada break; default: DE_ASSERT(false); break; } return retVal; } ProgramInterfaceDefinition::ProgramResourceUsage getCombinedProgramResourceUsage (const ProgramInterfaceDefinition::Program* program) { ProgramInterfaceDefinition::ProgramResourceUsage retVal; int numVertexOutputComponents = 0; int numFragmentInputComponents = 0; int numVertexOutputVectors = 0; int numFragmentInputVectors = 0; retVal.uniformBufferMaxBinding = -1; // max binding is inclusive upper bound. Allow 0 bindings by using negative value retVal.uniformBufferMaxSize = 0; retVal.numUniformBlocks = 0; retVal.numCombinedVertexUniformComponents = 0; retVal.numCombinedFragmentUniformComponents = 0; retVal.numCombinedGeometryUniformComponents = 0; retVal.numCombinedTessControlUniformComponents = 0; retVal.numCombinedTessEvalUniformComponents = 0; retVal.shaderStorageBufferMaxBinding = -1; // see above retVal.shaderStorageBufferMaxSize = 0; retVal.numShaderStorageBlocks = 0; retVal.numVaryingComponents = 0; retVal.numVaryingVectors = 0; retVal.numCombinedSamplers = 0; retVal.atomicCounterBufferMaxBinding = -1; // see above retVal.atomicCounterBufferMaxSize = 0; retVal.numAtomicCounterBuffers = 0; retVal.numAtomicCounters = 0; retVal.maxImageBinding = -1; // see above retVal.numCombinedImages = 0; retVal.numCombinedOutputResources = 0; retVal.numXFBInterleavedComponents = 0; retVal.numXFBSeparateAttribs = 0; retVal.numXFBSeparateComponents = 0; retVal.fragmentOutputMaxBinding = -1; // see above for (int shaderNdx = 0; shaderNdx < (int)program->getShaders().size(); ++shaderNdx) { const ProgramInterfaceDefinition::Shader* const shader = program->getShaders()[shaderNdx]; retVal.uniformBufferMaxBinding = de::max(retVal.uniformBufferMaxBinding, getMaxBufferBinding(shader, glu::STORAGE_UNIFORM)); retVal.uniformBufferMaxSize = de::max(retVal.uniformBufferMaxSize, getBufferMaxSize(shader, glu::STORAGE_UNIFORM)); retVal.numUniformBlocks += getNumShaderBlocks(shader, glu::STORAGE_UNIFORM); switch (shader->getType()) { case glu::SHADERTYPE_VERTEX: retVal.numCombinedVertexUniformComponents += getNumComponents(shader, glu::STORAGE_UNIFORM); break; case glu::SHADERTYPE_FRAGMENT: retVal.numCombinedFragmentUniformComponents += getNumComponents(shader, glu::STORAGE_UNIFORM); break; case glu::SHADERTYPE_GEOMETRY: retVal.numCombinedGeometryUniformComponents += getNumComponents(shader, glu::STORAGE_UNIFORM); break; case glu::SHADERTYPE_TESSELLATION_CONTROL: retVal.numCombinedTessControlUniformComponents += getNumComponents(shader, glu::STORAGE_UNIFORM); break; case glu::SHADERTYPE_TESSELLATION_EVALUATION: retVal.numCombinedTessEvalUniformComponents += getNumComponents(shader, glu::STORAGE_UNIFORM); break; default: break; } retVal.shaderStorageBufferMaxBinding = de::max(retVal.shaderStorageBufferMaxBinding, getMaxBufferBinding(shader, glu::STORAGE_BUFFER)); retVal.shaderStorageBufferMaxSize = de::max(retVal.shaderStorageBufferMaxSize, getBufferMaxSize(shader, glu::STORAGE_BUFFER)); retVal.numShaderStorageBlocks += getNumShaderBlocks(shader, glu::STORAGE_BUFFER); if (shader->getType() == glu::SHADERTYPE_VERTEX) { numVertexOutputComponents += getNumComponents(shader, glu::STORAGE_OUT); numVertexOutputVectors += getNumVectors(shader, glu::STORAGE_OUT); } else if (shader->getType() == glu::SHADERTYPE_FRAGMENT) { numFragmentInputComponents += getNumComponents(shader, glu::STORAGE_IN); numFragmentInputVectors += getNumVectors(shader, glu::STORAGE_IN); } retVal.numCombinedSamplers += getNumTypeInstances(shader, glu::STORAGE_UNIFORM, glu::isDataTypeSampler); retVal.atomicCounterBufferMaxBinding = de::max(retVal.atomicCounterBufferMaxBinding, getAtomicCounterMaxBinding(shader)); retVal.atomicCounterBufferMaxSize = de::max(retVal.atomicCounterBufferMaxSize, getAtomicCounterMaxBufferSize(shader)); retVal.numAtomicCounterBuffers += getNumAtomicCounterBuffers(shader); retVal.numAtomicCounters += getNumTypeInstances(shader, glu::STORAGE_UNIFORM, glu::isDataTypeAtomicCounter); retVal.maxImageBinding = de::max(retVal.maxImageBinding, getUniformMaxBinding(shader, glu::isDataTypeImage)); retVal.numCombinedImages += getNumTypeInstances(shader, glu::STORAGE_UNIFORM, glu::isDataTypeImage); retVal.numCombinedOutputResources += getNumTypeInstances(shader, glu::STORAGE_UNIFORM, glu::isDataTypeImage); retVal.numCombinedOutputResources += getNumShaderBlocks(shader, glu::STORAGE_BUFFER); if (shader->getType() == glu::SHADERTYPE_FRAGMENT) { retVal.numCombinedOutputResources += getNumVectors(shader, glu::STORAGE_OUT); retVal.fragmentOutputMaxBinding = de::max(retVal.fragmentOutputMaxBinding, getFragmentOutputMaxLocation(shader)); } } if (program->getTransformFeedbackMode() == GL_INTERLEAVED_ATTRIBS) retVal.numXFBInterleavedComponents = getNumXFBComponents(program); else if (program->getTransformFeedbackMode() == GL_SEPARATE_ATTRIBS) { retVal.numXFBSeparateAttribs = (int)program->getTransformFeedbackVaryings().size(); retVal.numXFBSeparateComponents = getNumMaxXFBOutputComponents(program); } // legacy limits retVal.numVaryingComponents = de::max(numVertexOutputComponents, numFragmentInputComponents); retVal.numVaryingVectors = de::max(numVertexOutputVectors, numFragmentInputVectors); return retVal; } } // Functional } // gles31 } // deqp