SSBO: add test cases and result validation
authorPeter Gal <pgal.u-szeged@partner.samsung.com>
Thu, 17 Dec 2015 17:04:33 +0000 (18:04 +0100)
committerPeter Gal <pgal.u-szeged@partner.samsung.com>
Fri, 18 Dec 2015 08:43:13 +0000 (09:43 +0100)
external/vulkancts/modules/vulkan/ssbo/vktSSBOLayoutCase.cpp
external/vulkancts/modules/vulkan/ssbo/vktSSBOLayoutCase.hpp
external/vulkancts/modules/vulkan/ssbo/vktSSBOLayoutTests.cpp

index b25d498..aa7f7ab 100644 (file)
  *//*--------------------------------------------------------------------*/
 
 #include "vktSSBOLayoutCase.hpp"
-#include "vkPrograms.hpp"
 #include "gluShaderProgram.hpp"
-#include "gluPixelTransfer.hpp"
 #include "gluContextInfo.hpp"
-#include "gluRenderContext.hpp"
-#include "gluProgramInterfaceQuery.hpp"
-#include "gluObjectWrapper.hpp"
 #include "gluShaderUtil.hpp"
 #include "gluVarType.hpp"
 #include "gluVarTypeUtil.hpp"
-#include "glwFunctions.hpp"
-#include "glwEnums.hpp"
 #include "tcuTestLog.hpp"
-#include "tcuSurface.hpp"
-#include "tcuRenderTarget.hpp"
 #include "deRandom.hpp"
 #include "deStringUtil.hpp"
 #include "deMemory.h"
 #include <algorithm>
 #include <map>
 
+#include "vkBuilderUtil.hpp"
 #include "vkMemUtil.hpp"
+#include "vkPrograms.hpp"
 #include "vkQueryUtil.hpp"
-#include "vkTypeUtil.hpp"
 #include "vkRef.hpp"
 #include "vkRefUtil.hpp"
-#include "vkBuilderUtil.hpp"
-
-
-using tcu::TestLog;
-using std::string;
-using std::vector;
-using std::map;
+#include "vkTypeUtil.hpp"
 
 namespace vkt
 {
 namespace ssbo
 {
 
+using tcu::TestLog;
+using std::string;
+using std::vector;
+using std::map;
 using glu::VarType;
 using glu::StructType;
 using glu::StructMember;
@@ -95,10 +85,10 @@ std::ostream& operator<< (std::ostream& str, const LayoutFlagsFmt& fmt)
                const char*     token;
        } bitDesc[] =
        {
-               { LAYOUT_SHARED,                "shared"                },
-               { LAYOUT_PACKED,                "packed"                },
+               //{ LAYOUT_SHARED,              "shared"                },
+               //{ LAYOUT_PACKED,              "packed"                },
                { LAYOUT_STD140,                "std140"                },
-               { LAYOUT_STD430,                "std430"                },
+               //{ LAYOUT_STD430,              "std430"                },
                { LAYOUT_ROW_MAJOR,             "row_major"             },
                { LAYOUT_COLUMN_MAJOR,  "column_major"  }
        };
@@ -262,17 +252,6 @@ BufferBlock& ShaderInterface::allocBlock (const char* name)
 
 namespace // Utilities
 {
-/*
-int findBlockIndex (const BufferLayout& layout, const string& name)
-{
-       for (int ndx = 0; ndx < (int)layout.blocks.size(); ndx++)
-       {
-               if (layout.blocks[ndx].name == name)
-                       return ndx;
-       }
-       return -1;
-}
-*/
 // Layout computation.
 
 int getDataTypeByteSize (glu::DataType type)
@@ -356,47 +335,9 @@ int computeStd140BaseAlignment (const VarType& type, deUint32 layoutFlags)
        }
 }
 
