/*------------------------------------------------------------------------- * 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 }; // 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