blob: c2b973f96dfddb24753f244e7411f5e30312c29c [file] [log] [blame]
/*-------------------------------------------------------------------------
* OpenGL Conformance Test Suite
* -----------------------------
*
* Copyright (c) 2016 Google Inc.
* Copyright (c) 2016 The Khronos Group Inc.
*
* 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 Uniform block case.
*/ /*-------------------------------------------------------------------*/
#include "glcUniformBlockCase.hpp"
#include "deInt32.h"
#include "deMemory.h"
#include "deRandom.hpp"
#include "deString.h"
#include "deStringUtil.hpp"
#include "gluContextInfo.hpp"
#include "gluDrawUtil.hpp"
#include "gluPixelTransfer.hpp"
#include "gluRenderContext.hpp"
#include "glwEnums.hpp"
#include "glwFunctions.hpp"
#include "tcuRenderTarget.hpp"
#include "tcuSurface.hpp"
#include "tcuTestLog.hpp"
#include <algorithm>
#include <map>
using tcu::TestLog;
using std::string;
using std::vector;
using std::map;
namespace deqp
{
namespace ub
{
struct PrecisionFlagsFmt
{
deUint32 flags;
PrecisionFlagsFmt(deUint32 flags_) : flags(flags_)
{
}
};
std::ostream& operator<<(std::ostream& str, const PrecisionFlagsFmt& fmt)
{
// Precision.
DE_ASSERT(dePop32(fmt.flags & (PRECISION_LOW | PRECISION_MEDIUM | PRECISION_HIGH)) <= 1);
str << (fmt.flags & PRECISION_LOW ?
"lowp" :
fmt.flags & PRECISION_MEDIUM ? "mediump" : fmt.flags & PRECISION_HIGH ? "highp" : "");
return str;
}
struct LayoutFlagsFmt
{
deUint32 flags;
LayoutFlagsFmt(deUint32 flags_) : flags(flags_)
{
}
};
std::ostream& operator<<(std::ostream& str, const LayoutFlagsFmt& fmt)
{
static const struct
{
deUint32 bit;
const char* token;
} bitDesc[] = { { LAYOUT_SHARED, "shared" },
{ LAYOUT_PACKED, "packed" },
{ LAYOUT_STD140, "std140" },
{ LAYOUT_ROW_MAJOR, "row_major" },
{ LAYOUT_COLUMN_MAJOR, "column_major" } };
deUint32 remBits = fmt.flags;
for (int descNdx = 0; descNdx < DE_LENGTH_OF_ARRAY(bitDesc); descNdx++)
{
if (remBits & bitDesc[descNdx].bit)
{
if (remBits != fmt.flags)
str << ", ";
str << bitDesc[descNdx].token;
remBits &= ~bitDesc[descNdx].bit;
}
}
DE_ASSERT(remBits == 0);
return str;
}
// VarType implementation.
VarType::VarType(void) : m_type(TYPE_LAST), m_flags(0)
{
}
VarType::VarType(const VarType& other) : m_type(TYPE_LAST), m_flags(0)
{
*this = other;
}
VarType::VarType(glu::DataType basicType, deUint32 flags) : m_type(TYPE_BASIC), m_flags(flags)
{
m_data.basicType = basicType;
}
VarType::VarType(const VarType& elementType, int arraySize) : m_type(TYPE_ARRAY), m_flags(0)
{
m_data.array.size = arraySize;
m_data.array.elementType = new VarType(elementType);
}
VarType::VarType(const StructType* structPtr) : m_type(TYPE_STRUCT), m_flags(0)
{
m_data.structPtr = structPtr;
}
VarType::~VarType(void)
{
if (m_type == TYPE_ARRAY)
delete m_data.array.elementType;
}
VarType& VarType::operator=(const VarType& other)
{
if (this == &other)
return *this; // Self-assignment.
if (m_type == TYPE_ARRAY)
delete m_data.array.elementType;
m_type = other.m_type;
m_flags = other.m_flags;
m_data = Data();
if (m_type == TYPE_ARRAY)
{
m_data.array.elementType = new VarType(*other.m_data.array.elementType);
m_data.array.size = other.m_data.array.size;
}
else
m_data = other.m_data;
return *this;
}
// StructType implementation.
void StructType::addMember(const char* name, const VarType& type, deUint32 flags)
{
m_members.push_back(StructMember(name, type, flags));
}
// Uniform implementation.
Uniform::Uniform(const char* name, const VarType& type, deUint32 flags) : m_name(name), m_type(type), m_flags(flags)
{
}
// UniformBlock implementation.
UniformBlock::UniformBlock(const char* blockName) : m_blockName(blockName), m_arraySize(0), m_flags(0)
{
}
struct BlockLayoutEntry
{
BlockLayoutEntry(void) : size(0)
{
}
std::string name;
int size;
std::vector<int> activeUniformIndices;
};
std::ostream& operator<<(std::ostream& stream, const BlockLayoutEntry& entry)
{
stream << entry.name << " { name = " << entry.name << ", size = " << entry.size << ", activeUniformIndices = [";
for (vector<int>::const_iterator i = entry.activeUniformIndices.begin(); i != entry.activeUniformIndices.end(); i++)
{
if (i != entry.activeUniformIndices.begin())
stream << ", ";
stream << *i;
}
stream << "] }";
return stream;
}
struct UniformLayoutEntry
{
UniformLayoutEntry(void)
: type(glu::TYPE_LAST), size(0), blockNdx(-1), offset(-1), arrayStride(-1), matrixStride(-1), isRowMajor(false)
{
}
std::string name;
glu::DataType type;
int size;
int blockNdx;
int offset;
int arrayStride;
int matrixStride;
bool isRowMajor;
};
std::ostream& operator<<(std::ostream& stream, const UniformLayoutEntry& entry)
{
stream << entry.name << " { type = " << glu::getDataTypeName(entry.type) << ", size = " << entry.size
<< ", blockNdx = " << entry.blockNdx << ", offset = " << entry.offset
<< ", arrayStride = " << entry.arrayStride << ", matrixStride = " << entry.matrixStride
<< ", isRowMajor = " << (entry.isRowMajor ? "true" : "false") << " }";
return stream;
}
class UniformLayout
{
public:
std::vector<BlockLayoutEntry> blocks;
std::vector<UniformLayoutEntry> uniforms;
int getUniformIndex(const char* name) const;
int getBlockIndex(const char* name) const;
};
// \todo [2012-01-24 pyry] Speed up lookups using hash.
int UniformLayout::getUniformIndex(const char* name) const
{
for (int ndx = 0; ndx < (int)uniforms.size(); ndx++)
{
if (uniforms[ndx].name == name)
return ndx;
}
return -1;
}
int UniformLayout::getBlockIndex(const char* name) const
{
for (int ndx = 0; ndx < (int)blocks.size(); ndx++)
{
if (blocks[ndx].name == name)
return ndx;
}
return -1;
}
// ShaderInterface implementation.
ShaderInterface::ShaderInterface(void)
{
}
ShaderInterface::~ShaderInterface(void)
{
for (std::vector<StructType*>::iterator i = m_structs.begin(); i != m_structs.end(); i++)
delete *i;
for (std::vector<UniformBlock*>::iterator i = m_uniformBlocks.begin(); i != m_uniformBlocks.end(); i++)
delete *i;
}
StructType& ShaderInterface::allocStruct(const char* name)
{
m_structs.reserve(m_structs.size() + 1);
m_structs.push_back(new StructType(name));
return *m_structs.back();
}
struct StructNameEquals
{
std::string name;
StructNameEquals(const char* name_) : name(name_)
{
}
bool operator()(const StructType* type) const
{
return type->getTypeName() && name == type->getTypeName();
}
};
const StructType* ShaderInterface::findStruct(const char* name) const
{
std::vector<StructType*>::const_iterator pos =
std::find_if(m_structs.begin(), m_structs.end(), StructNameEquals(name));
return pos != m_structs.end() ? *pos : DE_NULL;
}
void ShaderInterface::getNamedStructs(std::vector<const StructType*>& structs) const
{
for (std::vector<StructType*>::const_iterator i = m_structs.begin(); i != m_structs.end(); i++)
{
if ((*i)->getTypeName() != DE_NULL)
structs.push_back(*i);
}
}
UniformBlock& ShaderInterface::allocBlock(const char* name)
{
m_uniformBlocks.reserve(m_uniformBlocks.size() + 1);
m_uniformBlocks.push_back(new UniformBlock(name));
return *m_uniformBlocks.back();
}
namespace // Utilities
{
// Layout computation.
int getDataTypeByteSize(glu::DataType type)
{
return static_cast<int>(glu::getDataTypeScalarSize(type) * sizeof(deUint32));
}
int getDataTypeByteAlignment(glu::DataType type)
{
switch (type)
{
case glu::TYPE_FLOAT:
case glu::TYPE_INT:
case glu::TYPE_UINT:
case glu::TYPE_BOOL:
return static_cast<int>(1 * sizeof(deUint32));
case glu::TYPE_FLOAT_VEC2:
case glu::TYPE_INT_VEC2:
case glu::TYPE_UINT_VEC2:
case glu::TYPE_BOOL_VEC2:
return static_cast<int>(2 * sizeof(deUint32));
case glu::TYPE_FLOAT_VEC3:
case glu::TYPE_INT_VEC3:
case glu::TYPE_UINT_VEC3:
case glu::TYPE_BOOL_VEC3: // Fall-through to vec4
case glu::TYPE_FLOAT_VEC4:
case glu::TYPE_INT_VEC4:
case glu::TYPE_UINT_VEC4:
case glu::TYPE_BOOL_VEC4:
return static_cast<int>(4 * sizeof(deUint32));
default:
DE_ASSERT(false);
return 0;
}
}
int getDataTypeArrayStride(glu::DataType type)
{
DE_ASSERT(!glu::isDataTypeMatrix(type));
int baseStride = getDataTypeByteSize(type);
int vec4Alignment = static_cast<int>(sizeof(deUint32) * 4);
DE_ASSERT(baseStride <= vec4Alignment);
return de::max(baseStride, vec4Alignment); // Really? See rule 4.
}
int computeStd140BaseAlignment(const VarType& type)
{
const int vec4Alignment = static_cast<int>(sizeof(deUint32) * 4);
if (type.isBasicType())
{
glu::DataType basicType = type.getBasicType();
if (glu::isDataTypeMatrix(basicType))
{
bool isRowMajor = !!(type.getFlags() & LAYOUT_ROW_MAJOR);
int vecSize =
isRowMajor ? glu::getDataTypeMatrixNumColumns(basicType) : glu::getDataTypeMatrixNumRows(basicType);
return getDataTypeArrayStride(glu::getDataTypeFloatVec(vecSize));
}
else
return getDataTypeByteAlignment(basicType);
}
else if (type.isArrayType())
{
int elemAlignment = computeStd140BaseAlignment(type.getElementType());
// Round up to alignment of vec4
return deRoundUp32(elemAlignment, vec4Alignment);
}
else
{
DE_ASSERT(type.isStructType());
int maxBaseAlignment = 0;
for (StructType::ConstIterator memberIter = type.getStruct().begin(); memberIter != type.getStruct().end();
memberIter++)
maxBaseAlignment = de::max(maxBaseAlignment, computeStd140BaseAlignment(memberIter->getType()));
return deRoundUp32(maxBaseAlignment, vec4Alignment);
}
}
inline deUint32 mergeLayoutFlags(deUint32 prevFlags, deUint32 newFlags)
{
const deUint32 packingMask = LAYOUT_PACKED | LAYOUT_SHARED | LAYOUT_STD140;
const deUint32 matrixMask = LAYOUT_ROW_MAJOR | LAYOUT_COLUMN_MAJOR;
deUint32 mergedFlags = 0;
mergedFlags |= ((newFlags & packingMask) ? newFlags : prevFlags) & packingMask;
mergedFlags |= ((newFlags & matrixMask) ? newFlags : prevFlags) & matrixMask;
return mergedFlags;
}
void computeStd140Layout(UniformLayout& layout, int& curOffset, int curBlockNdx, const std::string& curPrefix,
const VarType& type, deUint32 layoutFlags)
{
int baseAlignment = computeStd140BaseAlignment(type);
curOffset = deAlign32(curOffset, baseAlignment);
if (type.isBasicType())
{
glu::DataType basicType = type.getBasicType();
UniformLayoutEntry entry;
entry.name = curPrefix;
entry.type = basicType;
entry.size = 1;
entry.arrayStride = 0;
entry.matrixStride = 0;
entry.blockNdx = curBlockNdx;
if (glu::isDataTypeMatrix(basicType))
{
// Array of vectors as specified in rules 5 & 7.
bool isRowMajor = !!(layoutFlags & LAYOUT_ROW_MAJOR);
int vecSize =
isRowMajor ? glu::getDataTypeMatrixNumColumns(basicType) : glu::getDataTypeMatrixNumRows(basicType);
int numVecs =
isRowMajor ? glu::getDataTypeMatrixNumRows(basicType) : glu::getDataTypeMatrixNumColumns(basicType);
int stride = getDataTypeArrayStride(glu::getDataTypeFloatVec(vecSize));
entry.offset = curOffset;
entry.matrixStride = stride;
entry.isRowMajor = isRowMajor;
curOffset += numVecs * stride;
}
else
{
// Scalar or vector.
entry.offset = curOffset;
curOffset += getDataTypeByteSize(basicType);
}
layout.uniforms.push_back(entry);
}
else if (type.isArrayType())
{
const VarType& elemType = type.getElementType();
if (elemType.isBasicType() && !glu::isDataTypeMatrix(elemType.getBasicType()))
{
// Array of scalars or vectors.
glu::DataType elemBasicType = elemType.getBasicType();
UniformLayoutEntry entry;
int stride = getDataTypeArrayStride(elemBasicType);
entry.name = curPrefix + "[0]"; // Array uniforms are always postfixed with [0]
entry.type = elemBasicType;
entry.blockNdx = curBlockNdx;
entry.offset = curOffset;
entry.size = type.getArraySize();
entry.arrayStride = stride;
entry.matrixStride = 0;
curOffset += stride * type.getArraySize();
layout.uniforms.push_back(entry);
}
else if (elemType.isBasicType() && glu::isDataTypeMatrix(elemType.getBasicType()))
{
// Array of matrices.
glu::DataType elemBasicType = elemType.getBasicType();
bool isRowMajor = !!(layoutFlags & LAYOUT_ROW_MAJOR);
int vecSize = isRowMajor ? glu::getDataTypeMatrixNumColumns(elemBasicType) :
glu::getDataTypeMatrixNumRows(elemBasicType);
int numVecs = isRowMajor ? glu::getDataTypeMatrixNumRows(elemBasicType) :
glu::getDataTypeMatrixNumColumns(elemBasicType);
int stride = getDataTypeArrayStride(glu::getDataTypeFloatVec(vecSize));
UniformLayoutEntry entry;
entry.name = curPrefix + "[0]"; // Array uniforms are always postfixed with [0]
entry.type = elemBasicType;
entry.blockNdx = curBlockNdx;
entry.offset = curOffset;
entry.size = type.getArraySize();
entry.arrayStride = stride * numVecs;
entry.matrixStride = stride;
entry.isRowMajor = isRowMajor;
curOffset += numVecs * type.getArraySize() * stride;
layout.uniforms.push_back(entry);
}
else
{
DE_ASSERT(elemType.isStructType() || elemType.isArrayType());
for (int elemNdx = 0; elemNdx < type.getArraySize(); elemNdx++)
computeStd140Layout(layout, curOffset, curBlockNdx, curPrefix + "[" + de::toString(elemNdx) + "]",
type.getElementType(), layoutFlags);
}
}
else
{
DE_ASSERT(type.isStructType());
for (StructType::ConstIterator memberIter = type.getStruct().begin(); memberIter != type.getStruct().end();
memberIter++)
computeStd140Layout(layout, curOffset, curBlockNdx, curPrefix + "." + memberIter->getName(),
memberIter->getType(), layoutFlags);
curOffset = deAlign32(curOffset, baseAlignment);
}
}
void computeStd140Layout(UniformLayout& layout, const ShaderInterface& interface)
{
// \todo [2012-01-23 pyry] Uniforms in default block.
int numUniformBlocks = interface.getNumUniformBlocks();
for (int blockNdx = 0; blockNdx < numUniformBlocks; blockNdx++)
{
const UniformBlock& block = interface.getUniformBlock(blockNdx);
bool hasInstanceName = block.getInstanceName() != DE_NULL;
std::string blockPrefix = hasInstanceName ? (std::string(block.getBlockName()) + ".") : std::string("");
int curOffset = 0;
int activeBlockNdx = (int)layout.blocks.size();
int firstUniformNdx = (int)layout.uniforms.size();
for (UniformBlock::ConstIterator uniformIter = block.begin(); uniformIter != block.end(); uniformIter++)
{
const Uniform& uniform = *uniformIter;
computeStd140Layout(layout, curOffset, activeBlockNdx, blockPrefix + uniform.getName(), uniform.getType(),
mergeLayoutFlags(block.getFlags(), uniform.getFlags()));
}
int uniformIndicesEnd = (int)layout.uniforms.size();
int blockSize = curOffset;
int numInstances = block.isArray() ? block.getArraySize() : 1;
// Create block layout entries for each instance.
for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
{
// Allocate entry for instance.
layout.blocks.push_back(BlockLayoutEntry());
BlockLayoutEntry& blockEntry = layout.blocks.back();
blockEntry.name = block.getBlockName();
blockEntry.size = blockSize;
// Compute active uniform set for block.
for (int uniformNdx = firstUniformNdx; uniformNdx < uniformIndicesEnd; uniformNdx++)
blockEntry.activeUniformIndices.push_back(uniformNdx);
if (block.isArray())
blockEntry.name += "[" + de::toString(instanceNdx) + "]";
}
}
}
// Value generator.
void generateValue(const UniformLayoutEntry& entry, void* basePtr, de::Random& rnd)
{
glu::DataType scalarType = glu::getDataTypeScalarType(entry.type);
int scalarSize = glu::getDataTypeScalarSize(entry.type);
bool isMatrix = glu::isDataTypeMatrix(entry.type);
int numVecs = isMatrix ? (entry.isRowMajor ? glu::getDataTypeMatrixNumRows(entry.type) :
glu::getDataTypeMatrixNumColumns(entry.type)) :
1;
int vecSize = scalarSize / numVecs;
bool isArray = entry.size > 1;
const int compSize = sizeof(deUint32);
DE_ASSERT(scalarSize % numVecs == 0);
for (int elemNdx = 0; elemNdx < entry.size; elemNdx++)
{
deUint8* elemPtr = (deUint8*)basePtr + entry.offset + (isArray ? elemNdx * entry.arrayStride : 0);
for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
{
deUint8* vecPtr = elemPtr + (isMatrix ? vecNdx * entry.matrixStride : 0);
for (int compNdx = 0; compNdx < vecSize; compNdx++)
{
deUint8* compPtr = vecPtr + compSize * compNdx;
switch (scalarType)
{
case glu::TYPE_FLOAT:
*((float*)compPtr) = (float)rnd.getInt(-9, 9);
break;
case glu::TYPE_INT:
*((int*)compPtr) = rnd.getInt(-9, 9);
break;
case glu::TYPE_UINT:
*((deUint32*)compPtr) = (deUint32)rnd.getInt(0, 9);
break;
// \note Random bit pattern is used for true values. Spec states that all non-zero values are
// interpreted as true but some implementations fail this.
case glu::TYPE_BOOL:
*((deUint32*)compPtr) = rnd.getBool() ? rnd.getUint32() | 1u : 0u;
break;
default:
DE_ASSERT(false);
}
}
}
}
}
void generateValues(const UniformLayout& layout, const std::map<int, void*>& blockPointers, deUint32 seed)
{
de::Random rnd(seed);
int numBlocks = (int)layout.blocks.size();
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
void* basePtr = blockPointers.find(blockNdx)->second;
int numEntries = (int)layout.blocks[blockNdx].activeUniformIndices.size();
for (int entryNdx = 0; entryNdx < numEntries; entryNdx++)
{
const UniformLayoutEntry& entry = layout.uniforms[layout.blocks[blockNdx].activeUniformIndices[entryNdx]];
generateValue(entry, basePtr, rnd);
}
}
}
// Shader generator.
static const char* s_compareFuncs =
"mediump float compare_float (highp float a, highp float b) { return abs(a - b) < 0.05 ? 1.0 : 0.0; }\n"
"mediump float compare_vec2 (highp vec2 a, highp vec2 b) { return compare_float(a.x, "
"b.x)*compare_float(a.y, b.y); }\n"
"mediump float compare_vec3 (highp vec3 a, highp vec3 b) { return compare_float(a.x, "
"b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z); }\n"
"mediump float compare_vec4 (highp vec4 a, highp vec4 b) { return compare_float(a.x, "
"b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z)*compare_float(a.w, b.w); }\n"
"mediump float compare_mat2 (highp mat2 a, highp mat2 b) { return compare_vec2(a[0], "
"b[0])*compare_vec2(a[1], b[1]); }\n"
"mediump float compare_mat2x3 (highp mat2x3 a, highp mat2x3 b){ return compare_vec3(a[0], "
"b[0])*compare_vec3(a[1], b[1]); }\n"
"mediump float compare_mat2x4 (highp mat2x4 a, highp mat2x4 b){ return compare_vec4(a[0], "
"b[0])*compare_vec4(a[1], b[1]); }\n"
"mediump float compare_mat3x2 (highp mat3x2 a, highp mat3x2 b){ return compare_vec2(a[0], "
"b[0])*compare_vec2(a[1], b[1])*compare_vec2(a[2], b[2]); }\n"
"mediump float compare_mat3 (highp mat3 a, highp mat3 b) { return compare_vec3(a[0], "
"b[0])*compare_vec3(a[1], b[1])*compare_vec3(a[2], b[2]); }\n"
"mediump float compare_mat3x4 (highp mat3x4 a, highp mat3x4 b){ return compare_vec4(a[0], "
"b[0])*compare_vec4(a[1], b[1])*compare_vec4(a[2], b[2]); }\n"
"mediump float compare_mat4x2 (highp mat4x2 a, highp mat4x2 b){ return compare_vec2(a[0], "
"b[0])*compare_vec2(a[1], b[1])*compare_vec2(a[2], b[2])*compare_vec2(a[3], b[3]); }\n"
"mediump float compare_mat4x3 (highp mat4x3 a, highp mat4x3 b){ return compare_vec3(a[0], "
"b[0])*compare_vec3(a[1], b[1])*compare_vec3(a[2], b[2])*compare_vec3(a[3], b[3]); }\n"
"mediump float compare_mat4 (highp mat4 a, highp mat4 b) { return compare_vec4(a[0], "
"b[0])*compare_vec4(a[1], b[1])*compare_vec4(a[2], b[2])*compare_vec4(a[3], b[3]); }\n"
"mediump float compare_int (highp int a, highp int b) { return a == b ? 1.0 : 0.0; }\n"
"mediump float compare_ivec2 (highp ivec2 a, highp ivec2 b) { return a == b ? 1.0 : 0.0; }\n"
"mediump float compare_ivec3 (highp ivec3 a, highp ivec3 b) { return a == b ? 1.0 : 0.0; }\n"
"mediump float compare_ivec4 (highp ivec4 a, highp ivec4 b) { return a == b ? 1.0 : 0.0; }\n"
"mediump float compare_uint (highp uint a, highp uint b) { return a == b ? 1.0 : 0.0; }\n"
"mediump float compare_uvec2 (highp uvec2 a, highp uvec2 b) { return a == b ? 1.0 : 0.0; }\n"
"mediump float compare_uvec3 (highp uvec3 a, highp uvec3 b) { return a == b ? 1.0 : 0.0; }\n"
"mediump float compare_uvec4 (highp uvec4 a, highp uvec4 b) { return a == b ? 1.0 : 0.0; }\n"
"mediump float compare_bool (bool a, bool b) { return a == b ? 1.0 : 0.0; }\n"
"mediump float compare_bvec2 (bvec2 a, bvec2 b) { return a == b ? 1.0 : 0.0; }\n"
"mediump float compare_bvec3 (bvec3 a, bvec3 b) { return a == b ? 1.0 : 0.0; }\n"
"mediump float compare_bvec4 (bvec4 a, bvec4 b) { return a == b ? 1.0 : 0.0; }\n";
struct Indent
{
int level;
Indent(int level_) : level(level_)
{
}
};
std::ostream& operator<<(std::ostream& str, const Indent& indent)
{
for (int i = 0; i < indent.level; i++)
str << "\t";
return str;
}
void generateDeclaration(std::ostringstream& src, const VarType& type, const char* name, int indentLevel,
deUint32 unusedHints);
void generateDeclaration(std::ostringstream& src, const Uniform& uniform, int indentLevel);
void generateDeclaration(std::ostringstream& src, const StructType& structType, int indentLevel);
void generateLocalDeclaration(std::ostringstream& src, const StructType& structType, int indentLevel);
void generateFullDeclaration(std::ostringstream& src, const StructType& structType, int indentLevel);
void generateDeclaration(std::ostringstream& src, const StructType& structType, int indentLevel)
{
DE_ASSERT(structType.getTypeName() != DE_NULL);
generateFullDeclaration(src, structType, indentLevel);
src << ";\n";
}
void generateFullDeclaration(std::ostringstream& src, const StructType& structType, int indentLevel)
{
src << "struct";
if (structType.getTypeName())
src << " " << structType.getTypeName();
src << "\n" << Indent(indentLevel) << "{\n";
for (StructType::ConstIterator memberIter = structType.begin(); memberIter != structType.end(); memberIter++)
{
src << Indent(indentLevel + 1);
generateDeclaration(src, memberIter->getType(), memberIter->getName(), indentLevel + 1,
memberIter->getFlags() & UNUSED_BOTH);
}
src << Indent(indentLevel) << "}";
}
void generateLocalDeclaration(std::ostringstream& src, const StructType& structType, int indentLevel)
{
if (structType.getTypeName() == DE_NULL)
generateFullDeclaration(src, structType, indentLevel);
else
src << structType.getTypeName();
}
void generateDeclaration(std::ostringstream& src, const VarType& type, const char* name, int indentLevel,
deUint32 unusedHints)
{
deUint32 flags = type.getFlags();
if ((flags & LAYOUT_MASK) != 0)
src << "layout(" << LayoutFlagsFmt(flags & LAYOUT_MASK) << ") ";
if ((flags & PRECISION_MASK) != 0)
src << PrecisionFlagsFmt(flags & PRECISION_MASK) << " ";
if (type.isBasicType())
src << glu::getDataTypeName(type.getBasicType()) << " " << name;
else if (type.isArrayType())
{
std::vector<int> arraySizes;
const VarType* curType = &type;
while (curType->isArrayType())
{
arraySizes.push_back(curType->getArraySize());
curType = &curType->getElementType();
}
if (curType->isBasicType())
{
if ((curType->getFlags() & PRECISION_MASK) != 0)
src << PrecisionFlagsFmt(curType->getFlags() & PRECISION_MASK) << " ";
src << glu::getDataTypeName(curType->getBasicType());
}
else
{
DE_ASSERT(curType->isStructType());
generateLocalDeclaration(src, curType->getStruct(), indentLevel + 1);
}
src << " " << name;
for (std::vector<int>::const_reverse_iterator sizeIter = arraySizes.rbegin(); sizeIter != arraySizes.rend();
sizeIter++)
src << "[" << *sizeIter << "]";
}
else
{
generateLocalDeclaration(src, type.getStruct(), indentLevel + 1);
src << " " << name;
}
src << ";";
// Print out unused hints.
if (unusedHints != 0)
src << " // unused in "
<< (unusedHints == UNUSED_BOTH ?
"both shaders" :
unusedHints == UNUSED_VERTEX ? "vertex shader" :
unusedHints == UNUSED_FRAGMENT ? "fragment shader" : "???");
src << "\n";
}
void generateDeclaration(std::ostringstream& src, const Uniform& uniform, int indentLevel)
{
if ((uniform.getFlags() & LAYOUT_MASK) != 0)
src << "layout(" << LayoutFlagsFmt(uniform.getFlags() & LAYOUT_MASK) << ") ";
generateDeclaration(src, uniform.getType(), uniform.getName(), indentLevel, uniform.getFlags() & UNUSED_BOTH);
}
void generateDeclaration(std::ostringstream& src, const UniformBlock& block)
{
if ((block.getFlags() & LAYOUT_MASK) != 0)
src << "layout(" << LayoutFlagsFmt(block.getFlags() & LAYOUT_MASK) << ") ";
src << "uniform " << block.getBlockName();
src << "\n{\n";
for (UniformBlock::ConstIterator uniformIter = block.begin(); uniformIter != block.end(); uniformIter++)
{
src << Indent(1);
generateDeclaration(src, *uniformIter, 1 /* indent level */);
}
src << "}";
if (block.getInstanceName() != DE_NULL)
{
src << " " << block.getInstanceName();
if (block.isArray())
src << "[" << block.getArraySize() << "]";
}
else
DE_ASSERT(!block.isArray());
src << ";\n";
}
void generateValueSrc(std::ostringstream& src, const UniformLayoutEntry& entry, const void* basePtr, int elementNdx)
{
glu::DataType scalarType = glu::getDataTypeScalarType(entry.type);
int scalarSize = glu::getDataTypeScalarSize(entry.type);
bool isArray = entry.size > 1;
const deUint8* elemPtr = (const deUint8*)basePtr + entry.offset + (isArray ? elementNdx * entry.arrayStride : 0);
const int compSize = sizeof(deUint32);
if (scalarSize > 1)
src << glu::getDataTypeName(entry.type) << "(";
if (glu::isDataTypeMatrix(entry.type))
{
int numRows = glu::getDataTypeMatrixNumRows(entry.type);
int numCols = glu::getDataTypeMatrixNumColumns(entry.type);
DE_ASSERT(scalarType == glu::TYPE_FLOAT);
// Constructed in column-wise order.
for (int colNdx = 0; colNdx < numCols; colNdx++)
{
for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
{
const deUint8* compPtr = elemPtr + (entry.isRowMajor ? rowNdx * entry.matrixStride + colNdx * compSize :
colNdx * entry.matrixStride + rowNdx * compSize);
if (colNdx > 0 || rowNdx > 0)
src << ", ";
src << de::floatToString(*((const float*)compPtr), 1);
}
}
}
else
{
for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
{
const deUint8* compPtr = elemPtr + scalarNdx * compSize;
if (scalarNdx > 0)
src << ", ";
switch (scalarType)
{
case glu::TYPE_FLOAT:
src << de::floatToString(*((const float*)compPtr), 1);
break;
case glu::TYPE_INT:
src << *((const int*)compPtr);
break;
case glu::TYPE_UINT:
src << *((const deUint32*)compPtr) << "u";
break;
case glu::TYPE_BOOL:
src << (*((const deUint32*)compPtr) != 0u ? "true" : "false");
break;
default:
DE_ASSERT(false);
}
}
}
if (scalarSize > 1)
src << ")";
}
void generateCompareSrc(std::ostringstream& src, const char* resultVar, const VarType& type, const char* srcName,
const char* apiName, const UniformLayout& layout, const void* basePtr, deUint32 unusedMask)
{
if (type.isBasicType() || (type.isArrayType() && type.getElementType().isBasicType()))
{
// Basic type or array of basic types.
bool isArray = type.isArrayType();
glu::DataType elementType = isArray ? type.getElementType().getBasicType() : type.getBasicType();
const char* typeName = glu::getDataTypeName(elementType);
std::string fullApiName = string(apiName) + (isArray ? "[0]" : ""); // Arrays are always postfixed with [0]
int uniformNdx = layout.getUniformIndex(fullApiName.c_str());
const UniformLayoutEntry& entry = layout.uniforms[uniformNdx];
if (isArray)
{
for (int elemNdx = 0; elemNdx < type.getArraySize(); elemNdx++)
{
src << "\tresult *= compare_" << typeName << "(" << srcName << "[" << elemNdx << "], ";
generateValueSrc(src, entry, basePtr, elemNdx);
src << ");\n";
}
}
else
{
src << "\tresult *= compare_" << typeName << "(" << srcName << ", ";
generateValueSrc(src, entry, basePtr, 0);
src << ");\n";
}
}
else if (type.isArrayType())
{
const VarType& elementType = type.getElementType();
DE_ASSERT(!elementType.isArrayType());
for (int elementNdx = 0; elementNdx < type.getArraySize(); elementNdx++)
{
std::string op = string("[") + de::toString(elementNdx) + "]";
generateCompareSrc(src, resultVar, elementType, (string(srcName) + op).c_str(),
(string(apiName) + op).c_str(), layout, basePtr, unusedMask);
}
}
else
{
DE_ASSERT(type.isStructType());
for (StructType::ConstIterator memberIter = type.getStruct().begin(); memberIter != type.getStruct().end();
memberIter++)
{
if (memberIter->getFlags() & unusedMask)
continue; // Skip member.
string op = string(".") + memberIter->getName();
generateCompareSrc(src, resultVar, memberIter->getType(), (string(srcName) + op).c_str(),
(string(apiName) + op).c_str(), layout, basePtr, unusedMask);
}
}
}
void generateCompareSrc(std::ostringstream& src, const char* resultVar, const ShaderInterface& interface,
const UniformLayout& layout, const std::map<int, void*>& blockPointers, bool isVertex)
{
deUint32 unusedMask = isVertex ? UNUSED_VERTEX : UNUSED_FRAGMENT;
for (int blockNdx = 0; blockNdx < interface.getNumUniformBlocks(); blockNdx++)
{
const UniformBlock& block = interface.getUniformBlock(blockNdx);
if ((block.getFlags() & (isVertex ? DECLARE_VERTEX : DECLARE_FRAGMENT)) == 0)
continue; // Skip.
bool hasInstanceName = block.getInstanceName() != DE_NULL;
bool isArray = block.isArray();
int numInstances = isArray ? block.getArraySize() : 1;
std::string apiPrefix = hasInstanceName ? string(block.getBlockName()) + "." : string("");
DE_ASSERT(!isArray || hasInstanceName);
for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
{
std::string instancePostfix = isArray ? string("[") + de::toString(instanceNdx) + "]" : string("");
std::string blockInstanceName = block.getBlockName() + instancePostfix;
std::string srcPrefix =
hasInstanceName ? string(block.getInstanceName()) + instancePostfix + "." : string("");
int activeBlockNdx = layout.getBlockIndex(blockInstanceName.c_str());
void* basePtr = blockPointers.find(activeBlockNdx)->second;
for (UniformBlock::ConstIterator uniformIter = block.begin(); uniformIter != block.end(); uniformIter++)
{
const Uniform& uniform = *uniformIter;
if (uniform.getFlags() & unusedMask)
continue; // Don't read from that uniform.
generateCompareSrc(src, resultVar, uniform.getType(), (srcPrefix + uniform.getName()).c_str(),
(apiPrefix + uniform.getName()).c_str(), layout, basePtr, unusedMask);
}
}
}
}
void generateVertexShader(std::ostringstream& src, glu::GLSLVersion glslVersion, const ShaderInterface& interface,
const UniformLayout& layout, const std::map<int, void*>& blockPointers)
{
DE_ASSERT(glslVersion == glu::GLSL_VERSION_300_ES || glslVersion == glu::GLSL_VERSION_310_ES ||
de::inRange<int>(glslVersion, glu::GLSL_VERSION_330, glu::GLSL_VERSION_430));
src << glu::getGLSLVersionDeclaration(glslVersion) << "\n";
src << "in highp vec4 a_position;\n";
src << "out mediump float v_vtxResult;\n";
src << "\n";
std::vector<const StructType*> namedStructs;
interface.getNamedStructs(namedStructs);
for (std::vector<const StructType*>::const_iterator structIter = namedStructs.begin();
structIter != namedStructs.end(); structIter++)
generateDeclaration(src, **structIter, 0);
for (int blockNdx = 0; blockNdx < interface.getNumUniformBlocks(); blockNdx++)
{
const UniformBlock& block = interface.getUniformBlock(blockNdx);
if (block.getFlags() & DECLARE_VERTEX)
generateDeclaration(src, block);
}
// Comparison utilities.
src << "\n" << s_compareFuncs;
src << "\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
" mediump float result = 1.0;\n";
// Value compare.
generateCompareSrc(src, "result", interface, layout, blockPointers, true);
src << " v_vtxResult = result;\n"
"}\n";
}
void generateFragmentShader(std::ostringstream& src, glu::GLSLVersion glslVersion, const ShaderInterface& interface,
const UniformLayout& layout, const std::map<int, void*>& blockPointers)
{
DE_ASSERT(glslVersion == glu::GLSL_VERSION_300_ES || glslVersion == glu::GLSL_VERSION_310_ES ||
de::inRange<int>(glslVersion, glu::GLSL_VERSION_330, glu::GLSL_VERSION_430));
src << glu::getGLSLVersionDeclaration(glslVersion) << "\n";
src << "in mediump float v_vtxResult;\n";
src << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
src << "\n";
std::vector<const StructType*> namedStructs;
interface.getNamedStructs(namedStructs);
for (std::vector<const StructType*>::const_iterator structIter = namedStructs.begin();
structIter != namedStructs.end(); structIter++)
generateDeclaration(src, **structIter, 0);
for (int blockNdx = 0; blockNdx < interface.getNumUniformBlocks(); blockNdx++)
{
const UniformBlock& block = interface.getUniformBlock(blockNdx);
if (block.getFlags() & DECLARE_FRAGMENT)
generateDeclaration(src, block);
}
// Comparison utilities.
src << "\n" << s_compareFuncs;
src << "\n"
"void main (void)\n"
"{\n"
" mediump float result = 1.0;\n";
// Value compare.
generateCompareSrc(src, "result", interface, layout, blockPointers, false);
src << " dEQP_FragColor = vec4(1.0, v_vtxResult, result, 1.0);\n"
"}\n";
}
void getGLUniformLayout(const glw::Functions& gl, UniformLayout& layout, deUint32 program)
{
int numActiveUniforms = 0;
int numActiveBlocks = 0;
gl.getProgramiv(program, GL_ACTIVE_UNIFORMS, &numActiveUniforms);
gl.getProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &numActiveBlocks);
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to get number of uniforms and uniform blocks");
// Block entries.
layout.blocks.resize(numActiveBlocks);
for (int blockNdx = 0; blockNdx < numActiveBlocks; blockNdx++)
{
BlockLayoutEntry& entry = layout.blocks[blockNdx];
int size;
int nameLen;
int numBlockUniforms;
gl.getActiveUniformBlockiv(program, (deUint32)blockNdx, GL_UNIFORM_BLOCK_DATA_SIZE, &size);
gl.getActiveUniformBlockiv(program, (deUint32)blockNdx, GL_UNIFORM_BLOCK_NAME_LENGTH, &nameLen);
gl.getActiveUniformBlockiv(program, (deUint32)blockNdx, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &numBlockUniforms);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform block query failed");
// \note Some implementations incorrectly return 0 as name length even though the length should include null terminator.
std::vector<char> nameBuf(nameLen > 0 ? nameLen : 1);
gl.getActiveUniformBlockName(program, (deUint32)blockNdx, (glw::GLsizei)nameBuf.size(), DE_NULL, &nameBuf[0]);
entry.name = std::string(&nameBuf[0]);
entry.size = size;
entry.activeUniformIndices.resize(numBlockUniforms);
if (numBlockUniforms > 0)
gl.getActiveUniformBlockiv(program, (deUint32)blockNdx, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES,
&entry.activeUniformIndices[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform block query failed");
}
if (numActiveUniforms > 0)
{
// Uniform entries.
std::vector<deUint32> uniformIndices(numActiveUniforms);
for (int i = 0; i < numActiveUniforms; i++)
uniformIndices[i] = (deUint32)i;
std::vector<int> types(numActiveUniforms);
std::vector<int> sizes(numActiveUniforms);
std::vector<int> nameLengths(numActiveUniforms);
std::vector<int> blockIndices(numActiveUniforms);
std::vector<int> offsets(numActiveUniforms);
std::vector<int> arrayStrides(numActiveUniforms);
std::vector<int> matrixStrides(numActiveUniforms);
std::vector<int> rowMajorFlags(numActiveUniforms);
// Execute queries.
gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_TYPE,
&types[0]);
gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_SIZE,
&sizes[0]);
gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_NAME_LENGTH,
&nameLengths[0]);
gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_BLOCK_INDEX,
&blockIndices[0]);
gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0], GL_UNIFORM_OFFSET,
&offsets[0]);
gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0],
GL_UNIFORM_ARRAY_STRIDE, &arrayStrides[0]);
gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0],
GL_UNIFORM_MATRIX_STRIDE, &matrixStrides[0]);
gl.getActiveUniformsiv(program, (glw::GLsizei)uniformIndices.size(), &uniformIndices[0],
GL_UNIFORM_IS_ROW_MAJOR, &rowMajorFlags[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "Active uniform query failed");
// Translate to LayoutEntries
layout.uniforms.resize(numActiveUniforms);
for (int uniformNdx = 0; uniformNdx < numActiveUniforms; uniformNdx++)
{
UniformLayoutEntry& entry = layout.uniforms[uniformNdx];
std::vector<char> nameBuf(nameLengths[uniformNdx]);
glw::GLsizei nameLen = 0;
int size = 0;
deUint32 type = GL_NONE;
gl.getActiveUniform(program, (deUint32)uniformNdx, (glw::GLsizei)nameBuf.size(), &nameLen, &size, &type,
&nameBuf[0]);
GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform name query failed");
// \note glGetActiveUniform() returns length without \0 and glGetActiveUniformsiv() with \0
if (nameLen + 1 != nameLengths[uniformNdx] || size != sizes[uniformNdx] ||
type != (deUint32)types[uniformNdx])
TCU_FAIL("Values returned by glGetActiveUniform() don't match with values queried with "
"glGetActiveUniformsiv().");
entry.name = std::string(&nameBuf[0]);
entry.type = glu::getDataTypeFromGLType(types[uniformNdx]);
entry.size = sizes[uniformNdx];
entry.blockNdx = blockIndices[uniformNdx];
entry.offset = offsets[uniformNdx];
entry.arrayStride = arrayStrides[uniformNdx];
entry.matrixStride = matrixStrides[uniformNdx];
entry.isRowMajor = rowMajorFlags[uniformNdx] != GL_FALSE;
}
}
}
void copyUniformData(const UniformLayoutEntry& dstEntry, void* dstBlockPtr, const UniformLayoutEntry& srcEntry,
const void* srcBlockPtr)
{
deUint8* dstBasePtr = (deUint8*)dstBlockPtr + dstEntry.offset;
const deUint8* srcBasePtr = (const deUint8*)srcBlockPtr + srcEntry.offset;
DE_ASSERT(dstEntry.size <= srcEntry.size);
DE_ASSERT(dstEntry.type == srcEntry.type);
int scalarSize = glu::getDataTypeScalarSize(dstEntry.type);
bool isMatrix = glu::isDataTypeMatrix(dstEntry.type);
const int compSize = sizeof(deUint32);
for (int elementNdx = 0; elementNdx < dstEntry.size; elementNdx++)
{
deUint8* dstElemPtr = dstBasePtr + elementNdx * dstEntry.arrayStride;
const deUint8* srcElemPtr = srcBasePtr + elementNdx * srcEntry.arrayStride;
if (isMatrix)
{
int numRows = glu::getDataTypeMatrixNumRows(dstEntry.type);
int numCols = glu::getDataTypeMatrixNumColumns(dstEntry.type);
for (int colNdx = 0; colNdx < numCols; colNdx++)
{
for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
{
deUint8* dstCompPtr =
dstElemPtr + (dstEntry.isRowMajor ? rowNdx * dstEntry.matrixStride + colNdx * compSize :
colNdx * dstEntry.matrixStride + rowNdx * compSize);
const deUint8* srcCompPtr =
srcElemPtr + (srcEntry.isRowMajor ? rowNdx * srcEntry.matrixStride + colNdx * compSize :
colNdx * srcEntry.matrixStride + rowNdx * compSize);
deMemcpy(dstCompPtr, srcCompPtr, compSize);
}
}
}
else
deMemcpy(dstElemPtr, srcElemPtr, scalarSize * compSize);
}
}
void copyUniformData(const UniformLayout& dstLayout, const std::map<int, void*>& dstBlockPointers,
const UniformLayout& srcLayout, const std::map<int, void*>& srcBlockPointers)
{
// \note Src layout is used as reference in case of activeUniforms happens to be incorrect in dstLayout blocks.
int numBlocks = (int)srcLayout.blocks.size();
for (int srcBlockNdx = 0; srcBlockNdx < numBlocks; srcBlockNdx++)
{
const BlockLayoutEntry& srcBlock = srcLayout.blocks[srcBlockNdx];
const void* srcBlockPtr = srcBlockPointers.find(srcBlockNdx)->second;
int dstBlockNdx = dstLayout.getBlockIndex(srcBlock.name.c_str());
void* dstBlockPtr = dstBlockNdx >= 0 ? dstBlockPointers.find(dstBlockNdx)->second : DE_NULL;
if (dstBlockNdx < 0)
continue;
for (vector<int>::const_iterator srcUniformNdxIter = srcBlock.activeUniformIndices.begin();
srcUniformNdxIter != srcBlock.activeUniformIndices.end(); srcUniformNdxIter++)
{
const UniformLayoutEntry& srcEntry = srcLayout.uniforms[*srcUniformNdxIter];
int dstUniformNdx = dstLayout.getUniformIndex(srcEntry.name.c_str());
if (dstUniformNdx < 0)
continue;
copyUniformData(dstLayout.uniforms[dstUniformNdx], dstBlockPtr, srcEntry, srcBlockPtr);
}
}
}
} // anonymous (utilities)
class UniformBufferManager
{
public:
UniformBufferManager(const glu::RenderContext& renderCtx);
~UniformBufferManager(void);
deUint32 allocBuffer(void);
private:
UniformBufferManager(const UniformBufferManager& other);
UniformBufferManager& operator=(const UniformBufferManager& other);
const glu::RenderContext& m_renderCtx;
std::vector<deUint32> m_buffers;
};
UniformBufferManager::UniformBufferManager(const glu::RenderContext& renderCtx) : m_renderCtx(renderCtx)
{
}
UniformBufferManager::~UniformBufferManager(void)
{
if (!m_buffers.empty())
m_renderCtx.getFunctions().deleteBuffers((glw::GLsizei)m_buffers.size(), &m_buffers[0]);
}
deUint32 UniformBufferManager::allocBuffer(void)
{
deUint32 buf = 0;
m_buffers.reserve(m_buffers.size() + 1);
m_renderCtx.getFunctions().genBuffers(1, &buf);
GLU_EXPECT_NO_ERROR(m_renderCtx.getFunctions().getError(), "Failed to allocate uniform buffer");
m_buffers.push_back(buf);
return buf;
}
} // ub
using namespace ub;
// UniformBlockCase.
UniformBlockCase::UniformBlockCase(Context& context, const char* name, const char* description,
glu::GLSLVersion glslVersion, BufferMode bufferMode)
: TestCase(context, name, description), m_glslVersion(glslVersion), m_bufferMode(bufferMode)
{
// \todo [2013-05-25 pyry] Support other versions as well.
DE_ASSERT(glslVersion == glu::GLSL_VERSION_300_ES || glslVersion == glu::GLSL_VERSION_330);
}
UniformBlockCase::~UniformBlockCase(void)
{
}
UniformBlockCase::IterateResult UniformBlockCase::iterate(void)
{
TestLog& log = m_testCtx.getLog();
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
UniformLayout refLayout; //!< std140 layout.
vector<deUint8> data; //!< Data.
map<int, void*> blockPointers; //!< Reference block pointers.
// Initialize result to pass.
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
// Compute reference layout.
computeStd140Layout(refLayout, m_interface);
// Assign storage for reference values.
{
int totalSize = 0;
for (vector<BlockLayoutEntry>::const_iterator blockIter = refLayout.blocks.begin();
blockIter != refLayout.blocks.end(); blockIter++)
totalSize += blockIter->size;
data.resize(totalSize);
// Pointers for each block.
int curOffset = 0;
for (int blockNdx = 0; blockNdx < (int)refLayout.blocks.size(); blockNdx++)
{
blockPointers[blockNdx] = &data[0] + curOffset;
curOffset += refLayout.blocks[blockNdx].size;
}
}
// Generate values.
generateValues(refLayout, blockPointers, 1 /* seed */);
// Generate shaders and build program.
std::ostringstream vtxSrc;
std::ostringstream fragSrc;
generateVertexShader(vtxSrc, m_glslVersion, m_interface, refLayout, blockPointers);
generateFragmentShader(fragSrc, m_glslVersion, m_interface, refLayout, blockPointers);
glu::ShaderProgram program(m_context.getRenderContext(),
glu::makeVtxFragSources(vtxSrc.str().c_str(), fragSrc.str().c_str()));
log << program;
if (!program.isOk())
{
// Compile failed.
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed");
return STOP;
}
// Query layout from GL.
UniformLayout glLayout;
getGLUniformLayout(gl, glLayout, program.getProgram());
// Print layout to log.
log << TestLog::Section("ActiveUniformBlocks", "Active Uniform Blocks");
for (int blockNdx = 0; blockNdx < (int)glLayout.blocks.size(); blockNdx++)
log << TestLog::Message << blockNdx << ": " << glLayout.blocks[blockNdx] << TestLog::EndMessage;
log << TestLog::EndSection;
log << TestLog::Section("ActiveUniforms", "Active Uniforms");
for (int uniformNdx = 0; uniformNdx < (int)glLayout.uniforms.size(); uniformNdx++)
log << TestLog::Message << uniformNdx << ": " << glLayout.uniforms[uniformNdx] << TestLog::EndMessage;
log << TestLog::EndSection;
// Check that we can even try rendering with given layout.
if (!checkLayoutIndices(glLayout) || !checkLayoutBounds(glLayout) || !compareTypes(refLayout, glLayout))
{
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid layout");
return STOP; // It is not safe to use the given layout.
}
// Verify all std140 blocks.
if (!compareStd140Blocks(refLayout, glLayout))
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid std140 layout");
// Verify all shared blocks - all uniforms should be active, and certain properties match.
if (!compareSharedBlocks(refLayout, glLayout))
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid shared layout");
// Check consistency with index queries
if (!checkIndexQueries(program.getProgram(), glLayout))
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Inconsintent block index query results");
// Use program.
gl.useProgram(program.getProgram());
// Assign binding points to all active uniform blocks.
for (int blockNdx = 0; blockNdx < (int)glLayout.blocks.size(); blockNdx++)
{
deUint32 binding = (deUint32)blockNdx; // \todo [2012-01-25 pyry] Randomize order?
gl.uniformBlockBinding(program.getProgram(), (deUint32)blockNdx, binding);
}
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to set uniform block bindings");
// Allocate buffers, write data and bind to targets.
UniformBufferManager bufferManager(m_context.getRenderContext());
if (m_bufferMode == BUFFERMODE_PER_BLOCK)
{
int numBlocks = (int)glLayout.blocks.size();
vector<vector<deUint8> > glData(numBlocks);
map<int, void*> glBlockPointers;
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
glData[blockNdx].resize(glLayout.blocks[blockNdx].size);
glBlockPointers[blockNdx] = &glData[blockNdx][0];
}
copyUniformData(glLayout, glBlockPointers, refLayout, blockPointers);
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
deUint32 buffer = bufferManager.allocBuffer();
deUint32 binding = (deUint32)blockNdx;
gl.bindBuffer(GL_UNIFORM_BUFFER, buffer);
gl.bufferData(GL_UNIFORM_BUFFER, (glw::GLsizeiptr)glData[blockNdx].size(), &glData[blockNdx][0],
GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to upload uniform buffer data");
gl.bindBufferBase(GL_UNIFORM_BUFFER, binding, buffer);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferBase(GL_UNIFORM_BUFFER) failed");
}
}
else
{
DE_ASSERT(m_bufferMode == BUFFERMODE_SINGLE);
int totalSize = 0;
int curOffset = 0;
int numBlocks = (int)glLayout.blocks.size();
int bindingAlignment = m_context.getContextInfo().getInt(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
map<int, int> glBlockOffsets;
// Compute total size and offsets.
curOffset = 0;
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
if (bindingAlignment > 0)
curOffset = deRoundUp32(curOffset, bindingAlignment);
glBlockOffsets[blockNdx] = curOffset;
curOffset += glLayout.blocks[blockNdx].size;
}
totalSize = curOffset;
// Assign block pointers.
vector<deUint8> glData(totalSize);
map<int, void*> glBlockPointers;
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
glBlockPointers[blockNdx] = &glData[glBlockOffsets[blockNdx]];
// Copy to gl format.
copyUniformData(glLayout, glBlockPointers, refLayout, blockPointers);
// Allocate buffer and upload data.
deUint32 buffer = bufferManager.allocBuffer();
gl.bindBuffer(GL_UNIFORM_BUFFER, buffer);
if (!glData.empty())
gl.bufferData(GL_UNIFORM_BUFFER, (glw::GLsizeiptr)glData.size(), &glData[0], GL_STATIC_DRAW);
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to upload uniform buffer data");
// Bind ranges to binding points.
curOffset = 0;
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
deUint32 binding = (deUint32)blockNdx;
gl.bindBufferRange(GL_UNIFORM_BUFFER, binding, buffer, (glw::GLintptr)glBlockOffsets[blockNdx],
(glw::GLsizeiptr)glLayout.blocks[blockNdx].size);
GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBufferRange(GL_UNIFORM_BUFFER) failed");
curOffset += glLayout.blocks[blockNdx].size;
}
}
bool renderOk = render(program);
if (!renderOk)
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Image compare failed");
return STOP;
}
bool UniformBlockCase::compareStd140Blocks(const UniformLayout& refLayout, const UniformLayout& cmpLayout) const
{
TestLog& log = m_testCtx.getLog();
bool isOk = true;
int numBlocks = m_interface.getNumUniformBlocks();
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
const UniformBlock& block = m_interface.getUniformBlock(blockNdx);
bool isArray = block.isArray();
std::string instanceName = string(block.getBlockName()) + (isArray ? "[0]" : "");
int refBlockNdx = refLayout.getBlockIndex(instanceName.c_str());
int cmpBlockNdx = cmpLayout.getBlockIndex(instanceName.c_str());
bool isUsed = (block.getFlags() & (DECLARE_VERTEX | DECLARE_FRAGMENT)) != 0;
if ((block.getFlags() & LAYOUT_STD140) == 0)
continue; // Not std140 layout.
DE_ASSERT(refBlockNdx >= 0);
if (cmpBlockNdx < 0)
{
// Not found, should it?
if (isUsed)
{
log << TestLog::Message << "Error: Uniform block '" << instanceName << "' not found"
<< TestLog::EndMessage;
isOk = false;
}
continue; // Skip block.
}
const BlockLayoutEntry& refBlockLayout = refLayout.blocks[refBlockNdx];
const BlockLayoutEntry& cmpBlockLayout = cmpLayout.blocks[cmpBlockNdx];
// \todo [2012-01-24 pyry] Verify that activeUniformIndices is correct.
// \todo [2012-01-24 pyry] Verify all instances.
if (refBlockLayout.activeUniformIndices.size() != cmpBlockLayout.activeUniformIndices.size())
{
log << TestLog::Message << "Error: Number of active uniforms differ in block '" << instanceName
<< "' (expected " << refBlockLayout.activeUniformIndices.size() << ", got "
<< cmpBlockLayout.activeUniformIndices.size() << ")" << TestLog::EndMessage;
isOk = false;
}
for (vector<int>::const_iterator ndxIter = refBlockLayout.activeUniformIndices.begin();
ndxIter != refBlockLayout.activeUniformIndices.end(); ndxIter++)
{
const UniformLayoutEntry& refEntry = refLayout.uniforms[*ndxIter];
int cmpEntryNdx = cmpLayout.getUniformIndex(refEntry.name.c_str());
if (cmpEntryNdx < 0)
{
log << TestLog::Message << "Error: Uniform '" << refEntry.name << "' not found" << TestLog::EndMessage;
isOk = false;
continue;
}
const UniformLayoutEntry& cmpEntry = cmpLayout.uniforms[cmpEntryNdx];
if (refEntry.type != cmpEntry.type || refEntry.size != cmpEntry.size ||
refEntry.offset != cmpEntry.offset || refEntry.arrayStride != cmpEntry.arrayStride ||
refEntry.matrixStride != cmpEntry.matrixStride || refEntry.isRowMajor != cmpEntry.isRowMajor)
{
log << TestLog::Message << "Error: Layout mismatch in '" << refEntry.name << "':\n"
<< " expected: type = " << glu::getDataTypeName(refEntry.type) << ", size = " << refEntry.size
<< ", offset = " << refEntry.offset << ", array stride = " << refEntry.arrayStride
<< ", matrix stride = " << refEntry.matrixStride
<< ", row major = " << (refEntry.isRowMajor ? "true" : "false") << "\n"
<< " got: type = " << glu::getDataTypeName(cmpEntry.type) << ", size = " << cmpEntry.size
<< ", offset = " << cmpEntry.offset << ", array stride = " << cmpEntry.arrayStride
<< ", matrix stride = " << cmpEntry.matrixStride
<< ", row major = " << (cmpEntry.isRowMajor ? "true" : "false") << TestLog::EndMessage;
isOk = false;
}
}
}
return isOk;
}
bool UniformBlockCase::compareSharedBlocks(const UniformLayout& refLayout, const UniformLayout& cmpLayout) const
{
TestLog& log = m_testCtx.getLog();
bool isOk = true;
int numBlocks = m_interface.getNumUniformBlocks();
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
const UniformBlock& block = m_interface.getUniformBlock(blockNdx);
bool isArray = block.isArray();
std::string instanceName = string(block.getBlockName()) + (isArray ? "[0]" : "");
int refBlockNdx = refLayout.getBlockIndex(instanceName.c_str());
int cmpBlockNdx = cmpLayout.getBlockIndex(instanceName.c_str());
bool isUsed = (block.getFlags() & (DECLARE_VERTEX | DECLARE_FRAGMENT)) != 0;
if ((block.getFlags() & LAYOUT_SHARED) == 0)
continue; // Not shared layout.
DE_ASSERT(refBlockNdx >= 0);
if (cmpBlockNdx < 0)
{
// Not found, should it?
if (isUsed)
{
log << TestLog::Message << "Error: Uniform block '" << instanceName << "' not found"
<< TestLog::EndMessage;
isOk = false;
}
continue; // Skip block.
}
const BlockLayoutEntry& refBlockLayout = refLayout.blocks[refBlockNdx];
const BlockLayoutEntry& cmpBlockLayout = cmpLayout.blocks[cmpBlockNdx];
if (refBlockLayout.activeUniformIndices.size() != cmpBlockLayout.activeUniformIndices.size())
{
log << TestLog::Message << "Error: Number of active uniforms differ in block '" << instanceName
<< "' (expected " << refBlockLayout.activeUniformIndices.size() << ", got "
<< cmpBlockLayout.activeUniformIndices.size() << ")" << TestLog::EndMessage;
isOk = false;
}
for (vector<int>::const_iterator ndxIter = refBlockLayout.activeUniformIndices.begin();
ndxIter != refBlockLayout.activeUniformIndices.end(); ndxIter++)
{
const UniformLayoutEntry& refEntry = refLayout.uniforms[*ndxIter];
int cmpEntryNdx = cmpLayout.getUniformIndex(refEntry.name.c_str());
if (cmpEntryNdx < 0)
{
log << TestLog::Message << "Error: Uniform '" << refEntry.name << "' not found" << TestLog::EndMessage;
isOk = false;
continue;
}
const UniformLayoutEntry& cmpEntry = cmpLayout.uniforms[cmpEntryNdx];
if (refEntry.type != cmpEntry.type || refEntry.size != cmpEntry.size ||
refEntry.isRowMajor != cmpEntry.isRowMajor)
{
log << TestLog::Message << "Error: Layout mismatch in '" << refEntry.name << "':\n"
<< " expected: type = " << glu::getDataTypeName(refEntry.type) << ", size = " << refEntry.size
<< ", row major = " << (refEntry.isRowMajor ? "true" : "false") << "\n"
<< " got: type = " << glu::getDataTypeName(cmpEntry.type) << ", size = " << cmpEntry.size
<< ", row major = " << (cmpEntry.isRowMajor ? "true" : "false") << TestLog::EndMessage;
isOk = false;
}
}
}
return isOk;
}
bool UniformBlockCase::compareTypes(const UniformLayout& refLayout, const UniformLayout& cmpLayout) const
{
TestLog& log = m_testCtx.getLog();
bool isOk = true;
int numBlocks = m_interface.getNumUniformBlocks();
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
const UniformBlock& block = m_interface.getUniformBlock(blockNdx);
bool isArray = block.isArray();
int numInstances = isArray ? block.getArraySize() : 1;
for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
{
std::ostringstream instanceName;
instanceName << block.getBlockName();
if (isArray)
instanceName << "[" << instanceNdx << "]";
int cmpBlockNdx = cmpLayout.getBlockIndex(instanceName.str().c_str());
if (cmpBlockNdx < 0)
continue;
const BlockLayoutEntry& cmpBlockLayout = cmpLayout.blocks[cmpBlockNdx];
for (vector<int>::const_iterator ndxIter = cmpBlockLayout.activeUniformIndices.begin();
ndxIter != cmpBlockLayout.activeUniformIndices.end(); ndxIter++)
{
const UniformLayoutEntry& cmpEntry = cmpLayout.uniforms[*ndxIter];
int refEntryNdx = refLayout.getUniformIndex(cmpEntry.name.c_str());
if (refEntryNdx < 0)
{
log << TestLog::Message << "Error: Uniform '" << cmpEntry.name << "' not found in reference layout"
<< TestLog::EndMessage;
isOk = false;
continue;
}
const UniformLayoutEntry& refEntry = refLayout.uniforms[refEntryNdx];
// \todo [2012-11-26 pyry] Should we check other properties as well?
if (refEntry.type != cmpEntry.type)
{
log << TestLog::Message << "Error: Uniform type mismatch in '" << refEntry.name << "':\n"
<< " expected: " << glu::getDataTypeName(refEntry.type) << "\n"
<< " got: " << glu::getDataTypeName(cmpEntry.type) << TestLog::EndMessage;
isOk = false;
}
}
}
}
return isOk;
}
bool UniformBlockCase::checkLayoutIndices(const UniformLayout& layout) const
{
TestLog& log = m_testCtx.getLog();
int numUniforms = (int)layout.uniforms.size();
int numBlocks = (int)layout.blocks.size();
bool isOk = true;
// Check uniform block indices.
for (int uniformNdx = 0; uniformNdx < numUniforms; uniformNdx++)
{
const UniformLayoutEntry& uniform = layout.uniforms[uniformNdx];
if (uniform.blockNdx < 0 || !deInBounds32(uniform.blockNdx, 0, numBlocks))
{
log << TestLog::Message << "Error: Invalid block index in uniform '" << uniform.name << "'"
<< TestLog::EndMessage;
isOk = false;
}
}
// Check active uniforms.
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
const BlockLayoutEntry& block = layout.blocks[blockNdx];
for (vector<int>::const_iterator uniformIter = block.activeUniformIndices.begin();
uniformIter != block.activeUniformIndices.end(); uniformIter++)
{
if (!deInBounds32(*uniformIter, 0, numUniforms))
{
log << TestLog::Message << "Error: Invalid active uniform index " << *uniformIter << " in block '"
<< block.name << "'" << TestLog::EndMessage;
isOk = false;
}
}
}
return isOk;
}
bool UniformBlockCase::checkLayoutBounds(const UniformLayout& layout) const
{
TestLog& log = m_testCtx.getLog();
int numUniforms = (int)layout.uniforms.size();
bool isOk = true;
for (int uniformNdx = 0; uniformNdx < numUniforms; uniformNdx++)
{
const UniformLayoutEntry& uniform = layout.uniforms[uniformNdx];
if (uniform.blockNdx < 0)
continue;
const BlockLayoutEntry& block = layout.blocks[uniform.blockNdx];
bool isMatrix = glu::isDataTypeMatrix(uniform.type);
int numVecs = isMatrix ? (uniform.isRowMajor ? glu::getDataTypeMatrixNumRows(uniform.type) :
glu::getDataTypeMatrixNumColumns(uniform.type)) :
1;
int numComps = isMatrix ? (uniform.isRowMajor ? glu::getDataTypeMatrixNumColumns(uniform.type) :
glu::getDataTypeMatrixNumRows(uniform.type)) :
glu::getDataTypeScalarSize(uniform.type);
int numElements = uniform.size;
const int compSize = sizeof(deUint32);
int vecSize = numComps * compSize;
int minOffset = 0;
int maxOffset = 0;
// For negative strides.
minOffset = de::min(minOffset, (numVecs - 1) * uniform.matrixStride);
minOffset = de::min(minOffset, (numElements - 1) * uniform.arrayStride);
minOffset = de::min(minOffset, (numElements - 1) * uniform.arrayStride + (numVecs - 1) * uniform.matrixStride);
maxOffset = de::max(maxOffset, vecSize);
maxOffset = de::max(maxOffset, (numVecs - 1) * uniform.matrixStride + vecSize);
maxOffset = de::max(maxOffset, (numElements - 1) * uniform.arrayStride + vecSize);
maxOffset = de::max(maxOffset,
(numElements - 1) * uniform.arrayStride + (numVecs - 1) * uniform.matrixStride + vecSize);
if (uniform.offset + minOffset < 0 || uniform.offset + maxOffset > block.size)
{
log << TestLog::Message << "Error: Uniform '" << uniform.name << "' out of block bounds"
<< TestLog::EndMessage;
isOk = false;
}
}
return isOk;
}
bool UniformBlockCase::checkIndexQueries(deUint32 program, const UniformLayout& layout) const
{
tcu::TestLog& log = m_testCtx.getLog();
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
bool allOk = true;
// \note Spec mandates that uniform blocks are assigned consecutive locations from 0
// to ACTIVE_UNIFORM_BLOCKS. BlockLayoutEntries are stored in that order in UniformLayout.
for (int blockNdx = 0; blockNdx < (int)layout.blocks.size(); blockNdx++)
{
const BlockLayoutEntry& block = layout.blocks[blockNdx];
const int queriedNdx = gl.getUniformBlockIndex(program, block.name.c_str());
if (queriedNdx != blockNdx)
{
log << TestLog::Message << "ERROR: glGetUniformBlockIndex(" << block.name << ") returned " << queriedNdx
<< ", expected " << blockNdx << "!" << TestLog::EndMessage;
allOk = false;
}
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformBlockIndex()");
}
return allOk;
}
bool UniformBlockCase::render(glu::ShaderProgram& program) const
{
tcu::TestLog& log = m_testCtx.getLog();
const glw::Functions& gl = m_context.getRenderContext().getFunctions();
de::Random rnd(deStringHash(getName()));
const tcu::RenderTarget& renderTarget = m_context.getRenderContext().getRenderTarget();
const int viewportW = de::min(renderTarget.getWidth(), 128);
const int viewportH = de::min(renderTarget.getHeight(), 128);
const int viewportX = rnd.getInt(0, renderTarget.getWidth() - viewportW);
const int viewportY = rnd.getInt(0, renderTarget.getHeight() - viewportH);
// Draw
{
const float position[] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, +1.0f, 0.0f, 1.0f,
+1.0f, -1.0f, 0.0f, 1.0f, +1.0f, +1.0f, 0.0f, 1.0f };
const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 };
gl.viewport(viewportX, viewportY, viewportW, viewportH);
glu::VertexArrayBinding posArray = glu::va::Float("a_position", 4, 4, 0, &position[0]);
glu::draw(m_context.getRenderContext(), program.getProgram(), 1, &posArray,
glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
GLU_EXPECT_NO_ERROR(gl.getError(), "Draw failed");
}
// Verify that all pixels are white.
{
tcu::Surface pixels(viewportW, viewportH);
int numFailedPixels = 0;
glu::readPixels(m_context.getRenderContext(), viewportX, viewportY, pixels.getAccess());
GLU_EXPECT_NO_ERROR(gl.getError(), "Reading pixels failed");
for (int y = 0; y < pixels.getHeight(); y++)
{
for (int x = 0; x < pixels.getWidth(); x++)
{
if (pixels.getPixel(x, y) != tcu::RGBA::white())
numFailedPixels += 1;
}
}
if (numFailedPixels > 0)
{
log << TestLog::Image("Image", "Rendered image", pixels);
log << TestLog::Message << "Image comparison failed, got " << numFailedPixels << " non-white pixels"
<< TestLog::EndMessage;
}
return numFailedPixels == 0;
}
}
} // deqp