blob: 70adcd6f781f912159575f04e8fa2f95a8373a16 [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2015 The Khronos Group Inc.
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* Copyright (c) 2016 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 Uniform block case.
*//*--------------------------------------------------------------------*/
#include "vktUniformBlockCase.hpp"
#include "vkPrograms.hpp"
#include "gluVarType.hpp"
#include "tcuTestLog.hpp"
#include "tcuSurface.hpp"
#include "deRandom.hpp"
#include "deStringUtil.hpp"
#include "tcuTextureUtil.hpp"
#include "deSharedPtr.hpp"
#include "deFloat16.h"
#include "vkMemUtil.hpp"
#include "vkQueryUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkRef.hpp"
#include "vkRefUtil.hpp"
#include "vkBuilderUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkObjUtil.hpp"
#include "vkImageUtil.hpp"
#include <map>
#include <set>
namespace vkt
{
namespace ubo
{
using namespace vk;
// VarType implementation.
VarType::VarType (void)
: m_type (TYPE_LAST)
, m_flags (0)
{
}
VarType::VarType (const VarType& other)
: m_type (TYPE_LAST)
, m_flags (0)
{
*this = other;
}
VarType::VarType (glu::DataType basicType, deUint32 flags)
: m_type (TYPE_BASIC)
, m_flags (flags)
{
m_data.basicType = basicType;
}
VarType::VarType (const VarType& elementType, int arraySize)
: m_type (TYPE_ARRAY)
, m_flags (0)
{
m_data.array.size = arraySize;
m_data.array.elementType = new VarType(elementType);
}
VarType::VarType (const StructType* structPtr, deUint32 flags)
: m_type (TYPE_STRUCT)
, m_flags (flags)
{
m_data.structPtr = structPtr;
}
VarType::~VarType (void)
{
if (m_type == TYPE_ARRAY)
delete m_data.array.elementType;
}
VarType& VarType::operator= (const VarType& other)
{
if (this == &other)
return *this; // Self-assignment.
VarType *oldElementType = m_type == TYPE_ARRAY ? m_data.array.elementType : DE_NULL;
m_type = other.m_type;
m_flags = other.m_flags;
m_data = Data();
if (m_type == TYPE_ARRAY)
{
m_data.array.elementType = new VarType(*other.m_data.array.elementType);
m_data.array.size = other.m_data.array.size;
}
else
m_data = other.m_data;
delete oldElementType;
return *this;
}
// StructType implementation.
void StructType::addMember (const std::string& name, const VarType& type, deUint32 flags)
{
m_members.push_back(StructMember(name, type, flags));
}
// Uniform implementation.
Uniform::Uniform (const std::string& name, const VarType& type, deUint32 flags)
: m_name (name)
, m_type (type)
, m_flags (flags)
{
}
// UniformBlock implementation.
UniformBlock::UniformBlock (const std::string& blockName)
: m_blockName (blockName)
, m_arraySize (0)
, m_flags (0)
{
}
std::ostream& operator<< (std::ostream& stream, const BlockLayoutEntry& entry)
{
stream << entry.name << " { name = " << entry.name
<< ", size = " << entry.size
<< ", activeUniformIndices = [";
for (std::vector<int>::const_iterator i = entry.activeUniformIndices.begin(); i != entry.activeUniformIndices.end(); i++)
{
if (i != entry.activeUniformIndices.begin())
stream << ", ";
stream << *i;
}
stream << "] }";
return stream;
}
std::ostream& operator<< (std::ostream& stream, const UniformLayoutEntry& entry)
{
stream << entry.name << " { type = " << glu::getDataTypeName(entry.type)
<< ", size = " << entry.size
<< ", blockNdx = " << entry.blockNdx
<< ", offset = " << entry.offset
<< ", arrayStride = " << entry.arrayStride
<< ", matrixStride = " << entry.matrixStride
<< ", isRowMajor = " << (entry.isRowMajor ? "true" : "false")
<< " }";
return stream;
}
int UniformLayout::getUniformLayoutIndex (int blockNdx, const std::string& name) const
{
for (int ndx = 0; ndx < (int)uniforms.size(); ndx++)
{
if (blocks[uniforms[ndx].blockNdx].blockDeclarationNdx == blockNdx &&
uniforms[ndx].name == name)
return ndx;
}
return -1;
}
int UniformLayout::getBlockLayoutIndex (int blockNdx, int instanceNdx) const
{
for (int ndx = 0; ndx < (int)blocks.size(); ndx++)
{
if (blocks[ndx].blockDeclarationNdx == blockNdx &&
blocks[ndx].instanceNdx == instanceNdx)
return ndx;
}
return -1;
}
// ShaderInterface implementation.
ShaderInterface::ShaderInterface (void)
{
}
ShaderInterface::~ShaderInterface (void)
{
}
StructType& ShaderInterface::allocStruct (const std::string& name)
{
m_structs.push_back(StructTypeSP(new StructType(name)));
return *m_structs.back();
}
struct StructNameEquals
{
std::string name;
StructNameEquals (const std::string& name_) : name(name_) {}
bool operator() (const StructTypeSP type) const
{
return type->hasTypeName() && name == type->getTypeName();
}
};
void ShaderInterface::getNamedStructs (std::vector<const StructType*>& structs) const
{
for (std::vector<StructTypeSP>::const_iterator i = m_structs.begin(); i != m_structs.end(); i++)
{
if ((*i)->hasTypeName())
structs.push_back((*i).get());
}
}
UniformBlock& ShaderInterface::allocBlock (const std::string& name)
{
m_uniformBlocks.push_back(UniformBlockSP(new UniformBlock(name)));
return *m_uniformBlocks.back();
}
bool ShaderInterface::usesBlockLayout (UniformFlags layoutFlag) const
{
for (int i = 0, num_blocks = getNumUniformBlocks() ; i < num_blocks ; i++)
{
if (m_uniformBlocks[i]->getFlags() & layoutFlag)
return true;
}
return false;
}
namespace // Utilities
{
struct PrecisionFlagsFmt
{
deUint32 flags;
PrecisionFlagsFmt (deUint32 flags_) : flags(flags_) {}
};
std::ostream& operator<< (std::ostream& str, const PrecisionFlagsFmt& fmt)
{
// Precision.
DE_ASSERT(dePop32(fmt.flags & (PRECISION_LOW|PRECISION_MEDIUM|PRECISION_HIGH)) <= 1);
str << (fmt.flags & PRECISION_LOW ? "lowp" :
fmt.flags & PRECISION_MEDIUM ? "mediump" :
fmt.flags & PRECISION_HIGH ? "highp" : "");
return str;
}
struct LayoutFlagsFmt
{
deUint32 flags;
deUint32 offset;
LayoutFlagsFmt (deUint32 flags_, deUint32 offset_ = 0u) : flags(flags_), offset(offset_) {}
};
std::ostream& operator<< (std::ostream& str, const LayoutFlagsFmt& fmt)
{
static const struct
{
deUint32 bit;
const char* token;
} bitDesc[] =
{
{ LAYOUT_STD140, "std140" },
{ LAYOUT_STD430, "std430" },
{ LAYOUT_SCALAR, "scalar" },
{ LAYOUT_ROW_MAJOR, "row_major" },
{ LAYOUT_COLUMN_MAJOR, "column_major" },
{ LAYOUT_OFFSET, "offset" },
};
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;
if (bitDesc[descNdx].bit == LAYOUT_OFFSET)
str << " = " << fmt.offset;
remBits &= ~bitDesc[descNdx].bit;
}
}
DE_ASSERT(remBits == 0);
return str;
}
// Layout computation.
int getDataTypeByteSize (glu::DataType type)
{
if (deInRange32(type, glu::TYPE_UINT8, glu::TYPE_UINT8_VEC4) || deInRange32(type, glu::TYPE_INT8, glu::TYPE_INT8_VEC4))
{
return glu::getDataTypeScalarSize(type)*(int)sizeof(deUint8);
}
if (deInRange32(type, glu::TYPE_UINT16, glu::TYPE_UINT16_VEC4) || deInRange32(type, glu::TYPE_INT16, glu::TYPE_INT16_VEC4) || deInRange32(type, glu::TYPE_FLOAT16, glu::TYPE_FLOAT16_VEC4))
{
return glu::getDataTypeScalarSize(type)*(int)sizeof(deUint16);
}
else
{
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);
case glu::TYPE_UINT8:
case glu::TYPE_INT8 : return 1*(int)sizeof(deUint8);
case glu::TYPE_UINT8_VEC2:
case glu::TYPE_INT8_VEC2: return 2*(int)sizeof(deUint8);
case glu::TYPE_UINT8_VEC3:
case glu::TYPE_INT8_VEC3: // Fall-through to vec4
case glu::TYPE_UINT8_VEC4:
case glu::TYPE_INT8_VEC4: return 4*(int)sizeof(deUint8);
case glu::TYPE_UINT16:
case glu::TYPE_INT16:
case glu::TYPE_FLOAT16: return 1*(int)sizeof(deUint16);
case glu::TYPE_UINT16_VEC2:
case glu::TYPE_INT16_VEC2:
case glu::TYPE_FLOAT16_VEC2: return 2*(int)sizeof(deUint16);
case glu::TYPE_UINT16_VEC3:
case glu::TYPE_INT16_VEC3:
case glu::TYPE_FLOAT16_VEC3: // Fall-through to vec4
case glu::TYPE_UINT16_VEC4:
case glu::TYPE_INT16_VEC4:
case glu::TYPE_FLOAT16_VEC4: return 4*(int)sizeof(deUint16);
default:
DE_ASSERT(false);
return 0;
}
}
deInt32 getminUniformBufferOffsetAlignment (Context &ctx)
{
VkPhysicalDeviceProperties properties;
ctx.getInstanceInterface().getPhysicalDeviceProperties(ctx.getPhysicalDevice(), &properties);
VkDeviceSize align = properties.limits.minUniformBufferOffsetAlignment;
DE_ASSERT(align == (VkDeviceSize)(deInt32)align);
return (deInt32)align;
}
static inline int deRoundUp32 (int a, int b)
{
int d = a/b;
return d*b == a ? a : (d+1)*b;
}
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;
}
}
int computeRelaxedBlockBaseAlignment (const VarType& type, deUint32 layoutFlags)
{
if (type.isBasicType())
{
glu::DataType basicType = type.getBasicType();
if (glu::isDataTypeVector(basicType))
return getDataTypeByteAlignment(glu::getDataTypeScalarType(basicType));
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, computeRelaxedBlockBaseAlignment(memberIter->getType(), layoutFlags));
return maxBaseAlignment;
}
}
int computeScalarBlockAlignment (const VarType& type, deUint32 layoutFlags)
{
if (type.isBasicType())
{
return getDataTypeByteAlignment(glu::getDataTypeScalarType(type.getBasicType()));
}
else if (type.isArrayType())
return computeScalarBlockAlignment(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, computeScalarBlockAlignment(memberIter->getType(), layoutFlags));
return maxBaseAlignment;
}
}
inline deUint32 mergeLayoutFlags (deUint32 prevFlags, deUint32 newFlags)
{
const deUint32 packingMask = LAYOUT_STD140|LAYOUT_STD430|LAYOUT_SCALAR;
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 (
UniformLayout& layout,
int curBlockNdx,
int baseOffset,
const std::string& curPrefix,
const VarType& type,
deUint32 layoutFlags)
{
// HACK to make code match SSBO tests
const int LAYOUT_RELAXED = 0;
// Reference layout uses std140 rules by default. std430 rules are
// choosen only for blocks that have std140 layout.
const int baseAlignment = (layoutFlags & LAYOUT_SCALAR) != 0 ? computeScalarBlockAlignment(type, layoutFlags) :
(layoutFlags & LAYOUT_STD430) != 0 ? computeStd430BaseAlignment(type, layoutFlags) :
(layoutFlags & LAYOUT_RELAXED) != 0 ? computeRelaxedBlockBaseAlignment(type, layoutFlags) :
computeStd140BaseAlignment(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();
UniformLayoutEntry 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 vecSize = isRowMajor ? glu::getDataTypeMatrixNumColumns(basicType)
: glu::getDataTypeMatrixNumRows(basicType);
const glu::DataType vecType = glu::getDataTypeFloatVec(vecSize);
const int numVecs = isRowMajor ? glu::getDataTypeMatrixNumRows(basicType)
: glu::getDataTypeMatrixNumColumns(basicType);
const int vecStride = (layoutFlags & LAYOUT_SCALAR) ? getDataTypeByteSize(vecType) : baseAlignment;
entry.offset = curOffset;
entry.matrixStride = vecStride;
entry.isRowMajor = isRowMajor;
curOffset += numVecs*entry.matrixStride;
}
else
{
if (!(layoutFlags & LAYOUT_SCALAR) && (layoutFlags & LAYOUT_RELAXED) &&
glu::isDataTypeVector(basicType) && (getDataTypeByteSize(basicType) <= 16 ? curOffset / 16 != (curOffset + getDataTypeByteSize(basicType) - 1) / 16 : curOffset % 16 != 0))
curOffset = deIntRoundToPow2(curOffset, 16);
// Scalar or vector.
entry.offset = curOffset;
curOffset += getDataTypeByteSize(basicType);
}
layout.uniforms.push_back(entry);
}
else if (type.isArrayType())
{
const VarType& elemType = type.getElementType();
if (elemType.isBasicType() && !glu::isDataTypeMatrix(elemType.getBasicType()))
{
// Array of scalars or vectors.
const glu::DataType elemBasicType = elemType.getBasicType();
const int stride = (layoutFlags & LAYOUT_SCALAR) ? getDataTypeByteSize(elemBasicType) : baseAlignment;
UniformLayoutEntry 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.uniforms.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 vecSize = isRowMajor ? glu::getDataTypeMatrixNumColumns(elemBasicType)
: glu::getDataTypeMatrixNumRows(elemBasicType);
const glu::DataType vecType = glu::getDataTypeFloatVec(vecSize);
const int numVecs = isRowMajor ? glu::getDataTypeMatrixNumRows(elemBasicType)
: glu::getDataTypeMatrixNumColumns(elemBasicType);
const int vecStride = (layoutFlags & LAYOUT_SCALAR) ? getDataTypeByteSize(vecType) : baseAlignment;
UniformLayoutEntry 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 += entry.arrayStride*type.getArraySize();
layout.uniforms.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);
if (!(layoutFlags & LAYOUT_SCALAR))
curOffset = deAlign32(curOffset, baseAlignment);
}
return curOffset-baseOffset;
}
void computeReferenceLayout (UniformLayout& layout, const ShaderInterface& interface)
{
int numUniformBlocks = interface.getNumUniformBlocks();
for (int blockNdx = 0; blockNdx < numUniformBlocks; blockNdx++)
{
const UniformBlock& block = interface.getUniformBlock(blockNdx);
bool hasInstanceName = block.hasInstanceName();
std::string blockPrefix = hasInstanceName ? (block.getBlockName() + ".") : "";
int curOffset = 0;
int activeBlockNdx = (int)layout.blocks.size();
int firstUniformNdx = (int)layout.uniforms.size();
for (UniformBlock::ConstIterator uniformIter = block.begin(); uniformIter != block.end(); uniformIter++)
{
const Uniform& uniform = *uniformIter;
curOffset += computeReferenceLayout(layout, activeBlockNdx, curOffset, blockPrefix + uniform.getName(), uniform.getType(), mergeLayoutFlags(block.getFlags(), uniform.getFlags()));
}
int uniformIndicesEnd = (int)layout.uniforms.size();
int blockSize = curOffset;
int numInstances = block.isArray() ? block.getArraySize() : 1;
// Create block layout entries for each instance.
for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
{
// Allocate entry for instance.
layout.blocks.push_back(BlockLayoutEntry());
BlockLayoutEntry& blockEntry = layout.blocks.back();
blockEntry.name = block.getBlockName();
blockEntry.size = blockSize;
blockEntry.bindingNdx = blockNdx;
blockEntry.blockDeclarationNdx = blockNdx;
blockEntry.instanceNdx = instanceNdx;
// Compute active uniform set for block.
for (int uniformNdx = firstUniformNdx; uniformNdx < uniformIndicesEnd; uniformNdx++)
blockEntry.activeUniformIndices.push_back(uniformNdx);
if (block.isArray())
blockEntry.name += "[" + de::toString(instanceNdx) + "]";
}
}
}
// Value generator.
void generateValue (const UniformLayoutEntry& entry, void* basePtr, de::Random& rnd)
{
glu::DataType scalarType = glu::getDataTypeScalarType(entry.type);
int scalarSize = glu::getDataTypeScalarSize(entry.type);
bool isMatrix = glu::isDataTypeMatrix(entry.type);
int numVecs = isMatrix ? (entry.isRowMajor ? glu::getDataTypeMatrixNumRows(entry.type) : glu::getDataTypeMatrixNumColumns(entry.type)) : 1;
int vecSize = scalarSize / numVecs;
bool isArray = entry.size > 1;
const size_t compSize = getDataTypeByteSize(scalarType);
DE_ASSERT(scalarSize%numVecs == 0);
for (int elemNdx = 0; elemNdx < entry.size; elemNdx++)
{
deUint8* elemPtr = (deUint8*)basePtr + entry.offset + (isArray ? elemNdx*entry.arrayStride : 0);
for (int vecNdx = 0; vecNdx < numVecs; vecNdx++)
{
deUint8* vecPtr = elemPtr + (isMatrix ? vecNdx*entry.matrixStride : 0);
for (int compNdx = 0; compNdx < vecSize; compNdx++)
{
deUint8* compPtr = vecPtr + compSize*compNdx;
switch (scalarType)
{
case glu::TYPE_FLOAT: *((float*)compPtr) = (float)rnd.getInt(-9, 9); break;
case glu::TYPE_INT: *((int*)compPtr) = rnd.getInt(-9, 9); break;
case glu::TYPE_UINT: *((deUint32*)compPtr) = (deUint32)rnd.getInt(0, 9); break;
case glu::TYPE_INT8: *((deInt8*)compPtr) = (deInt8)rnd.getInt(-9, 9); break;
case glu::TYPE_UINT8: *((deUint8*)compPtr) = (deUint8)rnd.getInt(0, 9); break;
case glu::TYPE_INT16: *((deInt16*)compPtr) = (deInt16)rnd.getInt(-9, 9); break;
case glu::TYPE_UINT16: *((deUint16*)compPtr) = (deUint16)rnd.getInt(0, 9); break;
case glu::TYPE_FLOAT16: *((deFloat16*)compPtr) = deFloat32To16((float)rnd.getInt(-9, 9)); break;
// \note Random bit pattern is used for true values. Spec states that all non-zero values are
// interpreted as true but some implementations fail this.
case glu::TYPE_BOOL: *((deUint32*)compPtr) = rnd.getBool() ? rnd.getUint32()|1u : 0u; break;
default:
DE_ASSERT(false);
}
}
}
}
}
void generateValues (const UniformLayout& layout, const std::map<int, void*>& blockPointers, deUint32 seed)
{
de::Random rnd (seed);
int numBlocks = (int)layout.blocks.size();
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
void* basePtr = blockPointers.find(blockNdx)->second;
int numEntries = (int)layout.blocks[blockNdx].activeUniformIndices.size();
for (int entryNdx = 0; entryNdx < numEntries; entryNdx++)
{
const UniformLayoutEntry& entry = layout.uniforms[layout.blocks[blockNdx].activeUniformIndices[entryNdx]];
generateValue(entry, basePtr, rnd);
}
}
}
// Shader generator.
const char* getCompareFuncForType (glu::DataType type)
{
switch (type)
{
case glu::TYPE_FLOAT: return "mediump float compare_float (highp float a, highp float b) { return abs(a - b) < 0.05 ? 1.0 : 0.0; }\n";
case glu::TYPE_FLOAT_VEC2: return "mediump float 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 "mediump float compare_vec3 (highp vec3 a, highp vec3 b) { return compare_float(a.x, b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z); }\n";
case glu::TYPE_FLOAT_VEC4: return "mediump float compare_vec4 (highp vec4 a, highp vec4 b) { return compare_float(a.x, b.x)*compare_float(a.y, b.y)*compare_float(a.z, b.z)*compare_float(a.w, b.w); }\n";
case glu::TYPE_FLOAT_MAT2: return "mediump float 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 "mediump float 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 "mediump float 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 "mediump float compare_mat3x2 (highp mat3x2 a, highp mat3x2 b){ return compare_vec2(a[0], b[0])*compare_vec2(a[1], b[1])*compare_vec2(a[2], b[2]); }\n";
case glu::TYPE_FLOAT_MAT3: return "mediump float compare_mat3 (highp mat3 a, highp mat3 b) { return compare_vec3(a[0], b[0])*compare_vec3(a[1], b[1])*compare_vec3(a[2], b[2]); }\n";
case glu::TYPE_FLOAT_MAT3X4: return "mediump float compare_mat3x4 (highp mat3x4 a, highp mat3x4 b){ return compare_vec4(a[0], b[0])*compare_vec4(a[1], b[1])*compare_vec4(a[2], b[2]); }\n";
case glu::TYPE_FLOAT_MAT4X2: return "mediump float compare_mat4x2 (highp mat4x2 a, highp mat4x2 b){ return compare_vec2(a[0], b[0])*compare_vec2(a[1], b[1])*compare_vec2(a[2], b[2])*compare_vec2(a[3], b[3]); }\n";
case glu::TYPE_FLOAT_MAT4X3: return "mediump float compare_mat4x3 (highp mat4x3 a, highp mat4x3 b){ return compare_vec3(a[0], b[0])*compare_vec3(a[1], b[1])*compare_vec3(a[2], b[2])*compare_vec3(a[3], b[3]); }\n";
case glu::TYPE_FLOAT_MAT4: return "mediump float compare_mat4 (highp mat4 a, highp mat4 b) { return compare_vec4(a[0], b[0])*compare_vec4(a[1], b[1])*compare_vec4(a[2], b[2])*compare_vec4(a[3], b[3]); }\n";
case glu::TYPE_INT: return "mediump float compare_int (highp int a, highp int b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_INT_VEC2: return "mediump float compare_ivec2 (highp ivec2 a, highp ivec2 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_INT_VEC3: return "mediump float compare_ivec3 (highp ivec3 a, highp ivec3 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_INT_VEC4: return "mediump float compare_ivec4 (highp ivec4 a, highp ivec4 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_UINT: return "mediump float compare_uint (highp uint a, highp uint b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_UINT_VEC2: return "mediump float compare_uvec2 (highp uvec2 a, highp uvec2 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_UINT_VEC3: return "mediump float compare_uvec3 (highp uvec3 a, highp uvec3 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_UINT_VEC4: return "mediump float compare_uvec4 (highp uvec4 a, highp uvec4 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_BOOL: return "mediump float compare_bool (bool a, bool b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_BOOL_VEC2: return "mediump float compare_bvec2 (bvec2 a, bvec2 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_BOOL_VEC3: return "mediump float compare_bvec3 (bvec3 a, bvec3 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_BOOL_VEC4: return "mediump float compare_bvec4 (bvec4 a, bvec4 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_FLOAT16: return "mediump float compare_float16_t(highp float a, highp float b) { return abs(a - b) < 0.05 ? 1.0 : 0.0; }\n";
case glu::TYPE_FLOAT16_VEC2: return "mediump float compare_f16vec2 (highp vec2 a, highp vec2 b) { return compare_float(a.x, b.x)*compare_float(a.y, b.y); }\n";
case glu::TYPE_FLOAT16_VEC3: return "mediump float compare_f16vec3 (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_FLOAT16_VEC4: return "mediump float compare_f16vec4 (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_INT8: return "mediump float compare_int8_t (highp int a, highp int b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_INT8_VEC2: return "mediump float compare_i8vec2 (highp ivec2 a, highp ivec2 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_INT8_VEC3: return "mediump float compare_i8vec3 (highp ivec3 a, highp ivec3 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_INT8_VEC4: return "mediump float compare_i8vec4 (highp ivec4 a, highp ivec4 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_UINT8: return "mediump float compare_uint8_t (highp uint a, highp uint b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_UINT8_VEC2: return "mediump float compare_u8vec2 (highp uvec2 a, highp uvec2 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_UINT8_VEC3: return "mediump float compare_u8vec3 (highp uvec3 a, highp uvec3 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_UINT8_VEC4: return "mediump float compare_u8vec4 (highp uvec4 a, highp uvec4 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_INT16: return "mediump float compare_int16_t (highp int a, highp int b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_INT16_VEC2: return "mediump float compare_i16vec2 (highp ivec2 a, highp ivec2 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_INT16_VEC3: return "mediump float compare_i16vec3 (highp ivec3 a, highp ivec3 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_INT16_VEC4: return "mediump float compare_i16vec4 (highp ivec4 a, highp ivec4 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_UINT16: return "mediump float compare_uint16_t (highp uint a, highp uint b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_UINT16_VEC2: return "mediump float compare_u16vec2 (highp uvec2 a, highp uvec2 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_UINT16_VEC3: return "mediump float compare_u16vec3 (highp uvec3 a, highp uvec3 b) { return a == b ? 1.0 : 0.0; }\n";
case glu::TYPE_UINT16_VEC4: return "mediump float compare_u16vec4 (highp uvec4 a, highp uvec4 b) { return a == b ? 1.0 : 0.0; }\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:
case glu::TYPE_FLOAT16_VEC2:
case glu::TYPE_FLOAT16_VEC3:
case glu::TYPE_FLOAT16_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.getStruct().begin(); iter != type.getStruct().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 UniformBlock& uniformBlock)
{
for (UniformBlock::ConstIterator iter = uniformBlock.begin(); iter != uniformBlock.end(); ++iter)
collectUniqueBasicTypes(basicTypes, iter->getType());
}
void collectUniqueBasicTypes (std::set<glu::DataType>& basicTypes, const ShaderInterface& interface)
{
for (int ndx = 0; ndx < interface.getNumUniformBlocks(); ++ndx)
collectUniqueBasicTypes(basicTypes, interface.getUniformBlock(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::ostringstream& src, const VarType& type, const std::string& name, int indentLevel, deUint32 unusedHints, deUint32 flagsMask, deUint32 offset);
void generateDeclaration (std::ostringstream& src, const Uniform& uniform, int indentLevel, deUint32 offset);
void generateDeclaration (std::ostringstream& src, const StructType& structType, int indentLevel);
void generateLocalDeclaration (std::ostringstream& src, const StructType& structType, int indentLevel);
void generateFullDeclaration (std::ostringstream& src, const StructType& structType, int indentLevel);
void generateDeclaration (std::ostringstream& src, const StructType& structType, int indentLevel)
{
DE_ASSERT(structType.hasTypeName());
generateFullDeclaration(src, structType, indentLevel);
src << ";\n";
}
void generateFullDeclaration (std::ostringstream& src, const StructType& structType, int indentLevel)
{
src << "struct";
if (structType.hasTypeName())
src << " " << structType.getTypeName();
src << "\n" << Indent(indentLevel) << "{\n";
for (StructType::ConstIterator memberIter = structType.begin(); memberIter != structType.end(); memberIter++)
{
src << Indent(indentLevel + 1);
generateDeclaration(src, memberIter->getType(), memberIter->getName(), indentLevel + 1, memberIter->getFlags() & UNUSED_BOTH, ~LAYOUT_OFFSET, 0u);
}
src << Indent(indentLevel) << "}";
}
void generateLocalDeclaration (std::ostringstream& src, const StructType& structType, int /* indentLevel */)
{
src << structType.getTypeName();
}
void generateLayoutAndPrecisionDeclaration (std::ostringstream& src, deUint32 flags, deUint32 offset)
{
if ((flags & LAYOUT_MASK) != 0)
src << "layout(" << LayoutFlagsFmt(flags & LAYOUT_MASK, offset) << ") ";
if ((flags & PRECISION_MASK) != 0)
src << PrecisionFlagsFmt(flags & PRECISION_MASK) << " ";
}
void generateDeclaration (std::ostringstream& src, const VarType& type, const std::string& name, int indentLevel, deUint32 unusedHints, deUint32 flagsMask, deUint32 offset)
{
generateLayoutAndPrecisionDeclaration(src, type.getFlags() & flagsMask, offset);
if (type.isBasicType())
src << glu::getDataTypeName(type.getBasicType()) << " " << name;
else if (type.isArrayType())
{
std::vector<int> arraySizes;
const VarType* curType = &type;
while (curType->isArrayType())
{
arraySizes.push_back(curType->getArraySize());
curType = &curType->getElementType();
}
generateLayoutAndPrecisionDeclaration(src, curType->getFlags() & flagsMask, offset);
if (curType->isBasicType())
src << glu::getDataTypeName(curType->getBasicType());
else
{
DE_ASSERT(curType->isStructType());
generateLocalDeclaration(src, curType->getStruct(), indentLevel+1);
}
src << " " << name;
for (std::vector<int>::const_iterator sizeIter = arraySizes.begin(); sizeIter != arraySizes.end(); sizeIter++)
src << "[" << *sizeIter << "]";
}
else
{
generateLocalDeclaration(src, type.getStruct(), indentLevel+1);
src << " " << name;
}
src << ";";
// Print out unused hints.
if (unusedHints != 0)
src << " // unused in " << (unusedHints == UNUSED_BOTH ? "both shaders" :
unusedHints == UNUSED_VERTEX ? "vertex shader" :
unusedHints == UNUSED_FRAGMENT ? "fragment shader" : "???");
src << "\n";
}
void generateDeclaration (std::ostringstream& src, const Uniform& uniform, int indentLevel, deUint32 offset)
{
if ((uniform.getFlags() & LAYOUT_MASK) != 0)
src << "layout(" << LayoutFlagsFmt(uniform.getFlags() & LAYOUT_MASK) << ") ";
generateDeclaration(src, uniform.getType(), uniform.getName(), indentLevel, uniform.getFlags() & UNUSED_BOTH, ~0u, offset);
}
deUint32 getBlockMemberOffset (int blockNdx, const UniformBlock& block, const Uniform& uniform, const UniformLayout& layout)
{
std::ostringstream name;
const VarType* curType = &uniform.getType();
if (block.getInstanceName().length() != 0)
name << block.getBlockName() << "."; // \note UniformLayoutEntry uses block name rather than instance name
name << uniform.getName();
while (!curType->isBasicType())
{
if (curType->isArrayType())
{
name << "[0]";
curType = &curType->getElementType();
}
if (curType->isStructType())
{
const StructType::ConstIterator firstMember = curType->getStruct().begin();
name << "." << firstMember->getName();
curType = &firstMember->getType();
}
}
const int uniformNdx = layout.getUniformLayoutIndex(blockNdx, name.str());
DE_ASSERT(uniformNdx >= 0);
return layout.uniforms[uniformNdx].offset;
}
template<typename T>
void semiShuffle (std::vector<T>& v)
{
const std::vector<T> src = v;
int i = -1;
int n = static_cast<int>(src.size());
v.clear();
while (n)
{
i += n;
v.push_back(src[i]);
n = (n > 0 ? 1 - n : -1 - n);
}
}
template<typename T>
//! \note Stores pointers to original elements
class Traverser
{
public:
template<typename Iter>
Traverser (const Iter beg, const Iter end, const bool shuffled)
{
for (Iter it = beg; it != end; ++it)
m_elements.push_back(&(*it));
if (shuffled)
semiShuffle(m_elements);
m_next = m_elements.begin();
}
T* next (void)
{
if (m_next != m_elements.end())
return *m_next++;
else
return DE_NULL;
}
private:
typename std::vector<T*> m_elements;
typename std::vector<T*>::const_iterator m_next;
};
glu::DataType getPromoteType(glu::DataType type)
{
switch (type)
{
case glu::TYPE_UINT8: return glu::TYPE_UINT;
case glu::TYPE_UINT8_VEC2: return glu::TYPE_UINT_VEC2;
case glu::TYPE_UINT8_VEC3: return glu::TYPE_UINT_VEC3;
case glu::TYPE_UINT8_VEC4: return glu::TYPE_UINT_VEC4;
case glu::TYPE_INT8: return glu::TYPE_INT;
case glu::TYPE_INT8_VEC2: return glu::TYPE_INT_VEC2;
case glu::TYPE_INT8_VEC3: return glu::TYPE_INT_VEC3;
case glu::TYPE_INT8_VEC4: return glu::TYPE_INT_VEC4;
case glu::TYPE_UINT16: return glu::TYPE_UINT;
case glu::TYPE_UINT16_VEC2: return glu::TYPE_UINT_VEC2;
case glu::TYPE_UINT16_VEC3: return glu::TYPE_UINT_VEC3;
case glu::TYPE_UINT16_VEC4: return glu::TYPE_UINT_VEC4;
case glu::TYPE_INT16: return glu::TYPE_INT;
case glu::TYPE_INT16_VEC2: return glu::TYPE_INT_VEC2;
case glu::TYPE_INT16_VEC3: return glu::TYPE_INT_VEC3;
case glu::TYPE_INT16_VEC4: return glu::TYPE_INT_VEC4;
case glu::TYPE_FLOAT16: return glu::TYPE_FLOAT;
case glu::TYPE_FLOAT16_VEC2: return glu::TYPE_FLOAT_VEC2;
case glu::TYPE_FLOAT16_VEC3: return glu::TYPE_FLOAT_VEC3;
case glu::TYPE_FLOAT16_VEC4: return glu::TYPE_FLOAT_VEC4;
default: return type;
}
}
void generateDeclaration (std::ostringstream& src, int blockNdx, const UniformBlock& block, const UniformLayout& layout, bool shuffleUniformMembers)
{
src << "layout(set = 0, binding = " << blockNdx;
if ((block.getFlags() & LAYOUT_MASK) != 0)
src << ", " << LayoutFlagsFmt(block.getFlags() & LAYOUT_MASK);
src << ") ";
src << "uniform " << block.getBlockName();
src << "\n{\n";
Traverser<const Uniform> uniforms(block.begin(), block.end(), shuffleUniformMembers);
while (const Uniform* pUniform = uniforms.next())
{
src << Indent(1);
generateDeclaration(src, *pUniform, 1 /* indent level */, getBlockMemberOffset(blockNdx, block, *pUniform, layout));
}
src << "}";
if (block.hasInstanceName())
{
src << " " << block.getInstanceName();
if (block.isArray())
{
if (block.getFlags() & LAYOUT_DESCRIPTOR_INDEXING)
src << "[]";
else
src << "[" << block.getArraySize() << "]";
}
}
else
DE_ASSERT(!block.isArray());
src << ";\n";
}
void generateValueSrc (std::ostringstream& src, const UniformLayoutEntry& entry, const void* basePtr, int elementNdx)
{
glu::DataType scalarType = glu::getDataTypeScalarType(entry.type);
int scalarSize = glu::getDataTypeScalarSize(entry.type);
bool isArray = entry.size > 1;
const deUint8* elemPtr = (const deUint8*)basePtr + entry.offset + (isArray ? elementNdx * entry.arrayStride : 0);
const size_t compSize = getDataTypeByteSize(scalarType);
if (scalarSize > 1)
src << glu::getDataTypeName(getPromoteType(entry.type)) << "(";
if (glu::isDataTypeMatrix(entry.type))
{
int numRows = glu::getDataTypeMatrixNumRows(entry.type);
int numCols = glu::getDataTypeMatrixNumColumns(entry.type);
DE_ASSERT(scalarType == glu::TYPE_FLOAT);
// Constructed in column-wise order.
for (int colNdx = 0; colNdx < numCols; colNdx++)
{
for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
{
const deUint8* compPtr = elemPtr + (entry.isRowMajor ? (rowNdx * entry.matrixStride + colNdx * compSize)
: (colNdx * entry.matrixStride + rowNdx * compSize));
if (colNdx > 0 || rowNdx > 0)
src << ", ";
src << de::floatToString(*((const float*)compPtr), 1);
}
}
}
else
{
for (int scalarNdx = 0; scalarNdx < scalarSize; scalarNdx++)
{
const deUint8* compPtr = elemPtr + scalarNdx * compSize;
if (scalarNdx > 0)
src << ", ";
switch (scalarType)
{
case glu::TYPE_FLOAT16: src << de::floatToString(deFloat16To32(*((const deFloat16*)compPtr)), 1); break;
case glu::TYPE_FLOAT: src << de::floatToString(*((const float*)compPtr), 1); break;
case glu::TYPE_INT8: src << (deUint32)*((const deInt8*)compPtr); break;
case glu::TYPE_INT16: src << *((const deInt16*)compPtr); break;
case glu::TYPE_INT: src << *((const int*)compPtr); break;
case glu::TYPE_UINT8: src << (deUint32)*((const deUint8*)compPtr) << "u"; break;
case glu::TYPE_UINT16: src << *((const deUint16*)compPtr) << "u"; 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 << ")";
}
bool isMatrix (glu::DataType elementType)
{
return (elementType >= glu::TYPE_FLOAT_MAT2) && (elementType <= glu::TYPE_FLOAT_MAT4);
}
void writeMatrixTypeSrc (int columnCount,
int rowCount,
std::string compare,
std::string compareType,
std::ostringstream& src,
const std::string& srcName,
const void* basePtr,
const UniformLayoutEntry& entry,
bool vector)
{
if (vector) // generateTestSrcMatrixPerVec
{
for (int colNdex = 0; colNdex < columnCount; colNdex++)
{
src << "\tresult *= " << compare + compareType << "(" << srcName << "[" << colNdex << "], ";
if (glu::isDataTypeMatrix(entry.type))
{
int scalarSize = glu::getDataTypeScalarSize(entry.type);
const deUint8* elemPtr = (const deUint8*)basePtr + entry.offset;
const int compSize = sizeof(deUint32);
if (scalarSize > 1)
src << compareType << "(";
for (int rowNdex = 0; rowNdex < rowCount; rowNdex++)
{
const deUint8* compPtr = elemPtr + (entry.isRowMajor ? (rowNdex * entry.matrixStride + colNdex * compSize)
: (colNdex * entry.matrixStride + rowNdex * compSize));
src << de::floatToString(*((const float*)compPtr), 1);
if (rowNdex < rowCount-1)
src << ", ";
}
src << "));\n";
}
else
{
generateValueSrc(src, entry, basePtr, 0);
src << "[" << colNdex << "]);\n";
}
}
}
else // generateTestSrcMatrixPerElement
{
for (int colNdex = 0; colNdex < columnCount; colNdex++)
{
for (int rowNdex = 0; rowNdex < rowCount; rowNdex++)
{
src << "\tresult *= " << compare + compareType << "(" << srcName << "[" << colNdex << "][" << rowNdex << "], ";
if (glu::isDataTypeMatrix(entry.type))
{
const deUint8* elemPtr = (const deUint8*)basePtr + entry.offset;
const int compSize = sizeof(deUint32);
const deUint8* compPtr = elemPtr + (entry.isRowMajor ? (rowNdex * entry.matrixStride + colNdex * compSize)
: (colNdex * entry.matrixStride + rowNdex * compSize));
src << de::floatToString(*((const float*)compPtr), 1) << ");\n";
}
else
{
generateValueSrc(src, entry, basePtr, 0);
src << "[" << colNdex << "][" << rowNdex << "]);\n";
}
}
}
}
}
void generateTestSrcMatrixPerVec (glu::DataType elementType,
std::ostringstream& src,
const std::string& srcName,
const void* basePtr,
const UniformLayoutEntry& entry,
bool vector)
{
std::string compare = "compare_";
switch (elementType)
{
case glu::TYPE_FLOAT_MAT2:
writeMatrixTypeSrc(2, 2, compare, "vec2", src, srcName, basePtr, entry, vector);
break;
case glu::TYPE_FLOAT_MAT2X3:
writeMatrixTypeSrc(2, 3, compare, "vec3", src, srcName, basePtr, entry, vector);
break;
case glu::TYPE_FLOAT_MAT2X4:
writeMatrixTypeSrc(2, 4, compare, "vec4", src, srcName, basePtr, entry, vector);
break;
case glu::TYPE_FLOAT_MAT3X4:
writeMatrixTypeSrc(3, 4, compare, "vec4", src, srcName, basePtr, entry, vector);
break;
case glu::TYPE_FLOAT_MAT4:
writeMatrixTypeSrc(4, 4, compare, "vec4", src, srcName, basePtr, entry, vector);
break;
case glu::TYPE_FLOAT_MAT4X2:
writeMatrixTypeSrc(4, 2, compare, "vec2", src, srcName, basePtr, entry, vector);
break;
case glu::TYPE_FLOAT_MAT4X3:
writeMatrixTypeSrc(4, 3, compare, "vec3", src, srcName, basePtr, entry, vector);
break;
default:
break;
}
}
void generateTestSrcMatrixPerElement (glu::DataType elementType,
std::ostringstream& src,
const std::string& srcName,
const void* basePtr,
const UniformLayoutEntry& entry,
bool vector)
{
std::string compare = "compare_";
std::string compareType = "float";
switch (elementType)
{
case glu::TYPE_FLOAT_MAT2:
writeMatrixTypeSrc(2, 2, compare, compareType, src, srcName, basePtr, entry, vector);
break;
case glu::TYPE_FLOAT_MAT2X3:
writeMatrixTypeSrc(2, 3, compare, compareType, src, srcName, basePtr, entry, vector);
break;
case glu::TYPE_FLOAT_MAT2X4:
writeMatrixTypeSrc(2, 4, compare, compareType, src, srcName, basePtr, entry, vector);
break;
case glu::TYPE_FLOAT_MAT3X4:
writeMatrixTypeSrc(3, 4, compare, compareType, src, srcName, basePtr, entry, vector);
break;
case glu::TYPE_FLOAT_MAT4:
writeMatrixTypeSrc(4, 4, compare, compareType, src, srcName, basePtr, entry, vector);
break;
case glu::TYPE_FLOAT_MAT4X2:
writeMatrixTypeSrc(4, 2, compare, compareType, src, srcName, basePtr, entry, vector);
break;
case glu::TYPE_FLOAT_MAT4X3:
writeMatrixTypeSrc(4, 3, compare, compareType, src, srcName, basePtr, entry, vector);
break;
default:
break;
}
}
void generateSingleCompare (std::ostringstream& src,
glu::DataType elementType,
const std::string& srcName,
const void* basePtr,
const UniformLayoutEntry& entry,
MatrixLoadFlags matrixLoadFlag)
{
if (matrixLoadFlag == LOAD_FULL_MATRIX)
{
const char* typeName = glu::getDataTypeName(elementType);
const char* castName = "";
glu::DataType promoteType = getPromoteType(elementType);
if (elementType != promoteType)
{
castName = glu::getDataTypeName(promoteType);
}
src << "\tresult *= compare_" << typeName << "(" << castName << "(" << srcName << "), ";
generateValueSrc(src, entry, basePtr, 0);
src << ");\n";
}
else
{
if (isMatrix(elementType))
{
generateTestSrcMatrixPerVec (elementType, src, srcName, basePtr, entry, true);
generateTestSrcMatrixPerElement (elementType, src, srcName, basePtr, entry, false);
}
}
}
void generateCompareSrc (std::ostringstream& src,
const char* resultVar,
const VarType& type,
const std::string& srcName,
const std::string& apiName,
const UniformLayout& layout,
int blockNdx,
const void* basePtr,
deUint32 unusedMask,
MatrixLoadFlags matrixLoadFlag)
{
if (type.isBasicType() || (type.isArrayType() && type.getElementType().isBasicType()))
{
// Basic type or array of basic types.
bool isArray = type.isArrayType();
glu::DataType elementType = isArray ? type.getElementType().getBasicType() : type.getBasicType();
const char* typeName = glu::getDataTypeName(elementType);
std::string fullApiName = std::string(apiName) + (isArray ? "[0]" : ""); // Arrays are always postfixed with [0]
int uniformNdx = layout.getUniformLayoutIndex(blockNdx, fullApiName);
const UniformLayoutEntry& entry = layout.uniforms[uniformNdx];
const char* castName = "";
glu::DataType promoteType = getPromoteType(elementType);
if (elementType != promoteType)
{
castName = glu::getDataTypeName(promoteType);
}
if (isArray)
{
for (int elemNdx = 0; elemNdx < type.getArraySize(); elemNdx++)
{
src << "\tresult *= compare_" << typeName << "(" << castName << "(" << srcName << "[" << elemNdx << "]), ";
generateValueSrc(src, entry, basePtr, elemNdx);
src << ");\n";
}
}
else
{
generateSingleCompare(src, elementType, srcName, basePtr, entry, matrixLoadFlag);
}
}
else if (type.isArrayType())
{
const VarType& elementType = type.getElementType();
for (int elementNdx = 0; elementNdx < type.getArraySize(); elementNdx++)
{
std::string op = std::string("[") + de::toString(elementNdx) + "]";
std::string elementSrcName = std::string(srcName) + op;
std::string elementApiName = std::string(apiName) + op;
generateCompareSrc(src, resultVar, elementType, elementSrcName, elementApiName, layout, blockNdx, basePtr, unusedMask, LOAD_FULL_MATRIX);
}
}
else
{
DE_ASSERT(type.isStructType());
for (StructType::ConstIterator memberIter = type.getStruct().begin(); memberIter != type.getStruct().end(); memberIter++)
{
if (memberIter->getFlags() & unusedMask)
continue; // Skip member.
std::string op = std::string(".") + memberIter->getName();
std::string memberSrcName = std::string(srcName) + op;
std::string memberApiName = std::string(apiName) + op;
generateCompareSrc(src, resultVar, memberIter->getType(), memberSrcName, memberApiName, layout, blockNdx, basePtr, unusedMask, LOAD_FULL_MATRIX);
}
}
}
void generateCompareSrc (std::ostringstream& src,
const char* resultVar,
const ShaderInterface& interface,
const UniformLayout& layout,
const std::map<int,
void*>& blockPointers,
bool isVertex,
MatrixLoadFlags matrixLoadFlag)
{
deUint32 unusedMask = isVertex ? UNUSED_VERTEX : UNUSED_FRAGMENT;
for (int blockNdx = 0; blockNdx < interface.getNumUniformBlocks(); blockNdx++)
{
const UniformBlock& block = interface.getUniformBlock(blockNdx);
if ((block.getFlags() & (isVertex ? DECLARE_VERTEX : DECLARE_FRAGMENT)) == 0)
continue; // Skip.
bool hasInstanceName = block.hasInstanceName();
bool isArray = block.isArray();
int numInstances = isArray ? block.getArraySize() : 1;
std::string apiPrefix = hasInstanceName ? block.getBlockName() + "." : std::string("");
DE_ASSERT(!isArray || hasInstanceName);
for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
{
std::string instancePostfix = "";
if (isArray)
{
std::string indexStr = de::toString(instanceNdx);
if (interface.usesBlockLayout(LAYOUT_DESCRIPTOR_INDEXING))
indexStr = std::string("nonuniformEXT(") + indexStr + ")";
instancePostfix = std::string("[") + indexStr + "]";
}
std::string blockInstanceName = block.getBlockName() + instancePostfix;
std::string srcPrefix = hasInstanceName ? block.getInstanceName() + instancePostfix + "." : std::string("");
int blockLayoutNdx = layout.getBlockLayoutIndex(blockNdx, instanceNdx);
void* basePtr = blockPointers.find(blockLayoutNdx)->second;
for (UniformBlock::ConstIterator uniformIter = block.begin(); uniformIter != block.end(); uniformIter++)
{
const Uniform& uniform = *uniformIter;
if (uniform.getFlags() & unusedMask)
continue; // Don't read from that uniform.
std::string srcName = srcPrefix + uniform.getName();
std::string apiName = apiPrefix + uniform.getName();
generateCompareSrc(src, resultVar, uniform.getType(), srcName, apiName, layout, blockNdx, basePtr, unusedMask, matrixLoadFlag);
}
}
}
}
std::string generateVertexShader (const ShaderInterface& interface, const UniformLayout& layout, const std::map<int, void*>& blockPointers, MatrixLoadFlags matrixLoadFlag, bool shuffleUniformMembers)
{
std::ostringstream src;
src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n";
src << "#extension GL_EXT_shader_16bit_storage : enable\n";
src << "#extension GL_EXT_shader_8bit_storage : enable\n";
src << "#extension GL_EXT_scalar_block_layout : enable\n";
src << "#extension GL_EXT_nonuniform_qualifier : enable\n";
src << "layout(location = 0) in highp vec4 a_position;\n";
src << "layout(location = 0) out mediump float v_vtxResult;\n";
src << "\n";
std::vector<const StructType*> namedStructs;
interface.getNamedStructs(namedStructs);
for (std::vector<const StructType*>::const_iterator structIter = namedStructs.begin(); structIter != namedStructs.end(); structIter++)
generateDeclaration(src, **structIter, 0);
for (int blockNdx = 0; blockNdx < interface.getNumUniformBlocks(); blockNdx++)
{
const UniformBlock& block = interface.getUniformBlock(blockNdx);
if (block.getFlags() & DECLARE_VERTEX)
generateDeclaration(src, blockNdx, block, layout, shuffleUniformMembers);
}
// Comparison utilities.
src << "\n";
generateCompareFuncs(src, interface);
src << "\n"
"void main (void)\n"
"{\n"
" gl_Position = a_position;\n"
" mediump float result = 1.0;\n";
// Value compare.
generateCompareSrc(src, "result", interface, layout, blockPointers, true, matrixLoadFlag);
src << " v_vtxResult = result;\n"
"}\n";
return src.str();
}
std::string generateFragmentShader (const ShaderInterface& interface, const UniformLayout& layout, const std::map<int, void*>& blockPointers, MatrixLoadFlags matrixLoadFlag, bool shuffleUniformMembers)
{
std::ostringstream src;
src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n";
src << "#extension GL_EXT_shader_16bit_storage : enable\n";
src << "#extension GL_EXT_shader_8bit_storage : enable\n";
src << "#extension GL_EXT_scalar_block_layout : enable\n";
src << "#extension GL_EXT_nonuniform_qualifier : enable\n";
src << "layout(location = 0) in mediump float v_vtxResult;\n";
src << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
src << "\n";
std::vector<const StructType*> namedStructs;
interface.getNamedStructs(namedStructs);
for (std::vector<const StructType*>::const_iterator structIter = namedStructs.begin(); structIter != namedStructs.end(); structIter++)
generateDeclaration(src, **structIter, 0);
for (int blockNdx = 0; blockNdx < interface.getNumUniformBlocks(); blockNdx++)
{
const UniformBlock& block = interface.getUniformBlock(blockNdx);
if (block.getFlags() & DECLARE_FRAGMENT)
generateDeclaration(src, blockNdx, block, layout, shuffleUniformMembers);
}
// Comparison utilities.
src << "\n";
generateCompareFuncs(src, interface);
src << "\n"
"void main (void)\n"
"{\n"
" mediump float result = 1.0;\n";
// Value compare.
generateCompareSrc(src, "result", interface, layout, blockPointers, false, matrixLoadFlag);
src << " dEQP_FragColor = vec4(1.0, v_vtxResult, result, 1.0);\n"
"}\n";
return src.str();
}
Move<VkBuffer> createBuffer (Context& context, VkDeviceSize bufferSize, vk::VkBufferUsageFlags usageFlags)
{
const VkDevice vkDevice = context.getDevice();
const DeviceInterface& vk = context.getDeviceInterface();
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
const VkBufferCreateInfo bufferInfo =
{
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkBufferCreateFlags flags;
bufferSize, // VkDeviceSize size;
usageFlags, // VkBufferUsageFlags usage;
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
1u, // deUint32 queueFamilyIndexCount;
&queueFamilyIndex // const deUint32* pQueueFamilyIndices;
};
return vk::createBuffer(vk, vkDevice, &bufferInfo);
}
Move<vk::VkImage> createImage2D (Context& context, deUint32 width, deUint32 height, vk::VkFormat format, vk::VkImageTiling tiling, vk::VkImageUsageFlags usageFlags)
{
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
const vk::VkImageCreateInfo params =
{
vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType
DE_NULL, // const void* pNext
0u, // VkImageCreateFlags flags
vk::VK_IMAGE_TYPE_2D, // VkImageType imageType
format, // VkFormat format
{ width, height, 1u }, // VkExtent3D extent
1u, // deUint32 mipLevels
1u, // deUint32 arrayLayers
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
tiling, // VkImageTiling tiling
usageFlags, // VkImageUsageFlags usage
vk::VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode
1u, // deUint32 queueFamilyIndexCount
&queueFamilyIndex, // const deUint32* pQueueFamilyIndices
vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout
};
return vk::createImage(context.getDeviceInterface(), context.getDevice(), &params);
}
de::MovePtr<vk::Allocation> allocateAndBindMemory (Context& context, vk::VkBuffer buffer, vk::MemoryRequirement memReqs)
{
const vk::DeviceInterface& vkd = context.getDeviceInterface();
const vk::VkMemoryRequirements bufReqs = vk::getBufferMemoryRequirements(vkd, context.getDevice(), buffer);
de::MovePtr<vk::Allocation> memory = context.getDefaultAllocator().allocate(bufReqs, memReqs);
vkd.bindBufferMemory(context.getDevice(), buffer, memory->getMemory(), memory->getOffset());
return memory;
}
de::MovePtr<vk::Allocation> allocateAndBindMemory (Context& context, vk::VkImage image, vk::MemoryRequirement memReqs)
{
const vk::DeviceInterface& vkd = context.getDeviceInterface();
const vk::VkMemoryRequirements imgReqs = vk::getImageMemoryRequirements(vkd, context.getDevice(), image);
de::MovePtr<vk::Allocation> memory = context.getDefaultAllocator().allocate(imgReqs, memReqs);
vkd.bindImageMemory(context.getDevice(), image, memory->getMemory(), memory->getOffset());
return memory;
}
Move<vk::VkImageView> createAttachmentView (Context& context, vk::VkImage image, vk::VkFormat format)
{
const vk::VkImageViewCreateInfo params =
{
vk::VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, // sType
DE_NULL, // pNext
0u, // flags
image, // image
vk::VK_IMAGE_VIEW_TYPE_2D, // viewType
format, // format
vk::makeComponentMappingRGBA(), // components
{ vk::VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u,1u }, // subresourceRange
};
return vk::createImageView(context.getDeviceInterface(), context.getDevice(), &params);
}
Move<vk::VkPipelineLayout> createPipelineLayout (Context& context, vk::VkDescriptorSetLayout descriptorSetLayout)
{
const vk::VkPipelineLayoutCreateInfo params =
{
vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, // sType
DE_NULL, // pNext
0u, // flags
1u, // setLayoutCount
&descriptorSetLayout, // pSetLayouts
0u, // pushConstantRangeCount
DE_NULL, // pPushConstantRanges
};
return vk::createPipelineLayout(context.getDeviceInterface(), context.getDevice(), &params);
}
Move<vk::VkCommandPool> createCmdPool (Context& context)
{
const deUint32 queueFamilyIndex = context.getUniversalQueueFamilyIndex();
return vk::createCommandPool(context.getDeviceInterface(), context.getDevice(), vk::VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
}
Move<vk::VkCommandBuffer> createCmdBuffer (Context& context, vk::VkCommandPool cmdPool)
{
return vk::allocateCommandBuffer(context.getDeviceInterface(), context.getDevice(), cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
}
// UniformBlockCaseInstance
class UniformBlockCaseInstance : public vkt::TestInstance
{
public:
UniformBlockCaseInstance (Context& context,
UniformBlockCase::BufferMode bufferMode,
const UniformLayout& layout,
const std::map<int, void*>& blockPointers);
virtual ~UniformBlockCaseInstance (void);
virtual tcu::TestStatus iterate (void);
private:
enum
{
RENDER_WIDTH = 100,
RENDER_HEIGHT = 100,
};
vk::Move<VkRenderPass> createRenderPass (vk::VkFormat format) const;
vk::Move<VkFramebuffer> createFramebuffer (vk::VkRenderPass renderPass, vk::VkImageView colorImageView) const;
vk::Move<VkDescriptorSetLayout> createDescriptorSetLayout (void) const;
vk::Move<VkDescriptorPool> createDescriptorPool (void) const;
vk::Move<VkPipeline> createPipeline (vk::VkShaderModule vtxShaderModule, vk::VkShaderModule fragShaderModule, vk::VkPipelineLayout pipelineLayout, vk::VkRenderPass renderPass) const;
vk::VkDescriptorBufferInfo addUniformData (deUint32 size, const void* dataPtr);
UniformBlockCase::BufferMode m_bufferMode;
const UniformLayout& m_layout;
const std::map<int, void*>& m_blockPointers;
typedef de::SharedPtr<vk::Unique<vk::VkBuffer> > VkBufferSp;
typedef de::SharedPtr<vk::Allocation> AllocationSp;
std::vector<VkBufferSp> m_uniformBuffers;
std::vector<AllocationSp> m_uniformAllocs;
};
UniformBlockCaseInstance::UniformBlockCaseInstance (Context& ctx,
UniformBlockCase::BufferMode bufferMode,
const UniformLayout& layout,
const std::map<int, void*>& blockPointers)
: vkt::TestInstance (ctx)
, m_bufferMode (bufferMode)
, m_layout (layout)
, m_blockPointers (blockPointers)
{
}
UniformBlockCaseInstance::~UniformBlockCaseInstance (void)
{
}
tcu::TestStatus UniformBlockCaseInstance::iterate (void)
{
const vk::DeviceInterface& vk = m_context.getDeviceInterface();
const vk::VkDevice device = m_context.getDevice();
const vk::VkQueue queue = m_context.getUniversalQueue();
const deUint32 queueFamilyIndex = m_context.getUniversalQueueFamilyIndex();
const float positions[] =
{
-1.0f, -1.0f, 0.0f, 1.0f,
-1.0f, +1.0f, 0.0f, 1.0f,
+1.0f, -1.0f, 0.0f, 1.0f,
+1.0f, +1.0f, 0.0f, 1.0f
};
const deUint32 indices[] = { 0, 1, 2, 2, 1, 3 };
vk::Unique<VkBuffer> positionsBuffer (createBuffer(m_context, sizeof(positions), vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT));
de::UniquePtr<Allocation> positionsAlloc (allocateAndBindMemory(m_context, *positionsBuffer, MemoryRequirement::HostVisible));
vk::Unique<VkBuffer> indicesBuffer (createBuffer(m_context, sizeof(indices), vk::VK_BUFFER_USAGE_INDEX_BUFFER_BIT|vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT));
de::UniquePtr<Allocation> indicesAlloc (allocateAndBindMemory(m_context, *indicesBuffer, MemoryRequirement::HostVisible));
int minUniformBufferOffsetAlignment = getminUniformBufferOffsetAlignment(m_context);
// Upload attrbiutes data
{
deMemcpy(positionsAlloc->getHostPtr(), positions, sizeof(positions));
flushAlloc(vk, device, *positionsAlloc);
deMemcpy(indicesAlloc->getHostPtr(), indices, sizeof(indices));
flushAlloc(vk, device, *indicesAlloc);
}
vk::Unique<VkImage> colorImage (createImage2D(m_context,
RENDER_WIDTH,
RENDER_HEIGHT,
vk::VK_FORMAT_R8G8B8A8_UNORM,
vk::VK_IMAGE_TILING_OPTIMAL,
vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
de::UniquePtr<Allocation> colorImageAlloc (allocateAndBindMemory(m_context, *colorImage, MemoryRequirement::Any));
vk::Unique<VkImageView> colorImageView (createAttachmentView(m_context, *colorImage, vk::VK_FORMAT_R8G8B8A8_UNORM));
vk::Unique<VkDescriptorSetLayout> descriptorSetLayout (createDescriptorSetLayout());
vk::Unique<VkDescriptorPool> descriptorPool (createDescriptorPool());
const VkDescriptorSetAllocateInfo descriptorSetAllocateInfo =
{
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
*descriptorPool, // VkDescriptorPool descriptorPool;
1u, // deUint32 setLayoutCount;
&descriptorSetLayout.get() // const VkDescriptorSetLayout* pSetLayouts;
};
vk::Unique<VkDescriptorSet> descriptorSet(vk::allocateDescriptorSet(vk, device, &descriptorSetAllocateInfo));
int numBlocks = (int)m_layout.blocks.size();
std::vector<vk::VkDescriptorBufferInfo> descriptors(numBlocks);
// Upload uniform data
{
vk::DescriptorSetUpdateBuilder descriptorSetUpdateBuilder;
if (m_bufferMode == UniformBlockCase::BUFFERMODE_PER_BLOCK)
{
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
const BlockLayoutEntry& block = m_layout.blocks[blockNdx];
const void* srcPtr = m_blockPointers.find(blockNdx)->second;
descriptors[blockNdx] = addUniformData(block.size, srcPtr);
descriptorSetUpdateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::bindingArrayElement(block.bindingNdx, block.instanceNdx),
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &descriptors[blockNdx]);
}
}
else
{
int currentOffset = 0;
std::map<int, int> offsets;
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
if (minUniformBufferOffsetAlignment > 0)
currentOffset = deAlign32(currentOffset, minUniformBufferOffsetAlignment);
offsets[blockNdx] = currentOffset;
currentOffset += m_layout.blocks[blockNdx].size;
}
deUint32 totalSize = currentOffset;
// Make a copy of the data that satisfies the device's min uniform buffer alignment
std::vector<deUint8> data;
data.resize(totalSize);
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
deMemcpy(&data[offsets[blockNdx]], m_blockPointers.find(blockNdx)->second, m_layout.blocks[blockNdx].size);
}
vk::VkBuffer buffer = addUniformData(totalSize, &data[0]).buffer;
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
const BlockLayoutEntry& block = m_layout.blocks[blockNdx];
deUint32 size = block.size;
const VkDescriptorBufferInfo descriptor =
{
buffer, // VkBuffer buffer;
(deUint32)offsets[blockNdx], // VkDeviceSize offset;
size, // VkDeviceSize range;
};
descriptors[blockNdx] = descriptor;
descriptorSetUpdateBuilder.writeSingle(*descriptorSet,
vk::DescriptorSetUpdateBuilder::Location::bindingArrayElement(block.bindingNdx, block.instanceNdx),
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
&descriptors[blockNdx]);
}
}
descriptorSetUpdateBuilder.update(vk, device);
}
vk::Unique<VkRenderPass> renderPass (createRenderPass(vk::VK_FORMAT_R8G8B8A8_UNORM));
vk::Unique<VkFramebuffer> framebuffer (createFramebuffer(*renderPass, *colorImageView));
vk::Unique<VkPipelineLayout> pipelineLayout (createPipelineLayout(m_context, *descriptorSetLayout));
vk::Unique<VkShaderModule> vtxShaderModule (vk::createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0));
vk::Unique<VkShaderModule> fragShaderModule (vk::createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0));
vk::Unique<VkPipeline> pipeline (createPipeline(*vtxShaderModule, *fragShaderModule, *pipelineLayout, *renderPass));
vk::Unique<VkCommandPool> cmdPool (createCmdPool(m_context));
vk::Unique<VkCommandBuffer> cmdBuffer (createCmdBuffer(m_context, *cmdPool));
vk::Unique<VkBuffer> readImageBuffer (createBuffer(m_context, (vk::VkDeviceSize)(RENDER_WIDTH * RENDER_HEIGHT * 4), vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT));
de::UniquePtr<Allocation> readImageAlloc (allocateAndBindMemory(m_context, *readImageBuffer, vk::MemoryRequirement::HostVisible));
// Record command buffer
const vk::VkCommandBufferBeginInfo beginInfo =
{
vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkCommandBufferUsageFlags flags;
(const vk::VkCommandBufferInheritanceInfo*)DE_NULL,
};
VK_CHECK(vk.beginCommandBuffer(*cmdBuffer, &beginInfo));
// Add barrier for initializing image state
{
const vk::VkImageMemoryBarrier initializeBarrier =
{
vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType;
DE_NULL, // const void* pNext
0, // VVkAccessFlags srcAccessMask;
vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, // VkAccessFlags dstAccessMask;
vk::VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout;
vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout newLayout;
queueFamilyIndex, // deUint32 srcQueueFamilyIndex;
queueFamilyIndex, // deUint32 dstQueueFamilyIndex;
*colorImage, // VkImage image;
{
vk::VK_IMAGE_ASPECT_COLOR_BIT, // VkImageAspectFlags aspectMask;
0u, // deUint32 baseMipLevel;
1u, // deUint32 mipLevels;
0u, // deUint32 baseArraySlice;
1u, // deUint32 arraySize;
} // VkImageSubresourceRange subresourceRange
};
vk.cmdPipelineBarrier(*cmdBuffer, vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, (vk::VkDependencyFlags)0,
0, (const vk::VkMemoryBarrier*)DE_NULL,
0, (const vk::VkBufferMemoryBarrier*)DE_NULL,
1, &initializeBarrier);
}
beginRenderPass(vk, *cmdBuffer, *renderPass, *framebuffer, makeRect2D(0, 0, RENDER_WIDTH, RENDER_HEIGHT), tcu::Vec4(0.125f, 0.25f, 0.75f, 1.0f));
vk.cmdBindPipeline(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
vk.cmdBindDescriptorSets(*cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &*descriptorSet, 0u, DE_NULL);
const vk::VkDeviceSize offsets[] = { 0u };
vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &*positionsBuffer, offsets);
vk.cmdBindIndexBuffer(*cmdBuffer, *indicesBuffer, (vk::VkDeviceSize)0, vk::VK_INDEX_TYPE_UINT32);
vk.cmdDrawIndexed(*cmdBuffer, DE_LENGTH_OF_ARRAY(indices), 1u, 0u, 0u, 0u);
endRenderPass(vk, *cmdBuffer);
copyImageToBuffer(vk, *cmdBuffer, *colorImage, *readImageBuffer, tcu::IVec2(RENDER_WIDTH, RENDER_HEIGHT));
endCommandBuffer(vk, *cmdBuffer);
// Submit the command buffer
submitCommandsAndWait(vk, device, queue, cmdBuffer.get());
// Read back the results
tcu::Surface surface(RENDER_WIDTH, RENDER_HEIGHT);
{
const tcu::TextureFormat textureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8);
const tcu::ConstPixelBufferAccess imgAccess(textureFormat, RENDER_WIDTH, RENDER_HEIGHT, 1, readImageAlloc->getHostPtr());
invalidateAlloc(vk, device, *readImageAlloc);
tcu::copy(surface.getAccess(), imgAccess);
}
// Check if the result image is all white
tcu::RGBA white(tcu::RGBA::white());
int numFailedPixels = 0;
for (int y = 0; y < surface.getHeight(); y++)
{
for (int x = 0; x < surface.getWidth(); x++)
{
if (surface.getPixel(x, y) != white)
numFailedPixels += 1;
}
}
if (numFailedPixels > 0)
{
tcu::TestLog& log = m_context.getTestContext().getLog();
log << tcu::TestLog::Image("Image", "Rendered image", surface);
log << tcu::TestLog::Message << "Image comparison failed, got " << numFailedPixels << " non-white pixels" << tcu::TestLog::EndMessage;
for (size_t blockNdx = 0; blockNdx < m_layout.blocks.size(); blockNdx++)
{
const BlockLayoutEntry& block = m_layout.blocks[blockNdx];
log << tcu::TestLog::Message << "Block index: " << blockNdx << " infos: " << block << tcu::TestLog::EndMessage;
}
for (size_t uniformNdx = 0; uniformNdx < m_layout.uniforms.size(); uniformNdx++)
{
log << tcu::TestLog::Message << "Uniform index: " << uniformNdx << " infos: " << m_layout.uniforms[uniformNdx] << tcu::TestLog::EndMessage;
}
return tcu::TestStatus::fail("Detected non-white pixels");
}
else
return tcu::TestStatus::pass("Full white image ok");
}
vk::VkDescriptorBufferInfo UniformBlockCaseInstance::addUniformData (deUint32 size, const void* dataPtr)
{
const VkDevice vkDevice = m_context.getDevice();
const DeviceInterface& vk = m_context.getDeviceInterface();
Move<VkBuffer> buffer = createBuffer(m_context, size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT);
de::MovePtr<Allocation> alloc = allocateAndBindMemory(m_context, *buffer, vk::MemoryRequirement::HostVisible);
deMemcpy(alloc->getHostPtr(), dataPtr, size);
flushAlloc(vk, vkDevice, *alloc);
const VkDescriptorBufferInfo descriptor =
{
*buffer, // VkBuffer buffer;
0u, // VkDeviceSize offset;
size, // VkDeviceSize range;
};
m_uniformBuffers.push_back(VkBufferSp(new vk::Unique<vk::VkBuffer>(buffer)));
m_uniformAllocs.push_back(AllocationSp(alloc.release()));
return descriptor;
}
vk::Move<VkRenderPass> UniformBlockCaseInstance::createRenderPass (vk::VkFormat format) const
{
const VkDevice vkDevice = m_context.getDevice();
const DeviceInterface& vk = m_context.getDeviceInterface();
return vk::makeRenderPass(vk, vkDevice, format);
}
vk::Move<VkFramebuffer> UniformBlockCaseInstance::createFramebuffer (vk::VkRenderPass renderPass, vk::VkImageView colorImageView) const
{
const VkDevice vkDevice = m_context.getDevice();
const DeviceInterface& vk = m_context.getDeviceInterface();
const VkFramebufferCreateInfo framebufferParams =
{
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, // VkStructureType sType;
DE_NULL, // const void* pNext;
0u, // VkFramebufferCreateFlags flags;
renderPass, // VkRenderPass renderPass;
1u, // deUint32 attachmentCount;
&colorImageView, // const VkImageView* pAttachments;
RENDER_WIDTH, // deUint32 width;
RENDER_HEIGHT, // deUint32 height;
1u // deUint32 layers;
};
return vk::createFramebuffer(vk, vkDevice, &framebufferParams);
}
vk::Move<VkDescriptorSetLayout> UniformBlockCaseInstance::createDescriptorSetLayout (void) const
{
int numBlocks = (int)m_layout.blocks.size();
int lastBindingNdx = -1;
std::vector<int> lengths;
for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
{
const BlockLayoutEntry& block = m_layout.blocks[blockNdx];
if (block.bindingNdx == lastBindingNdx)
{
lengths.back()++;
}
else
{
lengths.push_back(1);
lastBindingNdx = block.bindingNdx;
}
}
vk::DescriptorSetLayoutBuilder layoutBuilder;
for (size_t i = 0; i < lengths.size(); i++)
{
if (lengths[i] > 0)
{
layoutBuilder.addArrayBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, lengths[i], vk::VK_SHADER_STAGE_ALL);
}
else
{
layoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, vk::VK_SHADER_STAGE_ALL);
}
}
return layoutBuilder.build(m_context.getDeviceInterface(), m_context.getDevice());
}
vk::Move<VkDescriptorPool> UniformBlockCaseInstance::createDescriptorPool (void) const
{
vk::DescriptorPoolBuilder poolBuilder;
return poolBuilder
.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, (int)m_layout.blocks.size())
.build(m_context.getDeviceInterface(), m_context.getDevice(), VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
}
vk::Move<VkPipeline> UniformBlockCaseInstance::createPipeline (vk::VkShaderModule vtxShaderModule, vk::VkShaderModule fragShaderModule, vk::VkPipelineLayout pipelineLayout, vk::VkRenderPass renderPass) const
{
const VkDevice vkDevice = m_context.getDevice();
const DeviceInterface& vk = m_context.getDeviceInterface();
const std::vector<VkViewport> viewports (1, makeViewport(tcu::UVec2(RENDER_WIDTH, RENDER_HEIGHT)));
const std::vector<VkRect2D> scissors (1, makeRect2D(tcu::UVec2(RENDER_WIDTH, RENDER_HEIGHT)));
return vk::makeGraphicsPipeline(vk, // const DeviceInterface& vk
vkDevice, // const VkDevice device
pipelineLayout, // const VkPipelineLayout pipelineLayout
vtxShaderModule, // const VkShaderModule vertexShaderModule
DE_NULL, // const VkShaderModule tessellationControlShaderModule
DE_NULL, // const VkShaderModule tessellationEvalShaderModule
DE_NULL, // const VkShaderModule geometryShaderModule
fragShaderModule, // const VkShaderModule fragmentShaderModule
renderPass, // const VkRenderPass renderPass
viewports, // const std::vector<VkViewport>& viewports
scissors); // const std::vector<VkRect2D>& scissors
}
} // anonymous (utilities)
// UniformBlockCase.
UniformBlockCase::UniformBlockCase (tcu::TestContext& testCtx, const std::string& name, const std::string& description, BufferMode bufferMode, MatrixLoadFlags matrixLoadFlag, bool shuffleUniformMembers)
: TestCase (testCtx, name, description)
, m_bufferMode (bufferMode)
, m_matrixLoadFlag (matrixLoadFlag)
, m_shuffleUniformMembers (shuffleUniformMembers)
{
}
UniformBlockCase::~UniformBlockCase (void)
{
}
void UniformBlockCase::initPrograms (vk::SourceCollections& programCollection) const
{
DE_ASSERT(!m_vertShaderSource.empty());
DE_ASSERT(!m_fragShaderSource.empty());
vk::ShaderBuildOptions::Flags flags = vk::ShaderBuildOptions::Flags(0);
// TODO(dneto): If these tests ever use LAYOUT_RELAXED, then add support
// here as well.
if (usesBlockLayout(LAYOUT_SCALAR))
flags = vk::ShaderBuildOptions::FLAG_ALLOW_SCALAR_OFFSETS;
else if (usesBlockLayout(LAYOUT_STD430))
flags = vk::ShaderBuildOptions::FLAG_ALLOW_STD430_UBOS;
programCollection.glslSources.add("vert") << glu::VertexSource(m_vertShaderSource)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, vk::getBaselineSpirvVersion(programCollection.usedVulkanVersion), flags);
programCollection.glslSources.add("frag") << glu::FragmentSource(m_fragShaderSource)
<< vk::ShaderBuildOptions(programCollection.usedVulkanVersion, vk::getBaselineSpirvVersion(programCollection.usedVulkanVersion), flags);
}
TestInstance* UniformBlockCase::createInstance (Context& context) const
{
if (!context.get16BitStorageFeatures().uniformAndStorageBuffer16BitAccess && usesBlockLayout(LAYOUT_16BIT_STORAGE))
TCU_THROW(NotSupportedError, "uniformAndStorageBuffer16BitAccess not supported");
if (!context.get8BitStorageFeatures().uniformAndStorageBuffer8BitAccess && usesBlockLayout(LAYOUT_8BIT_STORAGE))
TCU_THROW(NotSupportedError, "uniformAndStorageBuffer8BitAccess not supported");
if (!context.getScalarBlockLayoutFeatures().scalarBlockLayout && !context.getUniformBufferStandardLayoutFeatures().uniformBufferStandardLayout && usesBlockLayout(LAYOUT_STD430))
TCU_THROW(NotSupportedError, "std430 not supported");
if (!context.getScalarBlockLayoutFeatures().scalarBlockLayout && usesBlockLayout(LAYOUT_SCALAR))
TCU_THROW(NotSupportedError, "scalarBlockLayout not supported");
if (!context.getDescriptorIndexingFeatures().shaderUniformBufferArrayNonUniformIndexing && usesBlockLayout(LAYOUT_DESCRIPTOR_INDEXING))
TCU_THROW(NotSupportedError, "Descriptor indexing over uniform buffer not supported");
return new UniformBlockCaseInstance(context, m_bufferMode, m_uniformLayout, m_blockPointers);
}
void UniformBlockCase::delayedInit (void)
{
const int vec4Alignment = (int)sizeof(deUint32)*4;
// Compute reference layout.
computeReferenceLayout(m_uniformLayout, m_interface);
// Assign storage for reference values.
{
int totalSize = 0;
for (std::vector<BlockLayoutEntry>::const_iterator blockIter = m_uniformLayout.blocks.begin(); blockIter != m_uniformLayout.blocks.end(); blockIter++)
{
// Include enough space for alignment of individual blocks
totalSize += deRoundUp32(blockIter->size, vec4Alignment);
}
m_data.resize(totalSize);
// Pointers for each block.
int curOffset = 0;
for (int blockNdx = 0; blockNdx < (int)m_uniformLayout.blocks.size(); blockNdx++)
{
m_blockPointers[blockNdx] = &m_data[0] + curOffset;
// Ensure each new block starts fully aligned to avoid unaligned host accesses
curOffset += deRoundUp32(m_uniformLayout.blocks[blockNdx].size, vec4Alignment);
}
}
// Generate values.
generateValues(m_uniformLayout, m_blockPointers, 1 /* seed */);
// Generate shaders.
m_vertShaderSource = generateVertexShader(m_interface, m_uniformLayout, m_blockPointers, m_matrixLoadFlag, m_shuffleUniformMembers);
m_fragShaderSource = generateFragmentShader(m_interface, m_uniformLayout, m_blockPointers, m_matrixLoadFlag, m_shuffleUniformMembers);
}
} // ubo
} // vkt