-int computeStd430BaseAlignment (const VarType& type, deUint32 layoutFlags)
-{
-       // Otherwise identical to std140 except that alignment of structures and arrays
-       // are not rounded up to alignment of vec4.
-
-       if (type.isBasicType())
-       {
-               glu::DataType basicType = type.getBasicType();
-
-               if (glu::isDataTypeMatrix(basicType))
-               {
-                       const bool      isRowMajor      = !!(layoutFlags & LAYOUT_ROW_MAJOR);
-                       const int       vecSize         = isRowMajor ? glu::getDataTypeMatrixNumColumns(basicType)
-                                                                                                : glu::getDataTypeMatrixNumRows(basicType);
-                       const int       vecAlign        = getDataTypeByteAlignment(glu::getDataTypeFloatVec(vecSize));
-
-                       return vecAlign;
-               }
-               else
-                       return getDataTypeByteAlignment(basicType);
-       }
-       else if (type.isArrayType())
-       {
-               return computeStd430BaseAlignment(type.getElementType(), layoutFlags);
-       }
-       else
-       {
-               DE_ASSERT(type.isStructType());
-
-               int maxBaseAlignment = 0;
-
-               for (StructType::ConstIterator memberIter = type.getStructPtr()->begin(); memberIter != type.getStructPtr()->end(); memberIter++)
-                       maxBaseAlignment = de::max(maxBaseAlignment, computeStd430BaseAlignment(memberIter->getType(), layoutFlags));
-
-               return maxBaseAlignment;
-       }
-}
-
 inline deUint32 mergeLayoutFlags (deUint32 prevFlags, deUint32 newFlags)
 {
-       const deUint32  packingMask             = LAYOUT_PACKED|LAYOUT_SHARED|LAYOUT_STD140|LAYOUT_STD430;
+       const deUint32  packingMask             = LAYOUT_STD140;
        const deUint32  matrixMask              = LAYOUT_ROW_MAJOR|LAYOUT_COLUMN_MAJOR;
 
        deUint32 mergedFlags = 0;
@@ -416,11 +357,7 @@ int computeReferenceLayout (
        const VarType&          type,
        deUint32                        layoutFlags)
 {
-       // Reference layout uses std430 rules by default. std140 rules are
-       // choosen only for blocks that have std140 layout.
-       const bool      isStd140                        = (layoutFlags & LAYOUT_STD140) != 0;
-       const int       baseAlignment           = isStd140 ? computeStd140BaseAlignment(type, layoutFlags)
-                                                                                          : computeStd430BaseAlignment(type, layoutFlags);
+       const int       baseAlignment           = computeStd140BaseAlignment(type, layoutFlags);
        int                     curOffset                       = deAlign32(baseOffset, baseAlignment);
        const int       topLevelArraySize       = 1; // Default values
        const int       topLevelArrayStride     = 0;
@@ -544,10 +481,8 @@ int computeReferenceLayout (BufferLayout& layout, int curBlockNdx, const std::st
                // Top-level arrays need special care.
                const int               topLevelArraySize       = varType.getArraySize() == VarType::UNSIZED_ARRAY ? 0 : varType.getArraySize();
                const string    prefix                          = blockPrefix + bufVar.getName() + "[0]";
-               const bool              isStd140                        = (blockLayoutFlags & LAYOUT_STD140) != 0;
                const int               vec4Align                       = (int)sizeof(deUint32)*4;
-               const int               baseAlignment           = isStd140 ? computeStd140BaseAlignment(varType, combinedFlags)
-                                                                                                          : computeStd430BaseAlignment(varType, combinedFlags);
+               const int               baseAlignment           = computeStd140BaseAlignment(varType, combinedFlags);
                int                             curOffset                       = deAlign32(baseOffset, baseAlignment);
                const VarType&  elemType                        = varType.getElementType();
 
@@ -556,7 +491,7 @@ int computeReferenceLayout (BufferLayout& layout, int curBlockNdx, const std::st
                        // Array of scalars or vectors.
                        const glu::DataType             elemBasicType   = elemType.getBasicType();
                        const int                               elemBaseAlign   = getDataTypeByteAlignment(elemBasicType);
-                       const int                               stride                  = isStd140 ? deAlign32(elemBaseAlign, vec4Align) : elemBaseAlign;
+                       const int                               stride                  = deAlign32(elemBaseAlign, vec4Align);
                        BufferVarLayoutEntry    entry;
 
                        entry.name                                      = prefix;
@@ -584,7 +519,7 @@ int computeReferenceLayout (BufferLayout& layout, int curBlockNdx, const std::st
                                                                                                                                 : glu::getDataTypeMatrixNumColumns(elemBasicType);
                        const glu::DataType             vecType                 = glu::getDataTypeFloatVec(vecSize);
                        const int                               vecBaseAlign    = getDataTypeByteAlignment(vecType);
-                       const int                               stride                  = isStd140 ? deAlign32(vecBaseAlign, vec4Align) : vecBaseAlign;
+                       const int                               stride                  = deAlign32(vecBaseAlign, vec4Align);
                        BufferVarLayoutEntry    entry;
 
                        entry.name                                      = prefix;
@@ -1239,7 +1174,7 @@ string generateComputeShader (const ShaderInterface& interface, const BufferLayo
        src << "\n";
 
        // Atomic counter for counting passed invocations.
-       src << "layout(std140, binding = 0) buffer AcBlock { uint ac_numPassed; };\n\n";
+       src << "layout(std140, binding = 0) buffer AcBlock { highp uint ac_numPassed; };\n\n";
 
        std::vector<const StructType*> namedStructs;
        interface.getNamedStructs(namedStructs);
@@ -1282,139 +1217,6 @@ string generateComputeShader (const ShaderInterface& interface, const BufferLayo
        return src.str();
 }
 
-/*
-void getGLBufferLayout (const glw::Functions& gl, BufferLayout& layout, deUint32 program)
-{
-       int             numActiveBufferVars     = 0;
-       int             numActiveBlocks         = 0;
-
-       gl.getProgramInterfaceiv(program, GL_BUFFER_VARIABLE,           GL_ACTIVE_RESOURCES,    &numActiveBufferVars);
-       gl.getProgramInterfaceiv(program, GL_SHADER_STORAGE_BLOCK,      GL_ACTIVE_RESOURCES,    &numActiveBlocks);
-
-       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to get number of buffer variables and buffer blocks");
-
-       // Block entries.
-       layout.blocks.resize(numActiveBlocks);
-       for (int blockNdx = 0; blockNdx < numActiveBlocks; blockNdx++)
-       {
-               BlockLayoutEntry&       entry                           = layout.blocks[blockNdx];
-               const deUint32          queryParams[]           = { GL_BUFFER_DATA_SIZE, GL_NUM_ACTIVE_VARIABLES, GL_NAME_LENGTH };
-               int                                     returnValues[]          = { 0, 0, 0 };
-
-               DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(queryParams) == DE_LENGTH_OF_ARRAY(returnValues));
-
-               {
-                       int returnLength = 0;
-                       gl.getProgramResourceiv(program, GL_SHADER_STORAGE_BLOCK, (deUint32)blockNdx, DE_LENGTH_OF_ARRAY(queryParams), &queryParams[0], DE_LENGTH_OF_ARRAY(returnValues), &returnLength, &returnValues[0]);
-                       GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceiv(GL_SHADER_STORAGE_BLOCK) failed");
-
-                       if (returnLength != DE_LENGTH_OF_ARRAY(returnValues))
-                               throw tcu::TestError("glGetProgramResourceiv(GL_SHADER_STORAGE_BLOCK) returned wrong number of values");
-               }
-
-               entry.size = returnValues[0];
-
-               // Query active variables
-               if (returnValues[1] > 0)
-               {
-                       const int               numBlockVars    = returnValues[1];
-                       const deUint32  queryArg                = GL_ACTIVE_VARIABLES;
-                       int                             retLength               = 0;
-
-                       entry.activeVarIndices.resize(numBlockVars);
-                       gl.getProgramResourceiv(program, GL_SHADER_STORAGE_BLOCK, (deUint32)blockNdx, 1, &queryArg, numBlockVars, &retLength, &entry.activeVarIndices[0]);
-                       GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceiv(GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_VARIABLES) failed");
-
-                       if (retLength != numBlockVars)
-                               throw tcu::TestError("glGetProgramResourceiv(GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_VARIABLES) returned wrong number of values");
-               }
-
-               // Query name
-               if (returnValues[2] > 0)
-               {
-                       const int               nameLen         = returnValues[2];
-                       int                             retLen          = 0;
-                       vector<char>    name            (nameLen);
-
-                       gl.getProgramResourceName(program, GL_SHADER_STORAGE_BLOCK, (deUint32)blockNdx, (glw::GLsizei)name.size(), &retLen, &name[0]);
-                       GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceName(GL_SHADER_STORAGE_BLOCK) failed");
-
-                       if (retLen+1 != nameLen)
-                               throw tcu::TestError("glGetProgramResourceName(GL_SHADER_STORAGE_BLOCK) returned invalid name. Number of characters written is inconsistent with NAME_LENGTH property.");
-                       if (name[nameLen-1] != 0)
-                               throw tcu::TestError("glGetProgramResourceName(GL_SHADER_STORAGE_BLOCK) returned invalid name. Expected null terminator at index " + de::toString(nameLen-1));
-
-                       entry.name = &name[0];
-               }
-               else
-                       throw tcu::TestError("glGetProgramResourceiv() returned invalid GL_NAME_LENGTH");
-       }
-
-       layout.bufferVars.resize(numActiveBufferVars);
-       for (int bufVarNdx = 0; bufVarNdx < numActiveBufferVars; bufVarNdx++)
-       {
-               BufferVarLayoutEntry&   entry                           = layout.bufferVars[bufVarNdx];
-               const deUint32                  queryParams[] =
-               {
-                       GL_BLOCK_INDEX,                                 // 0
-                       GL_TYPE,                                                // 1
-                       GL_OFFSET,                                              // 2
-                       GL_ARRAY_SIZE,                                  // 3
-                       GL_ARRAY_STRIDE,                                // 4
-                       GL_MATRIX_STRIDE,                               // 5
-                       GL_TOP_LEVEL_ARRAY_SIZE,                // 6
-                       GL_TOP_LEVEL_ARRAY_STRIDE,              // 7
-                       GL_IS_ROW_MAJOR,                                // 8
-                       GL_NAME_LENGTH                                  // 9
-               };
-               int returnValues[DE_LENGTH_OF_ARRAY(queryParams)];
-
-               DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(queryParams) == DE_LENGTH_OF_ARRAY(returnValues));
-
-               {
-                       int returnLength = 0;
-                       gl.getProgramResourceiv(program, GL_BUFFER_VARIABLE, (deUint32)bufVarNdx, DE_LENGTH_OF_ARRAY(queryParams), &queryParams[0], DE_LENGTH_OF_ARRAY(returnValues), &returnLength, &returnValues[0]);
-                       GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceiv(GL_BUFFER_VARIABLE) failed");
-
-                       if (returnLength != DE_LENGTH_OF_ARRAY(returnValues))
-                               throw tcu::TestError("glGetProgramResourceiv(GL_BUFFER_VARIABLE) returned wrong number of values");
-               }
-
-               // Map values
-               entry.blockNdx                          = returnValues[0];
-               entry.type                                      = glu::getDataTypeFromGLType(returnValues[1]);
-               entry.offset                            = returnValues[2];
-               entry.arraySize                         = returnValues[3];
-               entry.arrayStride                       = returnValues[4];
-               entry.matrixStride                      = returnValues[5];
-               entry.topLevelArraySize         = returnValues[6];
-               entry.topLevelArrayStride       = returnValues[7];
-               entry.isRowMajor                        = returnValues[8] != 0;
-
-               // Query name
-               DE_ASSERT(queryParams[9] == GL_NAME_LENGTH);
-               if (returnValues[9] > 0)
-               {
-                       const int               nameLen         = returnValues[9];
-                       int                             retLen          = 0;
-                       vector<char>    name            (nameLen);
-
-                       gl.getProgramResourceName(program, GL_BUFFER_VARIABLE, (deUint32)bufVarNdx, (glw::GLsizei)name.size(), &retLen, &name[0]);
-                       GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramResourceName(GL_BUFFER_VARIABLE) failed");
-
-                       if (retLen+1 != nameLen)
-                               throw tcu::TestError("glGetProgramResourceName(GL_BUFFER_VARIABLE) returned invalid name. Number of characters written is inconsistent with NAME_LENGTH property.");
-                       if (name[nameLen-1] != 0)
-                               throw tcu::TestError("glGetProgramResourceName(GL_BUFFER_VARIABLE) returned invalid name. Expected null terminator at index " + de::toString(nameLen-1));
-
-                       entry.name = &name[0];
-               }
-               else
-                       throw tcu::TestError("glGetProgramResourceiv() returned invalid GL_NAME_LENGTH");
-       }
-}
-*/
-
 void copyBufferVarData (const BufferVarLayoutEntry& dstEntry, const BlockDataPtr& dstBlockPtr, const BufferVarLayoutEntry& srcEntry, const BlockDataPtr& srcBlockPtr)
 {
        DE_ASSERT(dstEntry.arraySize <= srcEntry.arraySize);
@@ -1479,7 +1281,6 @@ void copyBufferVarData (const BufferVarLayoutEntry& dstEntry, const BlockDataPtr
        }
 }
 
-
 void copyData (const BufferLayout& dstLayout, const vector<BlockDataPtr>& dstBlockPointers, const BufferLayout& srcLayout, const vector<BlockDataPtr>& srcBlockPointers)
 {
        // \note Src layout is used as reference in case of activeVarIndices happens to be incorrect in dstLayout blocks.
@@ -1578,7 +1379,7 @@ void copyNonWrittenData (const ShaderInterface& interface, const BufferLayout& l
                }
        }
 }
-/*
+
 bool compareComponents (glu::DataType scalarType, const void* ref, const void* res, int numComps)
 {
        if (scalarType == glu::TYPE_FLOAT)
@@ -1761,7 +1562,7 @@ bool compareData (tcu::TestLog& log, const BufferLayout& refLayout, const vector
 
        return allOk;
 }
-*/
+
 string getBlockAPIName (const BufferBlock& block, int instanceNdx)
 {
        DE_ASSERT(block.isArray() || instanceNdx == 0);
@@ -1919,100 +1720,8 @@ vector<BlockDataPtr> blockLocationsToPtrs (const BufferLayout& layout, const vec
        return blockPtrs;
 }
 
-/*
-vector<void*> mapBuffers (const glw::Functions& gl, const vector<Buffer>& buffers, deUint32 access)
-{
-       vector<void*> mapPtrs(buffers.size(), DE_NULL);
-
-       try
-       {
-               for (int ndx = 0; ndx < (int)buffers.size(); ndx++)
-               {
-                       if (buffers[ndx].size > 0)
-                       {
-                               gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffers[ndx].buffer);
-                               mapPtrs[ndx] = gl.mapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, buffers[ndx].size, access);
-                               GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to map buffer");
-                               TCU_CHECK(mapPtrs[ndx]);
-                       }
-                       else
-                               mapPtrs[ndx] = DE_NULL;
-               }
-
-               return mapPtrs;
-       }
-       catch (...)
-       {
-               for (int ndx = 0; ndx < (int)buffers.size(); ndx++)
-               {
-                       if (mapPtrs[ndx])
-                       {
-                               gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffers[ndx].buffer);
-                               gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
-                       }
-               }
-
-               throw;
-       }
-}
-
-void unmapBuffers (const glw::Functions& gl, const vector<Buffer>& buffers)
-{
-       for (int ndx = 0; ndx < (int)buffers.size(); ndx++)
-       {
-               if (buffers[ndx].size > 0)
-               {
-                       gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffers[ndx].buffer);
-                       gl.unmapBuffer(GL_SHADER_STORAGE_BUFFER);
-               }
-       }
-
-       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to unmap buffer");
-}
-*/
 } // anonymous (utilities)
 
-/*
-class BufferManager
-{
-public:
-                                                               BufferManager   (const glu::RenderContext& renderCtx);
-                                                               ~BufferManager  (void);
-
-       deUint32                                        allocBuffer             (void);
-
-private:
-                                                               BufferManager   (const BufferManager& other);
-       BufferManager&                          operator=               (const BufferManager& other);
-
-       const glu::RenderContext&       m_renderCtx;
-       std::vector<deUint32>           m_buffers;
-};
-
-BufferManager::BufferManager (const glu::RenderContext& renderCtx)
-       : m_renderCtx(renderCtx)
-{
-}
-
-BufferManager::~BufferManager (void)
-{
-       if (!m_buffers.empty())
-               m_renderCtx.getFunctions().deleteBuffers((glw::GLsizei)m_buffers.size(), &m_buffers[0]);
-}
-
-deUint32 BufferManager::allocBuffer (void)
-{
-       deUint32 buf = 0;
-
-       m_buffers.reserve(m_buffers.size()+1);
-       m_renderCtx.getFunctions().genBuffers(1, &buf);
-       GLU_EXPECT_NO_ERROR(m_renderCtx.getFunctions().getError(), "Failed to allocate buffer");
-       m_buffers.push_back(buf);
-
-       return buf;
-}
-*/
-
 de::MovePtr<vk::Allocation> allocateAndBindMemory (Context& context, vk::VkBuffer buffer, vk::MemoryRequirement memReqs)
 {
        const vk::DeviceInterface&              vkd             = context.getDeviceInterface();
@@ -2024,7 +1733,6 @@ de::MovePtr<vk::Allocation> allocateAndBindMemory (Context& context, vk::VkBuffe
        return memory;
 }
 
-
 vk::Move<vk::VkBuffer> createBuffer (Context& context, vk::VkDeviceSize bufferSize, vk::VkBufferUsageFlags usageFlags)
 {
        const vk::VkDevice                      vkDevice                        = context.getDevice();
@@ -2046,7 +1754,6 @@ vk::Move<vk::VkBuffer> createBuffer (Context& context, vk::VkDeviceSize bufferSi
        return vk::createBuffer(vk, vkDevice, &bufferInfo);
 }
 
-
 // SSBOLayoutCaseInstance
 
 class SSBOLayoutCaseInstance : public TestInstance
@@ -2095,59 +1802,9 @@ SSBOLayoutCaseInstance::~SSBOLayoutCaseInstance (void)
 {
 }
 
-/*
-SSBOLayoutCaseInstance:: (void)
-{
-       const int                       numBlocks               = (int)glLayout.blocks.size();
-       const vector<int>       bufferSizes             = computeBufferSizes(m_interface, glLayout);
-
-       DE_ASSERT(bufferSizes.size() == glLayout.blocks.size());
-
-       blockLocations.resize(numBlocks);
-
-       if (m_bufferMode == BUFFERMODE_PER_BLOCK)
-       {
-               buffers.resize(numBlocks);
-
-               for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
-               {
-                       const int bufferSize = bufferSizes[blockNdx];
-
-                       buffers[blockNdx].size = bufferSize;
-                       blockLocations[blockNdx] = BlockLocation(blockNdx, 0, bufferSize);
-               }
-       }
-       else
-       {
-               DE_ASSERT(m_bufferMode == BUFFERMODE_SINGLE);
-
-               int             bindingAlignment        = 0;
-               int             totalSize                       = 0;
-
-               gl.getIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &bindingAlignment);
-
-               {
-                       int curOffset = 0;
-                       DE_ASSERT(bufferSizes.size() == glLayout.blocks.size());
-                       for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
-                       {
-                               const int bufferSize = bufferSizes[blockNdx];
-                               if (bindingAlignment > 0)
-                                       curOffset = deRoundUp32(curOffset, bindingAlignment);
-
-                               blockLocations[blockNdx] = BlockLocation(0, curOffset, bufferSize);
-                               curOffset += bufferSize;
-                       }
-                       totalSize = curOffset;
-               }
-
-               buffers.resize(1);
-               buffers[0].size = totalSize;
-       }
-}
-*/
 tcu::TestStatus SSBOLayoutCaseInstance::iterate (void)
 {
+       // todo: add compute stage availability check
        const vk::DeviceInterface&      vk                                      = m_context.getDeviceInterface();
        const vk::VkDevice                      device                          = m_context.getDevice();
        const vk::VkQueue                       queue                           = m_context.getUniversalQueue();
@@ -2194,6 +1851,8 @@ tcu::TestStatus SSBOLayoutCaseInstance::iterate (void)
        setUpdateBuilder
                .writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfo);
 
+       vector<BlockDataPtr>  mappedBlockPtrs;
+
        // Upload base buffers
        {
                const std::vector<int>                  bufferSizes             = computeBufferSizes(m_interface, m_refLayout);
@@ -2210,16 +1869,12 @@ tcu::TestStatus SSBOLayoutCaseInstance::iterate (void)
                                const deUint32 bufferSize = bufferSizes[blockNdx];
                                blockLocations[blockNdx] = BlockLocation(blockNdx, 0, bufferSize);
 
-                               vk::Move<vk::VkBuffer>                  buffer  = createBuffer(m_context, bufferSize, vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
-                               de::MovePtr<vk::Allocation>             alloc   = allocateAndBindMemory(m_context, *buffer, vk::MemoryRequirement::HostVisible);
+                               vk::Move<vk::VkBuffer>                          buffer          = createBuffer(m_context, bufferSize, vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
+                               de::MovePtr<vk::Allocation>                     alloc           = allocateAndBindMemory(m_context, *buffer, vk::MemoryRequirement::HostVisible);
+                               const vk::VkDescriptorBufferInfo        descriptor      = makeDescriptorBufferInfo(*buffer, 0ull, bufferSize + 4*sizeof(float));
 
                                mapPtrs[blockNdx] = alloc->getHostPtr();
 
-                               // \todo [2015-10-09 elecro] remove the '_hack_padding' variable if the driver support small uniforms,
-                               // that is for example one float big uniforms.
-                               const deUint32 hack_padding = bufferSize < 4 * sizeof(float) ? (deUint32)(3u * sizeof(float)) : 0u;
-                               const vk::VkDescriptorBufferInfo descriptor = makeDescriptorBufferInfo(*buffer, 0ull, bufferSize + hack_padding);
-
                                m_uniformBuffers.push_back(VkBufferSp(new vk::Unique<vk::VkBuffer>(buffer)));
                                m_uniformAllocs.push_back(AllocationSp(alloc.release()));
 
@@ -2231,9 +1886,10 @@ tcu::TestStatus SSBOLayoutCaseInstance::iterate (void)
                {
                        DE_ASSERT(m_bufferMode == SSBOLayoutCase::BUFFERMODE_SINGLE);
 
-                       //gl.getIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &bindingAlignment);
-                       const int bindingAlignment = 1;                 // minStorageBufferOffsetAlignment
-                       int curOffset = 0;
+                       vk::VkPhysicalDeviceProperties properties;
+                       m_context.getInstanceInterface().getPhysicalDeviceProperties(m_context.getPhysicalDevice(), &properties);
+                       const int       bindingAlignment        = (int)properties.limits.minStorageBufferOffsetAlignment;
+                       int                     curOffset                       = 0;
                        for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
                        {
                                const int bufferSize = bufferSizes[blockNdx];
@@ -2244,21 +1900,18 @@ tcu::TestStatus SSBOLayoutCaseInstance::iterate (void)
                                blockLocations[blockNdx] = BlockLocation(0, curOffset, bufferSize);
                                curOffset += bufferSize;
                        }
-                       const int totalBufferSize = curOffset;
 
-                       vk::Move<vk::VkBuffer>                  buffer  = createBuffer(m_context, totalBufferSize, vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
-                       de::MovePtr<vk::Allocation>             alloc   = allocateAndBindMemory(m_context, *buffer, vk::MemoryRequirement::HostVisible);
+                       const int                                               totalBufferSize = curOffset;
+                       vk::Move<vk::VkBuffer>                  buffer                  = createBuffer(m_context, totalBufferSize, vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
+                       de::MovePtr<vk::Allocation>             alloc                   = allocateAndBindMemory(m_context, *buffer, vk::MemoryRequirement::HostVisible);
 
                        mapPtrs.push_back(alloc->getHostPtr());
 
                        for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
                        {
-                               const deUint32 bufferSize = bufferSizes[blockNdx];
-                               const deUint32 offset = blockLocations[blockNdx].offset;
-                               // \todo [2015-10-09 elecro] remove the '_hack_padding' variable if the driver support small uniforms,
-                               // that is for example one float big uniforms.
-                               const deUint32 hack_padding = bufferSize < 4 * sizeof(float) ? (deUint32)(3u * sizeof(float)) : 0u;
-                               const vk::VkDescriptorBufferInfo descriptor = makeDescriptorBufferInfo(*buffer, offset, bufferSize + hack_padding);
+                               const deUint32                                          bufferSize      = bufferSizes[blockNdx];
+                               const deUint32                                          offset          = blockLocations[blockNdx].offset;
+                               const vk::VkDescriptorBufferInfo        descriptor      = makeDescriptorBufferInfo(*buffer, offset, bufferSize + 4*sizeof(float));
 
                                setUpdateBuilder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(blockNdx + 1),
                                                                                vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptor);
@@ -2269,8 +1922,7 @@ tcu::TestStatus SSBOLayoutCaseInstance::iterate (void)
                }
 
                {
-                       const vector<BlockDataPtr>  mappedBlockPtrs = blockLocationsToPtrs(m_refLayout, blockLocations, mapPtrs);
-
+                       mappedBlockPtrs = blockLocationsToPtrs(m_refLayout, blockLocations, mapPtrs);
                        copyData(m_refLayout, mappedBlockPtrs, m_refLayout, m_initialData.pointers);
 
                        DE_ASSERT(m_uniformAllocs.size() == bufferSizes.size());
@@ -2283,16 +1935,14 @@ tcu::TestStatus SSBOLayoutCaseInstance::iterate (void)
                }
        }
 
-       setUpdateBuilder
-               .update(vk, device);
+       setUpdateBuilder.update(vk, device);
 
-       const deUint32 descriptorSetCount = 1; // (descriptorSetLayout != DE_NULL ? 1u : 0);
        const vk::VkPipelineLayoutCreateInfo pipelineLayoutParams =
        {
                vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,      // VkStructureType                              sType;
                DE_NULL,                                                                                        // const void*                                  pNext;
                (vk::VkPipelineLayoutCreateFlags)0,
-               descriptorSetCount,                                                                     // deUint32                                             descriptorSetCount;
+               1u,                                                                                                     // deUint32                                             descriptorSetCount;
                &*descriptorSetLayout,                                                          // const VkDescriptorSetLayout* pSetLayouts;
                0u,                                                                                                     // deUint32                                             pushConstantRangeCount;
                DE_NULL,                                                                                        // const VkPushConstantRange*   pPushConstantRanges;
@@ -2300,7 +1950,6 @@ tcu::TestStatus SSBOLayoutCaseInstance::iterate (void)
        vk::Move<vk::VkPipelineLayout> pipelineLayout(createPipelineLayout(vk, device, &pipelineLayoutParams));
 
        vk::Move<vk::VkShaderModule> shaderModule (createShaderModule(vk, device, m_context.getBinaryCollection().get("compute"), 0));
-
        const vk::VkPipelineShaderStageCreateInfo pipelineShaderStageParams =
        {
                vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,// VkStructureType                              sType;
@@ -2344,12 +1993,12 @@ tcu::TestStatus SSBOLayoutCaseInstance::iterate (void)
 
        const vk::VkCommandBufferBeginInfo cmdBufBeginParams =
        {
-               vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,                //      VkStructureType                         sType;
-               DE_NULL,                                                                                //      const void*                                     pNext;
-               0u,                                                                                             //      VkCmdBufferOptimizeFlags        flags;
-               DE_NULL,                                                                                //      VkRenderPass                            renderPass;
-               0u,                                                                                             //      deUint32                                        subpass;
-               DE_NULL,                                                                                //      VkFramebuffer                           framebuffer;
+               vk::VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,        //      VkStructureType                         sType;
+               DE_NULL,                                                                                        //      const void*                                     pNext;
+               0u,                                                                                                     //      VkCmdBufferOptimizeFlags        flags;
+               DE_NULL,                                                                                        //      VkRenderPass                            renderPass;
+               0u,                                                                                                     //      deUint32                                        subpass;
+               DE_NULL,                                                                                        //      VkFramebuffer                           framebuffer;
                vk::VK_FALSE,
                (vk::VkQueryControlFlags)0,
                (vk::VkQueryPipelineStatisticFlags)0,
@@ -2383,15 +2032,36 @@ tcu::TestStatus SSBOLayoutCaseInstance::iterate (void)
                (const vk::VkSemaphore*)DE_NULL,
        };
 
-
        VK_CHECK(vk.queueSubmit(queue, 1u, &submitInfo, *fence));
        VK_CHECK(vk.waitForFences(device, 1u, &fence.get(), DE_TRUE, ~0ull));
 
-       // Validate result
+       // Read back ac_numPassed data
+       bool counterOk;
+       {
+               const int refCount = 1;
+               int resCount = 0;
 
-       return tcu::TestStatus::pass("OK");
-}
+               resCount = *(const int*)((const deUint8*)acBufferAlloc->getHostPtr());
 
+               counterOk = (refCount == resCount);
+               if (!counterOk)
+               {
+                       m_context.getTestContext().getLog() << TestLog::Message << "Error: ac_numPassed = " << resCount << ", expected " << refCount << TestLog::EndMessage;
+               }
+       }
+
+       // Validate result
+       const bool compareOk = compareData(m_context.getTestContext().getLog(), m_refLayout, m_writeData.pointers, m_refLayout, mappedBlockPtrs);
+
+       if (compareOk && counterOk)
+               return tcu::TestStatus::pass("Result comparison and counter values are OK");
+       else if (!compareOk && counterOk)
+               return tcu::TestStatus::fail("Result comparison failed");
+       else if (compareOk && !counterOk)
+               return tcu::TestStatus::fail("Counter value incorrect");
+       else
+               return tcu::TestStatus::fail("Result comparison and counter values are incorrect");
+}
 
 // SSBOLayoutCase.
 
@@ -2419,10 +2089,6 @@ TestInstance* SSBOLayoutCase::createInstance (Context& context) const
 
 void SSBOLayoutCase::init (void)
 {
-       //BufferLayout                          refLayout;              // std140 / std430 layout.
-       //RefDataStorage                                initialData;    // Initial data stored in buffer.
-       //RefDataStorage                                writeData;              // Data written by compute shader.
-
        computeReferenceLayout  (m_refLayout, m_interface);
        initRefDataStorage              (m_interface, m_refLayout, m_initialData);
        initRefDataStorage              (m_interface, m_refLayout, m_writeData);
@@ -2433,582 +2099,5 @@ void SSBOLayoutCase::init (void)
        m_computeShaderSrc = generateComputeShader(m_interface, m_refLayout, m_initialData.pointers, m_writeData.pointers);
 }
 
-#if 0
-
-SSBOLayoutCase::IterateResult SSBOLayoutCase::iterate (void)
-{
-       TestLog&                                        log                             = m_testCtx.getLog();
-       const glw::Functions&           gl                              = m_renderCtx.getFunctions();
-
-       BufferLayout                            refLayout;              // std140 / std430 layout.
-       BufferLayout                            glLayout;               // Layout reported by GL.
-       RefDataStorage                          initialData;    // Initial data stored in buffer.
-       RefDataStorage                          writeData;              // Data written by compute shader.
-
-       BufferManager                           bufferManager   (m_renderCtx);
-       vector<Buffer>                          buffers;                // Buffers allocated for storage
-       vector<BlockLocation>           blockLocations; // Block locations in storage (index, offset)
-
-       // Initialize result to pass.
-       m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
-
-       computeReferenceLayout  (refLayout, m_interface);
-       initRefDataStorage              (m_interface, refLayout, initialData);
-       initRefDataStorage              (m_interface, refLayout, writeData);
-       generateValues                  (refLayout, initialData.pointers, deStringHash(getName()) ^ 0xad2f7214);
-       generateValues                  (refLayout, writeData.pointers, deStringHash(getName()) ^ 0x25ca4e7);
-       copyNonWrittenData              (m_interface, refLayout, initialData.pointers, writeData.pointers);
-
-       const glu::ShaderProgram program(m_renderCtx, glu::ProgramSources() << glu::ComputeSource(generateComputeShader(m_glslVersion, m_interface, refLayout, initialData.pointers, writeData.pointers)));
-       log << program;
-
-       if (!program.isOk())
-       {
-               // Compile failed.
-               m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Compile failed");
-               return STOP;
-       }
-
-       // Query layout from GL.
-       getGLBufferLayout(gl, glLayout, program.getProgram());
-
-       // Print layout to log.
-       {
-               tcu::ScopedLogSection section(log, "ActiveBufferBlocks", "Active Buffer Blocks");
-               for (int blockNdx = 0; blockNdx < (int)glLayout.blocks.size(); blockNdx++)
-                       log << TestLog::Message << blockNdx << ": " << glLayout.blocks[blockNdx] << TestLog::EndMessage;
-       }
-
-       {
-               tcu::ScopedLogSection section(log, "ActiveBufferVars", "Active Buffer Variables");
-               for (int varNdx = 0; varNdx < (int)glLayout.bufferVars.size(); varNdx++)
-                       log << TestLog::Message << varNdx << ": " << glLayout.bufferVars[varNdx] << TestLog::EndMessage;
-       }
-
-       // Verify layouts.
-       {
-               if (!checkLayoutIndices(glLayout) || !checkLayoutBounds(glLayout) || !compareTypes(refLayout, glLayout))
-               {
-                       m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid layout");
-                       return STOP; // It is not safe to use the given layout.
-               }
-
-               if (!compareStdBlocks(refLayout, glLayout))
-                       m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid std140 or std430 layout");
-
-               if (!compareSharedBlocks(refLayout, glLayout))
-                       m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid shared layout");
-
-               if (!checkIndexQueries(program.getProgram(), glLayout))
-                       m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Inconsintent block index query results");
-       }
-
-       // Allocate GL buffers & compute placement.
-       {
-               const int                       numBlocks               = (int)glLayout.blocks.size();
-               const vector<int>       bufferSizes             = computeBufferSizes(m_interface, glLayout);
-
-               DE_ASSERT(bufferSizes.size() == glLayout.blocks.size());
-
-               blockLocations.resize(numBlocks);
-
-               if (m_bufferMode == BUFFERMODE_PER_BLOCK)
-               {
-                       buffers.resize(numBlocks);
-
-                       for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
-                       {
-                               const int bufferSize = bufferSizes[blockNdx];
-
-                               buffers[blockNdx].size = bufferSize;
-                               blockLocations[blockNdx] = BlockLocation(blockNdx, 0, bufferSize);
-                       }
-               }
-               else
-               {
-                       DE_ASSERT(m_bufferMode == BUFFERMODE_SINGLE);
-
-                       int             bindingAlignment        = 0;
-                       int             totalSize                       = 0;
-
-                       gl.getIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &bindingAlignment);
-
-                       {
-                               int curOffset = 0;
-                               DE_ASSERT(bufferSizes.size() == glLayout.blocks.size());
-                               for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
-                               {
-                                       const int bufferSize = bufferSizes[blockNdx];
-
-                                       if (bindingAlignment > 0)
-                                               curOffset = deRoundUp32(curOffset, bindingAlignment);
-
-                                       blockLocations[blockNdx] = BlockLocation(0, curOffset, bufferSize);
-                                       curOffset += bufferSize;
-                               }
-                               totalSize = curOffset;
-                       }
-
-                       buffers.resize(1);
-                       buffers[0].size = totalSize;
-               }
-
-               for (int bufNdx = 0; bufNdx < (int)buffers.size(); bufNdx++)
-               {
-                       const int               bufferSize      = buffers[bufNdx].size;
-                       const deUint32  buffer          = bufferManager.allocBuffer();
-
-                       gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
-                       gl.bufferData(GL_SHADER_STORAGE_BUFFER, bufferSize, DE_NULL, GL_STATIC_DRAW);
-                       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to allocate buffer");
-
-                       buffers[bufNdx].buffer = buffer;
-               }
-       }
-
-       {
-               const vector<void*>                     mapPtrs                 = mapBuffers(gl, buffers, GL_MAP_WRITE_BIT);
-               const vector<BlockDataPtr>      mappedBlockPtrs = blockLocationsToPtrs(glLayout, blockLocations, mapPtrs);
-
-               copyData(glLayout, mappedBlockPtrs, refLayout, initialData.pointers);
-
-               unmapBuffers(gl, buffers);
-       }
-
-       {
-               int bindingPoint = 0;
-
-               for (int blockDeclNdx = 0; blockDeclNdx < m_interface.getNumBlocks(); blockDeclNdx++)
-               {
-                       const BufferBlock&      block           = m_interface.getBlock(blockDeclNdx);
-                       const int                       numInst         = block.isArray() ? block.getArraySize() : 1;
-
-                       for (int instNdx = 0; instNdx < numInst; instNdx++)
-                       {
-                               const string    instName        = getBlockAPIName(block, instNdx);
-                               const int               layoutNdx       = findBlockIndex(glLayout, instName);
-
-                               if (layoutNdx >= 0)
-                               {
-                                       const BlockLocation& blockLoc = blockLocations[layoutNdx];
-
-                                       if (blockLoc.size > 0)
-                                               gl.bindBufferRange(GL_SHADER_STORAGE_BUFFER, bindingPoint, buffers[blockLoc.index].buffer, blockLoc.offset, blockLoc.size);
-                               }
-
-                               bindingPoint += 1;
-                       }
-               }
-       }
-
-       GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to bind buffers");
-
-       {
-               const bool execOk = execute(program.getProgram());
-
-               if (execOk)
-               {
-                       const vector<void*>                     mapPtrs                 = mapBuffers(gl, buffers, GL_MAP_READ_BIT);
-                       const vector<BlockDataPtr>      mappedBlockPtrs = blockLocationsToPtrs(glLayout, blockLocations, mapPtrs);
-
-                       const bool                                      compareOk               = compareData(m_testCtx.getLog(), refLayout, writeData.pointers, glLayout, mappedBlockPtrs);
-
-                       unmapBuffers(gl, buffers);
-
-                       if (!compareOk)
-                               m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result comparison failed");
-               }
-               else
-                       m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Shader execution failed");
-       }
-
-       return STOP;
-}
-
-bool SSBOLayoutCase::compareStdBlocks (const BufferLayout& refLayout, const BufferLayout& cmpLayout) const
-{
-       TestLog&        log                     = m_testCtx.getLog();
-       bool            isOk            = true;
-       int                     numBlocks       = m_interface.getNumBlocks();
-
-       for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
-       {
-               const BufferBlock&              block                   = m_interface.getBlock(blockNdx);
-               bool                                    isArray                 = block.isArray();
-               std::string                             instanceName    = string(block.getBlockName()) + (isArray ? "[0]" : "");
-               int                                             refBlockNdx             = refLayout.getBlockIndex(instanceName.c_str());
-               int                                             cmpBlockNdx             = cmpLayout.getBlockIndex(instanceName.c_str());
-
-               if ((block.getFlags() & (LAYOUT_STD140|LAYOUT_STD430)) == 0)
-                       continue; // Not std* layout.
-
-               DE_ASSERT(refBlockNdx >= 0);
-
-               if (cmpBlockNdx < 0)
-               {
-                       // Not found.
-                       log << TestLog::Message << "Error: Buffer block '" << instanceName << "' not found" << TestLog::EndMessage;
-                       isOk = false;
-                       continue;
-               }
-
-               const BlockLayoutEntry&         refBlockLayout  = refLayout.blocks[refBlockNdx];
-               const BlockLayoutEntry&         cmpBlockLayout  = cmpLayout.blocks[cmpBlockNdx];
-
-               // \todo [2012-01-24 pyry] Verify that activeVarIndices is correct.
-               // \todo [2012-01-24 pyry] Verify all instances.
-               if (refBlockLayout.activeVarIndices.size() != cmpBlockLayout.activeVarIndices.size())
-               {
-                       log << TestLog::Message << "Error: Number of active variables differ in block '" << instanceName
-                               << "' (expected " << refBlockLayout.activeVarIndices.size()
-                               << ", got " << cmpBlockLayout.activeVarIndices.size()
-                               << ")" << TestLog::EndMessage;
-                       isOk = false;
-               }
-
-               for (vector<int>::const_iterator ndxIter = refBlockLayout.activeVarIndices.begin(); ndxIter != refBlockLayout.activeVarIndices.end(); ndxIter++)
-               {
-                       const BufferVarLayoutEntry&     refEntry        = refLayout.bufferVars[*ndxIter];
-                       int                                                     cmpEntryNdx     = cmpLayout.getVariableIndex(refEntry.name.c_str());
-
-                       if (cmpEntryNdx < 0)
-                       {
-                               log << TestLog::Message << "Error: Buffer variable '" << refEntry.name << "' not found" << TestLog::EndMessage;
-                               isOk = false;
-                               continue;
-                       }
-
-                       const BufferVarLayoutEntry&     cmpEntry        = cmpLayout.bufferVars[cmpEntryNdx];
-
-                       if (refEntry.type                                       != cmpEntry.type                                ||
-                               refEntry.arraySize                              != cmpEntry.arraySize                   ||
-                               refEntry.offset                                 != cmpEntry.offset                              ||
-                               refEntry.arrayStride                    != cmpEntry.arrayStride                 ||
-                               refEntry.matrixStride                   != cmpEntry.matrixStride                ||
-                               refEntry.topLevelArraySize              != cmpEntry.topLevelArraySize   ||
-                               refEntry.topLevelArrayStride    != cmpEntry.topLevelArrayStride ||
-                               refEntry.isRowMajor                             != cmpEntry.isRowMajor)
-                       {
-                               log << TestLog::Message << "Error: Layout mismatch in '" << refEntry.name << "':\n"
-                                       << "  expected: " << refEntry << "\n"
-                                       << "  got: " << cmpEntry
-                                       << TestLog::EndMessage;
-                               isOk = false;
-                       }
-               }
-       }
-
-       return isOk;
-}
-
-bool SSBOLayoutCase::compareSharedBlocks (const BufferLayout& refLayout, const BufferLayout& cmpLayout) const
-{
-       TestLog&        log                     = m_testCtx.getLog();
-       bool            isOk            = true;
-       int                     numBlocks       = m_interface.getNumBlocks();
-
-       for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
-       {
-               const BufferBlock&              block                   = m_interface.getBlock(blockNdx);
-               bool                                    isArray                 = block.isArray();
-               std::string                             instanceName    = string(block.getBlockName()) + (isArray ? "[0]" : "");
-               int                                             refBlockNdx             = refLayout.getBlockIndex(instanceName.c_str());
-               int                                             cmpBlockNdx             = cmpLayout.getBlockIndex(instanceName.c_str());
-
-               if ((block.getFlags() & LAYOUT_SHARED) == 0)
-                       continue; // Not shared layout.
-
-               DE_ASSERT(refBlockNdx >= 0);
-
-               if (cmpBlockNdx < 0)
-               {
-                       // Not found, should it?
-                       log << TestLog::Message << "Error: Buffer block '" << instanceName << "' not found" << TestLog::EndMessage;
-                       isOk = false;
-                       continue;
-               }
-
-               const BlockLayoutEntry&         refBlockLayout  = refLayout.blocks[refBlockNdx];
-               const BlockLayoutEntry&         cmpBlockLayout  = cmpLayout.blocks[cmpBlockNdx];
-
-               if (refBlockLayout.activeVarIndices.size() != cmpBlockLayout.activeVarIndices.size())
-               {
-                       log << TestLog::Message << "Error: Number of active variables differ in block '" << instanceName
-                               << "' (expected " << refBlockLayout.activeVarIndices.size()
-                               << ", got " << cmpBlockLayout.activeVarIndices.size()
-                               << ")" << TestLog::EndMessage;
-                       isOk = false;
-               }
-
-               for (vector<int>::const_iterator ndxIter = refBlockLayout.activeVarIndices.begin(); ndxIter != refBlockLayout.activeVarIndices.end(); ndxIter++)
-               {
-                       const BufferVarLayoutEntry&     refEntry        = refLayout.bufferVars[*ndxIter];
-                       int                                                     cmpEntryNdx     = cmpLayout.getVariableIndex(refEntry.name.c_str());
-
-                       if (cmpEntryNdx < 0)
-                       {
-                               log << TestLog::Message << "Error: Buffer variable '" << refEntry.name << "' not found" << TestLog::EndMessage;
-                               isOk = false;
-                               continue;
-                       }
-
-                       const BufferVarLayoutEntry&     cmpEntry        = cmpLayout.bufferVars[cmpEntryNdx];
-
-                       if (refEntry.type                               != cmpEntry.type                                ||
-                               refEntry.arraySize                      != cmpEntry.arraySize                   ||
-                               refEntry.topLevelArraySize      != cmpEntry.topLevelArraySize   ||
-                               refEntry.isRowMajor     != cmpEntry.isRowMajor)
-                       {
-                               log << TestLog::Message << "Error: Type / array size mismatch in '" << refEntry.name << "':\n"
-                                       << "  expected: " << refEntry << "\n"
-                                       << "  got: " << cmpEntry
-                                       << TestLog::EndMessage;
-                               isOk = false;
-                       }
-               }
-       }
-
-       return isOk;
-}
-
-bool SSBOLayoutCase::compareTypes (const BufferLayout& refLayout, const BufferLayout& cmpLayout) const
-{
-       TestLog&        log                     = m_testCtx.getLog();
-       bool            isOk            = true;
-       int                     numBlocks       = m_interface.getNumBlocks();
-
-       for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
-       {
-               const BufferBlock&              block                   = m_interface.getBlock(blockNdx);
-               bool                                    isArray                 = block.isArray();
-               int                                             numInstances    = isArray ? block.getArraySize() : 1;
-
-               for (int instanceNdx = 0; instanceNdx < numInstances; instanceNdx++)
-               {
-                       std::ostringstream instanceName;
-
-                       instanceName << block.getBlockName();
-                       if (isArray)
-                               instanceName << "[" << instanceNdx << "]";
-
-                       int cmpBlockNdx = cmpLayout.getBlockIndex(instanceName.str().c_str());
-
-                       if (cmpBlockNdx < 0)
-                               continue;
-
-                       const BlockLayoutEntry& cmpBlockLayout = cmpLayout.blocks[cmpBlockNdx];
-
-                       for (vector<int>::const_iterator ndxIter = cmpBlockLayout.activeVarIndices.begin(); ndxIter != cmpBlockLayout.activeVarIndices.end(); ndxIter++)
-                       {
-                               const BufferVarLayoutEntry&     cmpEntry        = cmpLayout.bufferVars[*ndxIter];
-                               int                                                     refEntryNdx     = refLayout.getVariableIndex(cmpEntry.name.c_str());
-
-                               if (refEntryNdx < 0)
-                               {
-                                       log << TestLog::Message << "Error: Buffer variable '" << cmpEntry.name << "' not found in reference layout" << TestLog::EndMessage;
-                                       isOk = false;
-                                       continue;
-                               }
-
-                               const BufferVarLayoutEntry&     refEntry        = refLayout.bufferVars[refEntryNdx];
-
-                               if (refEntry.type != cmpEntry.type)
-                               {
-                                       log << TestLog::Message << "Error: Buffer variable type mismatch in '" << refEntry.name << "':\n"
-                                               << "  expected: " << glu::getDataTypeName(refEntry.type) << "\n"
-                                               << "  got: " << glu::getDataTypeName(cmpEntry.type)
-                                               << TestLog::EndMessage;
-                                       isOk = false;
-                               }
-
-                               if (refEntry.arraySize < cmpEntry.arraySize)
-                               {
-                                       log << TestLog::Message << "Error: Invalid array size in '" << refEntry.name << "': expected <= " << refEntry.arraySize << TestLog::EndMessage;
-                                       isOk = false;
-                               }
-
-                               if (refEntry.topLevelArraySize < cmpEntry.topLevelArraySize)
-                               {
-                                       log << TestLog::Message << "Error: Invalid top-level array size in '" << refEntry.name << "': expected <= " << refEntry.topLevelArraySize << TestLog::EndMessage;
-                                       isOk = false;
-                               }
-                       }
-               }
-       }
-
-       return isOk;
-}
-
-bool SSBOLayoutCase::checkLayoutIndices (const BufferLayout& layout) const
-{
-       TestLog&        log                     = m_testCtx.getLog();
-       int                     numVars         = (int)layout.bufferVars.size();
-       int                     numBlocks       = (int)layout.blocks.size();
-       bool            isOk            = true;
-
-       // Check variable block indices.
-       for (int varNdx = 0; varNdx < numVars; varNdx++)
-       {
-               const BufferVarLayoutEntry& bufVar = layout.bufferVars[varNdx];
-
-               if (bufVar.blockNdx < 0 || !deInBounds32(bufVar.blockNdx, 0, numBlocks))
-               {
-                       log << TestLog::Message << "Error: Invalid block index in buffer variable '" << bufVar.name << "'" << TestLog::EndMessage;
-                       isOk = false;
-               }
-       }
-
-       // Check active variables.
-       for (int blockNdx = 0; blockNdx < numBlocks; blockNdx++)
-       {
-               const BlockLayoutEntry& block = layout.blocks[blockNdx];
-
-               for (vector<int>::const_iterator varNdxIter = block.activeVarIndices.begin(); varNdxIter != block.activeVarIndices.end(); varNdxIter++)
-               {
-                       if (!deInBounds32(*varNdxIter, 0, numVars))
-                       {
-                               log << TestLog::Message << "Error: Invalid active variable index " << *varNdxIter << " in block '" << block.name << "'" << TestLog::EndMessage;
-                               isOk = false;
-                       }
-               }
-       }
-
-       return isOk;
-}
-
-bool SSBOLayoutCase::checkLayoutBounds (const BufferLayout& layout) const
-{
-       TestLog&        log                     = m_testCtx.getLog();
-       const int       numVars         = (int)layout.bufferVars.size();
-       bool            isOk            = true;
-
-       for (int varNdx = 0; varNdx < numVars; varNdx++)
-       {
-               const BufferVarLayoutEntry& var = layout.bufferVars[varNdx];
-
-               if (var.blockNdx < 0 || isUnsizedArray(var))
-                       continue;
-
-               const BlockLayoutEntry&         block                   = layout.blocks[var.blockNdx];
-               const bool                                      isMatrix                = glu::isDataTypeMatrix(var.type);
-               const int                                       numVecs                 = isMatrix ? (var.isRowMajor ? glu::getDataTypeMatrixNumRows(var.type) : glu::getDataTypeMatrixNumColumns(var.type)) : 1;
-               const int                                       numComps                = isMatrix ? (var.isRowMajor ? glu::getDataTypeMatrixNumColumns(var.type) : glu::getDataTypeMatrixNumRows(var.type)) : glu::getDataTypeScalarSize(var.type);
-               const int                                       numElements             = var.arraySize;
-               const int                                       topLevelSize    = var.topLevelArraySize;
-               const int                                       arrayStride             = var.arrayStride;
-               const int                                       topLevelStride  = var.topLevelArrayStride;
-               const int                                       compSize                = sizeof(deUint32);
-               const int                                       vecSize                 = numComps*compSize;
-
-               int                                                     minOffset               = 0;
-               int                                                     maxOffset               = 0;
-
-               // For negative strides.
-               minOffset       = de::min(minOffset, (numVecs-1)*var.matrixStride);
-               minOffset       = de::min(minOffset, (numElements-1)*arrayStride);
-               minOffset       = de::min(minOffset, (topLevelSize-1)*topLevelStride + (numElements-1)*arrayStride + (numVecs-1)*var.matrixStride);
-
-               maxOffset       = de::max(maxOffset, vecSize);
-               maxOffset       = de::max(maxOffset, (numVecs-1)*var.matrixStride + vecSize);
-               maxOffset       = de::max(maxOffset, (numElements-1)*arrayStride + vecSize);
-               maxOffset       = de::max(maxOffset, (topLevelSize-1)*topLevelStride + (numElements-1)*arrayStride + vecSize);
-               maxOffset       = de::max(maxOffset, (topLevelSize-1)*topLevelStride + (numElements-1)*arrayStride + (numVecs-1)*var.matrixStride + vecSize);
-
-               if (var.offset+minOffset < 0 || var.offset+maxOffset > block.size)
-               {
-                       log << TestLog::Message << "Error: Variable '" << var.name << "' out of block bounds" << TestLog::EndMessage;
-                       isOk = false;
-               }
-       }
-
-       return isOk;
-}
-
-bool SSBOLayoutCase::checkIndexQueries (deUint32 program, const BufferLayout& layout) const
-{
-       tcu::TestLog&                           log                     = m_testCtx.getLog();
-       const glw::Functions&           gl                      = m_renderCtx.getFunctions();
-       bool                                            allOk           = true;
-
-       // \note Spec mandates that buffer blocks are assigned consecutive locations from 0.
-       //               BlockLayoutEntries are stored in that order in UniformLayout.
-       for (int blockNdx = 0; blockNdx < (int)layout.blocks.size(); blockNdx++)
-       {
-               const BlockLayoutEntry&         block           = layout.blocks[blockNdx];
-               const int                                       queriedNdx      = gl.getProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, block.name.c_str());
-
-               if (queriedNdx != blockNdx)
-               {
-                       log << TestLog::Message << "ERROR: glGetProgramResourceIndex(" << block.name << ") returned " << queriedNdx << ", expected " << blockNdx << "!" << TestLog::EndMessage;
-                       allOk = false;
-               }
-
-               GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformBlockIndex()");
-       }
-
-       return allOk;
-}
-
-bool SSBOLayoutCase::execute (deUint32 program)
-{
-       const glw::Functions&                           gl                              = m_renderCtx.getFunctions();
-       const deUint32                                          numPassedLoc    = gl.getProgramResourceIndex(program, GL_UNIFORM, "ac_numPassed");
-       const glu::InterfaceVariableInfo        acVarInfo               = numPassedLoc != GL_INVALID_INDEX ? glu::getProgramInterfaceVariableInfo(gl, program, GL_UNIFORM, numPassedLoc)
-                                                                                                                                                                                  : glu::InterfaceVariableInfo();
-       const glu::InterfaceBlockInfo           acBufferInfo    = acVarInfo.atomicCounterBufferIndex != GL_INVALID_INDEX ? glu::getProgramInterfaceBlockInfo(gl, program, GL_ATOMIC_COUNTER_BUFFER, acVarInfo.atomicCounterBufferIndex)
-                                                                                                                                                                                                                                : glu::InterfaceBlockInfo();
-       const glu::Buffer                                       acBuffer                (m_renderCtx);
-       bool                                                            isOk                    = true;
-
-       if (numPassedLoc == GL_INVALID_INDEX)
-               throw tcu::TestError("No location for ac_numPassed found");
-
-       if (acBufferInfo.index == GL_INVALID_INDEX)
-               throw tcu::TestError("ac_numPassed buffer index is GL_INVALID_INDEX");
-
-       if (acBufferInfo.dataSize == 0)
-               throw tcu::TestError("ac_numPassed buffer size = 0");
-
-       // Initialize atomic counter buffer.
-       {
-               vector<deUint8> emptyData(acBufferInfo.dataSize, 0);
-
-               gl.bindBuffer(GL_ATOMIC_COUNTER_BUFFER, *acBuffer);
-               gl.bufferData(GL_ATOMIC_COUNTER_BUFFER, (glw::GLsizeiptr)emptyData.size(), &emptyData[0], GL_STATIC_READ);
-               gl.bindBufferBase(GL_ATOMIC_COUNTER_BUFFER, acBufferInfo.index, *acBuffer);
-               GLU_EXPECT_NO_ERROR(gl.getError(), "Setting up buffer for ac_numPassed failed");
-       }
-
-       gl.useProgram(program);
-       gl.dispatchCompute(1, 1, 1);
-       GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute() failed");
-
-       // Read back ac_numPassed data.
-       {
-               const void*     mapPtr          = gl.mapBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, acBufferInfo.dataSize, GL_MAP_READ_BIT);
-               const int       refCount        = 1;
-               int                     resCount        = 0;
-
-               GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER) failed");
-               TCU_CHECK(mapPtr);
-
-               resCount = *(const int*)((const deUint8*)mapPtr + acVarInfo.offset);
-
-               gl.unmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
-               GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER) failed");
-
-               if (refCount != resCount)
-               {
-                       m_testCtx.getLog() << TestLog::Message << "ERROR: ac_numPassed = " << resCount << ", expected " << refCount << TestLog::EndMessage;
-                       isOk = false;
-               }
-       }
-
-       GLU_EXPECT_NO_ERROR(gl.getError(), "Shader execution failed");
-
-       return isOk;
-}
-#endif
-
 } // ssbo
 } // vkt
index 1edde3b..e1783ce 100644 (file)
@@ -47,23 +47,20 @@ namespace ssbo
 
 enum BufferVarFlags
 {
-       LAYOUT_SHARED           = (1<<0),
-       LAYOUT_PACKED           = (1<<1),
-       LAYOUT_STD140           = (1<<2),
-       LAYOUT_STD430           = (1<<3),
-       LAYOUT_ROW_MAJOR        = (1<<4),
-       LAYOUT_COLUMN_MAJOR     = (1<<5),       //!< \note Lack of both flags means column-major matrix.
-       LAYOUT_MASK                     = LAYOUT_SHARED|LAYOUT_PACKED|LAYOUT_STD140|LAYOUT_STD430|LAYOUT_ROW_MAJOR|LAYOUT_COLUMN_MAJOR,
+       LAYOUT_STD140           = (1<<0),
+       LAYOUT_ROW_MAJOR        = (1<<1),
+       LAYOUT_COLUMN_MAJOR     = (1<<2),       //!< \note Lack of both flags means column-major matrix.
+       LAYOUT_MASK                     = /*LAYOUT_SHARED|LAYOUT_PACKED|LAYOUT_STD430|*/LAYOUT_STD140|LAYOUT_ROW_MAJOR|LAYOUT_COLUMN_MAJOR,
 
        // \todo [2013-10-14 pyry] Investigate adding these.
-/*     QUALIFIER_COHERENT      = (1<<6),
-       QUALIFIER_VOLATILE      = (1<<7),
-       QUALIFIER_RESTRICT      = (1<<8),
-       QUALIFIER_READONLY      = (1<<9),
-       QUALIFIER_WRITEONLY     = (1<<10),*/
-
-       ACCESS_READ                     = (1<<11),      //!< Buffer variable is read in the shader.
-       ACCESS_WRITE            = (1<<12),      //!< Buffer variable is written in the shader.
+/*     QUALIFIER_COHERENT      = (1<<3),
+       QUALIFIER_VOLATILE      = (1<<4),
+       QUALIFIER_RESTRICT      = (1<<5),
+       QUALIFIER_READONLY      = (1<<6),
+       QUALIFIER_WRITEONLY     = (1<<7),*/
+
+       ACCESS_READ                     = (1<<8),       //!< Buffer variable is read in the shader.
+       ACCESS_WRITE            = (1<<9),       //!< Buffer variable is written in the shader.
 };
 
 class BufferVar
@@ -238,17 +235,6 @@ public:
 protected:
     void                        init                        (void);
 
-/*
-       bool                                            compareStdBlocks                        (const bb::BufferLayout& refLayout, const bb::BufferLayout& cmpLayout) const;
-       bool                                            compareSharedBlocks                     (const bb::BufferLayout& refLayout, const bb::BufferLayout& cmpLayout) const;
-       bool                                            compareTypes                            (const bb::BufferLayout& refLayout, const bb::BufferLayout& cmpLayout) const;
-       bool                                            checkLayoutIndices                      (const bb::BufferLayout& layout) const;
-       bool                                            checkLayoutBounds                       (const bb::BufferLayout& layout) const;
-       bool                                            checkIndexQueries                       (deUint32 program, const bb::BufferLayout& layout) const;
-
-       bool                                            execute                                         (deUint32 program);
-*/
-
        BufferMode                                      m_bufferMode;
        ShaderInterface                         m_interface;
 
index fff8452..082f990 100644 (file)
 #include "vktSSBOLayoutCase.hpp"
 
 #include "deUniquePtr.hpp"
+#include "tcuCommandLine.hpp"
+#include "deRandom.hpp"
+#include "deStringUtil.hpp"
+#include "deString.h"
 
 namespace vkt
 {
 namespace ssbo
 {
+namespace
+{
 
+using std::string;
+using std::vector;
 using glu::VarType;
 using glu::StructType;
 
-class DEMOCase : public SSBOLayoutCase
+enum FeatureBits
+{
+       FEATURE_VECTORS                         = (1<<0),
+       FEATURE_MATRICES                        = (1<<1),
+       FEATURE_ARRAYS                          = (1<<2),
+       FEATURE_STRUCTS                         = (1<<3),
+       FEATURE_NESTED_STRUCTS          = (1<<4),
+       FEATURE_INSTANCE_ARRAYS         = (1<<5),
+       FEATURE_UNUSED_VARS                     = (1<<6),
+       FEATURE_UNUSED_MEMBERS          = (1<<7),
+       FEATURE_PACKED_LAYOUT           = (1<<8),
+       FEATURE_SHARED_LAYOUT           = (1<<9),
+       FEATURE_STD140_LAYOUT           = (1<<10),
+       FEATURE_STD430_LAYOUT           = (1<<11),
+       FEATURE_MATRIX_LAYOUT           = (1<<12),      //!< Matrix layout flags.
+       FEATURE_UNSIZED_ARRAYS          = (1<<13),
+       FEATURE_ARRAYS_OF_ARRAYS        = (1<<14)
+};
+
+class RandomSSBOLayoutCase : public SSBOLayoutCase
+{
+public:
+
+                                                       RandomSSBOLayoutCase            (tcu::TestContext& testCtx, const char* name, const char* description, BufferMode bufferMode, deUint32 features, deUint32 seed);
+
+private:
+       void                                    generateBlock                           (de::Random& rnd, deUint32 layoutFlags);
+       void                                    generateBufferVar                       (de::Random& rnd, BufferBlock& block, bool isLastMember);
+       glu::VarType                    generateType                            (de::Random& rnd, int typeDepth, bool arrayOk, bool unusedArrayOk);
+
+       deUint32                                m_features;
+       int                                             m_maxBlocks;
+       int                                             m_maxInstances;
+       int                                             m_maxArrayLength;
+       int                                             m_maxStructDepth;
+       int                                             m_maxBlockMembers;
+       int                                             m_maxStructMembers;
+       deUint32                                m_seed;
+
+       int                                             m_blockNdx;
+       int                                             m_bufferVarNdx;
+       int                                             m_structNdx;
+};
+
+RandomSSBOLayoutCase::RandomSSBOLayoutCase (tcu::TestContext& testCtx, const char* name, const char* description, BufferMode bufferMode, deUint32 features, deUint32 seed)
+       : SSBOLayoutCase                (testCtx, name, description, bufferMode)
+       , m_features                    (features)
+       , m_maxBlocks                   (4)
+       , m_maxInstances                ((features & FEATURE_INSTANCE_ARRAYS)   ? 3 : 0)
+       , m_maxArrayLength              ((features & FEATURE_ARRAYS)                    ? 8 : 0)
+       , m_maxStructDepth              ((features & FEATURE_STRUCTS)                   ? 2 : 0)
+       , m_maxBlockMembers             (5)
+       , m_maxStructMembers    (4)
+       , m_seed                                (seed)
+       , m_blockNdx                    (1)
+       , m_bufferVarNdx                (1)
+       , m_structNdx                   (1)
+{
+       de::Random rnd(m_seed);
+
+       const int numBlocks = rnd.getInt(1, m_maxBlocks);
+
+       for (int ndx = 0; ndx < numBlocks; ndx++)
+               generateBlock(rnd, 0);
+
+       init();
+}
+
+void RandomSSBOLayoutCase::generateBlock (de::Random& rnd, deUint32 layoutFlags)
+{
+       DE_ASSERT(m_blockNdx <= 'z' - 'a');
+
+       const float             instanceArrayWeight     = 0.3f;
+       BufferBlock&    block                           = m_interface.allocBlock((string("Block") + (char)('A' + m_blockNdx)).c_str());
+       int                             numInstances            = (m_maxInstances > 0 && rnd.getFloat() < instanceArrayWeight) ? rnd.getInt(0, m_maxInstances) : 0;
+       int                             numVars                         = rnd.getInt(1, m_maxBlockMembers);
+
+       if (numInstances > 0)
+               block.setArraySize(numInstances);
+
+       if (numInstances > 0 || rnd.getBool())
+               block.setInstanceName((string("block") + (char)('A' + m_blockNdx)).c_str());
+
+       // Layout flag candidates.
+       vector<deUint32> layoutFlagCandidates;
+       layoutFlagCandidates.push_back(0);
+       if (m_features & FEATURE_STD140_LAYOUT)
+               layoutFlagCandidates.push_back(LAYOUT_STD140);
+
+       layoutFlags |= rnd.choose<deUint32>(layoutFlagCandidates.begin(), layoutFlagCandidates.end());
+
+       if (m_features & FEATURE_MATRIX_LAYOUT)
+       {
+               static const deUint32 matrixCandidates[] = { 0, LAYOUT_ROW_MAJOR, LAYOUT_COLUMN_MAJOR };
+               layoutFlags |= rnd.choose<deUint32>(&matrixCandidates[0], &matrixCandidates[DE_LENGTH_OF_ARRAY(matrixCandidates)]);
+       }
+
+       block.setFlags(layoutFlags);
+
+       for (int ndx = 0; ndx < numVars; ndx++)
+               generateBufferVar(rnd, block, (ndx+1 == numVars));
+
+       if (numVars > 0)
+       {
+               const BufferVar&        lastVar                 = *(block.end()-1);
+               const glu::VarType&     lastType                = lastVar.getType();
+               const bool                      isUnsizedArr    = lastType.isArrayType() && (lastType.getArraySize() == glu::VarType::UNSIZED_ARRAY);
+
+               if (isUnsizedArr)
+               {
+                       for (int instanceNdx = 0; instanceNdx < (numInstances ? numInstances : 1); instanceNdx++)
+                       {
+                               const int arrSize = rnd.getInt(0, m_maxArrayLength);
+                               block.setLastUnsizedArraySize(instanceNdx, arrSize);
+                       }
+               }
+       }
+
+       m_blockNdx += 1;
+}
+
+static std::string genName (char first, char last, int ndx)
+{
+       std::string     str                     = "";
+       int                     alphabetLen     = last - first + 1;
+
+       while (ndx > alphabetLen)
+       {
+               str.insert(str.begin(), (char)(first + ((ndx-1)%alphabetLen)));
+               ndx = ((ndx-1) / alphabetLen);
+       }
+
+       str.insert(str.begin(), (char)(first + (ndx%(alphabetLen+1)) - 1));
+
+       return str;
+}
+
+void RandomSSBOLayoutCase::generateBufferVar (de::Random& rnd, BufferBlock& block, bool isLastMember)
+{
+       const float                     readWeight                      = 0.7f;
+       const float                     writeWeight                     = 0.7f;
+       const float                     accessWeight            = 0.85f;
+       const bool                      unusedOk                        = (m_features & FEATURE_UNUSED_VARS) != 0;
+       const std::string       name                            = genName('a', 'z', m_bufferVarNdx);
+       const glu::VarType      type                            = generateType(rnd, 0, true, isLastMember && (m_features & FEATURE_UNSIZED_ARRAYS));
+       const bool                      access                          = !unusedOk || (rnd.getFloat() < accessWeight);
+       const bool                      read                            = access ? (rnd.getFloat() < readWeight) : false;
+       const bool                      write                           = access ? (!read || (rnd.getFloat() < writeWeight)) : false;
+       const deUint32          flags                           = (read ? ACCESS_READ : 0) | (write ? ACCESS_WRITE : 0);
+
+       block.addMember(BufferVar(name.c_str(), type, flags));
+
+       m_bufferVarNdx += 1;
+}
+
+glu::VarType RandomSSBOLayoutCase::generateType (de::Random& rnd, int typeDepth, bool arrayOk, bool unsizedArrayOk)
+{
+       const float structWeight                = 0.1f;
+       const float arrayWeight                 = 0.1f;
+       const float     unsizedArrayWeight      = 0.8f;
+
+       DE_ASSERT(arrayOk || !unsizedArrayOk);
+
+       if (unsizedArrayOk && (rnd.getFloat() < unsizedArrayWeight))
+       {
+               const bool                      childArrayOk    = (m_features & FEATURE_ARRAYS_OF_ARRAYS) != 0;
+               const glu::VarType      elementType             = generateType(rnd, typeDepth, childArrayOk, false);
+               return glu::VarType(elementType, glu::VarType::UNSIZED_ARRAY);
+       }
+       else if (typeDepth < m_maxStructDepth && rnd.getFloat() < structWeight)
+       {
+               // \todo [2013-10-14 pyry] Implement unused flags for members!
+//             bool                                    unusedOk                        = (m_features & FEATURE_UNUSED_MEMBERS) != 0;
+               vector<glu::VarType>    memberTypes;
+               int                                             numMembers = rnd.getInt(1, m_maxStructMembers);
+
+               // Generate members first so nested struct declarations are in correct order.
+               for (int ndx = 0; ndx < numMembers; ndx++)
+                       memberTypes.push_back(generateType(rnd, typeDepth+1, true, false));
+
+               glu::StructType& structType = m_interface.allocStruct((string("s") + genName('A', 'Z', m_structNdx)).c_str());
+               m_structNdx += 1;
+
+               DE_ASSERT(numMembers <= 'Z' - 'A');
+               for (int ndx = 0; ndx < numMembers; ndx++)
+               {
+                       structType.addMember((string("m") + (char)('A' + ndx)).c_str(), memberTypes[ndx]);
+               }
+
+               return glu::VarType(&structType);
+       }
+       else if (m_maxArrayLength > 0 && arrayOk && rnd.getFloat() < arrayWeight)
+       {
+               const int                       arrayLength             = rnd.getInt(1, m_maxArrayLength);
+               const bool                      childArrayOk    = (m_features & FEATURE_ARRAYS_OF_ARRAYS) != 0;
+               const glu::VarType      elementType             = generateType(rnd, typeDepth, childArrayOk, false);
+
+               return glu::VarType(elementType, arrayLength);
+       }
+       else
+       {
+               vector<glu::DataType> typeCandidates;
+
+               typeCandidates.push_back(glu::TYPE_FLOAT);
+               typeCandidates.push_back(glu::TYPE_INT);
+               typeCandidates.push_back(glu::TYPE_UINT);
+               typeCandidates.push_back(glu::TYPE_BOOL);
+
+               if (m_features & FEATURE_VECTORS)
+               {
+                       typeCandidates.push_back(glu::TYPE_FLOAT_VEC2);
+                       typeCandidates.push_back(glu::TYPE_FLOAT_VEC3);
+                       typeCandidates.push_back(glu::TYPE_FLOAT_VEC4);
+                       typeCandidates.push_back(glu::TYPE_INT_VEC2);
+                       typeCandidates.push_back(glu::TYPE_INT_VEC3);
+                       typeCandidates.push_back(glu::TYPE_INT_VEC4);
+                       typeCandidates.push_back(glu::TYPE_UINT_VEC2);
+                       typeCandidates.push_back(glu::TYPE_UINT_VEC3);
+                       typeCandidates.push_back(glu::TYPE_UINT_VEC4);
+                       typeCandidates.push_back(glu::TYPE_BOOL_VEC2);
+                       typeCandidates.push_back(glu::TYPE_BOOL_VEC3);
+                       typeCandidates.push_back(glu::TYPE_BOOL_VEC4);
+               }
+
+               if (m_features & FEATURE_MATRICES)
+               {
+                       typeCandidates.push_back(glu::TYPE_FLOAT_MAT2);
+                       typeCandidates.push_back(glu::TYPE_FLOAT_MAT2X3);
+                       typeCandidates.push_back(glu::TYPE_FLOAT_MAT3X2);
+                       typeCandidates.push_back(glu::TYPE_FLOAT_MAT3);
+                       typeCandidates.push_back(glu::TYPE_FLOAT_MAT3X4);
+                       typeCandidates.push_back(glu::TYPE_FLOAT_MAT4X2);
+                       typeCandidates.push_back(glu::TYPE_FLOAT_MAT4X3);
+                       typeCandidates.push_back(glu::TYPE_FLOAT_MAT4);
+               }
+
+               glu::DataType   type            = rnd.choose<glu::DataType>(typeCandidates.begin(), typeCandidates.end());
+               glu::Precision  precision;
+
+               if (!glu::isDataTypeBoolOrBVec(type))
+               {
+                       // Precision.
+                       static const glu::Precision precisionCandidates[] = { glu::PRECISION_LOWP, glu::PRECISION_MEDIUMP, glu::PRECISION_HIGHP };
+                       precision = rnd.choose<glu::Precision>(&precisionCandidates[0], &precisionCandidates[DE_LENGTH_OF_ARRAY(precisionCandidates)]);
+               }
+               else
+                       precision = glu::PRECISION_LAST;
+
+               return glu::VarType(type, precision);
+       }
+}
+
+class BlockBasicTypeCase : public SSBOLayoutCase
+{
+public:
+       BlockBasicTypeCase (tcu::TestContext& testCtx, const char* name, const char* description, const VarType& type, deUint32 layoutFlags, int numInstances)
+               : SSBOLayoutCase(testCtx, name, description, BUFFERMODE_PER_BLOCK)
+       {
+               BufferBlock& block = m_interface.allocBlock("Block");
+               block.addMember(BufferVar("var", type, ACCESS_READ|ACCESS_WRITE));
+               block.setFlags(layoutFlags);
+
+               if (numInstances > 0)
+               {
+                       block.setArraySize(numInstances);
+                       block.setInstanceName("block");
+               }
+
+               init();
+       }
+};
+
+class BlockBasicUnsizedArrayCase : public SSBOLayoutCase
+{
+public:
+       BlockBasicUnsizedArrayCase (tcu::TestContext& testCtx, const char* name, const char* description, const VarType& elementType, int arraySize, deUint32 layoutFlags)
+               : SSBOLayoutCase(testCtx, name, description, BUFFERMODE_PER_BLOCK)
+       {
+               BufferBlock& block = m_interface.allocBlock("Block");
+               block.addMember(BufferVar("var", VarType(elementType, VarType::UNSIZED_ARRAY), ACCESS_READ|ACCESS_WRITE));
+               block.setFlags(layoutFlags);
+
+               block.setLastUnsizedArraySize(0, arraySize);
+
+               init();
+       }
+};
+
+static void createRandomCaseGroup (tcu::TestCaseGroup* parentGroup, tcu::TestContext& testCtx, const char* groupName, const char* description, SSBOLayoutCase::BufferMode bufferMode, deUint32 features, int numCases, deUint32 baseSeed)
+{
+       tcu::TestCaseGroup* group = new tcu::TestCaseGroup(testCtx, groupName, description);
+       parentGroup->addChild(group);
+
+       baseSeed += (deUint32)testCtx.getCommandLine().getBaseSeed();
+
+       for (int ndx = 0; ndx < numCases; ndx++)
+               group->addChild(new RandomSSBOLayoutCase(testCtx, de::toString(ndx).c_str(), "", bufferMode, features, (deUint32)ndx+baseSeed));
+}
+
+class BlockSingleStructCase : public SSBOLayoutCase
+{
+public:
+       BlockSingleStructCase (tcu::TestContext& testCtx, const char* name, const char* description, deUint32 layoutFlags, BufferMode bufferMode, int numInstances)
+               : SSBOLayoutCase        (testCtx, name, description, bufferMode)
+               , m_layoutFlags         (layoutFlags)
+               , m_numInstances        (numInstances)
+       {
+               StructType& typeS = m_interface.allocStruct("S");
+               typeS.addMember("a", VarType(glu::TYPE_INT_VEC3, glu::PRECISION_HIGHP)); // \todo [pyry] First member is unused.
+               typeS.addMember("b", VarType(VarType(glu::TYPE_FLOAT_MAT3, glu::PRECISION_MEDIUMP), 4));
+               typeS.addMember("c", VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
+
+               BufferBlock& block = m_interface.allocBlock("Block");
+               block.addMember(BufferVar("s", VarType(&typeS), ACCESS_READ|ACCESS_WRITE));
+               block.setFlags(m_layoutFlags);
+
+               if (m_numInstances > 0)
+               {
+                       block.setInstanceName("block");
+                       block.setArraySize(m_numInstances);
+               }
+
+               init();
+       }
+
+private:
+       deUint32        m_layoutFlags;
+       int                     m_numInstances;
+};
+
+class BlockSingleStructArrayCase : public SSBOLayoutCase
+{
+public:
+       BlockSingleStructArrayCase (tcu::TestContext& testCtx, const char* name, const char* description, deUint32 layoutFlags, BufferMode bufferMode, int numInstances)
+               : SSBOLayoutCase        (testCtx, name, description, bufferMode)
+               , m_layoutFlags         (layoutFlags)
+               , m_numInstances        (numInstances)
+       {
+               StructType& typeS = m_interface.allocStruct("S");
+               typeS.addMember("a", VarType(glu::TYPE_INT_VEC3, glu::PRECISION_HIGHP)); // \todo [pyry] UNUSED
+               typeS.addMember("b", VarType(VarType(glu::TYPE_FLOAT_MAT3, glu::PRECISION_MEDIUMP), 4));
+               typeS.addMember("c", VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
+
+               BufferBlock& block = m_interface.allocBlock("Block");
+               block.addMember(BufferVar("u", VarType(glu::TYPE_UINT, glu::PRECISION_LOWP), 0 /* no access */));
+               block.addMember(BufferVar("s", VarType(VarType(&typeS), 3), ACCESS_READ|ACCESS_WRITE));
+               block.addMember(BufferVar("v", VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_MEDIUMP), ACCESS_WRITE));
+               block.setFlags(m_layoutFlags);
+
+               if (m_numInstances > 0)
+               {
+                       block.setInstanceName("block");
+                       block.setArraySize(m_numInstances);
+               }
+
+               init();
+       }
+
+private:
+       deUint32        m_layoutFlags;
+       int                     m_numInstances;
+};
+
+class BlockSingleNestedStructCase : public SSBOLayoutCase
+{
+public:
+       BlockSingleNestedStructCase (tcu::TestContext& testCtx, const char* name, const char* description, deUint32 layoutFlags, BufferMode bufferMode, int numInstances)
+               : SSBOLayoutCase        (testCtx, name, description, bufferMode)
+               , m_layoutFlags         (layoutFlags)
+               , m_numInstances        (numInstances)
+       {
+               StructType& typeS = m_interface.allocStruct("S");
+               typeS.addMember("a", VarType(glu::TYPE_INT_VEC3, glu::PRECISION_HIGHP));
+               typeS.addMember("b", VarType(VarType(glu::TYPE_FLOAT_MAT3, glu::PRECISION_MEDIUMP), 4));
+               typeS.addMember("c", VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)); // \todo [pyry] UNUSED
+
+               StructType& typeT = m_interface.allocStruct("T");
+               typeT.addMember("a", VarType(glu::TYPE_FLOAT_MAT3, glu::PRECISION_MEDIUMP));
+               typeT.addMember("b", VarType(&typeS));
+
+               BufferBlock& block = m_interface.allocBlock("Block");
+               block.addMember(BufferVar("s", VarType(&typeS), ACCESS_READ));
+               block.addMember(BufferVar("v", VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_LOWP), 0 /* no access */));
+               block.addMember(BufferVar("t", VarType(&typeT), ACCESS_READ|ACCESS_WRITE));
+               block.addMember(BufferVar("u", VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP), ACCESS_WRITE));
+               block.setFlags(m_layoutFlags);
+
+               if (m_numInstances > 0)
+               {
+                       block.setInstanceName("block");
+                       block.setArraySize(m_numInstances);
+               }
+
+               init();
+       }
+
+private:
+       deUint32        m_layoutFlags;
+       int                     m_numInstances;
+};
+
+class BlockSingleNestedStructArrayCase : public SSBOLayoutCase
+{
+public:
+       BlockSingleNestedStructArrayCase (tcu::TestContext& testCtx, const char* name, const char* description, deUint32 layoutFlags, BufferMode bufferMode, int numInstances)
+               : SSBOLayoutCase        (testCtx, name, description, bufferMode)
+               , m_layoutFlags         (layoutFlags)
+               , m_numInstances        (numInstances)
+       {
+               StructType& typeS = m_interface.allocStruct("S");
+               typeS.addMember("a", VarType(glu::TYPE_INT_VEC3, glu::PRECISION_HIGHP));
+               typeS.addMember("b", VarType(VarType(glu::TYPE_INT_VEC2, glu::PRECISION_MEDIUMP), 4));
+               typeS.addMember("c", VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)); // \todo [pyry] UNUSED
+
+               StructType& typeT = m_interface.allocStruct("T");
+               typeT.addMember("a", VarType(glu::TYPE_FLOAT_MAT3, glu::PRECISION_MEDIUMP));
+               typeT.addMember("b", VarType(VarType(&typeS), 3));
+
+               BufferBlock& block = m_interface.allocBlock("Block");
+               block.addMember(BufferVar("s", VarType(&typeS), ACCESS_WRITE));
+               block.addMember(BufferVar("v", VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_LOWP), 0 /* no access */));
+               block.addMember(BufferVar("t", VarType(VarType(&typeT), 2), ACCESS_READ));
+               block.addMember(BufferVar("u", VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP), ACCESS_READ|ACCESS_WRITE));
+               block.setFlags(m_layoutFlags);
+
+               if (m_numInstances > 0)
+               {
+                       block.setInstanceName("block");
+                       block.setArraySize(m_numInstances);
+               }
+
+               init();
+       }
+
+private:
+       deUint32        m_layoutFlags;
+       int                     m_numInstances;
+};
+
+class BlockUnsizedStructArrayCase : public SSBOLayoutCase
+{
+public:
+       BlockUnsizedStructArrayCase (tcu::TestContext& testCtx, const char* name, const char* description, deUint32 layoutFlags, BufferMode bufferMode, int numInstances)
+               : SSBOLayoutCase        (testCtx, name, description, bufferMode)
+               , m_layoutFlags         (layoutFlags)
+               , m_numInstances        (numInstances)
+       {
+               StructType& typeS = m_interface.allocStruct("S");
+               typeS.addMember("a", VarType(glu::TYPE_UINT_VEC2, glu::PRECISION_HIGHP)); // \todo [pyry] UNUSED
+               typeS.addMember("b", VarType(VarType(glu::TYPE_FLOAT_MAT2X4, glu::PRECISION_MEDIUMP), 4));
+               typeS.addMember("c", VarType(glu::TYPE_FLOAT_VEC3, glu::PRECISION_HIGHP));
+
+               BufferBlock& block = m_interface.allocBlock("Block");
+               block.addMember(BufferVar("u", VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_LOWP), 0 /* no access */));
+               block.addMember(BufferVar("v", VarType(glu::TYPE_UINT, glu::PRECISION_MEDIUMP), ACCESS_WRITE));
+               block.addMember(BufferVar("s", VarType(VarType(&typeS), VarType::UNSIZED_ARRAY), ACCESS_READ|ACCESS_WRITE));
+               block.setFlags(m_layoutFlags);
+
+               if (m_numInstances > 0)
+               {
+                       block.setInstanceName("block");
+                       block.setArraySize(m_numInstances);
+               }
+
+               {
+                       de::Random rnd(246);
+                       for (int ndx = 0; ndx < (m_numInstances ? m_numInstances : 1); ndx++)
+                       {
+                               const int lastArrayLen = rnd.getInt(1, 5);
+                               block.setLastUnsizedArraySize(ndx, lastArrayLen);
+                       }
+               }
+
+               init();
+       }
+
+private:
+       deUint32        m_layoutFlags;
+       int                     m_numInstances;
+};
+
+class Block2LevelUnsizedStructArrayCase : public SSBOLayoutCase
+{
+public:
+       Block2LevelUnsizedStructArrayCase (tcu::TestContext& testCtx, const char* name, const char* description, deUint32 layoutFlags, BufferMode bufferMode, int numInstances)
+               : SSBOLayoutCase        (testCtx, name, description, bufferMode)
+               , m_layoutFlags         (layoutFlags)
+               , m_numInstances        (numInstances)
+       {
+               StructType& typeS = m_interface.allocStruct("S");
+               typeS.addMember("a", VarType(glu::TYPE_INT_VEC3, glu::PRECISION_HIGHP));
+               typeS.addMember("c", VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
+
+               BufferBlock& block = m_interface.allocBlock("Block");
+               block.addMember(BufferVar("u", VarType(glu::TYPE_UINT, glu::PRECISION_LOWP), 0 /* no access */));
+               block.addMember(BufferVar("v", VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_MEDIUMP), ACCESS_WRITE));
+               block.addMember(BufferVar("s", VarType(VarType(VarType(&typeS), 2), VarType::UNSIZED_ARRAY), ACCESS_READ|ACCESS_WRITE));
+               block.setFlags(m_layoutFlags);
+
+               if (m_numInstances > 0)
+               {
+                       block.setInstanceName("block");
+                       block.setArraySize(m_numInstances);
+               }
+
+               {
+                       de::Random rnd(2344);
+                       for (int ndx = 0; ndx < (m_numInstances ? m_numInstances : 1); ndx++)
+                       {
+                               const int lastArrayLen = rnd.getInt(1, 5);
+                               block.setLastUnsizedArraySize(ndx, lastArrayLen);
+                       }
+               }
+
+               init();
+       }
+
+private:
+       deUint32        m_layoutFlags;
+       int                     m_numInstances;
+};
+
+class BlockUnsizedNestedStructArrayCase : public SSBOLayoutCase
+{
+public:
+       BlockUnsizedNestedStructArrayCase (tcu::TestContext& testCtx, const char* name, const char* description, deUint32 layoutFlags, BufferMode bufferMode, int numInstances)
+               : SSBOLayoutCase        (testCtx, name, description, bufferMode)
+               , m_layoutFlags         (layoutFlags)
+               , m_numInstances        (numInstances)
+       {
+               StructType& typeS = m_interface.allocStruct("S");
+               typeS.addMember("a", VarType(glu::TYPE_UINT_VEC3, glu::PRECISION_HIGHP));
+               typeS.addMember("b", VarType(VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_MEDIUMP), 4));
+               typeS.addMember("c", VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP)); // \todo [pyry] UNUSED
+
+               StructType& typeT = m_interface.allocStruct("T");
+               typeT.addMember("a", VarType(glu::TYPE_FLOAT_MAT4X3, glu::PRECISION_MEDIUMP));
+               typeT.addMember("b", VarType(VarType(&typeS), 3));
+               typeT.addMember("c", VarType(glu::TYPE_INT, glu::PRECISION_HIGHP));
+
+               BufferBlock& block = m_interface.allocBlock("Block");
+               block.addMember(BufferVar("s", VarType(&typeS), ACCESS_WRITE));
+               block.addMember(BufferVar("v", VarType(glu::TYPE_FLOAT_VEC2, glu::PRECISION_LOWP), 0 /* no access */));
+               block.addMember(BufferVar("u", VarType(glu::TYPE_UINT, glu::PRECISION_HIGHP), ACCESS_READ|ACCESS_WRITE));
+               block.addMember(BufferVar("t", VarType(VarType(&typeT), VarType::UNSIZED_ARRAY), ACCESS_READ));
+               block.setFlags(m_layoutFlags);
+
+               if (m_numInstances > 0)
+               {
+                       block.setInstanceName("block");
+                       block.setArraySize(m_numInstances);
+               }
+
+               {
+                       de::Random rnd(7921);
+                       for (int ndx = 0; ndx < (m_numInstances ? m_numInstances : 1); ndx++)
+                       {
+                               const int lastArrayLen = rnd.getInt(1, 5);
+                               block.setLastUnsizedArraySize(ndx, lastArrayLen);
+                       }
+               }
+
+               init();
+       }
+
+private:
+       deUint32        m_layoutFlags;
+       int                     m_numInstances;
+};
+
+class BlockMultiBasicTypesCase : public SSBOLayoutCase
 {
 public:
-    DEMOCase (tcu::TestContext& testCtx, const char* name, const char* description, const VarType& type, deUint32 layoutFlags, int numInstances)
-        : SSBOLayoutCase(testCtx, name, description, BUFFERMODE_PER_BLOCK)
-    {
-        BufferBlock& block = m_interface.allocBlock("Block");
-        block.addMember(BufferVar("var", type, ACCESS_READ|ACCESS_WRITE));
-        block.setFlags(layoutFlags);
-
-        if (numInstances > 0)
-        {
-            block.setArraySize(numInstances);
-            block.setInstanceName("block");
-        }
+       BlockMultiBasicTypesCase (tcu::TestContext& testCtx, const char* name, const char* description, deUint32 flagsA, deUint32 flagsB, BufferMode bufferMode, int numInstances)
+               : SSBOLayoutCase        (testCtx, name, description, bufferMode)
+               , m_flagsA                      (flagsA)
+               , m_flagsB                      (flagsB)
+               , m_numInstances        (numInstances)
+       {
+               BufferBlock& blockA = m_interface.allocBlock("BlockA");
+               blockA.addMember(BufferVar("a", VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), ACCESS_READ|ACCESS_WRITE));
+               blockA.addMember(BufferVar("b", VarType(glu::TYPE_UINT_VEC3, glu::PRECISION_LOWP), 0 /* no access */));
+               blockA.addMember(BufferVar("c", VarType(glu::TYPE_FLOAT_MAT2, glu::PRECISION_MEDIUMP), ACCESS_READ));
+               blockA.setInstanceName("blockA");
+               blockA.setFlags(m_flagsA);
+
+               BufferBlock& blockB = m_interface.allocBlock("BlockB");
+               blockB.addMember(BufferVar("a", VarType(glu::TYPE_FLOAT_MAT3, glu::PRECISION_MEDIUMP), ACCESS_WRITE));
+               blockB.addMember(BufferVar("b", VarType(glu::TYPE_INT_VEC2, glu::PRECISION_LOWP), ACCESS_READ));
+               blockB.addMember(BufferVar("c", VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP), 0 /* no access */));
+               blockB.addMember(BufferVar("d", VarType(glu::TYPE_BOOL, glu::PRECISION_LAST), ACCESS_READ|ACCESS_WRITE));
+               blockB.setInstanceName("blockB");
+               blockB.setFlags(m_flagsB);
+
+               if (m_numInstances > 0)
+               {
+                       blockA.setArraySize(m_numInstances);
+                       blockB.setArraySize(m_numInstances);
+               }
 
                init();
-    }
+       }
+
+private:
+       deUint32        m_flagsA;
+       deUint32        m_flagsB;
+       int                     m_numInstances;
 };
 
