| /*------------------------------------------------------------------------- |
| * 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 unusedTrueConstantTypeFilter (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, unusedTrueConstantTypeFilter); |
| } |
| |
| 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 unused uniform added by generateProgramInterfaceProgramSources |
| * |
| * A uniform named "unusedZero" 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* getUnusedZeroUniformName() |
| { |
| return "unusedZero"; |
| } |
| |
| 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 " << getUnusedZeroUniformName() << "; // Default value is vec4(0.0).\n" |
| "highp vec4 readInputs()\n" |
| "{\n" |
| " highp vec4 retValue = " << getUnusedZeroUniformName() << ";\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 unusedValue)\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, |
| "unusedValue", |
| 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, "unusedValue", interface, shader->getType(), program); |
| containsUserDefinedOutputs = true; |
| } |
| } |
| |
| // Builtin-outputs that must be written to |
| |
| if (shader->getType() == glu::SHADERTYPE_VERTEX) |
| usageBuf << " gl_Position = unusedValue;\n"; |
| else if (shader->getType() == glu::SHADERTYPE_GEOMETRY) |
| usageBuf << " gl_Position = unusedValue;\n" |
| " EmitVertex();\n"; |
| else if (shader->getType() == glu::SHADERTYPE_TESSELLATION_CONTROL) |
| usageBuf << " gl_out[gl_InvocationID].gl_Position = unusedValue;\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 = unusedValue;\n"; |
| |
| // Output to sink input data to |
| |
| if (!containsUserDefinedOutputs) |
| { |
| if (shader->getType() == glu::SHADERTYPE_FRAGMENT) |
| usageBuf << " gl_FragDepth = dot(unusedValue.xy, unusedValue.xw);\n"; |
| else if (shader->getType() == glu::SHADERTYPE_COMPUTE) |
| usageBuf << " unusedOutputBlock.unusedValue = unusedValue;\n"; |
| } |
| |
| usageBuf << "}\n\n" |
| "void main()\n" |
| "{\n" |
| " writeOutputs(readInputs());\n" |
| "}\n"; |
| |
| // Interface for unused output |
| |
| if (shader->getType() == glu::SHADERTYPE_COMPUTE && !containsUserDefinedOutputs) |
| { |
| sourceBuf << "writeonly buffer UnusedOutputInterface\n" |
| << "{\n" |
| << " highp vec4 unusedValue;\n" |
| << "} unusedOutputBlock;\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 |