Add resource access binding model tests.
authorJarkko Pöyry <jpoyry@google.com>
Wed, 29 Jul 2015 00:49:01 +0000 (17:49 -0700)
committerPyry Haulos <phaulos@google.com>
Thu, 10 Sep 2015 17:31:00 +0000 (10:31 -0700)
Change-Id: Icf7d9a37bca6c9151dcb417094f76787379e6c9f

external/vulkancts/modules/vulkan/CMakeLists.txt
external/vulkancts/modules/vulkan/binding_model/CMakeLists.txt [new file with mode: 0644]
external/vulkancts/modules/vulkan/binding_model/vktBindingModelTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/binding_model/vktBindingModelTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/binding_model/vktBindingShaderAccessTests.cpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/binding_model/vktBindingShaderAccessTests.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/vktTestPackage.cpp

index cc4b7a8..683b09c 100644 (file)
@@ -2,10 +2,12 @@
 
 add_subdirectory(api)
 add_subdirectory(pipeline)
+add_subdirectory(binding_model)
 
 include_directories(
        api
        pipeline
+       binding_model
        )
 
 set(DEQP_VK_COMMON_SRCS
@@ -25,6 +27,7 @@ set(DEQP_VK_COMMON_LIBS
        glutil
        deqp-vk-api
        deqp-vk-pipeline
+       deqp-vk-binding-model
        )
 
 if (DE_OS_IS_WIN32 OR DE_OS_IS_UNIX OR DE_OS_IS_OSX)
diff --git a/external/vulkancts/modules/vulkan/binding_model/CMakeLists.txt b/external/vulkancts/modules/vulkan/binding_model/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fe3e727
--- /dev/null
@@ -0,0 +1,18 @@
+# dEQP-VK.binding_model
+
+include_directories(..)
+
+set(DEQP_VK_BINDING_MODEL_SRCS
+       vktBindingModelTests.cpp
+       vktBindingModelTests.hpp
+       vktBindingShaderAccessTests.cpp
+       vktBindingShaderAccessTests.hpp
+       )
+
+set(DEQP_VK_BINDING_MODEL_LIBS
+       tcutil
+       vkutil
+       )
+
+add_library(deqp-vk-binding-model STATIC ${DEQP_VK_BINDING_MODEL_SRCS})
+target_link_libraries(deqp-vk-binding-model ${DEQP_VK_BINDING_MODEL_LIBS})
diff --git a/external/vulkancts/modules/vulkan/binding_model/vktBindingModelTests.cpp b/external/vulkancts/modules/vulkan/binding_model/vktBindingModelTests.cpp
new file mode 100644 (file)
index 0000000..fbc6049
--- /dev/null
@@ -0,0 +1,60 @@
+/*-------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be
+ * included in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by
+ * Khronos, at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Binding Model tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktBindingModelTests.hpp"
+
+#include "vktBindingShaderAccessTests.hpp"
+
+#include "deUniquePtr.hpp"
+
+namespace vkt
+{
+namespace BindingModel
+{
+
+tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx)
+{
+       de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "binding_model", "Resource binding tests"));
+
+       group->addChild(createShaderAccessTests(testCtx));
+
+       // \todo [2015-07-30 jarkko] .change_binding.{between_renderpasses, within_pass}
+       // \todo [2015-07-30 jarkko] .descriptor_set_chain
+       // \todo [2015-07-30 jarkko] .update_descriptor_set
+
+       return group.release();
+}
+
+} // BindingModel
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/binding_model/vktBindingModelTests.hpp b/external/vulkancts/modules/vulkan/binding_model/vktBindingModelTests.hpp
new file mode 100644 (file)
index 0000000..09e7b5b
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef _VKTBINDINGMODELTESTS_HPP
+#define _VKTBINDINGMODELTESTS_HPP
+/*-------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be
+ * included in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by
+ * Khronos, at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Binding Model tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace BindingModel
+{
+
+tcu::TestCaseGroup* createTests (tcu::TestContext& testCtx);
+
+} // BindingModel
+} // vkt
+
+#endif // _VKTBINDINGMODELTESTS_HPP
diff --git a/external/vulkancts/modules/vulkan/binding_model/vktBindingShaderAccessTests.cpp b/external/vulkancts/modules/vulkan/binding_model/vktBindingShaderAccessTests.cpp
new file mode 100644 (file)
index 0000000..6b28952
--- /dev/null
@@ -0,0 +1,6529 @@
+/*-------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be
+ * included in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by
+ * Khronos, at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Binding shader access tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktBindingShaderAccessTests.hpp"
+
+#include "vktTestCase.hpp"
+
+#include "vkDefs.hpp"
+#include "vkRef.hpp"
+#include "vkRefUtil.hpp"
+#include "vkPlatform.hpp"
+#include "vkPrograms.hpp"
+#include "vkMemUtil.hpp"
+#include "vkBuilderUtil.hpp"
+#include "vkQueryUtil.hpp"
+
+#include "tcuVector.hpp"
+#include "tcuVectorUtil.hpp"
+#include "tcuTexture.hpp"
+#include "tcuTextureUtil.hpp"
+#include "tcuResultCollector.hpp"
+#include "tcuTestLog.hpp"
+#include "tcuRGBA.hpp"
+#include "tcuSurface.hpp"
+#include "tcuImageCompare.hpp"
+
+#include "deUniquePtr.hpp"
+#include "deSharedPtr.hpp"
+#include "deStringUtil.hpp"
+#include "deArrayUtil.hpp"
+
+#include "qpInfo.h"
+
+namespace vkt
+{
+namespace BindingModel
+{
+namespace
+{
+
+enum ResourceFlag
+{
+       RESOURCE_FLAG_IMMUTABLE_SAMPLER = (1u << 0u),
+
+       RESOURCE_FLAG_LAST                              = (1u << 1u)
+};
+
+static const char* const s_quadrantGenVertexPosSource =        "       highp int quadPhase = gl_VertexID % 6;\n"
+                                                                                                               "       highp int quadXcoord = int(quadPhase == 1 || quadPhase == 4 || quadPhase == 5);\n"
+                                                                                                               "       highp int quadYcoord = int(quadPhase == 2 || quadPhase == 3 || quadPhase == 5);\n"
+                                                                                                               "       highp int quadOriginX = (gl_VertexID / 6) % 2;\n"
+                                                                                                               "       highp int quadOriginY = (gl_VertexID / 6) / 2;\n"
+                                                                                                               "       quadrant_id = gl_VertexID / 6;\n"
+                                                                                                               "       result_position = vec4(float(quadOriginX + quadXcoord - 1), float(quadOriginY + quadYcoord - 1), 0.0, 1.0);\n";
+
+bool isUniformDescriptorType (vk::VkDescriptorType type)
+{
+       return type == vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
+                  type == vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ||
+                  type == vk::VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
+}
+
+bool isDynamicDescriptorType (vk::VkDescriptorType type)
+{
+       return type == vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC || type == vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
+}
+
+vk::VkFormat mapToVkTextureFormat (const tcu::TextureFormat& format)
+{
+       if (format == tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8))
+               return vk::VK_FORMAT_R8G8B8A8_UNORM;
+
+       DE_FATAL("Not implemented");
+       return vk::VK_FORMAT_UNDEFINED;
+}
+
+vk::VkImageType viewTypeToImageType (vk::VkImageViewType type)
+{
+       switch (type)
+       {
+               case vk::VK_IMAGE_VIEW_TYPE_1D:
+               case vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY:   return vk::VK_IMAGE_TYPE_1D;
+               case vk::VK_IMAGE_VIEW_TYPE_2D:
+               case vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY:   return vk::VK_IMAGE_TYPE_2D;
+               case vk::VK_IMAGE_VIEW_TYPE_3D:                 return vk::VK_IMAGE_TYPE_3D;
+               case vk::VK_IMAGE_VIEW_TYPE_CUBE:
+               case vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY: return vk::VK_IMAGE_TYPE_2D;
+
+               default:
+                       DE_FATAL("Impossible");
+                       return (vk::VkImageType)0;
+       }
+}
+
+vk::VkTexFilter mapMagFilterToVkTexFilter (tcu::Sampler::FilterMode mode)
+{
+       switch (mode)
+       {
+               case tcu::Sampler::NEAREST:     return vk::VK_TEX_FILTER_NEAREST;
+               case tcu::Sampler::LINEAR:      return vk::VK_TEX_FILTER_LINEAR;
+
+               default:
+                       DE_FATAL("Illegal filter mode");
+                       return (vk::VkTexFilter)0;
+       }
+}
+
+vk::VkTexFilter mapMinFilterToVkTexFilter (tcu::Sampler::FilterMode mode)
+{
+       switch (mode)
+       {
+               case tcu::Sampler::NEAREST:                                     return vk::VK_TEX_FILTER_NEAREST;
+               case tcu::Sampler::LINEAR:                                      return vk::VK_TEX_FILTER_LINEAR;
+               case tcu::Sampler::NEAREST_MIPMAP_NEAREST:      return vk::VK_TEX_FILTER_NEAREST;
+               case tcu::Sampler::LINEAR_MIPMAP_NEAREST:       return vk::VK_TEX_FILTER_LINEAR;
+               case tcu::Sampler::NEAREST_MIPMAP_LINEAR:       return vk::VK_TEX_FILTER_NEAREST;
+               case tcu::Sampler::LINEAR_MIPMAP_LINEAR:        return vk::VK_TEX_FILTER_LINEAR;
+
+               default:
+                       DE_FATAL("Illegal filter mode");
+                       return (vk::VkTexFilter)0;
+       }
+}
+
+vk::VkTexMipmapMode mapMinFilterToVkTexMipmapMode (tcu::Sampler::FilterMode mode)
+{
+       switch (mode)
+       {
+               case tcu::Sampler::NEAREST:                                     return vk::VK_TEX_MIPMAP_MODE_BASE;
+               case tcu::Sampler::LINEAR:                                      return vk::VK_TEX_MIPMAP_MODE_BASE;
+               case tcu::Sampler::NEAREST_MIPMAP_NEAREST:      return vk::VK_TEX_MIPMAP_MODE_NEAREST;
+               case tcu::Sampler::LINEAR_MIPMAP_NEAREST:       return vk::VK_TEX_MIPMAP_MODE_NEAREST;
+               case tcu::Sampler::NEAREST_MIPMAP_LINEAR:       return vk::VK_TEX_MIPMAP_MODE_LINEAR;
+               case tcu::Sampler::LINEAR_MIPMAP_LINEAR:        return vk::VK_TEX_MIPMAP_MODE_LINEAR;
+
+               default:
+                       DE_FATAL("Illegal filter mode");
+                       return (vk::VkTexMipmapMode)0;
+       }
+}
+
+vk::VkTexAddress mapToVkTexAddress (tcu::Sampler::WrapMode mode)
+{
+       switch (mode)
+       {
+               case tcu::Sampler::CLAMP_TO_EDGE:               return vk::VK_TEX_ADDRESS_CLAMP;
+               case tcu::Sampler::CLAMP_TO_BORDER:             return vk::VK_TEX_ADDRESS_CLAMP_BORDER;
+               case tcu::Sampler::REPEAT_GL:                   return vk::VK_TEX_ADDRESS_WRAP;
+               case tcu::Sampler::MIRRORED_REPEAT_GL:  return vk::VK_TEX_ADDRESS_MIRROR;
+
+               default:
+                       DE_FATAL("Illegal wrap mode");
+                       return (vk::VkTexAddress)0;
+       }
+}
+
+vk::VkCompareOp mapToVkCompareOp (tcu::Sampler::CompareMode mode)
+{
+       switch (mode)
+       {
+               case tcu::Sampler::COMPAREMODE_LESS:                            return vk::VK_COMPARE_OP_LESS;
+               case tcu::Sampler::COMPAREMODE_LESS_OR_EQUAL:           return vk::VK_COMPARE_OP_LESS_EQUAL;
+               case tcu::Sampler::COMPAREMODE_GREATER:                         return vk::VK_COMPARE_OP_GREATER;
+               case tcu::Sampler::COMPAREMODE_GREATER_OR_EQUAL:        return vk::VK_COMPARE_OP_GREATER_EQUAL;
+               case tcu::Sampler::COMPAREMODE_EQUAL:                           return vk::VK_COMPARE_OP_EQUAL;
+               case tcu::Sampler::COMPAREMODE_NOT_EQUAL:                       return vk::VK_COMPARE_OP_NOT_EQUAL;
+               case tcu::Sampler::COMPAREMODE_ALWAYS:                          return vk::VK_COMPARE_OP_ALWAYS;
+               case tcu::Sampler::COMPAREMODE_NEVER:                           return vk::VK_COMPARE_OP_NEVER;
+
+               default:
+                       DE_FATAL("Illegal compare mode");
+                       return (vk::VkCompareOp)0;
+       }
+}
+
+deUint32 getTextureLevelPyramidDataSize (const tcu::TextureLevelPyramid& srcImage)
+{
+       deUint32 dataSize = 0;
+       for (int level = 0; level < srcImage.getNumLevels(); ++level)
+       {
+               const tcu::ConstPixelBufferAccess srcAccess = srcImage.getLevel(level);
+
+               // tightly packed
+               DE_ASSERT(srcAccess.getFormat().getPixelSize() == srcAccess.getPixelPitch());
+
+               dataSize += srcAccess.getWidth() * srcAccess.getHeight() * srcAccess.getDepth() * srcAccess.getFormat().getPixelSize();
+       }
+       return dataSize;
+}
+
+void writeTextureLevelPyramidData (void* dst, deUint32 dstLen, const tcu::TextureLevelPyramid& srcImage, vk::VkImageViewType viewType, std::vector<vk::VkBufferImageCopy>* copySlices)
+{
+       // \note cube is copied face-by-face
+       const deUint32  arraySize       = (viewType == vk::VK_IMAGE_VIEW_TYPE_1D || viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY)                ? (srcImage.getLevel(0).getHeight()) :
+                                                                 (viewType == vk::VK_IMAGE_VIEW_TYPE_2D || viewType == vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY)                ? (srcImage.getLevel(0).getDepth()) :
+                                                                 (viewType == vk::VK_IMAGE_VIEW_TYPE_3D)                                                                                                               ? (1) :
+                                                                 (viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE || viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)    ? (srcImage.getLevel(0).getDepth()) :
+                                                                 ((deUint32)0);
+       deUint32                levelOffset     = 0;
+
+       DE_ASSERT(arraySize != 0);
+
+       for (int level = 0; level < srcImage.getNumLevels(); ++level)
+       {
+               const tcu::ConstPixelBufferAccess       srcAccess               = srcImage.getLevel(level);
+               const tcu::PixelBufferAccess            dstAccess               (srcAccess.getFormat(), srcAccess.getSize(), srcAccess.getPitch(), (deUint8*)dst + levelOffset);
+               const deUint32                                          dataSize                = srcAccess.getWidth() * srcAccess.getHeight() * srcAccess.getDepth() * srcAccess.getFormat().getPixelSize();
+               const deUint32                                          sliceDataSize   = dataSize / arraySize;
+               const deInt32                                           sliceHeight             = (viewType == vk::VK_IMAGE_VIEW_TYPE_1D || viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY) ? (1) : (srcAccess.getHeight());
+               const deInt32                                           sliceDepth              = (viewType == vk::VK_IMAGE_VIEW_TYPE_3D) ? (srcAccess.getDepth()) : (1);
+               const tcu::IVec3                                        sliceSize               (srcAccess.getWidth(), sliceHeight, sliceDepth);
+
+               // tightly packed
+               DE_ASSERT(srcAccess.getFormat().getPixelSize() == srcAccess.getPixelPitch());
+
+               for (int sliceNdx = 0; sliceNdx < (int)arraySize; ++sliceNdx)
+               {
+                       const vk::VkBufferImageCopy copySlice =
+                       {
+                               (vk::VkDeviceSize)levelOffset + sliceNdx * sliceDataSize,       // bufferOffset
+                               (deUint32)sliceSize.x(),                                                                        // bufferRowLength
+                               (deUint32)sliceSize.y(),                                                                        // bufferImageHeight
+                               {
+                                       vk::VK_IMAGE_ASPECT_COLOR,                      // aspect
+                                       (deUint32)level,                                        // mipLevel
+                                       (deUint32)sliceNdx                                      // arraySlice
+                               },                                                                                                                      // imageSubresource
+                               {
+                                       0,
+                                       0,
+                                       0,
+                               },                                                                                                                      // imageOffset
+                               {
+                                       sliceSize.x(),
+                                       sliceSize.y(),
+                                       sliceSize.z(),
+                               }                                                                                                                       // imageExtent
+                       };
+                       copySlices->push_back(copySlice);
+               }
+
+               DE_ASSERT(arraySize * sliceDataSize == dataSize);
+
+               tcu::copy(dstAccess, srcAccess);
+               levelOffset += dataSize;
+       }
+
+       DE_ASSERT(dstLen == levelOffset);
+       DE_UNREF(dstLen);
+}
+
+de::MovePtr<vk::Allocation> allocateAndBindObjectMemory (const vk::DeviceInterface& vki, vk::VkDevice device, vk::Allocator& allocator, vk::VkBuffer buffer, vk::MemoryRequirement requirement)
+{
+       const vk::VkMemoryRequirements  requirements    = vk::getBufferMemoryRequirements(vki, device, buffer);
+       de::MovePtr<vk::Allocation>             allocation              = allocator.allocate(requirements, requirement);
+
+       VK_CHECK(vki.bindBufferMemory(device, buffer, allocation->getMemory(), allocation->getOffset()));
+       return allocation;
+}
+
+de::MovePtr<vk::Allocation> allocateAndBindObjectMemory (const vk::DeviceInterface& vki, vk::VkDevice device, vk::Allocator& allocator, vk::VkImage image, vk::MemoryRequirement requirement)
+{
+       const vk::VkMemoryRequirements  requirements    = vk::getImageMemoryRequirements(vki, device, image);
+       de::MovePtr<vk::Allocation>             allocation              = allocator.allocate(requirements, requirement);
+
+       VK_CHECK(vki.bindImageMemory(device, image, allocation->getMemory(), allocation->getOffset()));
+       return allocation;
+}
+
+vk::VkDescriptorInfo createDescriptorInfo (vk::VkBufferView bufferView)
+{
+       const vk::VkDescriptorInfo resultInfo =
+       {
+               bufferView,                             // bufferView
+               0,                                              // sampler
+               0,                                              // imageView
+               0,                                              // attachmentView
+               (vk::VkImageLayout)0,   // imageLayout
+       };
+       return resultInfo;
+}
+
+vk::VkDescriptorInfo createDescriptorInfo (vk::VkSampler sampler)
+{
+       const vk::VkDescriptorInfo resultInfo =
+       {
+               0,                                              // bufferView
+               sampler,                                // sampler
+               0,                                              // imageView
+               0,                                              // attachmentView
+               (vk::VkImageLayout)0,   // imageLayout
+       };
+       return resultInfo;
+}
+
+vk::VkDescriptorInfo createDescriptorInfo (vk::VkImageView imageView, vk::VkImageLayout layout)
+{
+       const vk::VkDescriptorInfo resultInfo =
+       {
+               0,                                              // bufferView
+               0,                                              // sampler
+               imageView,                              // imageView
+               0,                                              // attachmentView
+               layout,                                 // imageLayout
+       };
+       return resultInfo;
+}
+
+vk::VkDescriptorInfo createDescriptorInfo (vk::VkSampler sampler, vk::VkImageView imageView, vk::VkImageLayout layout)
+{
+       const vk::VkDescriptorInfo resultInfo =
+       {
+               0,                                              // bufferView
+               sampler,                                // sampler
+               imageView,                              // imageView
+               0,                                              // attachmentView
+               layout,                                 // imageLayout
+       };
+       return resultInfo;
+}
+
+vk::VkClearValue createClearValueColor (const tcu::Vec4& color)
+{
+       vk::VkClearValue retVal;
+
+       retVal.color.f32[0] = color.x();
+       retVal.color.f32[1] = color.y();
+       retVal.color.f32[2] = color.z();
+       retVal.color.f32[3] = color.w();
+
+       return retVal;
+};
+
+void drawQuadrantReferenceResult (const tcu::PixelBufferAccess& dst, const tcu::Vec4& c1, const tcu::Vec4& c2, const tcu::Vec4& c3, const tcu::Vec4& c4)
+{
+       tcu::clear(tcu::getSubregion(dst, 0,                                    0,                                              dst.getWidth() / 2,                                             dst.getHeight() / 2),                                   c1);
+       tcu::clear(tcu::getSubregion(dst, dst.getWidth() / 2,   0,                                              dst.getWidth() - dst.getWidth() / 2,    dst.getHeight() / 2),                                   c2);
+       tcu::clear(tcu::getSubregion(dst, 0,                                    dst.getHeight() / 2,    dst.getWidth() / 2,                                             dst.getHeight() - dst.getHeight() / 2), c3);
+       tcu::clear(tcu::getSubregion(dst, dst.getWidth() / 2,   dst.getHeight() / 2,    dst.getWidth() - dst.getWidth() / 2,    dst.getHeight() - dst.getHeight() / 2), c4);
+}
+
+class SingleTargetRenderInstance : public vkt::TestInstance
+{
+public:
+                                                                                       SingleTargetRenderInstance      (Context&                       context,
+                                                                                                                                                const tcu::UVec2&      size);
+
+private:
+       static vk::Move<vk::VkImage>                    createColorAttachment           (const vk::DeviceInterface&             vki,
+                                                                                                                                                vk::VkDevice                                   device,
+                                                                                                                                                vk::Allocator&                                 allocator,
+                                                                                                                                                const tcu::TextureFormat&              format,
+                                                                                                                                                const tcu::UVec2&                              size,
+                                                                                                                                                de::MovePtr<vk::Allocation>*   outAllocation);
+
+       static vk::Move<vk::VkAttachmentView>   createColorAttachmentView       (const vk::DeviceInterface&     vki,
+                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                const tcu::TextureFormat&      format,
+                                                                                                                                                vk::VkImage                            image);
+
+       static vk::Move<vk::VkRenderPass>               createRenderPass                        (const vk::DeviceInterface&     vki,
+                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                const tcu::TextureFormat&      format);
+
+       static vk::Move<vk::VkFramebuffer>              createFramebuffer                       (const vk::DeviceInterface&     vki,
+                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                vk::VkRenderPass                       renderpass,
+                                                                                                                                                vk::VkAttachmentView           colorAttachmentView,
+                                                                                                                                                const tcu::UVec2&                      size);
+
+       static vk::Move<vk::VkCmdPool>                  createCommandPool                       (const vk::DeviceInterface&     vki,
+                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                deUint32                                       queueFamilyIndex);
+
+       virtual void                                                    logTestPlan                                     (void) const = 0;
+       virtual void                                                    renderToTarget                          (void) = 0;
+       virtual tcu::TestStatus                                 verifyResultImage                       (const tcu::ConstPixelBufferAccess& result) const = 0;
+
+       void                                                                    readRenderTarget                        (tcu::TextureLevel& dst);
+       tcu::TestStatus                                                 iterate                                         (void);
+
+protected:
+       const tcu::TextureFormat                                m_targetFormat;
+       const tcu::UVec2                                                m_targetSize;
+
+       const vk::DeviceInterface&                              m_vki;
+       const vk::VkDevice                                              m_device;
+       const vk::VkQueue                                               m_queue;
+       const deUint32                                                  m_queueFamilyIndex;
+       vk::Allocator&                                                  m_allocator;
+       de::MovePtr<vk::Allocation>                             m_colorAttachmentMemory;
+       const vk::Unique<vk::VkImage>                   m_colorAttachmentImage;
+       const vk::Unique<vk::VkAttachmentView>  m_colorAttachmentView;
+       const vk::Unique<vk::VkRenderPass>              m_renderPass;
+       const vk::Unique<vk::VkFramebuffer>             m_framebuffer;
+       const vk::Unique<vk::VkCmdPool>                 m_cmdPool;
+
+       bool                                                                    m_firstIteration;
+};
+
+SingleTargetRenderInstance::SingleTargetRenderInstance (Context&                       context,
+                                                                                                               const tcu::UVec2&       size)
+       : vkt::TestInstance                     (context)
+       , m_targetFormat                        (tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8)
+       , m_targetSize                          (size)
+       , m_vki                                         (context.getDeviceInterface())
+       , m_device                                      (context.getDevice())
+       , m_queue                                       (context.getUniversalQueue())
+       , m_queueFamilyIndex            (context.getUniversalQueueFamilyIndex())
+       , m_allocator                           (context.getDefaultAllocator())
+       , m_colorAttachmentMemory       (DE_NULL)
+       , m_colorAttachmentImage        (createColorAttachment(m_vki, m_device, m_allocator, m_targetFormat, m_targetSize, &m_colorAttachmentMemory))
+       , m_colorAttachmentView         (createColorAttachmentView(m_vki, m_device, m_targetFormat, *m_colorAttachmentImage))
+       , m_renderPass                          (createRenderPass(m_vki, m_device, m_targetFormat))
+       , m_framebuffer                         (createFramebuffer(m_vki, m_device, *m_renderPass, *m_colorAttachmentView, m_targetSize))
+       , m_cmdPool                                     (createCommandPool(m_vki, m_device, context.getUniversalQueueFamilyIndex()))
+       , m_firstIteration                      (true)
+{
+}
+
+vk::Move<vk::VkImage> SingleTargetRenderInstance::createColorAttachment (const vk::DeviceInterface&            vki,
+                                                                                                                                                vk::VkDevice                                   device,
+                                                                                                                                                vk::Allocator&                                 allocator,
+                                                                                                                                                const tcu::TextureFormat&              format,
+                                                                                                                                                const tcu::UVec2&                              size,
+                                                                                                                                                de::MovePtr<vk::Allocation>*   outAllocation)
+{
+       const vk::VkImageCreateInfo     imageInfo       =
+       {
+               vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+               DE_NULL,
+               vk::VK_IMAGE_TYPE_2D,                                                   // imageType
+               mapToVkTextureFormat(format),                                   // format
+               { (deInt32)size.x(), (deInt32)size.y(), 1 },    // extent
+               1,                                                                                              // mipLevels
+               1,                                                                                              // arraySize
+               1,                                                                                              // samples
+               vk::VK_IMAGE_TILING_OPTIMAL,                                    // tiling
+               vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | vk::VK_IMAGE_USAGE_TRANSFER_SOURCE_BIT,       // usage
+               0,                                                                                              // flags
+               vk::VK_SHARING_MODE_EXCLUSIVE,                                  // sharingMode
+               0u,                                                                                             // queueFamilyCount
+               DE_NULL,                                                                                // pQueueFamilyIndices
+       };
+
+       vk::Move<vk::VkImage>           image           (vk::createImage(vki, device, &imageInfo));
+       de::MovePtr<vk::Allocation>     allocation      (allocateAndBindObjectMemory(vki, device, allocator, *image, vk::MemoryRequirement::Any));
+
+       *outAllocation = allocation;
+       return image;
+}
+
+vk::Move<vk::VkAttachmentView> SingleTargetRenderInstance::createColorAttachmentView (const vk::DeviceInterface&       vki,
+                                                                                                                                                                         vk::VkDevice                                  device,
+                                                                                                                                                                         const tcu::TextureFormat&             format,
+                                                                                                                                                                         vk::VkImage                                   image)
+{
+       const vk::VkAttachmentViewCreateInfo createInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_ATTACHMENT_VIEW_CREATE_INFO,
+               DE_NULL,
+               image,                                                  // image
+               mapToVkTextureFormat(format),   // format
+               0u,                                                             // mipLevel
+               0u,                                                             // baseArraySlice
+               1u,                                                             // arraySize
+               0u,                                                             // flags
+       };
+
+       return vk::createAttachmentView(vki, device, &createInfo);
+}
+
+vk::Move<vk::VkRenderPass> SingleTargetRenderInstance::createRenderPass (const vk::DeviceInterface&            vki,
+                                                                                                                                                vk::VkDevice                                   device,
+                                                                                                                                                const tcu::TextureFormat&              format)
+{
+       const vk::VkAttachmentDescription       attachmentDescription   =
+       {
+               vk::VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION,
+               DE_NULL,
+               mapToVkTextureFormat(format),                                   // format
+               1u,                                                                                             // samples
+               vk::VK_ATTACHMENT_LOAD_OP_CLEAR,                                // loadOp
+               vk::VK_ATTACHMENT_STORE_OP_STORE,                               // storeOp
+               vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE,                    // stencilLoadOp
+               vk::VK_ATTACHMENT_STORE_OP_DONT_CARE,                   // stencilStoreOp
+               vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,   // initialLayout
+               vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,   // finalLayout
+       };
+       const vk::VkAttachmentReference         colorAttachment                 =
+       {
+               0u,                                                                                             // attachment
+               vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL    // layout
+       };
+       const vk::VkAttachmentReference         depthStencilAttachment  =
+       {
+               vk::VK_NO_ATTACHMENT,                                                   // attachment
+               vk::VK_IMAGE_LAYOUT_UNDEFINED                                   // layout
+       };
+       const vk::VkSubpassDescription          subpass                                 =
+       {
+               vk::VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION,
+               DE_NULL,
+               vk::VK_PIPELINE_BIND_POINT_GRAPHICS,                    // pipelineBindPoint
+               0u,                                                                                             // flags
+               0u,                                                                                             // inputCount
+               DE_NULL,                                                                                // inputAttachments
+               1u,                                                                                             // colorCount
+               &colorAttachment,                                                               // colorAttachments
+               DE_NULL,                                                                                // resolveAttachments
+               depthStencilAttachment,                                                 // depthStencilAttachment
+               0u,                                                                                             // preserveCount
+               DE_NULL                                                                                 // preserveAttachments
+       };
+       const vk::VkRenderPassCreateInfo        renderPassCreateInfo    =
+       {
+               vk::VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
+               DE_NULL,
+               1u,                                                                                             // attachmentCount
+               &attachmentDescription,                                                 // pAttachments
+               1u,                                                                                             // subpassCount
+               &subpass,                                                                               // pSubpasses
+               0u,                                                                                             // dependencyCount
+               DE_NULL,                                                                                // pDependencies
+       };
+
+       return vk::createRenderPass(vki, device, &renderPassCreateInfo);
+}
+
+vk::Move<vk::VkFramebuffer> SingleTargetRenderInstance::createFramebuffer (const vk::DeviceInterface&  vki,
+                                                                                                                                                  vk::VkDevice                                 device,
+                                                                                                                                                  vk::VkRenderPass                             renderpass,
+                                                                                                                                                  vk::VkAttachmentView                 colorAttachmentView,
+                                                                                                                                                  const tcu::UVec2&                    size)
+{
+       const vk::VkAttachmentBindInfo          colorAttachment                 =
+       {
+               colorAttachmentView,                                                    // view;
+               vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,   // layout;
+       };
+       const vk::VkFramebufferCreateInfo       framebufferCreateInfo   =
+       {
+               vk::VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
+               DE_NULL,
+               renderpass,                             // renderPass
+               1u,                                             // attachmentCount
+               &colorAttachment,               // pAttachments
+               size.x(),                               // width
+               size.y(),                               // height
+               1,                                              // layers
+       };
+
+       return vk::createFramebuffer(vki, device, &framebufferCreateInfo);
+}
+
+vk::Move<vk::VkCmdPool> SingleTargetRenderInstance::createCommandPool (const vk::DeviceInterface&      vki,
+                                                                                                                                          vk::VkDevice                                 device,
+                                                                                                                                          deUint32                                             queueFamilyIndex)
+{
+       const vk::VkCmdPoolCreateInfo createInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_POOL_CREATE_INFO,
+               DE_NULL,
+               queueFamilyIndex,                                               // queueFamilyIndex
+               vk::VK_CMD_POOL_CREATE_TRANSIENT_BIT,   // flags
+       };
+       return vk::createCommandPool(vki, device, &createInfo);
+}
+
+void SingleTargetRenderInstance::readRenderTarget (tcu::TextureLevel& dst)
+{
+       const deUint64                                          pixelDataSize                           = (deUint64)(m_targetSize.x() * m_targetSize.y() * m_targetFormat.getPixelSize());
+       const vk::VkBufferCreateInfo            bufferCreateInfo                        =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+               DE_NULL,
+               pixelDataSize,                                                                  // size
+               vk::VK_BUFFER_USAGE_TRANSFER_DESTINATION_BIT,   // usage
+               0u,                                                                                             // flags
+               vk::VK_SHARING_MODE_EXCLUSIVE,                                  // sharingMode
+               0u,                                                                                             // queueFamilyCount
+               DE_NULL,                                                                                // pQueueFamilyIndices
+       };
+       const vk::Unique<vk::VkBuffer>          buffer                                          (vk::createBuffer(m_vki, m_device, &bufferCreateInfo));
+       const vk::VkImageSubresourceRange       fullSubrange                            =
+       {
+               vk::VK_IMAGE_ASPECT_COLOR,                                              // aspect
+               0u,                                                                                             // baseMipLevel
+               1u,                                                                                             // mipLevels
+               0u,                                                                                             // baseArraySlice
+               1u,                                                                                             // arraySize
+       };
+       const vk::VkImageMemoryBarrier          imageBarrier                            =
+       {
+               vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+               DE_NULL,
+               vk::VK_MEMORY_OUTPUT_COLOR_ATTACHMENT_BIT,              // outputMask
+               vk::VK_MEMORY_INPUT_TRANSFER_BIT,                               // inputMask
+               vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,   // oldLayout
+               vk::VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL,    // newLayout
+               vk::VK_QUEUE_FAMILY_IGNORED,                                    // srcQueueFamilyIndex
+               vk::VK_QUEUE_FAMILY_IGNORED,                                    // destQueueFamilyIndex
+               *m_colorAttachmentImage,                                                // image
+               fullSubrange,                                                                   // subresourceRange
+       };
+       const vk::VkBufferMemoryBarrier         memoryBarrier                           =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+               DE_NULL,
+               vk::VK_MEMORY_OUTPUT_TRANSFER_BIT,                              // outputMask
+               vk::VK_MEMORY_INPUT_HOST_READ_BIT,                              // inputMask
+               vk::VK_QUEUE_FAMILY_IGNORED,                                    // srcQueueFamilyIndex
+               vk::VK_QUEUE_FAMILY_IGNORED,                                    // destQueueFamilyIndex
+               *buffer,                                                                                // buffer
+               0u,                                                                                             // offset
+               (vk::VkDeviceSize)pixelDataSize                                 // size
+       };
+       const vk::VkCmdBufferCreateInfo         cmdBufCreateInfo                        =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO,
+               DE_NULL,
+               *m_cmdPool,                                                     // cmdPool
+               vk::VK_CMD_BUFFER_LEVEL_PRIMARY,        // level
+               0u,                                                                     // flags
+       };
+       const vk::VkFenceCreateInfo                     fenceCreateInfo                         =
+       {
+               vk::VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+               DE_NULL,
+               0u,                                                                                             // flags
+       };
+       const vk::VkCmdBufferBeginInfo          cmdBufBeginInfo                         =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,
+               DE_NULL,
+               vk::VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | vk::VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT,    // flags
+               (vk::VkRenderPass)0u,                                                                                                                                                   // renderPass
+               (vk::VkFramebuffer)0u,                                                                                                                                                  // framebuffer
+       };
+       const vk::VkImageSubresource            firstSlice                                      =
+       {
+               vk::VK_IMAGE_ASPECT_COLOR,                                              // aspect
+               0,                                                                                              // mipLevel
+               0,                                                                                              // arraySlice
+       };
+       const vk::VkBufferImageCopy                     copyRegion                                      =
+       {
+               0u,                                                                                             // bufferOffset
+               m_targetSize.x(),                                                               // bufferRowLength
+               m_targetSize.y(),                                                               // bufferImageHeight
+               firstSlice,                                                                             // imageSubresource
+               { 0, 0, 0 },                                                                    // imageOffset
+               { (deInt32)m_targetSize.x(), (deInt32)m_targetSize.y(), 1 }             // imageExtent
+       };
+
+       const de::MovePtr<vk::Allocation>       bufferMemory                            = allocateAndBindObjectMemory(m_vki, m_device, m_allocator, *buffer, vk::MemoryRequirement::HostVisible);
+
+       const vk::Unique<vk::VkCmdBuffer>       cmd                                                     (vk::createCommandBuffer(m_vki, m_device, &cmdBufCreateInfo));
+       const vk::Unique<vk::VkFence>           cmdCompleteFence                        (vk::createFence(m_vki, m_device, &fenceCreateInfo));
+       const void* const                                       imageBarrierPtr                         = &imageBarrier;
+       const void* const                                       bufferBarrierPtr                        = &memoryBarrier;
+       const deUint64                                          infiniteTimeout                         = ~(deUint64)0u;
+
+       // copy content to buffer
+       VK_CHECK(m_vki.beginCommandBuffer(*cmd, &cmdBufBeginInfo));
+       m_vki.cmdPipelineBarrier(*cmd, vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_FALSE, 1, &imageBarrierPtr);
+       m_vki.cmdCopyImageToBuffer(*cmd, *m_colorAttachmentImage, vk::VK_IMAGE_LAYOUT_TRANSFER_SOURCE_OPTIMAL, *buffer, 1, &copyRegion);
+       m_vki.cmdPipelineBarrier(*cmd, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::VK_FALSE, 1, &bufferBarrierPtr);
+       VK_CHECK(m_vki.endCommandBuffer(*cmd));
+
+       // wait for transfer to complete
+       VK_CHECK(m_vki.queueSubmit(m_queue, 1, &cmd.get(), *cmdCompleteFence));
+       VK_CHECK(m_vki.waitForFences(m_device, 1, &cmdCompleteFence.get(), 0u, infiniteTimeout)); // \note: timeout is failure
+
+       dst.setStorage(m_targetFormat, m_targetSize.x(), m_targetSize.y());
+
+       // copy data
+       invalidateMappedMemoryRange(m_vki, m_device, bufferMemory->getMemory(), bufferMemory->getOffset(), pixelDataSize);
+       tcu::copy(dst, tcu::ConstPixelBufferAccess(dst.getFormat(), dst.getSize(), bufferMemory->getHostPtr()));
+}
+
+tcu::TestStatus SingleTargetRenderInstance::iterate (void)
+{
+       tcu::TextureLevel resultImage;
+
+       // log
+       if (m_firstIteration)
+       {
+               logTestPlan();
+               m_firstIteration = false;
+       }
+
+       // render
+       {
+               // transition to VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
+               const vk::VkImageSubresourceRange       fullSubrange                            =
+               {
+                       vk::VK_IMAGE_ASPECT_COLOR,                                              // aspect
+                       0u,                                                                                             // baseMipLevel
+                       1u,                                                                                             // mipLevels
+                       0u,                                                                                             // baseArraySlice
+                       1u,                                                                                             // arraySize
+               };
+               const vk::VkImageMemoryBarrier          imageBarrier                            =
+               {
+                       vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+                       DE_NULL,
+                       0u,                                                                                             // outputMask
+                       vk::VK_MEMORY_INPUT_COLOR_ATTACHMENT_BIT,               // inputMask
+                       vk::VK_IMAGE_LAYOUT_UNDEFINED,                                  // oldLayout
+                       vk::VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,   // newLayout
+                       vk::VK_QUEUE_FAMILY_IGNORED,                                    // srcQueueFamilyIndex
+                       vk::VK_QUEUE_FAMILY_IGNORED,                                    // destQueueFamilyIndex
+                       *m_colorAttachmentImage,                                                // image
+                       fullSubrange,                                                                   // subresourceRange
+               };
+               const vk::VkCmdBufferCreateInfo         cmdBufCreateInfo                        =
+               {
+                       vk::VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO,
+                       DE_NULL,
+                       *m_cmdPool,                                                                             // cmdPool
+                       vk::VK_CMD_BUFFER_LEVEL_PRIMARY,                                // level
+                       0u,                                                                                             // flags
+               };
+               const vk::VkCmdBufferBeginInfo          cmdBufBeginInfo                         =
+               {
+                       vk::VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,
+                       DE_NULL,
+                       vk::VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | vk::VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT,    // flags
+                       (vk::VkRenderPass)0u,                                                                                                                                                   // renderPass
+                       (vk::VkFramebuffer)0u,                                                                                                                                  // framebuffer
+               };
+
+               const vk::Unique<vk::VkCmdBuffer>       cmd                                     (vk::createCommandBuffer(m_vki, m_device, &cmdBufCreateInfo));
+               const void* const                                       imageBarrierPtr         = &imageBarrier;
+
+               VK_CHECK(m_vki.beginCommandBuffer(*cmd, &cmdBufBeginInfo));
+               m_vki.cmdPipelineBarrier(*cmd, vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::VK_FALSE, 1, &imageBarrierPtr);
+               VK_CHECK(m_vki.endCommandBuffer(*cmd));
+               VK_CHECK(m_vki.queueSubmit(m_queue, 1, &cmd.get(), 0));
+
+               // and then render to
+               renderToTarget();
+       }
+
+       // read and verify
+       readRenderTarget(resultImage);
+       return verifyResultImage(resultImage.getAccess());
+}
+
+class RenderInstanceShaders
+{
+public:
+                                                                                                               RenderInstanceShaders           (const vk::DeviceInterface&             vki,
+                                                                                                                                                                        vk::VkDevice                                   device,
+                                                                                                                                                                        const vk::BinaryCollection&    programCollection);
+
+       inline bool                                                                                     hasTessellationStage            (void) const { return *m_tessCtrlShader != 0 || *m_tessEvalShader != 0; }
+       inline deUint32                                                                         getNumStages                            (void) const { return (deUint32)m_stageInfos.size();                                    }
+       inline const vk::VkPipelineShaderStageCreateInfo*       getStages                                       (void) const { return &m_stageInfos[0];                                                                 }
+
+private:
+       void                                                                                            addStage                                        (const vk::DeviceInterface&             vki,
+                                                                                                                                                                        vk::VkDevice                                   device,
+                                                                                                                                                                        const vk::BinaryCollection&    programCollection,
+                                                                                                                                                                        const char*                                    name,
+                                                                                                                                                                        vk::VkShaderStage                              stage,
+                                                                                                                                                                        vk::Move<vk::VkShaderModule>*  outModule,
+                                                                                                                                                                        vk::Move<vk::VkShader>*                outShader);
+
+       vk::VkPipelineShaderStageCreateInfo                                     getShaderStageCreateInfo        (vk::VkShaderStage stage, vk::VkShader shader) const;
+
+       vk::Move<vk::VkShaderModule>                                            m_vertexShaderModule;
+       vk::Move<vk::VkShader>                                                          m_vertexShader;
+       vk::Move<vk::VkShaderModule>                                            m_tessCtrlShaderModule;
+       vk::Move<vk::VkShader>                                                          m_tessCtrlShader;
+       vk::Move<vk::VkShaderModule>                                            m_tessEvalShaderModule;
+       vk::Move<vk::VkShader>                                                          m_tessEvalShader;
+       vk::Move<vk::VkShaderModule>                                            m_geometryShaderModule;
+       vk::Move<vk::VkShader>                                                          m_geometryShader;
+       vk::Move<vk::VkShaderModule>                                            m_fragmentShaderModule;
+       vk::Move<vk::VkShader>                                                          m_fragmentShader;
+       std::vector<vk::VkPipelineShaderStageCreateInfo>        m_stageInfos;
+};
+
+RenderInstanceShaders::RenderInstanceShaders (const vk::DeviceInterface&       vki,
+                                                                                         vk::VkDevice                                  device,
+                                                                                         const vk::BinaryCollection&   programCollection)
+{
+       addStage(vki, device, programCollection, "vertex",              vk::VK_SHADER_STAGE_VERTEX,                             &m_vertexShaderModule,          &m_vertexShader);
+       addStage(vki, device, programCollection, "tess_ctrl",   vk::VK_SHADER_STAGE_TESS_CONTROL,               &m_tessCtrlShaderModule,        &m_tessCtrlShader);
+       addStage(vki, device, programCollection, "tess_eval",   vk::VK_SHADER_STAGE_TESS_EVALUATION,    &m_tessEvalShaderModule,        &m_tessEvalShader);
+       addStage(vki, device, programCollection, "geometry",    vk::VK_SHADER_STAGE_GEOMETRY,                   &m_geometryShaderModule,        &m_geometryShader);
+       addStage(vki, device, programCollection, "fragment",    vk::VK_SHADER_STAGE_FRAGMENT,                   &m_fragmentShaderModule,        &m_fragmentShader);
+
+       DE_ASSERT(!m_stageInfos.empty());
+}
+
+void RenderInstanceShaders::addStage (const vk::DeviceInterface&               vki,
+                                                                         vk::VkDevice                                          device,
+                                                                         const vk::BinaryCollection&           programCollection,
+                                                                         const char*                                           name,
+                                                                         vk::VkShaderStage                                     stage,
+                                                                         vk::Move<vk::VkShaderModule>*         outModule,
+                                                                         vk::Move<vk::VkShader>*                       outShader)
+{
+       if (programCollection.contains(name))
+       {
+               vk::Move<vk::VkShaderModule>    module          = createShaderModule(vki, device, programCollection.get(name), (vk::VkShaderModuleCreateFlags)0);
+               const vk::VkShaderCreateInfo    createInfo      =
+               {
+                       vk::VK_STRUCTURE_TYPE_SHADER_CREATE_INFO,
+                       DE_NULL,
+                       *module,                // module
+                       "main",                 // pName
+                       0u                              // flags
+               };
+               vk::Move<vk::VkShader>                  shader          = vk::createShader(vki, device, &createInfo);
+
+               m_stageInfos.push_back(getShaderStageCreateInfo(stage, *shader));
+               *outModule = module;
+               *outShader = shader;
+       }
+}
+
+vk::VkPipelineShaderStageCreateInfo RenderInstanceShaders::getShaderStageCreateInfo (vk::VkShaderStage stage, vk::VkShader shader) const
+{
+       const vk::VkPipelineShaderStageCreateInfo       stageCreateInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+               DE_NULL,
+               stage,                  // stage
+               shader,                 // shader
+               DE_NULL,                // pSpecializationInfo
+       };
+       return stageCreateInfo;
+}
+
+class SingleCmdRenderInstance : public SingleTargetRenderInstance
+{
+public:
+                                                                       SingleCmdRenderInstance (Context&                       context,
+                                                                                                                        bool                           isPrimaryCmdBuf,
+                                                                                                                        const tcu::UVec2&      renderSize);
+
+private:
+       vk::Move<vk::VkPipeline>                createPipeline                          (vk::VkPipelineLayout pipelineLayout);
+
+       virtual vk::VkPipelineLayout    getPipelineLayout                       (void) const = 0;
+       virtual void                                    writeDrawCmdBuffer                      (vk::VkCmdBuffer cmd) const = 0;
+
+       void                                                    renderToTarget                          (void);
+
+       const bool                                              m_isPrimaryCmdBuf;
+};
+
+SingleCmdRenderInstance::SingleCmdRenderInstance (Context&                     context,
+                                                                                                 bool                          isPrimaryCmdBuf,
+                                                                                                 const tcu::UVec2&     renderSize)
+       : SingleTargetRenderInstance    (context, renderSize)
+       , m_isPrimaryCmdBuf                             (isPrimaryCmdBuf)
+{
+}
+
+vk::Move<vk::VkPipeline> SingleCmdRenderInstance::createPipeline (vk::VkPipelineLayout pipelineLayout)
+{
+       const RenderInstanceShaders                                                     shaderStages            (m_vki, m_device, m_context.getBinaryCollection());
+       const vk::VkPrimitiveTopology                                           topology                        = shaderStages.hasTessellationStage() ? vk::VK_PRIMITIVE_TOPOLOGY_PATCH : vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+       const vk::VkPipelineVertexInputStateCreateInfo          vertexInputState        =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
+               DE_NULL,
+               0u,                                                                                     // bindingCount
+               DE_NULL,                                                                        // pVertexBindingDescriptions
+               0u,                                                                                     // attributeCount
+               DE_NULL,                                                                        // pVertexAttributeDescriptions
+       };
+       const vk::VkPipelineInputAssemblyStateCreateInfo        iaState                         =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
+               DE_NULL,
+               topology,                                                                       // topology
+               vk::VK_FALSE,                                                           // primitiveRestartEnable
+       };
+       const vk::VkPipelineTessellationStateCreateInfo         tessState                       =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
+               DE_NULL,
+               3u,                                                                                     // patchControlPoints
+       };
+       const vk::VkPipelineViewportStateCreateInfo                     vpState                         =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
+               DE_NULL,
+               1u,                                                                                     // viewportCount
+       };
+       const vk::VkPipelineRasterStateCreateInfo                       rsState                         =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_RASTER_STATE_CREATE_INFO,
+               DE_NULL,
+               vk::VK_TRUE,                                                            // depthClipEnable
+               vk::VK_FALSE,                                                           // rasterizerDiscardEnable
+               vk::VK_FILL_MODE_SOLID,                                         // fillMode
+               vk::VK_CULL_MODE_NONE,                                          // cullMode
+               vk::VK_FRONT_FACE_CCW,                                          // frontFace
+       };
+       const vk::VkPipelineMultisampleStateCreateInfo          msState                         =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
+               DE_NULL,
+               1u,                                                                                     // rasterSamples
+               vk::VK_FALSE,                                                           // sampleShadingEnable
+               0.0f,                                                                           // minSampleShading
+               0x01u                                                                           // sampleMask
+       };
+       const vk::VkPipelineDepthStencilStateCreateInfo         dsState                         =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
+               DE_NULL,
+               vk::VK_FALSE,                                                           // depthTestEnable
+               vk::VK_FALSE,                                                           // depthWriteEnable
+               vk::VK_COMPARE_OP_ALWAYS,                                       // depthCompareOp
+               vk::VK_FALSE,                                                           // depthBoundsEnable
+               vk::VK_FALSE,                                                           // stencilTestEnable
+               { vk::VK_STENCIL_OP_KEEP, vk::VK_STENCIL_OP_KEEP, vk::VK_STENCIL_OP_KEEP, vk::VK_COMPARE_OP_ALWAYS },   // front
+               { vk::VK_STENCIL_OP_KEEP, vk::VK_STENCIL_OP_KEEP, vk::VK_STENCIL_OP_KEEP, vk::VK_COMPARE_OP_ALWAYS },   // back
+       };
+       const vk::VkPipelineColorBlendAttachmentState           cbAttachment            =
+       {
+               vk::VK_FALSE,                                                           // blendEnable
+               vk::VK_BLEND_ZERO,                                                      // srcBlendColor
+               vk::VK_BLEND_ZERO,                                                      // destBlendColor
+               vk::VK_BLEND_OP_ADD,                                            // blendOpColor
+               vk::VK_BLEND_ZERO,                                                      // srcBlendAlpha
+               vk::VK_BLEND_ZERO,                                                      // destBlendAlpha
+               vk::VK_BLEND_OP_ADD,                                            // blendOpAlpha
+               vk::VK_CHANNEL_R_BIT | vk::VK_CHANNEL_G_BIT | vk::VK_CHANNEL_B_BIT | vk::VK_CHANNEL_A_BIT,      // channelWriteMask
+       };
+       const vk::VkPipelineColorBlendStateCreateInfo           cbState                         =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
+               DE_NULL,
+               vk::VK_FALSE,                                                           // alphaToCoverageEnable
+               vk::VK_FALSE,                                                           // logicOpEnable
+               vk::VK_LOGIC_OP_CLEAR,                                          // logicOp
+               1u,                                                                                     // attachmentCount
+               &cbAttachment,                                                          // pAttachments
+       };
+       const vk::VkGraphicsPipelineCreateInfo createInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
+               DE_NULL,
+               shaderStages.getNumStages(),                                                                    // stageCount
+               shaderStages.getStages(),                                                                               // pStages
+               &vertexInputState,                                                                                              // pVertexInputState
+               &iaState,                                                                                                               // pInputAssemblyState
+               (shaderStages.hasTessellationStage() ? &tessState : DE_NULL),   // pTessellationState
+               &vpState,                                                                                                               // pViewportState
+               &rsState,                                                                                                               // pRasterState
+               &msState,                                                                                                               // pMultisampleState
+               &dsState,                                                                                                               // pDepthStencilState
+               &cbState,                                                                                                               // pColorBlendState
+               0u,                                                                                                                             // flags
+               pipelineLayout,                                                                                                 // layout
+               *m_renderPass,                                                                                                  // renderPass
+               0u,                                                                                                                             // subpass
+               (vk::VkPipeline)0,                                                                                              // basePipelineHandle
+               0u,                                                                                                                             // basePipelineIndex
+       };
+       return createGraphicsPipeline(m_vki, m_device, (vk::VkPipelineCache)0u, &createInfo);
+}
+
+void SingleCmdRenderInstance::renderToTarget (void)
+{
+       const vk::VkViewport                                                            viewport                                        =
+       {
+               0.0f,                                                   // originX
+               0.0f,                                                   // originY
+               float(m_targetSize.x()),                // width
+               float(m_targetSize.y()),                // height
+               0.0f,                                                   // minDepth
+               1.0f,                                                   // maxDepth
+       };
+       const vk::VkRect2D                                                                      renderArea                                      =
+       {
+               { 0, 0 },                                                                                                       // offset
+               { (deInt32)m_targetSize.x(), (deInt32)m_targetSize.y() },       // extent
+       };
+       const vk::VkDynamicViewportStateCreateInfo                      viewportStateCreateInfo         =
+       {
+               vk::VK_STRUCTURE_TYPE_DYNAMIC_VIEWPORT_STATE_CREATE_INFO,
+               DE_NULL,
+               1,                                                              // viewportAndScissorCount
+               &viewport,                                              // pViewports
+               &renderArea,                                    // pScissors
+       };
+       const vk::VkDynamicRasterStateCreateInfo                        rasterStateCreateInfo           =
+       {
+               vk::VK_STRUCTURE_TYPE_DYNAMIC_RASTER_STATE_CREATE_INFO,
+               DE_NULL,
+               0.0f,                                                   // depthBias
+               0.0f,                                                   // depthBiasClamp
+               0.0f,                                                   // slopeScaledDepthBias
+               1.0f,                                                   // lineWidth
+       };
+       const vk::VkDynamicColorBlendStateCreateInfo            colorBlendStateCreateInfo       =
+       {
+               vk::VK_STRUCTURE_TYPE_DYNAMIC_COLOR_BLEND_STATE_CREATE_INFO,
+               DE_NULL,
+               { 0.0f, 0.0f, 0.0f, 0.0f },             // blendConst
+       };
+       const vk::VkDynamicDepthStencilStateCreateInfo          depthStencilStateCreateInfo     =
+       {
+               vk::VK_STRUCTURE_TYPE_DYNAMIC_DEPTH_STENCIL_STATE_CREATE_INFO,
+               DE_NULL,
+               0.0f,           // minDepthBounds
+               1.0f,           // maxDepthBounds
+               0u,                     // stencilReadMask
+               0u,                     // stencilWriteMask
+               0u,                     // stencilFrontRef
+               0u,                     // stencilBackRef
+       };
+       const vk::VkCmdBufferCreateInfo                                         mainCmdBufCreateInfo                    =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO,
+               DE_NULL,
+               *m_cmdPool,                                                     // cmdPool
+               vk::VK_CMD_BUFFER_LEVEL_PRIMARY,        // level
+               0u,                                                                     // flags
+       };
+       const vk::VkCmdBufferBeginInfo                                          mainCmdBufBeginInfo                             =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,
+               DE_NULL,
+               vk::VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | vk::VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT,    // flags
+               (vk::VkRenderPass)0u,                                                                                                                                                   // renderPass
+               (vk::VkFramebuffer)0u,                                                                                                                                                  // framebuffer
+       };
+       const vk::VkCmdBufferCreateInfo                                         passCmdBufCreateInfo                    =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO,
+               DE_NULL,
+               *m_cmdPool,                                                     // cmdPool
+               vk::VK_CMD_BUFFER_LEVEL_SECONDARY,      // level
+               0u,                                                                     // flags
+       };
+       const vk::VkCmdBufferBeginInfo                                          passCmdBufBeginInfo                             =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,
+               DE_NULL,
+               vk::VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | vk::VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT,    // flags
+               (vk::VkRenderPass)*m_renderPass,                                                                                                                                // renderPass
+               (vk::VkFramebuffer)*m_framebuffer,                                                                                                                              // framebuffer
+       };
+       const vk::VkFenceCreateInfo                                                     fenceCreateInfo                         =
+       {
+               vk::VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+               DE_NULL,
+               0u,                     // flags
+       };
+       const vk::VkClearValue                                                          clearValue                                      = createClearValueColor(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
+       const vk::VkRenderPassBeginInfo                                         renderPassBeginInfo                     =
+       {
+               vk::VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
+               DE_NULL,
+               *m_renderPass,          // renderPass
+               *m_framebuffer,         // framebuffer
+               renderArea,                     // renderArea
+               1u,                                     // attachmentCount
+               &clearValue,            // pAttachmentClearValues
+       };
+
+       const vk::VkPipelineLayout                                                      pipelineLayout                          (getPipelineLayout());
+       const vk::Unique<vk::VkPipeline>                                        pipeline                                        (createPipeline(pipelineLayout));
+       const vk::Unique<vk::VkCmdBuffer>                                       mainCmd                                         (vk::createCommandBuffer(m_vki, m_device, &mainCmdBufCreateInfo));
+       const vk::Unique<vk::VkCmdBuffer>                                       passCmd                                         ((m_isPrimaryCmdBuf) ? (vk::Move<vk::VkCmdBuffer>()) : (vk::createCommandBuffer(m_vki, m_device, &passCmdBufCreateInfo)));
+       const vk::Unique<vk::VkDynamicViewportState>            dynamicVpState                          (vk::createDynamicViewportState(m_vki, m_device, &viewportStateCreateInfo));
+       const vk::Unique<vk::VkDynamicRasterState>                      dynamicRsState                          (vk::createDynamicRasterState(m_vki, m_device, &rasterStateCreateInfo));
+       const vk::Unique<vk::VkDynamicColorBlendState>          dynamicCbState                          (vk::createDynamicColorBlendState(m_vki, m_device, &colorBlendStateCreateInfo));
+       const vk::Unique<vk::VkDynamicDepthStencilState>        dynamicDsState                          (vk::createDynamicDepthStencilState(m_vki, m_device, &depthStencilStateCreateInfo));
+       const vk::Unique<vk::VkFence>                                           fence                                           (vk::createFence(m_vki, m_device, &fenceCreateInfo));
+       const deUint64                                                                          infiniteTimeout                         = ~(deUint64)0u;
+       const vk::VkRenderPassContents                                          passContents                            = (m_isPrimaryCmdBuf) ? (vk::VK_RENDER_PASS_CONTENTS_INLINE) : (vk::VK_RENDER_PASS_CONTENTS_SECONDARY_CMD_BUFFERS);
+
+       VK_CHECK(m_vki.beginCommandBuffer(*mainCmd, &mainCmdBufBeginInfo));
+       m_vki.cmdBeginRenderPass(*mainCmd, &renderPassBeginInfo, passContents);
+
+       if (m_isPrimaryCmdBuf)
+       {
+               m_vki.cmdBindDynamicViewportState(*mainCmd, *dynamicVpState);
+               m_vki.cmdBindDynamicRasterState(*mainCmd, *dynamicRsState);
+               m_vki.cmdBindDynamicColorBlendState(*mainCmd, *dynamicCbState);
+               m_vki.cmdBindDynamicDepthStencilState(*mainCmd, *dynamicDsState);
+               m_vki.cmdBindPipeline(*mainCmd, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+               writeDrawCmdBuffer(*mainCmd);
+       }
+       else
+       {
+               VK_CHECK(m_vki.beginCommandBuffer(*passCmd, &passCmdBufBeginInfo));
+               m_vki.cmdBindDynamicViewportState(*passCmd, *dynamicVpState);
+               m_vki.cmdBindDynamicRasterState(*passCmd, *dynamicRsState);
+               m_vki.cmdBindDynamicColorBlendState(*passCmd, *dynamicCbState);
+               m_vki.cmdBindDynamicDepthStencilState(*passCmd, *dynamicDsState);
+               m_vki.cmdBindPipeline(*passCmd, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+               writeDrawCmdBuffer(*passCmd);
+               VK_CHECK(m_vki.endCommandBuffer(*passCmd));
+
+               m_vki.cmdExecuteCommands(*mainCmd, 1, &passCmd.get());
+       }
+
+       m_vki.cmdEndRenderPass(*mainCmd);
+       VK_CHECK(m_vki.endCommandBuffer(*mainCmd));
+
+       // submit and wait for them to finish before exiting scope. (Killing in-flight objects is a no-no).
+       VK_CHECK(m_vki.queueSubmit(m_queue, 1, &mainCmd.get(), *fence));
+       VK_CHECK(m_vki.waitForFences(m_device, 1, &fence.get(), 0u, infiniteTimeout)); // \note: timeout is failure
+}
+
+enum ShaderInputInterface
+{
+       SHADER_INPUT_SINGLE_DESCRIPTOR = 0,     //!< one descriptor
+       SHADER_INPUT_MULTIPLE_DESCRIPTORS,      //!< multiple descriptors
+       SHADER_INPUT_DESCRIPTOR_ARRAY,          //!< descriptor array
+
+       SHADER_INPUT_LAST
+};
+
+deUint32 getInterfaceNumResources (ShaderInputInterface shaderInterface)
+{
+       switch (shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:    return 1u;
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS: return 2u;
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:             return 2u;
+
+               default:
+                       DE_FATAL("Impossible");
+                       return 0u;
+       }
+}
+
+class BufferRenderInstance : public SingleCmdRenderInstance
+{
+public:
+                                                                                                       BufferRenderInstance            (Context&                                       context,
+                                                                                                                                                                bool                                           isPrimaryCmdBuf,
+                                                                                                                                                                vk::VkDescriptorType           descriptorType,
+                                                                                                                                                                vk::VkShaderStageFlags         stageFlags,
+                                                                                                                                                                ShaderInputInterface           shaderInterface,
+                                                                                                                                                                bool                                           viewOffset,
+                                                                                                                                                                bool                                           dynamicOffset,
+                                                                                                                                                                bool                                           dynamicOffsetNonZero);
+
+       static vk::Move<vk::VkBuffer>                                   createSourceBuffer                      (const vk::DeviceInterface&             vki,
+                                                                                                                                                                vk::VkDevice                                   device,
+                                                                                                                                                                vk::Allocator&                                 allocator,
+                                                                                                                                                                vk::VkDescriptorType                   descriptorType,
+                                                                                                                                                                deUint32                                               offset,
+                                                                                                                                                                deUint32                                               bufferSize,
+                                                                                                                                                                de::MovePtr<vk::Allocation>*   outMemory);
+
+       static vk::Move<vk::VkBufferView>                               createBufferView                        (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkBuffer                           buffer,
+                                                                                                                                                                deUint32                                       offset);
+
+       static vk::Move<vk::VkDescriptorPool>                   createDescriptorPool            (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkDescriptorType           descriptorType,
+                                                                                                                                                                ShaderInputInterface           shaderInterface);
+
+       static vk::Move<vk::VkDescriptorSetLayout>              createDescriptorSetLayout       (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkDescriptorType           descriptorType,
+                                                                                                                                                                ShaderInputInterface           shaderInterface,
+                                                                                                                                                                vk::VkShaderStageFlags         stageFlags);
+
+       static vk::Move<vk::VkDescriptorSet>                    createDescriptorSet                     (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkDescriptorSetLayout      descriptorSetLayout,
+                                                                                                                                                                vk::VkDescriptorPool           descriptorPool,
+                                                                                                                                                                vk::VkDescriptorType           descriptorType,
+                                                                                                                                                                ShaderInputInterface           shaderInterface,
+                                                                                                                                                                vk::VkBufferView                       bufferViewA,
+                                                                                                                                                                vk::VkBufferView                       bufferViewB);
+
+       static vk::Move<vk::VkPipelineLayout>                   createPipelineLayout            (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkDescriptorSetLayout      descriptorSetLayout);
+
+       void                                                                                    logTestPlan                                     (void) const;
+       vk::VkPipelineLayout                                                    getPipelineLayout                       (void) const;
+       void                                                                                    writeDrawCmdBuffer                      (vk::VkCmdBuffer cmd) const;
+       tcu::TestStatus                                                                 verifyResultImage                       (const tcu::ConstPixelBufferAccess& result) const;
+
+       enum
+       {
+               RENDER_SIZE                             = 128,
+               BUFFER_DATA_SIZE                = 8 * sizeof(float),
+               BUFFER_SIZE_A                   = 2048, //!< a lot more than required
+               BUFFER_SIZE_B                   = 2560, //!< a lot more than required
+
+               STATIC_OFFSET_VALUE_A   = 256,
+               DYNAMIC_OFFSET_VALUE_A  = 512,
+               STATIC_OFFSET_VALUE_B   = 1024,
+               DYNAMIC_OFFSET_VALUE_B  = 768,
+       };
+
+       const vk::VkDescriptorType                                              m_descriptorType;
+       const ShaderInputInterface                                              m_shaderInterface;
+       const bool                                                                              m_setViewOffset;
+       const bool                                                                              m_setDynamicOffset;
+       const bool                                                                              m_dynamicOffsetNonZero;
+       const vk::VkShaderStageFlags                                    m_stageFlags;
+
+       const deUint32                                                                  m_viewOffsetA;
+       const deUint32                                                                  m_viewOffsetB;
+       const deUint32                                                                  m_dynamicOffsetA;
+       const deUint32                                                                  m_dynamicOffsetB;
+       const deUint32                                                                  m_effectiveOffsetA;
+       const deUint32                                                                  m_effectiveOffsetB;
+       const deUint32                                                                  m_bufferSizeA;
+       const deUint32                                                                  m_bufferSizeB;
+
+       de::MovePtr<vk::Allocation>                                             m_bufferMemoryA;
+       de::MovePtr<vk::Allocation>                                             m_bufferMemoryB;
+       const vk::Unique<vk::VkBuffer>                                  m_sourceBufferA;
+       const vk::Unique<vk::VkBuffer>                                  m_sourceBufferB;
+       const vk::Unique<vk::VkBufferView>                              m_bufferViewA;
+       const vk::Unique<vk::VkBufferView>                              m_bufferViewB;
+       const vk::Unique<vk::VkDescriptorPool>                  m_descriptorPool;
+       const vk::Unique<vk::VkDescriptorSetLayout>             m_descriptorSetLayout;
+       const vk::Unique<vk::VkDescriptorSet>                   m_descriptorSet;
+       const vk::Unique<vk::VkPipelineLayout>                  m_pipelineLayout;
+};
+
+BufferRenderInstance::BufferRenderInstance     (Context&                               context,
+                                                                                        bool                                   isPrimaryCmdBuf,
+                                                                                        vk::VkDescriptorType   descriptorType,
+                                                                                        vk::VkShaderStageFlags stageFlags,
+                                                                                        ShaderInputInterface   shaderInterface,
+                                                                                        bool                                   viewOffset,
+                                                                                        bool                                   dynamicOffset,
+                                                                                        bool                                   dynamicOffsetNonZero)
+       : SingleCmdRenderInstance               (context, isPrimaryCmdBuf, tcu::UVec2(RENDER_SIZE, RENDER_SIZE))
+       , m_descriptorType                              (descriptorType)
+       , m_shaderInterface                             (shaderInterface)
+       , m_setViewOffset                               (viewOffset)
+       , m_setDynamicOffset                    (dynamicOffset)
+       , m_dynamicOffsetNonZero                (dynamicOffsetNonZero)
+       , m_stageFlags                                  (stageFlags)
+       , m_viewOffsetA                                 ((m_setViewOffset) ? ((deUint32)STATIC_OFFSET_VALUE_A) : (0u))
+       , m_viewOffsetB                                 ((m_setViewOffset) ? ((deUint32)STATIC_OFFSET_VALUE_B) : (0u))
+       , m_dynamicOffsetA                              ((dynamicOffsetNonZero) ? ((deUint32)DYNAMIC_OFFSET_VALUE_A) : (0u))
+       , m_dynamicOffsetB                              ((dynamicOffsetNonZero) ? ((deUint32)DYNAMIC_OFFSET_VALUE_B) : (0u))
+       , m_effectiveOffsetA                    ((isDynamicDescriptorType(m_descriptorType)) ? (m_dynamicOffsetA) : (m_viewOffsetA))
+       , m_effectiveOffsetB                    ((isDynamicDescriptorType(m_descriptorType)) ? (m_dynamicOffsetB) : (m_viewOffsetB))
+       , m_bufferSizeA                                 (BUFFER_SIZE_A)
+       , m_bufferSizeB                                 (BUFFER_SIZE_B)
+       , m_bufferMemoryA                               (DE_NULL)
+       , m_bufferMemoryB                               (DE_NULL)
+       , m_sourceBufferA                               (createSourceBuffer(m_vki, m_device, m_allocator, m_descriptorType, m_effectiveOffsetA, m_bufferSizeA, &m_bufferMemoryA))
+       , m_sourceBufferB                               ((getInterfaceNumResources(m_shaderInterface) == 1u)
+                                                                               ? vk::Move<vk::VkBuffer>()
+                                                                               : createSourceBuffer(m_vki, m_device, m_allocator, m_descriptorType, m_effectiveOffsetB, m_bufferSizeB, &m_bufferMemoryB))
+       , m_bufferViewA                                 (createBufferView(m_vki, m_device, *m_sourceBufferA, m_viewOffsetA))
+       , m_bufferViewB                                 ((getInterfaceNumResources(m_shaderInterface) == 1u)
+                                                                               ? vk::Move<vk::VkBufferView>()
+                                                                               : createBufferView(m_vki, m_device, *m_sourceBufferB, m_viewOffsetB))
+       , m_descriptorPool                              (createDescriptorPool(m_vki, m_device, m_descriptorType, m_shaderInterface))
+       , m_descriptorSetLayout                 (createDescriptorSetLayout(m_vki, m_device, m_descriptorType, m_shaderInterface, m_stageFlags))
+       , m_descriptorSet                               (createDescriptorSet(m_vki, m_device, *m_descriptorSetLayout, *m_descriptorPool, m_descriptorType, m_shaderInterface, *m_bufferViewA, *m_bufferViewB))
+       , m_pipelineLayout                              (createPipelineLayout(m_vki, m_device, *m_descriptorSetLayout))
+{
+       if (m_setDynamicOffset)
+               DE_ASSERT(isDynamicDescriptorType(m_descriptorType));
+       if (m_dynamicOffsetNonZero)
+               DE_ASSERT(m_setDynamicOffset);
+}
+
+vk::Move<vk::VkBuffer> BufferRenderInstance::createSourceBuffer (const vk::DeviceInterface&            vki,
+                                                                                                                                vk::VkDevice                                   device,
+                                                                                                                                vk::Allocator&                                 allocator,
+                                                                                                                                vk::VkDescriptorType                   descriptorType,
+                                                                                                                                deUint32                                               offset,
+                                                                                                                                deUint32                                               bufferSize,
+                                                                                                                                de::MovePtr<vk::Allocation>*   outMemory)
+{
+       static const float                              s_colors[]                      =
+       {
+               0.0f, 1.0f, 0.0f, 1.0f,         // green
+               1.0f, 1.0f, 0.0f, 1.0f,         // yellow
+       };
+       DE_STATIC_ASSERT(sizeof(s_colors) == BUFFER_DATA_SIZE);
+       DE_ASSERT(offset + BUFFER_DATA_SIZE <= bufferSize);
+       DE_ASSERT(offset % sizeof(float) == 0);
+       DE_ASSERT(bufferSize % sizeof(float) == 0);
+
+       const bool                                              isUniformBuffer         = isUniformDescriptorType(descriptorType);
+       const vk::VkBufferUsageFlags    usageFlags                      = (isUniformBuffer) ? (vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) : (vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
+       const float                                             preGuardValue           = 0.5f;
+       const float                                             postGuardValue          = 0.75f;
+       const vk::VkBufferCreateInfo    bufferCreateInfo        =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+               DE_NULL,
+               bufferSize,                                             // size
+               usageFlags,                                             // usage
+               0u,                                                             // flags
+               vk::VK_SHARING_MODE_EXCLUSIVE,  // sharingMode
+               0u,                                                             // queueFamilyCount
+               DE_NULL,                                                // pQueueFamilyIndices
+       };
+       vk::Move<vk::VkBuffer>                  buffer                          (vk::createBuffer(vki, device, &bufferCreateInfo));
+       de::MovePtr<vk::Allocation>             bufferMemory            = allocateAndBindObjectMemory(vki, device, allocator, *buffer, vk::MemoryRequirement::HostVisible);
+       void* const                                             mapPtr                          = bufferMemory->getHostPtr();
+
+       // guard with interesting values
+       for (size_t preGuardOffset = 0; preGuardOffset + sizeof(float) <= (size_t)offset; preGuardOffset += sizeof(float))
+               deMemcpy((deUint8*)mapPtr + preGuardOffset, &preGuardValue, sizeof(float));
+
+       deMemcpy((deUint8*)mapPtr + offset, s_colors, sizeof(s_colors));
+       for (size_t postGuardOffset = (size_t)offset + sizeof(s_colors); postGuardOffset + sizeof(float) <= (size_t)bufferSize; postGuardOffset += sizeof(float))
+               deMemcpy((deUint8*)mapPtr + postGuardOffset, &postGuardValue, sizeof(float));
+       deMemset((deUint8*)mapPtr + offset + sizeof(s_colors), 0x5A, (size_t)bufferSize - (size_t)offset - sizeof(s_colors)); // fill with interesting pattern that produces valid floats
+
+       flushMappedMemoryRange(vki, device, bufferMemory->getMemory(), bufferMemory->getOffset(), bufferSize);
+
+       *outMemory = bufferMemory;
+       return buffer;
+}
+
+vk::Move<vk::VkBufferView> BufferRenderInstance::createBufferView (const vk::DeviceInterface&  vki,
+                                                                                                                                  vk::VkDevice                                 device,
+                                                                                                                                  vk::VkBuffer                                 buffer,
+                                                                                                                                  deUint32                                             offset)
+{
+       const vk::VkBufferViewCreateInfo                createInfo      =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO,
+               DE_NULL,
+               buffer,                                                         // buffer
+               vk::VK_BUFFER_VIEW_TYPE_RAW,            // viewType
+               vk::VK_FORMAT_UNDEFINED,                        // format
+               (vk::VkDeviceSize)offset,                       // offset
+               (vk::VkDeviceSize)BUFFER_DATA_SIZE      // range
+       };
+       return vk::createBufferView(vki, device, &createInfo);
+}
+
+vk::Move<vk::VkDescriptorPool> BufferRenderInstance::createDescriptorPool (const vk::DeviceInterface&  vki,
+                                                                                                                                                  vk::VkDevice                                 device,
+                                                                                                                                                  vk::VkDescriptorType                 descriptorType,
+                                                                                                                                                  ShaderInputInterface                 shaderInterface)
+{
+       return vk::DescriptorPoolBuilder()
+               .addType(descriptorType, getInterfaceNumResources(shaderInterface))
+               .build(vki, device, vk::VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1);
+}
+
+vk::Move<vk::VkDescriptorSetLayout> BufferRenderInstance::createDescriptorSetLayout (const vk::DeviceInterface&        vki,
+                                                                                                                                                                        vk::VkDevice                           device,
+                                                                                                                                                                        vk::VkDescriptorType           descriptorType,
+                                                                                                                                                                        ShaderInputInterface           shaderInterface,
+                                                                                                                                                                        vk::VkShaderStageFlags         stageFlags)
+{
+       vk::DescriptorSetLayoutBuilder builder;
+
+       switch (shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.addSingleBinding(descriptorType, stageFlags);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.addSingleBinding(descriptorType, stageFlags);
+                       builder.addSingleBinding(descriptorType, stageFlags);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.addArrayBinding(descriptorType, 2u, stageFlags);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       return builder.build(vki, device);
+}
+
+vk::Move<vk::VkDescriptorSet> BufferRenderInstance::createDescriptorSet (const vk::DeviceInterface&    vki,
+                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                vk::VkDescriptorSetLayout      descriptorSetLayout,
+                                                                                                                                                vk::VkDescriptorPool           descriptorPool,
+                                                                                                                                                vk::VkDescriptorType           descriptorType,
+                                                                                                                                                ShaderInputInterface           shaderInterface,
+                                                                                                                                                vk::VkBufferView                       viewA,
+                                                                                                                                                vk::VkBufferView                       viewB)
+{
+       const vk::VkDescriptorInfo              bufferInfos[2]  =
+       {
+               createDescriptorInfo(viewA),
+               createDescriptorInfo(viewB),
+       };
+
+       vk::Move<vk::VkDescriptorSet>   descriptorSet   = allocDescriptorSet(vki, device, descriptorPool, vk::VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, descriptorSetLayout);
+       vk::DescriptorSetUpdateBuilder  builder;
+
+       switch (shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), descriptorType, &bufferInfos[0]);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), descriptorType, &bufferInfos[0]);
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), descriptorType, &bufferInfos[1]);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.writeArray(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), descriptorType, 2u, bufferInfos);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       builder.update(vki, device);
+       return descriptorSet;
+}
+
+vk::Move<vk::VkPipelineLayout> BufferRenderInstance::createPipelineLayout (const vk::DeviceInterface&  vki,
+                                                                                                                                                  vk::VkDevice                                 device,
+                                                                                                                                                  vk::VkDescriptorSetLayout    descriptorSetLayout)
+{
+       const vk::VkPipelineLayoutCreateInfo createInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+               DE_NULL,
+               1,                                              // descriptorSetCount
+               &descriptorSetLayout,   // pSetLayouts
+               0u,                                             // pushConstantRangeCount
+               DE_NULL,                                // pPushConstantRanges
+       };
+
+       return vk::createPipelineLayout(vki, device, &createInfo);
+}
+
+void BufferRenderInstance::logTestPlan (void) const
+{
+       std::ostringstream msg;
+
+       msg << "Rendering 2x2 yellow-green grid.\n"
+               << "Single descriptor set. Descriptor set contains "
+                       << ((m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? "single" :
+                           (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS) ? "two" :
+                           (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY) ? "an array (size 2) of" :
+                           (const char*)DE_NULL)
+               << " descriptor(s) of type " << vk::getDescriptorTypeName(m_descriptorType) << "\n"
+               << "Buffer view(s) have " << ((m_setViewOffset) ? ("non-") : ("")) << "zero offset.\n";
+
+       if (isDynamicDescriptorType(m_descriptorType))
+       {
+               if (m_setDynamicOffset)
+               {
+                       msg << "Source buffer(s) are given a dynamic offset at bind time.\n"
+                               << "The supplied dynamic offset is " << ((m_dynamicOffsetNonZero) ? ("non-") : ("")) << "zero.\n";
+               }
+               else
+               {
+                       msg << "Dynamic offset is not supplied at bind time. Expecting bind to offset 0.\n";
+               }
+       }
+
+       if (m_stageFlags == 0u)
+       {
+               msg << "Descriptors are not accessed in any shader stage.\n";
+       }
+       else
+       {
+               msg << "Descriptors are accessed in {"
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_VERTEX_BIT) != 0)                      ? (" vertex")                   : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_TESS_CONTROL_BIT) != 0)        ? (" tess_control")             : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_TESS_EVALUATION_BIT) != 0)     ? (" tess_evaluation")  : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_GEOMETRY_BIT) != 0)            ? (" geometry")                 : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_FRAGMENT_BIT) != 0)            ? (" fragment")                 : (""))
+                       << " } stages.\n";
+       }
+
+       m_context.getTestContext().getLog()
+               << tcu::TestLog::Message
+               << msg.str()
+               << tcu::TestLog::EndMessage;
+}
+
+vk::VkPipelineLayout BufferRenderInstance::getPipelineLayout (void) const
+{
+       return *m_pipelineLayout;
+}
+
+void BufferRenderInstance::writeDrawCmdBuffer (vk::VkCmdBuffer cmd) const
+{
+       const bool                                                      isUniformBuffer         = isUniformDescriptorType(m_descriptorType);
+
+       // \note dynamic offset replaces the view offset, i.e. it is not offset relative to the view offset
+       const deUint32                                          dynamicOffsets[]        =
+       {
+               m_dynamicOffsetA,
+               m_dynamicOffsetB,
+       };
+       const deUint32                                          numOffsets                      = (!m_setDynamicOffset) ? (0u) : (getInterfaceNumResources(m_shaderInterface));
+       const deUint32* const                           dynamicOffsetPtr        = (!m_setDynamicOffset) ? (DE_NULL) : (dynamicOffsets);
+
+       // make host writes device-visible
+       const vk::VkMemoryInputFlags            inputBit                        = (isUniformBuffer) ? (vk::VK_MEMORY_INPUT_UNIFORM_READ_BIT) : (vk::VK_MEMORY_INPUT_SHADER_READ_BIT);
+       const vk::VkBufferMemoryBarrier         memoryBarrierA          =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+               DE_NULL,
+               vk::VK_MEMORY_OUTPUT_HOST_WRITE_BIT,            // outputMask
+               inputBit,                                                                       // inputMask
+               vk::VK_QUEUE_FAMILY_IGNORED,                            // srcQueueFamilyIndex
+               vk::VK_QUEUE_FAMILY_IGNORED,                            // destQueueFamilyIndex
+               *m_sourceBufferA,                                                       // buffer
+               0u,                                                                                     // offset
+               (vk::VkDeviceSize)m_bufferSizeA,                        // size
+       };
+       const vk::VkBufferMemoryBarrier         memoryBarrierB          =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+               DE_NULL,
+               vk::VK_MEMORY_OUTPUT_HOST_WRITE_BIT,            // outputMask
+               inputBit,                                                                       // inputMask
+               vk::VK_QUEUE_FAMILY_IGNORED,                            // srcQueueFamilyIndex
+               vk::VK_QUEUE_FAMILY_IGNORED,                            // destQueueFamilyIndex
+               *m_sourceBufferB,                                                       // buffer
+               0u,                                                                                     // offset
+               (vk::VkDeviceSize)m_bufferSizeB,                        // size
+       };
+       const void* const                                       memoryBarriers[2]       =
+       {
+               &memoryBarrierA,
+               &memoryBarrierB,
+       };
+       const deUint32                                          numMemoryBarriers       = getInterfaceNumResources(m_shaderInterface);
+
+       m_vki.cmdBindDescriptorSets(cmd, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, getPipelineLayout(), 0, 1, &m_descriptorSet.get(), numOffsets, dynamicOffsetPtr);
+       m_vki.cmdPipelineBarrier(cmd, 0u, vk::VK_PIPELINE_STAGE_ALL_GRAPHICS, vk::VK_FALSE, numMemoryBarriers, memoryBarriers);
+       m_vki.cmdDraw(cmd, 0, 6 * 4, 0, 1); // render four quads (two separate triangles)
+}
+
+tcu::TestStatus BufferRenderInstance::verifyResultImage (const tcu::ConstPixelBufferAccess& result) const
+{
+       const tcu::Vec4         green           (0.0f, 1.0f, 0.0f, 1.0f);
+       const tcu::Vec4         yellow          (1.0f, 1.0f, 0.0f, 1.0f);
+       tcu::Surface            reference       (m_targetSize.x(), m_targetSize.y());
+
+       drawQuadrantReferenceResult(reference.getAccess(), yellow, green, green, yellow);
+
+       if (!bilinearCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", reference.getAccess(), result, tcu::RGBA(1, 1, 1, 1), tcu::COMPARE_LOG_RESULT))
+               return tcu::TestStatus::fail("Image verification failed");
+       else
+               return tcu::TestStatus::pass("Pass");
+}
+
+class ComputeInstanceResultBuffer
+{
+public:
+       enum
+       {
+               DATA_SIZE = sizeof(tcu::Vec4[4])
+       };
+
+                                                                                       ComputeInstanceResultBuffer     (const vk::DeviceInterface&             vki,
+                                                                                                                                                vk::VkDevice                                   device,
+                                                                                                                                                vk::Allocator&                                 allocator);
+
+       void                                                                    readResultContentsTo            (tcu::Vec4 (*results)[4]) const;
+
+       inline vk::VkBuffer                                             getBuffer                                       (void) const { return *m_buffer;                        }
+       inline vk::VkBufferView                                 getBufferView                           (void) const { return *m_bufferView;            }
+       inline const void*                                              getResultReadBarrier            (void) const { return &m_bufferBarrier;         }
+
+private:
+       static vk::Move<vk::VkBuffer>                   createResultBuffer                      (const vk::DeviceInterface&             vki,
+                                                                                                                                                vk::VkDevice                                   device,
+                                                                                                                                                vk::Allocator&                                 allocator,
+                                                                                                                                                de::MovePtr<vk::Allocation>*   outAllocation);
+
+       static vk::Move<vk::VkBufferView>               createResultBufferView          (const vk::DeviceInterface&     vki,
+                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                vk::VkBuffer                           buffer);
+
+       static vk::VkBufferMemoryBarrier                createResultBufferBarrier       (vk::VkBuffer buffer);
+
+       const vk::DeviceInterface&                              m_vki;
+       const vk::VkDevice                                              m_device;
+
+       de::MovePtr<vk::Allocation>                             m_bufferMem;
+       const vk::Unique<vk::VkBuffer>                  m_buffer;
+       const vk::Unique<vk::VkBufferView>              m_bufferView;
+       const vk::VkBufferMemoryBarrier                 m_bufferBarrier;
+};
+
+ComputeInstanceResultBuffer::ComputeInstanceResultBuffer (const vk::DeviceInterface&   vki,
+                                                                                                                 vk::VkDevice                                  device,
+                                                                                                                 vk::Allocator&                                allocator)
+       : m_vki                         (vki)
+       , m_device                      (device)
+       , m_bufferMem           (DE_NULL)
+       , m_buffer                      (createResultBuffer(m_vki, m_device, allocator, &m_bufferMem))
+       , m_bufferView          (createResultBufferView(m_vki, m_device, *m_buffer))
+       , m_bufferBarrier       (createResultBufferBarrier(*m_buffer))
+{
+}
+
+void ComputeInstanceResultBuffer::readResultContentsTo (tcu::Vec4 (*results)[4]) const
+{
+       invalidateMappedMemoryRange(m_vki, m_device, m_bufferMem->getMemory(), m_bufferMem->getOffset(), sizeof(*results));
+       deMemcpy(*results, m_bufferMem->getHostPtr(), sizeof(*results));
+}
+
+vk::Move<vk::VkBuffer> ComputeInstanceResultBuffer::createResultBuffer (const vk::DeviceInterface&             vki,
+                                                                                                                                               vk::VkDevice                                    device,
+                                                                                                                                               vk::Allocator&                                  allocator,
+                                                                                                                                               de::MovePtr<vk::Allocation>*    outAllocation)
+{
+       const vk::VkBufferCreateInfo    createInfo      =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+               DE_NULL,
+               (vk::VkDeviceSize)DATA_SIZE,                            // size
+               vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,         // usage
+               0u,                                                                                     // flags
+               vk::VK_SHARING_MODE_EXCLUSIVE,                          // sharingMode
+               0u,                                                                                     // queueFamilyCount
+               DE_NULL,                                                                        // pQueueFamilyIndices
+       };
+       vk::Move<vk::VkBuffer>                  buffer          (vk::createBuffer(vki, device, &createInfo));
+       de::MovePtr<vk::Allocation>             allocation      (allocateAndBindObjectMemory(vki, device, allocator, *buffer, vk::MemoryRequirement::HostVisible));
+       const float                                             clearValue      = -1.0f;
+       void*                                                   mapPtr          = allocation->getHostPtr();
+
+       for (size_t offset = 0; offset < DATA_SIZE; offset += sizeof(float))
+               deMemcpy(((deUint8*)mapPtr) + offset, &clearValue, sizeof(float));
+
+       flushMappedMemoryRange(vki, device, allocation->getMemory(), allocation->getOffset(), (vk::VkDeviceSize)DATA_SIZE);
+
+       *outAllocation = allocation;
+       return buffer;
+}
+
+vk::Move<vk::VkBufferView> ComputeInstanceResultBuffer::createResultBufferView (const vk::DeviceInterface&     vki,
+                                                                                                                                                               vk::VkDevice                            device,
+                                                                                                                                                               vk::VkBuffer                            buffer)
+{
+       const vk::VkBufferViewCreateInfo        createInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO,
+               DE_NULL,
+               buffer,                                                                 // buffer
+               vk::VK_BUFFER_VIEW_TYPE_RAW,                    // viewType
+               vk::VK_FORMAT_UNDEFINED,                                // format
+               (vk::VkDeviceSize)0u,                                   // offset
+               (vk::VkDeviceSize)DATA_SIZE                             // range
+       };
+       return vk::createBufferView(vki, device, &createInfo);
+}
+
+vk::VkBufferMemoryBarrier ComputeInstanceResultBuffer::createResultBufferBarrier (vk::VkBuffer buffer)
+{
+       const vk::VkBufferMemoryBarrier bufferBarrier =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+               DE_NULL,
+               vk::VK_MEMORY_OUTPUT_SHADER_WRITE_BIT,          // outputMask
+               vk::VK_MEMORY_INPUT_HOST_READ_BIT,                      // inputMask
+               vk::VK_QUEUE_FAMILY_IGNORED,                            // srcQueueFamilyIndex
+               vk::VK_QUEUE_FAMILY_IGNORED,                            // destQueueFamilyIndex
+               buffer,                                                                         // buffer
+               (vk::VkDeviceSize)0u,                                           // offset
+               DATA_SIZE,                                                                      // size
+       };
+       return bufferBarrier;
+}
+
+class ComputePipeline
+{
+public:
+                                                                                       ComputePipeline                 (const vk::DeviceInterface&                     vki,
+                                                                                                                                        vk::VkDevice                                           device,
+                                                                                                                                        const vk::BinaryCollection&            programCollection,
+                                                                                                                                        deUint32                                                       numDescriptorSets,
+                                                                                                                                        const vk::VkDescriptorSetLayout*       descriptorSetLayouts);
+
+       inline vk::VkPipeline                                   getPipeline                             (void) const { return *m_pipeline;                      };
+       inline vk::VkPipelineLayout                             getPipelineLayout               (void) const { return *m_pipelineLayout;        };
+
+private:
+       static vk::Move<vk::VkPipelineLayout>   createPipelineLayout    (const vk::DeviceInterface&                     vki,
+                                                                                                                                        vk::VkDevice                                           device,
+                                                                                                                                        deUint32                                                       numDescriptorSets,
+                                                                                                                                        const vk::VkDescriptorSetLayout*       descriptorSetLayouts);
+
+       static vk::Move<vk::VkPipeline>                 createPipeline                  (const vk::DeviceInterface&                     vki,
+                                                                                                                                        vk::VkDevice                                           device,
+                                                                                                                                        const vk::BinaryCollection&            programCollection,
+                                                                                                                                        vk::VkPipelineLayout                           layout);
+
+       const vk::Unique<vk::VkPipelineLayout>  m_pipelineLayout;
+       const vk::Unique<vk::VkPipeline>                m_pipeline;
+};
+
+ComputePipeline::ComputePipeline (const vk::DeviceInterface&           vki,
+                                                                 vk::VkDevice                                          device,
+                                                                 const vk::BinaryCollection&           programCollection,
+                                                                 deUint32                                                      numDescriptorSets,
+                                                                 const vk::VkDescriptorSetLayout*      descriptorSetLayouts)
+       : m_pipelineLayout      (createPipelineLayout(vki, device, numDescriptorSets, descriptorSetLayouts))
+       , m_pipeline            (createPipeline(vki, device, programCollection, *m_pipelineLayout))
+{
+}
+
+vk::Move<vk::VkPipelineLayout> ComputePipeline::createPipelineLayout (const vk::DeviceInterface&               vki,
+                                                                                                                                         vk::VkDevice                                          device,
+                                                                                                                                         deUint32                                                      numDescriptorSets,
+                                                                                                                                         const vk::VkDescriptorSetLayout*      descriptorSetLayouts)
+{
+       const vk::VkPipelineLayoutCreateInfo createInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+               DE_NULL,
+               numDescriptorSets,              // descriptorSetCount
+               descriptorSetLayouts,   // pSetLayouts
+               0u,                                             // pushConstantRangeCount
+               DE_NULL,                                // pPushConstantRanges
+       };
+       return vk::createPipelineLayout(vki, device, &createInfo);
+}
+
+vk::Move<vk::VkPipeline> ComputePipeline::createPipeline (const vk::DeviceInterface&   vki,
+                                                                                                                 vk::VkDevice                                  device,
+                                                                                                                 const vk::BinaryCollection&   programCollection,
+                                                                                                                 vk::VkPipelineLayout                  layout)
+{
+       const vk::Unique<vk::VkShaderModule>            computeModule           (vk::createShaderModule(vki, device, programCollection.get("compute"), (vk::VkShaderModuleCreateFlags)0u));
+       const vk::VkShaderCreateInfo                            shaderCreateInfo        =
+       {
+               vk::VK_STRUCTURE_TYPE_SHADER_CREATE_INFO,
+               DE_NULL,
+               *computeModule,         // module
+               "main",                         // pName
+               0u                                      // flags
+       };
+       const vk::Unique<vk::VkShader>                          computeShader           (vk::createShader(vki, device, &shaderCreateInfo));
+       const vk::VkPipelineShaderStageCreateInfo       cs                                      =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+               DE_NULL,
+               vk::VK_SHADER_STAGE_COMPUTE,    // stage
+               *computeShader,                                 // shader
+               DE_NULL,                                                // pSpecializationInfo
+       };
+       const vk::VkComputePipelineCreateInfo           createInfo                      =
+       {
+               vk::VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
+               DE_NULL,
+               cs,                                                             // cs
+               0u,                                                             // flags
+               layout,                                                 // layout
+               (vk::VkPipeline)0,                              // basePipelineHandle
+               0u,                                                             // basePipelineIndex
+       };
+       return createComputePipeline(vki, device, (vk::VkPipelineCache)0u, &createInfo);
+}
+
+class ComputeCommand
+{
+public:
+                                                                               ComputeCommand  (const vk::DeviceInterface&     vki,
+                                                                                                                vk::VkDevice                           device,
+                                                                                                                vk::VkPipeline                         pipeline,
+                                                                                                                vk::VkPipelineLayout           pipelineLayout,
+                                                                                                                const tcu::UVec3&                      numWorkGroups,
+                                                                                                                int                                            numDescriptorSets,
+                                                                                                                const vk::VkDescriptorSet*     descriptorSets,
+                                                                                                                int                                            numDynamicOffsets,
+                                                                                                                const deUint32*                        dynamicOffsets,
+                                                                                                                int                                            numPreBarriers,
+                                                                                                                const void* const*                     preBarriers,
+                                                                                                                int                                            numPostBarriers,
+                                                                                                                const void* const*                     postBarriers);
+
+       void                                                            submitAndWait   (deUint32 queueFamilyIndex, vk::VkQueue queue) const;
+
+private:
+       const vk::DeviceInterface&                      m_vki;
+       const vk::VkDevice                                      m_device;
+       const vk::VkPipeline                            m_pipeline;
+       const vk::VkPipelineLayout                      m_pipelineLayout;
+       const tcu::UVec3&                                       m_numWorkGroups;
+       const int                                                       m_numDescriptorSets;
+       const vk::VkDescriptorSet* const        m_descriptorSets;
+       const int                                                       m_numDynamicOffsets;
+       const deUint32* const                           m_dynamicOffsets;
+       const int                                                       m_numPreBarriers;
+       const void* const* const                        m_preBarriers;
+       const int                                                       m_numPostBarriers;
+       const void* const* const                        m_postBarriers;
+};
+
+ComputeCommand::ComputeCommand (const vk::DeviceInterface&     vki,
+                                                               vk::VkDevice                            device,
+                                                               vk::VkPipeline                          pipeline,
+                                                               vk::VkPipelineLayout            pipelineLayout,
+                                                               const tcu::UVec3&                       numWorkGroups,
+                                                               int                                                     numDescriptorSets,
+                                                               const vk::VkDescriptorSet*      descriptorSets,
+                                                               int                                                     numDynamicOffsets,
+                                                               const deUint32*                         dynamicOffsets,
+                                                               int                                                     numPreBarriers,
+                                                               const void* const*                      preBarriers,
+                                                               int                                                     numPostBarriers,
+                                                               const void* const*                      postBarriers)
+       : m_vki                                 (vki)
+       , m_device                              (device)
+       , m_pipeline                    (pipeline)
+       , m_pipelineLayout              (pipelineLayout)
+       , m_numWorkGroups               (numWorkGroups)
+       , m_numDescriptorSets   (numDescriptorSets)
+       , m_descriptorSets              (descriptorSets)
+       , m_numDynamicOffsets   (numDynamicOffsets)
+       , m_dynamicOffsets              (dynamicOffsets)
+       , m_numPreBarriers              (numPreBarriers)
+       , m_preBarriers                 (preBarriers)
+       , m_numPostBarriers             (numPostBarriers)
+       , m_postBarriers                (postBarriers)
+{
+}
+
+void ComputeCommand::submitAndWait (deUint32 queueFamilyIndex, vk::VkQueue queue) const
+{
+       const vk::VkCmdPoolCreateInfo                                   cmdPoolCreateInfo       =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_POOL_CREATE_INFO,
+               DE_NULL,
+               queueFamilyIndex,                                               // queueFamilyIndex
+               vk::VK_CMD_POOL_CREATE_TRANSIENT_BIT,   // flags
+       };
+       const vk::Unique<vk::VkCmdPool>                                 cmdPool                         (vk::createCommandPool(m_vki, m_device, &cmdPoolCreateInfo));
+
+       const vk::VkFenceCreateInfo                                             fenceCreateInfo         =
+       {
+               vk::VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+               DE_NULL,
+               0u,                     // flags
+       };
+
+       const vk::VkCmdBufferCreateInfo                                 cmdBufCreateInfo        =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO,
+               DE_NULL,
+               *cmdPool,                                                       // cmdPool
+               vk::VK_CMD_BUFFER_LEVEL_PRIMARY,        // level
+               0u,                                                                     // flags
+       };
+       const vk::VkCmdBufferBeginInfo                                  cmdBufBeginInfo         =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,
+               DE_NULL,
+               vk::VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | vk::VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT,    // flags
+               (vk::VkRenderPass)0u,                                                                                                                                                   // renderPass
+               (vk::VkFramebuffer)0u,                                                                                                                                                  // framebuffer
+       };
+
+       const vk::Unique<vk::VkFence>                                   cmdCompleteFence        (vk::createFence(m_vki, m_device, &fenceCreateInfo));
+       const vk::Unique<vk::VkCmdBuffer>                               cmd                                     (vk::createCommandBuffer(m_vki, m_device, &cmdBufCreateInfo));
+       const deUint64                                                                  infiniteTimeout         = ~(deUint64)0u;
+
+       VK_CHECK(m_vki.beginCommandBuffer(*cmd, &cmdBufBeginInfo));
+
+       m_vki.cmdBindPipeline(*cmd, vk::VK_PIPELINE_BIND_POINT_COMPUTE, m_pipeline);
+       m_vki.cmdBindDescriptorSets(*cmd, vk::VK_PIPELINE_BIND_POINT_COMPUTE, m_pipelineLayout, 0, m_numDescriptorSets, m_descriptorSets, m_numDynamicOffsets, m_dynamicOffsets);
+
+       if (m_numPreBarriers)
+               m_vki.cmdPipelineBarrier(*cmd, 0u, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, vk::VK_FALSE, m_numPreBarriers, m_preBarriers);
+
+       m_vki.cmdDispatch(*cmd, m_numWorkGroups.x(), m_numWorkGroups.y(), m_numWorkGroups.z());
+       m_vki.cmdPipelineBarrier(*cmd, vk::VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::VK_FALSE, m_numPostBarriers, m_postBarriers);
+       VK_CHECK(m_vki.endCommandBuffer(*cmd));
+
+       // run
+       VK_CHECK(m_vki.queueSubmit(queue, 1, &cmd.get(), *cmdCompleteFence));
+       VK_CHECK(m_vki.waitForFences(m_device, 1, &cmdCompleteFence.get(), 0u, infiniteTimeout)); // \note: timeout is failure
+}
+
+class BufferComputeInstance : public vkt::TestInstance
+{
+public:
+                                                                                       BufferComputeInstance           (Context&                               context,
+                                                                                                                                                vk::VkDescriptorType   descriptorType,
+                                                                                                                                                ShaderInputInterface   shaderInterface,
+                                                                                                                                                bool                                   viewOffset,
+                                                                                                                                                bool                                   dynamicOffset,
+                                                                                                                                                bool                                   dynamicOffsetNonZero);
+
+private:
+       vk::Move<vk::VkBuffer>                                  createColorDataBuffer           (deUint32 offset, deUint32 bufferSize, const tcu::Vec4& value1, const tcu::Vec4& value2, de::MovePtr<vk::Allocation>* outAllocation);
+       vk::Move<vk::VkBufferView>                              createBufferView                        (vk::VkBuffer buffer, deUint32 offset) const;
+       vk::Move<vk::VkDescriptorSetLayout>             createDescriptorSetLayout       (void) const;
+       vk::Move<vk::VkDescriptorPool>                  createDescriptorPool            (void) const;
+       vk::Move<vk::VkDescriptorSet>                   createDescriptorSet                     (vk::VkDescriptorPool pool, vk::VkDescriptorSetLayout layout, vk::VkBufferView viewA, vk::VkBufferView viewB, vk::VkBufferView viewRes) const;
+
+       tcu::TestStatus                                                 iterate                                         (void);
+       void                                                                    logTestPlan                                     (void) const;
+       tcu::TestStatus                                                 testResourceAccess                      (void);
+
+       enum
+       {
+               STATIC_OFFSET_VALUE_A   = 256,
+               DYNAMIC_OFFSET_VALUE_A  = 512,
+               STATIC_OFFSET_VALUE_B   = 1024,
+               DYNAMIC_OFFSET_VALUE_B  = 768,
+       };
+
+       const vk::VkDescriptorType                              m_descriptorType;
+       const ShaderInputInterface                              m_shaderInterface;
+       const bool                                                              m_setViewOffset;
+       const bool                                                              m_setDynamicOffset;
+       const bool                                                              m_dynamicOffsetNonZero;
+
+       const vk::DeviceInterface&                              m_vki;
+       const vk::VkDevice                                              m_device;
+       const vk::VkQueue                                               m_queue;
+       const deUint32                                                  m_queueFamilyIndex;
+       vk::Allocator&                                                  m_allocator;
+
+       const ComputeInstanceResultBuffer               m_result;
+};
+
+BufferComputeInstance::BufferComputeInstance (Context&                                 context,
+                                                                                         vk::VkDescriptorType          descriptorType,
+                                                                                         ShaderInputInterface          shaderInterface,
+                                                                                         bool                                          viewOffset,
+                                                                                         bool                                          dynamicOffset,
+                                                                                         bool                                          dynamicOffsetNonZero)
+       : vkt::TestInstance                     (context)
+       , m_descriptorType                      (descriptorType)
+       , m_shaderInterface                     (shaderInterface)
+       , m_setViewOffset                       (viewOffset)
+       , m_setDynamicOffset            (dynamicOffset)
+       , m_dynamicOffsetNonZero        (dynamicOffsetNonZero)
+       , m_vki                                         (context.getDeviceInterface())
+       , m_device                                      (context.getDevice())
+       , m_queue                                       (context.getUniversalQueue())
+       , m_queueFamilyIndex            (context.getUniversalQueueFamilyIndex())
+       , m_allocator                           (context.getDefaultAllocator())
+       , m_result                                      (m_vki, m_device, m_allocator)
+{
+       if (m_dynamicOffsetNonZero)
+               DE_ASSERT(m_setDynamicOffset);
+}
+
+vk::Move<vk::VkBuffer> BufferComputeInstance::createColorDataBuffer (deUint32 offset, deUint32 bufferSize, const tcu::Vec4& value1, const tcu::Vec4& value2, de::MovePtr<vk::Allocation>* outAllocation)
+{
+       DE_ASSERT(offset + sizeof(tcu::Vec4[2]) <= bufferSize);
+
+       const bool                                              isUniformBuffer         = isUniformDescriptorType(m_descriptorType);
+       const vk::VkBufferUsageFlags    usageFlags                      = (isUniformBuffer) ? (vk::VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) : (vk::VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
+       const vk::VkBufferCreateInfo    createInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+               DE_NULL,
+               (vk::VkDeviceSize)bufferSize,   // size
+               usageFlags,                                             // usage
+               0u,                                                             // flags
+               vk::VK_SHARING_MODE_EXCLUSIVE,  // sharingMode
+               0u,                                                             // queueFamilyCount
+               DE_NULL,                                                // pQueueFamilyIndices
+       };
+       vk::Move<vk::VkBuffer>                  buffer                          (vk::createBuffer(m_vki, m_device, &createInfo));
+       de::MovePtr<vk::Allocation>             allocation                      (allocateAndBindObjectMemory(m_vki, m_device, m_allocator, *buffer, vk::MemoryRequirement::HostVisible));
+       void*                                                   mapPtr                          = allocation->getHostPtr();
+
+       if (offset)
+               deMemset(mapPtr, 0x5A, (size_t)offset);
+       deMemcpy((deUint8*)mapPtr + offset, value1.getPtr(), sizeof(tcu::Vec4));
+       deMemcpy((deUint8*)mapPtr + offset + sizeof(tcu::Vec4), value2.getPtr(), sizeof(tcu::Vec4));
+       deMemset((deUint8*)mapPtr + offset + 2 * sizeof(tcu::Vec4), 0x5A, (size_t)bufferSize - (size_t)offset - 2 * sizeof(tcu::Vec4));
+
+       flushMappedMemoryRange(m_vki, m_device, allocation->getMemory(), allocation->getOffset(), bufferSize);
+
+       *outAllocation = allocation;
+       return buffer;
+}
+
+vk::Move<vk::VkBufferView> BufferComputeInstance::createBufferView (vk::VkBuffer buffer, deUint32 offset) const
+{
+       const vk::VkBufferViewCreateInfo createInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO,
+               DE_NULL,
+               buffer,                                                                 // buffer
+               vk::VK_BUFFER_VIEW_TYPE_RAW,                    // viewType
+               vk::VK_FORMAT_UNDEFINED,                                // format
+               (vk::VkDeviceSize)offset,                               // offset
+               (vk::VkDeviceSize)sizeof(tcu::Vec4[2])  // range
+       };
+       return vk::createBufferView(m_vki, m_device, &createInfo);
+}
+
+vk::Move<vk::VkDescriptorSetLayout> BufferComputeInstance::createDescriptorSetLayout (void) const
+{
+       vk::DescriptorSetLayoutBuilder builder;
+
+       builder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+
+       switch (m_shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.addSingleBinding(m_descriptorType, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.addSingleBinding(m_descriptorType, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+                       builder.addSingleBinding(m_descriptorType, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.addArrayBinding(m_descriptorType, 2u, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       };
+
+       return builder.build(m_vki, m_device);
+}
+
+vk::Move<vk::VkDescriptorPool> BufferComputeInstance::createDescriptorPool (void) const
+{
+       return vk::DescriptorPoolBuilder()
+               .addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+               .addType(m_descriptorType, getInterfaceNumResources(m_shaderInterface))
+               .build(m_vki, m_device, vk::VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1);
+}
+
+vk::Move<vk::VkDescriptorSet> BufferComputeInstance::createDescriptorSet (vk::VkDescriptorPool pool, vk::VkDescriptorSetLayout layout, vk::VkBufferView viewA, vk::VkBufferView viewB, vk::VkBufferView viewRes) const
+{
+       const vk::VkDescriptorInfo              resultInfo              = createDescriptorInfo(viewRes);
+       const vk::VkDescriptorInfo              bufferInfos[2]  =
+       {
+               createDescriptorInfo(viewA),
+               createDescriptorInfo(viewB),
+       };
+
+       vk::Move<vk::VkDescriptorSet>   descriptorSet   = allocDescriptorSet(m_vki, m_device, pool, vk::VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, layout);
+       vk::DescriptorSetUpdateBuilder  builder;
+
+       // result
+       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultInfo);
+
+       // buffers
+       switch (m_shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), m_descriptorType, &bufferInfos[0]);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), m_descriptorType, &bufferInfos[0]);
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(2u), m_descriptorType, &bufferInfos[1]);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.writeArray(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), m_descriptorType, 2u, bufferInfos);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       builder.update(m_vki, m_device);
+       return descriptorSet;
+}
+
+tcu::TestStatus BufferComputeInstance::iterate (void)
+{
+       logTestPlan();
+       return testResourceAccess();
+}
+
+void BufferComputeInstance::logTestPlan (void) const
+{
+       std::ostringstream msg;
+
+       msg << "Accessing resource in a compute program.\n"
+               << "Single descriptor set. Descriptor set contains "
+                       << ((m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? "single" :
+                               (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS) ? "two" :
+                               (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY) ? "an array (size 2) of" :
+                               (const char*)DE_NULL)
+               << " source descriptor(s) of type " << vk::getDescriptorTypeName(m_descriptorType)
+               << " and one destination VK_DESCRIPTOR_TYPE_STORAGE_BUFFER to store results to.\n"
+               << "Source descriptor buffer view(s) have " << ((m_setViewOffset) ? ("non-") : ("")) << "zero offset.\n";
+
+       if (isDynamicDescriptorType(m_descriptorType))
+       {
+               if (m_setDynamicOffset)
+               {
+                       msg << "Source buffer(s) are given a dynamic offset at bind time.\n"
+                               << "The supplied dynamic offset is " << ((m_dynamicOffsetNonZero) ? ("non-") : ("")) << "zero.\n";
+               }
+               else
+               {
+                       msg << "Dynamic offset is not supplied at bind time. Expecting bind to offset 0.\n";
+               }
+       }
+
+       msg << "Destination buffer is pre-initialized to -1.\n";
+
+       m_context.getTestContext().getLog()
+               << tcu::TestLog::Message
+               << msg.str()
+               << tcu::TestLog::EndMessage;
+}
+
+tcu::TestStatus BufferComputeInstance::testResourceAccess (void)
+{
+       enum
+       {
+               ADDRESSABLE_SIZE = 256, // allocate a lot more than required
+       };
+
+       const bool                                                                              isDynamicCase           = isDynamicDescriptorType(m_descriptorType);
+       const bool                                                                              isUniformBuffer         = isUniformDescriptorType(m_descriptorType);
+       const deUint32                                                                  bindTimeOffsets[]       =
+       {
+               (m_dynamicOffsetNonZero) ? ((deUint32)DYNAMIC_OFFSET_VALUE_A) : (0u),
+               (m_dynamicOffsetNonZero) ? ((deUint32)DYNAMIC_OFFSET_VALUE_B) : (0u),
+       };
+
+       const tcu::Vec4                                                                 colorA1                         = tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f);
+       const tcu::Vec4                                                                 colorA2                         = tcu::Vec4(1.0f, 1.0f, 0.0f, 1.0f);
+       const tcu::Vec4                                                                 colorB1                         = tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f);
+       const tcu::Vec4                                                                 colorB2                         = tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f);
+
+       const deUint32                                                                  dataOffsetA                     = (isDynamicCase) ? (bindTimeOffsets[0]) : (m_setViewOffset) ? ((deUint32)STATIC_OFFSET_VALUE_A) : (0u);
+       const deUint32                                                                  dataOffsetB                     = (isDynamicCase) ? (bindTimeOffsets[1]) : (m_setViewOffset) ? ((deUint32)STATIC_OFFSET_VALUE_B) : (0u);
+       const deUint32                                                                  viewOffsetA                     = (m_setViewOffset) ? ((deUint32)STATIC_OFFSET_VALUE_A) : (0u);
+       const deUint32                                                                  viewOffsetB                     = (m_setViewOffset) ? ((deUint32)STATIC_OFFSET_VALUE_B) : (0u);
+       const deUint32                                                                  bufferSizeA                     = dataOffsetA + ADDRESSABLE_SIZE;
+       const deUint32                                                                  bufferSizeB                     = dataOffsetB + ADDRESSABLE_SIZE;
+
+       de::MovePtr<vk::Allocation>                                             bufferMemA;
+       const vk::Unique<vk::VkBuffer>                                  bufferA                         (createColorDataBuffer(dataOffsetA, bufferSizeA, colorA1, colorA2, &bufferMemA));
+       const vk::Unique<vk::VkBufferView>                              bufferViewA                     (createBufferView(*bufferA, viewOffsetA));
+
+       de::MovePtr<vk::Allocation>                                             bufferMemB;
+       const vk::Unique<vk::VkBuffer>                                  bufferB                         ((getInterfaceNumResources(m_shaderInterface) == 1u)
+                                                                                                                                                       ? (vk::Move<vk::VkBuffer>())
+                                                                                                                                                       : (createColorDataBuffer(dataOffsetB, bufferSizeB, colorB1, colorB2, &bufferMemB)));
+       const vk::Unique<vk::VkBufferView>                              bufferViewB                     ((getInterfaceNumResources(m_shaderInterface) == 1u)
+                                                                                                                                                       ? (vk::Move<vk::VkBufferView>())
+                                                                                                                                                       : (createBufferView(*bufferB, viewOffsetB)));
+
+       const vk::Unique<vk::VkDescriptorSetLayout>             descriptorSetLayout     (createDescriptorSetLayout());
+       const vk::Unique<vk::VkDescriptorPool>                  descriptorPool          (createDescriptorPool());
+       const vk::Unique<vk::VkDescriptorSet>                   descriptorSet           (createDescriptorSet(*descriptorPool, *descriptorSetLayout, *bufferViewA, *bufferViewB, m_result.getBufferView()));
+       const ComputePipeline                                                   pipeline                        (m_vki, m_device, m_context.getBinaryCollection(), 1, &descriptorSetLayout.get());
+
+       const vk::VkMemoryInputFlags                                    inputBit                        = (isUniformBuffer) ? (vk::VK_MEMORY_INPUT_UNIFORM_READ_BIT) : (vk::VK_MEMORY_INPUT_SHADER_READ_BIT);
+       const vk::VkBufferMemoryBarrier                                 bufferBarrierA          =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+               DE_NULL,
+               vk::VK_MEMORY_OUTPUT_HOST_WRITE_BIT,            // outputMask
+               inputBit,                                                                       // inputMask
+               vk::VK_QUEUE_FAMILY_IGNORED,                            // srcQueueFamilyIndex
+               vk::VK_QUEUE_FAMILY_IGNORED,                            // destQueueFamilyIndex
+               *bufferA,                                                                       // buffer
+               (vk::VkDeviceSize)0u,                                           // offset
+               (vk::VkDeviceSize)bufferSizeA,                          // size
+       };
+       const vk::VkBufferMemoryBarrier                                 bufferBarrierB          =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+               DE_NULL,
+               vk::VK_MEMORY_OUTPUT_HOST_WRITE_BIT,            // outputMask
+               inputBit,                                                                       // inputMask
+               vk::VK_QUEUE_FAMILY_IGNORED,                            // srcQueueFamilyIndex
+               vk::VK_QUEUE_FAMILY_IGNORED,                            // destQueueFamilyIndex
+               *bufferB,                                                                       // buffer
+               (vk::VkDeviceSize)0u,                                           // offset
+               (vk::VkDeviceSize)bufferSizeB,                          // size
+       };
+
+       const deUint32                                                                  numSrcBuffers           = getInterfaceNumResources(m_shaderInterface);
+
+       const vk::VkDescriptorSet                                               descriptorSets[]        = { *descriptorSet };
+       const int                                                                               numDescriptorSets       = DE_LENGTH_OF_ARRAY(descriptorSets);
+       const deUint32* const                                                   dynamicOffsets          = (m_setDynamicOffset) ? (bindTimeOffsets) : (DE_NULL);
+       const deUint32                                                                  numDynamicOffsets       = (m_setDynamicOffset) ? (numSrcBuffers) : (0);
+       const void* const                                                               preBarriers[]           = { &bufferBarrierA, &bufferBarrierB };
+       const int                                                                               numPreBarriers          = numSrcBuffers;
+       const void* const                                                               postBarriers[]          = { m_result.getResultReadBarrier() };
+       const int                                                                               numPostBarriers         = DE_LENGTH_OF_ARRAY(postBarriers);
+
+       const ComputeCommand                                                    compute                         (m_vki,
+                                                                                                                                                m_device,
+                                                                                                                                                pipeline.getPipeline(),
+                                                                                                                                                pipeline.getPipelineLayout(),
+                                                                                                                                                tcu::UVec3(4, 1, 1),
+                                                                                                                                                numDescriptorSets,     descriptorSets,
+                                                                                                                                                numDynamicOffsets,     dynamicOffsets,
+                                                                                                                                                numPreBarriers,        preBarriers,
+                                                                                                                                                numPostBarriers,       postBarriers);
+
+       const tcu::Vec4                                                                 refQuadrantValue14      = (m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR)         ? (colorA2) :
+                                                                                                                                                 (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS)      ? (colorB2) :
+                                                                                                                                                 (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY)          ? (colorB2) :
+                                                                                                                                                                                                                                                                       (tcu::Vec4(-2.0f));
+       const tcu::Vec4                                                                 refQuadrantValue23      = (m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR)         ? (colorA1) :
+                                                                                                                                                 (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS)      ? (colorA1) :
+                                                                                                                                                 (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY)          ? (colorA1) :
+                                                                                                                                                                                                                                                                       (tcu::Vec4(-2.0f));
+       const tcu::Vec4                                                                 references[4]           =
+       {
+               refQuadrantValue14,
+               refQuadrantValue23,
+               refQuadrantValue23,
+               refQuadrantValue14,
+       };
+       tcu::Vec4                                                                               results[4];
+
+       compute.submitAndWait(m_queueFamilyIndex, m_queue);
+       m_result.readResultContentsTo(&results);
+
+       // verify
+       if (results[0] == references[0] &&
+               results[1] == references[1] &&
+               results[2] == references[2] &&
+               results[3] == references[3])
+       {
+               return tcu::TestStatus::pass("Pass");
+       }
+       else if (results[0] == tcu::Vec4(-1.0f) &&
+                        results[1] == tcu::Vec4(-1.0f) &&
+                        results[2] == tcu::Vec4(-1.0f) &&
+                        results[3] == tcu::Vec4(-1.0f))
+       {
+               m_context.getTestContext().getLog()
+                       << tcu::TestLog::Message
+                       << "Result buffer was not written to."
+                       << tcu::TestLog::EndMessage;
+               return tcu::TestStatus::fail("Result buffer was not written to");
+       }
+       else
+       {
+               m_context.getTestContext().getLog()
+                       << tcu::TestLog::Message
+                       << "Error expected ["
+                               << references[0] << ", "
+                               << references[1] << ", "
+                               << references[2] << ", "
+                               << references[3] << "], got ["
+                               << results[0] << ", "
+                               << results[1] << ", "
+                               << results[2] << ", "
+                               << results[3] << "]"
+                       << tcu::TestLog::EndMessage;
+               return tcu::TestStatus::fail("Invalid result values");
+       }
+}
+
+class QuadrantRendederCase : public vkt::TestCase
+{
+public:
+                                                                       QuadrantRendederCase            (tcu::TestContext&              testCtx,
+                                                                                                                                const char*                    name,
+                                                                                                                                const char*                    description,
+                                                                                                                                glu::GLSLVersion               glslVersion,
+                                                                                                                                vk::VkShaderStageFlags exitingStages,
+                                                                                                                                vk::VkShaderStageFlags activeStages);
+private:
+       virtual std::string                             genExtensionDeclarations        (vk::VkShaderStage stage) const = 0;
+       virtual std::string                             genResourceDeclarations         (vk::VkShaderStage stage, int numUsedBindings) const = 0;
+       virtual std::string                             genResourceAccessSource         (vk::VkShaderStage stage) const = 0;
+       virtual std::string                             genNoAccessSource                       (void) const = 0;
+
+       std::string                                             genVertexSource                         (void) const;
+       std::string                                             genTessCtrlSource                       (void) const;
+       std::string                                             genTessEvalSource                       (void) const;
+       std::string                                             genGeometrySource                       (void) const;
+       std::string                                             genFragmentSource                       (void) const;
+       std::string                                             genComputeSource                        (void) const;
+
+       void                                                    initPrograms                            (vk::SourceCollections& programCollection) const;
+
+protected:
+       const glu::GLSLVersion                  m_glslVersion;
+       const vk::VkShaderStageFlags    m_exitingStages;
+       const vk::VkShaderStageFlags    m_activeStages;
+};
+
+QuadrantRendederCase::QuadrantRendederCase (tcu::TestContext&          testCtx,
+                                                                                       const char*                             name,
+                                                                                       const char*                             description,
+                                                                                       glu::GLSLVersion                glslVersion,
+                                                                                       vk::VkShaderStageFlags  exitingStages,
+                                                                                       vk::VkShaderStageFlags  activeStages)
+       : vkt::TestCase         (testCtx, name, description)
+       , m_glslVersion         (glslVersion)
+       , m_exitingStages       (exitingStages)
+       , m_activeStages        (activeStages)
+{
+       DE_ASSERT((m_exitingStages & m_activeStages) == m_activeStages);
+}
+
+std::string QuadrantRendederCase::genVertexSource (void) const
+{
+       const char* const       nextStageName   = ((m_exitingStages & vk::VK_SHADER_STAGE_TESS_CONTROL_BIT) != 0u)      ? ("tsc")
+                                                                               : ((m_exitingStages & vk::VK_SHADER_STAGE_GEOMETRY_BIT) != 0u)          ? ("geo")
+                                                                               : ((m_exitingStages & vk::VK_SHADER_STAGE_FRAGMENT_BIT) != 0u)          ? ("frag")
+                                                                               : (DE_NULL);
+       const char* const       versionDecl             = glu::getGLSLVersionDeclaration(m_glslVersion);
+       std::ostringstream      buf;
+
+       if ((m_activeStages & vk::VK_SHADER_STAGE_VERTEX_BIT) != 0u)
+       {
+               // active vertex shader
+               buf << versionDecl << "\n"
+                       << genExtensionDeclarations(vk::VK_SHADER_STAGE_VERTEX)
+                       << genResourceDeclarations(vk::VK_SHADER_STAGE_VERTEX, 0)
+                       << "layout(location = 0) out highp vec4 " << nextStageName << "_color;\n"
+                       << "layout(location = 1) flat out highp int " << nextStageName << "_quadrant_id;\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    highp vec4 result_position;\n"
+                       << "    highp int quadrant_id;\n"
+                       << s_quadrantGenVertexPosSource
+                       << "    gl_Position = result_position;\n"
+                       << "    " << nextStageName << "_quadrant_id = quadrant_id;\n"
+                       << "\n"
+                       << "    highp vec4 result_color;\n"
+                       << genResourceAccessSource(vk::VK_SHADER_STAGE_VERTEX)
+                       << "    " << nextStageName << "_color = result_color;\n"
+                       << "}\n";
+       }
+       else
+       {
+               // do nothing
+               buf << versionDecl << "\n"
+                       << genExtensionDeclarations(vk::VK_SHADER_STAGE_VERTEX)
+                       << "layout(location = 1) flat out highp int " << nextStageName << "_quadrant_id;\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    highp vec4 result_position;\n"
+                       << "    highp int quadrant_id;\n"
+                       << s_quadrantGenVertexPosSource
+                       << "    gl_Position = result_position;\n"
+                       << "    " << nextStageName << "_quadrant_id = quadrant_id;\n"
+                       << "}\n";
+       }
+
+       return buf.str();
+}
+
+std::string QuadrantRendederCase::genTessCtrlSource (void) const
+{
+       const char* const       versionDecl             = glu::getGLSLVersionDeclaration(m_glslVersion);
+       const bool                      extRequired             = glu::glslVersionIsES(m_glslVersion) && m_glslVersion <= glu::GLSL_VERSION_310_ES;
+       const char* const       tessExtDecl             = extRequired ? "#extension GL_EXT_tessellation_shader : require\n" : "";
+       std::ostringstream      buf;
+
+       if ((m_activeStages & vk::VK_SHADER_STAGE_TESS_CONTROL_BIT) != 0u)
+       {
+               // contributing not implemented
+               DE_ASSERT(m_activeStages == vk::VK_SHADER_STAGE_TESS_CONTROL_BIT);
+
+               // active tc shader
+               buf << versionDecl << "\n"
+                       << tessExtDecl
+                       << genExtensionDeclarations(vk::VK_SHADER_STAGE_TESS_CONTROL)
+                       << "layout(vertices=3) out;\n"
+                       << genResourceDeclarations(vk::VK_SHADER_STAGE_TESS_CONTROL, 0)
+                       << "layout(location = 1) flat in highp int tsc_quadrant_id[];\n"
+                       << "layout(location = 0) out highp vec4 tes_color[];\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    highp vec4 result_color;\n"
+                       << "    highp int quadrant_id = tsc_quadrant_id[gl_InvocationID];\n"
+                       << genResourceAccessSource(vk::VK_SHADER_STAGE_TESS_CONTROL)
+                       << "\n"
+                       << "    tes_color[gl_InvocationID] = result_color;\n"
+                       << "\n"
+                       << "    // no dynamic input block indexing\n"
+                       << "    highp vec4 position;\n"
+                       << "    if (gl_InvocationID == 0)\n"
+                       << "            position = gl_in[0].gl_Position;\n"
+                       << "    else if (gl_InvocationID == 1)\n"
+                       << "            position = gl_in[1].gl_Position;\n"
+                       << "    else\n"
+                       << "            position = gl_in[2].gl_Position;\n"
+                       << "    gl_out[gl_InvocationID].gl_Position = position;\n"
+                       << "    gl_TessLevelInner[0] = 2.8;\n"
+                       << "    gl_TessLevelInner[1] = 2.8;\n"
+                       << "    gl_TessLevelOuter[0] = 2.8;\n"
+                       << "    gl_TessLevelOuter[1] = 2.8;\n"
+                       << "    gl_TessLevelOuter[2] = 2.8;\n"
+                       << "    gl_TessLevelOuter[3] = 2.8;\n"
+                       << "}\n";
+       }
+       else if ((m_activeStages & vk::VK_SHADER_STAGE_TESS_EVALUATION_BIT) != 0u)
+       {
+               // active te shader, tc passthru
+               buf << versionDecl << "\n"
+                       << tessExtDecl
+                       << "layout(vertices=3) out;\n"
+                       << "layout(location = 1) flat in highp int tsc_quadrant_id[];\n"
+                       << "layout(location = 1) flat out highp int tes_quadrant_id[];\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    tes_quadrant_id[gl_InvocationID] = tsc_quadrant_id[0];\n"
+                       << "\n"
+                       << "    // no dynamic input block indexing\n"
+                       << "    highp vec4 position;\n"
+                       << "    if (gl_InvocationID == 0)\n"
+                       << "            position = gl_in[0].gl_Position;\n"
+                       << "    else if (gl_InvocationID == 1)\n"
+                       << "            position = gl_in[1].gl_Position;\n"
+                       << "    else\n"
+                       << "            position = gl_in[2].gl_Position;\n"
+                       << "    gl_out[gl_InvocationID].gl_Position = position;\n"
+                       << "    gl_TessLevelInner[0] = 2.8;\n"
+                       << "    gl_TessLevelInner[1] = 2.8;\n"
+                       << "    gl_TessLevelOuter[0] = 2.8;\n"
+                       << "    gl_TessLevelOuter[1] = 2.8;\n"
+                       << "    gl_TessLevelOuter[2] = 2.8;\n"
+                       << "    gl_TessLevelOuter[3] = 2.8;\n"
+                       << "}\n";
+       }
+       else
+       {
+               // passthrough not implemented
+               DE_FATAL("not implemented");
+       }
+
+       return buf.str();
+}
+
+std::string QuadrantRendederCase::genTessEvalSource (void) const
+{
+       const char* const       versionDecl             = glu::getGLSLVersionDeclaration(m_glslVersion);
+       const bool                      extRequired             = glu::glslVersionIsES(m_glslVersion) && m_glslVersion <= glu::GLSL_VERSION_310_ES;
+       const char* const       tessExtDecl             = extRequired ? "#extension GL_EXT_tessellation_shader : require\n" : "";
+       std::ostringstream      buf;
+
+       if ((m_activeStages & vk::VK_SHADER_STAGE_TESS_EVALUATION_BIT) != 0u)
+       {
+               // contributing not implemented
+               DE_ASSERT(m_activeStages == vk::VK_SHADER_STAGE_TESS_EVALUATION_BIT);
+
+               // active te shader
+               buf << versionDecl << "\n"
+                       << tessExtDecl
+                       << genExtensionDeclarations(vk::VK_SHADER_STAGE_TESS_EVALUATION)
+                       << "layout(triangles) in;\n"
+                       << genResourceDeclarations(vk::VK_SHADER_STAGE_TESS_EVALUATION, 0)
+                       << "layout(location = 1) flat in highp int tes_quadrant_id[];\n"
+                       << "layout(location = 0) out highp vec4 frag_color;\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    highp vec4 result_color;\n"
+                       << "    highp int quadrant_id = tes_quadrant_id[0];\n"
+                       << genResourceAccessSource(vk::VK_SHADER_STAGE_TESS_EVALUATION)
+                       << "\n"
+                       << "    frag_color = result_color;\n"
+                       << "    gl_Position = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * gl_in[1].gl_Position + gl_TessCoord.z * gl_in[2].gl_Position;\n"
+                       << "}\n";
+       }
+       else if ((m_activeStages & vk::VK_SHADER_STAGE_TESS_CONTROL_BIT) != 0u)
+       {
+               // contributing not implemented
+               DE_ASSERT(m_activeStages == vk::VK_SHADER_STAGE_TESS_CONTROL_BIT);
+
+               // active tc shader, te is passthru
+               buf << versionDecl << "\n"
+                       << tessExtDecl
+                       << "layout(triangles) in;\n"
+                       << "layout(location = 0) in highp vec4 tes_color[];\n"
+                       << "layout(location = 0) out highp vec4 frag_color;\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    frag_color = tes_color[0];\n"
+                       << "    gl_Position = gl_TessCoord.x * gl_in[0].gl_Position + gl_TessCoord.y * gl_in[1].gl_Position + gl_TessCoord.z * gl_in[2].gl_Position;\n"
+                       << "}\n";
+       }
+       else
+       {
+               // passthrough not implemented
+               DE_FATAL("not implemented");
+       }
+
+       return buf.str();
+}
+
+std::string QuadrantRendederCase::genGeometrySource (void) const
+{
+       const char* const       versionDecl             = glu::getGLSLVersionDeclaration(m_glslVersion);
+       const bool                      extRequired             = glu::glslVersionIsES(m_glslVersion) && m_glslVersion <= glu::GLSL_VERSION_310_ES;
+       const char* const       geomExtDecl             = extRequired ? "#extension GL_EXT_geometry_shader : require\n" : "";
+       std::ostringstream      buf;
+
+       if ((m_activeStages & vk::VK_SHADER_STAGE_GEOMETRY_BIT) != 0u)
+       {
+               // contributing not implemented
+               DE_ASSERT(m_activeStages == vk::VK_SHADER_STAGE_GEOMETRY_BIT);
+
+               // active geometry shader
+               buf << versionDecl << "\n"
+                       << geomExtDecl
+                       << genExtensionDeclarations(vk::VK_SHADER_STAGE_GEOMETRY)
+                       << "layout(triangles) in;\n"
+                       << "layout(triangle_strip, max_vertices=4) out;\n"
+                       << genResourceDeclarations(vk::VK_SHADER_STAGE_GEOMETRY, 0)
+                       << "layout(location = 1) flat in highp int geo_quadrant_id[];\n"
+                       << "layout(location = 0) out highp vec4 frag_color;\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    highp int quadrant_id;\n"
+                       << "    highp vec4 result_color;\n"
+                       << "\n"
+                       << "    quadrant_id = geo_quadrant_id[0];\n"
+                       << genResourceAccessSource(vk::VK_SHADER_STAGE_GEOMETRY)
+                       << "    frag_color = result_color;\n"
+                       << "    gl_Position = gl_in[0].gl_Position;\n"
+                       << "    EmitVertex();\n"
+                       << "\n"
+                       << "    quadrant_id = geo_quadrant_id[1];\n"
+                       << genResourceAccessSource(vk::VK_SHADER_STAGE_GEOMETRY)
+                       << "    frag_color = result_color;\n"
+                       << "    gl_Position = gl_in[1].gl_Position;\n"
+                       << "    EmitVertex();\n"
+                       << "\n"
+                       << "    quadrant_id = geo_quadrant_id[2];\n"
+                       << genResourceAccessSource(vk::VK_SHADER_STAGE_GEOMETRY)
+                       << "    frag_color = result_color;\n"
+                       << "    gl_Position = gl_in[0].gl_Position * 0.5 + gl_in[2].gl_Position * 0.5;\n"
+                       << "    EmitVertex();\n"
+                       << "\n"
+                       << "    quadrant_id = geo_quadrant_id[0];\n"
+                       << genResourceAccessSource(vk::VK_SHADER_STAGE_GEOMETRY)
+                       << "    frag_color = result_color;\n"
+                       << "    gl_Position = gl_in[2].gl_Position;\n"
+                       << "    EmitVertex();\n"
+                       << "}\n";
+       }
+       else
+       {
+               // passthrough not implemented
+               DE_FATAL("not implemented");
+       }
+
+       return buf.str();
+}
+
+std::string QuadrantRendederCase::genFragmentSource (void) const
+{
+       const char* const       versionDecl             = glu::getGLSLVersionDeclaration(m_glslVersion);
+       std::ostringstream      buf;
+
+       if ((m_activeStages & vk::VK_SHADER_STAGE_FRAGMENT_BIT) != 0u)
+       {
+               buf << versionDecl << "\n"
+                       << genExtensionDeclarations(vk::VK_SHADER_STAGE_GEOMETRY)
+                       << genResourceDeclarations(vk::VK_SHADER_STAGE_FRAGMENT, 0);
+
+               if (m_activeStages != vk::VK_SHADER_STAGE_FRAGMENT_BIT)
+               {
+                       // there are other stages, this is just a contributor
+                       buf << "layout(location = 0) in mediump vec4 frag_color;\n";
+               }
+
+               buf << "layout(location = 1) flat in highp int frag_quadrant_id;\n"
+                       << "layout(location = 0) out mediump vec4 o_color;\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    highp int quadrant_id = frag_quadrant_id;\n"
+                       << "    highp vec4 result_color;\n"
+                       << genResourceAccessSource(vk::VK_SHADER_STAGE_FRAGMENT);
+
+               if (m_activeStages != vk::VK_SHADER_STAGE_FRAGMENT_BIT)
+               {
+                       // just contributor
+                       buf     << "    if (frag_quadrant_id < 2)\n"
+                               << "            o_color = result_color;\n"
+                               << "    else\n"
+                               << "            o_color = frag_color;\n";
+               }
+               else
+                       buf << "        o_color = result_color;\n";
+
+               buf << "}\n";
+       }
+       else if (m_activeStages == 0u)
+       {
+               // special case, no active stages
+               buf << versionDecl << "\n"
+                       << "layout(location = 1) flat in highp int frag_quadrant_id;\n"
+                       << "layout(location = 0) out mediump vec4 o_color;\n"
+                       << "void main (void)\n"
+                       << "{\n"
+                       << "    highp int quadrant_id = frag_quadrant_id;\n"
+                       << "    highp vec4 result_color;\n"
+                       << genNoAccessSource()
+                       << "    o_color = result_color;\n"
+                       << "}\n";
+       }
+       else
+       {
+               // passthrough
+               buf <<  versionDecl << "\n"
+                       <<      "layout(location = 0) in mediump vec4 frag_color;\n"
+                               "layout(location = 0) out mediump vec4 o_color;\n"
+                               "void main (void)\n"
+                               "{\n"
+                               "       o_color = frag_color;\n"
+                               "}\n";
+       }
+
+       return buf.str();
+}
+
+std::string QuadrantRendederCase::genComputeSource (void) const
+{
+       const char* const       versionDecl             = glu::getGLSLVersionDeclaration(m_glslVersion);
+       std::ostringstream      buf;
+
+       buf     << versionDecl << "\n"
+               << genExtensionDeclarations(vk::VK_SHADER_STAGE_COMPUTE)
+               << "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
+               << genResourceDeclarations(vk::VK_SHADER_STAGE_COMPUTE, 1)
+               << "layout(set = 0, binding = 0, std140) writeonly buffer OutBuf\n"
+               << "{\n"
+               << "    highp vec4 read_colors[4];\n"
+               << "} b_out;\n"
+               << "void main(void)\n"
+               << "{\n"
+               << "    highp int quadrant_id = int(gl_WorkGroupID.x);\n"
+               << "    highp vec4 result_color;\n"
+               << genResourceAccessSource(vk::VK_SHADER_STAGE_COMPUTE)
+               << "    b_out.read_colors[gl_WorkGroupID.x] = result_color;\n"
+               << "}\n";
+
+       return buf.str();
+}
+
+void QuadrantRendederCase::initPrograms (vk::SourceCollections& programCollection) const
+{
+       if ((m_exitingStages & vk::VK_SHADER_STAGE_VERTEX_BIT) != 0u)
+               programCollection.glslSources.add("vertex") << glu::VertexSource(genVertexSource());
+
+       if ((m_exitingStages & vk::VK_SHADER_STAGE_TESS_CONTROL_BIT) != 0u)
+               programCollection.glslSources.add("tess_ctrl") << glu::TessellationControlSource(genTessCtrlSource());
+
+       if ((m_exitingStages & vk::VK_SHADER_STAGE_TESS_EVALUATION_BIT) != 0u)
+               programCollection.glslSources.add("tess_eval") << glu::TessellationEvaluationSource(genTessEvalSource());
+
+       if ((m_exitingStages & vk::VK_SHADER_STAGE_GEOMETRY_BIT) != 0u)
+               programCollection.glslSources.add("geometry") << glu::GeometrySource(genGeometrySource());
+
+       if ((m_exitingStages & vk::VK_SHADER_STAGE_FRAGMENT_BIT) != 0u)
+               programCollection.glslSources.add("fragment") << glu::FragmentSource(genFragmentSource());
+
+       if ((m_exitingStages & vk::VK_SHADER_STAGE_COMPUTE_BIT) != 0u)
+               programCollection.glslSources.add("compute") << glu::ComputeSource(genComputeSource());
+}
+
+class BufferDescriptorCase : public QuadrantRendederCase
+{
+public:
+       enum
+       {
+               FLAG_VIEW_OFFSET                        = (1u << 1u),
+               FLAG_DYNAMIC_OFFSET_ZERO        = (1u << 2u),
+               FLAG_DYNAMIC_OFFSET_NONZERO     = (1u << 3u),
+       };
+       // enum continues where resource flags ends
+       DE_STATIC_ASSERT((deUint32)FLAG_VIEW_OFFSET == (deUint32)RESOURCE_FLAG_LAST);
+
+                                                                       BufferDescriptorCase            (tcu::TestContext&              testCtx,
+                                                                                                                                const char*                    name,
+                                                                                                                                const char*                    description,
+                                                                                                                                bool                                   isPrimaryCmdBuf,
+                                                                                                                                vk::VkDescriptorType   descriptorType,
+                                                                                                                                vk::VkShaderStageFlags exitingStages,
+                                                                                                                                vk::VkShaderStageFlags activeStages,
+                                                                                                                                ShaderInputInterface   shaderInterface,
+                                                                                                                                deUint32                               flags);
+
+private:
+       std::string                                             genExtensionDeclarations        (vk::VkShaderStage stage) const;
+       std::string                                             genResourceDeclarations         (vk::VkShaderStage stage, int numUsedBindings) const;
+       std::string                                             genResourceAccessSource         (vk::VkShaderStage stage) const;
+       std::string                                             genNoAccessSource                       (void) const;
+
+       vkt::TestInstance*                              createInstance                          (vkt::Context& context) const;
+
+       const bool                                              m_viewOffset;
+       const bool                                              m_dynamicOffsetSet;
+       const bool                                              m_dynamicOffsetNonZero;
+       const bool                                              m_isPrimaryCmdBuf;
+       const vk::VkDescriptorType              m_descriptorType;
+       const ShaderInputInterface              m_shaderInterface;
+};
+
+BufferDescriptorCase::BufferDescriptorCase (tcu::TestContext&          testCtx,
+                                                                                       const char*                             name,
+                                                                                       const char*                             description,
+                                                                                       bool                                    isPrimaryCmdBuf,
+                                                                                       vk::VkDescriptorType    descriptorType,
+                                                                                       vk::VkShaderStageFlags  exitingStages,
+                                                                                       vk::VkShaderStageFlags  activeStages,
+                                                                                       ShaderInputInterface    shaderInterface,
+                                                                                       deUint32                                flags)
+       : QuadrantRendederCase          (testCtx, name, description, glu::GLSL_VERSION_310_ES, exitingStages, activeStages)
+       , m_viewOffset                          ((flags & FLAG_VIEW_OFFSET) != 0u)
+       , m_dynamicOffsetSet            ((flags & (FLAG_DYNAMIC_OFFSET_ZERO | FLAG_DYNAMIC_OFFSET_NONZERO)) != 0u)
+       , m_dynamicOffsetNonZero        ((flags & FLAG_DYNAMIC_OFFSET_NONZERO) != 0u)
+       , m_isPrimaryCmdBuf                     (isPrimaryCmdBuf)
+       , m_descriptorType                      (descriptorType)
+       , m_shaderInterface                     (shaderInterface)
+{
+}
+
+std::string BufferDescriptorCase::genExtensionDeclarations (vk::VkShaderStage stage) const
+{
+       DE_UNREF(stage);
+       return "";
+}
+
+std::string BufferDescriptorCase::genResourceDeclarations (vk::VkShaderStage stage, int numUsedBindings) const
+{
+       DE_UNREF(stage);
+
+       const bool                      isUniform               = isUniformDescriptorType(m_descriptorType);
+       const char* const       storageType             = (isUniform) ? ("uniform") : ("buffer");
+       std::ostringstream      buf;
+
+       switch (m_shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       buf     << "layout(set = 0, binding = " << (numUsedBindings) << ", std140) " << storageType << " BufferName\n"
+                               << "{\n"
+                               << "    highp vec4 colorA;\n"
+                               << "    highp vec4 colorB;\n"
+                               << "} b_instance;\n";
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       buf     << "layout(set = 0, binding = " << (numUsedBindings) << ", std140) " << storageType << " BufferNameA\n"
+                               << "{\n"
+                               << "    highp vec4 colorA;\n"
+                               << "    highp vec4 colorB;\n"
+                               << "} b_instanceA;\n"
+                               << "layout(set = 0, binding = " << (numUsedBindings+1) << ", std140) " << storageType << " BufferNameB\n"
+                               << "{\n"
+                               << "    highp vec4 colorA;\n"
+                               << "    highp vec4 colorB;\n"
+                               << "} b_instanceB;\n";
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       buf     << "layout(set = 0, binding = " << (numUsedBindings) << ", std140) " << storageType << " BufferName\n"
+                               << "{\n"
+                               << "    highp vec4 colorA;\n"
+                               << "    highp vec4 colorB;\n"
+                               << "} b_instances[2];\n";
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       return buf.str();
+}
+
+std::string BufferDescriptorCase::genResourceAccessSource (vk::VkShaderStage stage) const
+{
+       DE_UNREF(stage);
+
+       std::ostringstream buf;
+
+       switch (m_shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       buf << "        if (quadrant_id == 1 || quadrant_id == 2)\n"
+                               << "            result_color = b_instance.colorA;\n"
+                               << "    else\n"
+                               << "            result_color = b_instance.colorB;\n";
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       buf << "        if (quadrant_id == 1 || quadrant_id == 2)\n"
+                               << "            result_color = b_instanceA.colorA;\n"
+                               << "    else\n"
+                               << "            result_color = b_instanceB.colorB;\n";
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       buf << "        if (quadrant_id == 1 || quadrant_id == 2)\n"
+                               << "            result_color = b_instances[0].colorA;\n"
+                               << "    else\n"
+                               << "            result_color = b_instances[1].colorB;\n";
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       return buf.str();
+}
+
+std::string BufferDescriptorCase::genNoAccessSource (void) const
+{
+       return "        if (quadrant_id == 1 || quadrant_id == 2)\n"
+                  "            result_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
+                  "    else\n"
+                  "            result_color = vec4(1.0, 1.0, 0.0, 1.0);\n";
+}
+
+vkt::TestInstance* BufferDescriptorCase::createInstance (vkt::Context& context) const
+{
+       if (m_exitingStages == vk::VK_SHADER_STAGE_COMPUTE_BIT)
+       {
+               DE_ASSERT(m_isPrimaryCmdBuf); // secondaries are only valid within renderpass
+               return new BufferComputeInstance(context, m_descriptorType, m_shaderInterface, m_viewOffset, m_dynamicOffsetSet, m_dynamicOffsetNonZero);
+       }
+       else
+               return new BufferRenderInstance(context, m_isPrimaryCmdBuf, m_descriptorType, m_activeStages, m_shaderInterface, m_viewOffset, m_dynamicOffsetSet, m_dynamicOffsetNonZero);
+}
+
+class ImageInstanceImages
+{
+public:
+                                                                               ImageInstanceImages             (const vk::DeviceInterface&             vki,
+                                                                                                                                vk::VkDevice                                   device,
+                                                                                                                                deUint32                                               queueFamilyIndex,
+                                                                                                                                vk::VkQueue                                    queue,
+                                                                                                                                vk::Allocator&                                 allocator,
+                                                                                                                                vk::VkDescriptorType                   descriptorType,
+                                                                                                                                vk::VkImageViewType                    viewType,
+                                                                                                                                int                                                    numImages,
+                                                                                                                                deUint32                                               baseMipLevel,
+                                                                                                                                deUint32                                               baseArraySlice);
+
+private:
+       static vk::Move<vk::VkImage>            createImage                             (const vk::DeviceInterface&                     vki,
+                                                                                                                                vk::VkDevice                                           device,
+                                                                                                                                vk::Allocator&                                         allocator,
+                                                                                                                                vk::VkDescriptorType                           descriptorType,
+                                                                                                                                vk::VkImageViewType                            viewType,
+                                                                                                                                const tcu::TextureLevelPyramid&        sourceImage,
+                                                                                                                                de::MovePtr<vk::Allocation>*           outAllocation);
+
+       static vk::Move<vk::VkImageView>        createImageView                 (const vk::DeviceInterface&                     vki,
+                                                                                                                                vk::VkDevice                                           device,
+                                                                                                                                vk::VkImageViewType                            viewType,
+                                                                                                                                const tcu::TextureLevelPyramid&        sourceImage,
+                                                                                                                                vk::VkImage                                            image,
+                                                                                                                                deUint32                                                       baseMipLevel,
+                                                                                                                                deUint32                                                       baseArraySlice);
+
+       void                                                            populateSourceImage             (tcu::TextureLevelPyramid*                      dst,
+                                                                                                                                bool                                                           isFirst) const;
+
+       void                                                            uploadImage                             (const vk::DeviceInterface&                     vki,
+                                                                                                                                vk::VkDevice                                           device,
+                                                                                                                                deUint32                                                       queueFamilyIndex,
+                                                                                                                                vk::VkQueue                                            queue,
+                                                                                                                                vk::Allocator&                                         allocator,
+                                                                                                                                vk::VkImage                                            image,
+                                                                                                                                const tcu::TextureLevelPyramid&        data);
+
+protected:
+       enum
+       {
+               IMAGE_SIZE              = 64,
+               NUM_MIP_LEVELS  = 2,
+               ARRAY_SIZE              = 2,
+       };
+
+       const vk::VkImageViewType                       m_viewType;
+       const deUint32                                          m_baseMipLevel;
+       const deUint32                                          m_baseArraySlice;
+
+       const tcu::TextureFormat                        m_imageFormat;
+       tcu::TextureLevelPyramid                        m_sourceImageA;
+       tcu::TextureLevelPyramid                        m_sourceImageB;
+
+       de::MovePtr<vk::Allocation>                     m_imageMemoryA;
+       de::MovePtr<vk::Allocation>                     m_imageMemoryB;
+       vk::Move<vk::VkImage>                           m_imageA;
+       vk::Move<vk::VkImage>                           m_imageB;
+       vk::Move<vk::VkImageView>                       m_imageViewA;
+       vk::Move<vk::VkImageView>                       m_imageViewB;
+};
+
+ImageInstanceImages::ImageInstanceImages (const vk::DeviceInterface&   vki,
+                                                                                 vk::VkDevice                                  device,
+                                                                                 deUint32                                              queueFamilyIndex,
+                                                                                 vk::VkQueue                                   queue,
+                                                                                 vk::Allocator&                                allocator,
+                                                                                 vk::VkDescriptorType                  descriptorType,
+                                                                                 vk::VkImageViewType                   viewType,
+                                                                                 int                                                   numImages,
+                                                                                 deUint32                                              baseMipLevel,
+                                                                                 deUint32                                              baseArraySlice)
+       : m_viewType            (viewType)
+       , m_baseMipLevel        (baseMipLevel)
+       , m_baseArraySlice      (baseArraySlice)
+       , m_imageFormat         (tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8)
+       , m_sourceImageA        (m_imageFormat, NUM_MIP_LEVELS)
+       , m_sourceImageB        (m_imageFormat, NUM_MIP_LEVELS)
+       , m_imageMemoryA        (DE_NULL)
+       , m_imageMemoryB        (DE_NULL)
+       , m_imageA                      (vk::Move<vk::VkImage>())
+       , m_imageB                      (vk::Move<vk::VkImage>())
+       , m_imageViewA          (vk::Move<vk::VkImageView>())
+       , m_imageViewB          (vk::Move<vk::VkImageView>())
+{
+       DE_ASSERT(numImages == 1 || numImages == 2);
+
+       populateSourceImage(&m_sourceImageA, true);
+       m_imageA = createImage(vki, device, allocator, descriptorType, viewType, m_sourceImageA, &m_imageMemoryA);
+       m_imageViewA = createImageView(vki, device, viewType, m_sourceImageA, *m_imageA, m_baseMipLevel, m_baseArraySlice);
+       uploadImage(vki, device, queueFamilyIndex, queue, allocator, *m_imageA, m_sourceImageA);
+
+       if (numImages == 2)
+       {
+               populateSourceImage(&m_sourceImageB, false);
+               m_imageB = createImage(vki, device, allocator, descriptorType, viewType, m_sourceImageB, &m_imageMemoryB);
+               m_imageViewB = createImageView(vki, device, viewType, m_sourceImageB, *m_imageB, m_baseMipLevel, m_baseArraySlice);
+               uploadImage(vki, device, queueFamilyIndex, queue, allocator, *m_imageB, m_sourceImageB);
+       }
+}
+
+vk::Move<vk::VkImage> ImageInstanceImages::createImage (const vk::DeviceInterface&                     vki,
+                                                                                                               vk::VkDevice                                            device,
+                                                                                                               vk::Allocator&                                          allocator,
+                                                                                                               vk::VkDescriptorType                            descriptorType,
+                                                                                                               vk::VkImageViewType                                     viewType,
+                                                                                                               const tcu::TextureLevelPyramid&         sourceImage,
+                                                                                                               de::MovePtr<vk::Allocation>*            outAllocation)
+{
+       const tcu::ConstPixelBufferAccess       baseLevel       = sourceImage.getLevel(0);
+       const bool                                                      isCube          = (viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE || viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY);
+       const bool                                                      isStorage       = (descriptorType == vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
+       const deUint32                                          readUsage       = (isStorage) ? (vk::VK_IMAGE_USAGE_STORAGE_BIT) : (vk::VK_IMAGE_USAGE_SAMPLED_BIT);
+       const deUint32                                          arraySize       = (viewType == vk::VK_IMAGE_VIEW_TYPE_1D || viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY)                ? (baseLevel.getHeight())
+                                                                                                       : (viewType == vk::VK_IMAGE_VIEW_TYPE_2D || viewType == vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY)                ? (baseLevel.getDepth())
+                                                                                                       : (viewType == vk::VK_IMAGE_VIEW_TYPE_3D)                                                                                                               ? (1)
+                                                                                                       : (viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE || viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)    ? (baseLevel.getDepth()) // cube: numFaces * numLayers
+                                                                                                                                                                                                                                                                                                       : (0);
+       const vk::VkExtent3D                            extent          =
+       {
+               // x
+               (deInt32)baseLevel.getWidth(),
+
+               // y
+               (viewType == vk::VK_IMAGE_VIEW_TYPE_1D || viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY) ? (1) : (deInt32)baseLevel.getHeight(),
+
+               // z
+               (viewType == vk::VK_IMAGE_VIEW_TYPE_3D) ? ((deInt32)baseLevel.getDepth()) : (1),
+       };
+       const vk::VkImageCreateInfo                     createInfo      =
+       {
+               vk::VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
+               DE_NULL,
+               viewTypeToImageType(viewType),                                                                                  // imageType
+               mapToVkTextureFormat(baseLevel.getFormat()),                                                    // format
+               extent,                                                                                                                                 // extent
+               (deUint32)sourceImage.getNumLevels(),                                                                   // mipLevels
+               arraySize,                                                                                                                              // arraySize
+               1,                                                                                                                                              // samples
+               vk::VK_IMAGE_TILING_OPTIMAL,                                                                                    // tiling
+               readUsage | vk::VK_IMAGE_USAGE_TRANSFER_DESTINATION_BIT,                                // usage
+               isCube ? ((deUint32)vk::VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) : (0u),    // flags
+               vk::VK_SHARING_MODE_EXCLUSIVE,                                                                                  // sharingMode
+               0u,                                                                                                                                             // queueFamilyCount
+               DE_NULL,                                                                                                                                // pQueueFamilyIndices
+       };
+       vk::Move<vk::VkImage>                           image           (vk::createImage(vki, device, &createInfo));
+
+       *outAllocation = allocateAndBindObjectMemory(vki, device, allocator, *image, vk::MemoryRequirement::Any);
+       return image;
+}
+
+vk::Move<vk::VkImageView> ImageInstanceImages::createImageView (const vk::DeviceInterface&                     vki,
+                                                                                                                               vk::VkDevice                                            device,
+                                                                                                                               vk::VkImageViewType                                     viewType,
+                                                                                                                               const tcu::TextureLevelPyramid&         sourceImage,
+                                                                                                                               vk::VkImage                                                     image,
+                                                                                                                               deUint32                                                        baseMipLevel,
+                                                                                                                               deUint32                                                        baseArraySlice)
+{
+       const tcu::ConstPixelBufferAccess       baseLevel                       = sourceImage.getLevel(0);
+       const deUint32                                          viewTypeBaseSlice       = (viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE || viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) ? (6 * baseArraySlice) : (baseArraySlice);
+       const deUint32                                          viewArraySize           = (viewType == vk::VK_IMAGE_VIEW_TYPE_1D)                       ? (1)
+                                                                                                                       : (viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY)         ? (baseLevel.getHeight() - viewTypeBaseSlice)
+                                                                                                                       : (viewType == vk::VK_IMAGE_VIEW_TYPE_2D)                       ? (1)
+                                                                                                                       : (viewType == vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY)         ? (baseLevel.getDepth() - viewTypeBaseSlice)
+                                                                                                                       : (viewType == vk::VK_IMAGE_VIEW_TYPE_3D)                       ? (1)
+                                                                                                                       : (viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE)                     ? (6)
+                                                                                                                       : (viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)       ? (baseLevel.getDepth() - viewTypeBaseSlice) // cube: numFaces * numLayers
+                                                                                                                                                                                                                               : (0);
+
+       DE_ASSERT(viewArraySize > 0);
+
+       const vk::VkImageSubresourceRange       resourceRange   =
+       {
+               vk::VK_IMAGE_ASPECT_COLOR,                                              // aspect
+               baseMipLevel,                                                                   // baseMipLevel
+               sourceImage.getNumLevels() - baseMipLevel,              // mipLevels
+               viewTypeBaseSlice,                                                              // baseArraySlice
+               viewArraySize,                                                                  // arraySize
+       };
+       const vk::VkImageViewCreateInfo         createInfo              =
+       {
+               vk::VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
+               DE_NULL,
+               image,                                                                                  // image
+               viewType,                                                                               // viewType
+               mapToVkTextureFormat(baseLevel.getFormat()),    // format
+               {
+                       vk::VK_CHANNEL_SWIZZLE_R,
+                       vk::VK_CHANNEL_SWIZZLE_G,
+                       vk::VK_CHANNEL_SWIZZLE_B,
+                       vk::VK_CHANNEL_SWIZZLE_A
+               },                                                                                              // channels
+               resourceRange,                                                                  // subresourceRange
+       };
+       return vk::createImageView(vki, device, &createInfo);
+}
+
+void ImageInstanceImages::populateSourceImage (tcu::TextureLevelPyramid* dst, bool isFirst) const
+{
+       const int numLevels = dst->getNumLevels();
+
+       for (int level = 0; level < numLevels; ++level)
+       {
+               const int       width   = IMAGE_SIZE >> level;
+               const int       height  = (m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D || m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY)            ? (ARRAY_SIZE)
+                                                                                                                                                                                                                                                               : (IMAGE_SIZE >> level);
+               const int       depth   = (m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D || m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY)            ? (1)
+                                                       : (m_viewType == vk::VK_IMAGE_VIEW_TYPE_2D || m_viewType == vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY)            ? (ARRAY_SIZE)
+                                                       : (m_viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE || m_viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)        ? (6 * ARRAY_SIZE)
+                                                       : (m_viewType == vk::VK_IMAGE_VIEW_TYPE_3D)                                                                                                                     ? (IMAGE_SIZE >> level)
+                                                                                                                                                                                                                                                               : (1);
+
+               dst->allocLevel(level, width, height, depth);
+
+               {
+                       const tcu::PixelBufferAccess levelAccess = dst->getLevel(level);
+
+                       for (int z = 0; z < depth; ++z)
+                       for (int y = 0; y < height; ++y)
+                       for (int x = 0; x < width; ++x)
+                       {
+                               const int                       gradPos = x + y + z;
+                               const int                       gradMax = width + height + depth - 3;
+
+                               const int                       red             = 255 * gradPos / gradMax;                                                                                                      //!< gradient from 0 -> max (detects large offset errors)
+                               const int                       green   = ((gradPos % 2 == 0) ? (127) : (0)) + ((gradPos % 4 < 3) ? (128) : (0));       //!< 3-level M pattern (detects small offset errors)
+                               const int                       blue    = (128 * level / numLevels) + (isFirst ? 127 : 0);                                                      //!< level and image index (detects incorrect lod / image)
+
+                               DE_ASSERT(de::inRange(red, 0, 255));
+                               DE_ASSERT(de::inRange(green, 0, 255));
+                               DE_ASSERT(de::inRange(blue, 0, 255));
+
+                               levelAccess.setPixel(tcu::IVec4(red, green, blue, 255), x, y, z);
+                       }
+               }
+       }
+}
+
+void ImageInstanceImages::uploadImage (const vk::DeviceInterface&              vki,
+                                                                          vk::VkDevice                                         device,
+                                                                          deUint32                                                     queueFamilyIndex,
+                                                                          vk::VkQueue                                          queue,
+                                                                          vk::Allocator&                                       allocator,
+                                                                          vk::VkImage                                          image,
+                                                                          const tcu::TextureLevelPyramid&      data)
+{
+       const deUint32                                          arraySize                                       = (m_viewType == vk::VK_IMAGE_VIEW_TYPE_3D) ? (1) :
+                                                                                                                                         (m_viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE || m_viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) ? (6 * (deUint32)ARRAY_SIZE) :
+                                                                                                                                         ((deUint32)ARRAY_SIZE);
+       const deUint32                                          dataBufferSize                          = getTextureLevelPyramidDataSize(data);
+       const vk::VkBufferCreateInfo            bufferCreateInfo                        =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+               DE_NULL,
+               dataBufferSize,                                                                         // size
+               vk::VK_BUFFER_USAGE_TRANSFER_SOURCE_BIT,                        // usage
+               0u,                                                                                                     // flags
+               vk::VK_SHARING_MODE_EXCLUSIVE,                                          // sharingMode
+               0u,                                                                                                     // queueFamilyCount
+               DE_NULL,                                                                                        // pQueueFamilyIndices
+       };
+       const vk::Unique<vk::VkBuffer>          dataBuffer                                      (vk::createBuffer(vki, device, &bufferCreateInfo));
+       const de::MovePtr<vk::Allocation>       dataBufferMemory                        = allocateAndBindObjectMemory(vki, device, allocator, *dataBuffer, vk::MemoryRequirement::HostVisible);
+       const vk::VkFenceCreateInfo                     fenceCreateInfo                         =
+       {
+               vk::VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
+               DE_NULL,
+               0u,                                                                                                     // flags
+       };
+       const vk::VkBufferMemoryBarrier         preMemoryBarrier                        =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+               DE_NULL,
+               vk::VK_MEMORY_OUTPUT_HOST_WRITE_BIT,                            // outputMask
+               vk::VK_MEMORY_INPUT_TRANSFER_BIT,                                       // inputMask
+               vk::VK_QUEUE_FAMILY_IGNORED,                                            // srcQueueFamilyIndex
+               vk::VK_QUEUE_FAMILY_IGNORED,                                            // destQueueFamilyIndex
+               *dataBuffer,                                                                            // buffer
+               0u,                                                                                                     // offset
+               dataBufferSize,                                                                         // size
+       };
+       const vk::VkImageSubresourceRange       fullSubrange                            =
+       {
+               vk::VK_IMAGE_ASPECT_COLOR,                                                      // aspect
+               0u,                                                                                                     // baseMipLevel
+               (deUint32)data.getNumLevels(),                                          // mipLevels
+               0u,                                                                                                     // baseArraySlice
+               arraySize,                                                                                      // arraySize
+       };
+       const vk::VkImageMemoryBarrier          preImageBarrier                         =
+       {
+               vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+               DE_NULL,
+               0u,                                                                                                     // outputMask
+               0u,                                                                                                     // inputMask
+               vk::VK_IMAGE_LAYOUT_UNDEFINED,                                          // oldLayout
+               vk::VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL,       // newLayout
+               vk::VK_QUEUE_FAMILY_IGNORED,                                            // srcQueueFamilyIndex
+               vk::VK_QUEUE_FAMILY_IGNORED,                                            // destQueueFamilyIndex
+               image,                                                                                          // image
+               fullSubrange                                                                            // subresourceRange
+       };
+       const vk::VkImageMemoryBarrier          postImageBarrier                        =
+       {
+               vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+               DE_NULL,
+               vk::VK_MEMORY_OUTPUT_TRANSFER_BIT,                                      // outputMask
+               vk::VK_MEMORY_INPUT_SHADER_READ_BIT,                            // inputMask
+               vk::VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL,       // oldLayout
+               vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,           // newLayout
+               vk::VK_QUEUE_FAMILY_IGNORED,                                            // srcQueueFamilyIndex
+               vk::VK_QUEUE_FAMILY_IGNORED,                                            // destQueueFamilyIndex
+               image,                                                                                          // image
+               fullSubrange                                                                            // subresourceRange
+       };
+       const vk::VkCmdPoolCreateInfo           cmdPoolCreateInfo                       =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_POOL_CREATE_INFO,
+               DE_NULL,
+               queueFamilyIndex,                                                                       // queueFamilyIndex
+               vk::VK_CMD_POOL_CREATE_TRANSIENT_BIT,                           // flags
+       };
+       const vk::Unique<vk::VkCmdPool>         cmdPool                                         (vk::createCommandPool(vki, device, &cmdPoolCreateInfo));
+       const vk::VkCmdBufferCreateInfo         cmdBufCreateInfo                        =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_BUFFER_CREATE_INFO,
+               DE_NULL,
+               *cmdPool,                                                                                       // cmdPool
+               vk::VK_CMD_BUFFER_LEVEL_PRIMARY,                                        // level
+               0u,                                                                                                     // flags
+       };
+       const vk::VkCmdBufferBeginInfo          cmdBufBeginInfo                         =
+       {
+               vk::VK_STRUCTURE_TYPE_CMD_BUFFER_BEGIN_INFO,
+               DE_NULL,
+               vk::VK_CMD_BUFFER_OPTIMIZE_SMALL_BATCH_BIT | vk::VK_CMD_BUFFER_OPTIMIZE_ONE_TIME_SUBMIT_BIT,    // flags
+               (vk::VkRenderPass)0u,                                                                                                                                                   // renderPass
+               (vk::VkFramebuffer)0u,                                                                                                                                                  // framebuffer
+       };
+
+       const vk::Unique<vk::VkCmdBuffer>       cmd                                                     (vk::createCommandBuffer(vki, device, &cmdBufCreateInfo));
+       const void* const                                       preBarriers[2]                          = { &preMemoryBarrier, &preImageBarrier };
+       const void* const                                       postBarriers[1]                         = { &postImageBarrier };
+       const vk::Unique<vk::VkFence>           cmdCompleteFence                        (vk::createFence(vki, device, &fenceCreateInfo));
+       const deUint64                                          infiniteTimeout                         = ~(deUint64)0u;
+       std::vector<vk::VkBufferImageCopy>      copySlices;
+
+       // copy data to buffer
+       writeTextureLevelPyramidData(dataBufferMemory->getHostPtr(), dataBufferSize, data, m_viewType , &copySlices);
+       flushMappedMemoryRange(vki, device, dataBufferMemory->getMemory(), dataBufferMemory->getOffset(), dataBufferSize);
+
+       // record command buffer
+       VK_CHECK(vki.beginCommandBuffer(*cmd, &cmdBufBeginInfo));
+       vki.cmdPipelineBarrier(*cmd, 0u, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_FALSE, DE_LENGTH_OF_ARRAY(preBarriers), preBarriers);
+       vki.cmdCopyBufferToImage(*cmd, *dataBuffer, image, vk::VK_IMAGE_LAYOUT_TRANSFER_DESTINATION_OPTIMAL, (deUint32)copySlices.size(), &copySlices[0]);
+       vki.cmdPipelineBarrier(*cmd, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::VK_FALSE, DE_LENGTH_OF_ARRAY(postBarriers), postBarriers);
+       VK_CHECK(vki.endCommandBuffer(*cmd));
+
+       // submit and wait for command buffer to complete before killing it
+       VK_CHECK(vki.queueSubmit(queue, 1, &cmd.get(), *cmdCompleteFence));
+       VK_CHECK(vki.waitForFences(device, 1, &cmdCompleteFence.get(), 0u, infiniteTimeout)); // \note: timeout is failure
+}
+
+class ImageFetchInstanceImages : private ImageInstanceImages
+{
+public:
+                                                               ImageFetchInstanceImages        (const vk::DeviceInterface&             vki,
+                                                                                                                        vk::VkDevice                                   device,
+                                                                                                                        deUint32                                               queueFamilyIndex,
+                                                                                                                        vk::VkQueue                                    queue,
+                                                                                                                        vk::Allocator&                                 allocator,
+                                                                                                                        vk::VkDescriptorType                   descriptorType,
+                                                                                                                        ShaderInputInterface                   shaderInterface,
+                                                                                                                        vk::VkImageViewType                    viewType,
+                                                                                                                        deUint32                                               baseMipLevel,
+                                                                                                                        deUint32                                               baseArraySlice);
+
+       static tcu::IVec3                       getFetchPos                                     (vk::VkImageViewType viewType, deUint32 baseMipLevel, deUint32 baseArraySlice, int fetchPosNdx);
+       tcu::Vec4                                       fetchImageValue                         (int fetchPosNdx) const;
+
+       inline vk::VkImageView          getImageViewA                           (void) const { return *m_imageViewA; }
+       inline vk::VkImageView          getImageViewB                           (void) const { return *m_imageViewB; }
+
+private:
+       enum
+       {
+               // some arbitrary sample points for all four quadrants
+               SAMPLE_POINT_0_X = 6,
+               SAMPLE_POINT_0_Y = 13,
+               SAMPLE_POINT_0_Z = 49,
+
+               SAMPLE_POINT_1_X = 51,
+               SAMPLE_POINT_1_Y = 40,
+               SAMPLE_POINT_1_Z = 44,
+
+               SAMPLE_POINT_2_X = 42,
+               SAMPLE_POINT_2_Y = 26,
+               SAMPLE_POINT_2_Z = 19,
+
+               SAMPLE_POINT_3_X = 25,
+               SAMPLE_POINT_3_Y = 25,
+               SAMPLE_POINT_3_Z = 18,
+       };
+
+       const ShaderInputInterface      m_shaderInterface;
+};
+
+ImageFetchInstanceImages::ImageFetchInstanceImages (const vk::DeviceInterface& vki,
+                                                                                                       vk::VkDevice                            device,
+                                                                                                       deUint32                                        queueFamilyIndex,
+                                                                                                       vk::VkQueue                                     queue,
+                                                                                                       vk::Allocator&                          allocator,
+                                                                                                       vk::VkDescriptorType            descriptorType,
+                                                                                                       ShaderInputInterface            shaderInterface,
+                                                                                                       vk::VkImageViewType                     viewType,
+                                                                                                       deUint32                                        baseMipLevel,
+                                                                                                       deUint32                                        baseArraySlice)
+       : ImageInstanceImages   (vki,
+                                                        device,
+                                                        queueFamilyIndex,
+                                                        queue,
+                                                        allocator,
+                                                        descriptorType,
+                                                        viewType,
+                                                        getInterfaceNumResources(shaderInterface),     // numImages
+                                                        baseMipLevel,
+                                                        baseArraySlice)
+       , m_shaderInterface             (shaderInterface)
+{
+}
+
+bool isImageViewTypeArray (vk::VkImageViewType type)
+{
+       return type == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY || type == vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY || type == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
+}
+
+tcu::IVec3 ImageFetchInstanceImages::getFetchPos (vk::VkImageViewType viewType, deUint32 baseMipLevel, deUint32 baseArraySlice, int fetchPosNdx)
+{
+       const tcu::IVec3        fetchPositions[4]       =
+       {
+               tcu::IVec3(SAMPLE_POINT_0_X, SAMPLE_POINT_0_Y, SAMPLE_POINT_0_Z),
+               tcu::IVec3(SAMPLE_POINT_1_X, SAMPLE_POINT_1_Y, SAMPLE_POINT_1_Z),
+               tcu::IVec3(SAMPLE_POINT_2_X, SAMPLE_POINT_2_Y, SAMPLE_POINT_2_Z),
+               tcu::IVec3(SAMPLE_POINT_3_X, SAMPLE_POINT_3_Y, SAMPLE_POINT_3_Z),
+       };
+       const tcu::IVec3        coord                           = de::getSizedArrayElement<4>(fetchPositions, fetchPosNdx);
+       const deUint32          imageSize                       = (deUint32)IMAGE_SIZE >> baseMipLevel;
+       const deUint32          arraySize                       = isImageViewTypeArray(viewType) ? ARRAY_SIZE - baseArraySlice : 1;
+
+       switch (viewType)
+       {
+               case vk::VK_IMAGE_VIEW_TYPE_1D:
+               case vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY:   return tcu::IVec3(coord.x() % imageSize, coord.y() % arraySize, 0);
+               case vk::VK_IMAGE_VIEW_TYPE_2D:
+               case vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY:   return tcu::IVec3(coord.x() % imageSize, coord.y() % imageSize, coord.z() % arraySize);
+               case vk::VK_IMAGE_VIEW_TYPE_CUBE:
+               case vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY: return tcu::IVec3(coord.x() % imageSize, coord.y() % imageSize, coord.z() % (arraySize * 6));
+               case vk::VK_IMAGE_VIEW_TYPE_3D:                 return tcu::IVec3(coord.x() % imageSize, coord.y() % imageSize, coord.z() % imageSize);
+               default:
+                       DE_FATAL("Impossible");
+                       return tcu::IVec3();
+       }
+}
+
+tcu::Vec4 ImageFetchInstanceImages::fetchImageValue (int fetchPosNdx) const
+{
+       DE_ASSERT(de::inBounds(fetchPosNdx, 0, 4));
+
+       const tcu::TextureLevelPyramid& fetchSrcA       = m_sourceImageA;
+       const tcu::TextureLevelPyramid& fetchSrcB       = (m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? (m_sourceImageA) : (m_sourceImageB);
+       const tcu::TextureLevelPyramid& fetchSrc        = ((fetchPosNdx % 2) == 0) ? (fetchSrcA) : (fetchSrcB); // sampling order is ABAB
+       const tcu::IVec3                                fetchPos        = getFetchPos(m_viewType, m_baseMipLevel, m_baseArraySlice, fetchPosNdx);
+
+       return fetchSrc.getLevel(m_baseMipLevel).getPixel(fetchPos.x(), fetchPos.y(), fetchPos.z());
+}
+
+class ImageFetchRenderInstance : public SingleCmdRenderInstance
+{
+public:
+                                                                                                       ImageFetchRenderInstance        (vkt::Context&                  context,
+                                                                                                                                                                bool                                   isPrimaryCmdBuf,
+                                                                                                                                                                vk::VkDescriptorType   descriptorType,
+                                                                                                                                                                vk::VkShaderStageFlags stageFlags,
+                                                                                                                                                                ShaderInputInterface   shaderInterface,
+                                                                                                                                                                vk::VkImageViewType    viewType,
+                                                                                                                                                                deUint32                               baseMipLevel,
+                                                                                                                                                                deUint32                               baseArraySlice);
+
+private:
+       static vk::Move<vk::VkDescriptorSetLayout>              createDescriptorSetLayout       (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkDescriptorType           descriptorType,
+                                                                                                                                                                ShaderInputInterface           shaderInterface,
+                                                                                                                                                                vk::VkShaderStageFlags         stageFlags);
+
+       static vk::Move<vk::VkPipelineLayout>                   createPipelineLayout            (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkDescriptorSetLayout      descriptorSetLayout);
+
+       static vk::Move<vk::VkDescriptorPool>                   createDescriptorPool            (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkDescriptorType           descriptorType,
+                                                                                                                                                                ShaderInputInterface           shaderInterface);
+
+       static vk::Move<vk::VkDescriptorSet>                    createDescriptorSet                     (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkDescriptorType           descriptorType,
+                                                                                                                                                                ShaderInputInterface           shaderInterface,
+                                                                                                                                                                vk::VkDescriptorSetLayout      layout,
+                                                                                                                                                                vk::VkDescriptorPool           pool,
+                                                                                                                                                                vk::VkImageView                        viewA,
+                                                                                                                                                                vk::VkImageView                        viewB);
+
+       void                                                                                    logTestPlan                                     (void) const;
+       vk::VkPipelineLayout                                                    getPipelineLayout                       (void) const;
+       void                                                                                    writeDrawCmdBuffer                      (vk::VkCmdBuffer cmd) const;
+       tcu::TestStatus                                                                 verifyResultImage                       (const tcu::ConstPixelBufferAccess& result) const;
+
+       enum
+       {
+               RENDER_SIZE = 128,
+       };
+
+       const vk::VkDescriptorType                                              m_descriptorType;
+       const vk::VkShaderStageFlags                                    m_stageFlags;
+       const ShaderInputInterface                                              m_shaderInterface;
+       const vk::VkImageViewType                                               m_viewType;
+       const deUint32                                                                  m_baseMipLevel;
+       const deUint32                                                                  m_baseArraySlice;
+
+       const vk::Unique<vk::VkDescriptorSetLayout>             m_descriptorSetLayout;
+       const vk::Unique<vk::VkPipelineLayout>                  m_pipelineLayout;
+       const ImageFetchInstanceImages                                  m_images;
+       const vk::Unique<vk::VkDescriptorPool>                  m_descriptorPool;
+       const vk::Unique<vk::VkDescriptorSet>                   m_descriptorSet;
+};
+
+ImageFetchRenderInstance::ImageFetchRenderInstance     (vkt::Context&                  context,
+                                                                                                        bool                                   isPrimaryCmdBuf,
+                                                                                                        vk::VkDescriptorType   descriptorType,
+                                                                                                        vk::VkShaderStageFlags stageFlags,
+                                                                                                        ShaderInputInterface   shaderInterface,
+                                                                                                        vk::VkImageViewType    viewType,
+                                                                                                        deUint32                               baseMipLevel,
+                                                                                                        deUint32                               baseArraySlice)
+       : SingleCmdRenderInstance       (context, isPrimaryCmdBuf, tcu::UVec2(RENDER_SIZE, RENDER_SIZE))
+       , m_descriptorType                      (descriptorType)
+       , m_stageFlags                          (stageFlags)
+       , m_shaderInterface                     (shaderInterface)
+       , m_viewType                            (viewType)
+       , m_baseMipLevel                        (baseMipLevel)
+       , m_baseArraySlice                      (baseArraySlice)
+       , m_descriptorSetLayout         (createDescriptorSetLayout(m_vki, m_device, m_descriptorType, m_shaderInterface, m_stageFlags))
+       , m_pipelineLayout                      (createPipelineLayout(m_vki, m_device, *m_descriptorSetLayout))
+       , m_images                                      (m_vki, m_device, m_queueFamilyIndex, m_queue, m_allocator, m_descriptorType, m_shaderInterface, m_viewType, m_baseMipLevel, m_baseArraySlice)
+       , m_descriptorPool                      (createDescriptorPool(m_vki, m_device, m_descriptorType, m_shaderInterface))
+       , m_descriptorSet                       (createDescriptorSet(m_vki, m_device, m_descriptorType, m_shaderInterface, *m_descriptorSetLayout, *m_descriptorPool, m_images.getImageViewA(), m_images.getImageViewB()))
+{
+}
+
+vk::Move<vk::VkDescriptorSetLayout> ImageFetchRenderInstance::createDescriptorSetLayout (const vk::DeviceInterface&            vki,
+                                                                                                                                                                                vk::VkDevice                                   device,
+                                                                                                                                                                                vk::VkDescriptorType                   descriptorType,
+                                                                                                                                                                                ShaderInputInterface                   shaderInterface,
+                                                                                                                                                                                vk::VkShaderStageFlags                 stageFlags)
+{
+       vk::DescriptorSetLayoutBuilder builder;
+
+       switch (shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.addSingleBinding(descriptorType, stageFlags);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.addSingleBinding(descriptorType, stageFlags);
+                       builder.addSingleBinding(descriptorType, stageFlags);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.addArrayBinding(descriptorType, 2u, stageFlags);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       return builder.build(vki, device);
+}
+
+vk::Move<vk::VkPipelineLayout> ImageFetchRenderInstance::createPipelineLayout (const vk::DeviceInterface&      vki,
+                                                                                                                                                          vk::VkDevice                                 device,
+                                                                                                                                                          vk::VkDescriptorSetLayout    descriptorSetLayout)
+{
+       const vk::VkPipelineLayoutCreateInfo createInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+               DE_NULL,
+               1,                                              // descriptorSetCount
+               &descriptorSetLayout,   // pSetLayouts
+               0u,                                             // pushConstantRangeCount
+               DE_NULL,                                // pPushConstantRanges
+       };
+       return vk::createPipelineLayout(vki, device, &createInfo);
+}
+
+vk::Move<vk::VkDescriptorPool> ImageFetchRenderInstance::createDescriptorPool (const vk::DeviceInterface&      vki,
+                                                                                                                                                          vk::VkDevice                                 device,
+                                                                                                                                                          vk::VkDescriptorType                 descriptorType,
+                                                                                                                                                          ShaderInputInterface                 shaderInterface)
+{
+       return vk::DescriptorPoolBuilder()
+               .addType(descriptorType, getInterfaceNumResources(shaderInterface))
+               .build(vki, device, vk::VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1);
+}
+
+vk::Move<vk::VkDescriptorSet> ImageFetchRenderInstance::createDescriptorSet (const vk::DeviceInterface&                vki,
+                                                                                                                                                        vk::VkDevice                                   device,
+                                                                                                                                                        vk::VkDescriptorType                   descriptorType,
+                                                                                                                                                        ShaderInputInterface                   shaderInterface,
+                                                                                                                                                        vk::VkDescriptorSetLayout              layout,
+                                                                                                                                                        vk::VkDescriptorPool                   pool,
+                                                                                                                                                        vk::VkImageView                                viewA,
+                                                                                                                                                        vk::VkImageView                                viewB)
+{
+       const vk::VkDescriptorInfo              imageInfos[2]   =
+       {
+               createDescriptorInfo(viewA, vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
+               createDescriptorInfo(viewB, vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
+       };
+
+       vk::Move<vk::VkDescriptorSet>   descriptorSet   = allocDescriptorSet(vki, device, pool, vk::VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, layout);
+       vk::DescriptorSetUpdateBuilder  builder;
+
+       switch (shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), descriptorType, &imageInfos[0]);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), descriptorType, &imageInfos[0]);
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), descriptorType, &imageInfos[1]);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.writeArray(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), descriptorType, 2u, imageInfos);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       builder.update(vki, device);
+       return descriptorSet;
+}
+
+void ImageFetchRenderInstance::logTestPlan (void) const
+{
+       std::ostringstream msg;
+
+       msg << "Rendering 2x2 grid.\n"
+               << "Single descriptor set. Descriptor set contains "
+                       << ((m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? "single" :
+                           (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS) ? "two" :
+                           (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY) ? "an array (size 2) of" :
+                           (const char*)DE_NULL)
+               << " descriptor(s) of type " << vk::getDescriptorTypeName(m_descriptorType) << "\n"
+               << "Image view type is " << vk::getImageViewTypeName(m_viewType) << "\n";
+
+       if (m_baseMipLevel)
+               msg << "Image view base mip level = " << m_baseMipLevel << "\n";
+       if (m_baseArraySlice)
+               msg << "Image view base array slice = " << m_baseArraySlice << "\n";
+
+       if (m_stageFlags == 0u)
+       {
+               msg << "Descriptors are not accessed in any shader stage.\n";
+       }
+       else
+       {
+               msg << "Color in each cell is fetched using the descriptor(s):\n";
+
+               for (int resultNdx = 0; resultNdx < 4; ++resultNdx)
+               {
+                       msg << "Test sample " << resultNdx << ": fetching at position " << m_images.getFetchPos(m_viewType, m_baseMipLevel, m_baseArraySlice, resultNdx);
+
+                       if (m_shaderInterface != SHADER_INPUT_SINGLE_DESCRIPTOR)
+                       {
+                               const int srcResourceNdx = (resultNdx % 2); // ABAB source
+                               msg << " from descriptor " << srcResourceNdx;
+                       }
+
+                       msg << "\n";
+               }
+
+               msg << "Descriptors are accessed in {"
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_VERTEX_BIT) != 0)                      ? (" vertex")                   : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_TESS_CONTROL_BIT) != 0)        ? (" tess_control")             : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_TESS_EVALUATION_BIT) != 0)     ? (" tess_evaluation")  : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_GEOMETRY_BIT) != 0)            ? (" geometry")                 : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_FRAGMENT_BIT) != 0)            ? (" fragment")                 : (""))
+                       << " } stages.";
+       }
+
+       m_context.getTestContext().getLog()
+               << tcu::TestLog::Message
+               << msg.str()
+               << tcu::TestLog::EndMessage;
+}
+
+vk::VkPipelineLayout ImageFetchRenderInstance::getPipelineLayout (void) const
+{
+       return *m_pipelineLayout;
+}
+
+void ImageFetchRenderInstance::writeDrawCmdBuffer (vk::VkCmdBuffer cmd) const
+{
+       m_vki.cmdBindDescriptorSets(cmd, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, getPipelineLayout(), 0, 1, &m_descriptorSet.get(), 0, DE_NULL);
+       m_vki.cmdDraw(cmd, 0, 6 * 4, 0, 1); // render four quads (two separate triangles)
+}
+
+tcu::TestStatus ImageFetchRenderInstance::verifyResultImage (const tcu::ConstPixelBufferAccess& result) const
+{
+       const tcu::Vec4         green           (0.0f, 1.0f, 0.0f, 1.0f);
+       const tcu::Vec4         yellow          (1.0f, 1.0f, 0.0f, 1.0f);
+       const bool                      doFetch         = (m_stageFlags != 0u); // no active stages? Then don't fetch
+       const tcu::Vec4         sample0         = (!doFetch) ? (yellow) : (m_images.fetchImageValue(0));
+       const tcu::Vec4         sample1         = (!doFetch) ? (green)  : (m_images.fetchImageValue(1));
+       const tcu::Vec4         sample2         = (!doFetch) ? (green)  : (m_images.fetchImageValue(2));
+       const tcu::Vec4         sample3         = (!doFetch) ? (yellow) : (m_images.fetchImageValue(3));
+       tcu::Surface            reference       (m_targetSize.x(), m_targetSize.y());
+
+       drawQuadrantReferenceResult(reference.getAccess(), sample0, sample1, sample2, sample3);
+
+       if (!bilinearCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", reference.getAccess(), result, tcu::RGBA(1, 1, 1, 1), tcu::COMPARE_LOG_RESULT))
+               return tcu::TestStatus::fail("Image verification failed");
+       else
+               return tcu::TestStatus::pass("Pass");
+}
+
+class ImageFetchComputeInstance : public vkt::TestInstance
+{
+public:
+                                                                                       ImageFetchComputeInstance       (vkt::Context&                  context,
+                                                                                                                                                vk::VkDescriptorType   descriptorType,
+                                                                                                                                                ShaderInputInterface   shaderInterface,
+                                                                                                                                                vk::VkImageViewType    viewType,
+                                                                                                                                                deUint32                               baseMipLevel,
+                                                                                                                                                deUint32                               baseArraySlice);
+
+private:
+       vk::Move<vk::VkDescriptorSetLayout>             createDescriptorSetLayout       (void) const;
+       vk::Move<vk::VkDescriptorPool>                  createDescriptorPool            (void) const;
+       vk::Move<vk::VkDescriptorSet>                   createDescriptorSet                     (vk::VkDescriptorPool pool, vk::VkDescriptorSetLayout layout) const;
+
+       tcu::TestStatus                                                 iterate                                         (void);
+       void                                                                    logTestPlan                                     (void) const;
+       tcu::TestStatus                                                 testResourceAccess                      (void);
+
+       const vk::VkDescriptorType                              m_descriptorType;
+       const ShaderInputInterface                              m_shaderInterface;
+       const vk::VkImageViewType                               m_viewType;
+       const deUint32                                                  m_baseMipLevel;
+       const deUint32                                                  m_baseArraySlice;
+
+       const vk::DeviceInterface&                              m_vki;
+       const vk::VkDevice                                              m_device;
+       const vk::VkQueue                                               m_queue;
+       const deUint32                                                  m_queueFamilyIndex;
+       vk::Allocator&                                                  m_allocator;
+
+       const ComputeInstanceResultBuffer               m_result;
+       const ImageFetchInstanceImages                  m_images;
+};
+
+ImageFetchComputeInstance::ImageFetchComputeInstance (Context&                         context,
+                                                                                                         vk::VkDescriptorType  descriptorType,
+                                                                                                         ShaderInputInterface  shaderInterface,
+                                                                                                         vk::VkImageViewType   viewType,
+                                                                                                         deUint32                              baseMipLevel,
+                                                                                                         deUint32                              baseArraySlice)
+       : vkt::TestInstance             (context)
+       , m_descriptorType              (descriptorType)
+       , m_shaderInterface             (shaderInterface)
+       , m_viewType                    (viewType)
+       , m_baseMipLevel                (baseMipLevel)
+       , m_baseArraySlice              (baseArraySlice)
+       , m_vki                                 (context.getDeviceInterface())
+       , m_device                              (context.getDevice())
+       , m_queue                               (context.getUniversalQueue())
+       , m_queueFamilyIndex    (context.getUniversalQueueFamilyIndex())
+       , m_allocator                   (context.getDefaultAllocator())
+       , m_result                              (m_vki, m_device, m_allocator)
+       , m_images                              (m_vki, m_device, m_queueFamilyIndex, m_queue, m_allocator, m_descriptorType, m_shaderInterface, m_viewType, m_baseMipLevel, m_baseArraySlice)
+{
+}
+
+vk::Move<vk::VkDescriptorSetLayout> ImageFetchComputeInstance::createDescriptorSetLayout (void) const
+{
+       vk::DescriptorSetLayoutBuilder builder;
+
+       builder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+
+       switch (m_shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.addSingleBinding(m_descriptorType, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.addSingleBinding(m_descriptorType, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+                       builder.addSingleBinding(m_descriptorType, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.addArrayBinding(m_descriptorType, 2u, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       };
+
+       return builder.build(m_vki, m_device);
+}
+
+vk::Move<vk::VkDescriptorPool> ImageFetchComputeInstance::createDescriptorPool (void) const
+{
+       return vk::DescriptorPoolBuilder()
+               .addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+               .addType(m_descriptorType, getInterfaceNumResources(m_shaderInterface))
+               .build(m_vki, m_device, vk::VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1);
+}
+
+vk::Move<vk::VkDescriptorSet> ImageFetchComputeInstance::createDescriptorSet (vk::VkDescriptorPool pool, vk::VkDescriptorSetLayout layout) const
+{
+       const vk::VkDescriptorInfo              resultInfo              = createDescriptorInfo(m_result.getBufferView());
+       const vk::VkDescriptorInfo              imageInfos[2]   =
+       {
+               createDescriptorInfo(m_images.getImageViewA(), vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
+               createDescriptorInfo(m_images.getImageViewB(), vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
+       };
+
+       vk::Move<vk::VkDescriptorSet>   descriptorSet   = allocDescriptorSet(m_vki, m_device, pool, vk::VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, layout);
+       vk::DescriptorSetUpdateBuilder  builder;
+
+       // result
+       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultInfo);
+
+       // images
+       switch (m_shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), m_descriptorType, &imageInfos[0]);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), m_descriptorType, &imageInfos[0]);
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(2u), m_descriptorType, &imageInfos[1]);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.writeArray(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), m_descriptorType, 2u, imageInfos);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       builder.update(m_vki, m_device);
+       return descriptorSet;
+}
+
+tcu::TestStatus ImageFetchComputeInstance::iterate (void)
+{
+       logTestPlan();
+       return testResourceAccess();
+}
+
+void ImageFetchComputeInstance::logTestPlan (void) const
+{
+       std::ostringstream msg;
+
+       msg << "Fetching 4 values from image in compute shader.\n"
+               << "Single descriptor set. Descriptor set contains "
+                       << ((m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? "single" :
+                           (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS) ? "two" :
+                           (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY) ? "an array (size 2) of" :
+                           (const char*)DE_NULL)
+               << " descriptor(s) of type " << vk::getDescriptorTypeName(m_descriptorType) << "\n"
+               << "Image view type is " << vk::getImageViewTypeName(m_viewType) << "\n";
+
+       if (m_baseMipLevel)
+               msg << "Image view base mip level = " << m_baseMipLevel << "\n";
+       if (m_baseArraySlice)
+               msg << "Image view base array slice = " << m_baseArraySlice << "\n";
+
+       for (int resultNdx = 0; resultNdx < 4; ++resultNdx)
+       {
+               msg << "Test sample " << resultNdx << ": fetch at position " << m_images.getFetchPos(m_viewType, m_baseMipLevel, m_baseArraySlice, resultNdx);
+
+               if (m_shaderInterface != SHADER_INPUT_SINGLE_DESCRIPTOR)
+               {
+                       const int srcResourceNdx = (resultNdx % 2); // ABAB source
+                       msg << " from descriptor " << srcResourceNdx;
+               }
+
+               msg << "\n";
+       }
+
+       m_context.getTestContext().getLog()
+               << tcu::TestLog::Message
+               << msg.str()
+               << tcu::TestLog::EndMessage;
+}
+
+tcu::TestStatus ImageFetchComputeInstance::testResourceAccess (void)
+{
+       const vk::Unique<vk::VkDescriptorSetLayout>             descriptorSetLayout     (createDescriptorSetLayout());
+       const vk::Unique<vk::VkDescriptorPool>                  descriptorPool          (createDescriptorPool());
+       const vk::Unique<vk::VkDescriptorSet>                   descriptorSet           (createDescriptorSet(*descriptorPool, *descriptorSetLayout));
+       const ComputePipeline                                                   pipeline                        (m_vki, m_device, m_context.getBinaryCollection(), 1, &descriptorSetLayout.get());
+
+       const vk::VkDescriptorSet                                               descriptorSets[]        = { *descriptorSet };
+       const int                                                                               numDescriptorSets       = DE_LENGTH_OF_ARRAY(descriptorSets);
+       const deUint32* const                                                   dynamicOffsets          = DE_NULL;
+       const int                                                                               numDynamicOffsets       = 0;
+       const void*     const*                                                          preBarriers                     = DE_NULL;
+       const int                                                                               numPreBarriers          = 0;
+       const void* const                                                               postBarriers[]          = { m_result.getResultReadBarrier() };
+       const int                                                                               numPostBarriers         = DE_LENGTH_OF_ARRAY(postBarriers);
+
+       const ComputeCommand                                                    compute                         (m_vki,
+                                                                                                                                                m_device,
+                                                                                                                                                pipeline.getPipeline(),
+                                                                                                                                                pipeline.getPipelineLayout(),
+                                                                                                                                                tcu::UVec3(4, 1, 1),
+                                                                                                                                                numDescriptorSets,     descriptorSets,
+                                                                                                                                                numDynamicOffsets,     dynamicOffsets,
+                                                                                                                                                numPreBarriers,        preBarriers,
+                                                                                                                                                numPostBarriers,       postBarriers);
+
+       tcu::Vec4                                                                               results[4];
+       bool                                                                                    anyResultSet            = false;
+       bool                                                                                    allResultsOk            = true;
+
+       compute.submitAndWait(m_queueFamilyIndex, m_queue);
+       m_result.readResultContentsTo(&results);
+
+       // verify
+       for (int resultNdx = 0; resultNdx < 4; ++resultNdx)
+       {
+               const tcu::Vec4 result                          = results[resultNdx];
+               const tcu::Vec4 reference                       = m_images.fetchImageValue(resultNdx);
+               const tcu::Vec4 conversionThreshold     = tcu::Vec4(1.0f / 255.0f);
+
+               if (result != tcu::Vec4(-1.0f))
+                       anyResultSet = true;
+
+               if (tcu::boolAny(tcu::greaterThan(tcu::abs(result - reference), conversionThreshold)))
+               {
+                       allResultsOk = false;
+
+                       m_context.getTestContext().getLog()
+                               << tcu::TestLog::Message
+                               << "Test sample " << resultNdx << ": Expected " << reference << ", got " << result
+                               << tcu::TestLog::EndMessage;
+               }
+       }
+
+       // read back and verify
+       if (allResultsOk)
+               return tcu::TestStatus::pass("Pass");
+       else if (anyResultSet)
+               return tcu::TestStatus::fail("Invalid result values");
+       else
+       {
+               m_context.getTestContext().getLog()
+                       << tcu::TestLog::Message
+                       << "Result buffer was not written to."
+                       << tcu::TestLog::EndMessage;
+               return tcu::TestStatus::fail("Result buffer was not written to");
+       }
+}
+
+class ImageSampleInstanceImages : private ImageInstanceImages
+{
+public:
+                                                                               ImageSampleInstanceImages       (const vk::DeviceInterface&             vki,
+                                                                                                                                        vk::VkDevice                                   device,
+                                                                                                                                        deUint32                                               queueFamilyIndex,
+                                                                                                                                        vk::VkQueue                                    queue,
+                                                                                                                                        vk::Allocator&                                 allocator,
+                                                                                                                                        vk::VkDescriptorType                   descriptorType,
+                                                                                                                                        ShaderInputInterface                   shaderInterface,
+                                                                                                                                        vk::VkImageViewType                    viewType,
+                                                                                                                                        deUint32                                               baseMipLevel,
+                                                                                                                                        deUint32                                               baseArraySlice,
+                                                                                                                                        bool                                                   immutable);
+
+       static tcu::Vec4                                        getSamplePos                            (vk::VkImageViewType viewType, deUint32 baseMipLevel, deUint32 baseArraySlice, int samplePosNdx);
+       tcu::Vec4                                                       fetchSampleValue                        (int samplePosNdx) const;
+
+       inline vk::VkImageView                          getImageViewA                           (void) const { return *m_imageViewA;    }
+       inline vk::VkImageView                          getImageViewB                           (void) const { return *m_imageViewB;    }
+       inline vk::VkSampler                            getSamplerA                                     (void) const { return *m_samplerA;              }
+       inline vk::VkSampler                            getSamplerB                                     (void) const { return *m_samplerB;              }
+       inline bool                                                     isImmutable                                     (void) const { return m_isImmutable;    }
+
+private:
+       static int                                                      getNumImages                            (vk::VkDescriptorType descriptorType, ShaderInputInterface shaderInterface);
+       static tcu::Sampler                                     createRefSampler                        (bool isFirst);
+       static vk::Move<vk::VkSampler>          createSampler                           (const vk::DeviceInterface& vki, vk::VkDevice device, const tcu::Sampler& sampler, const tcu::TextureFormat& format);
+
+       static tcu::Texture1DArrayView          getRef1DView                            (const tcu::TextureLevelPyramid& source, deUint32 baseMipLevel, deUint32 baseArraySlice, std::vector<tcu::ConstPixelBufferAccess>* levelStorage);
+       static tcu::Texture2DArrayView          getRef2DView                            (const tcu::TextureLevelPyramid& source, deUint32 baseMipLevel, deUint32 baseArraySlice, std::vector<tcu::ConstPixelBufferAccess>* levelStorage);
+       static tcu::Texture3DView                       getRef3DView                            (const tcu::TextureLevelPyramid& source, deUint32 baseMipLevel, deUint32 baseArraySlice, std::vector<tcu::ConstPixelBufferAccess>* levelStorage);
+       static tcu::TextureCubeArrayView        getRefCubeView                          (const tcu::TextureLevelPyramid& source, deUint32 baseMipLevel, deUint32 baseArraySlice, std::vector<tcu::ConstPixelBufferAccess>* levelStorage);
+
+       const vk::VkDescriptorType                      m_descriptorType;
+       const ShaderInputInterface                      m_shaderInterface;
+       const bool                                                      m_isImmutable;
+
+       const tcu::Sampler                                      m_refSamplerA;
+       const tcu::Sampler                                      m_refSamplerB;
+       const vk::Unique<vk::VkSampler>         m_samplerA;
+       const vk::Unique<vk::VkSampler>         m_samplerB;
+};
+
+ImageSampleInstanceImages::ImageSampleInstanceImages (const vk::DeviceInterface&       vki,
+                                                                                                         vk::VkDevice                                  device,
+                                                                                                         deUint32                                              queueFamilyIndex,
+                                                                                                         vk::VkQueue                                   queue,
+                                                                                                         vk::Allocator&                                allocator,
+                                                                                                         vk::VkDescriptorType                  descriptorType,
+                                                                                                         ShaderInputInterface                  shaderInterface,
+                                                                                                         vk::VkImageViewType                   viewType,
+                                                                                                         deUint32                                              baseMipLevel,
+                                                                                                         deUint32                                              baseArraySlice,
+                                                                                                         bool                                                  immutable)
+       : ImageInstanceImages   (vki,
+                                                        device,
+                                                        queueFamilyIndex,
+                                                        queue,
+                                                        allocator,
+                                                        descriptorType,
+                                                        viewType,
+                                                        getNumImages(descriptorType, shaderInterface),
+                                                        baseMipLevel,
+                                                        baseArraySlice)
+       , m_descriptorType              (descriptorType)
+       , m_shaderInterface             (shaderInterface)
+       , m_isImmutable                 (immutable)
+       , m_refSamplerA                 (createRefSampler(true))
+       , m_refSamplerB                 (createRefSampler(false))
+       , m_samplerA                    (createSampler(vki, device, m_refSamplerA, m_imageFormat))
+       , m_samplerB                    ((getInterfaceNumResources(m_shaderInterface) == 1u)
+                                                               ? vk::Move<vk::VkSampler>()
+                                                               : createSampler(vki, device, m_refSamplerB, m_imageFormat))
+{
+}
+
+tcu::Vec4 ImageSampleInstanceImages::getSamplePos (vk::VkImageViewType viewType, deUint32 baseMipLevel, deUint32 baseArraySlice, int samplePosNdx)
+{
+       DE_ASSERT(de::inBounds(samplePosNdx, 0, 4));
+
+       const deUint32  imageSize       = (deUint32)IMAGE_SIZE >> baseMipLevel;
+       const deUint32  arraySize       = isImageViewTypeArray(viewType) ? ARRAY_SIZE - baseArraySlice : 1;
+
+       // choose arbitrary values that are not ambiguous with NEAREST filtering
+
+       switch (viewType)
+       {
+               case vk::VK_IMAGE_VIEW_TYPE_1D:
+               case vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY:
+               case vk::VK_IMAGE_VIEW_TYPE_2D:
+               case vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY:
+               case vk::VK_IMAGE_VIEW_TYPE_3D:
+               {
+                       const tcu::Vec3 coords[4]       =
+                       {
+                               tcu::Vec3(0.75f,
+                                                 0.5f,
+                                                 (float)(12u % imageSize) + 0.25f),
+
+                               tcu::Vec3((float)(23u % imageSize) + 0.25f,
+                                                 (float)(73u % imageSize) + 0.5f,
+                                                 (float)(16u % imageSize) + 0.5f + (float)imageSize),
+
+                               tcu::Vec3(-(float)(43u % imageSize) + 0.25f,
+                                                 (float)(84u % imageSize) + 0.5f + (float)imageSize,
+                                                 (float)(117u % imageSize) + 0.75f),
+
+                               tcu::Vec3((float)imageSize + 0.5f,
+                                                 (float)(75u % imageSize) + 0.25f,
+                                                 (float)(83u % imageSize) + 0.25f + (float)imageSize),
+                       };
+                       const deUint32  slices[4]       =
+                       {
+                               0u % arraySize,
+                               4u % arraySize,
+                               9u % arraySize,
+                               2u % arraySize,
+                       };
+
+                       if (viewType == vk::VK_IMAGE_VIEW_TYPE_1D || viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY)
+                               return tcu::Vec4(coords[samplePosNdx].x() / (float)imageSize,
+                                                                (float)slices[samplePosNdx],
+                                                                0.0f,
+                                                                0.0f);
+                       else if (viewType == vk::VK_IMAGE_VIEW_TYPE_2D || viewType == vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY)
+                               return tcu::Vec4(coords[samplePosNdx].x() / (float)imageSize,
+                                                                coords[samplePosNdx].y() / (float)imageSize,
+                                                                (float)slices[samplePosNdx],
+                                                                0.0f);
+                       else if (viewType == vk::VK_IMAGE_VIEW_TYPE_3D)
+                               return tcu::Vec4(coords[samplePosNdx].x() / (float)imageSize,
+                                                                coords[samplePosNdx].y() / (float)imageSize,
+                                                                coords[samplePosNdx].z() / (float)imageSize,
+                                                                0.0f);
+                       else
+                       {
+                               DE_FATAL("Impossible");
+                               return tcu::Vec4();
+                       }
+               }
+
+               case vk::VK_IMAGE_VIEW_TYPE_CUBE:
+               case vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY:
+               {
+                       // \note these values are in [0, texSize]*3 space for convenience
+                       const tcu::Vec3 coords[4]       =
+                       {
+                               tcu::Vec3(0.75f,
+                                                 0.5f,
+                                                 (float)imageSize),
+
+                               tcu::Vec3((float)(13u % imageSize) + 0.25f,
+                                                 0.0f,
+                                                 (float)(16u % imageSize) + 0.5f),
+
+                               tcu::Vec3(0.0f,
+                                                 (float)(84u % imageSize) + 0.5f,
+                                                 (float)(10u % imageSize) + 0.75f),
+
+                               tcu::Vec3((float)imageSize,
+                                                 (float)(75u % imageSize) + 0.25f,
+                                                 (float)(83u % imageSize) + 0.75f),
+                       };
+                       const deUint32  slices[4]       =
+                       {
+                               1u % arraySize,
+                               2u % arraySize,
+                               9u % arraySize,
+                               5u % arraySize,
+                       };
+
+                       DE_ASSERT(de::inRange(coords[samplePosNdx].x(), 0.0f, (float)imageSize));
+                       DE_ASSERT(de::inRange(coords[samplePosNdx].y(), 0.0f, (float)imageSize));
+                       DE_ASSERT(de::inRange(coords[samplePosNdx].z(), 0.0f, (float)imageSize));
+
+                       // map to [-1, 1]*3 space
+                       return tcu::Vec4(coords[samplePosNdx].x() / (float)imageSize * 2.0f - 1.0f,
+                                                        coords[samplePosNdx].y() / (float)imageSize * 2.0f - 1.0f,
+                                                        coords[samplePosNdx].z() / (float)imageSize * 2.0f - 1.0f,
+                                                        (float)slices[samplePosNdx]);
+               }
+
+               default:
+                       DE_FATAL("Impossible");
+                       return tcu::Vec4();
+       }
+}
+
+tcu::Vec4 ImageSampleInstanceImages::fetchSampleValue (int samplePosNdx) const
+{
+       DE_ASSERT(de::inBounds(samplePosNdx, 0, 4));
+
+       // texture order is ABAB
+       const bool                                                                      isSamplerCase   = (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER);
+       const tcu::TextureLevelPyramid&                         sampleSrcA              = m_sourceImageA;
+       const tcu::TextureLevelPyramid&                         sampleSrcB              = (m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? (m_sourceImageA) : (m_sourceImageB);
+       const tcu::TextureLevelPyramid&                         sampleSrc               = (isSamplerCase) ? (sampleSrcA) : ((samplePosNdx % 2) == 0) ? (sampleSrcA) : (sampleSrcB);
+
+       // sampler order is ABAB
+       const tcu::Sampler&                                                     samplerA                = m_refSamplerA;
+       const tcu::Sampler&                                                     samplerB                = (m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? (m_refSamplerA) : (m_refSamplerB);
+       const tcu::Sampler&                                                     sampler                 = ((samplePosNdx % 2) == 0) ? (samplerA) : (samplerB);
+
+       const tcu::Vec4                                                         samplePos               = getSamplePos(m_viewType, m_baseMipLevel, m_baseArraySlice, samplePosNdx);
+       const float                                                                     lod                             = 0.0f;
+       std::vector<tcu::ConstPixelBufferAccess>        levelStorage;
+
+       switch (m_viewType)
+       {
+               case vk::VK_IMAGE_VIEW_TYPE_1D:
+               case vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY:   return getRef1DView(sampleSrc, m_baseMipLevel, m_baseArraySlice, &levelStorage).sample(sampler, samplePos.x(), samplePos.y(), lod);
+               case vk::VK_IMAGE_VIEW_TYPE_2D:
+               case vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY:   return getRef2DView(sampleSrc, m_baseMipLevel, m_baseArraySlice, &levelStorage).sample(sampler, samplePos.x(), samplePos.y(), samplePos.z(), lod);
+               case vk::VK_IMAGE_VIEW_TYPE_3D:                 return getRef3DView(sampleSrc, m_baseMipLevel, m_baseArraySlice, &levelStorage).sample(sampler, samplePos.x(), samplePos.y(), samplePos.z(), lod);
+               case vk::VK_IMAGE_VIEW_TYPE_CUBE:
+               case vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY: return getRefCubeView(sampleSrc, m_baseMipLevel, m_baseArraySlice, &levelStorage).sample(sampler, samplePos.x(), samplePos.y(), samplePos.z(), samplePos.w(), lod);
+
+               default:
+               {
+                       DE_FATAL("Impossible");
+                       return tcu::Vec4();
+               }
+       }
+}
+
+int ImageSampleInstanceImages::getNumImages (vk::VkDescriptorType descriptorType, ShaderInputInterface shaderInterface)
+{
+       // If we are testing separate samplers, just one image is enough
+       if (descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER)
+               return 1;
+       else if (descriptorType == vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+       {
+               // combined: numImages == numSamplers
+               return getInterfaceNumResources(shaderInterface);
+       }
+       else
+       {
+               DE_FATAL("Impossible");
+               return 0;
+       }
+}
+
+tcu::Sampler ImageSampleInstanceImages::createRefSampler (bool isFirst)
+{
+       if (isFirst)
+       {
+               // linear, wrapping
+               return tcu::Sampler(tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::LINEAR, tcu::Sampler::LINEAR);
+       }
+       else
+       {
+               // nearest, clamping
+               return tcu::Sampler(tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::NEAREST, tcu::Sampler::NEAREST);
+       }
+}
+
+vk::Move<vk::VkSampler> ImageSampleInstanceImages::createSampler (const vk::DeviceInterface& vki, vk::VkDevice device, const tcu::Sampler& sampler, const tcu::TextureFormat& format)
+{
+       const bool                                              compareEnabled  = (sampler.compare != tcu::Sampler::COMPAREMODE_NONE);
+       const vk::VkCompareOp                   compareOp               = (compareEnabled) ? (mapToVkCompareOp(sampler.compare)) : (vk::VK_COMPARE_OP_ALWAYS);
+       const tcu::TextureChannelClass  channelClass    = tcu::getTextureChannelClass(format.type);
+       const bool                                              isIntTexture    = channelClass == tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER || channelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER;
+       const vk::VkBorderColor                 borderColor             = (isIntTexture) ? (vk::VK_BORDER_COLOR_INT_OPAQUE_WHITE) : (vk::VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE);
+       const vk::VkSamplerCreateInfo   createInfo              =
+       {
+               vk::VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
+               DE_NULL,
+               mapMagFilterToVkTexFilter(sampler.magFilter),                                                                   // magFilter
+               mapMinFilterToVkTexFilter(sampler.minFilter),                                                                   // minFilter
+               mapMinFilterToVkTexMipmapMode(sampler.minFilter),                                                               // mipMode
+               mapToVkTexAddress(sampler.wrapS),                                                                                               // addressU
+               mapToVkTexAddress(sampler.wrapT),                                                                                               // addressV
+               mapToVkTexAddress(sampler.wrapR),                                                                                               // addressW
+               0.0f,                                                                                                                                                   // mipLodBias
+               1,                                                                                                                                                              // maxAnisotropy
+               (compareEnabled) ? (vk::VkBool32)(vk::VK_TRUE) : (vk::VkBool32)(vk::VK_FALSE),  // compareEnable
+               compareOp,                                                                                                                                              // compareOp
+               0.0f,                                                                                                                                                   // minLod
+               0.0f,                                                                                                                                                   // maxLod
+               borderColor,                                                                                                                                    // borderColor
+       };
+       return vk::createSampler(vki, device, &createInfo);
+}
+
+tcu::Texture1DArrayView ImageSampleInstanceImages::getRef1DView (const tcu::TextureLevelPyramid& source, deUint32 baseMipLevel, deUint32 baseArraySlice, std::vector<tcu::ConstPixelBufferAccess>* levelStorage)
+{
+       DE_ASSERT(levelStorage->empty());
+
+       const deUint32 numSlices = (deUint32)source.getLevel(0).getHeight();
+       const deUint32 numLevels = (deUint32)source.getNumLevels();
+
+       // cut pyramid from baseMipLevel
+       for (deUint32 level = baseMipLevel; level < numLevels; ++level)
+       {
+               // cut levels from baseArraySlice
+               const tcu::ConstPixelBufferAccess wholeLevel    = source.getLevel(level);
+               const tcu::ConstPixelBufferAccess cutLevel              = tcu::getSubregion(wholeLevel, 0, baseArraySlice, wholeLevel.getWidth(), numSlices - baseArraySlice);
+               levelStorage->push_back(cutLevel);
+       }
+
+       return tcu::Texture1DArrayView((int)levelStorage->size(), &levelStorage->front());
+}
+
+tcu::Texture2DArrayView ImageSampleInstanceImages::getRef2DView (const tcu::TextureLevelPyramid& source, deUint32 baseMipLevel, deUint32 baseArraySlice, std::vector<tcu::ConstPixelBufferAccess>* levelStorage)
+{
+       DE_ASSERT(levelStorage->empty());
+
+       const deUint32 numSlices = (deUint32)source.getLevel(0).getDepth();
+       const deUint32 numLevels = (deUint32)source.getNumLevels();
+
+       // cut pyramid from baseMipLevel
+       for (deUint32 level = baseMipLevel; level < numLevels; ++level)
+       {
+               // cut levels from baseArraySlice
+               const tcu::ConstPixelBufferAccess wholeLevel    = source.getLevel(level);
+               const tcu::ConstPixelBufferAccess cutLevel              = tcu::getSubregion(wholeLevel, 0, 0, baseArraySlice, wholeLevel.getWidth(), wholeLevel.getHeight(), numSlices - baseArraySlice);
+               levelStorage->push_back(cutLevel);
+       }
+
+       return tcu::Texture2DArrayView((int)levelStorage->size(), &levelStorage->front());
+}
+
+tcu::Texture3DView ImageSampleInstanceImages::getRef3DView (const tcu::TextureLevelPyramid& source, deUint32 baseMipLevel, deUint32 baseArraySlice, std::vector<tcu::ConstPixelBufferAccess>* levelStorage)
+{
+       DE_ASSERT(levelStorage->empty());
+       DE_ASSERT(baseArraySlice == 0);
+       DE_UNREF(baseArraySlice);
+
+       const deUint32 numLevels = (deUint32)source.getNumLevels();
+
+       // cut pyramid from baseMipLevel
+       for (deUint32 level = baseMipLevel; level < numLevels; ++level)
+               levelStorage->push_back(source.getLevel(level));
+
+       return tcu::Texture3DView((int)levelStorage->size(), &levelStorage->front());
+}
+
+tcu::TextureCubeArrayView ImageSampleInstanceImages::getRefCubeView (const tcu::TextureLevelPyramid& source, deUint32 baseMipLevel, deUint32 baseArraySlice, std::vector<tcu::ConstPixelBufferAccess>* levelStorage)
+{
+       DE_ASSERT(levelStorage->empty());
+
+       const deUint32 numSlices = (deUint32)source.getLevel(0).getDepth() / 6;
+       const deUint32 numLevels = (deUint32)source.getNumLevels();
+
+       // cut pyramid from baseMipLevel
+       for (deUint32 level = baseMipLevel; level < numLevels; ++level)
+       {
+               // cut levels from baseArraySlice
+               const tcu::ConstPixelBufferAccess wholeLevel    = source.getLevel(level);
+               const tcu::ConstPixelBufferAccess cutLevel              = tcu::getSubregion(wholeLevel, 0, 0, baseArraySlice * 6, wholeLevel.getWidth(), wholeLevel.getHeight(), (numSlices - baseArraySlice) * 6);
+               levelStorage->push_back(cutLevel);
+       }
+
+       return tcu::TextureCubeArrayView((int)levelStorage->size(), &levelStorage->front());
+}
+
+class ImageSampleRenderInstance : public SingleCmdRenderInstance
+{
+public:
+                                                                                                       ImageSampleRenderInstance               (vkt::Context&                  context,
+                                                                                                                                                                        bool                                   isPrimaryCmdBuf,
+                                                                                                                                                                        vk::VkDescriptorType   descriptorType,
+                                                                                                                                                                        vk::VkShaderStageFlags stageFlags,
+                                                                                                                                                                        ShaderInputInterface   shaderInterface,
+                                                                                                                                                                        vk::VkImageViewType    viewType,
+                                                                                                                                                                        deUint32                               baseMipLevel,
+                                                                                                                                                                        deUint32                               baseArraySlice,
+                                                                                                                                                                        bool                                   isImmutable);
+
+private:
+       static vk::Move<vk::VkDescriptorSetLayout>              createDescriptorSetLayout               (const vk::DeviceInterface&                     vki,
+                                                                                                                                                                        vk::VkDevice                                           device,
+                                                                                                                                                                        vk::VkDescriptorType                           descriptorType,
+                                                                                                                                                                        ShaderInputInterface                           shaderInterface,
+                                                                                                                                                                        vk::VkShaderStageFlags                         stageFlags,
+                                                                                                                                                                        const ImageSampleInstanceImages&       images);
+
+       static vk::Move<vk::VkPipelineLayout>                   createPipelineLayout                    (const vk::DeviceInterface&     vki,
+                                                                                                                                                                        vk::VkDevice                           device,
+                                                                                                                                                                        vk::VkDescriptorSetLayout      descriptorSetLayout);
+
+       static vk::Move<vk::VkDescriptorPool>                   createDescriptorPool                    (const vk::DeviceInterface&     vki,
+                                                                                                                                                                        vk::VkDevice                           device,
+                                                                                                                                                                        vk::VkDescriptorType           descriptorType,
+                                                                                                                                                                        ShaderInputInterface           shaderInterface,
+                                                                                                                                                                        bool                                           isImmutable);
+
+       static vk::Move<vk::VkDescriptorSet>                    createDescriptorSet                             (const vk::DeviceInterface&                     vki,
+                                                                                                                                                                        vk::VkDevice                                           device,
+                                                                                                                                                                        vk::VkDescriptorType                           descriptorType,
+                                                                                                                                                                        ShaderInputInterface                           shaderInterface,
+                                                                                                                                                                        vk::VkDescriptorSetLayout                      layout,
+                                                                                                                                                                        vk::VkDescriptorPool                           pool,
+                                                                                                                                                                        bool                                                           isImmutable,
+                                                                                                                                                                        const ImageSampleInstanceImages&       images);
+
+       static void                                                                             writeSamplerDescriptorSet               (const vk::DeviceInterface&                     vki,
+                                                                                                                                                                        vk::VkDevice                                           device,
+                                                                                                                                                                        ShaderInputInterface                           shaderInterface,
+                                                                                                                                                                        bool                                                           isImmutable,
+                                                                                                                                                                        const ImageSampleInstanceImages&       images,
+                                                                                                                                                                        vk::VkDescriptorSet                            descriptorSet);
+
+       static void                                                                             writeImageSamplerDescriptorSet  (const vk::DeviceInterface&                     vki,
+                                                                                                                                                                        vk::VkDevice                                           device,
+                                                                                                                                                                        ShaderInputInterface                           shaderInterface,
+                                                                                                                                                                        bool                                                           isImmutable,
+                                                                                                                                                                        const ImageSampleInstanceImages&       images,
+                                                                                                                                                                        vk::VkDescriptorSet                            descriptorSet);
+
+       void                                                                                    logTestPlan                                             (void) const;
+       vk::VkPipelineLayout                                                    getPipelineLayout                               (void) const;
+       void                                                                                    writeDrawCmdBuffer                              (vk::VkCmdBuffer cmd) const;
+       tcu::TestStatus                                                                 verifyResultImage                               (const tcu::ConstPixelBufferAccess& result) const;
+
+       enum
+       {
+               RENDER_SIZE = 128,
+       };
+
+       const vk::VkDescriptorType                                              m_descriptorType;
+       const vk::VkShaderStageFlags                                    m_stageFlags;
+       const ShaderInputInterface                                              m_shaderInterface;
+       const vk::VkImageViewType                                               m_viewType;
+       const deUint32                                                                  m_baseMipLevel;
+       const deUint32                                                                  m_baseArraySlice;
+       
+       const ImageSampleInstanceImages                                 m_images;
+       const vk::Unique<vk::VkDescriptorSetLayout>             m_descriptorSetLayout;
+       const vk::Unique<vk::VkPipelineLayout>                  m_pipelineLayout;
+       const vk::Unique<vk::VkDescriptorPool>                  m_descriptorPool;
+       const vk::Unique<vk::VkDescriptorSet>                   m_descriptorSet;
+};
+
+ImageSampleRenderInstance::ImageSampleRenderInstance (vkt::Context&                            context,
+                                                                                                         bool                                          isPrimaryCmdBuf,
+                                                                                                         vk::VkDescriptorType          descriptorType,
+                                                                                                         vk::VkShaderStageFlags        stageFlags,
+                                                                                                         ShaderInputInterface          shaderInterface,
+                                                                                                         vk::VkImageViewType           viewType,
+                                                                                                         deUint32                                      baseMipLevel,
+                                                                                                         deUint32                                      baseArraySlice,
+                                                                                                         bool                                          isImmutable)
+       : SingleCmdRenderInstance       (context, isPrimaryCmdBuf, tcu::UVec2(RENDER_SIZE, RENDER_SIZE))
+       , m_descriptorType                      (descriptorType)
+       , m_stageFlags                          (stageFlags)
+       , m_shaderInterface                     (shaderInterface)
+       , m_viewType                            (viewType)
+       , m_baseMipLevel                        (baseMipLevel)
+       , m_baseArraySlice                      (baseArraySlice)
+       , m_images                                      (m_vki, m_device, m_queueFamilyIndex, m_queue, m_allocator, m_descriptorType, m_shaderInterface, m_viewType, m_baseMipLevel, m_baseArraySlice, isImmutable)
+       , m_descriptorSetLayout         (createDescriptorSetLayout(m_vki, m_device, m_descriptorType, m_shaderInterface, m_stageFlags, m_images))
+       , m_pipelineLayout                      (createPipelineLayout(m_vki, m_device, *m_descriptorSetLayout))
+       , m_descriptorPool                      (createDescriptorPool(m_vki, m_device, m_descriptorType, m_shaderInterface, isImmutable))
+       , m_descriptorSet                       (createDescriptorSet(m_vki, m_device, m_descriptorType, m_shaderInterface, *m_descriptorSetLayout, *m_descriptorPool, isImmutable, m_images))
+{
+}
+
+vk::Move<vk::VkDescriptorSetLayout> ImageSampleRenderInstance::createDescriptorSetLayout (const vk::DeviceInterface&           vki,
+                                                                                                                                                                                 vk::VkDevice                                          device,
+                                                                                                                                                                                 vk::VkDescriptorType                          descriptorType,
+                                                                                                                                                                                 ShaderInputInterface                          shaderInterface,
+                                                                                                                                                                                 vk::VkShaderStageFlags                        stageFlags,
+                                                                                                                                                                                 const ImageSampleInstanceImages&      images)
+{
+       const vk::VkSampler                             samplers[2] =
+       {
+               images.getSamplerA(),
+               images.getSamplerB(),
+       };
+
+       vk::DescriptorSetLayoutBuilder  builder;
+
+       // with samplers, separate texture at binding 0
+       if (descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER)
+               builder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, stageFlags);
+
+       // (combined)samplers follow
+       switch (shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.addSingleSamplerBinding(descriptorType, stageFlags, (images.isImmutable()) ? (&samplers[0]) : (DE_NULL));
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.addSingleSamplerBinding(descriptorType, stageFlags, (images.isImmutable()) ? (&samplers[0]) : (DE_NULL));
+                       builder.addSingleSamplerBinding(descriptorType, stageFlags, (images.isImmutable()) ? (&samplers[1]) : (DE_NULL));
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.addArraySamplerBinding(descriptorType, 2u, stageFlags, (images.isImmutable()) ? (samplers) : (DE_NULL));
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       return builder.build(vki, device);
+}
+
+vk::Move<vk::VkPipelineLayout> ImageSampleRenderInstance::createPipelineLayout (const vk::DeviceInterface&     vki,
+                                                                                                                                                               vk::VkDevice                            device,
+                                                                                                                                                               vk::VkDescriptorSetLayout       descriptorSetLayout)
+{
+       const vk::VkPipelineLayoutCreateInfo createInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+               DE_NULL,
+               1,                                              // descriptorSetCount
+               &descriptorSetLayout,   // pSetLayouts
+               0u,                                             // pushConstantRangeCount
+               DE_NULL,                                // pPushConstantRanges
+       };
+       return vk::createPipelineLayout(vki, device, &createInfo);
+}
+
+vk::Move<vk::VkDescriptorPool> ImageSampleRenderInstance::createDescriptorPool (const vk::DeviceInterface&     vki,
+                                                                                                                                                               vk::VkDevice                            device,
+                                                                                                                                                               vk::VkDescriptorType            descriptorType,
+                                                                                                                                                               ShaderInputInterface            shaderInterface,
+                                                                                                                                                               bool                                            isImmutable)
+{
+       vk::DescriptorPoolBuilder builder;
+
+       if (descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER)
+       {
+               // separate samplers need image to sample
+               builder.addType(vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
+
+               // samplers needed only if they are specified in the descriptor set
+               if (!isImmutable)
+                       builder.addType(vk::VK_DESCRIPTOR_TYPE_SAMPLER, getInterfaceNumResources(shaderInterface));
+       }
+       else if (descriptorType == vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+       {
+               // combined image samplers
+               builder.addType(vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, getInterfaceNumResources(shaderInterface));
+       }
+       else
+               DE_FATAL("Impossible");
+
+       return builder.build(vki, device, vk::VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1);
+}
+
+vk::Move<vk::VkDescriptorSet> ImageSampleRenderInstance::createDescriptorSet (const vk::DeviceInterface&               vki,
+                                                                                                                                                         vk::VkDevice                                          device,
+                                                                                                                                                         vk::VkDescriptorType                          descriptorType,
+                                                                                                                                                         ShaderInputInterface                          shaderInterface,
+                                                                                                                                                         vk::VkDescriptorSetLayout                     layout,
+                                                                                                                                                         vk::VkDescriptorPool                          pool,
+                                                                                                                                                         bool                                                          isImmutable,
+                                                                                                                                                         const ImageSampleInstanceImages&      images)
+{
+       vk::Move<vk::VkDescriptorSet> descriptorSet = allocDescriptorSet(vki, device, pool, vk::VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, layout);
+
+       if (descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER)
+               writeSamplerDescriptorSet(vki, device,  shaderInterface, isImmutable, images, *descriptorSet);
+       else if (descriptorType == vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+               writeImageSamplerDescriptorSet(vki, device, shaderInterface, isImmutable, images, *descriptorSet);
+       else
+               DE_FATAL("Impossible");
+
+       return descriptorSet;
+}
+
+void ImageSampleRenderInstance::writeSamplerDescriptorSet (const vk::DeviceInterface&          vki,
+                                                                                                                  vk::VkDevice                                         device,
+                                                                                                                  ShaderInputInterface                         shaderInterface,
+                                                                                                                  bool                                                         isImmutable,
+                                                                                                                  const ImageSampleInstanceImages&     images,
+                                                                                                                  vk::VkDescriptorSet                          descriptorSet)
+{
+       const vk::VkDescriptorInfo              imageInfo                       = createDescriptorInfo(images.getImageViewA(), vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+       const vk::VkDescriptorInfo              samplersInfos[2]        =
+       {
+               createDescriptorInfo(images.getSamplerA()),
+               createDescriptorInfo(images.getSamplerB()),
+       };
+
+       vk::DescriptorSetUpdateBuilder  builder;
+
+       // stand alone texture
+       builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &imageInfo);
+
+       // samplers
+       if (!isImmutable)
+       {
+               switch (shaderInterface)
+               {
+                       case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                               builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_SAMPLER, &samplersInfos[0]);
+                               break;
+
+                       case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                               builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_SAMPLER, &samplersInfos[0]);
+                               builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(2u), vk::VK_DESCRIPTOR_TYPE_SAMPLER, &samplersInfos[1]);
+                               break;
+
+                       case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                               builder.writeArray(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_SAMPLER, 2u, samplersInfos);
+                               break;
+
+                       default:
+                               DE_FATAL("Impossible");
+               }
+       }
+
+       builder.update(vki, device);
+}
+
+void ImageSampleRenderInstance::writeImageSamplerDescriptorSet (const vk::DeviceInterface&                     vki,
+                                                                                                                               vk::VkDevice                                            device,
+                                                                                                                               ShaderInputInterface                            shaderInterface,
+                                                                                                                               bool                                                            isImmutable,
+                                                                                                                               const ImageSampleInstanceImages&        images,
+                                                                                                                               vk::VkDescriptorSet                                     descriptorSet)
+{
+       const vk::VkSampler                             samplers[2]                     =
+       {
+               (isImmutable) ? (0) : (images.getSamplerA()),
+               (isImmutable) ? (0) : (images.getSamplerB()),
+       };
+       const vk::VkDescriptorInfo              imageSamplers[2]        =
+       {
+               createDescriptorInfo(samplers[0], images.getImageViewA(), vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
+               createDescriptorInfo(samplers[1], images.getImageViewB(), vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
+       };
+
+       vk::DescriptorSetUpdateBuilder  builder;
+
+       // combined image samplers
+       switch (shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &imageSamplers[0]);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &imageSamplers[0]);
+                       builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &imageSamplers[1]);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.writeArray(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2u, imageSamplers);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       builder.update(vki, device);
+}
+
+void ImageSampleRenderInstance::logTestPlan (void) const
+{
+       std::ostringstream msg;
+
+       msg << "Rendering 2x2 grid.\n";
+
+       if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER)
+       {
+               msg << "Single descriptor set. Descriptor set contains "
+                       << ((m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? "single" :
+                           (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS) ? "two" :
+                           (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY) ? "an array (size 2) of" :
+                           (const char*)DE_NULL)
+                       << " VK_DESCRIPTOR_TYPE_SAMPLER descriptor(s) and a single texture.\n";
+       }
+       else if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+       {
+               msg << "Single descriptor set. Descriptor set contains "
+                       << ((m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? "single" :
+                           (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS) ? "two" :
+                           (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY) ? "an array (size 2) of" :
+                           (const char*)DE_NULL)
+                       << " VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptor(s).\n";
+       }
+       else
+               DE_FATAL("Impossible");
+
+       msg << "Image view type is " << vk::getImageViewTypeName(m_viewType) << "\n";
+
+       if (m_baseMipLevel)
+               msg << "Image view base mip level = " << m_baseMipLevel << "\n";
+       if (m_baseArraySlice)
+               msg << "Image view base array slice = " << m_baseArraySlice << "\n";
+
+       if (m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR)
+               msg << "Sampler mode is LINEAR, with WRAP\n";
+       else
+               msg << "Sampler 0 mode is LINEAR, with WRAP\nSampler 1 mode is NEAREST with CLAMP\n";
+
+       if (m_stageFlags == 0u)
+       {
+               msg << "Descriptors are not accessed in any shader stage.\n";
+       }
+       else
+       {
+               msg << "Color in each cell is fetched using the descriptor(s):\n";
+
+               for (int resultNdx = 0; resultNdx < 4; ++resultNdx)
+               {
+                       msg << "Test sample " << resultNdx << ": sample at position " << m_images.getSamplePos(m_viewType, m_baseMipLevel, m_baseArraySlice, resultNdx);
+
+                       if (m_shaderInterface != SHADER_INPUT_SINGLE_DESCRIPTOR)
+                       {
+                               const int srcResourceNdx = (resultNdx % 2); // ABAB source
+
+                               if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER)
+                                       msg << " using sampler " << srcResourceNdx;
+                               else if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+                                       msg << " from combined image sampler " << srcResourceNdx;
+                               else
+                                       DE_FATAL("Impossible");
+                       }
+                       msg << "\n";
+               }
+
+               msg << "Descriptors are accessed in {"
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_VERTEX_BIT) != 0)                      ? (" vertex")                   : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_TESS_CONTROL_BIT) != 0)        ? (" tess_control")             : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_TESS_EVALUATION_BIT) != 0)     ? (" tess_evaluation")  : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_GEOMETRY_BIT) != 0)            ? (" geometry")                 : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_FRAGMENT_BIT) != 0)            ? (" fragment")                 : (""))
+                       << " } stages.";
+       }
+
+       m_context.getTestContext().getLog()
+               << tcu::TestLog::Message
+               << msg.str()
+               << tcu::TestLog::EndMessage;
+}
+
+vk::VkPipelineLayout ImageSampleRenderInstance::getPipelineLayout (void) const
+{
+       return *m_pipelineLayout;
+}
+
+void ImageSampleRenderInstance::writeDrawCmdBuffer (vk::VkCmdBuffer cmd) const
+{
+       m_vki.cmdBindDescriptorSets(cmd, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, getPipelineLayout(), 0u, 1u, &m_descriptorSet.get(), 0u, DE_NULL);
+       m_vki.cmdDraw(cmd, 0u, 6u * 4u, 0u, 1u); // render four quads (two separate triangles)
+}
+
+tcu::TestStatus ImageSampleRenderInstance::verifyResultImage (const tcu::ConstPixelBufferAccess& result) const
+{
+       const tcu::Vec4         green           (0.0f, 1.0f, 0.0f, 1.0f);
+       const tcu::Vec4         yellow          (1.0f, 1.0f, 0.0f, 1.0f);
+       const bool                      doFetch         = (m_stageFlags != 0u); // no active stages? Then don't fetch
+       const tcu::Vec4         sample0         = (!doFetch) ? (yellow) : (m_images.fetchSampleValue(0));
+       const tcu::Vec4         sample1         = (!doFetch) ? (green)  : (m_images.fetchSampleValue(1));
+       const tcu::Vec4         sample2         = (!doFetch) ? (green)  : (m_images.fetchSampleValue(2));
+       const tcu::Vec4         sample3         = (!doFetch) ? (yellow) : (m_images.fetchSampleValue(3));
+       const tcu::RGBA         threshold       = tcu::RGBA(8, 8, 8, 8); // source image is high-frequency so the threshold is quite large to tolerate sampling errors
+       tcu::Surface            reference       (m_targetSize.x(), m_targetSize.y());
+
+       drawQuadrantReferenceResult(reference.getAccess(), sample0, sample1, sample2, sample3);
+
+       if (!bilinearCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", reference.getAccess(), result, threshold, tcu::COMPARE_LOG_RESULT))
+               return tcu::TestStatus::fail("Image verification failed");
+       else
+               return tcu::TestStatus::pass("Pass");
+}
+
+class ImageSampleComputeInstance : public vkt::TestInstance
+{
+public:
+                                                                                       ImageSampleComputeInstance              (vkt::Context&                  context,
+                                                                                                                                                        vk::VkDescriptorType   descriptorType,
+                                                                                                                                                        ShaderInputInterface   shaderInterface,
+                                                                                                                                                        vk::VkImageViewType    viewType,
+                                                                                                                                                        deUint32                               baseMipLevel,
+                                                                                                                                                        deUint32                               baseArraySlice,
+                                                                                                                                                        bool                                   isImmutableSampler);
+
+private:
+       vk::Move<vk::VkDescriptorSetLayout>             createDescriptorSetLayout               (void) const;
+       vk::Move<vk::VkDescriptorPool>                  createDescriptorPool                    (void) const;
+       vk::Move<vk::VkDescriptorSet>                   createDescriptorSet                             (vk::VkDescriptorPool pool, vk::VkDescriptorSetLayout layout) const;
+       void                                                                    writeImageSamplerDescriptorSet  (vk::VkDescriptorSet descriptorSet) const;
+       void                                                                    writeSamplerDescriptorSet               (vk::VkDescriptorSet descriptorSet) const;
+
+       tcu::TestStatus                                                 iterate                                                 (void);
+       void                                                                    logTestPlan                                             (void) const;
+       tcu::TestStatus                                                 testResourceAccess                              (void);
+
+       const vk::VkDescriptorType                              m_descriptorType;
+       const ShaderInputInterface                              m_shaderInterface;
+       const vk::VkImageViewType                               m_viewType;
+       const deUint32                                                  m_baseMipLevel;
+       const deUint32                                                  m_baseArraySlice;
+       const bool                                                              m_isImmutableSampler;
+
+       const vk::DeviceInterface&                              m_vki;
+       const vk::VkDevice                                              m_device;
+       const vk::VkQueue                                               m_queue;
+       const deUint32                                                  m_queueFamilyIndex;
+       vk::Allocator&                                                  m_allocator;
+
+       const ComputeInstanceResultBuffer               m_result;
+       const ImageSampleInstanceImages                 m_images;
+};
+
+ImageSampleComputeInstance::ImageSampleComputeInstance (Context&                               context,
+                                                                                                               vk::VkDescriptorType    descriptorType,
+                                                                                                               ShaderInputInterface    shaderInterface,
+                                                                                                               vk::VkImageViewType             viewType,
+                                                                                                               deUint32                                baseMipLevel,
+                                                                                                               deUint32                                baseArraySlice,
+                                                                                                               bool                                    isImmutableSampler)
+       : vkt::TestInstance             (context)
+       , m_descriptorType              (descriptorType)
+       , m_shaderInterface             (shaderInterface)
+       , m_viewType                    (viewType)
+       , m_baseMipLevel                (baseMipLevel)
+       , m_baseArraySlice              (baseArraySlice)
+       , m_isImmutableSampler  (isImmutableSampler)
+       , m_vki                                 (context.getDeviceInterface())
+       , m_device                              (context.getDevice())
+       , m_queue                               (context.getUniversalQueue())
+       , m_queueFamilyIndex    (context.getUniversalQueueFamilyIndex())
+       , m_allocator                   (context.getDefaultAllocator())
+       , m_result                              (m_vki, m_device, m_allocator)
+       , m_images                              (m_vki, m_device, m_queueFamilyIndex, m_queue, m_allocator, m_descriptorType, m_shaderInterface, m_viewType, m_baseMipLevel, m_baseArraySlice, isImmutableSampler)
+{
+}
+
+vk::Move<vk::VkDescriptorSetLayout> ImageSampleComputeInstance::createDescriptorSetLayout (void) const
+{
+       const vk::VkSampler                             samplers[2] =
+       {
+               m_images.getSamplerA(),
+               m_images.getSamplerB(),
+       };
+
+       vk::DescriptorSetLayoutBuilder  builder;
+
+       // result buffer
+       builder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+
+       // with samplers, separate texture at binding 0
+       if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER)
+               builder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+
+       // (combined)samplers follow
+       switch (m_shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.addSingleSamplerBinding(m_descriptorType, vk::VK_SHADER_STAGE_COMPUTE_BIT, (m_images.isImmutable()) ? (&samplers[0]) : (DE_NULL));
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.addSingleSamplerBinding(m_descriptorType, vk::VK_SHADER_STAGE_COMPUTE_BIT, (m_images.isImmutable()) ? (&samplers[0]) : (DE_NULL));
+                       builder.addSingleSamplerBinding(m_descriptorType, vk::VK_SHADER_STAGE_COMPUTE_BIT, (m_images.isImmutable()) ? (&samplers[1]) : (DE_NULL));
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.addArraySamplerBinding(m_descriptorType, 2u, vk::VK_SHADER_STAGE_COMPUTE_BIT, (m_images.isImmutable()) ? (samplers) : (DE_NULL));
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       };
+
+       return builder.build(m_vki, m_device);
+}
+
+vk::Move<vk::VkDescriptorPool> ImageSampleComputeInstance::createDescriptorPool (void) const
+{
+       vk::DescriptorPoolBuilder builder;
+
+       builder.addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
+       builder.addType(m_descriptorType, getInterfaceNumResources(m_shaderInterface));
+
+       if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER)
+               builder.addType(vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE);
+
+       return builder.build(m_vki, m_device, vk::VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1);
+}
+
+vk::Move<vk::VkDescriptorSet> ImageSampleComputeInstance::createDescriptorSet (vk::VkDescriptorPool pool, vk::VkDescriptorSetLayout layout) const
+{
+       vk::Move<vk::VkDescriptorSet> descriptorSet = allocDescriptorSet(m_vki, m_device, pool, vk::VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, layout);
+
+       if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER)
+               writeSamplerDescriptorSet(*descriptorSet);
+       else if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+               writeImageSamplerDescriptorSet(*descriptorSet);
+       else
+               DE_FATAL("Impossible");
+
+       return descriptorSet;
+}
+
+void ImageSampleComputeInstance::writeSamplerDescriptorSet (vk::VkDescriptorSet descriptorSet) const
+{
+       const vk::VkDescriptorInfo              resultInfo                      = createDescriptorInfo(m_result.getBufferView());
+       const vk::VkDescriptorInfo              imageInfo                       = createDescriptorInfo(m_images.getImageViewA(), vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
+       const vk::VkDescriptorInfo              samplersInfos[2]        =
+       {
+               createDescriptorInfo(m_images.getSamplerA()),
+               createDescriptorInfo(m_images.getSamplerB()),
+       };
+
+       vk::DescriptorSetUpdateBuilder  builder;
+
+       // result
+       builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultInfo);
+
+       // stand alone texture
+       builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, &imageInfo);
+
+       // samplers
+       if (!m_isImmutableSampler)
+       {
+               switch (m_shaderInterface)
+               {
+                       case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                               builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(2u), vk::VK_DESCRIPTOR_TYPE_SAMPLER, &samplersInfos[0]);
+                               break;
+
+                       case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                               builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(2u), vk::VK_DESCRIPTOR_TYPE_SAMPLER, &samplersInfos[0]);
+                               builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(3u), vk::VK_DESCRIPTOR_TYPE_SAMPLER, &samplersInfos[1]);
+                               break;
+
+                       case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                               builder.writeArray(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(2u), vk::VK_DESCRIPTOR_TYPE_SAMPLER, 2u, samplersInfos);
+                               break;
+
+                       default:
+                               DE_FATAL("Impossible");
+               }
+       }
+
+       builder.update(m_vki, m_device);
+}
+
+void ImageSampleComputeInstance::writeImageSamplerDescriptorSet (vk::VkDescriptorSet descriptorSet) const
+{
+       const vk::VkDescriptorInfo              resultInfo                      = createDescriptorInfo(m_result.getBufferView());
+       const vk::VkSampler                             samplers[2]                     =
+       {
+               (m_isImmutableSampler) ? (0) : (m_images.getSamplerA()),
+               (m_isImmutableSampler) ? (0) : (m_images.getSamplerB()),
+       };
+       const vk::VkDescriptorInfo              imageSamplers[2]        =
+       {
+               createDescriptorInfo(samplers[0], m_images.getImageViewA(), vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
+               createDescriptorInfo(samplers[1], m_images.getImageViewB(), vk::VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL),
+       };
+
+       vk::DescriptorSetUpdateBuilder  builder;
+
+       // result
+       builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultInfo);
+
+       // combined image samplers
+       switch (m_shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &imageSamplers[0]);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &imageSamplers[0]);
+                       builder.writeSingle(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(2u), vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &imageSamplers[1]);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.writeArray(descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2u, imageSamplers);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       builder.update(m_vki, m_device);
+}
+
+tcu::TestStatus ImageSampleComputeInstance::iterate (void)
+{
+       logTestPlan();
+       return testResourceAccess();
+}
+
+void ImageSampleComputeInstance::logTestPlan (void) const
+{
+       std::ostringstream msg;
+
+       msg << "Accessing resource in a compute program.\n";
+
+       if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER)
+       {
+               msg << "Single descriptor set. Descriptor set contains "
+                       << ((m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? "single" :
+                           (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS) ? "two" :
+                           (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY) ? "an array (size 2) of" :
+                           (const char*)DE_NULL)
+                       << " VK_DESCRIPTOR_TYPE_SAMPLER descriptor(s) and a single texture.\n";
+       }
+       else if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+       {
+               msg << "Single descriptor set. Descriptor set contains "
+                       << ((m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? "single" :
+                           (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS) ? "two" :
+                           (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY) ? "an array (size 2) of" :
+                           (const char*)DE_NULL)
+                       << " VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER descriptor(s).\n";
+       }
+       else
+               DE_FATAL("Impossible");
+
+       msg << "Image view type is " << vk::getImageViewTypeName(m_viewType) << "\n";
+
+       if (m_baseMipLevel)
+               msg << "Image view base mip level = " << m_baseMipLevel << "\n";
+       if (m_baseArraySlice)
+               msg << "Image view base array slice = " << m_baseArraySlice << "\n";
+
+       if (m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR)
+               msg << "Sampler mode is LINEAR, with WRAP\n";
+       else
+               msg << "Sampler 0 mode is LINEAR, with WRAP\nSampler 1 mode is NEAREST with CLAMP\n";
+
+       for (int resultNdx = 0; resultNdx < 4; ++resultNdx)
+       {
+               msg << "Test sample " << resultNdx << ": sample at position " << m_images.getSamplePos(m_viewType, m_baseMipLevel, m_baseArraySlice, resultNdx);
+
+               if (m_shaderInterface != SHADER_INPUT_SINGLE_DESCRIPTOR)
+               {
+                       const int srcResourceNdx = (resultNdx % 2); // ABAB source
+
+                       if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER)
+                               msg << " using sampler " << srcResourceNdx;
+                       else if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
+                               msg << " from combined image sampler " << srcResourceNdx;
+                       else
+                               DE_FATAL("Impossible");
+               }
+               msg << "\n";
+       }
+
+       m_context.getTestContext().getLog()
+               << tcu::TestLog::Message
+               << msg.str()
+               << tcu::TestLog::EndMessage;
+}
+
+tcu::TestStatus ImageSampleComputeInstance::testResourceAccess (void)
+{
+       const vk::Unique<vk::VkDescriptorSetLayout>             descriptorSetLayout     (createDescriptorSetLayout());
+       const vk::Unique<vk::VkDescriptorPool>                  descriptorPool          (createDescriptorPool());
+       const vk::Unique<vk::VkDescriptorSet>                   descriptorSet           (createDescriptorSet(*descriptorPool, *descriptorSetLayout));
+       const ComputePipeline                                                   pipeline                        (m_vki, m_device, m_context.getBinaryCollection(), 1, &descriptorSetLayout.get());
+
+       const vk::VkDescriptorSet                                               descriptorSets[]        = { *descriptorSet };
+       const int                                                                               numDescriptorSets       = DE_LENGTH_OF_ARRAY(descriptorSets);
+       const deUint32* const                                                   dynamicOffsets          = DE_NULL;
+       const int                                                                               numDynamicOffsets       = 0;
+       const void*     const*                                                          preBarriers                     = DE_NULL;
+       const int                                                                               numPreBarriers          = 0;
+       const void* const                                                               postBarriers[]          = { m_result.getResultReadBarrier() };
+       const int                                                                               numPostBarriers         = DE_LENGTH_OF_ARRAY(postBarriers);
+
+       const ComputeCommand                                                    compute                         (m_vki,
+                                                                                                                                                m_device,
+                                                                                                                                                pipeline.getPipeline(),
+                                                                                                                                                pipeline.getPipelineLayout(),
+                                                                                                                                                tcu::UVec3(4, 1, 1),
+                                                                                                                                                numDescriptorSets,     descriptorSets,
+                                                                                                                                                numDynamicOffsets,     dynamicOffsets,
+                                                                                                                                                numPreBarriers,        preBarriers,
+                                                                                                                                                numPostBarriers,       postBarriers);
+
+       tcu::Vec4                                                                               results[4];
+       bool                                                                                    anyResultSet            = false;
+       bool                                                                                    allResultsOk            = true;
+
+       compute.submitAndWait(m_queueFamilyIndex, m_queue);
+       m_result.readResultContentsTo(&results);
+
+       // verify
+       for (int resultNdx = 0; resultNdx < 4; ++resultNdx)
+       {
+               const tcu::Vec4 result                          = results[resultNdx];
+               const tcu::Vec4 reference                       = m_images.fetchSampleValue(resultNdx);
+
+               // source image is high-frequency so the threshold is quite large to tolerate sampling errors
+               const tcu::Vec4 samplingThreshold       = tcu::Vec4(8.0f / 255.0f);
+
+               if (result != tcu::Vec4(-1.0f))
+                       anyResultSet = true;
+
+               if (tcu::boolAny(tcu::greaterThan(tcu::abs(result - reference), samplingThreshold)))
+               {
+                       allResultsOk = false;
+
+                       m_context.getTestContext().getLog()
+                               << tcu::TestLog::Message
+                               << "Test sample " << resultNdx << ":\n"
+                               << "\tSampling at " << m_images.getSamplePos(m_viewType, m_baseMipLevel, m_baseArraySlice, resultNdx) << "\n"
+                               << "\tError expected " << reference << ", got " << result
+                               << tcu::TestLog::EndMessage;
+               }
+       }
+
+       // read back and verify
+       if (allResultsOk)
+               return tcu::TestStatus::pass("Pass");
+       else if (anyResultSet)
+               return tcu::TestStatus::fail("Invalid result values");
+       else
+       {
+               m_context.getTestContext().getLog()
+                       << tcu::TestLog::Message
+                       << "Result buffer was not written to."
+                       << tcu::TestLog::EndMessage;
+               return tcu::TestStatus::fail("Result buffer was not written to");
+       }
+}
+
+class ImageDescriptorCase : public QuadrantRendederCase
+{
+public:
+       enum
+       {
+               FLAG_BASE_MIP   = (1u << 1u),
+               FLAG_BASE_SLICE = (1u << 2u),
+       };
+       // enum continues where resource flags ends
+       DE_STATIC_ASSERT((deUint32)FLAG_BASE_MIP == (deUint32)RESOURCE_FLAG_LAST);
+
+                                                               ImageDescriptorCase                     (tcu::TestContext&              testCtx,
+                                                                                                                        const char*                    name,
+                                                                                                                        const char*                    description,
+                                                                                                                        bool                                   isPrimaryCmdBuf,
+                                                                                                                        vk::VkDescriptorType   descriptorType,
+                                                                                                                        vk::VkShaderStageFlags exitingStages,
+                                                                                                                        vk::VkShaderStageFlags activeStages,
+                                                                                                                        ShaderInputInterface   shaderInterface,
+                                                                                                                        vk::VkImageViewType    viewType,
+                                                                                                                        deUint32                               flags);
+
+private:
+       std::string                                     genExtensionDeclarations        (vk::VkShaderStage stage) const;
+       std::string                                     genResourceDeclarations         (vk::VkShaderStage stage, int numUsedBindings) const;
+       std::string                                     genFetchCoordStr                        (int fetchPosNdx) const;
+       std::string                                     genSampleCoordStr                       (int samplePosNdx) const;
+       std::string                                     genResourceAccessSource         (vk::VkShaderStage stage) const;
+       std::string                                     genNoAccessSource                       (void) const;
+
+       vkt::TestInstance*                      createInstance                          (vkt::Context& context) const;
+
+private:
+       const bool                                      m_isPrimaryCmdBuf;
+       const vk::VkDescriptorType      m_descriptorType;
+       const ShaderInputInterface      m_shaderInterface;
+       const vk::VkImageViewType       m_viewType;
+       const deUint32                          m_baseMipLevel;
+       const deUint32                          m_baseArraySlice;
+       const bool                                      m_isImmutableSampler;
+};
+
+ImageDescriptorCase::ImageDescriptorCase (tcu::TestContext&                    testCtx,
+                                                                                 const char*                           name,
+                                                                                 const char*                           description,
+                                                                                 bool                                          isPrimaryCmdBuf,
+                                                                                 vk::VkDescriptorType          descriptorType,
+                                                                                 vk::VkShaderStageFlags        exitingStages,
+                                                                                 vk::VkShaderStageFlags        activeStages,
+                                                                                 ShaderInputInterface          shaderInterface,
+                                                                                 vk::VkImageViewType           viewType,
+                                                                                 deUint32                                      flags)
+       : QuadrantRendederCase  (testCtx, name, description,
+                                                        // \note 1D textures are not supported in ES
+                                                        (viewType == vk::VK_IMAGE_VIEW_TYPE_1D || viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY) ? glu::GLSL_VERSION_440 : glu::GLSL_VERSION_310_ES,
+                                                        exitingStages, activeStages)
+       , m_isPrimaryCmdBuf             (isPrimaryCmdBuf)
+       , m_descriptorType              (descriptorType)
+       , m_shaderInterface             (shaderInterface)
+       , m_viewType                    (viewType)
+       , m_baseMipLevel                (((flags & FLAG_BASE_MIP) != 0) ? (1u) : (0u))
+       , m_baseArraySlice              (((flags & FLAG_BASE_SLICE) != 0) ? (1u) : (0u))
+       , m_isImmutableSampler  ((flags & RESOURCE_FLAG_IMMUTABLE_SAMPLER) != 0)
+{
+}
+
+std::string ImageDescriptorCase::genExtensionDeclarations (vk::VkShaderStage stage) const
+{
+       DE_UNREF(stage);
+
+       if (m_viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
+               return "#extension GL_OES_texture_cube_map_array : require\n";
+       else
+               return "";
+}
+
+std::string ImageDescriptorCase::genResourceDeclarations (vk::VkShaderStage stage, int numUsedBindings) const
+{
+       DE_UNREF(stage);
+
+       // Vulkan-style resources are arrays implicitly, OpenGL-style are not
+       const std::string       dimensionBase   = (m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D || m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY)            ? ("1D")
+                                                                               : (m_viewType == vk::VK_IMAGE_VIEW_TYPE_2D || m_viewType == vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY)            ? ("2D")
+                                                                               : (m_viewType == vk::VK_IMAGE_VIEW_TYPE_3D)                                                                                                                     ? ("3D")
+                                                                               : (m_viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE || m_viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)        ? ("Cube")
+                                                                               : (DE_NULL);
+       const std::string       dimensionArray  = (m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D || m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY)            ? ("1DArray")
+                                                                               : (m_viewType == vk::VK_IMAGE_VIEW_TYPE_2D || m_viewType == vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY)            ? ("2DArray")
+                                                                               : (m_viewType == vk::VK_IMAGE_VIEW_TYPE_3D)                                                                                                                     ? ("3D")
+                                                                               : (m_viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE || m_viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)        ? ("CubeArray")
+                                                                               : (DE_NULL);
+       const std::string       dimension               = isImageViewTypeArray(m_viewType) ? dimensionArray : dimensionBase;
+
+       switch (m_shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+               {
+                       switch (m_descriptorType)
+                       {
+                               case vk::VK_DESCRIPTOR_TYPE_SAMPLER:
+                                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + ") uniform highp texture" + dimensionBase + " u_separateTexture;\n"
+                                                  "layout(set = 0, binding = " + de::toString(numUsedBindings+1) + ") uniform highp sampler u_separateSampler;\n";
+                               case vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+                                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + ") uniform highp sampler" + dimension + " u_combinedTextureSampler;\n";
+                               case vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+                                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + ") uniform highp texture" + dimensionBase + " u_separateTexture;\n";
+                               case vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+                                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + ", rgba8) readonly uniform highp image" + dimension + " u_image;\n";
+                               default:
+                                       DE_FATAL("invalid descriptor");
+                                       return "";
+                       }
+               }
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       switch (m_descriptorType)
+                       {
+                               case vk::VK_DESCRIPTOR_TYPE_SAMPLER:
+                                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + ") uniform highp texture" + dimensionBase + " u_separateTexture;\n"
+                                                  "layout(set = 0, binding = " + de::toString(numUsedBindings+1) + ") uniform highp sampler u_separateSamplerA;\n"
+                                                  "layout(set = 0, binding = " + de::toString(numUsedBindings+2) + ") uniform highp sampler u_separateSamplerB;\n";
+                               case vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+                                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + ") uniform highp sampler" + dimension + " u_combinedTextureSamplerA;\n"
+                                                  "layout(set = 0, binding = " + de::toString(numUsedBindings+1) + ") uniform highp sampler" + dimension + " u_combinedTextureSamplerB;\n";
+                               case vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+                                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + ") uniform highp texture" + dimensionBase + " u_separateTextureA;\n"
+                                                  "layout(set = 0, binding = " + de::toString(numUsedBindings+1) + ") uniform highp texture" + dimensionBase + " u_separateTextureB;\n";
+                               case vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+                                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + ", rgba8) readonly uniform highp image" + dimension + " u_imageA;\n"
+                                                  "layout(set = 0, binding = " + de::toString(numUsedBindings+1) + ", rgba8) readonly uniform highp image" + dimension + " u_imageB;\n";
+                               default:
+                                       DE_FATAL("invalid descriptor");
+                                       return "";
+                       }
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       switch (m_descriptorType)
+                       {
+                               case vk::VK_DESCRIPTOR_TYPE_SAMPLER:
+                                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + ") uniform highp texture" + dimensionBase + " u_separateTexture;\n"
+                                                  "layout(set = 0, binding = " + de::toString(numUsedBindings+1) + ") uniform highp sampler u_separateSamplers[2];\n";
+                               case vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+                                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + ") uniform highp sampler" + dimension + " u_combinedTextureSampler[2];\n";
+                               case vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+                                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + ") uniform highp texture" + dimensionBase + " u_separateTexture[2];\n";
+                               case vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+                                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + ", rgba8) readonly uniform highp image" + dimension + " u_image[2];\n";
+                               default:
+                                       DE_FATAL("invalid descriptor");
+                                       return "";
+                       }
+
+               default:
+                       DE_FATAL("Impossible");
+                       return "";
+       }
+}
+
+std::string ImageDescriptorCase::genFetchCoordStr (int fetchPosNdx) const
+{
+       DE_ASSERT(m_descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE || m_descriptorType == vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
+       const tcu::IVec3 fetchPos = ImageFetchInstanceImages::getFetchPos(m_viewType, m_baseMipLevel, m_baseArraySlice, fetchPosNdx);
+
+       if (m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D)
+       {
+               return de::toString(fetchPos.x());
+       }
+       else if (m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY || m_viewType == vk::VK_IMAGE_VIEW_TYPE_2D)
+       {
+               std::ostringstream buf;
+               buf << "ivec2(" << fetchPos.x() << ", " << fetchPos.y() << ")";
+               return buf.str();
+       }
+       else
+       {
+               std::ostringstream buf;
+               buf << "ivec3(" << fetchPos.x() << ", " << fetchPos.y() << ", " << fetchPos.z() << ")";
+               return buf.str();
+       }
+}
+
+std::string ImageDescriptorCase::genSampleCoordStr (int samplePosNdx) const
+{
+       DE_ASSERT(m_descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER || m_descriptorType == vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
+       const tcu::Vec4 fetchPos = ImageSampleInstanceImages::getSamplePos(m_viewType, m_baseMipLevel, m_baseArraySlice, samplePosNdx);
+
+       if (m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D)
+       {
+               std::ostringstream buf;
+               buf << "float(" << fetchPos.x() << ")";
+               return buf.str();
+       }
+       else if (m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY || m_viewType == vk::VK_IMAGE_VIEW_TYPE_2D)
+       {
+               std::ostringstream buf;
+               buf << "vec2(float(" << fetchPos.x() << "), float(" << fetchPos.y() << "))";
+               return buf.str();
+       }
+       else if (m_viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)
+       {
+               std::ostringstream buf;
+               buf << "vec4(float(" << fetchPos.x() << "), float(" << fetchPos.y() << "), float(" << fetchPos.z() << "), float(" << fetchPos.w() << "))";
+               return buf.str();
+       }
+       else
+       {
+               std::ostringstream buf;
+               buf << "vec3(float(" << fetchPos.x() << "), float(" << fetchPos.y() << "), float(" << fetchPos.z() << "))";
+               return buf.str();
+       }
+}
+
+std::string ImageDescriptorCase::genResourceAccessSource (vk::VkShaderStage stage) const
+{
+       DE_UNREF(stage);
+
+       const char* const       dimensionArray  = (m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D || m_viewType == vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY)            ? ("1DArray")
+                                                                               : (m_viewType == vk::VK_IMAGE_VIEW_TYPE_2D || m_viewType == vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY)            ? ("2DArray")
+                                                                               : (m_viewType == vk::VK_IMAGE_VIEW_TYPE_3D)                                                                                                                     ? ("3D")
+                                                                               : (m_viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE || m_viewType == vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY)        ? ("CubeArray")
+                                                                               : (DE_NULL);
+       const char* const       accessPostfixA  = (m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR)         ? ("")
+                                                                               : (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS)      ? ("A")
+                                                                               : (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY)          ? ("[0]")
+                                                                               : (DE_NULL);
+       const char* const       accessPostfixB  = (m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR)         ? ("")
+                                                                               : (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS)      ? ("B")
+                                                                               : (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY)          ? ("[1]")
+                                                                               : (DE_NULL);
+
+       switch (m_descriptorType)
+       {
+               case vk::VK_DESCRIPTOR_TYPE_SAMPLER:
+               case vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+               {
+                       const std::string       coodStr[4]      =
+                       {
+                               genSampleCoordStr(0),
+                               genSampleCoordStr(1),
+                               genSampleCoordStr(2),
+                               genSampleCoordStr(3),
+                       };
+                       std::ostringstream      buf;
+
+                       if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLER)
+                       {
+                               buf << "        if (quadrant_id == 0)\n"
+                                       << "            result_color = textureLod(sampler" << dimensionArray << "(u_separateTexture, u_separateSampler" << accessPostfixA << "), " << coodStr[0] << ", 0.0);\n"
+                                       << "    else if (quadrant_id == 1)\n"
+                                       << "            result_color = textureLod(sampler" << dimensionArray << "(u_separateTexture, u_separateSampler" << accessPostfixB << "), " << coodStr[1] << ", 0.0);\n"
+                                       << "    else if (quadrant_id == 2)\n"
+                                       << "            result_color = textureLod(sampler" << dimensionArray << "(u_separateTexture, u_separateSampler" << accessPostfixA << "), " << coodStr[2] << ", 0.0);\n"
+                                       << "    else\n"
+                                       << "            result_color = textureLod(sampler" << dimensionArray << "(u_separateTexture, u_separateSampler" << accessPostfixB << "), " << coodStr[3] << ", 0.0);\n";
+                       }
+                       else
+                       {
+                               buf << "        if (quadrant_id == 0)\n"
+                                       << "            result_color = textureLod(u_combinedTextureSampler" << accessPostfixA << ", " << coodStr[0] << ", 0.0);\n"
+                                       << "    else if (quadrant_id == 1)\n"
+                                       << "            result_color = textureLod(u_combinedTextureSampler" << accessPostfixB << ", " << coodStr[1] << ", 0.0);\n"
+                                       << "    else if (quadrant_id == 2)\n"
+                                       << "            result_color = textureLod(u_combinedTextureSampler" << accessPostfixA << ", " << coodStr[2] << ", 0.0);\n"
+                                       << "    else\n"
+                                       << "            result_color = textureLod(u_combinedTextureSampler" << accessPostfixB << ", " << coodStr[3] << ", 0.0);\n";
+                       }
+
+                       return buf.str();
+               }
+
+               case vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+               case vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+               {
+                       const std::string       coodStr[4]      =
+                       {
+                               genFetchCoordStr(0),
+                               genFetchCoordStr(1),
+                               genFetchCoordStr(2),
+                               genFetchCoordStr(3),
+                       };
+                       std::ostringstream      buf;
+
+                       if (m_descriptorType == vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE)
+                       {
+                               buf << "        if (quadrant_id == 0)\n"
+                                       << "            result_color = textureFetch(u_separateTexture" << accessPostfixA << ", " << coodStr[0] << ", 0);\n"
+                                       << "    else if (quadrant_id == 1)\n"
+                                       << "            result_color = textureFetch(u_separateTexture" << accessPostfixB << ", " << coodStr[1] << ", 0);\n"
+                                       << "    else if (quadrant_id == 2)\n"
+                                       << "            result_color = textureFetch(u_separateTexture" << accessPostfixA << ", " << coodStr[2] << ", 0);\n"
+                                       << "    else\n"
+                                       << "            result_color = textureFetch(u_separateTexture" << accessPostfixB << ", " << coodStr[3] << ", 0);\n";
+                       }
+                       else
+                       {
+                               buf << "        if (quadrant_id == 0)\n"
+                                       << "            result_color = imageLoad(u_image" << accessPostfixA << ", " << coodStr[0] << ");\n"
+                                       << "    else if (quadrant_id == 1)\n"
+                                       << "            result_color = imageLoad(u_image" << accessPostfixB << ", " << coodStr[1] << ");\n"
+                                       << "    else if (quadrant_id == 2)\n"
+                                       << "            result_color = imageLoad(u_image" << accessPostfixA << ", " << coodStr[2] << ");\n"
+                                       << "    else\n"
+                                       << "            result_color = imageLoad(u_image" << accessPostfixB << ", " << coodStr[3] << ");\n";
+                       }
+
+                       return buf.str();
+               }
+
+               default:
+                       DE_FATAL("invalid descriptor");
+                       return "";
+       }
+}
+
+std::string ImageDescriptorCase::genNoAccessSource (void) const
+{
+       return "        if (quadrant_id == 1 || quadrant_id == 2)\n"
+                       "               result_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
+                       "       else\n"
+                       "               result_color = vec4(1.0, 1.0, 0.0, 1.0);\n";
+}
+
+vkt::TestInstance* ImageDescriptorCase::createInstance (vkt::Context& context) const
+{
+       switch (m_descriptorType)
+       {
+               case vk::VK_DESCRIPTOR_TYPE_SAMPLER:
+               case vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+                       if (m_exitingStages == vk::VK_SHADER_STAGE_COMPUTE_BIT)
+                       {
+                               DE_ASSERT(m_isPrimaryCmdBuf);
+                               return new ImageSampleComputeInstance(context, m_descriptorType, m_shaderInterface, m_viewType, m_baseMipLevel, m_baseArraySlice, m_isImmutableSampler);
+                       }
+                       else
+                               return new ImageSampleRenderInstance(context, m_isPrimaryCmdBuf, m_descriptorType, m_activeStages, m_shaderInterface, m_viewType, m_baseMipLevel, m_baseArraySlice, m_isImmutableSampler);
+
+               case vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+               case vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+                       if (m_exitingStages == vk::VK_SHADER_STAGE_COMPUTE_BIT)
+                       {
+                               DE_ASSERT(m_isPrimaryCmdBuf);
+                               return new ImageFetchComputeInstance(context, m_descriptorType, m_shaderInterface, m_viewType, m_baseMipLevel, m_baseArraySlice);
+                       }
+                       else
+                               return new ImageFetchRenderInstance(context, m_isPrimaryCmdBuf, m_descriptorType, m_activeStages, m_shaderInterface, m_viewType, m_baseMipLevel, m_baseArraySlice);
+
+               default:
+                       DE_FATAL("Impossible");
+                       return DE_NULL;
+       }
+}
+
+class TexelBufferInstanceBuffers
+{
+public:
+                                                                               TexelBufferInstanceBuffers      (const vk::DeviceInterface&             vki,
+                                                                                                                                        vk::VkDevice                                   device,
+                                                                                                                                        vk::Allocator&                                 allocator,
+                                                                                                                                        vk::VkDescriptorType                   descriptorType,
+                                                                                                                                        int                                                    numTexelBuffers,
+                                                                                                                                        bool                                                   hasViewOffset);
+
+private:
+       static vk::Move<vk::VkBuffer>           createBuffer                            (const vk::DeviceInterface&             vki,
+                                                                                                                                        vk::VkDevice                                   device,
+                                                                                                                                        vk::Allocator&                                 allocator,
+                                                                                                                                        vk::VkDescriptorType                   descriptorType,
+                                                                                                                                        de::MovePtr<vk::Allocation>    *outAllocation);
+
+       static vk::Move<vk::VkBufferView>       createBufferView                        (const vk::DeviceInterface&             vki,
+                                                                                                                                        vk::VkDevice                                   device,
+                                                                                                                                        const tcu::TextureFormat&              textureFormat,
+                                                                                                                                        deUint32                                               offset,
+                                                                                                                                        vk::VkBuffer                                   buffer);
+
+       static vk::VkBufferMemoryBarrier        createBarrier                           (vk::VkDescriptorType descriptorType, vk::VkBuffer buffer);
+
+       void                                                            populateSourceBuffer            (const tcu::PixelBufferAccess& access);
+       void                                                            uploadData                                      (const vk::DeviceInterface& vki, vk::VkDevice device, const vk::Allocation& memory, const de::ArrayBuffer<deUint8>& data);
+
+public:
+       static int                                                      getFetchPos                                     (int fetchPosNdx);
+       tcu::Vec4                                                       fetchTexelValue                         (int fetchPosNdx) const;
+
+       inline int                                                      getNumTexelBuffers                      (void) const { return m_numTexelBuffers;        }
+       const tcu::TextureFormat&                       getTextureFormat                        (void) const { return m_imageFormat;            }
+       inline vk::VkBufferView                         getBufferViewA                          (void) const { return *m_bufferViewA;           }
+       inline vk::VkBufferView                         getBufferViewB                          (void) const { return *m_bufferViewB;           }
+       inline const void*                                      getBufferInitBarrierA           (void) const { return &m_bufferBarrierA;        }
+       inline const void*                                      getBufferInitBarrierB           (void) const { return &m_bufferBarrierB;        }
+
+private:
+       enum
+       {
+               BUFFER_SIZE                     = 512,
+               VIEW_OFFSET_VALUE       = 256,
+               VIEW_DATA_SIZE          = 256,  //!< size in bytes
+               VIEW_WIDTH                      = 64,   //!< size in pixels
+       };
+       enum
+       {
+               // some arbitrary points
+               SAMPLE_POINT_0 = 6,
+               SAMPLE_POINT_1 = 51,
+               SAMPLE_POINT_2 = 42,
+               SAMPLE_POINT_3 = 25,
+       };
+
+       const deUint32                                          m_numTexelBuffers;
+       const tcu::TextureFormat                        m_imageFormat;
+       const deUint32                                          m_viewOffset;
+
+       de::ArrayBuffer<deUint8>                        m_sourceBufferA;
+       de::ArrayBuffer<deUint8>                        m_sourceBufferB;
+       const tcu::ConstPixelBufferAccess       m_sourceViewA;
+       const tcu::ConstPixelBufferAccess       m_sourceViewB;
+
+       de::MovePtr<vk::Allocation>                     m_bufferMemoryA;
+       de::MovePtr<vk::Allocation>                     m_bufferMemoryB;
+       const vk::Unique<vk::VkBuffer>          m_bufferA;
+       const vk::Unique<vk::VkBuffer>          m_bufferB;
+       const vk::Unique<vk::VkBufferView>      m_bufferViewA;
+       const vk::Unique<vk::VkBufferView>      m_bufferViewB;
+       const vk::VkBufferMemoryBarrier         m_bufferBarrierA;
+       const vk::VkBufferMemoryBarrier         m_bufferBarrierB;
+};
+
+TexelBufferInstanceBuffers::TexelBufferInstanceBuffers (const vk::DeviceInterface&             vki,
+                                                                                                               vk::VkDevice                                    device,
+                                                                                                               vk::Allocator&                                  allocator,
+                                                                                                               vk::VkDescriptorType                    descriptorType,
+                                                                                                               int                                                             numTexelBuffers,
+                                                                                                               bool                                                    hasViewOffset)
+       : m_numTexelBuffers     (numTexelBuffers)
+       , m_imageFormat         (tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8)
+       , m_viewOffset          ((hasViewOffset) ? ((deUint32)VIEW_OFFSET_VALUE) : (0u))
+       , m_sourceBufferA       (BUFFER_SIZE)
+       , m_sourceBufferB       ((numTexelBuffers == 1)
+                                                       ? (0u)
+                                                       : ((size_t)BUFFER_SIZE))
+       , m_sourceViewA         (m_imageFormat, tcu::IVec3(VIEW_WIDTH, 1, 1), m_sourceBufferA.getElementPtr(m_viewOffset))
+       , m_sourceViewB         (m_imageFormat, tcu::IVec3(VIEW_WIDTH, 1, 1), m_sourceBufferB.getElementPtr(m_viewOffset))
+       , m_bufferMemoryA       (DE_NULL)
+       , m_bufferMemoryB       (DE_NULL)
+       , m_bufferA                     (createBuffer(vki, device, allocator, descriptorType, &m_bufferMemoryA))
+       , m_bufferB                     ((numTexelBuffers == 1)
+                                                       ? vk::Move<vk::VkBuffer>()
+                                                       : createBuffer(vki, device, allocator, descriptorType, &m_bufferMemoryB))
+       , m_bufferViewA         (createBufferView(vki, device, m_imageFormat, m_viewOffset, *m_bufferA))
+       , m_bufferViewB         ((numTexelBuffers == 1)
+                                                       ? vk::Move<vk::VkBufferView>()
+                                                       : createBufferView(vki, device, m_imageFormat, m_viewOffset, *m_bufferB))
+       , m_bufferBarrierA      (createBarrier(descriptorType, *m_bufferA))
+       , m_bufferBarrierB      (createBarrier(descriptorType, *m_bufferB))
+{
+       DE_ASSERT(numTexelBuffers == 1 || numTexelBuffers == 2);
+       DE_ASSERT(VIEW_WIDTH * m_imageFormat.getPixelSize() == VIEW_DATA_SIZE);
+       DE_ASSERT(BUFFER_SIZE % m_imageFormat.getPixelSize() == 0);
+
+       // specify and upload
+
+       populateSourceBuffer(tcu::PixelBufferAccess(m_imageFormat, tcu::IVec3(BUFFER_SIZE / m_imageFormat.getPixelSize(), 1, 1), m_sourceBufferA.getPtr()));
+       uploadData(vki, device, *m_bufferMemoryA, m_sourceBufferA);
+
+       if (numTexelBuffers == 2)
+       {
+               populateSourceBuffer(tcu::PixelBufferAccess(m_imageFormat, tcu::IVec3(BUFFER_SIZE / m_imageFormat.getPixelSize(), 1, 1), m_sourceBufferB.getPtr()));
+               uploadData(vki, device, *m_bufferMemoryB, m_sourceBufferB);
+       }
+}
+
+vk::Move<vk::VkBuffer> TexelBufferInstanceBuffers::createBuffer (const vk::DeviceInterface&            vki,
+                                                                                                                                vk::VkDevice                                   device,
+                                                                                                                                vk::Allocator&                                 allocator,
+                                                                                                                                vk::VkDescriptorType                   descriptorType,
+                                                                                                                                de::MovePtr<vk::Allocation>    *outAllocation)
+{
+       const vk::VkBufferUsageFlags    usage           = (isUniformDescriptorType(descriptorType)) ? (vk::VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT) : (vk::VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT);
+       const vk::VkBufferCreateInfo    createInfo      =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
+               DE_NULL,
+               (vk::VkDeviceSize)BUFFER_SIZE,          // size
+               usage,                                                          // usage
+               0u,                                                                     // flags
+               vk::VK_SHARING_MODE_EXCLUSIVE,          // sharingMode
+               0u,                                                                     // queueFamilyCount
+               DE_NULL,                                                        // pQueueFamilyIndices
+       };
+       vk::Move<vk::VkBuffer>                  buffer          (vk::createBuffer(vki, device, &createInfo));
+       de::MovePtr<vk::Allocation>             allocation      (allocateAndBindObjectMemory(vki, device, allocator, *buffer, vk::MemoryRequirement::HostVisible));
+
+       *outAllocation = allocation;
+       return buffer;
+}
+
+vk::Move<vk::VkBufferView> TexelBufferInstanceBuffers::createBufferView (const vk::DeviceInterface&            vki,
+                                                                                                                                                vk::VkDevice                                   device,
+                                                                                                                                                const tcu::TextureFormat&              textureFormat,
+                                                                                                                                                deUint32                                               offset,
+                                                                                                                                                vk::VkBuffer                                   buffer)
+{
+       const vk::VkBufferViewCreateInfo createInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO,
+               DE_NULL,
+               buffer,                                                                 // buffer
+               vk::VK_BUFFER_VIEW_TYPE_FORMATTED,              // viewType
+               mapToVkTextureFormat(textureFormat),    // format
+               (vk::VkDeviceSize)offset,                               // offset
+               (vk::VkDeviceSize)VIEW_DATA_SIZE                // range
+       };
+       return vk::createBufferView(vki, device, &createInfo);
+}
+
+vk::VkBufferMemoryBarrier TexelBufferInstanceBuffers::createBarrier (vk::VkDescriptorType descriptorType, vk::VkBuffer buffer)
+{
+       const vk::VkMemoryInputFlags    inputBit        = (isUniformDescriptorType(descriptorType)) ? (vk::VK_MEMORY_INPUT_UNIFORM_READ_BIT) : (vk::VK_MEMORY_INPUT_SHADER_READ_BIT);
+       const vk::VkBufferMemoryBarrier barrier         =
+       {
+               vk::VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+               DE_NULL,
+               vk::VK_MEMORY_OUTPUT_HOST_WRITE_BIT,    // outputMask
+               inputBit,                                                               // inputMask
+               vk::VK_QUEUE_FAMILY_IGNORED,                    // srcQueueFamilyIndex
+               vk::VK_QUEUE_FAMILY_IGNORED,                    // destQueueFamilyIndex
+               buffer  ,                                                               // buffer
+               0u,                                                                             // offset
+               (vk::VkDeviceSize)BUFFER_SIZE                   // size
+       };
+       return barrier;
+}
+
+void TexelBufferInstanceBuffers::populateSourceBuffer (const tcu::PixelBufferAccess& access)
+{
+       DE_ASSERT(access.getHeight() == 1);
+       DE_ASSERT(access.getDepth() == 1);
+
+       const deInt32 width = access.getWidth();
+
+       for (int x = 0; x < width; ++x)
+       {
+               const int                       red             = 255 * x / width;                                                                                              //!< gradient from 0 -> max (detects large offset errors)
+               const int                       green   = ((x % 2 == 0) ? (127) : (0)) + ((x % 4 < 3) ? (128) : (0));   //!< 3-level M pattern (detects small offset errors)
+               const int                       blue    = 16 * (x % 16);                                                                                                //!< 16-long triangle wave
+
+               DE_ASSERT(de::inRange(red, 0, 255));
+               DE_ASSERT(de::inRange(green, 0, 255));
+               DE_ASSERT(de::inRange(blue, 0, 255));
+
+               access.setPixel(tcu::IVec4(red, green, blue, 255), x, 0, 0);
+       }
+}
+
+void TexelBufferInstanceBuffers::uploadData (const vk::DeviceInterface& vki, vk::VkDevice device, const vk::Allocation& memory, const de::ArrayBuffer<deUint8>& data)
+{
+       deMemcpy(memory.getHostPtr(), data.getPtr(), data.size());
+       flushMappedMemoryRange(vki, device, memory.getMemory(), memory.getOffset(), data.size());
+}
+
+int TexelBufferInstanceBuffers::getFetchPos (int fetchPosNdx)
+{
+       static const int fetchPositions[4] =
+       {
+               SAMPLE_POINT_0,
+               SAMPLE_POINT_1,
+               SAMPLE_POINT_2,
+               SAMPLE_POINT_3,
+       };
+       return de::getSizedArrayElement<4>(fetchPositions, fetchPosNdx);
+}
+
+tcu::Vec4 TexelBufferInstanceBuffers::fetchTexelValue (int fetchPosNdx) const
+{
+       // source order is ABAB
+       const tcu::ConstPixelBufferAccess&      texelSrcA       = m_sourceViewA;
+       const tcu::ConstPixelBufferAccess&      texelSrcB       = (m_numTexelBuffers == 1) ? (m_sourceViewA) : (m_sourceViewB);
+       const tcu::ConstPixelBufferAccess&      texelSrc        = ((fetchPosNdx % 2) == 0) ? (texelSrcA) : (texelSrcB);
+
+       return texelSrc.getPixel(getFetchPos(fetchPosNdx), 0, 0);
+}
+
+class TexelBufferRenderInstance : public SingleCmdRenderInstance
+{
+public:
+                                                                                                       TexelBufferRenderInstance       (vkt::Context&                  context,
+                                                                                                                                                                bool                                   isPrimaryCmdBuf,
+                                                                                                                                                                vk::VkDescriptorType   descriptorType,
+                                                                                                                                                                vk::VkShaderStageFlags stageFlags,
+                                                                                                                                                                ShaderInputInterface   shaderInterface,
+                                                                                                                                                                bool                                   nonzeroViewOffset);
+
+private:
+       static vk::Move<vk::VkDescriptorSetLayout>              createDescriptorSetLayout       (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkDescriptorType           descriptorType,
+                                                                                                                                                                ShaderInputInterface           shaderInterface,
+                                                                                                                                                                vk::VkShaderStageFlags         stageFlags);
+
+       static vk::Move<vk::VkPipelineLayout>                   createPipelineLayout            (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkDescriptorSetLayout      descriptorSetLayout);
+
+       static vk::Move<vk::VkDescriptorPool>                   createDescriptorPool            (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkDescriptorType           descriptorType,
+                                                                                                                                                                ShaderInputInterface           shaderInterface);
+
+       static vk::Move<vk::VkDescriptorSet>                    createDescriptorSet                     (const vk::DeviceInterface&     vki,
+                                                                                                                                                                vk::VkDevice                           device,
+                                                                                                                                                                vk::VkDescriptorType           descriptorType,
+                                                                                                                                                                ShaderInputInterface           shaderInterface,
+                                                                                                                                                                vk::VkDescriptorSetLayout      layout,
+                                                                                                                                                                vk::VkDescriptorPool           pool,
+                                                                                                                                                                vk::VkBufferView                       viewA,
+                                                                                                                                                                vk::VkBufferView                       viewB);
+
+       void                                                                                    logTestPlan                                     (void) const;
+       vk::VkPipelineLayout                                                    getPipelineLayout                       (void) const;
+       void                                                                                    writeDrawCmdBuffer                      (vk::VkCmdBuffer cmd) const;
+       tcu::TestStatus                                                                 verifyResultImage                       (const tcu::ConstPixelBufferAccess& result) const;
+
+       enum
+       {
+               RENDER_SIZE = 128,
+       };
+
+       const vk::VkDescriptorType                                              m_descriptorType;
+       const vk::VkShaderStageFlags                                    m_stageFlags;
+       const ShaderInputInterface                                              m_shaderInterface;
+       const bool                                                                              m_nonzeroViewOffset;
+
+       const vk::Unique<vk::VkDescriptorSetLayout>             m_descriptorSetLayout;
+       const vk::Unique<vk::VkPipelineLayout>                  m_pipelineLayout;
+       const TexelBufferInstanceBuffers                                m_texelBuffers;
+       const vk::Unique<vk::VkDescriptorPool>                  m_descriptorPool;
+       const vk::Unique<vk::VkDescriptorSet>                   m_descriptorSet;
+};
+
+TexelBufferRenderInstance::TexelBufferRenderInstance (vkt::Context&                            context,
+                                                                                                         bool                                          isPrimaryCmdBuf,
+                                                                                                         vk::VkDescriptorType          descriptorType,
+                                                                                                         vk::VkShaderStageFlags        stageFlags,
+                                                                                                         ShaderInputInterface          shaderInterface,
+                                                                                                         bool                                          nonzeroViewOffset)
+       : SingleCmdRenderInstance       (context, isPrimaryCmdBuf, tcu::UVec2(RENDER_SIZE, RENDER_SIZE))
+       , m_descriptorType                      (descriptorType)
+       , m_stageFlags                          (stageFlags)
+       , m_shaderInterface                     (shaderInterface)
+       , m_nonzeroViewOffset           (nonzeroViewOffset)
+       , m_descriptorSetLayout         (createDescriptorSetLayout(m_vki, m_device, m_descriptorType, m_shaderInterface, m_stageFlags))
+       , m_pipelineLayout                      (createPipelineLayout(m_vki, m_device, *m_descriptorSetLayout))
+       , m_texelBuffers                        (m_vki, m_device, m_allocator, m_descriptorType, getInterfaceNumResources(m_shaderInterface), m_nonzeroViewOffset)
+       , m_descriptorPool                      (createDescriptorPool(m_vki, m_device, m_descriptorType, m_shaderInterface))
+       , m_descriptorSet                       (createDescriptorSet(m_vki, m_device, m_descriptorType, m_shaderInterface, *m_descriptorSetLayout, *m_descriptorPool, m_texelBuffers.getBufferViewA(), m_texelBuffers.getBufferViewB()))
+{
+}
+
+vk::Move<vk::VkDescriptorSetLayout> TexelBufferRenderInstance::createDescriptorSetLayout (const vk::DeviceInterface&   vki,
+                                                                                                                                                                                 vk::VkDevice                                  device,
+                                                                                                                                                                                 vk::VkDescriptorType                  descriptorType,
+                                                                                                                                                                                 ShaderInputInterface                  shaderInterface,
+                                                                                                                                                                                 vk::VkShaderStageFlags                stageFlags)
+{
+       vk::DescriptorSetLayoutBuilder builder;
+
+       switch (shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.addSingleBinding(descriptorType, stageFlags);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.addSingleBinding(descriptorType, stageFlags);
+                       builder.addSingleBinding(descriptorType, stageFlags);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.addArrayBinding(descriptorType, 2u, stageFlags);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       return builder.build(vki, device);
+}
+
+vk::Move<vk::VkPipelineLayout> TexelBufferRenderInstance::createPipelineLayout (const vk::DeviceInterface&     vki,
+                                                                                                                                                               vk::VkDevice                            device,
+                                                                                                                                                               vk::VkDescriptorSetLayout       descriptorSetLayout)
+{
+       const vk::VkPipelineLayoutCreateInfo createInfo =
+       {
+               vk::VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
+               DE_NULL,
+               1,                                              // descriptorSetCount
+               &descriptorSetLayout,   // pSetLayouts
+               0u,                                             // pushConstantRangeCount
+               DE_NULL,                                // pPushConstantRanges
+       };
+       return vk::createPipelineLayout(vki, device, &createInfo);
+}
+
+vk::Move<vk::VkDescriptorPool> TexelBufferRenderInstance::createDescriptorPool (const vk::DeviceInterface&     vki,
+                                                                                                                                                               vk::VkDevice                                    device,
+                                                                                                                                                               vk::VkDescriptorType                    descriptorType,
+                                                                                                                                                               ShaderInputInterface                    shaderInterface)
+{
+       return vk::DescriptorPoolBuilder()
+               .addType(descriptorType, getInterfaceNumResources(shaderInterface))
+               .build(vki, device, vk::VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1);
+}
+
+vk::Move<vk::VkDescriptorSet> TexelBufferRenderInstance::createDescriptorSet (const vk::DeviceInterface&       vki,
+                                                                                                                                                         vk::VkDevice                                  device,
+                                                                                                                                                         vk::VkDescriptorType                  descriptorType,
+                                                                                                                                                         ShaderInputInterface                  shaderInterface,
+                                                                                                                                                         vk::VkDescriptorSetLayout             layout,
+                                                                                                                                                         vk::VkDescriptorPool                  pool,
+                                                                                                                                                         vk::VkBufferView                              viewA,
+                                                                                                                                                         vk::VkBufferView                              viewB)
+{
+       const vk::VkDescriptorInfo              texelBufferInfos[2]     =
+       {
+               createDescriptorInfo(viewA),
+               createDescriptorInfo(viewB),
+       };
+
+       vk::Move<vk::VkDescriptorSet>   descriptorSet           = allocDescriptorSet(vki, device, pool, vk::VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, layout);
+       vk::DescriptorSetUpdateBuilder  builder;
+
+       switch (shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), descriptorType, &texelBufferInfos[0]);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), descriptorType, &texelBufferInfos[0]);
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), descriptorType, &texelBufferInfos[1]);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.writeArray(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), descriptorType, 2u, texelBufferInfos);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       builder.update(vki, device);
+       return descriptorSet;
+}
+
+void TexelBufferRenderInstance::logTestPlan (void) const
+{
+       std::ostringstream msg;
+
+       msg << "Rendering 2x2 grid.\n"
+               << "Single descriptor set. Descriptor set contains "
+                       << ((m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? "single" :
+                           (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS) ? "two" :
+                           (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY) ? "an array (size 2) of" :
+                           (const char*)DE_NULL)
+               << " descriptor(s) of type " << vk::getDescriptorTypeName(m_descriptorType) << "\n"
+               << "Buffer view is created with a " << ((m_nonzeroViewOffset) ? ("non-zero") : ("zero")) << " offset.\n"
+               << "Buffer format is " << vk::getFormatName(mapToVkTextureFormat(m_texelBuffers.getTextureFormat())) << ".\n";
+
+       if (m_stageFlags == 0u)
+       {
+               msg << "Descriptors are not accessed in any shader stage.\n";
+       }
+       else
+       {
+               msg << "Color in each cell is fetched using the descriptor(s):\n";
+
+               for (int resultNdx = 0; resultNdx < 4; ++resultNdx)
+               {
+                       msg << "Test sample " << resultNdx << ": fetch at position " << m_texelBuffers.getFetchPos(resultNdx);
+
+                       if (m_shaderInterface != SHADER_INPUT_SINGLE_DESCRIPTOR)
+                       {
+                               const int srcResourceNdx = (resultNdx % 2); // ABAB source
+                               msg << " from texelBuffer " << srcResourceNdx;
+                       }
+
+                       msg << "\n";
+               }
+
+               msg << "Descriptors are accessed in {"
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_VERTEX_BIT) != 0)                      ? (" vertex")                   : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_TESS_CONTROL_BIT) != 0)        ? (" tess_control")             : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_TESS_EVALUATION_BIT) != 0)     ? (" tess_evaluation")  : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_GEOMETRY_BIT) != 0)            ? (" geometry")                 : (""))
+                       << (((m_stageFlags & vk::VK_SHADER_STAGE_FRAGMENT_BIT) != 0)            ? (" fragment")                 : (""))
+                       << " } stages.";
+       }
+
+       m_context.getTestContext().getLog()
+               << tcu::TestLog::Message
+               << msg.str()
+               << tcu::TestLog::EndMessage;
+}
+
+vk::VkPipelineLayout TexelBufferRenderInstance::getPipelineLayout (void) const
+{
+       return *m_pipelineLayout;
+}
+
+void TexelBufferRenderInstance::writeDrawCmdBuffer (vk::VkCmdBuffer cmd) const
+{
+       m_vki.cmdBindDescriptorSets(cmd, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, getPipelineLayout(), 0, 1, &m_descriptorSet.get(), 0, DE_NULL);
+       m_vki.cmdDraw(cmd, 0, 6 * 4, 0, 1); // render four quads (two separate triangles)
+}
+
+tcu::TestStatus TexelBufferRenderInstance::verifyResultImage (const tcu::ConstPixelBufferAccess& result) const
+{
+       const tcu::Vec4         green           (0.0f, 1.0f, 0.0f, 1.0f);
+       const tcu::Vec4         yellow          (1.0f, 1.0f, 0.0f, 1.0f);
+       const bool                      doFetch         = (m_stageFlags != 0u); // no active stages? Then don't fetch
+       const tcu::Vec4         sample0         = (!doFetch) ? (yellow) : (m_texelBuffers.fetchTexelValue(0));
+       const tcu::Vec4         sample1         = (!doFetch) ? (green)  : (m_texelBuffers.fetchTexelValue(1));
+       const tcu::Vec4         sample2         = (!doFetch) ? (green)  : (m_texelBuffers.fetchTexelValue(2));
+       const tcu::Vec4         sample3         = (!doFetch) ? (yellow) : (m_texelBuffers.fetchTexelValue(3));
+       tcu::Surface            reference       (m_targetSize.x(), m_targetSize.y());
+
+       drawQuadrantReferenceResult(reference.getAccess(), sample0, sample1, sample2, sample3);
+
+       if (!bilinearCompare(m_context.getTestContext().getLog(), "Compare", "Result comparison", reference.getAccess(), result, tcu::RGBA(1, 1, 1, 1), tcu::COMPARE_LOG_RESULT))
+               return tcu::TestStatus::fail("Image verification failed");
+       else
+               return tcu::TestStatus::pass("Pass");
+}
+
+class TexelBufferComputeInstance : public vkt::TestInstance
+{
+public:
+                                                                                       TexelBufferComputeInstance      (vkt::Context&                  context,
+                                                                                                                                                vk::VkDescriptorType   descriptorType,
+                                                                                                                                                ShaderInputInterface   shaderInterface,
+                                                                                                                                                bool                                   nonzeroViewOffset);
+
+private:
+       vk::Move<vk::VkDescriptorSetLayout>             createDescriptorSetLayout       (void) const;
+       vk::Move<vk::VkDescriptorPool>                  createDescriptorPool            (void) const;
+       vk::Move<vk::VkDescriptorSet>                   createDescriptorSet                     (vk::VkDescriptorPool pool, vk::VkDescriptorSetLayout layout) const;
+
+       tcu::TestStatus                                                 iterate                                         (void);
+       void                                                                    logTestPlan                                     (void) const;
+       tcu::TestStatus                                                 testResourceAccess                      (void);
+
+       const vk::VkDescriptorType                              m_descriptorType;
+       const ShaderInputInterface                              m_shaderInterface;
+       const bool                                                              m_nonzeroViewOffset;
+
+       const vk::DeviceInterface&                              m_vki;
+       const vk::VkDevice                                              m_device;
+       const vk::VkQueue                                               m_queue;
+       const deUint32                                                  m_queueFamilyIndex;
+       vk::Allocator&                                                  m_allocator;
+
+       const ComputeInstanceResultBuffer               m_result;
+       const TexelBufferInstanceBuffers                m_texelBuffers;
+};
+
+TexelBufferComputeInstance::TexelBufferComputeInstance (Context&                               context,
+                                                                                                               vk::VkDescriptorType    descriptorType,
+                                                                                                               ShaderInputInterface    shaderInterface,
+                                                                                                               bool                                    nonzeroViewOffset)
+       : vkt::TestInstance             (context)
+       , m_descriptorType              (descriptorType)
+       , m_shaderInterface             (shaderInterface)
+       , m_nonzeroViewOffset   (nonzeroViewOffset)
+       , m_vki                                 (context.getDeviceInterface())
+       , m_device                              (context.getDevice())
+       , m_queue                               (context.getUniversalQueue())
+       , m_queueFamilyIndex    (context.getUniversalQueueFamilyIndex())
+       , m_allocator                   (context.getDefaultAllocator())
+       , m_result                              (m_vki, m_device, m_allocator)
+       , m_texelBuffers                (m_vki, m_device, m_allocator, m_descriptorType, getInterfaceNumResources(m_shaderInterface), m_nonzeroViewOffset)
+{
+}
+
+vk::Move<vk::VkDescriptorSetLayout> TexelBufferComputeInstance::createDescriptorSetLayout (void) const
+{
+       vk::DescriptorSetLayoutBuilder builder;
+
+       builder.addSingleBinding(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+
+       switch (m_shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.addSingleBinding(m_descriptorType, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.addSingleBinding(m_descriptorType, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+                       builder.addSingleBinding(m_descriptorType, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.addArrayBinding(m_descriptorType, 2u, vk::VK_SHADER_STAGE_COMPUTE_BIT);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       };
+
+       return builder.build(m_vki, m_device);
+}
+
+vk::Move<vk::VkDescriptorPool> TexelBufferComputeInstance::createDescriptorPool (void) const
+{
+       return vk::DescriptorPoolBuilder()
+               .addType(vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
+               .addType(m_descriptorType, getInterfaceNumResources(m_shaderInterface))
+               .build(m_vki, m_device, vk::VK_DESCRIPTOR_POOL_USAGE_ONE_SHOT, 1);
+}
+
+vk::Move<vk::VkDescriptorSet> TexelBufferComputeInstance::createDescriptorSet (vk::VkDescriptorPool pool, vk::VkDescriptorSetLayout layout) const
+{
+       const vk::VkDescriptorInfo              resultInfo                      = createDescriptorInfo(m_result.getBufferView());
+       const vk::VkDescriptorInfo              texelBufferInfos[2]     =
+       {
+               createDescriptorInfo(m_texelBuffers.getBufferViewA()),
+               createDescriptorInfo(m_texelBuffers.getBufferViewB()),
+       };
+
+       vk::Move<vk::VkDescriptorSet>   descriptorSet           = allocDescriptorSet(m_vki, m_device, pool, vk::VK_DESCRIPTOR_SET_USAGE_ONE_SHOT, layout);
+       vk::DescriptorSetUpdateBuilder  builder;
+
+       // result
+       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(0u), vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultInfo);
+
+       // texel buffers
+       switch (m_shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), m_descriptorType, &texelBufferInfos[0]);
+                       break;
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), m_descriptorType, &texelBufferInfos[0]);
+                       builder.writeSingle(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(2u), m_descriptorType, &texelBufferInfos[1]);
+                       break;
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       builder.writeArray(*descriptorSet, vk::DescriptorSetUpdateBuilder::Location::binding(1u), m_descriptorType, 2u, texelBufferInfos);
+                       break;
+
+               default:
+                       DE_FATAL("Impossible");
+       }
+
+       builder.update(m_vki, m_device);
+       return descriptorSet;
+}
+
+tcu::TestStatus TexelBufferComputeInstance::iterate (void)
+{
+       logTestPlan();
+       return testResourceAccess();
+}
+
+void TexelBufferComputeInstance::logTestPlan (void) const
+{
+       std::ostringstream msg;
+
+       msg << "Fetching 4 values from image in compute shader.\n"
+               << "Single descriptor set. Descriptor set contains "
+                       << ((m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR) ? "single" :
+                           (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS) ? "two" :
+                           (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY) ? "an array (size 2) of" :
+                           (const char*)DE_NULL)
+               << " descriptor(s) of type " << vk::getDescriptorTypeName(m_descriptorType) << "\n"
+               << "Buffer view is created with a " << ((m_nonzeroViewOffset) ? ("non-zero") : ("zero")) << " offset.\n"
+               << "Buffer format is " << vk::getFormatName(mapToVkTextureFormat(m_texelBuffers.getTextureFormat())) << ".\n";
+
+       for (int resultNdx = 0; resultNdx < 4; ++resultNdx)
+       {
+               msg << "Test sample " << resultNdx << ": fetch at position " << m_texelBuffers.getFetchPos(resultNdx);
+
+               if (m_shaderInterface != SHADER_INPUT_SINGLE_DESCRIPTOR)
+               {
+                       const int srcResourceNdx = (resultNdx % 2); // ABAB source
+                       msg << " from texelBuffer " << srcResourceNdx;
+               }
+
+               msg << "\n";
+       }
+
+       m_context.getTestContext().getLog()
+               << tcu::TestLog::Message
+               << msg.str()
+               << tcu::TestLog::EndMessage;
+}
+
+tcu::TestStatus TexelBufferComputeInstance::testResourceAccess (void)
+{
+       const vk::Unique<vk::VkDescriptorSetLayout>             descriptorSetLayout     (createDescriptorSetLayout());
+       const vk::Unique<vk::VkDescriptorPool>                  descriptorPool          (createDescriptorPool());
+       const vk::Unique<vk::VkDescriptorSet>                   descriptorSet           (createDescriptorSet(*descriptorPool, *descriptorSetLayout));
+       const ComputePipeline                                                   pipeline                        (m_vki, m_device, m_context.getBinaryCollection(), 1, &descriptorSetLayout.get());
+
+       const vk::VkDescriptorSet                                               descriptorSets[]        = { *descriptorSet };
+       const int                                                                               numDescriptorSets       = DE_LENGTH_OF_ARRAY(descriptorSets);
+       const deUint32* const                                                   dynamicOffsets          = DE_NULL;
+       const int                                                                               numDynamicOffsets       = 0;
+       const void*     const                                                           preBarriers[]           = { m_texelBuffers.getBufferInitBarrierA(), m_texelBuffers.getBufferInitBarrierB() };
+       const int                                                                               numPreBarriers          = m_texelBuffers.getNumTexelBuffers();
+       const void* const                                                               postBarriers[]          = { m_result.getResultReadBarrier() };
+       const int                                                                               numPostBarriers         = DE_LENGTH_OF_ARRAY(postBarriers);
+
+       const ComputeCommand                                                    compute                         (m_vki,
+                                                                                                                                                m_device,
+                                                                                                                                                pipeline.getPipeline(),
+                                                                                                                                                pipeline.getPipelineLayout(),
+                                                                                                                                                tcu::UVec3(4, 1, 1),
+                                                                                                                                                numDescriptorSets,     descriptorSets,
+                                                                                                                                                numDynamicOffsets,     dynamicOffsets,
+                                                                                                                                                numPreBarriers,        preBarriers,
+                                                                                                                                                numPostBarriers,       postBarriers);
+
+       tcu::Vec4                                                                               results[4];
+       bool                                                                                    anyResultSet            = false;
+       bool                                                                                    allResultsOk            = true;
+
+       compute.submitAndWait(m_queueFamilyIndex, m_queue);
+       m_result.readResultContentsTo(&results);
+
+       // verify
+       for (int resultNdx = 0; resultNdx < 4; ++resultNdx)
+       {
+               const tcu::Vec4 result                          = results[resultNdx];
+               const tcu::Vec4 reference                       = m_texelBuffers.fetchTexelValue(resultNdx);
+               const tcu::Vec4 conversionThreshold     = tcu::Vec4(1.0f / 255.0f);
+
+               if (result != tcu::Vec4(-1.0f))
+                       anyResultSet = true;
+
+               if (tcu::boolAny(tcu::greaterThan(tcu::abs(result - reference), conversionThreshold)))
+               {
+                       allResultsOk = false;
+
+                       m_context.getTestContext().getLog()
+                               << tcu::TestLog::Message
+                               << "Test sample " << resultNdx << ": Expected " << reference << ", got " << result
+                               << tcu::TestLog::EndMessage;
+               }
+       }
+
+       // read back and verify
+       if (allResultsOk)
+               return tcu::TestStatus::pass("Pass");
+       else if (anyResultSet)
+               return tcu::TestStatus::fail("Invalid result values");
+       else
+       {
+               m_context.getTestContext().getLog()
+                       << tcu::TestLog::Message
+                       << "Result buffer was not written to."
+                       << tcu::TestLog::EndMessage;
+               return tcu::TestStatus::fail("Result buffer was not written to");
+       }
+}
+
+class TexelBufferDescriptorCase : public QuadrantRendederCase
+{
+public:
+       enum
+       {
+               FLAG_VIEW_OFFSET = (1u << 1u),
+       };
+       // enum continues where resource flags ends
+       DE_STATIC_ASSERT((deUint32)FLAG_VIEW_OFFSET == (deUint32)RESOURCE_FLAG_LAST);
+
+                                                               TexelBufferDescriptorCase       (tcu::TestContext&              testCtx,
+                                                                                                                        const char*                    name,
+                                                                                                                        const char*                    description,
+                                                                                                                        bool                                   isPrimaryCmdBuf,
+                                                                                                                        vk::VkDescriptorType   descriptorType,
+                                                                                                                        vk::VkShaderStageFlags exitingStages,
+                                                                                                                        vk::VkShaderStageFlags activeStages,
+                                                                                                                        ShaderInputInterface   shaderInterface,
+                                                                                                                        deUint32                               flags);
+
+private:
+       std::string                                     genExtensionDeclarations        (vk::VkShaderStage stage) const;
+       std::string                                     genResourceDeclarations         (vk::VkShaderStage stage, int numUsedBindings) const;
+       std::string                                     genResourceAccessSource         (vk::VkShaderStage stage) const;
+       std::string                                     genNoAccessSource                       (void) const;
+
+       vkt::TestInstance*                      createInstance                          (vkt::Context& context) const;
+
+       const bool                                      m_isPrimaryCmdBuf;
+       const vk::VkDescriptorType      m_descriptorType;
+       const ShaderInputInterface      m_shaderInterface;
+       const bool                                      m_nonzeroViewOffset;
+};
+
+TexelBufferDescriptorCase::TexelBufferDescriptorCase (tcu::TestContext&                        testCtx,
+                                                                                                         const char*                           name,
+                                                                                                         const char*                           description,
+                                                                                                         bool                                          isPrimaryCmdBuf,
+                                                                                                         vk::VkDescriptorType          descriptorType,
+                                                                                                         vk::VkShaderStageFlags        exitingStages,
+                                                                                                         vk::VkShaderStageFlags        activeStages,
+                                                                                                         ShaderInputInterface          shaderInterface,
+                                                                                                         deUint32                                      flags)
+       : QuadrantRendederCase  (testCtx, name, description, glu::GLSL_VERSION_310_ES, exitingStages, activeStages)
+       , m_isPrimaryCmdBuf             (isPrimaryCmdBuf)
+       , m_descriptorType              (descriptorType)
+       , m_shaderInterface             (shaderInterface)
+       , m_nonzeroViewOffset   (((flags & FLAG_VIEW_OFFSET) != 0) ? (1u) : (0u))
+{
+}
+
+std::string TexelBufferDescriptorCase::genExtensionDeclarations (vk::VkShaderStage stage) const
+{
+       DE_UNREF(stage);
+       return "#extension GL_EXT_texture_buffer : require\n";
+}
+
+std::string TexelBufferDescriptorCase::genResourceDeclarations (vk::VkShaderStage stage, int numUsedBindings) const
+{
+       DE_UNREF(stage);
+
+       const bool                      isUniform               = isUniformDescriptorType(m_descriptorType);
+       const char* const       storageType             = (isUniform) ? ("samplerBuffer ") : ("readonly imageBuffer ");
+       const char* const       formatQualifier = (isUniform) ? ("") : (", rgba8");
+
+       switch (m_shaderInterface)
+       {
+               case SHADER_INPUT_SINGLE_DESCRIPTOR:
+                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + formatQualifier + ") uniform highp " + storageType + " u_texelBuffer;\n";
+
+               case SHADER_INPUT_MULTIPLE_DESCRIPTORS:
+                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + formatQualifier + ") uniform highp " + storageType + " u_texelBufferA;\n"
+                                  "layout(set = 0, binding = " + de::toString(numUsedBindings+1) + formatQualifier + ") uniform highp " + storageType + " u_texelBufferB;\n";
+
+               case SHADER_INPUT_DESCRIPTOR_ARRAY:
+                       return "layout(set = 0, binding = " + de::toString(numUsedBindings) + formatQualifier + ") uniform highp " + storageType + " u_texelBuffer[2];\n";
+
+               default:
+                       DE_FATAL("Impossible");
+                       return "";
+       }
+}
+
+std::string TexelBufferDescriptorCase::genResourceAccessSource (vk::VkShaderStage stage) const
+{
+       DE_UNREF(stage);
+
+       const char* const       accessPostfixA  = (m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR)         ? ("")
+                                                                               : (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS)      ? ("A")
+                                                                               : (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY)          ? ("[0]")
+                                                                               : (DE_NULL);
+       const char* const       accessPostfixB  = (m_shaderInterface == SHADER_INPUT_SINGLE_DESCRIPTOR)         ? ("")
+                                                                               : (m_shaderInterface == SHADER_INPUT_MULTIPLE_DESCRIPTORS)      ? ("B")
+                                                                               : (m_shaderInterface == SHADER_INPUT_DESCRIPTOR_ARRAY)          ? ("[1]")
+                                                                               : (DE_NULL);
+       const char* const       fetchFunc               = (isUniformDescriptorType(m_descriptorType)) ? ("texelFetch") : ("imageLoad");
+       std::ostringstream      buf;
+
+       buf << "        if (quadrant_id == 0)\n"
+               << "            result_color = " << fetchFunc << "(u_texelBuffer" << accessPostfixA << ", " << TexelBufferInstanceBuffers::getFetchPos(0) << ");\n"
+               << "    else if (quadrant_id == 1)\n"
+               << "            result_color = " << fetchFunc << "(u_texelBuffer" << accessPostfixB << ", " << TexelBufferInstanceBuffers::getFetchPos(1) << ");\n"
+               << "    else if (quadrant_id == 2)\n"
+               << "            result_color = " << fetchFunc << "(u_texelBuffer" << accessPostfixA << ", " << TexelBufferInstanceBuffers::getFetchPos(2) << ");\n"
+               << "    else\n"
+               << "            result_color = " << fetchFunc << "(u_texelBuffer" << accessPostfixB << ", " << TexelBufferInstanceBuffers::getFetchPos(3) << ");\n";
+
+       return buf.str();
+}
+
+std::string TexelBufferDescriptorCase::genNoAccessSource (void) const
+{
+       return "        if (quadrant_id == 1 || quadrant_id == 2)\n"
+                       "               result_color = vec4(0.0, 1.0, 0.0, 1.0);\n"
+                       "       else\n"
+                       "               result_color = vec4(1.0, 1.0, 0.0, 1.0);\n";
+}
+
+vkt::TestInstance* TexelBufferDescriptorCase::createInstance (vkt::Context& context) const
+{
+       if (m_exitingStages == vk::VK_SHADER_STAGE_COMPUTE_BIT)
+       {
+               DE_ASSERT(m_isPrimaryCmdBuf); // secondaries are only valid within renderpass
+               return new TexelBufferComputeInstance(context, m_descriptorType, m_shaderInterface, m_nonzeroViewOffset);
+       }
+       else
+               return new TexelBufferRenderInstance(context, m_isPrimaryCmdBuf, m_descriptorType, m_activeStages, m_shaderInterface, m_nonzeroViewOffset);
+}
+
+void createShaderAccessImageTests (tcu::TestCaseGroup*         group,
+                                                                  bool                                         isPrimaryCmdBuf,
+                                                                  vk::VkDescriptorType         descriptorType,
+                                                                  vk::VkShaderStageFlags       exitingStages,
+                                                                  vk::VkShaderStageFlags       activeStages,
+                                                                  ShaderInputInterface         dimension,
+                                                                  deUint32                                     resourceFlags)
+{
+       static const struct
+       {
+               vk::VkImageViewType     viewType;
+               const char*                     name;
+               const char*                     description;
+               deUint32                        flags;
+       } s_imageTypes[] =
+       {
+               { vk::VK_IMAGE_VIEW_TYPE_1D,                    "1d",                                           "1D image view",                                                                0u                                                                              },
+               { vk::VK_IMAGE_VIEW_TYPE_1D,                    "1d_base_mip",                          "1D image subview with base mip level",                 ImageDescriptorCase::FLAG_BASE_MIP              },
+               { vk::VK_IMAGE_VIEW_TYPE_1D,                    "1d_base_slice",                        "1D image subview with base array slice",               ImageDescriptorCase::FLAG_BASE_SLICE    },
+
+               { vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY,              "1d_array",                                     "1D array image view",                                                  0u                                                                              },
+               { vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY,              "1d_array_base_mip",            "1D array image subview with base mip level",   ImageDescriptorCase::FLAG_BASE_MIP              },
+               { vk::VK_IMAGE_VIEW_TYPE_1D_ARRAY,              "1d_array_base_slice",          "1D array image subview with base array slice", ImageDescriptorCase::FLAG_BASE_SLICE    },
+
+               { vk::VK_IMAGE_VIEW_TYPE_2D,                    "2d",                                           "2D image view",                                                                0u                                                                              },
+               { vk::VK_IMAGE_VIEW_TYPE_2D,                    "2d_base_mip",                          "2D image subview with base mip level",                 ImageDescriptorCase::FLAG_BASE_MIP              },
+               { vk::VK_IMAGE_VIEW_TYPE_2D,                    "2d_base_slice",                        "2D image subview with base array slice",               ImageDescriptorCase::FLAG_BASE_SLICE    },
+
+               { vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY,              "2d_array",                                     "2D array image view",                                                  0u                                                                              },
+               { vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY,              "2d_array_base_mip",            "2D array image subview with base mip level",   ImageDescriptorCase::FLAG_BASE_MIP              },
+               { vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY,              "2d_array_base_slice",          "2D array image subview with base array slice", ImageDescriptorCase::FLAG_BASE_SLICE    },
+
+               { vk::VK_IMAGE_VIEW_TYPE_3D,                    "3d",                                           "3D image view",                                                                0u                                                                              },
+               { vk::VK_IMAGE_VIEW_TYPE_3D,                    "3d_base_mip",                          "3D image subview with base mip level",                 ImageDescriptorCase::FLAG_BASE_MIP              },
+               // no 3d array textures
+
+               { vk::VK_IMAGE_VIEW_TYPE_CUBE,                  "cube",                                         "Cube image view",                                                              0u                                                                              },
+               { vk::VK_IMAGE_VIEW_TYPE_CUBE,                  "cube_base_mip",                        "Cube image subview with base mip level",               ImageDescriptorCase::FLAG_BASE_MIP              },
+               { vk::VK_IMAGE_VIEW_TYPE_CUBE,                  "cube_base_slice",                      "Cube image subview with base array slice",             ImageDescriptorCase::FLAG_BASE_SLICE    },
+
+               { vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY,    "cube_array",                           "Cube image view",                                                              0u                                                                              },
+               { vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY,    "cube_array_base_mip",          "Cube image subview with base mip level",               ImageDescriptorCase::FLAG_BASE_MIP              },
+               { vk::VK_IMAGE_VIEW_TYPE_CUBE_ARRAY,    "cube_array_base_slice",        "Cube image subview with base array slice",             ImageDescriptorCase::FLAG_BASE_SLICE    },
+       };
+
+       for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_imageTypes); ++ndx)
+       {
+               // never overlap
+               DE_ASSERT((s_imageTypes[ndx].flags & resourceFlags) == 0u);
+
+               group->addChild(new ImageDescriptorCase(group->getTestContext(),
+                                                                                               s_imageTypes[ndx].name,
+                                                                                               s_imageTypes[ndx].description,
+                                                                                               isPrimaryCmdBuf,
+                                                                                               descriptorType,
+                                                                                               exitingStages,
+                                                                                               activeStages,
+                                                                                               dimension,
+                                                                                               s_imageTypes[ndx].viewType,
+                                                                                               s_imageTypes[ndx].flags | resourceFlags));
+       }
+}
+
+void createShaderAccessTexelBufferTests (tcu::TestCaseGroup*   group,
+                                                                                bool                                   isPrimaryCmdBuf,
+                                                                                vk::VkDescriptorType   descriptorType,
+                                                                                vk::VkShaderStageFlags exitingStages,
+                                                                                vk::VkShaderStageFlags activeStages,
+                                                                                ShaderInputInterface   dimension,
+                                                                                deUint32                               resourceFlags)
+{
+       DE_ASSERT(resourceFlags == 0);
+       DE_UNREF(resourceFlags);
+
+       static const struct
+       {
+               const char*     name;
+               const char*     description;
+               deUint32        flags;
+       } s_texelBufferTypes[] =
+       {
+               { "offset_zero",                "View offset is zero",          0u                                                                                      },
+               { "offset_nonzero",             "View offset is non-zero",      TexelBufferDescriptorCase::FLAG_VIEW_OFFSET     },
+       };
+
+       for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_texelBufferTypes); ++ndx)
+       {
+               group->addChild(new TexelBufferDescriptorCase(group->getTestContext(),
+                                                                                                         s_texelBufferTypes[ndx].name,
+                                                                                                         s_texelBufferTypes[ndx].description,
+                                                                                                         isPrimaryCmdBuf,
+                                                                                                         descriptorType,
+                                                                                                         exitingStages,
+                                                                                                         activeStages,
+                                                                                                         dimension,
+                                                                                                         s_texelBufferTypes[ndx].flags));
+       }
+}
+
+void createShaderAccessBufferTests (tcu::TestCaseGroup*                group,
+                                                                       bool                                    isPrimaryCmdBuf,
+                                                                       vk::VkDescriptorType    descriptorType,
+                                                                       vk::VkShaderStageFlags  exitingStages,
+                                                                       vk::VkShaderStageFlags  activeStages,
+                                                                       ShaderInputInterface    dimension,
+                                                                       deUint32                                resourceFlags)
+{
+       DE_ASSERT(resourceFlags == 0u);
+       DE_UNREF(resourceFlags);
+
+       static const struct
+       {
+               const char*     name;
+               const char*     description;
+               bool            isForDynamicCases;
+               deUint32        flags;
+       } s_bufferTypes[] =
+       {
+               { "offset_view_zero",                                           "View offset is zero",                                                                          false,  0u                                                                                                                                                                                      },
+               { "offset_view_nonzero",                                        "View offset is non-zero",                                                                      false,  BufferDescriptorCase::FLAG_VIEW_OFFSET                                                                                                          },
+
+               { "offset_view_zero_dynamic_zero",                      "View offset is zero, dynamic offset is zero",                          true,   BufferDescriptorCase::FLAG_DYNAMIC_OFFSET_ZERO                                                                                          },
+               { "offset_view_zero_dynamic_nonzero",           "View offset is zero, dynamic offset is non-zero",                      true,   BufferDescriptorCase::FLAG_DYNAMIC_OFFSET_NONZERO                                                                                       },
+               { "offset_view_zero_dynamic_not_set",           "View offset is zero, dynamic offset is not supplied",          true,   0u                                                                                                                                                                                      },
+               // \note no case for offset_view_nonzero_dynamic_zero since it doesn't produce any additional coverage
+               { "offset_view_nonzero_dynamic_nonzero",        "View offset is non-zero, dynamic offset is non-zero",          true,   BufferDescriptorCase::FLAG_VIEW_OFFSET | BufferDescriptorCase::FLAG_DYNAMIC_OFFSET_NONZERO      },
+               { "offset_view_nonzero_dynamic_not_set",        "View offset is non-zero, dynamic offset is not supplied",      true,   BufferDescriptorCase::FLAG_VIEW_OFFSET                                                                                                          },
+       };
+
+       const bool isDynamicCase = isDynamicDescriptorType(descriptorType);
+
+       for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_bufferTypes); ++ndx)
+       {
+               if (isDynamicCase == s_bufferTypes[ndx].isForDynamicCases)
+                       group->addChild(new BufferDescriptorCase(group->getTestContext(),
+                                                                                                        s_bufferTypes[ndx].name,
+                                                                                                        s_bufferTypes[ndx].description,
+                                                                                                        isPrimaryCmdBuf,
+                                                                                                        descriptorType,
+                                                                                                        exitingStages,
+                                                                                                        activeStages,
+                                                                                                        dimension,
+                                                                                                        s_bufferTypes[ndx].flags));
+       }
+}
+
+} // anonymous
+
+tcu::TestCaseGroup* createShaderAccessTests (tcu::TestContext& testCtx)
+{
+       static const struct
+       {
+               const bool      isPrimary;
+               const char*     name;
+               const char*     description;
+       } s_bindTypes[] =
+       {
+               { true,         "primary_cmd_buf",      "Bind in primary command buffer"        },
+               { false,        "seconday_cmd_buf",     "Bind in secondary command buffer"      },
+       };
+       static const struct
+       {
+               const vk::VkDescriptorType      descriptorType;
+               const char*                                     name;
+               const char*                                     description;
+               deUint32                                        flags;
+       } s_descriptorTypes[] =
+       {
+               { vk::VK_DESCRIPTOR_TYPE_SAMPLER,                                       "sampler_mutable",                                      "VK_DESCRIPTOR_TYPE_SAMPLER with mutable sampler",                                      0u                                                              },
+               { vk::VK_DESCRIPTOR_TYPE_SAMPLER,                                       "sampler_immutable",                            "VK_DESCRIPTOR_TYPE_SAMPLER with immutable sampler",                            RESOURCE_FLAG_IMMUTABLE_SAMPLER },
+               { vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,        "combined_image_sampler_mutable",       "VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER with mutable sampler",       0u                                                              },
+               { vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,        "combined_image_sampler_immutable",     "VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER with immutable sampler",     RESOURCE_FLAG_IMMUTABLE_SAMPLER },
+               { vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE,                         "sampled_image",                                        "VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE",                                                                     0u                                                              },
+               { vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,                         "storage_image",                                        "VK_DESCRIPTOR_TYPE_STORAGE_IMAGE",                                                                     0u                                                              },
+               { vk::VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,          "uniform_texel_buffer",                         "VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER",                                                      0u                                                              },
+               { vk::VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER,          "storage_texel_buffer",                         "VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER",                                                      0u                                                              },
+               { vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,                        "uniform_buffer",                                       "VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER",                                                            0u                                                              },
+               { vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,                        "storage_buffer",                                       "VK_DESCRIPTOR_TYPE_STORAGE_BUFFER",                                                            0u                                                              },
+               { vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,        "uniform_buffer_dynamic",                       "VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC",                                            0u                                                              },
+               { vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC,        "storage_buffer_dynamic",                       "VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC",                                            0u                                                              },
+       };
+       static const struct
+       {
+               const char*                             name;
+               const char*                             description;
+               vk::VkShaderStageFlags  existingStages;                         //!< stages that exists
+               vk::VkShaderStageFlags  activeStages;                           //!< stages that access resource
+               bool                                    supportsSecondaryCmdBufs;
+       } s_shaderStages[] =
+       {
+               {
+                       "no_access",
+                       "No accessing stages",
+                       vk::VK_SHADER_STAGE_VERTEX_BIT | vk::VK_SHADER_STAGE_FRAGMENT_BIT,
+                       0u,
+                       true,
+               },
+               {
+                       "vertex",
+                       "Vertex stage",
+                       vk::VK_SHADER_STAGE_VERTEX_BIT | vk::VK_SHADER_STAGE_FRAGMENT_BIT,
+                       vk::VK_SHADER_STAGE_VERTEX_BIT,
+                       true,
+               },
+               {
+                       "tess_ctrl",
+                       "Tessellation control stage",
+                       vk::VK_SHADER_STAGE_VERTEX_BIT | vk::VK_SHADER_STAGE_TESS_CONTROL_BIT | vk::VK_SHADER_STAGE_TESS_EVALUATION_BIT | vk::VK_SHADER_STAGE_FRAGMENT_BIT,
+                       vk::VK_SHADER_STAGE_TESS_CONTROL_BIT,
+                       true,
+               },
+               {
+                       "tess_eval",
+                       "Tessellation evaluation stage",
+                       vk::VK_SHADER_STAGE_VERTEX_BIT | vk::VK_SHADER_STAGE_TESS_CONTROL_BIT | vk::VK_SHADER_STAGE_TESS_EVALUATION_BIT | vk::VK_SHADER_STAGE_FRAGMENT_BIT,
+                       vk::VK_SHADER_STAGE_TESS_EVALUATION_BIT,
+                       true,
+               },
+               {
+                       "geometry",
+                       "Geometry stage",
+                       vk::VK_SHADER_STAGE_VERTEX_BIT | vk::VK_SHADER_STAGE_GEOMETRY_BIT | vk::VK_SHADER_STAGE_FRAGMENT_BIT,
+                       vk::VK_SHADER_STAGE_GEOMETRY_BIT,
+                       true,
+               },
+               {
+                       "fragment",
+                       "Fragment stage",
+                       vk::VK_SHADER_STAGE_VERTEX_BIT | vk::VK_SHADER_STAGE_FRAGMENT_BIT,
+                       vk::VK_SHADER_STAGE_FRAGMENT_BIT,
+                       true,
+               },
+               {
+                       "compute",
+                       "Compute stage",
+                       vk::VK_SHADER_STAGE_COMPUTE_BIT,
+                       vk::VK_SHADER_STAGE_COMPUTE_BIT,
+                       false,
+               },
+               {
+                       "vertex_fragment",
+                       "Vertex and fragment stages",
+                       vk::VK_SHADER_STAGE_VERTEX_BIT | vk::VK_SHADER_STAGE_FRAGMENT_BIT,
+                       vk::VK_SHADER_STAGE_VERTEX_BIT | vk::VK_SHADER_STAGE_FRAGMENT_BIT,
+                       true,
+               },
+       };
+       static const struct
+       {
+               ShaderInputInterface    dimension;
+               const char*                             name;
+               const char*                             description;
+       } s_variableDimensions[] =
+       {
+               { SHADER_INPUT_SINGLE_DESCRIPTOR,               "single_descriptor",    "Single descriptor"             },
+               { SHADER_INPUT_MULTIPLE_DESCRIPTORS,    "multiple_descriptors", "Multiple descriptors"  },
+               { SHADER_INPUT_DESCRIPTOR_ARRAY,                "descriptor_array",             "Descriptor array"              },
+       };
+
+       de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "shader_access", "Access resource via descriptor in a single descriptor set"));
+
+       // .primary_cmd_buf...
+       for (int bindTypeNdx = 0; bindTypeNdx < DE_LENGTH_OF_ARRAY(s_bindTypes); ++bindTypeNdx)
+       {
+               de::MovePtr<tcu::TestCaseGroup> bindGroup(new tcu::TestCaseGroup(testCtx, s_bindTypes[bindTypeNdx].name, s_bindTypes[bindTypeNdx].description));
+
+               // .sampler, .combined_image_sampler, other resource types ...
+               for (int descriptorNdx = 0; descriptorNdx < DE_LENGTH_OF_ARRAY(s_descriptorTypes); ++descriptorNdx)
+               {
+                       de::MovePtr<tcu::TestCaseGroup> typeGroup(new tcu::TestCaseGroup(testCtx, s_descriptorTypes[descriptorNdx].name, s_descriptorTypes[descriptorNdx].description));
+
+                       for (int stageNdx = 0; stageNdx < DE_LENGTH_OF_ARRAY(s_shaderStages); ++stageNdx)
+                       {
+                               if (s_bindTypes[bindTypeNdx].isPrimary || s_shaderStages[stageNdx].supportsSecondaryCmdBufs)
+                               {
+                                       de::MovePtr<tcu::TestCaseGroup> stageGroup(new tcu::TestCaseGroup(testCtx, s_shaderStages[stageNdx].name, s_shaderStages[stageNdx].description));
+
+                                       for (int dimensionNdx = 0; dimensionNdx < DE_LENGTH_OF_ARRAY(s_variableDimensions); ++dimensionNdx)
+                                       {
+                                               de::MovePtr<tcu::TestCaseGroup> dimensionGroup(new tcu::TestCaseGroup(testCtx, s_variableDimensions[dimensionNdx].name, s_variableDimensions[dimensionNdx].description));
+                                               void                                                    (*createTestsFunc)(tcu::TestCaseGroup*          group,
+                                                                                                                                                  bool                                         isPrimaryCmdBuf,
+                                                                                                                                                  vk::VkDescriptorType         descriptorType,
+                                                                                                                                                  vk::VkShaderStageFlags       existingStages,
+                                                                                                                                                  vk::VkShaderStageFlags       activeStages,
+                                                                                                                                                  ShaderInputInterface         dimension,
+                                                                                                                                                  deUint32                                     resourceFlags);
+
+                                               switch (s_descriptorTypes[descriptorNdx].descriptorType)
+                                               {
+                                                       case vk::VK_DESCRIPTOR_TYPE_SAMPLER:
+                                                       case vk::VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+                                                       case vk::VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+                                                       case vk::VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+                                                               createTestsFunc = createShaderAccessImageTests;
+                                                               break;
+
+                                                       case vk::VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
+                                                       case vk::VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
+                                                               createTestsFunc = createShaderAccessTexelBufferTests;
+                                                               break;
+
+                                                       case vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+                                                       case vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
+                                                       case vk::VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
+                                                       case vk::VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
+                                                               createTestsFunc = createShaderAccessBufferTests;
+                                                               break;
+
+                                                       default:
+                                                               createTestsFunc = DE_NULL;
+                                                               DE_FATAL("Impossible");
+                                               }
+
+                                               if (createTestsFunc)
+                                               {
+                                                       createTestsFunc(dimensionGroup.get(),
+                                                                                       s_bindTypes[bindTypeNdx].isPrimary,
+                                                                                       s_descriptorTypes[descriptorNdx].descriptorType,
+                                                                                       s_shaderStages[stageNdx].existingStages,
+                                                                                       s_shaderStages[stageNdx].activeStages,
+                                                                                       s_variableDimensions[dimensionNdx].dimension,
+                                                                                       s_descriptorTypes[descriptorNdx].flags);
+                                               }
+                                               else
+                                                       DE_FATAL("Impossible");
+
+                                               stageGroup->addChild(dimensionGroup.release());
+                                       }
+
+                                       typeGroup->addChild(stageGroup.release());
+                               }
+                       }
+
+                       bindGroup->addChild(typeGroup.release());
+               }
+
+               group->addChild(bindGroup.release());
+       }
+
+       return group.release();
+}
+
+} // BindingModel
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/binding_model/vktBindingShaderAccessTests.hpp b/external/vulkancts/modules/vulkan/binding_model/vktBindingShaderAccessTests.hpp
new file mode 100644 (file)
index 0000000..7ff1b1a
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef _VKTBINDINGSHADERACCESSTESTS_HPP
+#define _VKTBINDINGSHADERACCESSTESTS_HPP
+/*-------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2015 Google Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and/or associated documentation files (the
+ * "Materials"), to deal in the Materials without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Materials, and to
+ * permit persons to whom the Materials are furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice(s) and this permission notice shall be
+ * included in all copies or substantial portions of the Materials.
+ *
+ * The Materials are Confidential Information as defined by the
+ * Khronos Membership Agreement until designated non-confidential by
+ * Khronos, at which point this condition clause shall be removed.
+ *
+ * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+ *
+ *//*!
+ * \file
+ * \brief Binding shader access tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "tcuTestCase.hpp"
+
+namespace vkt
+{
+namespace BindingModel
+{
+
+tcu::TestCaseGroup* createShaderAccessTests (tcu::TestContext& testCtx);
+
+} // BindingModel
+} // vkt
+
+#endif // _VKTBINDINGSHADERACCESSTESTS_HPP
index 5f2288c..e46490d 100644 (file)
@@ -49,6 +49,7 @@
 #include "vktInfo.hpp"
 #include "vktApiTests.hpp"
 #include "vktPipelineTests.hpp"
+#include "vktBindingModelTests.hpp"
 
 #include <vector>
 #include <sstream>
@@ -237,9 +238,10 @@ tcu::TestCaseExecutor* TestPackage::createExecutor (void) const
 
 void TestPackage::init (void)
 {
-       addChild(createInfoTests                (m_testCtx));
-       addChild(api::createTests               (m_testCtx));
-       addChild(pipeline::createTests  (m_testCtx));
+       addChild(createInfoTests                        (m_testCtx));
+       addChild(api::createTests                       (m_testCtx));
+       addChild(pipeline::createTests          (m_testCtx));
+       addChild(BindingModel::createTests      (m_testCtx));
 }
 
 } // vkt