+class BlockMultiNestedStructCase : public SSBOLayoutCase
+{
+public:
+       BlockMultiNestedStructCase (tcu::TestContext& testCtx, const char* name, const char* description, deUint32 flagsA, deUint32 flagsB, BufferMode bufferMode, int numInstances)
+               : SSBOLayoutCase        (testCtx, name, description, bufferMode)
+               , m_flagsA                      (flagsA)
+               , m_flagsB                      (flagsB)
+               , m_numInstances        (numInstances)
+       {
+               StructType& typeS = m_interface.allocStruct("S");
+               typeS.addMember("a", VarType(glu::TYPE_FLOAT_MAT3, glu::PRECISION_LOWP));
+               typeS.addMember("b", VarType(VarType(glu::TYPE_INT_VEC2, glu::PRECISION_MEDIUMP), 4));
+               typeS.addMember("c", VarType(glu::TYPE_FLOAT_VEC4, glu::PRECISION_HIGHP));
+
+               StructType& typeT = m_interface.allocStruct("T");
+               typeT.addMember("a", VarType(glu::TYPE_UINT, glu::PRECISION_MEDIUMP)); // \todo [pyry] UNUSED
+               typeT.addMember("b", VarType(&typeS));
+               typeT.addMember("c", VarType(glu::TYPE_BOOL_VEC4, glu::PRECISION_LAST));
+
+               BufferBlock& blockA = m_interface.allocBlock("BlockA");
+               blockA.addMember(BufferVar("a", VarType(glu::TYPE_FLOAT, glu::PRECISION_HIGHP), ACCESS_READ|ACCESS_WRITE));
+               blockA.addMember(BufferVar("b", VarType(&typeS), ACCESS_WRITE));
+               blockA.addMember(BufferVar("c", VarType(glu::TYPE_UINT_VEC3, glu::PRECISION_LOWP), 0 /* no access */));
+               blockA.setInstanceName("blockA");
+               blockA.setFlags(m_flagsA);
+
+               BufferBlock& blockB = m_interface.allocBlock("BlockB");
+               blockB.addMember(BufferVar("a", VarType(glu::TYPE_FLOAT_MAT2, glu::PRECISION_MEDIUMP), ACCESS_WRITE));
+               blockB.addMember(BufferVar("b", VarType(&typeT), ACCESS_READ|ACCESS_WRITE));
+               blockB.addMember(BufferVar("c", VarType(glu::TYPE_BOOL_VEC4, glu::PRECISION_LAST), 0 /* no access */));
+               blockB.addMember(BufferVar("d", VarType(glu::TYPE_BOOL, glu::PRECISION_LAST), ACCESS_READ|ACCESS_WRITE));
+               blockB.setInstanceName("blockB");
+               blockB.setFlags(m_flagsB);
+
+               if (m_numInstances > 0)
+               {
+                       blockA.setArraySize(m_numInstances);
+                       blockB.setArraySize(m_numInstances);
+               }
+
+               init();
+       }
+
+private:
+       deUint32        m_flagsA;
+       deUint32        m_flagsB;
+       int                     m_numInstances;
+};
+
+class SSBOLayoutTests : public tcu::TestCaseGroup
+{
+public:
+                                                       SSBOLayoutTests         (tcu::TestContext& testCtx);
+                                                       ~SSBOLayoutTests        (void);
+
+       void                                    init                            (void);
+
+private:
+                                                       SSBOLayoutTests         (const SSBOLayoutTests& other);
+       SSBOLayoutTests&                operator=                       (const SSBOLayoutTests& other);
+};
+
+
+SSBOLayoutTests::SSBOLayoutTests (tcu::TestContext& testCtx)
+       : TestCaseGroup(testCtx, "layout", "SSBO Layout Tests")
+{
+}
+
+SSBOLayoutTests::~SSBOLayoutTests (void)
+{
+}
+
+void SSBOLayoutTests::init (void)
+{
+       static const glu::DataType basicTypes[] =
+       {
+               glu::TYPE_FLOAT,
+               glu::TYPE_FLOAT_VEC2,
+               glu::TYPE_FLOAT_VEC3,
+               glu::TYPE_FLOAT_VEC4,
+               glu::TYPE_INT,
+               glu::TYPE_INT_VEC2,
+               glu::TYPE_INT_VEC3,
+               glu::TYPE_INT_VEC4,
+               glu::TYPE_UINT,
+               glu::TYPE_UINT_VEC2,
+               glu::TYPE_UINT_VEC3,
+               glu::TYPE_UINT_VEC4,
+               glu::TYPE_BOOL,
+               glu::TYPE_BOOL_VEC2,
+               glu::TYPE_BOOL_VEC3,
+               glu::TYPE_BOOL_VEC4,
+               glu::TYPE_FLOAT_MAT2,
+               glu::TYPE_FLOAT_MAT3,
+               glu::TYPE_FLOAT_MAT4,
+               glu::TYPE_FLOAT_MAT2X3,
+               glu::TYPE_FLOAT_MAT2X4,
+               glu::TYPE_FLOAT_MAT3X2,
+               glu::TYPE_FLOAT_MAT3X4,
+               glu::TYPE_FLOAT_MAT4X2,
+               glu::TYPE_FLOAT_MAT4X3
+       };
+
+       static const struct
+       {
+               const char*             name;
+               deUint32                flags;
+       } layoutFlags[] =
+       {
+               { "std140",             LAYOUT_STD140   },
+       };
+
+       static const struct
+       {
+               const char*             name;
+               deUint32                flags;
+       } matrixFlags[] =
+       {
+               { "row_major",          LAYOUT_ROW_MAJOR        },
+               { "column_major",       LAYOUT_COLUMN_MAJOR }
+       };
+
+       static const struct
+       {
+               const char*                                                     name;
+               SSBOLayoutCase::BufferMode              mode;
+       } bufferModes[] =
+       {
+               { "per_block_buffer",   SSBOLayoutCase::BUFFERMODE_PER_BLOCK },
+               { "single_buffer",              SSBOLayoutCase::BUFFERMODE_SINGLE       }
+       };
+
+       // ubo.single_basic_type
+       {
+               tcu::TestCaseGroup* singleBasicTypeGroup = new tcu::TestCaseGroup(m_testCtx, "single_basic_type", "Single basic variable in single buffer");
+               addChild(singleBasicTypeGroup);
+
+               for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+               {
+                       tcu::TestCaseGroup* layoutGroup = new tcu::TestCaseGroup(m_testCtx, layoutFlags[layoutFlagNdx].name, "");
+                       singleBasicTypeGroup->addChild(layoutGroup);
+
+                       for (int basicTypeNdx = 0; basicTypeNdx < DE_LENGTH_OF_ARRAY(basicTypes); basicTypeNdx++)
+                       {
+                               glu::DataType   type            = basicTypes[basicTypeNdx];
+                               const char*             typeName        = glu::getDataTypeName(type);
+
+                               if (glu::isDataTypeBoolOrBVec(type))
+                                       layoutGroup->addChild(new BlockBasicTypeCase(m_testCtx, typeName, "", VarType(type, glu::PRECISION_LAST), layoutFlags[layoutFlagNdx].flags, 0));
+                               else
+                               {
+                                       for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
+                                       {
+                                               const glu::Precision    precision       = glu::Precision(precNdx);
+                                               const string                    caseName        = string(glu::getPrecisionName(precision)) + "_" + typeName;
+
+                                               layoutGroup->addChild(new BlockBasicTypeCase(m_testCtx, caseName.c_str(), "", VarType(type, precision), layoutFlags[layoutFlagNdx].flags, 0));
+                                       }
+                               }
+
+                               if (glu::isDataTypeMatrix(type))
+                               {
+                                       for (int matFlagNdx = 0; matFlagNdx < DE_LENGTH_OF_ARRAY(matrixFlags); matFlagNdx++)
+                                       {
+                                               for (int precNdx = 0; precNdx < glu::PRECISION_LAST; precNdx++)
+                                               {
+                                                       const glu::Precision    precision       = glu::Precision(precNdx);
+                                                       const string                    caseName        = string(matrixFlags[matFlagNdx].name) + "_" + string(glu::getPrecisionName(precision)) + "_" + typeName;
+
+                                                       layoutGroup->addChild(new BlockBasicTypeCase(m_testCtx, caseName.c_str(), "", glu::VarType(type, precision), layoutFlags[layoutFlagNdx].flags|matrixFlags[matFlagNdx].flags, 0));
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       // ubo.single_basic_array
+       {
+               tcu::TestCaseGroup* singleBasicArrayGroup = new tcu::TestCaseGroup(m_testCtx, "single_basic_array", "Single basic array variable in single buffer");
+               addChild(singleBasicArrayGroup);
+
+               for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+               {
+                       tcu::TestCaseGroup* layoutGroup = new tcu::TestCaseGroup(m_testCtx, layoutFlags[layoutFlagNdx].name, "");
+                       singleBasicArrayGroup->addChild(layoutGroup);
+
+                       for (int basicTypeNdx = 0; basicTypeNdx < DE_LENGTH_OF_ARRAY(basicTypes); basicTypeNdx++)
+                       {
+                               glu::DataType   type            = basicTypes[basicTypeNdx];
+                               const char*             typeName        = glu::getDataTypeName(type);
+                               const int               arraySize       = 3;
+
+                               layoutGroup->addChild(new BlockBasicTypeCase(m_testCtx, typeName, "",
+                                                                                                                        VarType(VarType(type, glu::isDataTypeBoolOrBVec(type) ? glu::PRECISION_LAST : glu::PRECISION_HIGHP), arraySize),
+                                                                                                                        layoutFlags[layoutFlagNdx].flags, 0));
+
+                               if (glu::isDataTypeMatrix(type))
+                               {
+                                       for (int matFlagNdx = 0; matFlagNdx < DE_LENGTH_OF_ARRAY(matrixFlags); matFlagNdx++)
+                                               layoutGroup->addChild(new BlockBasicTypeCase(m_testCtx, (string(matrixFlags[matFlagNdx].name) + "_" + typeName).c_str(), "",
+                                                                                                                                        VarType(VarType(type, glu::PRECISION_HIGHP), arraySize),
+                                                                                                                                        layoutFlags[layoutFlagNdx].flags|matrixFlags[matFlagNdx].flags, 0));
+                               }
+                       }
+               }
+       }
+
+       // ubo.basic_unsized_array
+       {
+               tcu::TestCaseGroup* basicUnsizedArray = new tcu::TestCaseGroup(m_testCtx, "basic_unsized_array", "Basic unsized array tests");
+               addChild(basicUnsizedArray);
+
+               for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+               {
+                       tcu::TestCaseGroup* layoutGroup = new tcu::TestCaseGroup(m_testCtx, layoutFlags[layoutFlagNdx].name, "");
+                       basicUnsizedArray->addChild(layoutGroup);
+
+                       for (int basicTypeNdx = 0; basicTypeNdx < DE_LENGTH_OF_ARRAY(basicTypes); basicTypeNdx++)
+                       {
+                               glu::DataType   type            = basicTypes[basicTypeNdx];
+                               const char*             typeName        = glu::getDataTypeName(type);
+                               const int               arraySize       = 19;
+
+                               layoutGroup->addChild(new BlockBasicUnsizedArrayCase(m_testCtx, typeName, "",
+                                                                                                                                        VarType(type, glu::isDataTypeBoolOrBVec(type) ? glu::PRECISION_LAST : glu::PRECISION_HIGHP),
+                                                                                                                                        arraySize, layoutFlags[layoutFlagNdx].flags));
+
+                               if (glu::isDataTypeMatrix(type))
+                               {
+                                       for (int matFlagNdx = 0; matFlagNdx < DE_LENGTH_OF_ARRAY(matrixFlags); matFlagNdx++)
+                                               layoutGroup->addChild(new BlockBasicUnsizedArrayCase(m_testCtx, (string(matrixFlags[matFlagNdx].name) + "_" + typeName).c_str(), "",
+                                                                                                                                                        VarType(type, glu::PRECISION_HIGHP), arraySize,
+                                                                                                                                                        layoutFlags[layoutFlagNdx].flags|matrixFlags[matFlagNdx].flags));
+                               }
+                       }
+               }
+       }
+
+       // ubo.2_level_array
+       {
+               tcu::TestCaseGroup* nestedArrayGroup = new tcu::TestCaseGroup(m_testCtx, "2_level_array", "2-level nested array");
+               addChild(nestedArrayGroup);
+
+               for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+               {
+                       tcu::TestCaseGroup* layoutGroup = new tcu::TestCaseGroup(m_testCtx, layoutFlags[layoutFlagNdx].name, "");
+                       nestedArrayGroup->addChild(layoutGroup);
+
+                       for (int basicTypeNdx = 0; basicTypeNdx < DE_LENGTH_OF_ARRAY(basicTypes); basicTypeNdx++)
+                       {
+                               glu::DataType   type            = basicTypes[basicTypeNdx];
+                               const char*             typeName        = glu::getDataTypeName(type);
+                               const int               childSize       = 3;
+                               const int               parentSize      = 4;
+                               const VarType   childType       (VarType(type, glu::isDataTypeBoolOrBVec(type) ? glu::PRECISION_LAST : glu::PRECISION_HIGHP), childSize);
+                               const VarType   fullType        (childType, parentSize);
+
+                               layoutGroup->addChild(new BlockBasicTypeCase(m_testCtx, typeName, "", fullType, layoutFlags[layoutFlagNdx].flags, 0));
+
+                               if (glu::isDataTypeMatrix(type))
+                               {
+                                       for (int matFlagNdx = 0; matFlagNdx < DE_LENGTH_OF_ARRAY(matrixFlags); matFlagNdx++)
+                                               layoutGroup->addChild(new BlockBasicTypeCase(m_testCtx, (string(matrixFlags[matFlagNdx].name) + "_" + typeName).c_str(), "",
+                                                                                                                                        fullType,
+                                                                                                                                        layoutFlags[layoutFlagNdx].flags|matrixFlags[matFlagNdx].flags, 0));
+                               }
+                       }
+               }
+       }
+
+       // ubo.3_level_array
+       {
+               tcu::TestCaseGroup* nestedArrayGroup = new tcu::TestCaseGroup(m_testCtx, "3_level_array", "3-level nested array");
+               addChild(nestedArrayGroup);
+
+               for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+               {
+                       tcu::TestCaseGroup* layoutGroup = new tcu::TestCaseGroup(m_testCtx, layoutFlags[layoutFlagNdx].name, "");
+                       nestedArrayGroup->addChild(layoutGroup);
+
+                       for (int basicTypeNdx = 0; basicTypeNdx < DE_LENGTH_OF_ARRAY(basicTypes); basicTypeNdx++)
+                       {
+                               glu::DataType   type            = basicTypes[basicTypeNdx];
+                               const char*             typeName        = glu::getDataTypeName(type);
+                               const int               childSize0      = 3;
+                               const int               childSize1      = 2;
+                               const int               parentSize      = 4;
+                               const VarType   childType0      (VarType(type, glu::isDataTypeBoolOrBVec(type) ? glu::PRECISION_LAST : glu::PRECISION_HIGHP), childSize0);
+                               const VarType   childType1      (childType0, childSize1);
+                               const VarType   fullType        (childType1, parentSize);
+
+                               layoutGroup->addChild(new BlockBasicTypeCase(m_testCtx, typeName, "", fullType, layoutFlags[layoutFlagNdx].flags, 0));
+
+                               if (glu::isDataTypeMatrix(type))
+                               {
+                                       for (int matFlagNdx = 0; matFlagNdx < DE_LENGTH_OF_ARRAY(matrixFlags); matFlagNdx++)
+                                               layoutGroup->addChild(new BlockBasicTypeCase(m_testCtx, (string(matrixFlags[matFlagNdx].name) + "_" + typeName).c_str(), "",
+                                                                                                                                        fullType,
+                                                                                                                                        layoutFlags[layoutFlagNdx].flags|matrixFlags[matFlagNdx].flags, 0));
+                               }
+                       }
+               }
+       }
+
+       // ubo.3_level_unsized_array
+       {
+               tcu::TestCaseGroup* nestedArrayGroup = new tcu::TestCaseGroup(m_testCtx, "3_level_unsized_array", "3-level nested array, top-level array unsized");
+               addChild(nestedArrayGroup);
+
+               for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+               {
+                       tcu::TestCaseGroup* layoutGroup = new tcu::TestCaseGroup(m_testCtx, layoutFlags[layoutFlagNdx].name, "");
+                       nestedArrayGroup->addChild(layoutGroup);
+
+                       for (int basicTypeNdx = 0; basicTypeNdx < DE_LENGTH_OF_ARRAY(basicTypes); basicTypeNdx++)
+                       {
+                               glu::DataType   type            = basicTypes[basicTypeNdx];
+                               const char*             typeName        = glu::getDataTypeName(type);
+                               const int               childSize0      = 2;
+                               const int               childSize1      = 4;
+                               const int               parentSize      = 3;
+                               const VarType   childType0      (VarType(type, glu::isDataTypeBoolOrBVec(type) ? glu::PRECISION_LAST : glu::PRECISION_HIGHP), childSize0);
+                               const VarType   childType1      (childType0, childSize1);
+
+                               layoutGroup->addChild(new BlockBasicUnsizedArrayCase(m_testCtx, typeName, "", childType1, parentSize, layoutFlags[layoutFlagNdx].flags));
+
+                               if (glu::isDataTypeMatrix(type))
+                               {
+                                       for (int matFlagNdx = 0; matFlagNdx < DE_LENGTH_OF_ARRAY(matrixFlags); matFlagNdx++)
+                                               layoutGroup->addChild(new BlockBasicUnsizedArrayCase(m_testCtx, (string(matrixFlags[matFlagNdx].name) + "_" + typeName).c_str(), "",
+                                                                                                                                                        childType1, parentSize,
+                                                                                                                                                        layoutFlags[layoutFlagNdx].flags|matrixFlags[matFlagNdx].flags));
+                               }
+                       }
+               }
+       }
+
+       // ubo.single_struct
+       {
+               tcu::TestCaseGroup* singleStructGroup = new tcu::TestCaseGroup(m_testCtx, "single_struct", "Single struct in uniform block");
+               addChild(singleStructGroup);
+
+               for (int modeNdx = 0; modeNdx < DE_LENGTH_OF_ARRAY(bufferModes); modeNdx++)
+               {
+                       tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[modeNdx].name, "");
+                       singleStructGroup->addChild(modeGroup);
+
+                       for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+                       {
+                               for (int isArray = 0; isArray < 2; isArray++)
+                               {
+                                       const deUint32  caseFlags       = layoutFlags[layoutFlagNdx].flags;
+                                       string                  caseName        = layoutFlags[layoutFlagNdx].name;
+
+                                       if (bufferModes[modeNdx].mode == SSBOLayoutCase::BUFFERMODE_SINGLE && isArray == 0)
+                                               continue; // Doesn't make sense to add this variant.
+
+                                       if (isArray)
+                                               caseName += "_instance_array";
+
+                                       modeGroup->addChild(new BlockSingleStructCase(m_testCtx, caseName.c_str(), "", caseFlags, bufferModes[modeNdx].mode, isArray ? 3 : 0));
+                               }
+                       }
+               }
+       }
+
+       // ubo.single_struct_array
+       {
+               tcu::TestCaseGroup* singleStructArrayGroup = new tcu::TestCaseGroup(m_testCtx, "single_struct_array", "Struct array in one uniform block");
+               addChild(singleStructArrayGroup);
+
+               for (int modeNdx = 0; modeNdx < DE_LENGTH_OF_ARRAY(bufferModes); modeNdx++)
+               {
+                       tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[modeNdx].name, "");
+                       singleStructArrayGroup->addChild(modeGroup);
+
+                       for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+                       {
+                               for (int isArray = 0; isArray < 2; isArray++)
+                               {
+                                       std::string     baseName        = layoutFlags[layoutFlagNdx].name;
+                                       deUint32        baseFlags       = layoutFlags[layoutFlagNdx].flags;
+
+                                       if (bufferModes[modeNdx].mode == SSBOLayoutCase::BUFFERMODE_SINGLE && isArray == 0)
+                                               continue; // Doesn't make sense to add this variant.
+
+                                       if (isArray)
+                                               baseName += "_instance_array";
+
+                                       modeGroup->addChild(new BlockSingleStructArrayCase(m_testCtx, baseName.c_str(), "", baseFlags, bufferModes[modeNdx].mode, isArray ? 3 : 0));
+                               }
+                       }
+               }
+       }
+
+       // ubo.single_nested_struct
+       {
+               tcu::TestCaseGroup* singleNestedStructGroup = new tcu::TestCaseGroup(m_testCtx, "single_nested_struct", "Nested struct in one uniform block");
+               addChild(singleNestedStructGroup);
+
+               for (int modeNdx = 0; modeNdx < DE_LENGTH_OF_ARRAY(bufferModes); modeNdx++)
+               {
+                       tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[modeNdx].name, "");
+                       singleNestedStructGroup->addChild(modeGroup);
+
+                       for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+                       {
+                               for (int isArray = 0; isArray < 2; isArray++)
+                               {
+                                       std::string     baseName        = layoutFlags[layoutFlagNdx].name;
+                                       deUint32        baseFlags       = layoutFlags[layoutFlagNdx].flags;
+
+                                       if (bufferModes[modeNdx].mode == SSBOLayoutCase::BUFFERMODE_SINGLE && isArray == 0)
+                                               continue; // Doesn't make sense to add this variant.
+
+                                       if (isArray)
+                                               baseName += "_instance_array";
+
+                                       modeGroup->addChild(new BlockSingleNestedStructCase(m_testCtx, baseName.c_str(), "", baseFlags, bufferModes[modeNdx].mode, isArray ? 3 : 0));
+                               }
+                       }
+               }
+       }
+
+       // ubo.single_nested_struct_array
+       {
+               tcu::TestCaseGroup* singleNestedStructArrayGroup = new tcu::TestCaseGroup(m_testCtx, "single_nested_struct_array", "Nested struct array in one uniform block");
+               addChild(singleNestedStructArrayGroup);
+
+               for (int modeNdx = 0; modeNdx < DE_LENGTH_OF_ARRAY(bufferModes); modeNdx++)
+               {
+                       tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[modeNdx].name, "");
+                       singleNestedStructArrayGroup->addChild(modeGroup);
+
+                       for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+                       {
+                               for (int isArray = 0; isArray < 2; isArray++)
+                               {
+                                       std::string     baseName        = layoutFlags[layoutFlagNdx].name;
+                                       deUint32        baseFlags       = layoutFlags[layoutFlagNdx].flags;
+
+                                       if (bufferModes[modeNdx].mode == SSBOLayoutCase::BUFFERMODE_SINGLE && isArray == 0)
+                                               continue; // Doesn't make sense to add this variant.
+
+                                       if (isArray)
+                                               baseName += "_instance_array";
+
+                                       modeGroup->addChild(new BlockSingleNestedStructArrayCase(m_testCtx, baseName.c_str(), "", baseFlags, bufferModes[modeNdx].mode, isArray ? 3 : 0));
+                               }
+                       }
+               }
+       }
+
+       // ubo.unsized_struct_array
+       {
+               tcu::TestCaseGroup* singleStructArrayGroup = new tcu::TestCaseGroup(m_testCtx, "unsized_struct_array", "Unsized struct array in one uniform block");
+               addChild(singleStructArrayGroup);
+
+               for (int modeNdx = 0; modeNdx < DE_LENGTH_OF_ARRAY(bufferModes); modeNdx++)
+               {
+                       tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[modeNdx].name, "");
+                       singleStructArrayGroup->addChild(modeGroup);
+
+                       for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+                       {
+                               for (int isArray = 0; isArray < 2; isArray++)
+                               {
+                                       std::string     baseName        = layoutFlags[layoutFlagNdx].name;
+                                       deUint32        baseFlags       = layoutFlags[layoutFlagNdx].flags;
+
+                                       if (bufferModes[modeNdx].mode == SSBOLayoutCase::BUFFERMODE_SINGLE && isArray == 0)
+                                               continue; // Doesn't make sense to add this variant.
+
+                                       if (isArray)
+                                               baseName += "_instance_array";
+
+                                       modeGroup->addChild(new BlockUnsizedStructArrayCase(m_testCtx, baseName.c_str(),        "", baseFlags, bufferModes[modeNdx].mode, isArray ? 3 : 0));
+                               }
+                       }
+               }
+       }
+
+       // ubo.2_level_unsized_struct_array
+       {
+               tcu::TestCaseGroup* structArrayGroup = new tcu::TestCaseGroup(m_testCtx, "2_level_unsized_struct_array", "Unsized 2-level struct array in one uniform block");
+               addChild(structArrayGroup);
+
+               for (int modeNdx = 0; modeNdx < DE_LENGTH_OF_ARRAY(bufferModes); modeNdx++)
+               {
+                       tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[modeNdx].name, "");
+                       structArrayGroup->addChild(modeGroup);
+
+                       for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+                       {
+                               for (int isArray = 0; isArray < 2; isArray++)
+                               {
+                                       std::string     baseName        = layoutFlags[layoutFlagNdx].name;
+                                       deUint32        baseFlags       = layoutFlags[layoutFlagNdx].flags;
+
+                                       if (bufferModes[modeNdx].mode == SSBOLayoutCase::BUFFERMODE_SINGLE && isArray == 0)
+                                               continue; // Doesn't make sense to add this variant.
+
+                                       if (isArray)
+                                               baseName += "_instance_array";
+
+                                       modeGroup->addChild(new Block2LevelUnsizedStructArrayCase(m_testCtx, baseName.c_str(),  "", baseFlags, bufferModes[modeNdx].mode, isArray ? 3 : 0));
+                               }
+                       }
+               }
+       }
+
+       // ubo.unsized_nested_struct_array
+       {
+               tcu::TestCaseGroup* singleNestedStructArrayGroup = new tcu::TestCaseGroup(m_testCtx, "unsized_nested_struct_array", "Unsized, nested struct array in one uniform block");
+               addChild(singleNestedStructArrayGroup);
+
+               for (int modeNdx = 0; modeNdx < DE_LENGTH_OF_ARRAY(bufferModes); modeNdx++)
+               {
+                       tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[modeNdx].name, "");
+                       singleNestedStructArrayGroup->addChild(modeGroup);
+
+                       for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+                       {
+                               for (int isArray = 0; isArray < 2; isArray++)
+                               {
+                                       std::string     baseName        = layoutFlags[layoutFlagNdx].name;
+                                       deUint32        baseFlags       = layoutFlags[layoutFlagNdx].flags;
+
+                                       if (bufferModes[modeNdx].mode == SSBOLayoutCase::BUFFERMODE_SINGLE && isArray == 0)
+                                               continue; // Doesn't make sense to add this variant.
+
+                                       if (isArray)
+                                               baseName += "_instance_array";
+
+                                       modeGroup->addChild(new BlockUnsizedNestedStructArrayCase(m_testCtx, baseName.c_str(), "", baseFlags, bufferModes[modeNdx].mode, isArray ? 3 : 0));
+                               }
+                       }
+               }
+       }
+
+       // ubo.instance_array_basic_type
+       {
+               tcu::TestCaseGroup* instanceArrayBasicTypeGroup = new tcu::TestCaseGroup(m_testCtx, "instance_array_basic_type", "Single basic variable in instance array");
+               addChild(instanceArrayBasicTypeGroup);
+
+               for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+               {
+                       tcu::TestCaseGroup* layoutGroup = new tcu::TestCaseGroup(m_testCtx, layoutFlags[layoutFlagNdx].name, "");
+                       instanceArrayBasicTypeGroup->addChild(layoutGroup);
+
+                       for (int basicTypeNdx = 0; basicTypeNdx < DE_LENGTH_OF_ARRAY(basicTypes); basicTypeNdx++)
+                       {
+                               glu::DataType   type                    = basicTypes[basicTypeNdx];
+                               const char*             typeName                = glu::getDataTypeName(type);
+                               const int               numInstances    = 3;
+
+                               layoutGroup->addChild(new BlockBasicTypeCase(m_testCtx, typeName, "",
+                                                                                                                        VarType(type, glu::isDataTypeBoolOrBVec(type) ? glu::PRECISION_LAST : glu::PRECISION_HIGHP),
+                                                                                                                        layoutFlags[layoutFlagNdx].flags, numInstances));
+
+                               if (glu::isDataTypeMatrix(type))
+                               {
+                                       for (int matFlagNdx = 0; matFlagNdx < DE_LENGTH_OF_ARRAY(matrixFlags); matFlagNdx++)
+                                               layoutGroup->addChild(new BlockBasicTypeCase(m_testCtx, (string(matrixFlags[matFlagNdx].name) + "_" + typeName).c_str(), "",
+                                                                                                                                        VarType(type, glu::PRECISION_HIGHP), layoutFlags[layoutFlagNdx].flags|matrixFlags[matFlagNdx].flags,
+                                                                                                                                        numInstances));
+                               }
+                       }
+               }
+       }
+
+       // ubo.multi_basic_types
+       {
+               tcu::TestCaseGroup* multiBasicTypesGroup = new tcu::TestCaseGroup(m_testCtx, "multi_basic_types", "Multiple buffers with basic types");
+               addChild(multiBasicTypesGroup);
+
+               for (int modeNdx = 0; modeNdx < DE_LENGTH_OF_ARRAY(bufferModes); modeNdx++)
+               {
+                       tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[modeNdx].name, "");
+                       multiBasicTypesGroup->addChild(modeGroup);
+
+                       for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+                       {
+                               for (int isArray = 0; isArray < 2; isArray++)
+                               {
+                                       std::string     baseName        = layoutFlags[layoutFlagNdx].name;
+                                       deUint32        baseFlags       = layoutFlags[layoutFlagNdx].flags;
+
+                                       if (isArray)
+                                               baseName += "_instance_array";
+
+                                       modeGroup->addChild(new BlockMultiBasicTypesCase(m_testCtx, baseName.c_str(), "", baseFlags, baseFlags, bufferModes[modeNdx].mode, isArray ? 3 : 0));
+                               }
+                       }
+               }
+       }
+
+       // ubo.multi_nested_struct
+       {
+               tcu::TestCaseGroup* multiNestedStructGroup = new tcu::TestCaseGroup(m_testCtx, "multi_nested_struct", "Multiple buffers with nested structs");
+               addChild(multiNestedStructGroup);
+
+               for (int modeNdx = 0; modeNdx < DE_LENGTH_OF_ARRAY(bufferModes); modeNdx++)
+               {
+                       tcu::TestCaseGroup* modeGroup = new tcu::TestCaseGroup(m_testCtx, bufferModes[modeNdx].name, "");
+                       multiNestedStructGroup->addChild(modeGroup);
+
+                       for (int layoutFlagNdx = 0; layoutFlagNdx < DE_LENGTH_OF_ARRAY(layoutFlags); layoutFlagNdx++)
+                       {
+                               for (int isArray = 0; isArray < 2; isArray++)
+                               {
+                                       std::string     baseName        = layoutFlags[layoutFlagNdx].name;
+                                       deUint32        baseFlags       = layoutFlags[layoutFlagNdx].flags;
+
+                                       if (isArray)
+                                               baseName += "_instance_array";
+
+                                       modeGroup->addChild(new BlockMultiNestedStructCase(m_testCtx, baseName.c_str(), "", baseFlags, baseFlags, bufferModes[modeNdx].mode, isArray ? 3 : 0));
+                               }
+                       }
+               }
+       }
+
+       // ubo.random
+       {
+               const deUint32  allLayouts              = FEATURE_PACKED_LAYOUT|FEATURE_SHARED_LAYOUT|FEATURE_STD140_LAYOUT;
+               const deUint32  allBasicTypes   = FEATURE_VECTORS|FEATURE_MATRICES;
+               const deUint32  unused                  = FEATURE_UNUSED_MEMBERS|FEATURE_UNUSED_VARS;
+               const deUint32  unsized                 = FEATURE_UNSIZED_ARRAYS;
+               const deUint32  matFlags                = FEATURE_MATRIX_LAYOUT;
+
+               tcu::TestCaseGroup* randomGroup = new tcu::TestCaseGroup(m_testCtx, "random", "Random Uniform Block cases");
+               addChild(randomGroup);
+
+               // Basic types.
+               createRandomCaseGroup(randomGroup, m_testCtx, "scalar_types",           "Scalar types only, per-block buffers",                         SSBOLayoutCase::BUFFERMODE_PER_BLOCK,   allLayouts|unused,                                                                                                                                                      25, 0);
+               createRandomCaseGroup(randomGroup, m_testCtx, "vector_types",           "Scalar and vector types only, per-block buffers",      SSBOLayoutCase::BUFFERMODE_PER_BLOCK,   allLayouts|unused|FEATURE_VECTORS,                                                                                                                      25, 25);
+               createRandomCaseGroup(randomGroup, m_testCtx, "basic_types",            "All basic types, per-block buffers",                           SSBOLayoutCase::BUFFERMODE_PER_BLOCK,   allLayouts|unused|allBasicTypes|matFlags,                                                                                                       25, 50);
+               createRandomCaseGroup(randomGroup, m_testCtx, "basic_arrays",           "Arrays, per-block buffers",                                            SSBOLayoutCase::BUFFERMODE_PER_BLOCK,   allLayouts|unused|allBasicTypes|matFlags|FEATURE_ARRAYS,                                                                        25, 50);
+               createRandomCaseGroup(randomGroup, m_testCtx, "unsized_arrays",         "Unsized arrays, per-block buffers",                            SSBOLayoutCase::BUFFERMODE_PER_BLOCK,   allLayouts|unused|allBasicTypes|matFlags|unsized|FEATURE_ARRAYS,                                                        25, 50);
+               createRandomCaseGroup(randomGroup, m_testCtx, "arrays_of_arrays",       "Arrays of arrays, per-block buffers",                          SSBOLayoutCase::BUFFERMODE_PER_BLOCK,   allLayouts|unused|allBasicTypes|matFlags|unsized|FEATURE_ARRAYS|FEATURE_ARRAYS_OF_ARRAYS,       25, 950);
+
+               createRandomCaseGroup(randomGroup, m_testCtx, "basic_instance_arrays",                                  "Basic instance arrays, per-block buffers",                             SSBOLayoutCase::BUFFERMODE_PER_BLOCK,   allLayouts|unused|allBasicTypes|matFlags|unsized|FEATURE_INSTANCE_ARRAYS,                                                                                                                       25, 75);
+               createRandomCaseGroup(randomGroup, m_testCtx, "nested_structs",                                                 "Nested structs, per-block buffers",                                    SSBOLayoutCase::BUFFERMODE_PER_BLOCK,   allLayouts|unused|allBasicTypes|matFlags|unsized|FEATURE_STRUCTS,                                                                                                                                       25, 100);
+               createRandomCaseGroup(randomGroup, m_testCtx, "nested_structs_arrays",                                  "Nested structs, arrays, per-block buffers",                    SSBOLayoutCase::BUFFERMODE_PER_BLOCK,   allLayouts|unused|allBasicTypes|matFlags|unsized|FEATURE_STRUCTS|FEATURE_ARRAYS|FEATURE_ARRAYS_OF_ARRAYS,                                                       25, 150);
+               createRandomCaseGroup(randomGroup, m_testCtx, "nested_structs_instance_arrays",                 "Nested structs, instance arrays, per-block buffers",   SSBOLayoutCase::BUFFERMODE_PER_BLOCK,   allLayouts|unused|allBasicTypes|matFlags|unsized|FEATURE_STRUCTS|FEATURE_INSTANCE_ARRAYS,                                                                                       25, 125);
+               createRandomCaseGroup(randomGroup, m_testCtx, "nested_structs_arrays_instance_arrays",  "Nested structs, instance arrays, per-block buffers",   SSBOLayoutCase::BUFFERMODE_PER_BLOCK,   allLayouts|unused|allBasicTypes|matFlags|unsized|FEATURE_STRUCTS|FEATURE_ARRAYS|FEATURE_ARRAYS_OF_ARRAYS|FEATURE_INSTANCE_ARRAYS,       25, 175);
+
+               createRandomCaseGroup(randomGroup, m_testCtx, "all_per_block_buffers",  "All random features, per-block buffers",       SSBOLayoutCase::BUFFERMODE_PER_BLOCK,   ~0u,    50, 200);
+               createRandomCaseGroup(randomGroup, m_testCtx, "all_shared_buffer",              "All random features, shared buffer",           SSBOLayoutCase::BUFFERMODE_SINGLE,              ~0u,    50, 250);
+       }
+}
+
+} // anonymous
 
 tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx)
 {
        de::MovePtr<tcu::TestCaseGroup> ssboTestGroup (new tcu::TestCaseGroup(testCtx, "ssbo", "Shader Storage Buffer Object Tests"));
 
-       ssboTestGroup->addChild(new DEMOCase(testCtx, "demo", "demo", VarType(glu::TYPE_FLOAT, glu::PRECISION_LAST), LAYOUT_STD140, 0));
+       ssboTestGroup->addChild(new SSBOLayoutTests(testCtx));
 
        return ssboTestGroup.release();
 }