You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
665 lines
20 KiB
665 lines
20 KiB
/*-------------------------------------------------------------------------
|
|
* 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
|