You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2673 lines
90 KiB
2673 lines
90 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program OpenGL ES 3.1 Module
|
|
* -------------------------------------------------
|
|
*
|
|
* Copyright 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief SSBO layout case.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "es31fSSBOLayoutCase.hpp"
|
|
#include "gluRenderContext.hpp"
|
|
#include "gluShaderProgram.hpp"
|
|
#include "gluPixelTransfer.hpp"
|
|
#include "gluContextInfo.hpp"
|
|
#include "gluRenderContext.hpp"
|
|
#include "gluProgramInterfaceQuery.hpp"
|
|
#include "gluObjectWrapper.hpp"
|
|
#include "gluVarTypeUtil.hpp"
|
|
#include "glwFunctions.hpp"
|
|
#include "glwEnums.hpp"
|
|
#include "tcuTestLog.hpp"
|
|
#include "tcuSurface.hpp"
|
|
#include "tcuRenderTarget.hpp"
|
|
#include "deInt32.h"
|
|
#include "deRandom.hpp"
|
|
#include "deMath.h"
|
|
#include "deMemory.h"
|
|
#include "deString.h"
|
|
#include "deStringUtil.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <map>
|
|
|
|
using tcu::TestLog;
|
|
using std::string;
|
|
using std::vector;
|
|
using std::map;
|
|
|
|
namespace deqp
|
|
{
|
|
namespace gles31
|
|
{
|
|
|
|
using glu::VarType;
|
|
using glu::StructType;
|
|
using glu::StructMember;
|
|
|
|
namespace bb
|
|
{
|
|
|
|
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_STD430, "std430" },
|
|
{ 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;
|
|
}
|
|
|
|
// BufferVar implementation.
|
|
|
|
BufferVar::BufferVar (const char* name, const VarType& type, deUint32 flags)
|
|
: m_name (name)
|
|
, m_type (type)
|
|
, m_flags (flags)
|
|
{
|
|
}
|
|
|
|
// BufferBlock implementation.
|
|
|
|
BufferBlock::BufferBlock (const char* blockName)
|
|
: m_blockName (blockName)
|
|
, m_arraySize (-1)
|
|
, m_flags (0)
|
|
{
|
|
setArraySize(0);
|
|
}
|
|
|
|
void BufferBlock::setArraySize (int arraySize)
|
|
{
|
|
DE_ASSERT(arraySize >= 0);
|
|
m_lastUnsizedArraySizes.resize(arraySize == 0 ? 1 : arraySize, 0);
|
|
m_arraySize = arraySize;
|
|
}
|
|
|
|
struct BlockLayoutEntry
|
|
{
|
|
BlockLayoutEntry (void)
|
|
: size(0)
|
|
{
|
|
}
|
|
|
|
std::string name;
|
|
int size;
|
|
std::vector<int> activeVarIndices;
|
|
};
|
|
|
|
std::ostream& operator<< (std::ostream& stream, const BlockLayoutEntry& entry)
|
|
{
|
|
stream << entry.name << " { name = " << entry.name
|
|
<< ", size = " << entry.size
|
|
<< ", activeVarIndices = [";
|
|
|
|
for (vector<int>::const_iterator i = entry.activeVarIndices.begin(); i != entry.activeVarIndices.end(); i++)
|
|
{
|
|
if (i != entry.activeVarIndices.begin())
|
|
stream << ", ";
|
|
stream << *i;
|
|
}
|
|
|
|
stream << "] }";
|
|
return stream;
|
|
}
|
|
|
|
struct BufferVarLayoutEntry
|
|
{
|
|
BufferVarLayoutEntry (void)
|
|
: type (glu::TYPE_LAST)
|
|
, blockNdx (-1)
|
|
, offset (-1)
|
|
, arraySize (-1)
|
|
, arrayStride (-1)
|
|
, matrixStride (-1)
|
|
, topLevelArraySize (-1)
|
|
, topLevelArrayStride (-1)
|
|
, isRowMajor (false)
|
|
{
|
|
}
|
|
|
|
std::string name;
|
|
glu::DataType type;
|
|
int blockNdx;
|
|
int offset;
|
|
int arraySize;
|
|
int arrayStride;
|
|
int matrixStride;
|
|
int topLevelArraySize;
|
|
int topLevelArrayStride;
|
|
bool isRowMajor;
|
|
};
|
|
|
|
static bool isUnsizedArray (const BufferVarLayoutEntry& entry)
|
|
{
|
|
DE_ASSERT(entry.arraySize != 0 || entry.topLevelArraySize != 0);
|
|
return entry.arraySize == 0 || entry.topLevelArraySize == 0;
|
|
}
|
|
|
|
std::ostream& operator<< (std::ostream& stream, const BufferVarLayoutEntry& entry)
|
|
{
|
|
stream << entry.name << " { type = " << glu::getDataTypeName(entry.type)
|
|
<< ", blockNdx = " << entry.blockNdx
|
|
<< ", offset = " << entry.offset
|
|
<< ", arraySize = " << entry.arraySize
|
|
<< ", arrayStride = " << entry.arrayStride
|
|
<< ", matrixStride = " << entry.matrixStride
|
|
<< ", topLevelArraySize = " << entry.topLevelArraySize
|
|
<< ", topLevelArrayStride = " << entry.topLevelArrayStride
|
|
<< ", isRowMajor = " << (entry.isRowMajor ? "true" : "false")
|
|
<< " }";
|
|
return stream;
|
|
}
|
|
|
|
class BufferLayout
|
|
{
|
|
public:
|
|
std::vector<BlockLayoutEntry> blocks;
|
|
std::vector<BufferVarLayoutEntry> bufferVars;
|
|
|
|
int getVariableIndex (const string& name) const;
|
|
int getBlockIndex (const string& name) const;
|
|
};
|
|
|
|
// \todo [2012-01-24 pyry] Speed up lookups using hash.
|
|
|
|
int BufferLayout::getVariableIndex (const string& name) const
|
|
{
|
|
for (int ndx = 0; ndx < (int)bufferVars.size(); ndx++)
|
|
{
|
|
if (bufferVars[ndx].name == name)
|
|
return ndx;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int BufferLayout::getBlockIndex (const string& 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<BufferBlock*>::iterator i = m_bufferBlocks.begin(); i != m_bufferBlocks.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);
|
|
}
|
|
}
|
|
|
|
BufferBlock& ShaderInterface::allocBlock (const char* name)
|
|
{
|
|
m_bufferBlocks.reserve(m_bufferBlocks.size()+1);
|
|
m_bufferBlocks.push_back(new BufferBlock(name));
|
|
return *m_bufferBlocks.back();
|
|
}
|
|
|
|
// BlockDataPtr
|
|
|
|
struct BlockDataPtr
|
|
{
|
|
void* ptr;
|
|
int size; //!< Redundant, for debugging purposes.
|
|
int lastUnsizedArraySize;
|
|
|
|
BlockDataPtr (void* ptr_, int size_, int lastUnsizedArraySize_)
|
|
: ptr (ptr_)
|
|
, size (size_)
|
|
, lastUnsizedArraySize (lastUnsizedArraySize_)
|
|
{
|
|
}
|
|
|
|
BlockDataPtr (void)
|
|
: ptr (DE_NULL)
|
|
, size (0)
|
|
, lastUnsizedArraySize (0)
|
|
{
|
|
}
|
|
};
|
|
|
|
namespace // Utilities
|
|
{
|
|
|
|
int findBlockIndex (const BufferLayout& layout, const string& name)
|
|
{
|
|
for (int ndx = 0; ndx < (int)layout.blocks.size(); ndx++)
|
|
{
|
|
if (layout.blocks[ndx].name == name)
|
|
return ndx;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Layout computation.
|
|
|
|
int getDataTypeByteSize (glu::DataType type)
|
|
{
|
|
return glu::getDataTypeScalarSize(type)*(int)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 1*(int)sizeof(deUint32);
|
|
|
|
case glu::TYPE_FLOAT_VEC2:
|
|
case glu::TYPE_INT_VEC2:
|
|
case glu::TYPE_UINT_VEC2:
|
|
case glu::TYPE_BOOL_VEC2: return 2*(int)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 4*(int)sizeof(deUint32);
|
|
|
|
default:
|
|
DE_ASSERT(false);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int computeStd140BaseAlignment (const VarType& type, deUint32 layoutFlags)
|
|
{
|
|
const int vec4Alignment = (int)sizeof(deUint32)*4;
|
|
|
|
if (type.isBasicType())
|
|
{
|
|
glu::DataType basicType = type.getBasicType();
|
|
|
|
if (glu::isDataTypeMatrix(basicType))
|
|
{
|
|
const bool isRowMajor = !!(layoutFlags & LAYOUT_ROW_MAJOR);
|
|
const int vecSize = isRowMajor ? glu::getDataTypeMatrixNumColumns(basicType)
|
|
: glu::getDataTypeMatrixNumRows(basicType);
|
|
const int vecAlign = deAlign32(getDataTypeByteAlignment(glu::getDataTypeFloatVec(vecSize)), vec4Alignment);
|
|
|
|
return vecAlign;
|
|
}
|
|
else
|
|
return getDataTypeByteAlignment(basicType);
|
|
}
|
|
else if (type.isArrayType())
|
|
{
|
|
int elemAlignment = computeStd140BaseAlignment(type.getElementType(), layoutFlags);
|
|
|
|
// Round up to alignment of vec4
|
|
return deAlign32(elemAlignment, vec4Alignment);
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(type.isStructType());
|
|
|
|
int maxBaseAlignment = 0;
|
|
|
|
for (StructType::ConstIterator memberIter = type.getStructPtr()->begin(); memberIter != type.getStructPtr()->end(); memberIter++)
|
|
maxBaseAlignment = de::max(maxBaseAlignment, computeStd140BaseAlignment(memberIter->getType(), layoutFlags));
|
|
|
|
return deAlign32(maxBaseAlignment, vec4Alignment);
|
|
}
|
|
}
|
|
|
|
int computeStd430BaseAlignment (const VarType& type, deUint32 layoutFlags)
|
|
{
|
|
// Otherwise identical to std140 except that alignment of structures and arrays
|
|
// are not rounded up to alignment of vec4.
|
|
|
|
if (type.isBasicType())
|
|
{
|
|
glu::DataType basicType = type.getBasicType();
|
|
|
|
if (glu::isDataTypeMatrix(basicType))
|
|
{
|
|
const bool isRowMajor = !!(layoutFlags & LAYOUT_ROW_MAJOR);
|
|
const int vecSize = isRowMajor ? glu::getDataTypeMatrixNumColumns(basicType)
|
|
: glu::getDataTypeMatrixNumRows(basicType);
|
|
const int vecAlign = getDataTypeByteAlignment(glu::getDataTypeFloatVec(vecSize));
|
|
|
|
return vecAlign;
|
|
}
|
|
else
|
|
return getDataTypeByteAlignment(basicType);
|
|
}
|
|
else if (type.isArrayType())
|
|
{
|
|
return computeStd430BaseAlignment(type.getElementType(), layoutFlags);
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(type.isStructType());
|
|
|
|
int maxBaseAlignment = 0;
|
|
|
|
for (StructType::ConstIterator memberIter = type.getStructPtr()->begin(); memberIter != type.getStructPtr()->end(); memberIter++)
|
|
maxBaseAlignment = de::max(maxBaseAlignment, computeStd430BaseAlignment(memberIter->getType(), layoutFlags));
|
|
|
|
return maxBaseAlignment;
|
|
}
|
|
}
|
|
|
|
inline deUint32 mergeLayoutFlags (deUint32 prevFlags, deUint32 newFlags)
|
|
{
|
|
const deUint32 packingMask = LAYOUT_PACKED|LAYOUT_SHARED|LAYOUT_STD140|LAYOUT_STD430;
|
|
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;
|
|
}
|
|
|
|
//! Appends all child elements to layout, returns value that should be appended to offset.
|
|
int computeReferenceLayout (
|
|
BufferLayout& layout,
|
|
int curBlockNdx,
|
|
int baseOffset,
|
|
const std::string& curPrefix,
|
|
const VarType& type,
|
|
deUint32 layoutFlags)
|
|
{
|
|
// Reference layout uses std430 rules by default. std140 rules are
|
|
// choosen only for blocks that have std140 layout.
|
|
const bool isStd140 = (layoutFlags & LAYOUT_STD140) != 0;
|
|
const int baseAlignment = isStd140 ? computeStd140BaseAlignment(type, layoutFlags)
|
|
: computeStd430BaseAlignment(type, layoutFlags);
|
|
int curOffset = deAlign32(baseOffset, baseAlignment);
|
|
const int topLevelArraySize = 1; // Default values
|
|
const int topLevelArrayStride = 0;
|
|
|
|
if (type.isBasicType())
|
|
{
|
|
const glu::DataType basicType = type.getBasicType();
|
|
BufferVarLayoutEntry entry;
|
|
|
|
entry.name = curPrefix;
|
|
entry.type = basicType;
|
|
entry.arraySize = 1;
|
|
entry.arrayStride = 0;
|
|
entry.matrixStride = 0;
|
|
entry.topLevelArraySize = topLevelArraySize;
|
|
entry.topLevelArrayStride = topLevelArrayStride;
|
|
entry.blockNdx = curBlockNdx;
|
|
|
|
if (glu::isDataTypeMatrix(basicType))
|
|
{
|
|
// Array of vectors as specified in rules 5 & 7.
|
|
const bool isRowMajor = !!(layoutFlags & LAYOUT_ROW_MAJOR);
|
|
const int numVecs = isRowMajor ? glu::getDataTypeMatrixNumRows(basicType)
|
|
: glu::getDataTypeMatrixNumColumns(basicType);
|
|
|
|
entry.offset = curOffset;
|
|
entry.matrixStride = baseAlignment;
|
|
entry.isRowMajor = isRowMajor;
|
|
|
|
curOffset += numVecs*baseAlignment;
|
|
}
|
|
else
|
|
{
|
|
// Scalar or vector.
|
|
entry.offset = curOffset;
|
|
|
|
curOffset += getDataTypeByteSize(basicType);
|
|
}
|
|
|
|
layout.bufferVars.push_back(entry);
|
|
}
|
|
else if (type.isArrayType())
|
|
{
|
|
const VarType& elemType = type.getElementType();
|
|
|
|
if (elemType.isBasicType() && !glu::isDataTypeMatrix(elemType.getBasicType()))
|
|
{
|
|
// Array of scalars or vectors.
|
|
const glu::DataType elemBasicType = elemType.getBasicType();
|
|
const int stride = baseAlignment;
|
|
BufferVarLayoutEntry entry;
|
|
|
|
entry.name = curPrefix + "[0]"; // Array variables are always postfixed with [0]
|
|
entry.type = elemBasicType;
|
|
entry.blockNdx = curBlockNdx;
|
|
entry.offset = curOffset;
|
|
entry.arraySize = type.getArraySize();
|
|
entry.arrayStride = stride;
|
|
entry.matrixStride = 0;
|
|
entry.topLevelArraySize = topLevelArraySize;
|
|
entry.topLevelArrayStride = topLevelArrayStride;
|
|
|
|
curOffset += stride*type.getArraySize();
|
|
|
|
layout.bufferVars.push_back(entry);
|
|
}
|
|
else if (elemType.isBasicType() && glu::isDataTypeMatrix(elemType.getBasicType()))
|
|
{
|
|
// Array of matrices.
|
|
const glu::DataType elemBasicType = elemType.getBasicType();
|
|
const bool isRowMajor = !!(layoutFlags & LAYOUT_ROW_MAJOR);
|
|
const int numVecs = isRowMajor ? glu::getDataTypeMatrixNumRows(elemBasicType)
|
|
: glu::getDataTypeMatrixNumColumns(elemBasicType);
|
|
const int vecStride = baseAlignment;
|
|
BufferVarLayoutEntry entry;
|
|
|
|
entry.name = curPrefix + "[0]"; // Array variables are always postfixed with [0]
|
|
entry.type = elemBasicType;
|
|
entry.blockNdx = curBlockNdx;
|
|
entry.offset = curOffset;
|
|
entry.arraySize = type.getArraySize();
|
|
entry.arrayStride = vecStride*numVecs;
|
|
entry.matrixStride = vecStride;
|
|
entry.isRowMajor = isRowMajor;
|
|
entry.topLevelArraySize = topLevelArraySize;
|
|
entry.topLevelArrayStride = topLevelArrayStride;
|
|
|
|
curOffset += numVecs*vecStride*type.getArraySize();
|
|
|
|
layout.bufferVars.push_back(entry);
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(elemType.isStructType() || elemType.isArrayType());
|
|
|
|
for (int elemNdx = 0; elemNdx < type.getArraySize(); elemNdx++)
|
|
curOffset += computeReferenceLayout(layout, curBlockNdx, curOffset, curPrefix + "[" + de::toString(elemNdx) + "]", type.getElementType(), layoutFlags);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(type.isStructType());
|
|
|
|
for (StructType::ConstIterator memberIter = type.getStructPtr()->begin(); memberIter != type.getStructPtr()->end(); memberIter++)
|
|
curOffset += computeReferenceLayout(layout, curBlockNdx, curOffset, curPrefix + "." + memberIter->getName(), memberIter->getType(), layoutFlags);
|
|
|
|
curOffset = deAlign32(curOffset, baseAlignment);
|
|
}
|
|
|
|
return curOffset-baseOffset;
|
|
}
|
|
|
|
//! Appends all child elements to layout, returns offset increment.
|
|
int computeReferenceLayout (BufferLayout& layout, int curBlockNdx, const std::string& blockPrefix, int baseOffset, const BufferVar& bufVar, deUint32 blockLayoutFlags)
|
|
{
|
|
const VarType& varType = bufVar.getType();
|
|
const deUint32 combinedFlags = mergeLayoutFlags(blockLayoutFlags, bufVar.getFlags());
|
|
|
|
if (varType.isArrayType())
|
|
{
|
|
// Top-level arrays need special care.
|
|
const int topLevelArraySize = varType.getArraySize() == VarType::UNSIZED_ARRAY ? 0 : varType.getArraySize();
|
|
const string prefix = blockPrefix + bufVar.getName() + "[0]";
|
|
const bool isStd140 = (blockLayoutFlags & LAYOUT_STD140) != 0;
|
|
const int vec4Align = (int)sizeof(deUint32)*4;
|
|
const int baseAlignment = isStd140 ? computeStd140BaseAlignment(varType, combinedFlags)
|
|
: computeStd430BaseAlignment(varType, combinedFlags);
|
|
int curOffset = deAlign32(baseOffset, baseAlignment);
|
|
const VarType& elemType = varType.getElementType();
|
|
|
|
if (elemType.isBasicType() && !glu::isDataTypeMatrix(elemType.getBasicType()))
|
|
{
|
|
// Array of scalars or vectors.
|
|
const glu::DataType elemBasicType = elemType.getBasicType();
|
|
const int elemBaseAlign = getDataTypeByteAlignment(elemBasicType);
|
|
const int stride = isStd140 ? deAlign32(elemBaseAlign, vec4Align) : elemBaseAlign;
|
|
BufferVarLayoutEntry entry;
|
|
|
|
entry.name = prefix;
|
|
entry.topLevelArraySize = 1;
|
|
entry.topLevelArrayStride = 0;
|
|
entry.type = elemBasicType;
|
|
entry.blockNdx = curBlockNdx;
|
|
entry.offset = curOffset;
|
|
entry.arraySize = topLevelArraySize;
|
|
entry.arrayStride = stride;
|
|
entry.matrixStride = 0;
|
|
|
|
layout.bufferVars.push_back(entry);
|
|
|
|
curOffset += stride*topLevelArraySize;
|
|
}
|
|
else if (elemType.isBasicType() && glu::isDataTypeMatrix(elemType.getBasicType()))
|
|
{
|
|
// Array of matrices.
|
|
const glu::DataType elemBasicType = elemType.getBasicType();
|
|
const bool isRowMajor = !!(combinedFlags & LAYOUT_ROW_MAJOR);
|
|
const int vecSize = isRowMajor ? glu::getDataTypeMatrixNumColumns(elemBasicType)
|
|
: glu::getDataTypeMatrixNumRows(elemBasicType);
|
|
const int numVecs = isRowMajor ? glu::getDataTypeMatrixNumRows(elemBasicType)
|
|
: glu::getDataTypeMatrixNumColumns(elemBasicType);
|
|
const glu::DataType vecType = glu::getDataTypeFloatVec(vecSize);
|
|
const int vecBaseAlign = getDataTypeByteAlignment(vecType);
|
|
const int stride = isStd140 ? deAlign32(vecBaseAlign, vec4Align) : vecBaseAlign;
|
|
BufferVarLayoutEntry entry;
|
|
|
|
entry.name = prefix;
|
|
entry.topLevelArraySize = 1;
|
|
entry.topLevelArrayStride = 0;
|
|
entry.type = elemBasicType;
|
|
entry.blockNdx = curBlockNdx;
|
|
entry.offset = curOffset;
|
|
entry.arraySize = topLevelArraySize;
|
|
entry.arrayStride = stride*numVecs;
|
|
entry.matrixStride = stride;
|
|
entry.isRowMajor = isRowMajor;
|
|
|
|
layout.bufferVars.push_back(entry);
|
|
|
|
curOffset += stride*numVecs*topLevelArraySize;
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(elemType.isStructType() || elemType.isArrayType());
|
|
|
|
// Struct base alignment is not added multiple times as curOffset supplied to computeReferenceLayout
|
|
// was already aligned correctly. Thus computeReferenceLayout should not add any extra padding
|
|
// before struct. Padding after struct will be added as it should.
|
|
//
|
|
// Stride could be computed prior to creating child elements, but it would essentially require running
|
|
// the layout computation twice. Instead we fix stride to child elements afterwards.
|
|
|
|
const int firstChildNdx = (int)layout.bufferVars.size();
|
|
const int stride = computeReferenceLayout(layout, curBlockNdx, curOffset, prefix, varType.getElementType(), combinedFlags);
|
|
|
|
for (int childNdx = firstChildNdx; childNdx < (int)layout.bufferVars.size(); childNdx++)
|
|
{
|
|
layout.bufferVars[childNdx].topLevelArraySize = topLevelArraySize;
|
|
layout.bufferVars[childNdx].topLevelArrayStride = stride;
|
|
}
|
|
|
|
curOffset += stride*topLevelArraySize;
|
|
}
|
|
|
|
return curOffset-baseOffset;
|
|
}
|
|
else
|
|
return computeReferenceLayout(layout, curBlockNdx, baseOffset, blockPrefix + bufVar.getName(), varType, combinedFlags);
|
|
}
|
|
|
|
void computeReferenceLayout (BufferLayout& layout, const ShaderInterface& interface)
|
|
{
|
|
int numBlocks = interface.getNumBlocks();
|
|
|
|
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
|
|
{
|
|
const BufferBlock& block = interface.getBlock(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 firstVarNdx = (int)layout.bufferVars.size();
|
|
|
|
for (BufferBlock::const_iterator varIter = block.begin(); varIter != block.end(); varIter++)
|
|
{
|
|
const BufferVar& bufVar = *varIter;
|
|
curOffset += computeReferenceLayout(layout, activeBlockNdx, blockPrefix, curOffset, bufVar, block.getFlags());
|
|
}
|
|
|
|
int varIndicesEnd = (int)layout.bufferVars.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 variable set for block.
|
|
for (int varNdx = firstVarNdx; varNdx < varIndicesEnd; varNdx++)
|
|
blockEntry.activeVarIndices.push_back(varNdx);
|
|
|
|
if (block.isArray())
|
|
blockEntry.name += "[" + de::toString(instanceNdx) + "]";
|
|
}
|
|
}
|
|
}
|
|
|
|
// Value generator.
|
|
|
|
void generateValue (const BufferVarLayoutEntry& entry, int unsizedArraySize, void* basePtr, de::Random& rnd)
|
|
{
|
|
const glu::DataType scalarType = glu::getDataTypeScalarType(entry.type);
|
|
const int scalarSize = glu::getDataTypeScalarSize(entry.type);
|
|
const int arraySize = entry.arraySize == 0 ? unsizedArraySize : entry.arraySize;
|
|
const int arrayStride = entry.arrayStride;
|
|
const int topLevelSize = entry.topLevelArraySize == 0 ? unsizedArraySize : entry.topLevelArraySize;
|
|
const int topLevelStride = entry.topLevelArrayStride;
|
|
const bool isMatrix = glu::isDataTypeMatrix(entry.type);
|
|
const int numVecs = isMatrix ? (entry.isRowMajor ? glu::getDataTypeMatrixNumRows(entry.type) : glu::getDataTypeMatrixNumColumns(entry.type)) : 1;
|
|
const int vecSize = scalarSize / numVecs;
|
|
const int compSize = sizeof(deUint32);
|
|
|
|
DE_ASSERT(scalarSize%numVecs == 0);
|
|
DE_ASSERT(topLevelSize >= 0);
|
|
DE_ASSERT(arraySize >= 0);
|
|
|
|
for (int topElemNdx = 0; topElemNdx < topLevelSize; topElemNdx++)
|
|
{
|
|
deUint8* const topElemPtr = (deUint8*)basePtr + entry.offset + topElemNdx*topLevelStride;
|
|
|
|
for (int elemNdx = 0; elemNdx < arraySize; elemNdx++)
|
|
{
|
|
deUint8* const elemPtr = topElemPtr + elemNdx*arrayStride;
|
|
|
|
for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
|
|
{
|
|
deUint8* const vecPtr = elemPtr + (isMatrix ? vecNdx*entry.matrixStride : 0);
|
|
|
|
for (int compNdx = 0; compNdx < vecSize; compNdx++)
|
|
{
|
|
deUint8* const 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 BufferLayout& layout, const vector<BlockDataPtr>& blockPointers, deUint32 seed)
|
|
{
|
|
de::Random rnd (seed);
|
|
const int numBlocks = (int)layout.blocks.size();
|
|
|
|
DE_ASSERT(numBlocks == (int)blockPointers.size());
|
|
|
|
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
|
|
{
|
|
const BlockLayoutEntry& blockLayout = layout.blocks[blockNdx];
|
|
const BlockDataPtr& blockPtr = blockPointers[blockNdx];
|
|
const int numEntries = (int)layout.blocks[blockNdx].activeVarIndices.size();
|
|
|
|
for (int entryNdx = 0; entryNdx < numEntries; entryNdx++)
|
|
{
|
|
const int varNdx = blockLayout.activeVarIndices[entryNdx];
|
|
const BufferVarLayoutEntry& varEntry = layout.bufferVars[varNdx];
|
|
|
|
generateValue(varEntry, blockPtr.lastUnsizedArraySize, blockPtr.ptr, rnd);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Shader generator.
|
|
|
|
const char* getCompareFuncForType (glu::DataType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case glu::TYPE_FLOAT: return "bool compare_float (highp float a, highp float b) { return abs(a - b) < 0.05; }\n";
|
|
case glu::TYPE_FLOAT_VEC2: return "bool compare_vec2 (highp vec2 a, highp vec2 b) { return compare_float(a.x, b.x)&&compare_float(a.y, b.y); }\n";
|
|
case glu::TYPE_FLOAT_VEC3: return "bool 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";
|
|
case glu::TYPE_FLOAT_VEC4: return "bool 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";
|
|
case glu::TYPE_FLOAT_MAT2: return "bool compare_mat2 (highp mat2 a, highp mat2 b) { return compare_vec2(a[0], b[0])&&compare_vec2(a[1], b[1]); }\n";
|
|
case glu::TYPE_FLOAT_MAT2X3: return "bool compare_mat2x3 (highp mat2x3 a, highp mat2x3 b){ return compare_vec3(a[0], b[0])&&compare_vec3(a[1], b[1]); }\n";
|
|
case glu::TYPE_FLOAT_MAT2X4: return "bool compare_mat2x4 (highp mat2x4 a, highp mat2x4 b){ return compare_vec4(a[0], b[0])&&compare_vec4(a[1], b[1]); }\n";
|
|
case glu::TYPE_FLOAT_MAT3X2: return "bool 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";
|
|
case glu::TYPE_FLOAT_MAT3: return "bool 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";
|
|
case glu::TYPE_FLOAT_MAT3X4: return "bool 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";
|
|
case glu::TYPE_FLOAT_MAT4X2: return "bool 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";
|
|
case glu::TYPE_FLOAT_MAT4X3: return "bool 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";
|
|
case glu::TYPE_FLOAT_MAT4: return "bool 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";
|
|
case glu::TYPE_INT: return "bool compare_int (highp int a, highp int b) { return a == b; }\n";
|
|
case glu::TYPE_INT_VEC2: return "bool compare_ivec2 (highp ivec2 a, highp ivec2 b) { return a == b; }\n";
|
|
case glu::TYPE_INT_VEC3: return "bool compare_ivec3 (highp ivec3 a, highp ivec3 b) { return a == b; }\n";
|
|
case glu::TYPE_INT_VEC4: return "bool compare_ivec4 (highp ivec4 a, highp ivec4 b) { return a == b; }\n";
|
|
case glu::TYPE_UINT: return "bool compare_uint (highp uint a, highp uint b) { return a == b; }\n";
|
|
case glu::TYPE_UINT_VEC2: return "bool compare_uvec2 (highp uvec2 a, highp uvec2 b) { return a == b; }\n";
|
|
case glu::TYPE_UINT_VEC3: return "bool compare_uvec3 (highp uvec3 a, highp uvec3 b) { return a == b; }\n";
|
|
case glu::TYPE_UINT_VEC4: return "bool compare_uvec4 (highp uvec4 a, highp uvec4 b) { return a == b; }\n";
|
|
case glu::TYPE_BOOL: return "bool compare_bool (bool a, bool b) { return a == b; }\n";
|
|
case glu::TYPE_BOOL_VEC2: return "bool compare_bvec2 (bvec2 a, bvec2 b) { return a == b; }\n";
|
|
case glu::TYPE_BOOL_VEC3: return "bool compare_bvec3 (bvec3 a, bvec3 b) { return a == b; }\n";
|
|
case glu::TYPE_BOOL_VEC4: return "bool compare_bvec4 (bvec4 a, bvec4 b) { return a == b; }\n";
|
|
default:
|
|
DE_ASSERT(false);
|
|
return DE_NULL;
|
|
}
|
|
}
|
|
|
|
void getCompareDependencies (std::set<glu::DataType>& compareFuncs, glu::DataType basicType)
|
|
{
|
|
switch (basicType)
|
|
{
|
|
case glu::TYPE_FLOAT_VEC2:
|
|
case glu::TYPE_FLOAT_VEC3:
|
|
case glu::TYPE_FLOAT_VEC4:
|
|
compareFuncs.insert(glu::TYPE_FLOAT);
|
|
compareFuncs.insert(basicType);
|
|
break;
|
|
|
|
case glu::TYPE_FLOAT_MAT2:
|
|
case glu::TYPE_FLOAT_MAT2X3:
|
|
case glu::TYPE_FLOAT_MAT2X4:
|
|
case glu::TYPE_FLOAT_MAT3X2:
|
|
case glu::TYPE_FLOAT_MAT3:
|
|
case glu::TYPE_FLOAT_MAT3X4:
|
|
case glu::TYPE_FLOAT_MAT4X2:
|
|
case glu::TYPE_FLOAT_MAT4X3:
|
|
case glu::TYPE_FLOAT_MAT4:
|
|
compareFuncs.insert(glu::TYPE_FLOAT);
|
|
compareFuncs.insert(glu::getDataTypeFloatVec(glu::getDataTypeMatrixNumRows(basicType)));
|
|
compareFuncs.insert(basicType);
|
|
break;
|
|
|
|
default:
|
|
compareFuncs.insert(basicType);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void collectUniqueBasicTypes (std::set<glu::DataType>& basicTypes, const VarType& type)
|
|
{
|
|
if (type.isStructType())
|
|
{
|
|
for (StructType::ConstIterator iter = type.getStructPtr()->begin(); iter != type.getStructPtr()->end(); ++iter)
|
|
collectUniqueBasicTypes(basicTypes, iter->getType());
|
|
}
|
|
else if (type.isArrayType())
|
|
collectUniqueBasicTypes(basicTypes, type.getElementType());
|
|
else
|
|
{
|
|
DE_ASSERT(type.isBasicType());
|
|
basicTypes.insert(type.getBasicType());
|
|
}
|
|
}
|
|
|
|
void collectUniqueBasicTypes (std::set<glu::DataType>& basicTypes, const BufferBlock& bufferBlock)
|
|
{
|
|
for (BufferBlock::const_iterator iter = bufferBlock.begin(); iter != bufferBlock.end(); ++iter)
|
|
collectUniqueBasicTypes(basicTypes, iter->getType());
|
|
}
|
|
|
|
void collectUniqueBasicTypes (std::set<glu::DataType>& basicTypes, const ShaderInterface& interface)
|
|
{
|
|
for (int ndx = 0; ndx < interface.getNumBlocks(); ++ndx)
|
|
collectUniqueBasicTypes(basicTypes, interface.getBlock(ndx));
|
|
}
|
|
|
|
void generateCompareFuncs (std::ostream& str, const ShaderInterface& interface)
|
|
{
|
|
std::set<glu::DataType> types;
|
|
std::set<glu::DataType> compareFuncs;
|
|
|
|
// Collect unique basic types
|
|
collectUniqueBasicTypes(types, interface);
|
|
|
|
// Set of compare functions required
|
|
for (std::set<glu::DataType>::const_iterator iter = types.begin(); iter != types.end(); ++iter)
|
|
{
|
|
getCompareDependencies(compareFuncs, *iter);
|
|
}
|
|
|
|
for (int type = 0; type < glu::TYPE_LAST; ++type)
|
|
{
|
|
if (compareFuncs.find(glu::DataType(type)) != compareFuncs.end())
|
|
str << getCompareFuncForType(glu::DataType(type));
|
|
}
|
|
}
|
|
|
|
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::ostream& src, const BufferVar& bufferVar, int indentLevel)
|
|
{
|
|
// \todo [pyry] Qualifiers
|
|
|
|
if ((bufferVar.getFlags() & LAYOUT_MASK) != 0)
|
|
src << "layout(" << LayoutFlagsFmt(bufferVar.getFlags() & LAYOUT_MASK) << ") ";
|
|
|
|
src << glu::declare(bufferVar.getType(), bufferVar.getName(), indentLevel);
|
|
}
|
|
|
|
void generateDeclaration (std::ostream& src, const BufferBlock& block, int bindingPoint)
|
|
{
|
|
src << "layout(";
|
|
|
|
if ((block.getFlags() & LAYOUT_MASK) != 0)
|
|
src << LayoutFlagsFmt(block.getFlags() & LAYOUT_MASK) << ", ";
|
|
|
|
src << "binding = " << bindingPoint;
|
|
|
|
src << ") ";
|
|
|
|
src << "buffer " << block.getBlockName();
|
|
src << "\n{\n";
|
|
|
|
for (BufferBlock::const_iterator varIter = block.begin(); varIter != block.end(); varIter++)
|
|
{
|
|
src << Indent(1);
|
|
generateDeclaration(src, *varIter, 1 /* indent level */);
|
|
src << ";\n";
|
|
}
|
|
|
|
src << "}";
|
|
|
|
if (block.getInstanceName() != DE_NULL)
|
|
{
|
|
src << " " << block.getInstanceName();
|
|
if (block.isArray())
|
|
src << "[" << block.getArraySize() << "]";
|
|
}
|
|
else
|
|
DE_ASSERT(!block.isArray());
|
|
|
|
src << ";\n";
|
|
}
|
|
|
|
void generateImmMatrixSrc (std::ostream& src, glu::DataType basicType, int matrixStride, bool isRowMajor, const void* valuePtr)
|
|
{
|
|
DE_ASSERT(glu::isDataTypeMatrix(basicType));
|
|
|
|
const int compSize = sizeof(deUint32);
|
|
const int numRows = glu::getDataTypeMatrixNumRows(basicType);
|
|
const int numCols = glu::getDataTypeMatrixNumColumns(basicType);
|
|
|
|
src << glu::getDataTypeName(basicType) << "(";
|
|
|
|
// Constructed in column-wise order.
|
|
for (int colNdx = 0; colNdx < numCols; colNdx++)
|
|
{
|
|
for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
|
|
{
|
|
const deUint8* compPtr = (const deUint8*)valuePtr + (isRowMajor ? rowNdx*matrixStride + colNdx*compSize
|
|
: colNdx*matrixStride + rowNdx*compSize);
|
|
|
|
if (colNdx > 0 || rowNdx > 0)
|
|
src << ", ";
|
|
|
|
src << de::floatToString(*((const float*)compPtr), 1);
|
|
}
|
|
}
|
|
|
|
src << ")";
|
|
}
|
|
|
|
void generateImmScalarVectorSrc (std::ostream& src, glu::DataType basicType, const void* valuePtr)
|
|
{
|
|
DE_ASSERT(glu::isDataTypeFloatOrVec(basicType) ||
|
|
glu::isDataTypeIntOrIVec(basicType) ||
|
|
glu::isDataTypeUintOrUVec(basicType) ||
|
|
glu::isDataTypeBoolOrBVec(basicType));
|
|
|
|
const glu::DataType scalarType = glu::getDataTypeScalarType(basicType);
|
|
const int scalarSize = glu::getDataTypeScalarSize(basicType);
|
|
const int compSize = sizeof(deUint32);
|
|
|
|
if (scalarSize > 1)
|
|
src << glu::getDataTypeName(basicType) << "(";
|
|
|
|
for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
|
|
{
|
|
const deUint8* compPtr = (const deUint8*)valuePtr + 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 << ")";
|
|
}
|
|
|
|
string getAPIName (const BufferBlock& block, const BufferVar& var, const glu::TypeComponentVector& accessPath)
|
|
{
|
|
std::ostringstream name;
|
|
|
|
if (block.getInstanceName())
|
|
name << block.getBlockName() << ".";
|
|
|
|
name << var.getName();
|
|
|
|
for (glu::TypeComponentVector::const_iterator pathComp = accessPath.begin(); pathComp != accessPath.end(); pathComp++)
|
|
{
|
|
if (pathComp->type == glu::VarTypeComponent::STRUCT_MEMBER)
|
|
{
|
|
const VarType curType = glu::getVarType(var.getType(), accessPath.begin(), pathComp);
|
|
const StructType* structPtr = curType.getStructPtr();
|
|
|
|
name << "." << structPtr->getMember(pathComp->index).getName();
|
|
}
|
|
else if (pathComp->type == glu::VarTypeComponent::ARRAY_ELEMENT)
|
|
{
|
|
if (pathComp == accessPath.begin() || (pathComp+1) == accessPath.end())
|
|
name << "[0]"; // Top- / bottom-level array
|
|
else
|
|
name << "[" << pathComp->index << "]";
|
|
}
|
|
else
|
|
DE_ASSERT(false);
|
|
}
|
|
|
|
return name.str();
|
|
}
|
|
|
|
string getShaderName (const BufferBlock& block, int instanceNdx, const BufferVar& var, const glu::TypeComponentVector& accessPath)
|
|
{
|
|
std::ostringstream name;
|
|
|
|
if (block.getInstanceName())
|
|
{
|
|
name << block.getInstanceName();
|
|
|
|
if (block.isArray())
|
|
name << "[" << instanceNdx << "]";
|
|
|
|
name << ".";
|
|
}
|
|
else
|
|
DE_ASSERT(instanceNdx == 0);
|
|
|
|
name << var.getName();
|
|
|
|
for (glu::TypeComponentVector::const_iterator pathComp = accessPath.begin(); pathComp != accessPath.end(); pathComp++)
|
|
{
|
|
if (pathComp->type == glu::VarTypeComponent::STRUCT_MEMBER)
|
|
{
|
|
const VarType curType = glu::getVarType(var.getType(), accessPath.begin(), pathComp);
|
|
const StructType* structPtr = curType.getStructPtr();
|
|
|
|
name << "." << structPtr->getMember(pathComp->index).getName();
|
|
}
|
|
else if (pathComp->type == glu::VarTypeComponent::ARRAY_ELEMENT)
|
|
name << "[" << pathComp->index << "]";
|
|
else
|
|
DE_ASSERT(false);
|
|
}
|
|
|
|
return name.str();
|
|
}
|
|
|
|
int computeOffset (const BufferVarLayoutEntry& varLayout, const glu::TypeComponentVector& accessPath)
|
|
{
|
|
const int topLevelNdx = (accessPath.size() > 1 && accessPath.front().type == glu::VarTypeComponent::ARRAY_ELEMENT) ? accessPath.front().index : 0;
|
|
const int bottomLevelNdx = (!accessPath.empty() && accessPath.back().type == glu::VarTypeComponent::ARRAY_ELEMENT) ? accessPath.back().index : 0;
|
|
|
|
return varLayout.offset + varLayout.topLevelArrayStride*topLevelNdx + varLayout.arrayStride*bottomLevelNdx;
|
|
}
|
|
|
|
void generateCompareSrc (
|
|
std::ostream& src,
|
|
const char* resultVar,
|
|
const BufferLayout& bufferLayout,
|
|
const BufferBlock& block,
|
|
int instanceNdx,
|
|
const BlockDataPtr& blockPtr,
|
|
const BufferVar& bufVar,
|
|
const glu::SubTypeAccess& accessPath)
|
|
{
|
|
const VarType curType = accessPath.getType();
|
|
|
|
if (curType.isArrayType())
|
|
{
|
|
const int arraySize = curType.getArraySize() == VarType::UNSIZED_ARRAY ? block.getLastUnsizedArraySize(instanceNdx) : curType.getArraySize();
|
|
|
|
for (int elemNdx = 0; elemNdx < arraySize; elemNdx++)
|
|
generateCompareSrc(src, resultVar, bufferLayout, block, instanceNdx, blockPtr, bufVar, accessPath.element(elemNdx));
|
|
}
|
|
else if (curType.isStructType())
|
|
{
|
|
const int numMembers = curType.getStructPtr()->getNumMembers();
|
|
|
|
for (int memberNdx = 0; memberNdx < numMembers; memberNdx++)
|
|
generateCompareSrc(src, resultVar, bufferLayout, block, instanceNdx, blockPtr, bufVar, accessPath.member(memberNdx));
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(curType.isBasicType());
|
|
|
|
const string apiName = getAPIName(block, bufVar, accessPath.getPath());
|
|
const int varNdx = bufferLayout.getVariableIndex(apiName);
|
|
|
|
DE_ASSERT(varNdx >= 0);
|
|
{
|
|
const BufferVarLayoutEntry& varLayout = bufferLayout.bufferVars[varNdx];
|
|
const string shaderName = getShaderName(block, instanceNdx, bufVar, accessPath.getPath());
|
|
const glu::DataType basicType = curType.getBasicType();
|
|
const bool isMatrix = glu::isDataTypeMatrix(basicType);
|
|
const char* typeName = glu::getDataTypeName(basicType);
|
|
const void* valuePtr = (const deUint8*)blockPtr.ptr + computeOffset(varLayout, accessPath.getPath());
|
|
|
|
src << "\t" << resultVar << " = " << resultVar << " && compare_" << typeName << "(" << shaderName << ", ";
|
|
|
|
if (isMatrix)
|
|
generateImmMatrixSrc(src, basicType, varLayout.matrixStride, varLayout.isRowMajor, valuePtr);
|
|
else
|
|
generateImmScalarVectorSrc(src, basicType, valuePtr);
|
|
|
|
src << ");\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void generateCompareSrc (std::ostream& src, const char* resultVar, const ShaderInterface& interface, const BufferLayout& layout, const vector<BlockDataPtr>& blockPointers)
|
|
{
|
|
for (int declNdx = 0; declNdx < interface.getNumBlocks(); declNdx++)
|
|
{
|
|
const BufferBlock& block = interface.getBlock(declNdx);
|
|
const bool isArray = block.isArray();
|
|
const int numInstances = isArray ? block.getArraySize() : 1;
|
|
|
|
DE_ASSERT(!isArray || block.getInstanceName());
|
|
|
|
for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
|
|
{
|
|
const string instanceName = block.getBlockName() + (isArray ? "[" + de::toString(instanceNdx) + "]" : string(""));
|
|
const int blockNdx = layout.getBlockIndex(instanceName);
|
|
const BlockDataPtr& blockPtr = blockPointers[blockNdx];
|
|
|
|
for (BufferBlock::const_iterator varIter = block.begin(); varIter != block.end(); varIter++)
|
|
{
|
|
const BufferVar& bufVar = *varIter;
|
|
|
|
if ((bufVar.getFlags() & ACCESS_READ) == 0)
|
|
continue; // Don't read from that variable.
|
|
|
|
generateCompareSrc(src, resultVar, layout, block, instanceNdx, blockPtr, bufVar, glu::SubTypeAccess(bufVar.getType()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// \todo [2013-10-14 pyry] Almost identical to generateCompareSrc - unify?
|
|
|
|
void generateWriteSrc (
|
|
std::ostream& src,
|
|
const BufferLayout& bufferLayout,
|
|
const BufferBlock& block,
|
|
int instanceNdx,
|
|
const BlockDataPtr& blockPtr,
|
|
const BufferVar& bufVar,
|
|
const glu::SubTypeAccess& accessPath)
|
|
{
|
|
const VarType curType = accessPath.getType();
|
|
|
|
if (curType.isArrayType())
|
|
{
|
|
const int arraySize = curType.getArraySize() == VarType::UNSIZED_ARRAY ? block.getLastUnsizedArraySize(instanceNdx) : curType.getArraySize();
|
|
|
|
for (int elemNdx = 0; elemNdx < arraySize; elemNdx++)
|
|
generateWriteSrc(src, bufferLayout, block, instanceNdx, blockPtr, bufVar, accessPath.element(elemNdx));
|
|
}
|
|
else if (curType.isStructType())
|
|
{
|
|
const int numMembers = curType.getStructPtr()->getNumMembers();
|
|
|
|
for (int memberNdx = 0; memberNdx < numMembers; memberNdx++)
|
|
generateWriteSrc(src, bufferLayout, block, instanceNdx, blockPtr, bufVar, accessPath.member(memberNdx));
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(curType.isBasicType());
|
|
|
|
const string apiName = getAPIName(block, bufVar, accessPath.getPath());
|
|
const int varNdx = bufferLayout.getVariableIndex(apiName);
|
|
|
|
DE_ASSERT(varNdx >= 0);
|
|
{
|
|
const BufferVarLayoutEntry& varLayout = bufferLayout.bufferVars[varNdx];
|
|
const string shaderName = getShaderName(block, instanceNdx, bufVar, accessPath.getPath());
|
|
const glu::DataType basicType = curType.getBasicType();
|
|
const bool isMatrix = glu::isDataTypeMatrix(basicType);
|
|
const void* valuePtr = (const deUint8*)blockPtr.ptr + computeOffset(varLayout, accessPath.getPath());
|
|
|
|
src << "\t" << shaderName << " = ";
|
|
|
|
if (isMatrix)
|
|
generateImmMatrixSrc(src, basicType, varLayout.matrixStride, varLayout.isRowMajor, valuePtr);
|
|
else
|
|
generateImmScalarVectorSrc(src, basicType, valuePtr);
|
|
|
|
src << ";\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
void generateWriteSrc (std::ostream& src, const ShaderInterface& interface, const BufferLayout& layout, const vector<BlockDataPtr>& blockPointers)
|
|
{
|
|
for (int declNdx = 0; declNdx < interface.getNumBlocks(); declNdx++)
|
|
{
|
|
const BufferBlock& block = interface.getBlock(declNdx);
|
|
const bool isArray = block.isArray();
|
|
const int numInstances = isArray ? block.getArraySize() : 1;
|
|
|
|
DE_ASSERT(!isArray || block.getInstanceName());
|
|
|
|
for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
|
|
{
|
|
const string instanceName = block.getBlockName() + (isArray ? "[" + de::toString(instanceNdx) + "]" : string(""));
|
|
const int blockNdx = layout.getBlockIndex(instanceName);
|
|
const BlockDataPtr& blockPtr = blockPointers[blockNdx];
|
|
|
|
for (BufferBlock::const_iterator varIter = block.begin(); varIter != block.end(); varIter++)
|
|
{
|
|
const BufferVar& bufVar = *varIter;
|
|
|
|
if ((bufVar.getFlags() & ACCESS_WRITE) == 0)
|
|
continue; // Don't write to that variable.
|
|
|
|
generateWriteSrc(src, layout, block, instanceNdx, blockPtr, bufVar, glu::SubTypeAccess(bufVar.getType()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
string generateComputeShader (const glw::Functions& gl, glu::GLSLVersion glslVersion, const ShaderInterface& interface, const BufferLayout& layout, const vector<BlockDataPtr>& comparePtrs, const vector<BlockDataPtr>& writePtrs)
|
|
{
|
|
std::ostringstream src;
|
|
glw::GLint maxShaderStorageBufferBindings;
|
|
glw::GLint maxComputeShaderStorageBlocks;
|
|
|
|
DE_ASSERT(glslVersion == glu::GLSL_VERSION_310_ES || glslVersion == glu::GLSL_VERSION_430);
|
|
|
|
gl.getIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxShaderStorageBufferBindings);
|
|
gl.getIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &maxComputeShaderStorageBlocks);
|
|
|
|
src << glu::getGLSLVersionDeclaration(glslVersion) << "\n";
|
|
src << "layout(local_size_x = 1) in;\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++)
|
|
src << glu::declare(*structIter) << ";\n";
|
|
|
|
{
|
|
int bindingPoint = 0;
|
|
|
|
for (int blockNdx = 0; blockNdx < interface.getNumBlocks(); blockNdx++)
|
|
{
|
|
const BufferBlock& block = interface.getBlock(blockNdx);
|
|
generateDeclaration(src, block, bindingPoint);
|
|
|
|
bindingPoint += block.isArray() ? block.getArraySize() : 1;
|
|
}
|
|
|
|
if (bindingPoint > maxShaderStorageBufferBindings)
|
|
{
|
|
throw tcu::NotSupportedError("Test requires support for more SSBO bindings than implementation exposes");
|
|
}
|
|
if (bindingPoint > maxComputeShaderStorageBlocks)
|
|
{
|
|
throw tcu::NotSupportedError("Test requires support for more compute shader storage blocks than implementation exposes");
|
|
}
|
|
}
|
|
|
|
// Atomic counter for counting passed invocations.
|
|
src << "\nlayout(binding = 0) uniform atomic_uint ac_numPassed;\n";
|
|
|
|
// Comparison utilities.
|
|
src << "\n";
|
|
generateCompareFuncs(src, interface);
|
|
|
|
src << "\n"
|
|
"void main (void)\n"
|
|
"{\n"
|
|
" bool allOk = true;\n";
|
|
|
|
// Value compare.
|
|
generateCompareSrc(src, "allOk", interface, layout, comparePtrs);
|
|
|
|
src << " if (allOk)\n"
|
|
<< " atomicCounterIncrement(ac_numPassed);\n"
|
|
<< "\n";
|
|
|
|
// Value write.
|
|
generateWriteSrc(src, interface, layout, writePtrs);
|
|
|
|
src << "}\n";
|
|
|
|
return src.str();
|
|
}
|
|
|
|
void getGLBufferLayout (const glw::Functions& gl, BufferLayout& layout, deUint32 program)
|
|
{
|
|
int numActiveBufferVars = 0;
|
|
int numActiveBlocks = 0;
|
|
|
|
gl.getProgramInterfaceiv(program, GL_BUFFER_VARIABLE, GL_ACTIVE_RESOURCES, &numActiveBufferVars);
|
|
gl.getProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &numActiveBlocks);
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to get number of buffer variables and buffer blocks");
|
|
|
|
// Block entries.
|
|
layout.blocks.resize(numActiveBlocks);
|
|
for (int blockNdx = 0; blockNdx < numActiveBlocks; blockNdx++)
|
|
{
|
|
BlockLayoutEntry& entry = layout.blocks[blockNdx];
|
|
const deUint32 queryParams[] = { GL_BUFFER_DATA_SIZE, GL_NUM_ACTIVE_VARIABLES, GL_NAME_LENGTH };
|
|
int returnValues[] = { 0, 0, 0 };
|
|
|
|
DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(queryParams) == DE_LENGTH_OF_ARRAY(returnValues));
|
|
|
|
{
|
|
int returnLength = 0;
|
|
gl.getProgramResourceiv(program, GL_SHADER_STORAGE_BLOCK, (deUint32)blockNdx, DE_LENGTH_OF_ARRAY(queryParams), &queryParams[0], DE_LENGTH_OF_ARRAY(returnValues), &returnLength, &returnValues[0]);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceiv(GL_SHADER_STORAGE_BLOCK) failed");
|
|
|
|
if (returnLength != DE_LENGTH_OF_ARRAY(returnValues))
|
|
throw tcu::TestError("glGetProgramResourceiv(GL_SHADER_STORAGE_BLOCK) returned wrong number of values");
|
|
}
|
|
|
|
entry.size = returnValues[0];
|
|
|
|
// Query active variables
|
|
if (returnValues[1] > 0)
|
|
{
|
|
const int numBlockVars = returnValues[1];
|
|
const deUint32 queryArg = GL_ACTIVE_VARIABLES;
|
|
int retLength = 0;
|
|
|
|
entry.activeVarIndices.resize(numBlockVars);
|
|
gl.getProgramResourceiv(program, GL_SHADER_STORAGE_BLOCK, (deUint32)blockNdx, 1, &queryArg, numBlockVars, &retLength, &entry.activeVarIndices[0]);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceiv(GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_VARIABLES) failed");
|
|
|
|
if (retLength != numBlockVars)
|
|
throw tcu::TestError("glGetProgramResourceiv(GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_VARIABLES) returned wrong number of values");
|
|
}
|
|
|
|
// Query name
|
|
if (returnValues[2] > 0)
|
|
{
|
|
const int nameLen = returnValues[2];
|
|
int retLen = 0;
|
|
vector<char> name (nameLen);
|
|
|
|
gl.getProgramResourceName(program, GL_SHADER_STORAGE_BLOCK, (deUint32)blockNdx, (glw::GLsizei)name.size(), &retLen, &name[0]);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceName(GL_SHADER_STORAGE_BLOCK) failed");
|
|
|
|
if (retLen+1 != nameLen)
|
|
throw tcu::TestError("glGetProgramResourceName(GL_SHADER_STORAGE_BLOCK) returned invalid name. Number of characters written is inconsistent with NAME_LENGTH property.");
|
|
if (name[nameLen-1] != 0)
|
|
throw tcu::TestError("glGetProgramResourceName(GL_SHADER_STORAGE_BLOCK) returned invalid name. Expected null terminator at index " + de::toString(nameLen-1));
|
|
|
|
entry.name = &name[0];
|
|
}
|
|
else
|
|
throw tcu::TestError("glGetProgramResourceiv() returned invalid GL_NAME_LENGTH");
|
|
}
|
|
|
|
layout.bufferVars.resize(numActiveBufferVars);
|
|
for (int bufVarNdx = 0; bufVarNdx < numActiveBufferVars; bufVarNdx++)
|
|
{
|
|
BufferVarLayoutEntry& entry = layout.bufferVars[bufVarNdx];
|
|
const deUint32 queryParams[] =
|
|
{
|
|
GL_BLOCK_INDEX, // 0
|
|
GL_TYPE, // 1
|
|
GL_OFFSET, // 2
|
|
GL_ARRAY_SIZE, // 3
|
|
GL_ARRAY_STRIDE, // 4
|
|
GL_MATRIX_STRIDE, // 5
|
|
GL_TOP_LEVEL_ARRAY_SIZE, // 6
|
|
GL_TOP_LEVEL_ARRAY_STRIDE, // 7
|
|
GL_IS_ROW_MAJOR, // 8
|
|
GL_NAME_LENGTH // 9
|
|
};
|
|
int returnValues[DE_LENGTH_OF_ARRAY(queryParams)];
|
|
|
|
DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(queryParams) == DE_LENGTH_OF_ARRAY(returnValues));
|
|
|
|
{
|
|
int returnLength = 0;
|
|
gl.getProgramResourceiv(program, GL_BUFFER_VARIABLE, (deUint32)bufVarNdx, DE_LENGTH_OF_ARRAY(queryParams), &queryParams[0], DE_LENGTH_OF_ARRAY(returnValues), &returnLength, &returnValues[0]);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceiv(GL_BUFFER_VARIABLE) failed");
|
|
|
|
if (returnLength != DE_LENGTH_OF_ARRAY(returnValues))
|
|
throw tcu::TestError("glGetProgramResourceiv(GL_BUFFER_VARIABLE) returned wrong number of values");
|
|
}
|
|
|
|
// Map values
|
|
entry.blockNdx = returnValues[0];
|
|
entry.type = glu::getDataTypeFromGLType(returnValues[1]);
|
|
entry.offset = returnValues[2];
|
|
entry.arraySize = returnValues[3];
|
|
entry.arrayStride = returnValues[4];
|
|
entry.matrixStride = returnValues[5];
|
|
entry.topLevelArraySize = returnValues[6];
|
|
entry.topLevelArrayStride = returnValues[7];
|
|
entry.isRowMajor = returnValues[8] != 0;
|
|
|
|
// Query name
|
|
DE_ASSERT(queryParams[9] == GL_NAME_LENGTH);
|
|
if (returnValues[9] > 0)
|
|
{
|
|
const int nameLen = returnValues[9];
|
|
int retLen = 0;
|
|
vector<char> name (nameLen);
|
|
|
|
gl.getProgramResourceName(program, GL_BUFFER_VARIABLE, (deUint32)bufVarNdx, (glw::GLsizei)name.size(), &retLen, &name[0]);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceName(GL_BUFFER_VARIABLE) failed");
|
|
|
|
if (retLen+1 != nameLen)
|
|
throw tcu::TestError("glGetProgramResourceName(GL_BUFFER_VARIABLE) returned invalid name. Number of characters written is inconsistent with NAME_LENGTH property.");
|
|
if (name[nameLen-1] != 0)
|
|
throw tcu::TestError("glGetProgramResourceName(GL_BUFFER_VARIABLE) returned invalid name. Expected null terminator at index " + de::toString(nameLen-1));
|
|
|
|
entry.name = &name[0];
|
|
}
|
|
else
|
|
throw tcu::TestError("glGetProgramResourceiv() returned invalid GL_NAME_LENGTH");
|
|
}
|
|
}
|
|
|
|
void copyBufferVarData (const BufferVarLayoutEntry& dstEntry, const BlockDataPtr& dstBlockPtr, const BufferVarLayoutEntry& srcEntry, const BlockDataPtr& srcBlockPtr)
|
|
{
|
|
DE_ASSERT(dstEntry.arraySize <= srcEntry.arraySize);
|
|
DE_ASSERT(dstEntry.topLevelArraySize <= srcEntry.topLevelArraySize);
|
|
DE_ASSERT(dstBlockPtr.lastUnsizedArraySize <= srcBlockPtr.lastUnsizedArraySize);
|
|
DE_ASSERT(dstEntry.type == srcEntry.type);
|
|
|
|
deUint8* const dstBasePtr = (deUint8*)dstBlockPtr.ptr + dstEntry.offset;
|
|
const deUint8* const srcBasePtr = (const deUint8*)srcBlockPtr.ptr + srcEntry.offset;
|
|
const int scalarSize = glu::getDataTypeScalarSize(dstEntry.type);
|
|
const bool isMatrix = glu::isDataTypeMatrix(dstEntry.type);
|
|
const int compSize = sizeof(deUint32);
|
|
const int dstArraySize = dstEntry.arraySize == 0 ? dstBlockPtr.lastUnsizedArraySize : dstEntry.arraySize;
|
|
const int dstArrayStride = dstEntry.arrayStride;
|
|
const int dstTopLevelSize = dstEntry.topLevelArraySize == 0 ? dstBlockPtr.lastUnsizedArraySize : dstEntry.topLevelArraySize;
|
|
const int dstTopLevelStride = dstEntry.topLevelArrayStride;
|
|
const int srcArraySize = srcEntry.arraySize == 0 ? srcBlockPtr.lastUnsizedArraySize : srcEntry.arraySize;
|
|
const int srcArrayStride = srcEntry.arrayStride;
|
|
const int srcTopLevelSize = srcEntry.topLevelArraySize == 0 ? srcBlockPtr.lastUnsizedArraySize : srcEntry.topLevelArraySize;
|
|
const int srcTopLevelStride = srcEntry.topLevelArrayStride;
|
|
|
|
DE_ASSERT(dstArraySize <= srcArraySize && dstTopLevelSize <= srcTopLevelSize);
|
|
DE_UNREF(srcArraySize && srcTopLevelSize);
|
|
|
|
for (int topElemNdx = 0; topElemNdx < dstTopLevelSize; topElemNdx++)
|
|
{
|
|
deUint8* const dstTopPtr = dstBasePtr + topElemNdx*dstTopLevelStride;
|
|
const deUint8* const srcTopPtr = srcBasePtr + topElemNdx*srcTopLevelStride;
|
|
|
|
for (int elementNdx = 0; elementNdx < dstArraySize; elementNdx++)
|
|
{
|
|
deUint8* const dstElemPtr = dstTopPtr + elementNdx*dstArrayStride;
|
|
const deUint8* const srcElemPtr = srcTopPtr + elementNdx*srcArrayStride;
|
|
|
|
if (isMatrix)
|
|
{
|
|
const int numRows = glu::getDataTypeMatrixNumRows(dstEntry.type);
|
|
const 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);
|
|
|
|
DE_ASSERT((deIntptr)(srcCompPtr + compSize) - (deIntptr)srcBlockPtr.ptr <= (deIntptr)srcBlockPtr.size);
|
|
DE_ASSERT((deIntptr)(dstCompPtr + compSize) - (deIntptr)dstBlockPtr.ptr <= (deIntptr)dstBlockPtr.size);
|
|
deMemcpy(dstCompPtr, srcCompPtr, compSize);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT((deIntptr)(srcElemPtr + scalarSize*compSize) - (deIntptr)srcBlockPtr.ptr <= (deIntptr)srcBlockPtr.size);
|
|
DE_ASSERT((deIntptr)(dstElemPtr + scalarSize*compSize) - (deIntptr)dstBlockPtr.ptr <= (deIntptr)dstBlockPtr.size);
|
|
deMemcpy(dstElemPtr, srcElemPtr, scalarSize*compSize);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void copyData (const BufferLayout& dstLayout, const vector<BlockDataPtr>& dstBlockPointers, const BufferLayout& srcLayout, const vector<BlockDataPtr>& srcBlockPointers)
|
|
{
|
|
// \note Src layout is used as reference in case of activeVarIndices 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 BlockDataPtr& srcBlockPtr = srcBlockPointers[srcBlockNdx];
|
|
int dstBlockNdx = dstLayout.getBlockIndex(srcBlock.name.c_str());
|
|
|
|
if (dstBlockNdx >= 0)
|
|
{
|
|
DE_ASSERT(de::inBounds(dstBlockNdx, 0, (int)dstBlockPointers.size()));
|
|
|
|
const BlockDataPtr& dstBlockPtr = dstBlockPointers[dstBlockNdx];
|
|
|
|
for (vector<int>::const_iterator srcVarNdxIter = srcBlock.activeVarIndices.begin(); srcVarNdxIter != srcBlock.activeVarIndices.end(); srcVarNdxIter++)
|
|
{
|
|
const BufferVarLayoutEntry& srcEntry = srcLayout.bufferVars[*srcVarNdxIter];
|
|
int dstVarNdx = dstLayout.getVariableIndex(srcEntry.name.c_str());
|
|
|
|
if (dstVarNdx >= 0)
|
|
copyBufferVarData(dstLayout.bufferVars[dstVarNdx], dstBlockPtr, srcEntry, srcBlockPtr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void copyNonWrittenData (
|
|
const BufferLayout& layout,
|
|
const BufferBlock& block,
|
|
int instanceNdx,
|
|
const BlockDataPtr& srcBlockPtr,
|
|
const BlockDataPtr& dstBlockPtr,
|
|
const BufferVar& bufVar,
|
|
const glu::SubTypeAccess& accessPath)
|
|
{
|
|
const VarType curType = accessPath.getType();
|
|
|
|
if (curType.isArrayType())
|
|
{
|
|
const int arraySize = curType.getArraySize() == VarType::UNSIZED_ARRAY ? block.getLastUnsizedArraySize(instanceNdx) : curType.getArraySize();
|
|
|
|
for (int elemNdx = 0; elemNdx < arraySize; elemNdx++)
|
|
copyNonWrittenData(layout, block, instanceNdx, srcBlockPtr, dstBlockPtr, bufVar, accessPath.element(elemNdx));
|
|
}
|
|
else if (curType.isStructType())
|
|
{
|
|
const int numMembers = curType.getStructPtr()->getNumMembers();
|
|
|
|
for (int memberNdx = 0; memberNdx < numMembers; memberNdx++)
|
|
copyNonWrittenData(layout, block, instanceNdx, srcBlockPtr, dstBlockPtr, bufVar, accessPath.member(memberNdx));
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(curType.isBasicType());
|
|
|
|
const string apiName = getAPIName(block, bufVar, accessPath.getPath());
|
|
const int varNdx = layout.getVariableIndex(apiName);
|
|
|
|
DE_ASSERT(varNdx >= 0);
|
|
{
|
|
const BufferVarLayoutEntry& varLayout = layout.bufferVars[varNdx];
|
|
copyBufferVarData(varLayout, dstBlockPtr, varLayout, srcBlockPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void copyNonWrittenData (const ShaderInterface& interface, const BufferLayout& layout, const vector<BlockDataPtr>& srcPtrs, const vector<BlockDataPtr>& dstPtrs)
|
|
{
|
|
for (int declNdx = 0; declNdx < interface.getNumBlocks(); declNdx++)
|
|
{
|
|
const BufferBlock& block = interface.getBlock(declNdx);
|
|
const bool isArray = block.isArray();
|
|
const int numInstances = isArray ? block.getArraySize() : 1;
|
|
|
|
DE_ASSERT(!isArray || block.getInstanceName());
|
|
|
|
for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
|
|
{
|
|
const string instanceName = block.getBlockName() + (isArray ? "[" + de::toString(instanceNdx) + "]" : string(""));
|
|
const int blockNdx = layout.getBlockIndex(instanceName);
|
|
const BlockDataPtr& srcBlockPtr = srcPtrs[blockNdx];
|
|
const BlockDataPtr& dstBlockPtr = dstPtrs[blockNdx];
|
|
|
|
for (BufferBlock::const_iterator varIter = block.begin(); varIter != block.end(); varIter++)
|
|
{
|
|
const BufferVar& bufVar = *varIter;
|
|
|
|
if (bufVar.getFlags() & ACCESS_WRITE)
|
|
continue;
|
|
|
|
copyNonWrittenData(layout, block, instanceNdx, srcBlockPtr, dstBlockPtr, bufVar, glu::SubTypeAccess(bufVar.getType()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool compareComponents (glu::DataType scalarType, const void* ref, const void* res, int numComps)
|
|
{
|
|
if (scalarType == glu::TYPE_FLOAT)
|
|
{
|
|
const float threshold = 0.05f; // Same as used in shaders - should be fine for values being used.
|
|
|
|
for (int ndx = 0; ndx < numComps; ndx++)
|
|
{
|
|
const float refVal = *((const float*)ref + ndx);
|
|
const float resVal = *((const float*)res + ndx);
|
|
|
|
if (!(deFloatAbs(resVal - refVal) <= threshold))
|
|
return false;
|
|
}
|
|
}
|
|
else if (scalarType == glu::TYPE_BOOL)
|
|
{
|
|
for (int ndx = 0; ndx < numComps; ndx++)
|
|
{
|
|
const deUint32 refVal = *((const deUint32*)ref + ndx);
|
|
const deUint32 resVal = *((const deUint32*)res + ndx);
|
|
|
|
if ((refVal != 0) != (resVal != 0))
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(scalarType == glu::TYPE_INT || scalarType == glu::TYPE_UINT);
|
|
|
|
for (int ndx = 0; ndx < numComps; ndx++)
|
|
{
|
|
const deUint32 refVal = *((const deUint32*)ref + ndx);
|
|
const deUint32 resVal = *((const deUint32*)res + ndx);
|
|
|
|
if (refVal != resVal)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool compareBufferVarData (tcu::TestLog& log, const BufferVarLayoutEntry& refEntry, const BlockDataPtr& refBlockPtr, const BufferVarLayoutEntry& resEntry, const BlockDataPtr& resBlockPtr)
|
|
{
|
|
DE_ASSERT(resEntry.arraySize <= refEntry.arraySize);
|
|
DE_ASSERT(resEntry.topLevelArraySize <= refEntry.topLevelArraySize);
|
|
DE_ASSERT(resBlockPtr.lastUnsizedArraySize <= refBlockPtr.lastUnsizedArraySize);
|
|
DE_ASSERT(resEntry.type == refEntry.type);
|
|
|
|
deUint8* const resBasePtr = (deUint8*)resBlockPtr.ptr + resEntry.offset;
|
|
const deUint8* const refBasePtr = (const deUint8*)refBlockPtr.ptr + refEntry.offset;
|
|
const glu::DataType scalarType = glu::getDataTypeScalarType(refEntry.type);
|
|
const int scalarSize = glu::getDataTypeScalarSize(resEntry.type);
|
|
const bool isMatrix = glu::isDataTypeMatrix(resEntry.type);
|
|
const int compSize = sizeof(deUint32);
|
|
const int maxPrints = 3;
|
|
int numFailed = 0;
|
|
|
|
const int resArraySize = resEntry.arraySize == 0 ? resBlockPtr.lastUnsizedArraySize : resEntry.arraySize;
|
|
const int resArrayStride = resEntry.arrayStride;
|
|
const int resTopLevelSize = resEntry.topLevelArraySize == 0 ? resBlockPtr.lastUnsizedArraySize : resEntry.topLevelArraySize;
|
|
const int resTopLevelStride = resEntry.topLevelArrayStride;
|
|
const int refArraySize = refEntry.arraySize == 0 ? refBlockPtr.lastUnsizedArraySize : refEntry.arraySize;
|
|
const int refArrayStride = refEntry.arrayStride;
|
|
const int refTopLevelSize = refEntry.topLevelArraySize == 0 ? refBlockPtr.lastUnsizedArraySize : refEntry.topLevelArraySize;
|
|
const int refTopLevelStride = refEntry.topLevelArrayStride;
|
|
|
|
DE_ASSERT(resArraySize <= refArraySize && resTopLevelSize <= refTopLevelSize);
|
|
DE_UNREF(refArraySize && refTopLevelSize);
|
|
|
|
for (int topElemNdx = 0; topElemNdx < resTopLevelSize; topElemNdx++)
|
|
{
|
|
deUint8* const resTopPtr = resBasePtr + topElemNdx*resTopLevelStride;
|
|
const deUint8* const refTopPtr = refBasePtr + topElemNdx*refTopLevelStride;
|
|
|
|
for (int elementNdx = 0; elementNdx < resArraySize; elementNdx++)
|
|
{
|
|
deUint8* const resElemPtr = resTopPtr + elementNdx*resArrayStride;
|
|
const deUint8* const refElemPtr = refTopPtr + elementNdx*refArrayStride;
|
|
|
|
if (isMatrix)
|
|
{
|
|
const int numRows = glu::getDataTypeMatrixNumRows(resEntry.type);
|
|
const int numCols = glu::getDataTypeMatrixNumColumns(resEntry.type);
|
|
bool isOk = true;
|
|
|
|
for (int colNdx = 0; colNdx < numCols; colNdx++)
|
|
{
|
|
for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
|
|
{
|
|
deUint8* resCompPtr = resElemPtr + (resEntry.isRowMajor ? rowNdx*resEntry.matrixStride + colNdx*compSize
|
|
: colNdx*resEntry.matrixStride + rowNdx*compSize);
|
|
const deUint8* refCompPtr = refElemPtr + (refEntry.isRowMajor ? rowNdx*refEntry.matrixStride + colNdx*compSize
|
|
: colNdx*refEntry.matrixStride + rowNdx*compSize);
|
|
|
|
DE_ASSERT((deIntptr)(refCompPtr + compSize) - (deIntptr)refBlockPtr.ptr <= (deIntptr)refBlockPtr.size);
|
|
DE_ASSERT((deIntptr)(resCompPtr + compSize) - (deIntptr)resBlockPtr.ptr <= (deIntptr)resBlockPtr.size);
|
|
|
|
isOk = isOk && compareComponents(scalarType, resCompPtr, refCompPtr, 1);
|
|
}
|
|
}
|
|
|
|
if (!isOk)
|
|
{
|
|
numFailed += 1;
|
|
if (numFailed < maxPrints)
|
|
{
|
|
std::ostringstream expected, got;
|
|
generateImmMatrixSrc(expected, refEntry.type, refEntry.matrixStride, refEntry.isRowMajor, refElemPtr);
|
|
generateImmMatrixSrc(got, resEntry.type, resEntry.matrixStride, resEntry.isRowMajor, resElemPtr);
|
|
log << TestLog::Message << "ERROR: mismatch in " << refEntry.name << ", top-level ndx " << topElemNdx << ", bottom-level ndx " << elementNdx << ":\n"
|
|
<< " expected " << expected.str() << "\n"
|
|
<< " got " << got.str()
|
|
<< TestLog::EndMessage;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT((deIntptr)(refElemPtr + scalarSize*compSize) - (deIntptr)refBlockPtr.ptr <= (deIntptr)refBlockPtr.size);
|
|
DE_ASSERT((deIntptr)(resElemPtr + scalarSize*compSize) - (deIntptr)resBlockPtr.ptr <= (deIntptr)resBlockPtr.size);
|
|
|
|
const bool isOk = compareComponents(scalarType, resElemPtr, refElemPtr, scalarSize);
|
|
|
|
if (!isOk)
|
|
{
|
|
numFailed += 1;
|
|
if (numFailed < maxPrints)
|
|
{
|
|
std::ostringstream expected, got;
|
|
generateImmScalarVectorSrc(expected, refEntry.type, refElemPtr);
|
|
generateImmScalarVectorSrc(got, resEntry.type, resElemPtr);
|
|
log << TestLog::Message << "ERROR: mismatch in " << refEntry.name << ", top-level ndx " << topElemNdx << ", bottom-level ndx " << elementNdx << ":\n"
|
|
<< " expected " << expected.str() << "\n"
|
|
<< " got " << got.str()
|
|
<< TestLog::EndMessage;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (numFailed >= maxPrints)
|
|
log << TestLog::Message << "... (" << numFailed << " failures for " << refEntry.name << " in total)" << TestLog::EndMessage;
|
|
|
|
return numFailed == 0;
|
|
}
|
|
|
|
bool compareData (tcu::TestLog& log, const BufferLayout& refLayout, const vector<BlockDataPtr>& refBlockPointers, const BufferLayout& resLayout, const vector<BlockDataPtr>& resBlockPointers)
|
|
{
|
|
const int numBlocks = (int)refLayout.blocks.size();
|
|
bool allOk = true;
|
|
|
|
for (int refBlockNdx = 0; refBlockNdx < numBlocks; refBlockNdx++)
|
|
{
|
|
const BlockLayoutEntry& refBlock = refLayout.blocks[refBlockNdx];
|
|
const BlockDataPtr& refBlockPtr = refBlockPointers[refBlockNdx];
|
|
int resBlockNdx = resLayout.getBlockIndex(refBlock.name.c_str());
|
|
|
|
if (resBlockNdx >= 0)
|
|
{
|
|
DE_ASSERT(de::inBounds(resBlockNdx, 0, (int)resBlockPointers.size()));
|
|
|
|
const BlockDataPtr& resBlockPtr = resBlockPointers[resBlockNdx];
|
|
|
|
for (vector<int>::const_iterator refVarNdxIter = refBlock.activeVarIndices.begin(); refVarNdxIter != refBlock.activeVarIndices.end(); refVarNdxIter++)
|
|
{
|
|
const BufferVarLayoutEntry& refEntry = refLayout.bufferVars[*refVarNdxIter];
|
|
int resVarNdx = resLayout.getVariableIndex(refEntry.name.c_str());
|
|
|
|
if (resVarNdx >= 0)
|
|
{
|
|
const BufferVarLayoutEntry& resEntry = resLayout.bufferVars[resVarNdx];
|
|
allOk = compareBufferVarData(log, refEntry, refBlockPtr, resEntry, resBlockPtr) && allOk;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return allOk;
|
|
}
|
|
|
|
string getBlockAPIName (const BufferBlock& block, int instanceNdx)
|
|
{
|
|
DE_ASSERT(block.isArray() || instanceNdx == 0);
|
|
return block.getBlockName() + (block.isArray() ? ("[" + de::toString(instanceNdx) + "]") : string());
|
|
}
|
|
|
|
// \note Some implementations don't report block members in the order they are declared.
|
|
// For checking whether size has to be adjusted by some top-level array actual size,
|
|
// we only need to know a) whether there is a unsized top-level array, and b)
|
|
// what is stride of that array.
|
|
|
|
static bool hasUnsizedArray (const BufferLayout& layout, const BlockLayoutEntry& entry)
|
|
{
|
|
for (vector<int>::const_iterator varNdx = entry.activeVarIndices.begin(); varNdx != entry.activeVarIndices.end(); ++varNdx)
|
|
{
|
|
if (isUnsizedArray(layout.bufferVars[*varNdx]))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int getUnsizedArrayStride (const BufferLayout& layout, const BlockLayoutEntry& entry)
|
|
{
|
|
for (vector<int>::const_iterator varNdx = entry.activeVarIndices.begin(); varNdx != entry.activeVarIndices.end(); ++varNdx)
|
|
{
|
|
const BufferVarLayoutEntry& varEntry = layout.bufferVars[*varNdx];
|
|
|
|
if (varEntry.arraySize == 0)
|
|
return varEntry.arrayStride;
|
|
else if (varEntry.topLevelArraySize == 0)
|
|
return varEntry.topLevelArrayStride;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
vector<int> computeBufferSizes (const ShaderInterface& interface, const BufferLayout& layout)
|
|
{
|
|
vector<int> sizes(layout.blocks.size());
|
|
|
|
for (int declNdx = 0; declNdx < interface.getNumBlocks(); declNdx++)
|
|
{
|
|
const BufferBlock& block = interface.getBlock(declNdx);
|
|
const bool isArray = block.isArray();
|
|
const int numInstances = isArray ? block.getArraySize() : 1;
|
|
|
|
for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
|
|
{
|
|
const string apiName = getBlockAPIName(block, instanceNdx);
|
|
const int blockNdx = layout.getBlockIndex(apiName);
|
|
|
|
if (blockNdx >= 0)
|
|
{
|
|
const BlockLayoutEntry& blockLayout = layout.blocks[blockNdx];
|
|
const int baseSize = blockLayout.size;
|
|
const bool isLastUnsized = hasUnsizedArray(layout, blockLayout);
|
|
const int lastArraySize = isLastUnsized ? block.getLastUnsizedArraySize(instanceNdx) : 0;
|
|
const int stride = isLastUnsized ? getUnsizedArrayStride(layout, blockLayout) : 0;
|
|
|
|
sizes[blockNdx] = baseSize + lastArraySize*stride;
|
|
}
|
|
}
|
|
}
|
|
|
|
return sizes;
|
|
}
|
|
|
|
BlockDataPtr getBlockDataPtr (const BufferLayout& layout, const BlockLayoutEntry& blockLayout, void* ptr, int bufferSize)
|
|
{
|
|
const bool isLastUnsized = hasUnsizedArray(layout, blockLayout);
|
|
const int baseSize = blockLayout.size;
|
|
|
|
if (isLastUnsized)
|
|
{
|
|
const int lastArrayStride = getUnsizedArrayStride(layout, blockLayout);
|
|
const int lastArraySize = (bufferSize-baseSize) / (lastArrayStride ? lastArrayStride : 1);
|
|
|
|
DE_ASSERT(baseSize + lastArraySize*lastArrayStride == bufferSize);
|
|
|
|
return BlockDataPtr(ptr, bufferSize, lastArraySize);
|
|
}
|
|
else
|
|
return BlockDataPtr(ptr, bufferSize, 0);
|
|
}
|
|
|
|
struct RefDataStorage
|
|
{
|
|
vector<deUint8> data;
|
|
vector<BlockDataPtr> pointers;
|
|
};
|
|
|
|
struct Buffer
|
|
{
|
|
deUint32 buffer;
|
|
int size;
|
|
|
|
Buffer (deUint32 buffer_, int size_) : buffer(buffer_), size(size_) {}
|
|
Buffer (void) : buffer(0), size(0) {}
|
|
};
|
|
|
|
struct BlockLocation
|
|
{
|
|
int index;
|
|
int offset;
|
|
int size;
|
|
|
|
BlockLocation (int index_, int offset_, int size_) : index(index_), offset(offset_), size(size_) {}
|
|
BlockLocation (void) : index(0), offset(0), size(0) {}
|
|
};
|
|
|
|
void initRefDataStorage (const ShaderInterface& interface, const BufferLayout& layout, RefDataStorage& storage)
|
|
{
|
|
DE_ASSERT(storage.data.empty() && storage.pointers.empty());
|
|
|
|
const vector<int> bufferSizes = computeBufferSizes(interface, layout);
|
|
int totalSize = 0;
|
|
|
|
for (vector<int>::const_iterator sizeIter = bufferSizes.begin(); sizeIter != bufferSizes.end(); ++sizeIter)
|
|
totalSize += *sizeIter;
|
|
|
|
storage.data.resize(totalSize);
|
|
|
|
// Pointers for each block.
|
|
{
|
|
deUint8* basePtr = storage.data.empty() ? DE_NULL : &storage.data[0];
|
|
int curOffset = 0;
|
|
|
|
DE_ASSERT(bufferSizes.size() == layout.blocks.size());
|
|
DE_ASSERT(totalSize == 0 || basePtr);
|
|
|
|
storage.pointers.resize(layout.blocks.size());
|
|
|
|
for (int blockNdx = 0; blockNdx < (int)layout.blocks.size(); blockNdx++)
|
|
{
|
|
const BlockLayoutEntry& blockLayout = layout.blocks[blockNdx];
|
|
const int bufferSize = bufferSizes[blockNdx];
|
|
|
|
storage.pointers[blockNdx] = getBlockDataPtr(layout, blockLayout, basePtr + curOffset, bufferSize);
|
|
|
|
curOffset += bufferSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
vector<BlockDataPtr> blockLocationsToPtrs (const BufferLayout& layout, const vector<BlockLocation>& blockLocations, const vector<void*>& bufPtrs)
|
|
{
|
|
vector<BlockDataPtr> blockPtrs(blockLocations.size());
|
|
|
|
DE_ASSERT(layout.blocks.size() == blockLocations.size());
|
|
|
|
for (int blockNdx = 0; blockNdx < (int)layout.blocks.size(); blockNdx++)
|
|
{
|
|
const BlockLayoutEntry& blockLayout = layout.blocks[blockNdx];
|
|
const BlockLocation& location = blockLocations[blockNdx];
|
|
|
|
blockPtrs[blockNdx] = getBlockDataPtr(layout, blockLayout, (deUint8*)bufPtrs[location.index] + location.offset, location.size);
|
|
}
|
|
|
|
return blockPtrs;
|
|
}
|
|
|
|
vector<void*> mapBuffers (const glw::Functions& gl, const vector<Buffer>& buffers, deUint32 access)
|
|
{
|
|
vector<void*> mapPtrs(buffers.size(), DE_NULL);
|
|
|
|
try
|
|
{
|
|
for (int ndx = 0; ndx < (int)buffers.size(); ndx++)
|
|
{
|
|
if (buffers[ndx].size > 0)
|
|
{
|
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffers[ndx].buffer);
|
|
mapPtrs[ndx] = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, buffers[ndx].size, access);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to map buffer");
|
|
TCU_CHECK(mapPtrs[ndx]);
|
|
}
|
|
else
|
|
mapPtrs[ndx] = DE_NULL;
|
|
}
|
|
|
|
return mapPtrs;
|
|
}
|
|
catch (...)
|
|
{
|
|
for (int ndx = 0; ndx < (int)buffers.size(); ndx++)
|
|
{
|
|
if (mapPtrs[ndx])
|
|
{
|
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffers[ndx].buffer);
|
|
gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
|
}
|
|
}
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
void unmapBuffers (const glw::Functions& gl, const vector<Buffer>& buffers)
|
|
{
|
|
for (int ndx = 0; ndx < (int)buffers.size(); ndx++)
|
|
{
|
|
if (buffers[ndx].size > 0)
|
|
{
|
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffers[ndx].buffer);
|
|
gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
|
}
|
|
}
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to unmap buffer");
|
|
}
|
|
|
|
} // anonymous (utilities)
|
|
|
|
class BufferManager
|
|
{
|
|
public:
|
|
BufferManager (const glu::RenderContext& renderCtx);
|
|
~BufferManager (void);
|
|
|
|
deUint32 allocBuffer (void);
|
|
|
|
private:
|
|
BufferManager (const BufferManager& other);
|
|
BufferManager& operator= (const BufferManager& other);
|
|
|
|
const glu::RenderContext& m_renderCtx;
|
|
std::vector<deUint32> m_buffers;
|
|
};
|
|
|
|
BufferManager::BufferManager (const glu::RenderContext& renderCtx)
|
|
: m_renderCtx(renderCtx)
|
|
{
|
|
}
|
|
|
|
BufferManager::~BufferManager (void)
|
|
{
|
|
if (!m_buffers.empty())
|
|
m_renderCtx.getFunctions().deleteBuffers((glw::GLsizei)m_buffers.size(), &m_buffers[0]);
|
|
}
|
|
|
|
deUint32 BufferManager::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 buffer");
|
|
m_buffers.push_back(buf);
|
|
|
|
return buf;
|
|
}
|
|
|
|
} // bb
|
|
|
|
using namespace bb;
|
|
|
|
// SSBOLayoutCase.
|
|
|
|
SSBOLayoutCase::SSBOLayoutCase (tcu::TestContext& testCtx, glu::RenderContext& renderCtx, const char* name, const char* description, glu::GLSLVersion glslVersion, BufferMode bufferMode)
|
|
: TestCase (testCtx, name, description)
|
|
, m_renderCtx (renderCtx)
|
|
, m_glslVersion (glslVersion)
|
|
, m_bufferMode (bufferMode)
|
|
{
|
|
DE_ASSERT(glslVersion == glu::GLSL_VERSION_310_ES || glslVersion == glu::GLSL_VERSION_430);
|
|
}
|
|
|
|
SSBOLayoutCase::~SSBOLayoutCase (void)
|
|
{
|
|
}
|
|
|
|
SSBOLayoutCase::IterateResult SSBOLayoutCase::iterate (void)
|
|
{
|
|
TestLog& log = m_testCtx.getLog();
|
|
const glw::Functions& gl = m_renderCtx.getFunctions();
|
|
|
|
BufferLayout refLayout; // std140 / std430 layout.
|
|
BufferLayout glLayout; // Layout reported by GL.
|
|
RefDataStorage initialData; // Initial data stored in buffer.
|
|
RefDataStorage writeData; // Data written by compute shader.
|
|
|
|
BufferManager bufferManager (m_renderCtx);
|
|
vector<Buffer> buffers; // Buffers allocated for storage
|
|
vector<BlockLocation> blockLocations; // Block locations in storage (index, offset)
|
|
|
|
// Initialize result to pass.
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
|
|
|
|
computeReferenceLayout (refLayout, m_interface);
|
|
initRefDataStorage (m_interface, refLayout, initialData);
|
|
initRefDataStorage (m_interface, refLayout, writeData);
|
|
generateValues (refLayout, initialData.pointers, deStringHash(getName()) ^ 0xad2f7214);
|
|
generateValues (refLayout, writeData.pointers, deStringHash(getName()) ^ 0x25ca4e7);
|
|
copyNonWrittenData (m_interface, refLayout, initialData.pointers, writeData.pointers);
|
|
|
|
const glu::ShaderProgram program(m_renderCtx, glu::ProgramSources() << glu::ComputeSource(generateComputeShader(gl, m_glslVersion, m_interface, refLayout, initialData.pointers, writeData.pointers)));
|
|
log << program;
|
|
|
|
if (!program.isOk())
|
|
{
|
|
// Compile failed.
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed");
|
|
return STOP;
|
|
}
|
|
|
|
// Query layout from GL.
|
|
getGLBufferLayout(gl, glLayout, program.getProgram());
|
|
|
|
// Print layout to log.
|
|
{
|
|
tcu::ScopedLogSection section(log, "ActiveBufferBlocks", "Active Buffer Blocks");
|
|
for (int blockNdx = 0; blockNdx < (int)glLayout.blocks.size(); blockNdx++)
|
|
log << TestLog::Message << blockNdx << ": " << glLayout.blocks[blockNdx] << TestLog::EndMessage;
|
|
}
|
|
|
|
{
|
|
tcu::ScopedLogSection section(log, "ActiveBufferVars", "Active Buffer Variables");
|
|
for (int varNdx = 0; varNdx < (int)glLayout.bufferVars.size(); varNdx++)
|
|
log << TestLog::Message << varNdx << ": " << glLayout.bufferVars[varNdx] << TestLog::EndMessage;
|
|
}
|
|
|
|
// Verify layouts.
|
|
{
|
|
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.
|
|
}
|
|
|
|
if (!compareStdBlocks(refLayout, glLayout))
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid std140 or std430 layout");
|
|
|
|
if (!compareSharedBlocks(refLayout, glLayout))
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid shared layout");
|
|
|
|
if (!checkIndexQueries(program.getProgram(), glLayout))
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Inconsintent block index query results");
|
|
}
|
|
|
|
// Allocate GL buffers & compute placement.
|
|
{
|
|
const int numBlocks = (int)glLayout.blocks.size();
|
|
const vector<int> bufferSizes = computeBufferSizes(m_interface, glLayout);
|
|
|
|
DE_ASSERT(bufferSizes.size() == glLayout.blocks.size());
|
|
|
|
blockLocations.resize(numBlocks);
|
|
|
|
if (m_bufferMode == BUFFERMODE_PER_BLOCK)
|
|
{
|
|
buffers.resize(numBlocks);
|
|
|
|
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
|
|
{
|
|
const int bufferSize = bufferSizes[blockNdx];
|
|
|
|
buffers[blockNdx].size = bufferSize;
|
|
blockLocations[blockNdx] = BlockLocation(blockNdx, 0, bufferSize);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DE_ASSERT(m_bufferMode == BUFFERMODE_SINGLE);
|
|
|
|
int bindingAlignment = 0;
|
|
int totalSize = 0;
|
|
|
|
gl.getIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &bindingAlignment);
|
|
|
|
{
|
|
int curOffset = 0;
|
|
DE_ASSERT(bufferSizes.size() == glLayout.blocks.size());
|
|
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
|
|
{
|
|
const int bufferSize = bufferSizes[blockNdx];
|
|
|
|
if (bindingAlignment > 0)
|
|
curOffset = deRoundUp32(curOffset, bindingAlignment);
|
|
|
|
blockLocations[blockNdx] = BlockLocation(0, curOffset, bufferSize);
|
|
curOffset += bufferSize;
|
|
}
|
|
totalSize = curOffset;
|
|
}
|
|
|
|
buffers.resize(1);
|
|
buffers[0].size = totalSize;
|
|
}
|
|
|
|
for (int bufNdx = 0; bufNdx < (int)buffers.size(); bufNdx++)
|
|
{
|
|
const int bufferSize = buffers[bufNdx].size;
|
|
const deUint32 buffer = bufferManager.allocBuffer();
|
|
|
|
gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
|
|
gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, DE_NULL, GL_STATIC_DRAW);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to allocate buffer");
|
|
|
|
buffers[bufNdx].buffer = buffer;
|
|
}
|
|
}
|
|
|
|
{
|
|
const vector<void*> mapPtrs = mapBuffers(gl, buffers, GL_MAP_WRITE_BIT);
|
|
const vector<BlockDataPtr> mappedBlockPtrs = blockLocationsToPtrs(glLayout, blockLocations, mapPtrs);
|
|
|
|
copyData(glLayout, mappedBlockPtrs, refLayout, initialData.pointers);
|
|
|
|
unmapBuffers(gl, buffers);
|
|
}
|
|
|
|
{
|
|
int bindingPoint = 0;
|
|
|
|
for (int blockDeclNdx = 0; blockDeclNdx < m_interface.getNumBlocks(); blockDeclNdx++)
|
|
{
|
|
const BufferBlock& block = m_interface.getBlock(blockDeclNdx);
|
|
const int numInst = block.isArray() ? block.getArraySize() : 1;
|
|
|
|
for (int instNdx = 0; instNdx < numInst; instNdx++)
|
|
{
|
|
const string instName = getBlockAPIName(block, instNdx);
|
|
const int layoutNdx = findBlockIndex(glLayout, instName);
|
|
|
|
if (layoutNdx >= 0)
|
|
{
|
|
const BlockLocation& blockLoc = blockLocations[layoutNdx];
|
|
|
|
if (blockLoc.size > 0)
|
|
gl.bindBufferRange(GL_SHADER_STORAGE_BUFFER, bindingPoint, buffers[blockLoc.index].buffer, blockLoc.offset, blockLoc.size);
|
|
}
|
|
|
|
bindingPoint += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to bind buffers");
|
|
|
|
{
|
|
const bool execOk = execute(program.getProgram());
|
|
|
|
if (execOk)
|
|
{
|
|
const vector<void*> mapPtrs = mapBuffers(gl, buffers, GL_MAP_READ_BIT);
|
|
const vector<BlockDataPtr> mappedBlockPtrs = blockLocationsToPtrs(glLayout, blockLocations, mapPtrs);
|
|
|
|
const bool compareOk = compareData(m_testCtx.getLog(), refLayout, writeData.pointers, glLayout, mappedBlockPtrs);
|
|
|
|
unmapBuffers(gl, buffers);
|
|
|
|
if (!compareOk)
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result comparison failed");
|
|
}
|
|
else
|
|
m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Shader execution failed");
|
|
}
|
|
|
|
return STOP;
|
|
}
|
|
|
|
bool SSBOLayoutCase::compareStdBlocks (const BufferLayout& refLayout, const BufferLayout& cmpLayout) const
|
|
{
|
|
TestLog& log = m_testCtx.getLog();
|
|
bool isOk = true;
|
|
int numBlocks = m_interface.getNumBlocks();
|
|
|
|
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
|
|
{
|
|
const BufferBlock& block = m_interface.getBlock(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());
|
|
|
|
if ((block.getFlags() & (LAYOUT_STD140|LAYOUT_STD430)) == 0)
|
|
continue; // Not std* layout.
|
|
|
|
DE_ASSERT(refBlockNdx >= 0);
|
|
|
|
if (cmpBlockNdx < 0)
|
|
{
|
|
// Not found.
|
|
log << TestLog::Message << "Error: Buffer block '" << instanceName << "' not found" << TestLog::EndMessage;
|
|
isOk = false;
|
|
continue;
|
|
}
|
|
|
|
const BlockLayoutEntry& refBlockLayout = refLayout.blocks[refBlockNdx];
|
|
const BlockLayoutEntry& cmpBlockLayout = cmpLayout.blocks[cmpBlockNdx];
|
|
|
|
// \todo [2012-01-24 pyry] Verify that activeVarIndices is correct.
|
|
// \todo [2012-01-24 pyry] Verify all instances.
|
|
if (refBlockLayout.activeVarIndices.size() != cmpBlockLayout.activeVarIndices.size())
|
|
{
|
|
log << TestLog::Message << "Error: Number of active variables differ in block '" << instanceName
|
|
<< "' (expected " << refBlockLayout.activeVarIndices.size()
|
|
<< ", got " << cmpBlockLayout.activeVarIndices.size()
|
|
<< ")" << TestLog::EndMessage;
|
|
isOk = false;
|
|
}
|
|
|
|
for (vector<int>::const_iterator ndxIter = refBlockLayout.activeVarIndices.begin(); ndxIter != refBlockLayout.activeVarIndices.end(); ndxIter++)
|
|
{
|
|
const BufferVarLayoutEntry& refEntry = refLayout.bufferVars[*ndxIter];
|
|
int cmpEntryNdx = cmpLayout.getVariableIndex(refEntry.name.c_str());
|
|
|
|
if (cmpEntryNdx < 0)
|
|
{
|
|
log << TestLog::Message << "Error: Buffer variable '" << refEntry.name << "' not found" << TestLog::EndMessage;
|
|
isOk = false;
|
|
continue;
|
|
}
|
|
|
|
const BufferVarLayoutEntry& cmpEntry = cmpLayout.bufferVars[cmpEntryNdx];
|
|
|
|
if (refEntry.type != cmpEntry.type ||
|
|
refEntry.arraySize != cmpEntry.arraySize ||
|
|
refEntry.offset != cmpEntry.offset ||
|
|
refEntry.arrayStride != cmpEntry.arrayStride ||
|
|
refEntry.matrixStride != cmpEntry.matrixStride ||
|
|
refEntry.topLevelArraySize != cmpEntry.topLevelArraySize ||
|
|
refEntry.topLevelArrayStride != cmpEntry.topLevelArrayStride ||
|
|
refEntry.isRowMajor != cmpEntry.isRowMajor)
|
|
{
|
|
log << TestLog::Message << "Error: Layout mismatch in '" << refEntry.name << "':\n"
|
|
<< " expected: " << refEntry << "\n"
|
|
<< " got: " << cmpEntry
|
|
<< TestLog::EndMessage;
|
|
isOk = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return isOk;
|
|
}
|
|
|
|
bool SSBOLayoutCase::compareSharedBlocks (const BufferLayout& refLayout, const BufferLayout& cmpLayout) const
|
|
{
|
|
TestLog& log = m_testCtx.getLog();
|
|
bool isOk = true;
|
|
int numBlocks = m_interface.getNumBlocks();
|
|
|
|
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
|
|
{
|
|
const BufferBlock& block = m_interface.getBlock(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());
|
|
|
|
if ((block.getFlags() & LAYOUT_SHARED) == 0)
|
|
continue; // Not shared layout.
|
|
|
|
DE_ASSERT(refBlockNdx >= 0);
|
|
|
|
if (cmpBlockNdx < 0)
|
|
{
|
|
// Not found, should it?
|
|
log << TestLog::Message << "Error: Buffer block '" << instanceName << "' not found" << TestLog::EndMessage;
|
|
isOk = false;
|
|
continue;
|
|
}
|
|
|
|
const BlockLayoutEntry& refBlockLayout = refLayout.blocks[refBlockNdx];
|
|
const BlockLayoutEntry& cmpBlockLayout = cmpLayout.blocks[cmpBlockNdx];
|
|
|
|
if (refBlockLayout.activeVarIndices.size() != cmpBlockLayout.activeVarIndices.size())
|
|
{
|
|
log << TestLog::Message << "Error: Number of active variables differ in block '" << instanceName
|
|
<< "' (expected " << refBlockLayout.activeVarIndices.size()
|
|
<< ", got " << cmpBlockLayout.activeVarIndices.size()
|
|
<< ")" << TestLog::EndMessage;
|
|
isOk = false;
|
|
}
|
|
|
|
for (vector<int>::const_iterator ndxIter = refBlockLayout.activeVarIndices.begin(); ndxIter != refBlockLayout.activeVarIndices.end(); ndxIter++)
|
|
{
|
|
const BufferVarLayoutEntry& refEntry = refLayout.bufferVars[*ndxIter];
|
|
int cmpEntryNdx = cmpLayout.getVariableIndex(refEntry.name.c_str());
|
|
|
|
if (cmpEntryNdx < 0)
|
|
{
|
|
log << TestLog::Message << "Error: Buffer variable '" << refEntry.name << "' not found" << TestLog::EndMessage;
|
|
isOk = false;
|
|
continue;
|
|
}
|
|
|
|
const BufferVarLayoutEntry& cmpEntry = cmpLayout.bufferVars[cmpEntryNdx];
|
|
|
|
if (refEntry.type != cmpEntry.type ||
|
|
refEntry.arraySize != cmpEntry.arraySize ||
|
|
refEntry.topLevelArraySize != cmpEntry.topLevelArraySize ||
|
|
refEntry.isRowMajor != cmpEntry.isRowMajor)
|
|
{
|
|
log << TestLog::Message << "Error: Type / array size mismatch in '" << refEntry.name << "':\n"
|
|
<< " expected: " << refEntry << "\n"
|
|
<< " got: " << cmpEntry
|
|
<< TestLog::EndMessage;
|
|
isOk = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return isOk;
|
|
}
|
|
|
|
bool SSBOLayoutCase::compareTypes (const BufferLayout& refLayout, const BufferLayout& cmpLayout) const
|
|
{
|
|
TestLog& log = m_testCtx.getLog();
|
|
bool isOk = true;
|
|
int numBlocks = m_interface.getNumBlocks();
|
|
|
|
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
|
|
{
|
|
const BufferBlock& block = m_interface.getBlock(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.activeVarIndices.begin(); ndxIter != cmpBlockLayout.activeVarIndices.end(); ndxIter++)
|
|
{
|
|
const BufferVarLayoutEntry& cmpEntry = cmpLayout.bufferVars[*ndxIter];
|
|
int refEntryNdx = refLayout.getVariableIndex(cmpEntry.name.c_str());
|
|
|
|
if (refEntryNdx < 0)
|
|
{
|
|
log << TestLog::Message << "Error: Buffer variable '" << cmpEntry.name << "' not found in reference layout" << TestLog::EndMessage;
|
|
isOk = false;
|
|
continue;
|
|
}
|
|
|
|
const BufferVarLayoutEntry& refEntry = refLayout.bufferVars[refEntryNdx];
|
|
|
|
if (refEntry.type != cmpEntry.type)
|
|
{
|
|
log << TestLog::Message << "Error: Buffer variable type mismatch in '" << refEntry.name << "':\n"
|
|
<< " expected: " << glu::getDataTypeName(refEntry.type) << "\n"
|
|
<< " got: " << glu::getDataTypeName(cmpEntry.type)
|
|
<< TestLog::EndMessage;
|
|
isOk = false;
|
|
}
|
|
|
|
if (refEntry.arraySize < cmpEntry.arraySize)
|
|
{
|
|
log << TestLog::Message << "Error: Invalid array size in '" << refEntry.name << "': expected <= " << refEntry.arraySize << TestLog::EndMessage;
|
|
isOk = false;
|
|
}
|
|
|
|
if (refEntry.topLevelArraySize < cmpEntry.topLevelArraySize)
|
|
{
|
|
log << TestLog::Message << "Error: Invalid top-level array size in '" << refEntry.name << "': expected <= " << refEntry.topLevelArraySize << TestLog::EndMessage;
|
|
isOk = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return isOk;
|
|
}
|
|
|
|
bool SSBOLayoutCase::checkLayoutIndices (const BufferLayout& layout) const
|
|
{
|
|
TestLog& log = m_testCtx.getLog();
|
|
int numVars = (int)layout.bufferVars.size();
|
|
int numBlocks = (int)layout.blocks.size();
|
|
bool isOk = true;
|
|
|
|
// Check variable block indices.
|
|
for (int varNdx = 0; varNdx < numVars; varNdx++)
|
|
{
|
|
const BufferVarLayoutEntry& bufVar = layout.bufferVars[varNdx];
|
|
|
|
if (bufVar.blockNdx < 0 || !deInBounds32(bufVar.blockNdx, 0, numBlocks))
|
|
{
|
|
log << TestLog::Message << "Error: Invalid block index in buffer variable '" << bufVar.name << "'" << TestLog::EndMessage;
|
|
isOk = false;
|
|
}
|
|
}
|
|
|
|
// Check active variables.
|
|
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
|
|
{
|
|
const BlockLayoutEntry& block = layout.blocks[blockNdx];
|
|
|
|
for (vector<int>::const_iterator varNdxIter = block.activeVarIndices.begin(); varNdxIter != block.activeVarIndices.end(); varNdxIter++)
|
|
{
|
|
if (!deInBounds32(*varNdxIter, 0, numVars))
|
|
{
|
|
log << TestLog::Message << "Error: Invalid active variable index " << *varNdxIter << " in block '" << block.name << "'" << TestLog::EndMessage;
|
|
isOk = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return isOk;
|
|
}
|
|
|
|
bool SSBOLayoutCase::checkLayoutBounds (const BufferLayout& layout) const
|
|
{
|
|
TestLog& log = m_testCtx.getLog();
|
|
const int numVars = (int)layout.bufferVars.size();
|
|
bool isOk = true;
|
|
|
|
for (int varNdx = 0; varNdx < numVars; varNdx++)
|
|
{
|
|
const BufferVarLayoutEntry& var = layout.bufferVars[varNdx];
|
|
|
|
if (var.blockNdx < 0 || isUnsizedArray(var))
|
|
continue;
|
|
|
|
const BlockLayoutEntry& block = layout.blocks[var.blockNdx];
|
|
const bool isMatrix = glu::isDataTypeMatrix(var.type);
|
|
const int numVecs = isMatrix ? (var.isRowMajor ? glu::getDataTypeMatrixNumRows(var.type) : glu::getDataTypeMatrixNumColumns(var.type)) : 1;
|
|
const int numComps = isMatrix ? (var.isRowMajor ? glu::getDataTypeMatrixNumColumns(var.type) : glu::getDataTypeMatrixNumRows(var.type)) : glu::getDataTypeScalarSize(var.type);
|
|
const int numElements = var.arraySize;
|
|
const int topLevelSize = var.topLevelArraySize;
|
|
const int arrayStride = var.arrayStride;
|
|
const int topLevelStride = var.topLevelArrayStride;
|
|
const int compSize = sizeof(deUint32);
|
|
const int vecSize = numComps*compSize;
|
|
|
|
int minOffset = 0;
|
|
int maxOffset = 0;
|
|
|
|
// For negative strides.
|
|
minOffset = de::min(minOffset, (numVecs-1)*var.matrixStride);
|
|
minOffset = de::min(minOffset, (numElements-1)*arrayStride);
|
|
minOffset = de::min(minOffset, (topLevelSize-1)*topLevelStride + (numElements-1)*arrayStride + (numVecs-1)*var.matrixStride);
|
|
|
|
maxOffset = de::max(maxOffset, vecSize);
|
|
maxOffset = de::max(maxOffset, (numVecs-1)*var.matrixStride + vecSize);
|
|
maxOffset = de::max(maxOffset, (numElements-1)*arrayStride + vecSize);
|
|
maxOffset = de::max(maxOffset, (topLevelSize-1)*topLevelStride + (numElements-1)*arrayStride + vecSize);
|
|
maxOffset = de::max(maxOffset, (topLevelSize-1)*topLevelStride + (numElements-1)*arrayStride + (numVecs-1)*var.matrixStride + vecSize);
|
|
|
|
if (var.offset+minOffset < 0 || var.offset+maxOffset > block.size)
|
|
{
|
|
log << TestLog::Message << "Error: Variable '" << var.name << "' out of block bounds" << TestLog::EndMessage;
|
|
isOk = false;
|
|
}
|
|
}
|
|
|
|
return isOk;
|
|
}
|
|
|
|
bool SSBOLayoutCase::checkIndexQueries (deUint32 program, const BufferLayout& layout) const
|
|
{
|
|
tcu::TestLog& log = m_testCtx.getLog();
|
|
const glw::Functions& gl = m_renderCtx.getFunctions();
|
|
bool allOk = true;
|
|
|
|
// \note Spec mandates that buffer blocks are assigned consecutive locations from 0.
|
|
// 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.getProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, block.name.c_str());
|
|
|
|
if (queriedNdx != blockNdx)
|
|
{
|
|
log << TestLog::Message << "ERROR: glGetProgramResourceIndex(" << block.name << ") returned " << queriedNdx << ", expected " << blockNdx << "!" << TestLog::EndMessage;
|
|
allOk = false;
|
|
}
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformBlockIndex()");
|
|
}
|
|
|
|
return allOk;
|
|
}
|
|
|
|
bool SSBOLayoutCase::execute (deUint32 program)
|
|
{
|
|
const glw::Functions& gl = m_renderCtx.getFunctions();
|
|
const deUint32 numPassedLoc = gl.getProgramResourceIndex(program, GL_UNIFORM, "ac_numPassed");
|
|
const glu::InterfaceVariableInfo acVarInfo = numPassedLoc != GL_INVALID_INDEX ? glu::getProgramInterfaceVariableInfo(gl, program, GL_UNIFORM, numPassedLoc)
|
|
: glu::InterfaceVariableInfo();
|
|
const glu::InterfaceBlockInfo acBufferInfo = acVarInfo.atomicCounterBufferIndex != GL_INVALID_INDEX ? glu::getProgramInterfaceBlockInfo(gl, program, GL_ATOMIC_COUNTER_BUFFER, acVarInfo.atomicCounterBufferIndex)
|
|
: glu::InterfaceBlockInfo();
|
|
const glu::Buffer acBuffer (m_renderCtx);
|
|
bool isOk = true;
|
|
|
|
if (numPassedLoc == GL_INVALID_INDEX)
|
|
throw tcu::TestError("No location for ac_numPassed found");
|
|
|
|
if (acBufferInfo.index == GL_INVALID_INDEX)
|
|
throw tcu::TestError("ac_numPassed buffer index is GL_INVALID_INDEX");
|
|
|
|
if (acBufferInfo.dataSize == 0)
|
|
throw tcu::TestError("ac_numPassed buffer size = 0");
|
|
|
|
// Initialize atomic counter buffer.
|
|
{
|
|
vector<deUint8> emptyData(acBufferInfo.dataSize, 0);
|
|
|
|
gl.bindBuffer(GL_ATOMIC_COUNTER_BUFFER, *acBuffer);
|
|
gl.bufferData(GL_ATOMIC_COUNTER_BUFFER, (glw::GLsizeiptr)emptyData.size(), &emptyData[0], GL_STATIC_READ);
|
|
gl.bindBufferBase(GL_ATOMIC_COUNTER_BUFFER, acBufferInfo.index, *acBuffer);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Setting up buffer for ac_numPassed failed");
|
|
}
|
|
|
|
gl.useProgram(program);
|
|
gl.dispatchCompute(1, 1, 1);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute() failed");
|
|
|
|
// Read back ac_numPassed data.
|
|
{
|
|
const void* mapPtr = gl.mapBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, acBufferInfo.dataSize, GL_MAP_READ_BIT);
|
|
const int refCount = 1;
|
|
int resCount = 0;
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER) failed");
|
|
TCU_CHECK(mapPtr);
|
|
|
|
resCount = *(const int*)((const deUint8*)mapPtr + acVarInfo.offset);
|
|
|
|
gl.unmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER) failed");
|
|
|
|
if (refCount != resCount)
|
|
{
|
|
m_testCtx.getLog() << TestLog::Message << "ERROR: ac_numPassed = " << resCount << ", expected " << refCount << TestLog::EndMessage;
|
|
isOk = false;
|
|
}
|
|
}
|
|
|
|
GLU_EXPECT_NO_ERROR(gl.getError(), "Shader execution failed");
|
|
|
|
return isOk;
|
|
}
|
|
|
|
} // gles31
|
|
} // deqp
|