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