| /*------------------------------------------------------------------------- |
| * 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 |
| *//*--------------------------------------------------------------------*/ |
| |
| #include "es31fProgramInterfaceDefinition.hpp" |
| #include "es31fProgramInterfaceDefinitionUtil.hpp" |
| #include "gluVarType.hpp" |
| #include "gluShaderProgram.hpp" |
| #include "deSTLUtil.hpp" |
| #include "deStringUtil.hpp" |
| #include "glwEnums.hpp" |
| |
| #include <set> |
| |
| namespace deqp |
| { |
| namespace gles31 |
| { |
| namespace Functional |
| { |
| namespace ProgramInterfaceDefinition |
| { |
| namespace |
| { |
| |
| static const glu::ShaderType s_shaderStageOrder[] = |
| { |
| glu::SHADERTYPE_COMPUTE, |
| |
| glu::SHADERTYPE_VERTEX, |
| glu::SHADERTYPE_TESSELLATION_CONTROL, |
| glu::SHADERTYPE_TESSELLATION_EVALUATION, |
| glu::SHADERTYPE_GEOMETRY, |
| glu::SHADERTYPE_FRAGMENT, |
| glu::SHADERTYPE_RAYGEN, |
| glu::SHADERTYPE_ANY_HIT, |
| glu::SHADERTYPE_CLOSEST_HIT, |
| glu::SHADERTYPE_MISS, |
| glu::SHADERTYPE_INTERSECTION, |
| glu::SHADERTYPE_CALLABLE, |
| }; |
| |
| // s_shaderStageOrder does not contain ShaderType_LAST |
| DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_shaderStageOrder) == glu::SHADERTYPE_LAST); |
| |
| static bool containsMatchingSubtype (const glu::VarType& varType, bool (*predicate)(glu::DataType)) |
| { |
| if (varType.isBasicType() && predicate(varType.getBasicType())) |
| return true; |
| |
| if (varType.isArrayType()) |
| return containsMatchingSubtype(varType.getElementType(), predicate); |
| |
| if (varType.isStructType()) |
| for (int memberNdx = 0; memberNdx < varType.getStructPtr()->getNumMembers(); ++memberNdx) |
| if (containsMatchingSubtype(varType.getStructPtr()->getMember(memberNdx).getType(), predicate)) |
| return true; |
| |
| return false; |
| } |
| |
| static bool containsMatchingSubtype (const std::vector<glu::VariableDeclaration>& decls, bool (*predicate)(glu::DataType)) |
| { |
| for (int varNdx = 0; varNdx < (int)decls.size(); ++varNdx) |
| if (containsMatchingSubtype(decls[varNdx].varType, predicate)) |
| return true; |
| return false; |
| } |
| |
| static bool isOpaqueType (glu::DataType type) |
| { |
| return glu::isDataTypeAtomicCounter(type) || |
| glu::isDataTypeImage(type) || |
| glu::isDataTypeSampler(type); |
| } |
| |
| static int getShaderStageIndex (glu::ShaderType stage) |
| { |
| const glu::ShaderType* const it = std::find(DE_ARRAY_BEGIN(s_shaderStageOrder), DE_ARRAY_END(s_shaderStageOrder), stage); |
| |
| if (it == DE_ARRAY_END(s_shaderStageOrder)) |
| return -1; |
| else |
| { |
| const int index = (int)(it - DE_ARRAY_BEGIN(s_shaderStageOrder)); |
| return index; |
| } |
| } |
| |
| } // anonymous |
| |
| Shader::Shader (glu::ShaderType type, glu::GLSLVersion version) |
| : m_shaderType (type) |
| , m_version (version) |
| { |
| } |
| |
| Shader::~Shader (void) |
| { |
| } |
| |
| static bool isIllegalVertexInput (const glu::VarType& varType) |
| { |
| // booleans, opaque types, arrays, structs are not allowed as inputs |
| if (!varType.isBasicType()) |
| return true; |
| if (glu::isDataTypeBoolOrBVec(varType.getBasicType())) |
| return true; |
| return false; |
| } |
| |
| static bool isIllegalVertexOutput (const glu::VarType& varType, bool insideAStruct = false, bool insideAnArray = false) |
| { |
| // booleans, opaque types, arrays of arrays, arrays of structs, array in struct, struct struct are not allowed as vertex outputs |
| |
| if (varType.isBasicType()) |
| { |
| const bool isOpaqueType = !glu::isDataTypeScalar(varType.getBasicType()) && !glu::isDataTypeVector(varType.getBasicType()) && !glu::isDataTypeMatrix(varType.getBasicType()); |
| |
| if (glu::isDataTypeBoolOrBVec(varType.getBasicType())) |
| return true; |
| |
| if (isOpaqueType) |
| return true; |
| |
| return false; |
| } |
| else if (varType.isArrayType()) |
| { |
| if (insideAnArray || insideAStruct) |
| return true; |
| |
| return isIllegalVertexOutput(varType.getElementType(), insideAStruct, true); |
| } |
| else if (varType.isStructType()) |
| { |
| if (insideAnArray || insideAStruct) |
| return true; |
| |
| for (int ndx = 0; ndx < varType.getStructPtr()->getNumMembers(); ++ndx) |
| if (isIllegalVertexOutput(varType.getStructPtr()->getMember(ndx).getType(), true, insideAnArray)) |
| return true; |
| |
| return false; |
| } |
| else |
| { |
| DE_ASSERT(false); |
| return true; |
| } |
| } |
| |
| static bool isIllegalFragmentInput (const glu::VarType& varType) |
| { |
| return isIllegalVertexOutput(varType); |
| } |
| |
| static bool isIllegalFragmentOutput (const glu::VarType& varType, bool insideAnArray = false) |
| { |
| // booleans, opaque types, matrices, structs, arrays of arrays are not allowed as outputs |
| |
| if (varType.isBasicType()) |
| { |
| const bool isOpaqueType = !glu::isDataTypeScalar(varType.getBasicType()) && !glu::isDataTypeVector(varType.getBasicType()) && !glu::isDataTypeMatrix(varType.getBasicType()); |
| |
| if (glu::isDataTypeBoolOrBVec(varType.getBasicType()) || isOpaqueType || glu::isDataTypeMatrix(varType.getBasicType())) |
| return true; |
| return false; |
| } |
| else if (varType.isArrayType()) |
| { |
| if (insideAnArray) |
| return true; |
| return isIllegalFragmentOutput(varType.getElementType(), true); |
| } |
| else if (varType.isStructType()) |
| return true; |
| else |
| { |
| DE_ASSERT(false); |
| return true; |
| } |
| } |
| |
| static bool isTypeIntegerOrContainsIntegers (const glu::VarType& varType) |
| { |
| if (varType.isBasicType()) |
| return glu::isDataTypeIntOrIVec(varType.getBasicType()) || glu::isDataTypeUintOrUVec(varType.getBasicType()); |
| else if (varType.isArrayType()) |
| return isTypeIntegerOrContainsIntegers(varType.getElementType()); |
| else if (varType.isStructType()) |
| { |
| for (int ndx = 0; ndx < varType.getStructPtr()->getNumMembers(); ++ndx) |
| if (isTypeIntegerOrContainsIntegers(varType.getStructPtr()->getMember(ndx).getType())) |
| return true; |
| return false; |
| } |
| else |
| { |
| DE_ASSERT(false); |
| return true; |
| } |
| } |
| |
| bool Shader::isValid (void) const |
| { |
| // Default block variables |
| { |
| for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx) |
| { |
| // atomic declaration in the default block without binding |
| if (m_defaultBlock.variables[varNdx].layout.binding == -1 && |
| containsMatchingSubtype(m_defaultBlock.variables[varNdx].varType, glu::isDataTypeAtomicCounter)) |
| return false; |
| |
| // atomic declaration in a struct |
| if (m_defaultBlock.variables[varNdx].varType.isStructType() && |
| containsMatchingSubtype(m_defaultBlock.variables[varNdx].varType, glu::isDataTypeAtomicCounter)) |
| return false; |
| |
| // Unsupported layout qualifiers |
| |
| if (m_defaultBlock.variables[varNdx].layout.matrixOrder != glu::MATRIXORDER_LAST) |
| return false; |
| |
| if (containsMatchingSubtype(m_defaultBlock.variables[varNdx].varType, glu::isDataTypeSampler)) |
| { |
| const glu::Layout layoutWithLocationAndBinding(m_defaultBlock.variables[varNdx].layout.location, m_defaultBlock.variables[varNdx].layout.binding); |
| |
| if (m_defaultBlock.variables[varNdx].layout != layoutWithLocationAndBinding) |
| return false; |
| } |
| } |
| } |
| |
| // Interface blocks |
| { |
| for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx) |
| { |
| // ES31 disallows interface block array arrays |
| if (m_defaultBlock.interfaceBlocks[interfaceNdx].dimensions.size() > 1) |
| return false; |
| |
| // Interface block arrays must have instance name |
| if (!m_defaultBlock.interfaceBlocks[interfaceNdx].dimensions.empty() && m_defaultBlock.interfaceBlocks[interfaceNdx].instanceName.empty()) |
| return false; |
| |
| // Opaque types in interface block |
| if (containsMatchingSubtype(m_defaultBlock.interfaceBlocks[interfaceNdx].variables, isOpaqueType)) |
| return false; |
| } |
| } |
| |
| // Shader type specific |
| |
| if (m_shaderType == glu::SHADERTYPE_VERTEX) |
| { |
| for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx) |
| { |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN && isIllegalVertexInput(m_defaultBlock.variables[varNdx].varType)) |
| return false; |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_OUT && isIllegalVertexOutput(m_defaultBlock.variables[varNdx].varType)) |
| return false; |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_OUT && m_defaultBlock.variables[varNdx].interpolation != glu::INTERPOLATION_FLAT && isTypeIntegerOrContainsIntegers(m_defaultBlock.variables[varNdx].varType)) |
| return false; |
| } |
| for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx) |
| { |
| if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_IN || |
| m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_IN || |
| m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_OUT) |
| { |
| return false; |
| } |
| } |
| } |
| else if (m_shaderType == glu::SHADERTYPE_FRAGMENT) |
| { |
| for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx) |
| { |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN && isIllegalFragmentInput(m_defaultBlock.variables[varNdx].varType)) |
| return false; |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN && m_defaultBlock.variables[varNdx].interpolation != glu::INTERPOLATION_FLAT && isTypeIntegerOrContainsIntegers(m_defaultBlock.variables[varNdx].varType)) |
| return false; |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_OUT && isIllegalFragmentOutput(m_defaultBlock.variables[varNdx].varType)) |
| return false; |
| } |
| for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx) |
| { |
| if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_IN || |
| m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_OUT || |
| m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_OUT) |
| { |
| return false; |
| } |
| } |
| } |
| else if (m_shaderType == glu::SHADERTYPE_COMPUTE) |
| { |
| for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx) |
| { |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN || |
| m_defaultBlock.variables[varNdx].storage == glu::STORAGE_PATCH_IN || |
| m_defaultBlock.variables[varNdx].storage == glu::STORAGE_OUT || |
| m_defaultBlock.variables[varNdx].storage == glu::STORAGE_PATCH_OUT) |
| { |
| return false; |
| } |
| } |
| for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx) |
| { |
| if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_IN || |
| m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_IN || |
| m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_OUT || |
| m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_OUT) |
| { |
| return false; |
| } |
| } |
| } |
| else if (m_shaderType == glu::SHADERTYPE_GEOMETRY) |
| { |
| for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx) |
| { |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_PATCH_IN || |
| m_defaultBlock.variables[varNdx].storage == glu::STORAGE_PATCH_OUT) |
| { |
| return false; |
| } |
| // arrayed input |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN && !m_defaultBlock.variables[varNdx].varType.isArrayType()) |
| return false; |
| } |
| for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx) |
| { |
| if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_IN || |
| m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_OUT) |
| { |
| return false; |
| } |
| // arrayed input |
| if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_IN && m_defaultBlock.interfaceBlocks[interfaceNdx].dimensions.empty()) |
| return false; |
| } |
| } |
| else if (m_shaderType == glu::SHADERTYPE_TESSELLATION_CONTROL) |
| { |
| for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx) |
| { |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_PATCH_IN) |
| return false; |
| // arrayed input |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN && !m_defaultBlock.variables[varNdx].varType.isArrayType()) |
| return false; |
| // arrayed output |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_OUT && !m_defaultBlock.variables[varNdx].varType.isArrayType()) |
| return false; |
| } |
| for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx) |
| { |
| if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_IN) |
| return false; |
| // arrayed input |
| if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_IN && m_defaultBlock.interfaceBlocks[interfaceNdx].dimensions.empty()) |
| return false; |
| // arrayed output |
| if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_OUT && m_defaultBlock.interfaceBlocks[interfaceNdx].dimensions.empty()) |
| return false; |
| } |
| } |
| else if (m_shaderType == glu::SHADERTYPE_TESSELLATION_EVALUATION) |
| { |
| for (int varNdx = 0; varNdx < (int)m_defaultBlock.variables.size(); ++varNdx) |
| { |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_PATCH_OUT) |
| return false; |
| // arrayed input |
| if (m_defaultBlock.variables[varNdx].storage == glu::STORAGE_IN && !m_defaultBlock.variables[varNdx].varType.isArrayType()) |
| return false; |
| } |
| for (int interfaceNdx = 0; interfaceNdx < (int)m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx) |
| { |
| if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_PATCH_OUT) |
| return false; |
| // arrayed input |
| if (m_defaultBlock.interfaceBlocks[interfaceNdx].storage == glu::STORAGE_IN && m_defaultBlock.interfaceBlocks[interfaceNdx].dimensions.empty()) |
| return false; |
| } |
| } |
| else |
| DE_ASSERT(false); |
| |
| return true; |
| } |
| |
| Program::Program (void) |
| : m_separable (false) |
| , m_xfbMode (0) |
| , m_geoNumOutputVertices (0) |
| , m_tessNumOutputVertices (0) |
| { |
| } |
| |
| static void collectStructPtrs (std::set<const glu::StructType*>& dst, const glu::VarType& type) |
| { |
| if (type.isArrayType()) |
| collectStructPtrs(dst, type.getElementType()); |
| else if (type.isStructType()) |
| { |
| dst.insert(type.getStructPtr()); |
| |
| for (int memberNdx = 0; memberNdx < type.getStructPtr()->getNumMembers(); ++memberNdx) |
| collectStructPtrs(dst, type.getStructPtr()->getMember(memberNdx).getType()); |
| } |
| } |
| |
| Program::~Program (void) |
| { |
| // delete shader struct types, need to be done by the program since shaders might share struct types |
| { |
| std::set<const glu::StructType*> structTypes; |
| |
| for (int shaderNdx = 0; shaderNdx < (int)m_shaders.size(); ++shaderNdx) |
| { |
| for (int varNdx = 0; varNdx < (int)m_shaders[shaderNdx]->m_defaultBlock.variables.size(); ++varNdx) |
| collectStructPtrs(structTypes, m_shaders[shaderNdx]->m_defaultBlock.variables[varNdx].varType); |
| |
| for (int interfaceNdx = 0; interfaceNdx < (int)m_shaders[shaderNdx]->m_defaultBlock.interfaceBlocks.size(); ++interfaceNdx) |
| for (int varNdx = 0; varNdx < (int)m_shaders[shaderNdx]->m_defaultBlock.interfaceBlocks[interfaceNdx].variables.size(); ++varNdx) |
| collectStructPtrs(structTypes, m_shaders[shaderNdx]->m_defaultBlock.interfaceBlocks[interfaceNdx].variables[varNdx].varType); |
| } |
| |
| for (std::set<const glu::StructType*>::iterator it = structTypes.begin(); it != structTypes.end(); ++it) |
| delete *it; |
| } |
| |
| for (int shaderNdx = 0; shaderNdx < (int)m_shaders.size(); ++shaderNdx) |
| delete m_shaders[shaderNdx]; |
| m_shaders.clear(); |
| } |
| |
| Shader* Program::addShader (glu::ShaderType type, glu::GLSLVersion version) |
| { |
| DE_ASSERT(type < glu::SHADERTYPE_LAST); |
| |
| Shader* shader; |
| |
| // make sure push_back() cannot throw |
| m_shaders.reserve(m_shaders.size() + 1); |
| |
| shader = new Shader(type, version); |
| m_shaders.push_back(shader); |
| |
| return shader; |
| } |
| |
| void Program::setSeparable (bool separable) |
| { |
| m_separable = separable; |
| } |
| |
| bool Program::isSeparable (void) const |
| { |
| return m_separable; |
| } |
| |
| const std::vector<Shader*>& Program::getShaders (void) const |
| { |
| return m_shaders; |
| } |
| |
| glu::ShaderType Program::getFirstStage (void) const |
| { |
| const int nullValue = DE_LENGTH_OF_ARRAY(s_shaderStageOrder); |
| int firstStage = nullValue; |
| |
| for (int shaderNdx = 0; shaderNdx < (int)m_shaders.size(); ++shaderNdx) |
| { |
| const int index = getShaderStageIndex(m_shaders[shaderNdx]->getType()); |
| if (index != -1) |
| firstStage = de::min(firstStage, index); |
| } |
| |
| if (firstStage == nullValue) |
| return glu::SHADERTYPE_LAST; |
| else |
| return s_shaderStageOrder[firstStage]; |
| } |
| |
| glu::ShaderType Program::getLastStage (void) const |
| { |
| const int nullValue = -1; |
| int lastStage = nullValue; |
| |
| for (int shaderNdx = 0; shaderNdx < (int)m_shaders.size(); ++shaderNdx) |
| { |
| const int index = getShaderStageIndex(m_shaders[shaderNdx]->getType()); |
| if (index != -1) |
| lastStage = de::max(lastStage, index); |
| } |
| |
| if (lastStage == nullValue) |
| return glu::SHADERTYPE_LAST; |
| else |
| return s_shaderStageOrder[lastStage]; |
| } |
| |
| bool Program::hasStage (glu::ShaderType stage) const |
| { |
| for (int shaderNdx = 0; shaderNdx < (int)m_shaders.size(); ++shaderNdx) |
| { |
| if (m_shaders[shaderNdx]->getType() == stage) |
| return true; |
| } |
| return false; |
| } |
| |
| void Program::addTransformFeedbackVarying (const std::string& varName) |
| { |
| m_xfbVaryings.push_back(varName); |
| } |
| |
| const std::vector<std::string>& Program::getTransformFeedbackVaryings (void) const |
| { |
| return m_xfbVaryings; |
| } |
| |
| void Program::setTransformFeedbackMode (deUint32 mode) |
| { |
| m_xfbMode = mode; |
| } |
| |
| deUint32 Program::getTransformFeedbackMode (void) const |
| { |
| return m_xfbMode; |
| } |
| |
| deUint32 Program::getGeometryNumOutputVertices (void) const |
| { |
| return m_geoNumOutputVertices; |
| } |
| |
| void Program::setGeometryNumOutputVertices (deUint32 vertices) |
| { |
| m_geoNumOutputVertices = vertices; |
| } |
| |
| deUint32 Program::getTessellationNumOutputPatchVertices (void) const |
| { |
| return m_tessNumOutputVertices; |
| } |
| |
| void Program::setTessellationNumOutputPatchVertices (deUint32 vertices) |
| { |
| m_tessNumOutputVertices = vertices; |
| } |
| |
| bool Program::isValid (void) const |
| { |
| const bool isOpenGLES = (m_shaders.empty()) ? (false) : (glu::glslVersionIsES(m_shaders[0]->getVersion())); |
| bool computePresent = false; |
| bool vertexPresent = false; |
| bool fragmentPresent = false; |
| bool tessControlPresent = false; |
| bool tessEvalPresent = false; |
| bool geometryPresent = false; |
| |
| if (m_shaders.empty()) |
| return false; |
| |
| for (int ndx = 0; ndx < (int)m_shaders.size(); ++ndx) |
| if (!m_shaders[ndx]->isValid()) |
| return false; |
| |
| // same version |
| for (int ndx = 1; ndx < (int)m_shaders.size(); ++ndx) |
| if (m_shaders[0]->getVersion() != m_shaders[ndx]->getVersion()) |
| return false; |
| |
| for (int ndx = 0; ndx < (int)m_shaders.size(); ++ndx) |
| { |
| switch (m_shaders[ndx]->getType()) |
| { |
| case glu::SHADERTYPE_COMPUTE: computePresent = true; break; |
| case glu::SHADERTYPE_VERTEX: vertexPresent = true; break; |
| case glu::SHADERTYPE_FRAGMENT: fragmentPresent = true; break; |
| case glu::SHADERTYPE_TESSELLATION_CONTROL: tessControlPresent = true; break; |
| case glu::SHADERTYPE_TESSELLATION_EVALUATION: tessEvalPresent = true; break; |
| case glu::SHADERTYPE_GEOMETRY: geometryPresent = true; break; |
| default: |
| DE_ASSERT(false); |
| break; |
| } |
| } |
| // compute present -> no other stages present |
| { |
| const bool nonComputePresent = vertexPresent || fragmentPresent || tessControlPresent || tessEvalPresent || geometryPresent; |
| if (computePresent && nonComputePresent) |
| return false; |
| } |
| |
| // must contain both vertex and fragment shaders |
| if (!computePresent && !m_separable) |
| { |
| if (!vertexPresent || !fragmentPresent) |
| return false; |
| } |
| |
| // tess.Eval present <=> tess.Control present |
| if (!m_separable) |
| { |
| if (tessEvalPresent != tessControlPresent) |
| return false; |
| } |
| |
| if ((m_tessNumOutputVertices != 0) != (tessControlPresent || tessEvalPresent)) |
| return false; |
| |
| if ((m_geoNumOutputVertices != 0) != geometryPresent) |
| return false; |
| |
| for (int ndx = 0; ndx < (int)m_xfbVaryings.size(); ++ndx) |
| { |
| // user-defined |
| if (!de::beginsWith(m_xfbVaryings[ndx], "gl_")) |
| { |
| std::vector<ProgramInterfaceDefinition::VariablePathComponent> path; |
| if (!findProgramVariablePathByPathName(path, this, m_xfbVaryings[ndx], VariableSearchFilter::createShaderTypeStorageFilter(getProgramTransformFeedbackStage(this), glu::STORAGE_OUT))) |
| return false; |
| if (!path.back().isVariableType()) |
| return false; |
| |
| // Khronos bug #12787 disallowed capturing whole structs in OpenGL ES. |
| if (path.back().getVariableType()->isStructType() && isOpenGLES) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| } // ProgramInterfaceDefinition |
| } // Functional |
| } // gles31 |
| } // deqp |