/*------------------------------------------------------------------------ * 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 "deInt32.h" #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 #include 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::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& structs) const { for (std::vector::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; } 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& 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& 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& 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& basicTypes, const UniformBlock& uniformBlock) { for (UniformBlock::ConstIterator iter = uniformBlock.begin(); iter != uniformBlock.end(); ++iter) collectUniqueBasicTypes(basicTypes, iter->getType()); } void collectUniqueBasicTypes (std::set& 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 types; std::set compareFuncs; // Collect unique basic types collectUniqueBasicTypes(types, interface); // Set of compare functions required for (std::set::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 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::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 void semiShuffle (std::vector& v) { const std::vector src = v; int i = -1; int n = static_cast(src.size()); v.clear(); while (n) { i += n; v.push_back(src[i]); n = (n > 0 ? 1 - n : -1 - n); } } template //! \note Stores pointers to original elements class Traverser { public: template 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 m_elements; typename std::vector::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 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& 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& 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 namedStructs; interface.getNamedStructs(namedStructs); for (std::vector::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& 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 namedStructs; interface.getNamedStructs(namedStructs); for (std::vector::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 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 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(), ¶ms); } de::MovePtr 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 memory = context.getDefaultAllocator().allocate(bufReqs, memReqs); vkd.bindBufferMemory(context.getDevice(), buffer, memory->getMemory(), memory->getOffset()); return memory; } de::MovePtr 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 memory = context.getDefaultAllocator().allocate(imgReqs, memReqs); vkd.bindImageMemory(context.getDevice(), image, memory->getMemory(), memory->getOffset()); return memory; } Move 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(), ¶ms); } Move 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(), ¶ms); } Move 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 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& blockPointers); virtual ~UniformBlockCaseInstance (void); virtual tcu::TestStatus iterate (void); private: enum { RENDER_WIDTH = 100, RENDER_HEIGHT = 100, }; vk::Move createRenderPass (vk::VkFormat format) const; vk::Move createFramebuffer (vk::VkRenderPass renderPass, vk::VkImageView colorImageView) const; vk::Move createDescriptorSetLayout (void) const; vk::Move createDescriptorPool (void) const; vk::Move 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& m_blockPointers; typedef de::SharedPtr > VkBufferSp; typedef de::SharedPtr AllocationSp; std::vector m_uniformBuffers; std::vector m_uniformAllocs; }; UniformBlockCaseInstance::UniformBlockCaseInstance (Context& ctx, UniformBlockCase::BufferMode bufferMode, const UniformLayout& layout, const std::map& 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 positionsBuffer (createBuffer(m_context, sizeof(positions), vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)); de::UniquePtr positionsAlloc (allocateAndBindMemory(m_context, *positionsBuffer, MemoryRequirement::HostVisible)); vk::Unique indicesBuffer (createBuffer(m_context, sizeof(indices), vk::VK_BUFFER_USAGE_INDEX_BUFFER_BIT|vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)); de::UniquePtr 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 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 colorImageAlloc (allocateAndBindMemory(m_context, *colorImage, MemoryRequirement::Any)); vk::Unique colorImageView (createAttachmentView(m_context, *colorImage, vk::VK_FORMAT_R8G8B8A8_UNORM)); vk::Unique descriptorSetLayout (createDescriptorSetLayout()); vk::Unique 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 descriptorSet(vk::allocateDescriptorSet(vk, device, &descriptorSetAllocateInfo)); int numBlocks = (int)m_layout.blocks.size(); std::vector 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 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 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 renderPass (createRenderPass(vk::VK_FORMAT_R8G8B8A8_UNORM)); vk::Unique framebuffer (createFramebuffer(*renderPass, *colorImageView)); vk::Unique pipelineLayout (createPipelineLayout(m_context, *descriptorSetLayout)); vk::Unique vtxShaderModule (vk::createShaderModule(vk, device, m_context.getBinaryCollection().get("vert"), 0)); vk::Unique fragShaderModule (vk::createShaderModule(vk, device, m_context.getBinaryCollection().get("frag"), 0)); vk::Unique pipeline (createPipeline(*vtxShaderModule, *fragShaderModule, *pipelineLayout, *renderPass)); vk::Unique cmdPool (createCmdPool(m_context)); vk::Unique cmdBuffer (createCmdBuffer(m_context, *cmdPool)); vk::Unique readImageBuffer (createBuffer(m_context, (vk::VkDeviceSize)(RENDER_WIDTH * RENDER_HEIGHT * 4), vk::VK_BUFFER_USAGE_TRANSFER_DST_BIT)); de::UniquePtr 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 buffer = createBuffer(m_context, size, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT); de::MovePtr 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(buffer))); m_uniformAllocs.push_back(AllocationSp(alloc.release())); return descriptor; } vk::Move 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 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 UniformBlockCaseInstance::createDescriptorSetLayout (void) const { int numBlocks = (int)m_layout.blocks.size(); int lastBindingNdx = -1; std::vector 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 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 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 viewports (1, makeViewport(tcu::UVec2(RENDER_WIDTH, RENDER_HEIGHT))); const std::vector 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& viewports scissors); // const std::vector& 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 (usesBlockLayout(LAYOUT_DESCRIPTOR_INDEXING) && ( !context.getDescriptorIndexingFeatures().shaderUniformBufferArrayNonUniformIndexing || !context.getDescriptorIndexingFeatures().runtimeDescriptorArray ) ) 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::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