Cherry-pick SPIR-V assembly test improvements

These SPIR-V assembly test framework improvements are needed by new SPIR-V
tests.

Contains following commits, sans new tests:

e47e81cda3ab7215d6089f4f5f631fa4f4385e10
ca8cfd0d24bc210c1cfeb3ded0c2c0d2492aa66b
56bd1576eb438e10e8ccb22e17b08354fbf3bac7
56f121e90ff24a88b375db1a74c296d17dd97a74
05e91483c6f2f48b408ed1a638cbb32468ed8dfa
fc1e2f0942477676e92f7fe632ef441a90f570f8
318e6d042b1aaa15327342b39967f0ae97d23347
c35c2901d9d604631326eec11b80b3e6b7b7d55f
0d10dbfa6e176e18f69edcace62f921bb5225168
251a30e3cf94411b7453dd4e05cf24e1d45f579e
4fe79607bfac070a32d9554e3705379073e3672b
8977777244ce058cb79e1cf3b43e528322e8308c
6a51980397447d1f726e71d9126e47f84dc80a6f

Change-Id: Ibedf226d3160365651155327dac50e8e9987a6d4
diff --git a/Android.mk b/Android.mk
index 51a82b4..d032bb7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -855,8 +855,10 @@
 	external/vulkancts/modules/vulkan/sparse_resources/vktSparseResourcesTestsUtil.cpp \
 	external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderCase.cpp \
 	external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.cpp \
+	external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.cpp \
 	external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmInstructionTests.cpp \
 	external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmTests.cpp \
+	external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmUtils.cpp \
 	external/vulkancts/modules/vulkan/ssbo/vktSSBOLayoutCase.cpp \
 	external/vulkancts/modules/vulkan/ssbo/vktSSBOLayoutTests.cpp \
 	external/vulkancts/modules/vulkan/synchronization/vktSynchronizationBasicEventTests.cpp \
diff --git a/external/vulkancts/framework/vulkan/vkBasicTypes.inl b/external/vulkancts/framework/vulkan/vkBasicTypes.inl
index ca9cf41..ea03621 100644
--- a/external/vulkancts/framework/vulkan/vkBasicTypes.inl
+++ b/external/vulkancts/framework/vulkan/vkBasicTypes.inl
@@ -157,6 +157,7 @@
 	VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR				= 1000059007,
 	VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR	= 1000059008,
 	VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR	= 1000080000,
+	VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR		= 1000083000,
 	VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR								= 1000084000,
 	VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR		= 1000085000,
 	VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE							= 1000092000,
diff --git a/external/vulkancts/framework/vulkan/vkStrUtil.inl b/external/vulkancts/framework/vulkan/vkStrUtil.inl
index cb8ccda..f65183f 100644
--- a/external/vulkancts/framework/vulkan/vkStrUtil.inl
+++ b/external/vulkancts/framework/vulkan/vkStrUtil.inl
@@ -336,6 +336,7 @@
 std::ostream&	operator<<	(std::ostream& s, const VkSparseImageFormatProperties2KHR& value);
 std::ostream&	operator<<	(std::ostream& s, const VkPhysicalDeviceSparseImageFormatInfo2KHR& value);
 std::ostream&	operator<<	(std::ostream& s, const VkPhysicalDevicePushDescriptorPropertiesKHR& value);
+std::ostream&	operator<<	(std::ostream& s, const VkPhysicalDevice16BitStorageFeaturesKHR& value);
 std::ostream&	operator<<	(std::ostream& s, const VkRectLayerKHR& value);
 std::ostream&	operator<<	(std::ostream& s, const VkPresentRegionKHR& value);
 std::ostream&	operator<<	(std::ostream& s, const VkPresentRegionsKHR& value);
diff --git a/external/vulkancts/framework/vulkan/vkStrUtilImpl.inl b/external/vulkancts/framework/vulkan/vkStrUtilImpl.inl
index e919afc..cfd9ed2 100644
--- a/external/vulkancts/framework/vulkan/vkStrUtilImpl.inl
+++ b/external/vulkancts/framework/vulkan/vkStrUtilImpl.inl
@@ -170,6 +170,7 @@
 		case VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR:			return "VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR";
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR:	return "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR";
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR:	return "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR";
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR:		return "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR";
 		case VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR:								return "VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR";
 		case VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR:		return "VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR";
 		case VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE:						return "VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE";
@@ -3415,6 +3416,19 @@
 	return s;
 }
 
+std::ostream& operator<< (std::ostream& s, const VkPhysicalDevice16BitStorageFeaturesKHR& value)
+{
+	s << "VkPhysicalDevice16BitStorageFeaturesKHR = {\n";
+	s << "\tsType = " << value.sType << '\n';
+	s << "\tpNext = " << value.pNext << '\n';
+	s << "\tstorageUniformBufferBlock16 = " << value.storageUniformBufferBlock16 << '\n';
+	s << "\tstorageUniform16 = " << value.storageUniform16 << '\n';
+	s << "\tstoragePushConstant16 = " << value.storagePushConstant16 << '\n';
+	s << "\tstorageInputOutput16 = " << value.storageInputOutput16 << '\n';
+	s << '}';
+	return s;
+}
+
 std::ostream& operator<< (std::ostream& s, const VkRectLayerKHR& value)
 {
 	s << "VkRectLayerKHR = {\n";
diff --git a/external/vulkancts/framework/vulkan/vkStructTypes.inl b/external/vulkancts/framework/vulkan/vkStructTypes.inl
index 6cb7cb2..f01dabc 100644
--- a/external/vulkancts/framework/vulkan/vkStructTypes.inl
+++ b/external/vulkancts/framework/vulkan/vkStructTypes.inl
@@ -1441,6 +1441,16 @@
 	deUint32		maxPushDescriptors;
 };
 
+struct VkPhysicalDevice16BitStorageFeaturesKHR
+{
+	VkStructureType	sType;
+	const void*		pNext;
+	VkBool32		storageUniformBufferBlock16;
+	VkBool32		storageUniform16;
+	VkBool32		storagePushConstant16;
+	VkBool32		storageInputOutput16;
+};
+
 struct VkRectLayerKHR
 {
 	VkOffset2D	offset;
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/CMakeLists.txt b/external/vulkancts/modules/vulkan/spirv_assembly/CMakeLists.txt
index 22eff93..b390b3c 100644
--- a/external/vulkancts/modules/vulkan/spirv_assembly/CMakeLists.txt
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/CMakeLists.txt
@@ -7,10 +7,14 @@
 	vktSpvAsmComputeShaderCase.hpp
 	vktSpvAsmComputeShaderTestUtil.cpp
 	vktSpvAsmComputeShaderTestUtil.hpp
+	vktSpvAsmGraphicsShaderTestUtil.cpp
+	vktSpvAsmGraphicsShaderTestUtil.hpp
 	vktSpvAsmInstructionTests.cpp
 	vktSpvAsmInstructionTests.hpp
 	vktSpvAsmTests.cpp
 	vktSpvAsmTests.hpp
+	vktSpvAsmUtils.cpp
+	vktSpvAsmUtils.hpp
 	)
 
 set(DEQP_VK_SPIRV_ASSEMBLY_LIBS
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderCase.cpp b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderCase.cpp
index be9cb24..4b6e8e0 100644
--- a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderCase.cpp
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderCase.cpp
@@ -24,9 +24,11 @@
 #include "vktSpvAsmComputeShaderCase.hpp"
 
 #include "deSharedPtr.hpp"
+#include "deSTLUtil.hpp"
 
 #include "vkBuilderUtil.hpp"
 #include "vkMemUtil.hpp"
+#include "vkPlatform.hpp"
 #include "vkRefUtil.hpp"
 #include "vkQueryUtil.hpp"
 #include "vkTypeUtil.hpp"
@@ -49,15 +51,24 @@
  * The memory is created as host visible and passed back as a vk::Allocation
  * instance via outMemory.
  *//*--------------------------------------------------------------------*/
-Move<VkBuffer> createBufferAndBindMemory (const DeviceInterface& vkdi, const VkDevice& device, Allocator& allocator, size_t numBytes, AllocationMp* outMemory)
+Move<VkBuffer> createBufferAndBindMemory (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorType dtype, Allocator& allocator, size_t numBytes, AllocationMp* outMemory)
 {
-	const VkBufferCreateInfo bufferCreateInfo =
+	VkBufferUsageFlags			usageBit		= (VkBufferUsageFlags)0;
+
+	switch (dtype)
+	{
+		case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:	usageBit = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; break;
+		case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:	usageBit = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; break;
+		default:								DE_ASSERT(false);
+	}
+
+	const VkBufferCreateInfo bufferCreateInfo	=
 	{
 		VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,	// sType
 		DE_NULL,								// pNext
 		0u,										// flags
 		numBytes,								// size
-		VK_BUFFER_USAGE_STORAGE_BUFFER_BIT,		// usage
+		usageBit,								// usage
 		VK_SHARING_MODE_EXCLUSIVE,				// sharingMode
 		0u,										// queueFamilyCount
 		DE_NULL,								// pQueueFamilyIndices
@@ -90,17 +101,16 @@
 }
 
 /*--------------------------------------------------------------------*//*!
- * \brief Create a descriptor set layout with numBindings descriptors
+ * \brief Create a descriptor set layout with the given descriptor types
  *
- * All descriptors are created for shader storage buffer objects and
- * compute pipeline.
+ * All descriptors are created for compute pipeline.
  *//*--------------------------------------------------------------------*/
-Move<VkDescriptorSetLayout> createDescriptorSetLayout (const DeviceInterface& vkdi, const VkDevice& device, size_t numBindings)
+Move<VkDescriptorSetLayout> createDescriptorSetLayout (const DeviceInterface& vkdi, const VkDevice& device, const vector<VkDescriptorType>& dtypes)
 {
 	DescriptorSetLayoutBuilder builder;
 
-	for (size_t bindingNdx = 0; bindingNdx < numBindings; ++bindingNdx)
-		builder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_SHADER_STAGE_COMPUTE_BIT);
+	for (size_t bindingNdx = 0; bindingNdx < dtypes.size(); ++bindingNdx)
+		builder.addSingleBinding(dtypes[bindingNdx], VK_SHADER_STAGE_COMPUTE_BIT);
 
 	return builder.build(vkdi, device);
 }
@@ -108,9 +118,9 @@
 /*--------------------------------------------------------------------*//*!
  * \brief Create a pipeline layout with one descriptor set
  *//*--------------------------------------------------------------------*/
-Move<VkPipelineLayout> createPipelineLayout (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorSetLayout descriptorSetLayout)
+Move<VkPipelineLayout> createPipelineLayout (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorSetLayout descriptorSetLayout, const vkt::SpirVAssembly::BufferSp& pushConstants)
 {
-	const VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo =
+	VkPipelineLayoutCreateInfo		createInfo	=
 	{
 		VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,	// sType
 		DE_NULL,										// pNext
@@ -121,30 +131,47 @@
 		DE_NULL,										// pPushConstantRanges
 	};
 
-	return createPipelineLayout(vkdi, device, &pipelineLayoutCreateInfo);
+	VkPushConstantRange				range		=
+	{
+		VK_SHADER_STAGE_COMPUTE_BIT,					// stageFlags
+		0,												// offset
+		0,												// size
+	};
+
+	if (pushConstants != DE_NULL)
+	{
+		range.size							= static_cast<deUint32>(pushConstants->getNumBytes());
+		createInfo.pushConstantRangeCount	= 1;
+		createInfo.pPushConstantRanges		= &range;
+	}
+
+	return createPipelineLayout(vkdi, device, &createInfo);
 }
 
 /*--------------------------------------------------------------------*//*!
- * \brief Create a one-time descriptor pool for one descriptor set
- *
- * The pool supports numDescriptors storage buffer descriptors.
+ * \brief Create a one-time descriptor pool for one descriptor set that
+ * support the given descriptor types.
  *//*--------------------------------------------------------------------*/
-inline Move<VkDescriptorPool> createDescriptorPool (const DeviceInterface& vkdi, const VkDevice& device, deUint32 numDescriptors)
+inline Move<VkDescriptorPool> createDescriptorPool (const DeviceInterface& vkdi, const VkDevice& device, const vector<VkDescriptorType>& dtypes)
 {
-	return DescriptorPoolBuilder()
-		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, numDescriptors)
-		.build(vkdi, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, /* maxSets = */ 1);
+	DescriptorPoolBuilder builder;
+
+	for (size_t typeNdx = 0; typeNdx < dtypes.size(); ++typeNdx)
+		builder.addType(dtypes[typeNdx], 1);
+
+	return builder.build(vkdi, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, /* maxSets = */ 1);
 }
 
 /*--------------------------------------------------------------------*//*!
  * \brief Create a descriptor set
  *
- * The descriptor set's layout should contain numViews descriptors.
- * All the descriptors represent buffer views, and they are sequentially
- * binded to binding point starting from 0.
+ * The descriptor set's layout contains the given descriptor types,
+ * sequentially binded to binding points starting from 0.
  *//*--------------------------------------------------------------------*/
-Move<VkDescriptorSet> createDescriptorSet (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorPool pool, VkDescriptorSetLayout layout, size_t numViews, const vector<VkDescriptorBufferInfo>& descriptorInfos)
+Move<VkDescriptorSet> createDescriptorSet (const DeviceInterface& vkdi, const VkDevice& device, VkDescriptorPool pool, VkDescriptorSetLayout layout, const vector<VkDescriptorType>& dtypes, const vector<VkDescriptorBufferInfo>& descriptorInfos)
 {
+	DE_ASSERT(dtypes.size() == descriptorInfos.size());
+
 	const VkDescriptorSetAllocateInfo	allocInfo	=
 	{
 		VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
@@ -157,8 +184,8 @@
 	Move<VkDescriptorSet>				descriptorSet	= allocateDescriptorSet(vkdi, device, &allocInfo);
 	DescriptorSetUpdateBuilder			builder;
 
-	for (deUint32 descriptorNdx = 0; descriptorNdx < numViews; ++descriptorNdx)
-		builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(descriptorNdx), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &descriptorInfos[descriptorNdx]);
+	for (deUint32 descriptorNdx = 0; descriptorNdx < dtypes.size(); ++descriptorNdx)
+		builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(descriptorNdx), dtypes[descriptorNdx], &descriptorInfos[descriptorNdx]);
 	builder.update(vkdi, device);
 
 	return descriptorSet;
@@ -246,18 +273,24 @@
 class SpvAsmComputeShaderInstance : public TestInstance
 {
 public:
-								SpvAsmComputeShaderInstance	(Context& ctx, const ComputeShaderSpec& spec);
-	tcu::TestStatus				iterate						(void);
+										SpvAsmComputeShaderInstance	(Context& ctx, const ComputeShaderSpec& spec, const ComputeTestFeatures features);
+	tcu::TestStatus						iterate						(void);
 
 private:
-	const ComputeShaderSpec&	m_shaderSpec;
+	const Unique<VkDevice>				m_device;
+	const DeviceDriver					m_deviceInterface;
+	const VkQueue						m_queue;
+	const de::UniquePtr<vk::Allocator>	m_allocator;
+	const ComputeShaderSpec&			m_shaderSpec;
+	const ComputeTestFeatures			m_features;
 };
 
 // ComputeShaderTestCase implementations
 
-SpvAsmComputeShaderCase::SpvAsmComputeShaderCase (tcu::TestContext& testCtx, const char* name, const char* description, const ComputeShaderSpec& spec)
+SpvAsmComputeShaderCase::SpvAsmComputeShaderCase (tcu::TestContext& testCtx, const char* name, const char* description, const ComputeShaderSpec& spec, const ComputeTestFeatures features)
 	: TestCase		(testCtx, name, description)
 	, m_shaderSpec	(spec)
+	, m_features	(features)
 {
 }
 
@@ -268,40 +301,72 @@
 
 TestInstance* SpvAsmComputeShaderCase::createInstance (Context& ctx) const
 {
-	return new SpvAsmComputeShaderInstance(ctx, m_shaderSpec);
+	return new SpvAsmComputeShaderInstance(ctx, m_shaderSpec, m_features);
 }
 
 // ComputeShaderTestInstance implementations
 
-SpvAsmComputeShaderInstance::SpvAsmComputeShaderInstance (Context& ctx, const ComputeShaderSpec& spec)
-	: TestInstance	(ctx)
-	, m_shaderSpec	(spec)
+SpvAsmComputeShaderInstance::SpvAsmComputeShaderInstance (Context& ctx, const ComputeShaderSpec& spec, const ComputeTestFeatures features)
+	: TestInstance		(ctx)
+	, m_device			(createDeviceWithExtensions(ctx.getInstanceInterface(), ctx.getPhysicalDevice(), ctx.getUniversalQueueFamilyIndex(), ctx.getDeviceExtensions(), spec.extensions))
+	, m_deviceInterface	(ctx.getInstanceInterface(), *m_device)
+	, m_queue			(getDeviceQueue(m_deviceInterface, *m_device, ctx.getUniversalQueueFamilyIndex(), 0))
+	, m_allocator		(createAllocator(ctx.getInstanceInterface(), ctx.getPhysicalDevice(), m_deviceInterface, *m_device))
+	, m_shaderSpec		(spec)
+	, m_features		(features)
 {
 }
 
 tcu::TestStatus SpvAsmComputeShaderInstance::iterate (void)
 {
-	const DeviceInterface&				vkdi				= m_context.getDeviceInterface();
-	const VkDevice&						device				= m_context.getDevice();
-	Allocator&							allocator			= m_context.getDefaultAllocator();
+	const VkPhysicalDeviceFeatures&		features			= m_context.getDeviceFeatures();
+	const DeviceInterface&				vkdi				= m_deviceInterface;
+	const VkDevice&						device				= *m_device;
+	Allocator&							allocator			= *m_allocator;
+
+	if ((m_features == COMPUTE_TEST_USES_INT16 || m_features == COMPUTE_TEST_USES_INT16_INT64) && !features.shaderInt16)
+	{
+		TCU_THROW(NotSupportedError, "shaderInt16 feature is not supported");
+	}
+
+	if ((m_features == COMPUTE_TEST_USES_INT64 || m_features == COMPUTE_TEST_USES_INT16_INT64) && !features.shaderInt64)
+	{
+		TCU_THROW(NotSupportedError, "shaderInt64 feature is not supported");
+	}
+
+	{
+		const InstanceInterface&			vki					= m_context.getInstanceInterface();
+		const VkPhysicalDevice				physicalDevice		= m_context.getPhysicalDevice();
+
+		// 16bit storage features
+		{
+			if (!is16BitStorageFeaturesSupported(vki, physicalDevice, m_context.getInstanceExtensions(), m_shaderSpec.requestedExtensionFeatures.ext16BitStorage))
+				TCU_THROW(NotSupportedError, "Requested 16bit storage features not supported");
+		}
+	}
 
 	vector<AllocationSp>				inputAllocs;
 	vector<AllocationSp>				outputAllocs;
 	vector<BufferHandleSp>				inputBuffers;
 	vector<BufferHandleSp>				outputBuffers;
 	vector<VkDescriptorBufferInfo>		descriptorInfos;
+	vector<VkDescriptorType>			descriptorTypes;
 
 	DE_ASSERT(!m_shaderSpec.outputs.empty());
-	const size_t						numBuffers			= m_shaderSpec.inputs.size() + m_shaderSpec.outputs.size();
 
 	// Create buffer object, allocate storage, and create view for all input/output buffers.
 
-	for (size_t inputNdx = 0; inputNdx < m_shaderSpec.inputs.size(); ++inputNdx)
+	for (deUint32 inputNdx = 0; inputNdx < m_shaderSpec.inputs.size(); ++inputNdx)
 	{
+		if (m_shaderSpec.inputTypes.count(inputNdx) != 0)
+			descriptorTypes.push_back(m_shaderSpec.inputTypes.at(inputNdx));
+		else
+			descriptorTypes.push_back(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
+
 		AllocationMp		alloc;
 		const BufferSp&		input		= m_shaderSpec.inputs[inputNdx];
 		const size_t		numBytes	= input->getNumBytes();
-		BufferHandleUp*		buffer		= new BufferHandleUp(createBufferAndBindMemory(vkdi, device, allocator, numBytes, &alloc));
+		BufferHandleUp*		buffer		= new BufferHandleUp(createBufferAndBindMemory(vkdi, device, descriptorTypes.back(), allocator, numBytes, &alloc));
 
 		setMemory(vkdi, device, &*alloc, numBytes, input->data());
 		descriptorInfos.push_back(vk::makeDescriptorBufferInfo(**buffer, 0u, numBytes));
@@ -309,12 +374,14 @@
 		inputAllocs.push_back(de::SharedPtr<Allocation>(alloc.release()));
 	}
 
-	for (size_t outputNdx = 0; outputNdx < m_shaderSpec.outputs.size(); ++outputNdx)
+	for (deUint32 outputNdx = 0; outputNdx < m_shaderSpec.outputs.size(); ++outputNdx)
 	{
+		descriptorTypes.push_back(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
+
 		AllocationMp		alloc;
 		const BufferSp&		output		= m_shaderSpec.outputs[outputNdx];
 		const size_t		numBytes	= output->getNumBytes();
-		BufferHandleUp*		buffer		= new BufferHandleUp(createBufferAndBindMemory(vkdi, device, allocator, numBytes, &alloc));
+		BufferHandleUp*		buffer		= new BufferHandleUp(createBufferAndBindMemory(vkdi, device, descriptorTypes.back(), allocator, numBytes, &alloc));
 
 		fillMemoryWithValue(vkdi, device, &*alloc, numBytes, 0xff);
 		descriptorInfos.push_back(vk::makeDescriptorBufferInfo(**buffer, 0u, numBytes));
@@ -324,10 +391,10 @@
 
 	// Create layouts and descriptor set.
 
-	Unique<VkDescriptorSetLayout>		descriptorSetLayout	(createDescriptorSetLayout(vkdi, device, numBuffers));
-	Unique<VkPipelineLayout>			pipelineLayout		(createPipelineLayout(vkdi, device, *descriptorSetLayout));
-	Unique<VkDescriptorPool>			descriptorPool		(createDescriptorPool(vkdi, device, (deUint32)numBuffers));
-	Unique<VkDescriptorSet>				descriptorSet		(createDescriptorSet(vkdi, device, *descriptorPool, *descriptorSetLayout, numBuffers, descriptorInfos));
+	Unique<VkDescriptorSetLayout>		descriptorSetLayout	(createDescriptorSetLayout(vkdi, device, descriptorTypes));
+	Unique<VkPipelineLayout>			pipelineLayout		(createPipelineLayout(vkdi, device, *descriptorSetLayout, m_shaderSpec.pushConstants));
+	Unique<VkDescriptorPool>			descriptorPool		(createDescriptorPool(vkdi, device, descriptorTypes));
+	Unique<VkDescriptorSet>				descriptorSet		(createDescriptorSet(vkdi, device, *descriptorPool, *descriptorSetLayout, descriptorTypes, descriptorInfos));
 
 	// Create compute shader and pipeline.
 
@@ -354,6 +421,13 @@
 	VK_CHECK(vkdi.beginCommandBuffer(*cmdBuffer, &cmdBufferBeginInfo));
 	vkdi.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *computePipeline);
 	vkdi.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0, 1, &descriptorSet.get(), 0, DE_NULL);
+	if (m_shaderSpec.pushConstants != DE_NULL)
+	{
+		const deUint32	size	= static_cast<deUint32>(m_shaderSpec.pushConstants->getNumBytes());
+		const void*		data	= m_shaderSpec.pushConstants->data();
+
+		vkdi.cmdPushConstants(*cmdBuffer, *pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, /* offset = */ 0, /* size = */ size, data);
+	}
 	vkdi.cmdDispatch(*cmdBuffer, numWorkGroups.x(), numWorkGroups.y(), numWorkGroups.z());
 	VK_CHECK(vkdi.endCommandBuffer(*cmdBuffer));
 
@@ -374,14 +448,14 @@
 		(const VkSemaphore*)DE_NULL,
 	};
 
-	VK_CHECK(vkdi.queueSubmit(m_context.getUniversalQueue(), 1, &submitInfo, *cmdCompleteFence));
+	VK_CHECK(vkdi.queueSubmit(m_queue, 1, &submitInfo, *cmdCompleteFence));
 	VK_CHECK(vkdi.waitForFences(device, 1, &cmdCompleteFence.get(), 0u, infiniteTimeout)); // \note: timeout is failure
 
 	// Check output.
 	if (m_shaderSpec.verifyIO)
 	{
 		if (!(*m_shaderSpec.verifyIO)(m_shaderSpec.inputs, outputAllocs, m_shaderSpec.outputs, m_context.getTestContext().getLog()))
-			return tcu::TestStatus::fail("Output doesn't match with expected");
+			return tcu::TestStatus(m_shaderSpec.failResult, m_shaderSpec.failMessage);
 	}
 	else
 	{
@@ -389,55 +463,11 @@
 		{
 			const BufferSp& expectedOutput = m_shaderSpec.outputs[outputNdx];
 			if (deMemCmp(expectedOutput->data(), outputAllocs[outputNdx]->getHostPtr(), expectedOutput->getNumBytes()))
-				return tcu::TestStatus::fail("Output doesn't match with expected");
+				return tcu::TestStatus(m_shaderSpec.failResult, m_shaderSpec.failMessage);
 		}
 	}
 
-	return tcu::TestStatus::pass("Ouput match with expected");
-}
-
-class ConvertTestInstance : public SpvAsmComputeShaderInstance
-{
-public:
-						ConvertTestInstance	(Context& ctx, const ComputeShaderSpec& spec, const ConvertTestFeatures features);
-	tcu::TestStatus		iterate				(void);
-private:
-	const ConvertTestFeatures	m_features;
-};
-
-ConvertTestInstance::ConvertTestInstance (Context& ctx, const ComputeShaderSpec& spec, const ConvertTestFeatures features)
-	: SpvAsmComputeShaderInstance	(ctx, spec)
-	, m_features					(features)
-{
-}
-
-tcu::TestStatus ConvertTestInstance::iterate (void)
-{
-	const VkPhysicalDeviceFeatures&		features = m_context.getDeviceFeatures();
-
-	if ((m_features == CONVERT_TEST_USES_INT16 || m_features == CONVERT_TEST_USES_INT16_INT64) && !features.shaderInt16)
-	{
-		throw tcu::NotSupportedError("shaderInt16 feature is not supported");
-	}
-
-	if ((m_features == CONVERT_TEST_USES_INT64 || m_features == CONVERT_TEST_USES_INT16_INT64) && !features.shaderInt64)
-	{
-		throw tcu::NotSupportedError("shaderInt64 feature is not supported");
-	}
-
-	return SpvAsmComputeShaderInstance::iterate();
-}
-
-ConvertTestCase::ConvertTestCase (tcu::TestContext& testCtx, const char* name, const char* description, const ComputeShaderSpec& spec, const ConvertTestFeatures features)
-	: SpvAsmComputeShaderCase	(testCtx, name, description, spec)
-	, m_shaderSpec				(spec)
-	, m_features				(features)
-{
-}
-
-TestInstance* ConvertTestCase::createInstance (Context& ctx) const
-{
-	return new ConvertTestInstance(ctx, m_shaderSpec, m_features);
+	return tcu::TestStatus::pass("Output match with expected");
 }
 
 } // SpirVAssembly
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderCase.hpp b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderCase.hpp
index bd70ffc..50c69e9 100644
--- a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderCase.hpp
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderCase.hpp
@@ -33,32 +33,24 @@
 namespace SpirVAssembly
 {
 
+enum ComputeTestFeatures
+{
+	COMPUTE_TEST_USES_NONE,
+	COMPUTE_TEST_USES_INT16,
+	COMPUTE_TEST_USES_INT64,
+	COMPUTE_TEST_USES_INT16_INT64,
+};
+
 class SpvAsmComputeShaderCase : public TestCase
 {
 public:
-						SpvAsmComputeShaderCase	(tcu::TestContext& testCtx, const char* name, const char* description, const ComputeShaderSpec& spec);
+						SpvAsmComputeShaderCase	(tcu::TestContext& testCtx, const char* name, const char* description, const ComputeShaderSpec& spec, const ComputeTestFeatures features = COMPUTE_TEST_USES_NONE);
 	void				initPrograms			(vk::SourceCollections& programCollection) const;
 	TestInstance*		createInstance			(Context& ctx) const;
 
 private:
 	ComputeShaderSpec	m_shaderSpec;
-};
-
-enum ConvertTestFeatures
-{
-	CONVERT_TEST_USES_INT16,
-	CONVERT_TEST_USES_INT64,
-	CONVERT_TEST_USES_INT16_INT64,
-};
-
-class ConvertTestCase : public SpvAsmComputeShaderCase
-{
-public:
-						ConvertTestCase	(tcu::TestContext& testCtx, const char* name, const char* description, const ComputeShaderSpec& spec, const ConvertTestFeatures features);
-	TestInstance*		createInstance	(Context& ctx) const;
-private:
-	ComputeShaderSpec			m_shaderSpec;
-	const ConvertTestFeatures	m_features;
+	const ComputeTestFeatures	m_features;
 };
 
 } // SpirVAssembly
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.cpp b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.cpp
index 5727fb5..2d60026 100644
--- a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.cpp
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.cpp
@@ -23,4 +23,58 @@
 
 #include "vktSpvAsmComputeShaderTestUtil.hpp"
 
-DE_EMPTY_CPP_FILE
+namespace vkt
+{
+namespace SpirVAssembly
+{
+
+const char* getComputeAsmShaderPreamble (void)
+{
+	return
+		"OpCapability Shader\n"
+		"OpMemoryModel Logical GLSL450\n"
+		"OpEntryPoint GLCompute %main \"main\" %id\n"
+		"OpExecutionMode %main LocalSize 1 1 1\n";
+}
+
+const char* getComputeAsmCommonTypes (void)
+{
+	return
+		"%bool      = OpTypeBool\n"
+		"%void      = OpTypeVoid\n"
+		"%voidf     = OpTypeFunction %void\n"
+		"%u32       = OpTypeInt 32 0\n"
+		"%i32       = OpTypeInt 32 1\n"
+		"%f32       = OpTypeFloat 32\n"
+		"%uvec3     = OpTypeVector %u32 3\n"
+		"%fvec3     = OpTypeVector %f32 3\n"
+		"%uvec3ptr  = OpTypePointer Input %uvec3\n"
+		"%i32ptr    = OpTypePointer Uniform %i32\n"
+		"%f32ptr    = OpTypePointer Uniform %f32\n"
+		"%i32arr    = OpTypeRuntimeArray %i32\n"
+		"%f32arr    = OpTypeRuntimeArray %f32\n";
+}
+
+const char* getComputeAsmInputOutputBuffer (void)
+{
+	return
+		"%buf     = OpTypeStruct %f32arr\n"
+		"%bufptr  = OpTypePointer Uniform %buf\n"
+		"%indata    = OpVariable %bufptr Uniform\n"
+		"%outdata   = OpVariable %bufptr Uniform\n";
+}
+
+const char* getComputeAsmInputOutputBufferTraits (void)
+{
+	return
+		"OpDecorate %buf BufferBlock\n"
+		"OpDecorate %indata DescriptorSet 0\n"
+		"OpDecorate %indata Binding 0\n"
+		"OpDecorate %outdata DescriptorSet 0\n"
+		"OpDecorate %outdata Binding 1\n"
+		"OpDecorate %f32arr ArrayStride 4\n"
+		"OpMemberDecorate %buf 0 Offset 0\n";
+}
+
+} // SpirVAssembly
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.hpp b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.hpp
index 4c6b644..09adf1d 100644
--- a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.hpp
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.hpp
@@ -24,13 +24,16 @@
  *//*--------------------------------------------------------------------*/
 
 #include "deDefs.h"
+#include "deFloat16.h"
 #include "deSharedPtr.hpp"
 #include "tcuTestLog.hpp"
 #include "tcuVector.hpp"
 #include "vkMemUtil.hpp"
+#include "vktSpvAsmUtils.hpp"
 
 #include <string>
 #include <vector>
+#include <map>
 
 using namespace vk;
 
@@ -80,6 +83,10 @@
 typedef Buffer<deInt32>		Int32Buffer;
 typedef Buffer<tcu::Vec4>	Vec4Buffer;
 
+typedef bool (*ComputeVerifyIOFunc) (const std::vector<BufferSp>&		inputs,
+									 const std::vector<AllocationSp>&	outputAllocations,
+									 const std::vector<BufferSp>&		expectedOutputs,
+									 tcu::TestLog&						log);
 
 /*--------------------------------------------------------------------*//*!
  * \brief Specification for a compute shader.
@@ -89,24 +96,54 @@
  *//*--------------------------------------------------------------------*/
 struct ComputeShaderSpec
 {
-	std::string				assembly;
-	std::string				entryPoint;
-	std::vector<BufferSp>	inputs;
-	std::vector<BufferSp>	outputs;
-	tcu::IVec3				numWorkGroups;
-	std::vector<deUint32>	specConstants;
+	std::string								assembly;
+	std::string								entryPoint;
+	std::vector<BufferSp>					inputs;
+	// Mapping from input index (in the inputs field) to the descriptor type.
+	std::map<deUint32, VkDescriptorType>	inputTypes;
+	std::vector<BufferSp>					outputs;
+	tcu::IVec3								numWorkGroups;
+	std::vector<deUint32>					specConstants;
+	BufferSp								pushConstants;
+	std::vector<std::string>				extensions;
+	ExtensionFeatures						requestedExtensionFeatures;
+	qpTestResult							failResult;
+	std::string								failMessage;
 	// If null, a default verification will be performed by comparing the memory pointed to by outputAllocations
 	// and the contents of expectedOutputs. Otherwise the function pointed to by verifyIO will be called.
 	// If true is returned, then the test case is assumed to have passed, if false is returned, then the test
-	// case is assumed to have failed.
-	bool					(*verifyIO)(const std::vector<BufferSp>& inputs, const std::vector<AllocationSp>& outputAllocations, const std::vector<BufferSp>& expectedOutputs, tcu::TestLog& log);
-							ComputeShaderSpec()
-								: entryPoint	("main")
-								, verifyIO		(DE_NULL)
-							{}
+	// case is assumed to have failed. Exact meaning of failure can be customized with failResult.
+	ComputeVerifyIOFunc						verifyIO;
 
+											ComputeShaderSpec (void)
+												: entryPoint					("main")
+												, pushConstants					(DE_NULL)
+												, requestedExtensionFeatures	()
+												, failResult					(QP_TEST_RESULT_FAIL)
+												, failMessage					("Output doesn't match with expected")
+												, verifyIO						(DE_NULL)
+											{}
 };
 
+/*--------------------------------------------------------------------*//*!
+ * \brief Helper functions for SPIR-V assembly shared by various tests
+ *//*--------------------------------------------------------------------*/
+
+const char* getComputeAsmShaderPreamble				(void);
+const char* getComputeAsmCommonTypes				(void);
+
+/*--------------------------------------------------------------------*//*!
+ * Declares two uniform variables (indata, outdata) of type
+ * "struct { float[] }". Depends on type "f32arr" (for "float[]").
+ *//*--------------------------------------------------------------------*/
+const char* getComputeAsmInputOutputBuffer			(void);
+/*--------------------------------------------------------------------*//*!
+ * Declares buffer type and layout for uniform variables indata and
+ * outdata. Both of them are SSBO bounded to descriptor set 0.
+ * indata is at binding point 0, while outdata is at 1.
+ *//*--------------------------------------------------------------------*/
+const char* getComputeAsmInputOutputBufferTraits	(void);
+
 } // SpirVAssembly
 } // vkt
 
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.cpp b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.cpp
new file mode 100644
index 0000000..a62ee13
--- /dev/null
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.cpp
@@ -0,0 +1,3490 @@
+/*-------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Graphics pipeline for SPIR-V assembly tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktSpvAsmGraphicsShaderTestUtil.hpp"
+
+#include "tcuFloat.hpp"
+#include "tcuStringTemplate.hpp"
+
+#include "vkDefs.hpp"
+#include "vkMemUtil.hpp"
+#include "vkPlatform.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkRefUtil.hpp"
+#include "vkTypeUtil.hpp"
+
+#include "deRandom.hpp"
+
+namespace vkt
+{
+namespace SpirVAssembly
+{
+
+using namespace vk;
+using std::map;
+using std::string;
+using std::vector;
+using tcu::Float16;
+using tcu::Float32;
+using tcu::IVec3;
+using tcu::IVec4;
+using tcu::RGBA;
+using tcu::TestLog;
+using tcu::TestStatus;
+using tcu::Vec4;
+using de::UniquePtr;
+using tcu::StringTemplate;
+using tcu::Vec4;
+
+deUint32 IFDataType::getElementNumBytes (void) const
+{
+	if (elementType < NUMBERTYPE_END32)
+		return 4;
+
+	return 2;
+}
+
+VkFormat IFDataType::getVkFormat (void) const
+{
+	if (numElements == 1)
+	{
+		switch (elementType)
+		{
+			case NUMBERTYPE_FLOAT32:	return VK_FORMAT_R32_SFLOAT;
+			case NUMBERTYPE_INT32:		return VK_FORMAT_R32_SINT;
+			case NUMBERTYPE_UINT32:		return VK_FORMAT_R32_UINT;
+			case NUMBERTYPE_FLOAT16:	return VK_FORMAT_R16_SFLOAT;
+			case NUMBERTYPE_INT16:		return VK_FORMAT_R16_SINT;
+			case NUMBERTYPE_UINT16:		return VK_FORMAT_R16_UINT;
+			default:					break;
+		}
+	}
+	else if (numElements == 2)
+	{
+		switch (elementType)
+		{
+			case NUMBERTYPE_FLOAT32:	return VK_FORMAT_R32G32_SFLOAT;
+			case NUMBERTYPE_INT32:		return VK_FORMAT_R32G32_SINT;
+			case NUMBERTYPE_UINT32:		return VK_FORMAT_R32G32_UINT;
+			case NUMBERTYPE_FLOAT16:	return VK_FORMAT_R16G16_SFLOAT;
+			case NUMBERTYPE_INT16:		return VK_FORMAT_R16G16_SINT;
+			case NUMBERTYPE_UINT16:		return VK_FORMAT_R16G16_UINT;
+			default:					break;
+		}
+	}
+	else if (numElements == 3)
+	{
+		switch (elementType)
+		{
+			case NUMBERTYPE_FLOAT32:	return VK_FORMAT_R32G32B32_SFLOAT;
+			case NUMBERTYPE_INT32:		return VK_FORMAT_R32G32B32_SINT;
+			case NUMBERTYPE_UINT32:		return VK_FORMAT_R32G32B32_UINT;
+			case NUMBERTYPE_FLOAT16:	return VK_FORMAT_R16G16B16_SFLOAT;
+			case NUMBERTYPE_INT16:		return VK_FORMAT_R16G16B16_SINT;
+			case NUMBERTYPE_UINT16:		return VK_FORMAT_R16G16B16_UINT;
+			default:					break;
+		}
+	}
+	else if (numElements == 4)
+	{
+		switch (elementType)
+		{
+			case NUMBERTYPE_FLOAT32:	return VK_FORMAT_R32G32B32A32_SFLOAT;
+			case NUMBERTYPE_INT32:		return VK_FORMAT_R32G32B32A32_SINT;
+			case NUMBERTYPE_UINT32:		return VK_FORMAT_R32G32B32A32_UINT;
+			case NUMBERTYPE_FLOAT16:	return VK_FORMAT_R16G16B16A16_SFLOAT;
+			case NUMBERTYPE_INT16:		return VK_FORMAT_R16G16B16A16_SINT;
+			case NUMBERTYPE_UINT16:		return VK_FORMAT_R16G16B16A16_UINT;
+			default:					break;
+		}
+	}
+
+	DE_ASSERT(false);
+	return VK_FORMAT_UNDEFINED;
+}
+
+tcu::TextureFormat IFDataType::getTextureFormat (void) const
+{
+	tcu::TextureFormat::ChannelType		ct	= tcu::TextureFormat::CHANNELTYPE_LAST;
+	tcu::TextureFormat::ChannelOrder	co	= tcu::TextureFormat::CHANNELORDER_LAST;
+
+	switch (elementType)
+	{
+		case NUMBERTYPE_FLOAT32:	ct = tcu::TextureFormat::FLOAT;				break;
+		case NUMBERTYPE_INT32:		ct = tcu::TextureFormat::SIGNED_INT32;		break;
+		case NUMBERTYPE_UINT32:		ct = tcu::TextureFormat::UNSIGNED_INT32;	break;
+		case NUMBERTYPE_FLOAT16:	ct = tcu::TextureFormat::HALF_FLOAT;		break;
+		case NUMBERTYPE_INT16:		ct = tcu::TextureFormat::SIGNED_INT16;		break;
+		case NUMBERTYPE_UINT16:		ct = tcu::TextureFormat::UNSIGNED_INT16;	break;
+		default:					DE_ASSERT(false);
+	}
+
+	switch (numElements)
+	{
+		case 1:				co = tcu::TextureFormat::R;					break;
+		case 2:				co = tcu::TextureFormat::RG;				break;
+		case 3:				co = tcu::TextureFormat::RGB;				break;
+		case 4:				co = tcu::TextureFormat::RGBA;				break;
+		default:			DE_ASSERT(false);
+	}
+
+	return tcu::TextureFormat(co, ct);
+}
+
+string IFDataType::str (void) const
+{
+	string	ret;
+
+	switch (elementType)
+	{
+		case NUMBERTYPE_FLOAT32:	ret = "f32"; break;
+		case NUMBERTYPE_INT32:		ret = "i32"; break;
+		case NUMBERTYPE_UINT32:		ret = "u32"; break;
+		case NUMBERTYPE_FLOAT16:	ret = "f16"; break;
+		case NUMBERTYPE_INT16:		ret = "i16"; break;
+		case NUMBERTYPE_UINT16:		ret = "u16"; break;
+		default:					DE_ASSERT(false);
+	}
+
+	if (numElements == 1)
+		return ret;
+
+	return string("v") + numberToString(numElements) + ret;
+}
+
+VkBufferUsageFlagBits getMatchingBufferUsageFlagBit(VkDescriptorType dType)
+{
+	switch (dType)
+	{
+		case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+		case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
+		default:								DE_ASSERT(0 && "not implemented");
+	}
+	return (VkBufferUsageFlagBits)0;
+}
+
+InstanceContext::InstanceContext (const RGBA						(&inputs)[4],
+								  const RGBA						(&outputs)[4],
+								  const map<string, string>&		testCodeFragments_,
+								  const StageToSpecConstantMap&		specConstants_,
+								  const PushConstants&				pushConsants_,
+								  const GraphicsResources&			resources_,
+								  const GraphicsInterfaces&			interfaces_,
+								  const vector<string>&				extensions_,
+								  const vector<string>&				features_,
+								  ExtensionFeatures					extensionFeatures_)
+	: testCodeFragments				(testCodeFragments_)
+	, specConstants					(specConstants_)
+	, hasTessellation				(false)
+	, requiredStages				(static_cast<VkShaderStageFlagBits>(0))
+	, requiredDeviceExtensions		(extensions_)
+	, requiredDeviceFeatures		(features_)
+	, requestedExtensionFeatures	(extensionFeatures_)
+	, pushConstants					(pushConsants_)
+	, resources						(resources_)
+	, interfaces					(interfaces_)
+	, failResult					(QP_TEST_RESULT_FAIL)
+	, failMessageTemplate			("${reason}")
+{
+	inputColors[0]		= inputs[0];
+	inputColors[1]		= inputs[1];
+	inputColors[2]		= inputs[2];
+	inputColors[3]		= inputs[3];
+
+	outputColors[0]		= outputs[0];
+	outputColors[1]		= outputs[1];
+	outputColors[2]		= outputs[2];
+	outputColors[3]		= outputs[3];
+}
+
+InstanceContext::InstanceContext (const InstanceContext& other)
+	: moduleMap						(other.moduleMap)
+	, testCodeFragments				(other.testCodeFragments)
+	, specConstants					(other.specConstants)
+	, hasTessellation				(other.hasTessellation)
+	, requiredStages				(other.requiredStages)
+	, requiredDeviceExtensions		(other.requiredDeviceExtensions)
+	, requiredDeviceFeatures		(other.requiredDeviceFeatures)
+	, requestedExtensionFeatures	(other.requestedExtensionFeatures)
+	, pushConstants					(other.pushConstants)
+	, resources						(other.resources)
+	, interfaces					(other.interfaces)
+	, failResult					(other.failResult)
+	, failMessageTemplate			(other.failMessageTemplate)
+{
+	inputColors[0]		= other.inputColors[0];
+	inputColors[1]		= other.inputColors[1];
+	inputColors[2]		= other.inputColors[2];
+	inputColors[3]		= other.inputColors[3];
+
+	outputColors[0]		= other.outputColors[0];
+	outputColors[1]		= other.outputColors[1];
+	outputColors[2]		= other.outputColors[2];
+	outputColors[3]		= other.outputColors[3];
+}
+
+string InstanceContext::getSpecializedFailMessage (const string& failureReason)
+{
+	map<string, string>		parameters;
+	parameters["reason"]	= failureReason;
+	return StringTemplate(failMessageTemplate).specialize(parameters);
+}
+
+ShaderElement::ShaderElement (const string&				moduleName_,
+							  const string&				entryPoint_,
+							  VkShaderStageFlagBits		shaderStage_)
+		: moduleName(moduleName_)
+		, entryName(entryPoint_)
+		, stage(shaderStage_)
+{
+}
+
+void getDefaultColors (RGBA (&colors)[4])
+{
+	colors[0] = RGBA::white();
+	colors[1] = RGBA::red();
+	colors[2] = RGBA::green();
+	colors[3] = RGBA::blue();
+}
+
+void getHalfColorsFullAlpha (RGBA (&colors)[4])
+{
+	colors[0] = RGBA(127, 127, 127, 255);
+	colors[1] = RGBA(127, 0,   0,	255);
+	colors[2] = RGBA(0,	  127, 0,	255);
+	colors[3] = RGBA(0,	  0,   127, 255);
+}
+
+void getInvertedDefaultColors (RGBA (&colors)[4])
+{
+	colors[0] = RGBA(0,		0,		0,		255);
+	colors[1] = RGBA(0,		255,	255,	255);
+	colors[2] = RGBA(255,	0,		255,	255);
+	colors[3] = RGBA(255,	255,	0,		255);
+}
+
+// For the current InstanceContext, constructs the required modules and shader stage create infos.
+void createPipelineShaderStages (const DeviceInterface&						vk,
+								 const VkDevice								vkDevice,
+								 InstanceContext&							instance,
+								 Context&									context,
+								 vector<ModuleHandleSp>&					modules,
+								 vector<VkPipelineShaderStageCreateInfo>&	createInfos)
+{
+	for (ModuleMap::const_iterator moduleNdx = instance.moduleMap.begin(); moduleNdx != instance.moduleMap.end(); ++moduleNdx)
+	{
+		const ModuleHandleSp mod(new Unique<VkShaderModule>(createShaderModule(vk, vkDevice, context.getBinaryCollection().get(moduleNdx->first), 0)));
+		modules.push_back(ModuleHandleSp(mod));
+		for (vector<EntryToStage>::const_iterator shaderNdx = moduleNdx->second.begin(); shaderNdx != moduleNdx->second.end(); ++shaderNdx)
+		{
+			const EntryToStage&						stage			= *shaderNdx;
+			const VkPipelineShaderStageCreateInfo	shaderParam		=
+			{
+				VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,	//	VkStructureType			sType;
+				DE_NULL,												//	const void*				pNext;
+				(VkPipelineShaderStageCreateFlags)0,
+				stage.second,											//	VkShaderStageFlagBits	stage;
+				**modules.back(),										//	VkShaderModule			module;
+				stage.first.c_str(),									//	const char*				pName;
+				(const VkSpecializationInfo*)DE_NULL,
+			};
+			createInfos.push_back(shaderParam);
+		}
+	}
+}
+
+#define SPIRV_ASSEMBLY_TYPES																	\
+	"%void = OpTypeVoid\n"																		\
+	"%bool = OpTypeBool\n"																		\
+																								\
+	"%i32 = OpTypeInt 32 1\n"																	\
+	"%u32 = OpTypeInt 32 0\n"																	\
+																								\
+	"%f32 = OpTypeFloat 32\n"																	\
+	"%v2i32 = OpTypeVector %i32 2\n"															\
+	"%v2u32 = OpTypeVector %u32 2\n"															\
+	"%v2f32 = OpTypeVector %f32 2\n"															\
+	"%v3f32 = OpTypeVector %f32 3\n"															\
+	"%v4i32 = OpTypeVector %i32 4\n"															\
+	"%v4u32 = OpTypeVector %u32 4\n"															\
+	"%v4f32 = OpTypeVector %f32 4\n"															\
+	"%v4bool = OpTypeVector %bool 4\n"															\
+																								\
+	"%v4f32_function = OpTypeFunction %v4f32 %v4f32\n"											\
+	"%bool_function = OpTypeFunction %bool\n"													\
+	"%fun = OpTypeFunction %void\n"																\
+																								\
+	"%ip_f32 = OpTypePointer Input %f32\n"														\
+	"%ip_i32 = OpTypePointer Input %i32\n"														\
+	"%ip_u32 = OpTypePointer Input %u32\n"														\
+	"%ip_v3f32 = OpTypePointer Input %v3f32\n"													\
+	"%ip_v2f32 = OpTypePointer Input %v2f32\n"													\
+	"%ip_v2i32 = OpTypePointer Input %v2i32\n"													\
+	"%ip_v2u32 = OpTypePointer Input %v2u32\n"													\
+	"%ip_v4f32 = OpTypePointer Input %v4f32\n"													\
+	"%ip_v4i32 = OpTypePointer Input %v4i32\n"													\
+	"%ip_v4u32 = OpTypePointer Input %v4u32\n"													\
+																								\
+	"%op_f32 = OpTypePointer Output %f32\n"														\
+	"%op_i32 = OpTypePointer Output %i32\n"														\
+	"%op_u32 = OpTypePointer Output %u32\n"														\
+	"%op_v2f32 = OpTypePointer Output %v2f32\n"													\
+	"%op_v2i32 = OpTypePointer Output %v2i32\n"													\
+	"%op_v2u32 = OpTypePointer Output %v2u32\n"													\
+	"%op_v4f32 = OpTypePointer Output %v4f32\n"													\
+	"%op_v4i32 = OpTypePointer Output %v4i32\n"													\
+	"%op_v4u32 = OpTypePointer Output %v4u32\n"													\
+																								\
+	"%fp_f32   = OpTypePointer Function %f32\n"													\
+	"%fp_i32   = OpTypePointer Function %i32\n"													\
+	"%fp_v4f32 = OpTypePointer Function %v4f32\n"
+
+#define SPIRV_ASSEMBLY_CONSTANTS																\
+	"%c_f32_1 = OpConstant %f32 1.0\n"															\
+	"%c_f32_0 = OpConstant %f32 0.0\n"															\
+	"%c_f32_0_5 = OpConstant %f32 0.5\n"														\
+	"%c_f32_n1  = OpConstant %f32 -1.\n"														\
+	"%c_f32_7 = OpConstant %f32 7.0\n"															\
+	"%c_f32_8 = OpConstant %f32 8.0\n"															\
+	"%c_i32_0 = OpConstant %i32 0\n"															\
+	"%c_i32_1 = OpConstant %i32 1\n"															\
+	"%c_i32_2 = OpConstant %i32 2\n"															\
+	"%c_i32_3 = OpConstant %i32 3\n"															\
+	"%c_i32_4 = OpConstant %i32 4\n"															\
+	"%c_u32_0 = OpConstant %u32 0\n"															\
+	"%c_u32_1 = OpConstant %u32 1\n"															\
+	"%c_u32_2 = OpConstant %u32 2\n"															\
+	"%c_u32_3 = OpConstant %u32 3\n"															\
+	"%c_u32_32 = OpConstant %u32 32\n"															\
+	"%c_u32_4 = OpConstant %u32 4\n"															\
+	"%c_u32_31_bits = OpConstant %u32 0x7FFFFFFF\n"												\
+	"%c_v4f32_1_1_1_1 = OpConstantComposite %v4f32 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_1\n"		\
+	"%c_v4f32_1_0_0_1 = OpConstantComposite %v4f32 %c_f32_1 %c_f32_0 %c_f32_0 %c_f32_1\n"		\
+	"%c_v4f32_0_5_0_5_0_5_0_5 = OpConstantComposite %v4f32 %c_f32_0_5 %c_f32_0_5 %c_f32_0_5 %c_f32_0_5\n"
+
+#define SPIRV_ASSEMBLY_ARRAYS																	\
+	"%a1f32 = OpTypeArray %f32 %c_u32_1\n"														\
+	"%a2f32 = OpTypeArray %f32 %c_u32_2\n"														\
+	"%a3v4f32 = OpTypeArray %v4f32 %c_u32_3\n"													\
+	"%a4f32 = OpTypeArray %f32 %c_u32_4\n"														\
+	"%a32v4f32 = OpTypeArray %v4f32 %c_u32_32\n"												\
+	"%ip_a3v4f32 = OpTypePointer Input %a3v4f32\n"												\
+	"%ip_a32v4f32 = OpTypePointer Input %a32v4f32\n"											\
+	"%op_a2f32 = OpTypePointer Output %a2f32\n"													\
+	"%op_a3v4f32 = OpTypePointer Output %a3v4f32\n"												\
+	"%op_a4f32 = OpTypePointer Output %a4f32\n"
+
+// Creates vertex-shader assembly by specializing a boilerplate StringTemplate
+// on fragments, which must (at least) map "testfun" to an OpFunction definition
+// for %test_code that takes and returns a %v4f32.  Boilerplate IDs are prefixed
+// with "BP_" to avoid collisions with fragments.
+//
+// It corresponds roughly to this GLSL:
+//;
+// layout(location = 0) in vec4 position;
+// layout(location = 1) in vec4 color;
+// layout(location = 1) out highp vec4 vtxColor;
+// void main (void) { gl_Position = position; vtxColor = test_func(color); }
+string makeVertexShaderAssembly(const map<string, string>& fragments)
+{
+// \todo [2015-11-23 awoloszyn] Remove OpName once these have stabalized
+	static const char vertexShaderBoilerplate[] =
+		"OpCapability Shader\n"
+		"OpCapability ClipDistance\n"
+		"OpCapability CullDistance\n"
+		"${capability:opt}\n"
+		"${extension:opt}\n"
+		"OpMemoryModel Logical GLSL450\n"
+		"OpEntryPoint Vertex %main \"main\" %BP_stream %BP_position %BP_vtx_color %BP_color %BP_gl_VertexIndex %BP_gl_InstanceIndex ${IF_entrypoint:opt} \n"
+		"${debug:opt}\n"
+		"OpName %main \"main\"\n"
+		"OpName %BP_gl_PerVertex \"gl_PerVertex\"\n"
+		"OpMemberName %BP_gl_PerVertex 0 \"gl_Position\"\n"
+		"OpMemberName %BP_gl_PerVertex 1 \"gl_PointSize\"\n"
+		"OpMemberName %BP_gl_PerVertex 2 \"gl_ClipDistance\"\n"
+		"OpMemberName %BP_gl_PerVertex 3 \"gl_CullDistance\"\n"
+		"OpName %test_code \"testfun(vf4;\"\n"
+		"OpName %BP_stream \"\"\n"
+		"OpName %BP_position \"position\"\n"
+		"OpName %BP_vtx_color \"vtxColor\"\n"
+		"OpName %BP_color \"color\"\n"
+		"OpName %BP_gl_VertexIndex \"gl_VertexIndex\"\n"
+		"OpName %BP_gl_InstanceIndex \"gl_InstanceIndex\"\n"
+		"OpMemberDecorate %BP_gl_PerVertex 0 BuiltIn Position\n"
+		"OpMemberDecorate %BP_gl_PerVertex 1 BuiltIn PointSize\n"
+		"OpMemberDecorate %BP_gl_PerVertex 2 BuiltIn ClipDistance\n"
+		"OpMemberDecorate %BP_gl_PerVertex 3 BuiltIn CullDistance\n"
+		"OpDecorate %BP_gl_PerVertex Block\n"
+		"OpDecorate %BP_position Location 0\n"
+		"OpDecorate %BP_vtx_color Location 1\n"
+		"OpDecorate %BP_color Location 1\n"
+		"OpDecorate %BP_gl_VertexIndex BuiltIn VertexIndex\n"
+		"OpDecorate %BP_gl_InstanceIndex BuiltIn InstanceIndex\n"
+		"${IF_decoration:opt}\n"
+		"${decoration:opt}\n"
+		SPIRV_ASSEMBLY_TYPES
+		SPIRV_ASSEMBLY_CONSTANTS
+		SPIRV_ASSEMBLY_ARRAYS
+		"%BP_gl_PerVertex = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+		"%BP_op_gl_PerVertex = OpTypePointer Output %BP_gl_PerVertex\n"
+		"%BP_stream = OpVariable %BP_op_gl_PerVertex Output\n"
+		"%BP_position = OpVariable %ip_v4f32 Input\n"
+		"%BP_vtx_color = OpVariable %op_v4f32 Output\n"
+		"%BP_color = OpVariable %ip_v4f32 Input\n"
+		"%BP_gl_VertexIndex = OpVariable %ip_i32 Input\n"
+		"%BP_gl_InstanceIndex = OpVariable %ip_i32 Input\n"
+		"${pre_main:opt}\n"
+		"${IF_variable:opt}\n"
+		"%main = OpFunction %void None %fun\n"
+		"%BP_label = OpLabel\n"
+		"${IF_carryforward:opt}\n"
+		"%BP_pos = OpLoad %v4f32 %BP_position\n"
+		"%BP_gl_pos = OpAccessChain %op_v4f32 %BP_stream %c_i32_0\n"
+		"OpStore %BP_gl_pos %BP_pos\n"
+		"%BP_col = OpLoad %v4f32 %BP_color\n"
+		"%BP_col_transformed = OpFunctionCall %v4f32 %test_code %BP_col\n"
+		"OpStore %BP_vtx_color %BP_col_transformed\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+		"${interface_op_func:opt}\n"
+
+		"%isUniqueIdZero = OpFunction %bool None %bool_function\n"
+		"%getId_label = OpLabel\n"
+		"%vert_id = OpLoad %i32 %BP_gl_VertexIndex\n"
+		"%is_id_0 = OpIEqual %bool %vert_id %c_i32_0\n"
+		"OpReturnValue %is_id_0\n"
+		"OpFunctionEnd\n"
+
+		"${testfun}\n";
+	return tcu::StringTemplate(vertexShaderBoilerplate).specialize(fragments);
+}
+
+// Creates tess-control-shader assembly by specializing a boilerplate
+// StringTemplate on fragments, which must (at least) map "testfun" to an
+// OpFunction definition for %test_code that takes and returns a %v4f32.
+// Boilerplate IDs are prefixed with "BP_" to avoid collisions with fragments.
+//
+// It roughly corresponds to the following GLSL.
+//
+// #version 450
+// layout(vertices = 3) out;
+// layout(location = 1) in vec4 in_color[];
+// layout(location = 1) out vec4 out_color[];
+//
+// void main() {
+//   out_color[gl_InvocationID] = testfun(in_color[gl_InvocationID]);
+//   gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
+//   if (gl_InvocationID == 0) {
+//     gl_TessLevelOuter[0] = 1.0;
+//     gl_TessLevelOuter[1] = 1.0;
+//     gl_TessLevelOuter[2] = 1.0;
+//     gl_TessLevelInner[0] = 1.0;
+//   }
+// }
+string makeTessControlShaderAssembly (const map<string, string>& fragments)
+{
+	static const char tessControlShaderBoilerplate[] =
+		"OpCapability Tessellation\n"
+		"OpCapability ClipDistance\n"
+		"OpCapability CullDistance\n"
+		"${capability:opt}\n"
+		"${extension:opt}\n"
+		"OpMemoryModel Logical GLSL450\n"
+		"OpEntryPoint TessellationControl %BP_main \"main\" %BP_out_color %BP_gl_InvocationID %BP_in_color %BP_gl_out %BP_gl_in %BP_gl_TessLevelOuter %BP_gl_TessLevelInner ${IF_entrypoint:opt} \n"
+		"OpExecutionMode %BP_main OutputVertices 3\n"
+		"${debug:opt}\n"
+		"OpName %BP_main \"main\"\n"
+		"OpName %test_code \"testfun(vf4;\"\n"
+		"OpName %BP_out_color \"out_color\"\n"
+		"OpName %BP_gl_InvocationID \"gl_InvocationID\"\n"
+		"OpName %BP_in_color \"in_color\"\n"
+		"OpName %BP_gl_PerVertex \"gl_PerVertex\"\n"
+		"OpMemberName %BP_gl_PerVertex 0 \"gl_Position\"\n"
+		"OpMemberName %BP_gl_PerVertex 1 \"gl_PointSize\"\n"
+		"OpMemberName %BP_gl_PerVertex 2 \"gl_ClipDistance\"\n"
+		"OpMemberName %BP_gl_PerVertex 3 \"gl_CullDistance\"\n"
+		"OpName %BP_gl_out \"gl_out\"\n"
+		"OpName %BP_gl_PVOut \"gl_PerVertex\"\n"
+		"OpMemberName %BP_gl_PVOut 0 \"gl_Position\"\n"
+		"OpMemberName %BP_gl_PVOut 1 \"gl_PointSize\"\n"
+		"OpMemberName %BP_gl_PVOut 2 \"gl_ClipDistance\"\n"
+		"OpMemberName %BP_gl_PVOut 3 \"gl_CullDistance\"\n"
+		"OpName %BP_gl_in \"gl_in\"\n"
+		"OpName %BP_gl_TessLevelOuter \"gl_TessLevelOuter\"\n"
+		"OpName %BP_gl_TessLevelInner \"gl_TessLevelInner\"\n"
+		"OpDecorate %BP_out_color Location 1\n"
+		"OpDecorate %BP_gl_InvocationID BuiltIn InvocationId\n"
+		"OpDecorate %BP_in_color Location 1\n"
+		"OpMemberDecorate %BP_gl_PerVertex 0 BuiltIn Position\n"
+		"OpMemberDecorate %BP_gl_PerVertex 1 BuiltIn PointSize\n"
+		"OpMemberDecorate %BP_gl_PerVertex 2 BuiltIn ClipDistance\n"
+		"OpMemberDecorate %BP_gl_PerVertex 3 BuiltIn CullDistance\n"
+		"OpDecorate %BP_gl_PerVertex Block\n"
+		"OpMemberDecorate %BP_gl_PVOut 0 BuiltIn Position\n"
+		"OpMemberDecorate %BP_gl_PVOut 1 BuiltIn PointSize\n"
+		"OpMemberDecorate %BP_gl_PVOut 2 BuiltIn ClipDistance\n"
+		"OpMemberDecorate %BP_gl_PVOut 3 BuiltIn CullDistance\n"
+		"OpDecorate %BP_gl_PVOut Block\n"
+		"OpDecorate %BP_gl_TessLevelOuter Patch\n"
+		"OpDecorate %BP_gl_TessLevelOuter BuiltIn TessLevelOuter\n"
+		"OpDecorate %BP_gl_TessLevelInner Patch\n"
+		"OpDecorate %BP_gl_TessLevelInner BuiltIn TessLevelInner\n"
+		"${IF_decoration:opt}\n"
+		"${decoration:opt}\n"
+		SPIRV_ASSEMBLY_TYPES
+		SPIRV_ASSEMBLY_CONSTANTS
+		SPIRV_ASSEMBLY_ARRAYS
+		"%BP_out_color = OpVariable %op_a3v4f32 Output\n"
+		"%BP_gl_InvocationID = OpVariable %ip_i32 Input\n"
+		"%BP_in_color = OpVariable %ip_a32v4f32 Input\n"
+		"%BP_gl_PerVertex = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+		"%BP_a3_gl_PerVertex = OpTypeArray %BP_gl_PerVertex %c_u32_3\n"
+		"%BP_op_a3_gl_PerVertex = OpTypePointer Output %BP_a3_gl_PerVertex\n"
+		"%BP_gl_out = OpVariable %BP_op_a3_gl_PerVertex Output\n"
+		"%BP_gl_PVOut = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+		"%BP_a32_gl_PVOut = OpTypeArray %BP_gl_PVOut %c_u32_32\n"
+		"%BP_ip_a32_gl_PVOut = OpTypePointer Input %BP_a32_gl_PVOut\n"
+		"%BP_gl_in = OpVariable %BP_ip_a32_gl_PVOut Input\n"
+		"%BP_gl_TessLevelOuter = OpVariable %op_a4f32 Output\n"
+		"%BP_gl_TessLevelInner = OpVariable %op_a2f32 Output\n"
+		"${pre_main:opt}\n"
+		"${IF_variable:opt}\n"
+
+		"%BP_main = OpFunction %void None %fun\n"
+		"%BP_label = OpLabel\n"
+		"%BP_gl_Invoc = OpLoad %i32 %BP_gl_InvocationID\n"
+		"${IF_carryforward:opt}\n"
+		"%BP_in_col_loc = OpAccessChain %ip_v4f32 %BP_in_color %BP_gl_Invoc\n"
+		"%BP_out_col_loc = OpAccessChain %op_v4f32 %BP_out_color %BP_gl_Invoc\n"
+		"%BP_in_col_val = OpLoad %v4f32 %BP_in_col_loc\n"
+		"%BP_clr_transformed = OpFunctionCall %v4f32 %test_code %BP_in_col_val\n"
+		"OpStore %BP_out_col_loc %BP_clr_transformed\n"
+
+		"%BP_in_pos_loc = OpAccessChain %ip_v4f32 %BP_gl_in %BP_gl_Invoc %c_i32_0\n"
+		"%BP_out_pos_loc = OpAccessChain %op_v4f32 %BP_gl_out %BP_gl_Invoc %c_i32_0\n"
+		"%BP_in_pos_val = OpLoad %v4f32 %BP_in_pos_loc\n"
+		"OpStore %BP_out_pos_loc %BP_in_pos_val\n"
+
+		"%BP_cmp = OpIEqual %bool %BP_gl_Invoc %c_i32_0\n"
+		"OpSelectionMerge %BP_merge_label None\n"
+		"OpBranchConditional %BP_cmp %BP_if_label %BP_merge_label\n"
+		"%BP_if_label = OpLabel\n"
+		"%BP_gl_TessLevelOuterPos_0 = OpAccessChain %op_f32 %BP_gl_TessLevelOuter %c_i32_0\n"
+		"%BP_gl_TessLevelOuterPos_1 = OpAccessChain %op_f32 %BP_gl_TessLevelOuter %c_i32_1\n"
+		"%BP_gl_TessLevelOuterPos_2 = OpAccessChain %op_f32 %BP_gl_TessLevelOuter %c_i32_2\n"
+		"%BP_gl_TessLevelInnerPos_0 = OpAccessChain %op_f32 %BP_gl_TessLevelInner %c_i32_0\n"
+		"OpStore %BP_gl_TessLevelOuterPos_0 %c_f32_1\n"
+		"OpStore %BP_gl_TessLevelOuterPos_1 %c_f32_1\n"
+		"OpStore %BP_gl_TessLevelOuterPos_2 %c_f32_1\n"
+		"OpStore %BP_gl_TessLevelInnerPos_0 %c_f32_1\n"
+		"OpBranch %BP_merge_label\n"
+		"%BP_merge_label = OpLabel\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+		"${interface_op_func:opt}\n"
+
+		"%isUniqueIdZero = OpFunction %bool None %bool_function\n"
+		"%getId_label = OpLabel\n"
+		"%invocation_id = OpLoad %i32 %BP_gl_InvocationID\n"
+		"%is_id_0 = OpIEqual %bool %invocation_id %c_i32_0\n"
+		"OpReturnValue %is_id_0\n"
+		"OpFunctionEnd\n"
+
+		"${testfun}\n";
+	return tcu::StringTemplate(tessControlShaderBoilerplate).specialize(fragments);
+}
+
+// Creates tess-evaluation-shader assembly by specializing a boilerplate
+// StringTemplate on fragments, which must (at least) map "testfun" to an
+// OpFunction definition for %test_code that takes and returns a %v4f32.
+// Boilerplate IDs are prefixed with "BP_" to avoid collisions with fragments.
+//
+// It roughly corresponds to the following glsl.
+//
+// #version 450
+//
+// layout(triangles, equal_spacing, ccw) in;
+// layout(location = 1) in vec4 in_color[];
+// layout(location = 1) out vec4 out_color;
+//
+// #define interpolate(val)
+//   vec4(gl_TessCoord.x) * val[0] + vec4(gl_TessCoord.y) * val[1] +
+//          vec4(gl_TessCoord.z) * val[2]
+//
+// void main() {
+//   gl_Position = vec4(gl_TessCoord.x) * gl_in[0].gl_Position +
+//                  vec4(gl_TessCoord.y) * gl_in[1].gl_Position +
+//                  vec4(gl_TessCoord.z) * gl_in[2].gl_Position;
+//   out_color = testfun(interpolate(in_color));
+// }
+string makeTessEvalShaderAssembly(const map<string, string>& fragments)
+{
+	static const char tessEvalBoilerplate[] =
+		"OpCapability Tessellation\n"
+		"OpCapability ClipDistance\n"
+		"OpCapability CullDistance\n"
+		"${capability:opt}\n"
+		"${extension:opt}\n"
+		"OpMemoryModel Logical GLSL450\n"
+		"OpEntryPoint TessellationEvaluation %BP_main \"main\" %BP_stream %BP_gl_TessCoord %BP_gl_PrimitiveID %BP_gl_in %BP_out_color %BP_in_color ${IF_entrypoint:opt} \n"
+		"OpExecutionMode %BP_main Triangles\n"
+		"OpExecutionMode %BP_main SpacingEqual\n"
+		"OpExecutionMode %BP_main VertexOrderCcw\n"
+		"${debug:opt}\n"
+		"OpName %BP_main \"main\"\n"
+		"OpName %test_code \"testfun(vf4;\"\n"
+		"OpName %BP_gl_PerVertexOut \"gl_PerVertex\"\n"
+		"OpMemberName %BP_gl_PerVertexOut 0 \"gl_Position\"\n"
+		"OpMemberName %BP_gl_PerVertexOut 1 \"gl_PointSize\"\n"
+		"OpMemberName %BP_gl_PerVertexOut 2 \"gl_ClipDistance\"\n"
+		"OpMemberName %BP_gl_PerVertexOut 3 \"gl_CullDistance\"\n"
+		"OpName %BP_stream \"\"\n"
+		"OpName %BP_gl_TessCoord \"gl_TessCoord\"\n"
+		"OpName %BP_gl_PerVertexIn \"gl_PerVertex\"\n"
+		"OpName %BP_gl_PrimitiveID \"gl_PrimitiveID\"\n"
+		"OpMemberName %BP_gl_PerVertexIn 0 \"gl_Position\"\n"
+		"OpMemberName %BP_gl_PerVertexIn 1 \"gl_PointSize\"\n"
+		"OpMemberName %BP_gl_PerVertexIn 2 \"gl_ClipDistance\"\n"
+		"OpMemberName %BP_gl_PerVertexIn 3 \"gl_CullDistance\"\n"
+		"OpName %BP_gl_in \"gl_in\"\n"
+		"OpName %BP_out_color \"out_color\"\n"
+		"OpName %BP_in_color \"in_color\"\n"
+		"OpMemberDecorate %BP_gl_PerVertexOut 0 BuiltIn Position\n"
+		"OpMemberDecorate %BP_gl_PerVertexOut 1 BuiltIn PointSize\n"
+		"OpMemberDecorate %BP_gl_PerVertexOut 2 BuiltIn ClipDistance\n"
+		"OpMemberDecorate %BP_gl_PerVertexOut 3 BuiltIn CullDistance\n"
+		"OpDecorate %BP_gl_PerVertexOut Block\n"
+		"OpDecorate %BP_gl_PrimitiveID BuiltIn PrimitiveId\n"
+		"OpDecorate %BP_gl_TessCoord BuiltIn TessCoord\n"
+		"OpMemberDecorate %BP_gl_PerVertexIn 0 BuiltIn Position\n"
+		"OpMemberDecorate %BP_gl_PerVertexIn 1 BuiltIn PointSize\n"
+		"OpMemberDecorate %BP_gl_PerVertexIn 2 BuiltIn ClipDistance\n"
+		"OpMemberDecorate %BP_gl_PerVertexIn 3 BuiltIn CullDistance\n"
+		"OpDecorate %BP_gl_PerVertexIn Block\n"
+		"OpDecorate %BP_out_color Location 1\n"
+		"OpDecorate %BP_in_color Location 1\n"
+		"${IF_decoration:opt}\n"
+		"${decoration:opt}\n"
+		SPIRV_ASSEMBLY_TYPES
+		SPIRV_ASSEMBLY_CONSTANTS
+		SPIRV_ASSEMBLY_ARRAYS
+		"%BP_gl_PerVertexOut = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+		"%BP_op_gl_PerVertexOut = OpTypePointer Output %BP_gl_PerVertexOut\n"
+		"%BP_stream = OpVariable %BP_op_gl_PerVertexOut Output\n"
+		"%BP_gl_TessCoord = OpVariable %ip_v3f32 Input\n"
+		"%BP_gl_PrimitiveID = OpVariable %op_i32 Input\n"
+		"%BP_gl_PerVertexIn = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+		"%BP_a32_gl_PerVertexIn = OpTypeArray %BP_gl_PerVertexIn %c_u32_32\n"
+		"%BP_ip_a32_gl_PerVertexIn = OpTypePointer Input %BP_a32_gl_PerVertexIn\n"
+		"%BP_gl_in = OpVariable %BP_ip_a32_gl_PerVertexIn Input\n"
+		"%BP_out_color = OpVariable %op_v4f32 Output\n"
+		"%BP_in_color = OpVariable %ip_a32v4f32 Input\n"
+		"${pre_main:opt}\n"
+		"${IF_variable:opt}\n"
+		"%BP_main = OpFunction %void None %fun\n"
+		"%BP_label = OpLabel\n"
+		"${IF_carryforward:opt}\n"
+		"%BP_gl_TC_0 = OpAccessChain %ip_f32 %BP_gl_TessCoord %c_u32_0\n"
+		"%BP_gl_TC_1 = OpAccessChain %ip_f32 %BP_gl_TessCoord %c_u32_1\n"
+		"%BP_gl_TC_2 = OpAccessChain %ip_f32 %BP_gl_TessCoord %c_u32_2\n"
+		"%BP_gl_in_gl_Pos_0 = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_0 %c_i32_0\n"
+		"%BP_gl_in_gl_Pos_1 = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_1 %c_i32_0\n"
+		"%BP_gl_in_gl_Pos_2 = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_2 %c_i32_0\n"
+
+		"%BP_gl_OPos = OpAccessChain %op_v4f32 %BP_stream %c_i32_0\n"
+		"%BP_in_color_0 = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_0\n"
+		"%BP_in_color_1 = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_1\n"
+		"%BP_in_color_2 = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_2\n"
+
+		"%BP_TC_W_0 = OpLoad %f32 %BP_gl_TC_0\n"
+		"%BP_TC_W_1 = OpLoad %f32 %BP_gl_TC_1\n"
+		"%BP_TC_W_2 = OpLoad %f32 %BP_gl_TC_2\n"
+		"%BP_v4f32_TC_0 = OpCompositeConstruct %v4f32 %BP_TC_W_0 %BP_TC_W_0 %BP_TC_W_0 %BP_TC_W_0\n"
+		"%BP_v4f32_TC_1 = OpCompositeConstruct %v4f32 %BP_TC_W_1 %BP_TC_W_1 %BP_TC_W_1 %BP_TC_W_1\n"
+		"%BP_v4f32_TC_2 = OpCompositeConstruct %v4f32 %BP_TC_W_2 %BP_TC_W_2 %BP_TC_W_2 %BP_TC_W_2\n"
+
+		"%BP_gl_IP_0 = OpLoad %v4f32 %BP_gl_in_gl_Pos_0\n"
+		"%BP_gl_IP_1 = OpLoad %v4f32 %BP_gl_in_gl_Pos_1\n"
+		"%BP_gl_IP_2 = OpLoad %v4f32 %BP_gl_in_gl_Pos_2\n"
+
+		"%BP_IP_W_0 = OpFMul %v4f32 %BP_v4f32_TC_0 %BP_gl_IP_0\n"
+		"%BP_IP_W_1 = OpFMul %v4f32 %BP_v4f32_TC_1 %BP_gl_IP_1\n"
+		"%BP_IP_W_2 = OpFMul %v4f32 %BP_v4f32_TC_2 %BP_gl_IP_2\n"
+
+		"%BP_pos_sum_0 = OpFAdd %v4f32 %BP_IP_W_0 %BP_IP_W_1\n"
+		"%BP_pos_sum_1 = OpFAdd %v4f32 %BP_pos_sum_0 %BP_IP_W_2\n"
+
+		"OpStore %BP_gl_OPos %BP_pos_sum_1\n"
+
+		"%BP_IC_0 = OpLoad %v4f32 %BP_in_color_0\n"
+		"%BP_IC_1 = OpLoad %v4f32 %BP_in_color_1\n"
+		"%BP_IC_2 = OpLoad %v4f32 %BP_in_color_2\n"
+
+		"%BP_IC_W_0 = OpFMul %v4f32 %BP_v4f32_TC_0 %BP_IC_0\n"
+		"%BP_IC_W_1 = OpFMul %v4f32 %BP_v4f32_TC_1 %BP_IC_1\n"
+		"%BP_IC_W_2 = OpFMul %v4f32 %BP_v4f32_TC_2 %BP_IC_2\n"
+
+		"%BP_col_sum_0 = OpFAdd %v4f32 %BP_IC_W_0 %BP_IC_W_1\n"
+		"%BP_col_sum_1 = OpFAdd %v4f32 %BP_col_sum_0 %BP_IC_W_2\n"
+
+		"%BP_clr_transformed = OpFunctionCall %v4f32 %test_code %BP_col_sum_1\n"
+
+		"OpStore %BP_out_color %BP_clr_transformed\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+		"${interface_op_func:opt}\n"
+
+		"%isUniqueIdZero = OpFunction %bool None %bool_function\n"
+		"%getId_label = OpLabel\n"
+		"%primitive_id = OpLoad %i32 %BP_gl_PrimitiveID\n"
+		"%is_id_0 = OpIEqual %bool %primitive_id %c_i32_0\n"
+		"OpReturnValue %is_id_0\n"
+		"OpFunctionEnd\n"
+
+		"${testfun}\n";
+	return tcu::StringTemplate(tessEvalBoilerplate).specialize(fragments);
+}
+
+// Creates geometry-shader assembly by specializing a boilerplate StringTemplate
+// on fragments, which must (at least) map "testfun" to an OpFunction definition
+// for %test_code that takes and returns a %v4f32.  Boilerplate IDs are prefixed
+// with "BP_" to avoid collisions with fragments.
+//
+// Derived from this GLSL:
+//
+// #version 450
+// layout(triangles) in;
+// layout(triangle_strip, max_vertices = 3) out;
+//
+// layout(location = 1) in vec4 in_color[];
+// layout(location = 1) out vec4 out_color;
+//
+// void main() {
+//   gl_Position = gl_in[0].gl_Position;
+//   out_color = test_fun(in_color[0]);
+//   EmitVertex();
+//   gl_Position = gl_in[1].gl_Position;
+//   out_color = test_fun(in_color[1]);
+//   EmitVertex();
+//   gl_Position = gl_in[2].gl_Position;
+//   out_color = test_fun(in_color[2]);
+//   EmitVertex();
+//   EndPrimitive();
+// }
+string makeGeometryShaderAssembly(const map<string, string>& fragments)
+{
+	static const char geometryShaderBoilerplate[] =
+		"OpCapability Geometry\n"
+		"OpCapability ClipDistance\n"
+		"OpCapability CullDistance\n"
+		"${capability:opt}\n"
+		"${extension:opt}\n"
+		"OpMemoryModel Logical GLSL450\n"
+		"OpEntryPoint Geometry %BP_main \"main\" %BP_out_gl_position %BP_gl_PrimitiveID %BP_gl_in %BP_out_color %BP_in_color ${IF_entrypoint:opt} \n"
+		"OpExecutionMode %BP_main Triangles\n"
+		"OpExecutionMode %BP_main OutputTriangleStrip\n"
+		"OpExecutionMode %BP_main OutputVertices 3\n"
+		"${debug:opt}\n"
+		"OpName %BP_main \"main\"\n"
+		"OpName %BP_gl_PrimitiveID \"gl_PrimitiveID\"\n"
+		"OpName %BP_per_vertex_in \"gl_PerVertex\"\n"
+		"OpMemberName %BP_per_vertex_in 0 \"gl_Position\"\n"
+		"OpMemberName %BP_per_vertex_in 1 \"gl_PointSize\"\n"
+		"OpMemberName %BP_per_vertex_in 2 \"gl_ClipDistance\"\n"
+		"OpMemberName %BP_per_vertex_in 3 \"gl_CullDistance\"\n"
+		"OpName %BP_gl_in \"gl_in\"\n"
+		"OpName %BP_out_color \"out_color\"\n"
+		"OpName %BP_in_color \"in_color\"\n"
+		"OpName %test_code \"testfun(vf4;\"\n"
+		"OpDecorate %BP_gl_PrimitiveID BuiltIn PrimitiveId\n"
+		"OpDecorate %BP_out_gl_position BuiltIn Position\n"
+		"OpMemberDecorate %BP_per_vertex_in 0 BuiltIn Position\n"
+		"OpMemberDecorate %BP_per_vertex_in 1 BuiltIn PointSize\n"
+		"OpMemberDecorate %BP_per_vertex_in 2 BuiltIn ClipDistance\n"
+		"OpMemberDecorate %BP_per_vertex_in 3 BuiltIn CullDistance\n"
+		"OpDecorate %BP_per_vertex_in Block\n"
+		"OpDecorate %BP_out_color Location 1\n"
+		"OpDecorate %BP_in_color Location 1\n"
+		"${IF_decoration:opt}\n"
+		"${decoration:opt}\n"
+		SPIRV_ASSEMBLY_TYPES
+		SPIRV_ASSEMBLY_CONSTANTS
+		SPIRV_ASSEMBLY_ARRAYS
+		"%BP_per_vertex_in = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+		"%BP_a3_per_vertex_in = OpTypeArray %BP_per_vertex_in %c_u32_3\n"
+		"%BP_ip_a3_per_vertex_in = OpTypePointer Input %BP_a3_per_vertex_in\n"
+
+		"%BP_gl_in = OpVariable %BP_ip_a3_per_vertex_in Input\n"
+		"%BP_out_color = OpVariable %op_v4f32 Output\n"
+		"%BP_in_color = OpVariable %ip_a3v4f32 Input\n"
+		"%BP_gl_PrimitiveID = OpVariable %ip_i32 Input\n"
+		"%BP_out_gl_position = OpVariable %op_v4f32 Output\n"
+		"${pre_main:opt}\n"
+		"${IF_variable:opt}\n"
+
+		"%BP_main = OpFunction %void None %fun\n"
+		"%BP_label = OpLabel\n"
+
+		"${IF_carryforward:opt}\n"
+
+		"%BP_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_0 %c_i32_0\n"
+		"%BP_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_1 %c_i32_0\n"
+		"%BP_gl_in_2_gl_position = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_2 %c_i32_0\n"
+
+		"%BP_in_position_0 = OpLoad %v4f32 %BP_gl_in_0_gl_position\n"
+		"%BP_in_position_1 = OpLoad %v4f32 %BP_gl_in_1_gl_position\n"
+		"%BP_in_position_2 = OpLoad %v4f32 %BP_gl_in_2_gl_position \n"
+
+		"%BP_in_color_0_ptr = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_0\n"
+		"%BP_in_color_1_ptr = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_1\n"
+		"%BP_in_color_2_ptr = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_2\n"
+
+		"%BP_in_color_0 = OpLoad %v4f32 %BP_in_color_0_ptr\n"
+		"%BP_in_color_1 = OpLoad %v4f32 %BP_in_color_1_ptr\n"
+		"%BP_in_color_2 = OpLoad %v4f32 %BP_in_color_2_ptr\n"
+
+		"%BP_transformed_in_color_0 = OpFunctionCall %v4f32 %test_code %BP_in_color_0\n"
+		"%BP_transformed_in_color_1 = OpFunctionCall %v4f32 %test_code %BP_in_color_1\n"
+		"%BP_transformed_in_color_2 = OpFunctionCall %v4f32 %test_code %BP_in_color_2\n"
+
+
+		"OpStore %BP_out_gl_position %BP_in_position_0\n"
+		"OpStore %BP_out_color %BP_transformed_in_color_0\n"
+		"OpEmitVertex\n"
+
+		"OpStore %BP_out_gl_position %BP_in_position_1\n"
+		"OpStore %BP_out_color %BP_transformed_in_color_1\n"
+		"OpEmitVertex\n"
+
+		"OpStore %BP_out_gl_position %BP_in_position_2\n"
+		"OpStore %BP_out_color %BP_transformed_in_color_2\n"
+		"OpEmitVertex\n"
+
+		"OpEndPrimitive\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+		"${interface_op_func:opt}\n"
+
+		"%isUniqueIdZero = OpFunction %bool None %bool_function\n"
+		"%getId_label = OpLabel\n"
+		"%primitive_id = OpLoad %i32 %BP_gl_PrimitiveID\n"
+		"%is_id_0 = OpIEqual %bool %primitive_id %c_i32_0\n"
+		"OpReturnValue %is_id_0\n"
+		"OpFunctionEnd\n"
+
+		"${testfun}\n";
+	return tcu::StringTemplate(geometryShaderBoilerplate).specialize(fragments);
+}
+
+// Creates fragment-shader assembly by specializing a boilerplate StringTemplate
+// on fragments, which must (at least) map "testfun" to an OpFunction definition
+// for %test_code that takes and returns a %v4f32.  Boilerplate IDs are prefixed
+// with "BP_" to avoid collisions with fragments.
+//
+// Derived from this GLSL:
+//
+// layout(location = 1) in highp vec4 vtxColor;
+// layout(location = 0) out highp vec4 fragColor;
+// highp vec4 testfun(highp vec4 x) { return x; }
+// void main(void) { fragColor = testfun(vtxColor); }
+//
+// with modifications including passing vtxColor by value and ripping out
+// testfun() definition.
+string makeFragmentShaderAssembly(const map<string, string>& fragments)
+{
+	static const char fragmentShaderBoilerplate[] =
+		"OpCapability Shader\n"
+		"${capability:opt}\n"
+		"${extension:opt}\n"
+		"OpMemoryModel Logical GLSL450\n"
+		"OpEntryPoint Fragment %BP_main \"main\" %BP_vtxColor %BP_fragColor %BP_gl_FragCoord ${IF_entrypoint:opt} \n"
+		"OpExecutionMode %BP_main OriginUpperLeft\n"
+		"${debug:opt}\n"
+		"OpName %BP_main \"main\"\n"
+		"OpName %BP_gl_FragCoord \"fragCoord\"\n"
+		"OpName %BP_fragColor \"fragColor\"\n"
+		"OpName %BP_vtxColor \"vtxColor\"\n"
+		"OpName %test_code \"testfun(vf4;\"\n"
+		"OpDecorate %BP_fragColor Location 0\n"
+		"OpDecorate %BP_vtxColor Location 1\n"
+		"OpDecorate %BP_gl_FragCoord BuiltIn FragCoord\n"
+		"${IF_decoration:opt}\n"
+		"${decoration:opt}\n"
+		SPIRV_ASSEMBLY_TYPES
+		SPIRV_ASSEMBLY_CONSTANTS
+		SPIRV_ASSEMBLY_ARRAYS
+		"%BP_gl_FragCoord = OpVariable %ip_v4f32 Input\n"
+		"%BP_fragColor = OpVariable %op_v4f32 Output\n"
+		"%BP_vtxColor = OpVariable %ip_v4f32 Input\n"
+		"${pre_main:opt}\n"
+		"${IF_variable:opt}\n"
+		"%BP_main = OpFunction %void None %fun\n"
+		"%BP_label_main = OpLabel\n"
+		"${IF_carryforward:opt}\n"
+		"%BP_tmp1 = OpLoad %v4f32 %BP_vtxColor\n"
+		"%BP_tmp2 = OpFunctionCall %v4f32 %test_code %BP_tmp1\n"
+		"OpStore %BP_fragColor %BP_tmp2\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+		"${interface_op_func:opt}\n"
+
+		"%isUniqueIdZero = OpFunction %bool None %bool_function\n"
+		"%getId_label = OpLabel\n"
+		"%loc_x_coord = OpAccessChain %ip_f32 %BP_gl_FragCoord %c_i32_0\n"
+		"%loc_y_coord = OpAccessChain %ip_f32 %BP_gl_FragCoord %c_i32_1\n"
+		"%x_coord = OpLoad %f32 %loc_x_coord\n"
+		"%y_coord = OpLoad %f32 %loc_y_coord\n"
+		"%is_x_idx0 = OpFOrdEqual %bool %x_coord %c_f32_0_5\n"
+		"%is_y_idx0 = OpFOrdEqual %bool %y_coord %c_f32_0_5\n"
+		"%is_frag_0 = OpLogicalAnd %bool %is_x_idx0 %is_y_idx0\n"
+		"OpReturnValue %is_frag_0\n"
+		"OpFunctionEnd\n"
+
+		"${testfun}\n";
+	return tcu::StringTemplate(fragmentShaderBoilerplate).specialize(fragments);
+}
+
+// Creates mappings from placeholders to pass-through shader code which copies
+// the input to the output faithfully.
+map<string, string> passthruInterface(const IFDataType& data_type)
+{
+	const string		var_type	= data_type.str();
+	map<string, string>	fragments	= passthruFragments();
+	const string		functype	= string("%") + var_type + "_" + var_type + "_function";
+
+	fragments["interface_op_func"]	=
+		string("%interface_op_func = OpFunction %") + var_type + " None " + functype + "\n"
+		"               %io_param1 = OpFunctionParameter %" + var_type + "\n"
+		"                %IF_label = OpLabel\n"
+		"                            OpReturnValue %io_param1\n"
+		"                            OpFunctionEnd\n";
+	fragments["input_type"]			= var_type;
+	fragments["output_type"]		= var_type;
+	fragments["pre_main"]			= "";
+
+	if (!data_type.elementIs32bit())
+	{
+		if (data_type.elementType == NUMBERTYPE_FLOAT16)
+		{
+			fragments["pre_main"]	+= "%f16 = OpTypeFloat 16\n";
+		}
+		else if (data_type.elementType == NUMBERTYPE_INT16)
+		{
+			fragments["pre_main"]	+= "%i16 = OpTypeInt 16 1\n";
+		}
+		else
+		{
+			fragments["pre_main"]	+= "%u16 = OpTypeInt 16 0\n";
+		}
+
+		fragments["capability"]		= "OpCapability StorageInputOutput16\n";
+
+		if (data_type.isVector())
+		{
+			fragments["pre_main"]	+= "%" + var_type + " = OpTypeVector %" + IFDataType(1, data_type.elementType).str() + " " + numberToString(data_type.numElements) + "\n";
+		}
+
+		fragments["pre_main"]		+=
+			"%ip_" + var_type + " = OpTypePointer Input %" + var_type + "\n"
+			"%op_" + var_type + " = OpTypePointer Output %" + var_type + "\n";
+	}
+
+	fragments["pre_main"]			+=
+		functype + " = OpTypeFunction %" + var_type + " %" + var_type + "\n"
+		"%a3" + var_type + " = OpTypeArray %" + var_type + " %c_i32_3\n"
+		"%ip_a3" + var_type + " = OpTypePointer Input %a3" + var_type + "\n"
+		"%op_a3" + var_type + " = OpTypePointer Output %a3" + var_type + "\n";
+
+	return fragments;
+}
+
+// Returns mappings from interface placeholders to their concrete values.
+//
+// The concrete values should be specialized again to provide ${input_type}
+// and ${output_type}.
+//
+// %ip_${input_type} and %op_${output_type} should also be defined in the final code.
+map<string, string> fillInterfacePlaceholderVert (void)
+{
+	map<string, string>	fragments	;
+
+	fragments["IF_entrypoint"]		= "%IF_input %IF_output";
+	fragments["IF_variable"]		=
+		" %IF_input = OpVariable %ip_${input_type} Input\n"
+		"%IF_output = OpVariable %op_${output_type} Output\n";
+	fragments["IF_decoration"]		=
+		"OpDecorate  %IF_input Location 2\n"
+		"OpDecorate %IF_output Location 2\n";
+	fragments["IF_carryforward"]	=
+		"%IF_input_val = OpLoad %${input_type} %IF_input\n"
+		"   %IF_result = OpFunctionCall %${output_type} %interface_op_func %IF_input_val\n"
+		"                OpStore %IF_output %IF_result\n";
+
+	// Make sure the rest still need to be instantialized.
+	fragments["capability"]			= "${capability:opt}";
+	fragments["extension"]			= "${extension:opt}";
+	fragments["debug"]				= "${debug:opt}";
+	fragments["decoration"]			= "${decoration:opt}";
+	fragments["pre_main"]			= "${pre_main:opt}";
+	fragments["testfun"]			= "${testfun}";
+	fragments["interface_op_func"]	= "${interface_op_func}";
+
+	return fragments;
+}
+
+// Returns mappings from interface placeholders to their concrete values.
+//
+// The concrete values should be specialized again to provide ${input_type}
+// and ${output_type}.
+//
+// %ip_${input_type} and %op_${output_type} should also be defined in the final code.
+map<string, string> fillInterfacePlaceholderFrag (void)
+{
+	map<string, string>	fragments	;
+
+	fragments["IF_entrypoint"]		= "%IF_input %IF_output";
+	fragments["IF_variable"]		=
+		" %IF_input = OpVariable %ip_${input_type} Input\n"
+		"%IF_output = OpVariable %op_${output_type} Output\n";
+	fragments["IF_decoration"]		=
+		"OpDecorate  %IF_input Location 2\n"
+		"OpDecorate %IF_output Location 1\n";  // Fragment shader should write to location #1.
+	fragments["IF_carryforward"]	=
+		"%IF_input_val = OpLoad %${input_type} %IF_input\n"
+		"   %IF_result = OpFunctionCall %${output_type} %interface_op_func %IF_input_val\n"
+		"                OpStore %IF_output %IF_result\n";
+
+	// Make sure the rest still need to be instantialized.
+	fragments["capability"]			= "${capability:opt}";
+	fragments["extension"]			= "${extension:opt}";
+	fragments["debug"]				= "${debug:opt}";
+	fragments["decoration"]			= "${decoration:opt}";
+	fragments["pre_main"]			= "${pre_main:opt}";
+	fragments["testfun"]			= "${testfun}";
+	fragments["interface_op_func"]	= "${interface_op_func}";
+
+	return fragments;
+}
+
+// Returns mappings from interface placeholders to their concrete values.
+//
+// The concrete values should be specialized again to provide ${input_type}
+// and ${output_type}.
+//
+// %ip_${input_type}, %op_${output_type}, %ip_a3${input_type}, and $op_a3${output_type}
+// should also be defined in the final code.
+map<string, string> fillInterfacePlaceholderTessCtrl (void)
+{
+	map<string, string>	fragments	;
+
+	fragments["IF_entrypoint"]		= "%IF_input %IF_output";
+	fragments["IF_variable"]		=
+		" %IF_input = OpVariable %ip_a3${input_type} Input\n"
+		"%IF_output = OpVariable %op_a3${output_type} Output\n";
+	fragments["IF_decoration"]		=
+		"OpDecorate  %IF_input Location 2\n"
+		"OpDecorate %IF_output Location 2\n";
+	fragments["IF_carryforward"]	=
+		" %IF_input_ptr0 = OpAccessChain %ip_${input_type} %IF_input %c_i32_0\n"
+		" %IF_input_ptr1 = OpAccessChain %ip_${input_type} %IF_input %c_i32_1\n"
+		" %IF_input_ptr2 = OpAccessChain %ip_${input_type} %IF_input %c_i32_2\n"
+		"%IF_output_ptr0 = OpAccessChain %op_${output_type} %IF_output %c_i32_0\n"
+		"%IF_output_ptr1 = OpAccessChain %op_${output_type} %IF_output %c_i32_1\n"
+		"%IF_output_ptr2 = OpAccessChain %op_${output_type} %IF_output %c_i32_2\n"
+		"%IF_input_val0 = OpLoad %${input_type} %IF_input_ptr0\n"
+		"%IF_input_val1 = OpLoad %${input_type} %IF_input_ptr1\n"
+		"%IF_input_val2 = OpLoad %${input_type} %IF_input_ptr2\n"
+		"%IF_input_res0 = OpFunctionCall %${output_type} %interface_op_func %IF_input_val0\n"
+		"%IF_input_res1 = OpFunctionCall %${output_type} %interface_op_func %IF_input_val1\n"
+		"%IF_input_res2 = OpFunctionCall %${output_type} %interface_op_func %IF_input_val2\n"
+		"OpStore %IF_output_ptr0 %IF_input_res0\n"
+		"OpStore %IF_output_ptr1 %IF_input_res1\n"
+		"OpStore %IF_output_ptr2 %IF_input_res2\n";
+
+	// Make sure the rest still need to be instantialized.
+	fragments["capability"]			= "${capability:opt}";
+	fragments["extension"]			= "${extension:opt}";
+	fragments["debug"]				= "${debug:opt}";
+	fragments["decoration"]			= "${decoration:opt}";
+	fragments["pre_main"]			= "${pre_main:opt}";
+	fragments["testfun"]			= "${testfun}";
+	fragments["interface_op_func"]	= "${interface_op_func}";
+
+	return fragments;
+}
+
+// Returns mappings from interface placeholders to their concrete values.
+//
+// The concrete values should be specialized again to provide ${input_type}
+// and ${output_type}.
+//
+// %ip_${input_type}, %op_${output_type}, %ip_a3${input_type}, and $op_a3${output_type}
+// should also be defined in the final code.
+map<string, string> fillInterfacePlaceholderTessEvalGeom (void)
+{
+	map<string, string>	fragments	;
+
+	fragments["IF_entrypoint"]		= "%IF_input %IF_output";
+	fragments["IF_variable"]		=
+		" %IF_input = OpVariable %ip_a3${input_type} Input\n"
+		"%IF_output = OpVariable %op_${output_type} Output\n";
+	fragments["IF_decoration"]		=
+		"OpDecorate  %IF_input Location 2\n"
+		"OpDecorate %IF_output Location 2\n";
+	fragments["IF_carryforward"]	=
+		// Only get the first value since all three values are the same anyway.
+		" %IF_input_ptr0 = OpAccessChain %ip_${input_type} %IF_input %c_i32_0\n"
+		" %IF_input_val0 = OpLoad %${input_type} %IF_input_ptr0\n"
+		" %IF_input_res0 = OpFunctionCall %${output_type} %interface_op_func %IF_input_val0\n"
+		"OpStore %IF_output %IF_input_res0\n";
+
+	// Make sure the rest still need to be instantialized.
+	fragments["capability"]			= "${capability:opt}";
+	fragments["extension"]			= "${extension:opt}";
+	fragments["debug"]				= "${debug:opt}";
+	fragments["decoration"]			= "${decoration:opt}";
+	fragments["pre_main"]			= "${pre_main:opt}";
+	fragments["testfun"]			= "${testfun}";
+	fragments["interface_op_func"]	= "${interface_op_func}";
+
+	return fragments;
+}
+
+map<string, string> passthruFragments(void)
+{
+	map<string, string> fragments;
+	fragments["testfun"] =
+		// A %test_code function that returns its argument unchanged.
+		"%test_code = OpFunction %v4f32 None %v4f32_function\n"
+		"%param1 = OpFunctionParameter %v4f32\n"
+		"%label_testfun = OpLabel\n"
+		"OpReturnValue %param1\n"
+		"OpFunctionEnd\n";
+	return fragments;
+}
+
+// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
+// Vertex shader gets custom code from context, the rest are pass-through.
+void addShaderCodeCustomVertex(vk::SourceCollections& dst, InstanceContext context)
+{
+	if (!context.interfaces.empty())
+	{
+		// Inject boilerplate code to wire up additional input/output variables between stages.
+		// Just copy the contents in input variable to output variable in all stages except
+		// the customized stage.
+		dst.spirvAsmSources.add("vert") << StringTemplate(makeVertexShaderAssembly(fillInterfacePlaceholderVert())).specialize(context.testCodeFragments);
+		dst.spirvAsmSources.add("frag") << StringTemplate(makeFragmentShaderAssembly(fillInterfacePlaceholderFrag())).specialize(passthruInterface(context.interfaces.getOutputType()));
+	} else {
+		map<string, string> passthru = passthruFragments();
+
+		dst.spirvAsmSources.add("vert") << makeVertexShaderAssembly(context.testCodeFragments);
+		dst.spirvAsmSources.add("frag") << makeFragmentShaderAssembly(passthru);
+	}
+}
+
+// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
+// Tessellation control shader gets custom code from context, the rest are
+// pass-through.
+void addShaderCodeCustomTessControl(vk::SourceCollections& dst, InstanceContext context)
+{
+	if (!context.interfaces.empty())
+	{
+		// Inject boilerplate code to wire up additional input/output variables between stages.
+		// Just copy the contents in input variable to output variable in all stages except
+		// the customized stage.
+		dst.spirvAsmSources.add("vert") << StringTemplate(makeVertexShaderAssembly(fillInterfacePlaceholderVert())).specialize(passthruInterface(context.interfaces.getInputType()));
+		dst.spirvAsmSources.add("tessc") << StringTemplate(makeTessControlShaderAssembly(fillInterfacePlaceholderTessCtrl())).specialize(context.testCodeFragments);
+		dst.spirvAsmSources.add("tesse") << StringTemplate(makeTessEvalShaderAssembly(fillInterfacePlaceholderTessEvalGeom())).specialize(passthruInterface(context.interfaces.getOutputType()));
+		dst.spirvAsmSources.add("frag") << StringTemplate(makeFragmentShaderAssembly(fillInterfacePlaceholderFrag())).specialize(passthruInterface(context.interfaces.getOutputType()));
+	}
+	else
+	{
+		map<string, string> passthru = passthruFragments();
+
+		dst.spirvAsmSources.add("vert") << makeVertexShaderAssembly(passthru);
+		dst.spirvAsmSources.add("tessc") << makeTessControlShaderAssembly(context.testCodeFragments);
+		dst.spirvAsmSources.add("tesse") << makeTessEvalShaderAssembly(passthru);
+		dst.spirvAsmSources.add("frag") << makeFragmentShaderAssembly(passthru);
+	}
+}
+
+// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
+// Tessellation evaluation shader gets custom code from context, the rest are
+// pass-through.
+void addShaderCodeCustomTessEval(vk::SourceCollections& dst, InstanceContext context)
+{
+	if (!context.interfaces.empty())
+	{
+		// Inject boilerplate code to wire up additional input/output variables between stages.
+		// Just copy the contents in input variable to output variable in all stages except
+		// the customized stage.
+		dst.spirvAsmSources.add("vert") << StringTemplate(makeVertexShaderAssembly(fillInterfacePlaceholderVert())).specialize(passthruInterface(context.interfaces.getInputType()));
+		dst.spirvAsmSources.add("tessc") << StringTemplate(makeTessControlShaderAssembly(fillInterfacePlaceholderTessCtrl())).specialize(passthruInterface(context.interfaces.getInputType()));
+		dst.spirvAsmSources.add("tesse") << StringTemplate(makeTessEvalShaderAssembly(fillInterfacePlaceholderTessEvalGeom())).specialize(context.testCodeFragments);
+		dst.spirvAsmSources.add("frag") << StringTemplate(makeFragmentShaderAssembly(fillInterfacePlaceholderFrag())).specialize(passthruInterface(context.interfaces.getOutputType()));
+	}
+	else
+	{
+		map<string, string> passthru = passthruFragments();
+		dst.spirvAsmSources.add("vert") << makeVertexShaderAssembly(passthru);
+		dst.spirvAsmSources.add("tessc") << makeTessControlShaderAssembly(passthru);
+		dst.spirvAsmSources.add("tesse") << makeTessEvalShaderAssembly(context.testCodeFragments);
+		dst.spirvAsmSources.add("frag") << makeFragmentShaderAssembly(passthru);
+	}
+}
+
+// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
+// Geometry shader gets custom code from context, the rest are pass-through.
+void addShaderCodeCustomGeometry(vk::SourceCollections& dst, InstanceContext context)
+{
+	if (!context.interfaces.empty())
+	{
+		// Inject boilerplate code to wire up additional input/output variables between stages.
+		// Just copy the contents in input variable to output variable in all stages except
+		// the customized stage.
+		dst.spirvAsmSources.add("vert") << StringTemplate(makeVertexShaderAssembly(fillInterfacePlaceholderVert())).specialize(passthruInterface(context.interfaces.getInputType()));
+		dst.spirvAsmSources.add("geom") << StringTemplate(makeGeometryShaderAssembly(fillInterfacePlaceholderTessEvalGeom())).specialize(context.testCodeFragments);
+		dst.spirvAsmSources.add("frag") << StringTemplate(makeFragmentShaderAssembly(fillInterfacePlaceholderFrag())).specialize(passthruInterface(context.interfaces.getOutputType()));
+	}
+	else
+	{
+		map<string, string> passthru = passthruFragments();
+		dst.spirvAsmSources.add("vert") << makeVertexShaderAssembly(passthru);
+		dst.spirvAsmSources.add("geom") << makeGeometryShaderAssembly(context.testCodeFragments);
+		dst.spirvAsmSources.add("frag") << makeFragmentShaderAssembly(passthru);
+	}
+}
+
+// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
+// Fragment shader gets custom code from context, the rest are pass-through.
+void addShaderCodeCustomFragment(vk::SourceCollections& dst, InstanceContext context)
+{
+	if (!context.interfaces.empty())
+	{
+		// Inject boilerplate code to wire up additional input/output variables between stages.
+		// Just copy the contents in input variable to output variable in all stages except
+		// the customized stage.
+		dst.spirvAsmSources.add("vert") << StringTemplate(makeVertexShaderAssembly(fillInterfacePlaceholderVert())).specialize(passthruInterface(context.interfaces.getInputType()));
+		dst.spirvAsmSources.add("frag") << StringTemplate(makeFragmentShaderAssembly(fillInterfacePlaceholderFrag())).specialize(context.testCodeFragments);
+	}
+	else
+	{
+		map<string, string> passthru = passthruFragments();
+		dst.spirvAsmSources.add("vert") << makeVertexShaderAssembly(passthru);
+		dst.spirvAsmSources.add("frag") << makeFragmentShaderAssembly(context.testCodeFragments);
+	}
+}
+
+void createCombinedModule(vk::SourceCollections& dst, InstanceContext)
+{
+	// \todo [2015-12-07 awoloszyn] Make tessellation / geometry conditional
+	// \todo [2015-12-07 awoloszyn] Remove OpName and OpMemberName at some point
+	dst.spirvAsmSources.add("module") <<
+		"OpCapability Shader\n"
+		"OpCapability ClipDistance\n"
+		"OpCapability CullDistance\n"
+		"OpCapability Geometry\n"
+		"OpCapability Tessellation\n"
+		"OpMemoryModel Logical GLSL450\n"
+
+		"OpEntryPoint Vertex %vert_main \"main\" %vert_Position %vert_vtxColor %vert_color %vert_vtxPosition %vert_vertex_id %vert_instance_id\n"
+		"OpEntryPoint Geometry %geom_main \"main\" %geom_out_gl_position %geom_gl_in %geom_out_color %geom_in_color\n"
+		"OpEntryPoint TessellationControl %tessc_main \"main\" %tessc_out_color %tessc_gl_InvocationID %tessc_in_color %tessc_out_position %tessc_in_position %tessc_gl_TessLevelOuter %tessc_gl_TessLevelInner\n"
+		"OpEntryPoint TessellationEvaluation %tesse_main \"main\" %tesse_stream %tesse_gl_tessCoord %tesse_in_position %tesse_out_color %tesse_in_color \n"
+		"OpEntryPoint Fragment %frag_main \"main\" %frag_vtxColor %frag_fragColor\n"
+
+		"OpExecutionMode %geom_main Triangles\n"
+		"OpExecutionMode %geom_main OutputTriangleStrip\n"
+		"OpExecutionMode %geom_main OutputVertices 3\n"
+
+		"OpExecutionMode %tessc_main OutputVertices 3\n"
+
+		"OpExecutionMode %tesse_main Triangles\n"
+		"OpExecutionMode %tesse_main SpacingEqual\n"
+		"OpExecutionMode %tesse_main VertexOrderCcw\n"
+
+		"OpExecutionMode %frag_main OriginUpperLeft\n"
+
+		"OpName %vert_main \"main\"\n"
+		"OpName %vert_vtxPosition \"vtxPosition\"\n"
+		"OpName %vert_Position \"position\"\n"
+		"OpName %vert_vtxColor \"vtxColor\"\n"
+		"OpName %vert_color \"color\"\n"
+		"OpName %vert_vertex_id \"gl_VertexIndex\"\n"
+		"OpName %vert_instance_id \"gl_InstanceIndex\"\n"
+		"OpName %geom_main \"main\"\n"
+		"OpName %geom_per_vertex_in \"gl_PerVertex\"\n"
+		"OpMemberName %geom_per_vertex_in 0 \"gl_Position\"\n"
+		"OpMemberName %geom_per_vertex_in 1 \"gl_PointSize\"\n"
+		"OpMemberName %geom_per_vertex_in 2 \"gl_ClipDistance\"\n"
+		"OpMemberName %geom_per_vertex_in 3 \"gl_CullDistance\"\n"
+		"OpName %geom_gl_in \"gl_in\"\n"
+		"OpName %geom_out_color \"out_color\"\n"
+		"OpName %geom_in_color \"in_color\"\n"
+		"OpName %tessc_main \"main\"\n"
+		"OpName %tessc_out_color \"out_color\"\n"
+		"OpName %tessc_gl_InvocationID \"gl_InvocationID\"\n"
+		"OpName %tessc_in_color \"in_color\"\n"
+		"OpName %tessc_out_position \"out_position\"\n"
+		"OpName %tessc_in_position \"in_position\"\n"
+		"OpName %tessc_gl_TessLevelOuter \"gl_TessLevelOuter\"\n"
+		"OpName %tessc_gl_TessLevelInner \"gl_TessLevelInner\"\n"
+		"OpName %tesse_main \"main\"\n"
+		"OpName %tesse_per_vertex_out \"gl_PerVertex\"\n"
+		"OpMemberName %tesse_per_vertex_out 0 \"gl_Position\"\n"
+		"OpMemberName %tesse_per_vertex_out 1 \"gl_PointSize\"\n"
+		"OpMemberName %tesse_per_vertex_out 2 \"gl_ClipDistance\"\n"
+		"OpMemberName %tesse_per_vertex_out 3 \"gl_CullDistance\"\n"
+		"OpName %tesse_stream \"\"\n"
+		"OpName %tesse_gl_tessCoord \"gl_TessCoord\"\n"
+		"OpName %tesse_in_position \"in_position\"\n"
+		"OpName %tesse_out_color \"out_color\"\n"
+		"OpName %tesse_in_color \"in_color\"\n"
+		"OpName %frag_main \"main\"\n"
+		"OpName %frag_fragColor \"fragColor\"\n"
+		"OpName %frag_vtxColor \"vtxColor\"\n"
+
+		"; Vertex decorations\n"
+		"OpDecorate %vert_vtxPosition Location 2\n"
+		"OpDecorate %vert_Position Location 0\n"
+		"OpDecorate %vert_vtxColor Location 1\n"
+		"OpDecorate %vert_color Location 1\n"
+		"OpDecorate %vert_vertex_id BuiltIn VertexIndex\n"
+		"OpDecorate %vert_instance_id BuiltIn InstanceIndex\n"
+
+		"; Geometry decorations\n"
+		"OpDecorate %geom_out_gl_position BuiltIn Position\n"
+		"OpMemberDecorate %geom_per_vertex_in 0 BuiltIn Position\n"
+		"OpMemberDecorate %geom_per_vertex_in 1 BuiltIn PointSize\n"
+		"OpMemberDecorate %geom_per_vertex_in 2 BuiltIn ClipDistance\n"
+		"OpMemberDecorate %geom_per_vertex_in 3 BuiltIn CullDistance\n"
+		"OpDecorate %geom_per_vertex_in Block\n"
+		"OpDecorate %geom_out_color Location 1\n"
+		"OpDecorate %geom_in_color Location 1\n"
+
+		"; Tessellation Control decorations\n"
+		"OpDecorate %tessc_out_color Location 1\n"
+		"OpDecorate %tessc_gl_InvocationID BuiltIn InvocationId\n"
+		"OpDecorate %tessc_in_color Location 1\n"
+		"OpDecorate %tessc_out_position Location 2\n"
+		"OpDecorate %tessc_in_position Location 2\n"
+		"OpDecorate %tessc_gl_TessLevelOuter Patch\n"
+		"OpDecorate %tessc_gl_TessLevelOuter BuiltIn TessLevelOuter\n"
+		"OpDecorate %tessc_gl_TessLevelInner Patch\n"
+		"OpDecorate %tessc_gl_TessLevelInner BuiltIn TessLevelInner\n"
+
+		"; Tessellation Evaluation decorations\n"
+		"OpMemberDecorate %tesse_per_vertex_out 0 BuiltIn Position\n"
+		"OpMemberDecorate %tesse_per_vertex_out 1 BuiltIn PointSize\n"
+		"OpMemberDecorate %tesse_per_vertex_out 2 BuiltIn ClipDistance\n"
+		"OpMemberDecorate %tesse_per_vertex_out 3 BuiltIn CullDistance\n"
+		"OpDecorate %tesse_per_vertex_out Block\n"
+		"OpDecorate %tesse_gl_tessCoord BuiltIn TessCoord\n"
+		"OpDecorate %tesse_in_position Location 2\n"
+		"OpDecorate %tesse_out_color Location 1\n"
+		"OpDecorate %tesse_in_color Location 1\n"
+
+		"; Fragment decorations\n"
+		"OpDecorate %frag_fragColor Location 0\n"
+		"OpDecorate %frag_vtxColor Location 1\n"
+
+		SPIRV_ASSEMBLY_TYPES
+		SPIRV_ASSEMBLY_CONSTANTS
+		SPIRV_ASSEMBLY_ARRAYS
+
+		"; Vertex Variables\n"
+		"%vert_vtxPosition = OpVariable %op_v4f32 Output\n"
+		"%vert_Position = OpVariable %ip_v4f32 Input\n"
+		"%vert_vtxColor = OpVariable %op_v4f32 Output\n"
+		"%vert_color = OpVariable %ip_v4f32 Input\n"
+		"%vert_vertex_id = OpVariable %ip_i32 Input\n"
+		"%vert_instance_id = OpVariable %ip_i32 Input\n"
+
+		"; Geometry Variables\n"
+		"%geom_per_vertex_in = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+		"%geom_a3_per_vertex_in = OpTypeArray %geom_per_vertex_in %c_u32_3\n"
+		"%geom_ip_a3_per_vertex_in = OpTypePointer Input %geom_a3_per_vertex_in\n"
+		"%geom_gl_in = OpVariable %geom_ip_a3_per_vertex_in Input\n"
+		"%geom_out_color = OpVariable %op_v4f32 Output\n"
+		"%geom_in_color = OpVariable %ip_a3v4f32 Input\n"
+		"%geom_out_gl_position = OpVariable %op_v4f32 Output\n"
+
+		"; Tessellation Control Variables\n"
+		"%tessc_out_color = OpVariable %op_a3v4f32 Output\n"
+		"%tessc_gl_InvocationID = OpVariable %ip_i32 Input\n"
+		"%tessc_in_color = OpVariable %ip_a32v4f32 Input\n"
+		"%tessc_out_position = OpVariable %op_a3v4f32 Output\n"
+		"%tessc_in_position = OpVariable %ip_a32v4f32 Input\n"
+		"%tessc_gl_TessLevelOuter = OpVariable %op_a4f32 Output\n"
+		"%tessc_gl_TessLevelInner = OpVariable %op_a2f32 Output\n"
+
+		"; Tessellation Evaluation Decorations\n"
+		"%tesse_per_vertex_out = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+		"%tesse_op_per_vertex_out = OpTypePointer Output %tesse_per_vertex_out\n"
+		"%tesse_stream = OpVariable %tesse_op_per_vertex_out Output\n"
+		"%tesse_gl_tessCoord = OpVariable %ip_v3f32 Input\n"
+		"%tesse_in_position = OpVariable %ip_a32v4f32 Input\n"
+		"%tesse_out_color = OpVariable %op_v4f32 Output\n"
+		"%tesse_in_color = OpVariable %ip_a32v4f32 Input\n"
+
+		"; Fragment Variables\n"
+		"%frag_fragColor = OpVariable %op_v4f32 Output\n"
+		"%frag_vtxColor = OpVariable %ip_v4f32 Input\n"
+
+		"; Vertex Entry\n"
+		"%vert_main = OpFunction %void None %fun\n"
+		"%vert_label = OpLabel\n"
+		"%vert_tmp_position = OpLoad %v4f32 %vert_Position\n"
+		"OpStore %vert_vtxPosition %vert_tmp_position\n"
+		"%vert_tmp_color = OpLoad %v4f32 %vert_color\n"
+		"OpStore %vert_vtxColor %vert_tmp_color\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+
+		"; Geometry Entry\n"
+		"%geom_main = OpFunction %void None %fun\n"
+		"%geom_label = OpLabel\n"
+		"%geom_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %geom_gl_in %c_i32_0 %c_i32_0\n"
+		"%geom_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %geom_gl_in %c_i32_1 %c_i32_0\n"
+		"%geom_gl_in_2_gl_position = OpAccessChain %ip_v4f32 %geom_gl_in %c_i32_2 %c_i32_0\n"
+		"%geom_in_position_0 = OpLoad %v4f32 %geom_gl_in_0_gl_position\n"
+		"%geom_in_position_1 = OpLoad %v4f32 %geom_gl_in_1_gl_position\n"
+		"%geom_in_position_2 = OpLoad %v4f32 %geom_gl_in_2_gl_position \n"
+		"%geom_in_color_0_ptr = OpAccessChain %ip_v4f32 %geom_in_color %c_i32_0\n"
+		"%geom_in_color_1_ptr = OpAccessChain %ip_v4f32 %geom_in_color %c_i32_1\n"
+		"%geom_in_color_2_ptr = OpAccessChain %ip_v4f32 %geom_in_color %c_i32_2\n"
+		"%geom_in_color_0 = OpLoad %v4f32 %geom_in_color_0_ptr\n"
+		"%geom_in_color_1 = OpLoad %v4f32 %geom_in_color_1_ptr\n"
+		"%geom_in_color_2 = OpLoad %v4f32 %geom_in_color_2_ptr\n"
+		"OpStore %geom_out_gl_position %geom_in_position_0\n"
+		"OpStore %geom_out_color %geom_in_color_0\n"
+		"OpEmitVertex\n"
+		"OpStore %geom_out_gl_position %geom_in_position_1\n"
+		"OpStore %geom_out_color %geom_in_color_1\n"
+		"OpEmitVertex\n"
+		"OpStore %geom_out_gl_position %geom_in_position_2\n"
+		"OpStore %geom_out_color %geom_in_color_2\n"
+		"OpEmitVertex\n"
+		"OpEndPrimitive\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+
+		"; Tessellation Control Entry\n"
+		"%tessc_main = OpFunction %void None %fun\n"
+		"%tessc_label = OpLabel\n"
+		"%tessc_invocation_id = OpLoad %i32 %tessc_gl_InvocationID\n"
+		"%tessc_in_color_ptr = OpAccessChain %ip_v4f32 %tessc_in_color %tessc_invocation_id\n"
+		"%tessc_in_position_ptr = OpAccessChain %ip_v4f32 %tessc_in_position %tessc_invocation_id\n"
+		"%tessc_in_color_val = OpLoad %v4f32 %tessc_in_color_ptr\n"
+		"%tessc_in_position_val = OpLoad %v4f32 %tessc_in_position_ptr\n"
+		"%tessc_out_color_ptr = OpAccessChain %op_v4f32 %tessc_out_color %tessc_invocation_id\n"
+		"%tessc_out_position_ptr = OpAccessChain %op_v4f32 %tessc_out_position %tessc_invocation_id\n"
+		"OpStore %tessc_out_color_ptr %tessc_in_color_val\n"
+		"OpStore %tessc_out_position_ptr %tessc_in_position_val\n"
+		"%tessc_is_first_invocation = OpIEqual %bool %tessc_invocation_id %c_i32_0\n"
+		"OpSelectionMerge %tessc_merge_label None\n"
+		"OpBranchConditional %tessc_is_first_invocation %tessc_first_invocation %tessc_merge_label\n"
+		"%tessc_first_invocation = OpLabel\n"
+		"%tessc_tess_outer_0 = OpAccessChain %op_f32 %tessc_gl_TessLevelOuter %c_i32_0\n"
+		"%tessc_tess_outer_1 = OpAccessChain %op_f32 %tessc_gl_TessLevelOuter %c_i32_1\n"
+		"%tessc_tess_outer_2 = OpAccessChain %op_f32 %tessc_gl_TessLevelOuter %c_i32_2\n"
+		"%tessc_tess_inner = OpAccessChain %op_f32 %tessc_gl_TessLevelInner %c_i32_0\n"
+		"OpStore %tessc_tess_outer_0 %c_f32_1\n"
+		"OpStore %tessc_tess_outer_1 %c_f32_1\n"
+		"OpStore %tessc_tess_outer_2 %c_f32_1\n"
+		"OpStore %tessc_tess_inner %c_f32_1\n"
+		"OpBranch %tessc_merge_label\n"
+		"%tessc_merge_label = OpLabel\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+
+		"; Tessellation Evaluation Entry\n"
+		"%tesse_main = OpFunction %void None %fun\n"
+		"%tesse_label = OpLabel\n"
+		"%tesse_tc_0_ptr = OpAccessChain %ip_f32 %tesse_gl_tessCoord %c_u32_0\n"
+		"%tesse_tc_1_ptr = OpAccessChain %ip_f32 %tesse_gl_tessCoord %c_u32_1\n"
+		"%tesse_tc_2_ptr = OpAccessChain %ip_f32 %tesse_gl_tessCoord %c_u32_2\n"
+		"%tesse_tc_0 = OpLoad %f32 %tesse_tc_0_ptr\n"
+		"%tesse_tc_1 = OpLoad %f32 %tesse_tc_1_ptr\n"
+		"%tesse_tc_2 = OpLoad %f32 %tesse_tc_2_ptr\n"
+		"%tesse_in_pos_0_ptr = OpAccessChain %ip_v4f32 %tesse_in_position %c_i32_0\n"
+		"%tesse_in_pos_1_ptr = OpAccessChain %ip_v4f32 %tesse_in_position %c_i32_1\n"
+		"%tesse_in_pos_2_ptr = OpAccessChain %ip_v4f32 %tesse_in_position %c_i32_2\n"
+		"%tesse_in_pos_0 = OpLoad %v4f32 %tesse_in_pos_0_ptr\n"
+		"%tesse_in_pos_1 = OpLoad %v4f32 %tesse_in_pos_1_ptr\n"
+		"%tesse_in_pos_2 = OpLoad %v4f32 %tesse_in_pos_2_ptr\n"
+		"%tesse_in_pos_0_weighted = OpVectorTimesScalar %v4f32 %tesse_in_pos_0 %tesse_tc_0\n"
+		"%tesse_in_pos_1_weighted = OpVectorTimesScalar %v4f32 %tesse_in_pos_1 %tesse_tc_1\n"
+		"%tesse_in_pos_2_weighted = OpVectorTimesScalar %v4f32 %tesse_in_pos_2 %tesse_tc_2\n"
+		"%tesse_out_pos_ptr = OpAccessChain %op_v4f32 %tesse_stream %c_i32_0\n"
+		"%tesse_in_pos_0_plus_pos_1 = OpFAdd %v4f32 %tesse_in_pos_0_weighted %tesse_in_pos_1_weighted\n"
+		"%tesse_computed_out = OpFAdd %v4f32 %tesse_in_pos_0_plus_pos_1 %tesse_in_pos_2_weighted\n"
+		"OpStore %tesse_out_pos_ptr %tesse_computed_out\n"
+		"%tesse_in_clr_0_ptr = OpAccessChain %ip_v4f32 %tesse_in_color %c_i32_0\n"
+		"%tesse_in_clr_1_ptr = OpAccessChain %ip_v4f32 %tesse_in_color %c_i32_1\n"
+		"%tesse_in_clr_2_ptr = OpAccessChain %ip_v4f32 %tesse_in_color %c_i32_2\n"
+		"%tesse_in_clr_0 = OpLoad %v4f32 %tesse_in_clr_0_ptr\n"
+		"%tesse_in_clr_1 = OpLoad %v4f32 %tesse_in_clr_1_ptr\n"
+		"%tesse_in_clr_2 = OpLoad %v4f32 %tesse_in_clr_2_ptr\n"
+		"%tesse_in_clr_0_weighted = OpVectorTimesScalar %v4f32 %tesse_in_clr_0 %tesse_tc_0\n"
+		"%tesse_in_clr_1_weighted = OpVectorTimesScalar %v4f32 %tesse_in_clr_1 %tesse_tc_1\n"
+		"%tesse_in_clr_2_weighted = OpVectorTimesScalar %v4f32 %tesse_in_clr_2 %tesse_tc_2\n"
+		"%tesse_in_clr_0_plus_col_1 = OpFAdd %v4f32 %tesse_in_clr_0_weighted %tesse_in_clr_1_weighted\n"
+		"%tesse_computed_clr = OpFAdd %v4f32 %tesse_in_clr_0_plus_col_1 %tesse_in_clr_2_weighted\n"
+		"OpStore %tesse_out_color %tesse_computed_clr\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+
+		"; Fragment Entry\n"
+		"%frag_main = OpFunction %void None %fun\n"
+		"%frag_label_main = OpLabel\n"
+		"%frag_tmp1 = OpLoad %v4f32 %frag_vtxColor\n"
+		"OpStore %frag_fragColor %frag_tmp1\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n";
+}
+
+void createMultipleEntries(vk::SourceCollections& dst, InstanceContext)
+{
+	dst.spirvAsmSources.add("vert") <<
+	// This module contains 2 vertex shaders. One that is a passthrough
+	// and a second that inverts the color of the output (1.0 - color).
+		"OpCapability Shader\n"
+		"OpMemoryModel Logical GLSL450\n"
+		"OpEntryPoint Vertex %main \"vert1\" %Position %vtxColor %color %vtxPosition %vertex_id %instance_id\n"
+		"OpEntryPoint Vertex %main2 \"vert2\" %Position %vtxColor %color %vtxPosition %vertex_id %instance_id\n"
+
+		"OpName %main \"vert1\"\n"
+		"OpName %main2 \"vert2\"\n"
+		"OpName %vtxPosition \"vtxPosition\"\n"
+		"OpName %Position \"position\"\n"
+		"OpName %vtxColor \"vtxColor\"\n"
+		"OpName %color \"color\"\n"
+		"OpName %vertex_id \"gl_VertexIndex\"\n"
+		"OpName %instance_id \"gl_InstanceIndex\"\n"
+
+		"OpDecorate %vtxPosition Location 2\n"
+		"OpDecorate %Position Location 0\n"
+		"OpDecorate %vtxColor Location 1\n"
+		"OpDecorate %color Location 1\n"
+		"OpDecorate %vertex_id BuiltIn VertexIndex\n"
+		"OpDecorate %instance_id BuiltIn InstanceIndex\n"
+		SPIRV_ASSEMBLY_TYPES
+		SPIRV_ASSEMBLY_CONSTANTS
+		SPIRV_ASSEMBLY_ARRAYS
+		"%cval = OpConstantComposite %v4f32 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_0\n"
+		"%vtxPosition = OpVariable %op_v4f32 Output\n"
+		"%Position = OpVariable %ip_v4f32 Input\n"
+		"%vtxColor = OpVariable %op_v4f32 Output\n"
+		"%color = OpVariable %ip_v4f32 Input\n"
+		"%vertex_id = OpVariable %ip_i32 Input\n"
+		"%instance_id = OpVariable %ip_i32 Input\n"
+
+		"%main = OpFunction %void None %fun\n"
+		"%label = OpLabel\n"
+		"%tmp_position = OpLoad %v4f32 %Position\n"
+		"OpStore %vtxPosition %tmp_position\n"
+		"%tmp_color = OpLoad %v4f32 %color\n"
+		"OpStore %vtxColor %tmp_color\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+
+		"%main2 = OpFunction %void None %fun\n"
+		"%label2 = OpLabel\n"
+		"%tmp_position2 = OpLoad %v4f32 %Position\n"
+		"OpStore %vtxPosition %tmp_position2\n"
+		"%tmp_color2 = OpLoad %v4f32 %color\n"
+		"%tmp_color3 = OpFSub %v4f32 %cval %tmp_color2\n"
+		"%tmp_color4 = OpVectorInsertDynamic %v4f32 %tmp_color3 %c_f32_1 %c_i32_3\n"
+		"OpStore %vtxColor %tmp_color4\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n";
+
+	dst.spirvAsmSources.add("frag") <<
+		// This is a single module that contains 2 fragment shaders.
+		// One that passes color through and the other that inverts the output
+		// color (1.0 - color).
+		"OpCapability Shader\n"
+		"OpMemoryModel Logical GLSL450\n"
+		"OpEntryPoint Fragment %main \"frag1\" %vtxColor %fragColor\n"
+		"OpEntryPoint Fragment %main2 \"frag2\" %vtxColor %fragColor\n"
+		"OpExecutionMode %main OriginUpperLeft\n"
+		"OpExecutionMode %main2 OriginUpperLeft\n"
+
+		"OpName %main \"frag1\"\n"
+		"OpName %main2 \"frag2\"\n"
+		"OpName %fragColor \"fragColor\"\n"
+		"OpName %vtxColor \"vtxColor\"\n"
+		"OpDecorate %fragColor Location 0\n"
+		"OpDecorate %vtxColor Location 1\n"
+		SPIRV_ASSEMBLY_TYPES
+		SPIRV_ASSEMBLY_CONSTANTS
+		SPIRV_ASSEMBLY_ARRAYS
+		"%cval = OpConstantComposite %v4f32 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_0\n"
+		"%fragColor = OpVariable %op_v4f32 Output\n"
+		"%vtxColor = OpVariable %ip_v4f32 Input\n"
+
+		"%main = OpFunction %void None %fun\n"
+		"%label_main = OpLabel\n"
+		"%tmp1 = OpLoad %v4f32 %vtxColor\n"
+		"OpStore %fragColor %tmp1\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+
+		"%main2 = OpFunction %void None %fun\n"
+		"%label_main2 = OpLabel\n"
+		"%tmp2 = OpLoad %v4f32 %vtxColor\n"
+		"%tmp3 = OpFSub %v4f32 %cval %tmp2\n"
+		"%tmp4 = OpVectorInsertDynamic %v4f32 %tmp3 %c_f32_1 %c_i32_3\n"
+		"OpStore %fragColor %tmp4\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n";
+
+	dst.spirvAsmSources.add("geom") <<
+		"OpCapability Geometry\n"
+		"OpCapability ClipDistance\n"
+		"OpCapability CullDistance\n"
+		"OpMemoryModel Logical GLSL450\n"
+		"OpEntryPoint Geometry %geom1_main \"geom1\" %out_gl_position %gl_in %out_color %in_color\n"
+		"OpEntryPoint Geometry %geom2_main \"geom2\" %out_gl_position %gl_in %out_color %in_color\n"
+		"OpExecutionMode %geom1_main Triangles\n"
+		"OpExecutionMode %geom2_main Triangles\n"
+		"OpExecutionMode %geom1_main OutputTriangleStrip\n"
+		"OpExecutionMode %geom2_main OutputTriangleStrip\n"
+		"OpExecutionMode %geom1_main OutputVertices 3\n"
+		"OpExecutionMode %geom2_main OutputVertices 3\n"
+		"OpName %geom1_main \"geom1\"\n"
+		"OpName %geom2_main \"geom2\"\n"
+		"OpName %per_vertex_in \"gl_PerVertex\"\n"
+		"OpMemberName %per_vertex_in 0 \"gl_Position\"\n"
+		"OpMemberName %per_vertex_in 1 \"gl_PointSize\"\n"
+		"OpMemberName %per_vertex_in 2 \"gl_ClipDistance\"\n"
+		"OpMemberName %per_vertex_in 3 \"gl_CullDistance\"\n"
+		"OpName %gl_in \"gl_in\"\n"
+		"OpName %out_color \"out_color\"\n"
+		"OpName %in_color \"in_color\"\n"
+		"OpDecorate %out_gl_position BuiltIn Position\n"
+		"OpMemberDecorate %per_vertex_in 0 BuiltIn Position\n"
+		"OpMemberDecorate %per_vertex_in 1 BuiltIn PointSize\n"
+		"OpMemberDecorate %per_vertex_in 2 BuiltIn ClipDistance\n"
+		"OpMemberDecorate %per_vertex_in 3 BuiltIn CullDistance\n"
+		"OpDecorate %per_vertex_in Block\n"
+		"OpDecorate %out_color Location 1\n"
+		"OpDecorate %in_color Location 1\n"
+		SPIRV_ASSEMBLY_TYPES
+		SPIRV_ASSEMBLY_CONSTANTS
+		SPIRV_ASSEMBLY_ARRAYS
+		"%cval = OpConstantComposite %v4f32 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_0\n"
+		"%per_vertex_in = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+		"%a3_per_vertex_in = OpTypeArray %per_vertex_in %c_u32_3\n"
+		"%ip_a3_per_vertex_in = OpTypePointer Input %a3_per_vertex_in\n"
+		"%gl_in = OpVariable %ip_a3_per_vertex_in Input\n"
+		"%out_color = OpVariable %op_v4f32 Output\n"
+		"%in_color = OpVariable %ip_a3v4f32 Input\n"
+		"%out_gl_position = OpVariable %op_v4f32 Output\n"
+
+		"%geom1_main = OpFunction %void None %fun\n"
+		"%geom1_label = OpLabel\n"
+		"%geom1_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_0 %c_i32_0\n"
+		"%geom1_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_1 %c_i32_0\n"
+		"%geom1_gl_in_2_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_2 %c_i32_0\n"
+		"%geom1_in_position_0 = OpLoad %v4f32 %geom1_gl_in_0_gl_position\n"
+		"%geom1_in_position_1 = OpLoad %v4f32 %geom1_gl_in_1_gl_position\n"
+		"%geom1_in_position_2 = OpLoad %v4f32 %geom1_gl_in_2_gl_position \n"
+		"%geom1_in_color_0_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_0\n"
+		"%geom1_in_color_1_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_1\n"
+		"%geom1_in_color_2_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_2\n"
+		"%geom1_in_color_0 = OpLoad %v4f32 %geom1_in_color_0_ptr\n"
+		"%geom1_in_color_1 = OpLoad %v4f32 %geom1_in_color_1_ptr\n"
+		"%geom1_in_color_2 = OpLoad %v4f32 %geom1_in_color_2_ptr\n"
+		"OpStore %out_gl_position %geom1_in_position_0\n"
+		"OpStore %out_color %geom1_in_color_0\n"
+		"OpEmitVertex\n"
+		"OpStore %out_gl_position %geom1_in_position_1\n"
+		"OpStore %out_color %geom1_in_color_1\n"
+		"OpEmitVertex\n"
+		"OpStore %out_gl_position %geom1_in_position_2\n"
+		"OpStore %out_color %geom1_in_color_2\n"
+		"OpEmitVertex\n"
+		"OpEndPrimitive\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+
+		"%geom2_main = OpFunction %void None %fun\n"
+		"%geom2_label = OpLabel\n"
+		"%geom2_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_0 %c_i32_0\n"
+		"%geom2_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_1 %c_i32_0\n"
+		"%geom2_gl_in_2_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_2 %c_i32_0\n"
+		"%geom2_in_position_0 = OpLoad %v4f32 %geom2_gl_in_0_gl_position\n"
+		"%geom2_in_position_1 = OpLoad %v4f32 %geom2_gl_in_1_gl_position\n"
+		"%geom2_in_position_2 = OpLoad %v4f32 %geom2_gl_in_2_gl_position \n"
+		"%geom2_in_color_0_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_0\n"
+		"%geom2_in_color_1_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_1\n"
+		"%geom2_in_color_2_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_2\n"
+		"%geom2_in_color_0 = OpLoad %v4f32 %geom2_in_color_0_ptr\n"
+		"%geom2_in_color_1 = OpLoad %v4f32 %geom2_in_color_1_ptr\n"
+		"%geom2_in_color_2 = OpLoad %v4f32 %geom2_in_color_2_ptr\n"
+		"%geom2_transformed_in_color_0 = OpFSub %v4f32 %cval %geom2_in_color_0\n"
+		"%geom2_transformed_in_color_1 = OpFSub %v4f32 %cval %geom2_in_color_1\n"
+		"%geom2_transformed_in_color_2 = OpFSub %v4f32 %cval %geom2_in_color_2\n"
+		"%geom2_transformed_in_color_0_a = OpVectorInsertDynamic %v4f32 %geom2_transformed_in_color_0 %c_f32_1 %c_i32_3\n"
+		"%geom2_transformed_in_color_1_a = OpVectorInsertDynamic %v4f32 %geom2_transformed_in_color_1 %c_f32_1 %c_i32_3\n"
+		"%geom2_transformed_in_color_2_a = OpVectorInsertDynamic %v4f32 %geom2_transformed_in_color_2 %c_f32_1 %c_i32_3\n"
+		"OpStore %out_gl_position %geom2_in_position_0\n"
+		"OpStore %out_color %geom2_transformed_in_color_0_a\n"
+		"OpEmitVertex\n"
+		"OpStore %out_gl_position %geom2_in_position_1\n"
+		"OpStore %out_color %geom2_transformed_in_color_1_a\n"
+		"OpEmitVertex\n"
+		"OpStore %out_gl_position %geom2_in_position_2\n"
+		"OpStore %out_color %geom2_transformed_in_color_2_a\n"
+		"OpEmitVertex\n"
+		"OpEndPrimitive\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n";
+
+	dst.spirvAsmSources.add("tessc") <<
+		"OpCapability Tessellation\n"
+		"OpMemoryModel Logical GLSL450\n"
+		"OpEntryPoint TessellationControl %tessc1_main \"tessc1\" %out_color %gl_InvocationID %in_color %out_position %in_position %gl_TessLevelOuter %gl_TessLevelInner\n"
+		"OpEntryPoint TessellationControl %tessc2_main \"tessc2\" %out_color %gl_InvocationID %in_color %out_position %in_position %gl_TessLevelOuter %gl_TessLevelInner\n"
+		"OpExecutionMode %tessc1_main OutputVertices 3\n"
+		"OpExecutionMode %tessc2_main OutputVertices 3\n"
+		"OpName %tessc1_main \"tessc1\"\n"
+		"OpName %tessc2_main \"tessc2\"\n"
+		"OpName %out_color \"out_color\"\n"
+		"OpName %gl_InvocationID \"gl_InvocationID\"\n"
+		"OpName %in_color \"in_color\"\n"
+		"OpName %out_position \"out_position\"\n"
+		"OpName %in_position \"in_position\"\n"
+		"OpName %gl_TessLevelOuter \"gl_TessLevelOuter\"\n"
+		"OpName %gl_TessLevelInner \"gl_TessLevelInner\"\n"
+		"OpDecorate %out_color Location 1\n"
+		"OpDecorate %gl_InvocationID BuiltIn InvocationId\n"
+		"OpDecorate %in_color Location 1\n"
+		"OpDecorate %out_position Location 2\n"
+		"OpDecorate %in_position Location 2\n"
+		"OpDecorate %gl_TessLevelOuter Patch\n"
+		"OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter\n"
+		"OpDecorate %gl_TessLevelInner Patch\n"
+		"OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner\n"
+		SPIRV_ASSEMBLY_TYPES
+		SPIRV_ASSEMBLY_CONSTANTS
+		SPIRV_ASSEMBLY_ARRAYS
+		"%cval = OpConstantComposite %v4f32 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_0\n"
+		"%out_color = OpVariable %op_a3v4f32 Output\n"
+		"%gl_InvocationID = OpVariable %ip_i32 Input\n"
+		"%in_color = OpVariable %ip_a32v4f32 Input\n"
+		"%out_position = OpVariable %op_a3v4f32 Output\n"
+		"%in_position = OpVariable %ip_a32v4f32 Input\n"
+		"%gl_TessLevelOuter = OpVariable %op_a4f32 Output\n"
+		"%gl_TessLevelInner = OpVariable %op_a2f32 Output\n"
+
+		"%tessc1_main = OpFunction %void None %fun\n"
+		"%tessc1_label = OpLabel\n"
+		"%tessc1_invocation_id = OpLoad %i32 %gl_InvocationID\n"
+		"%tessc1_in_color_ptr = OpAccessChain %ip_v4f32 %in_color %tessc1_invocation_id\n"
+		"%tessc1_in_position_ptr = OpAccessChain %ip_v4f32 %in_position %tessc1_invocation_id\n"
+		"%tessc1_in_color_val = OpLoad %v4f32 %tessc1_in_color_ptr\n"
+		"%tessc1_in_position_val = OpLoad %v4f32 %tessc1_in_position_ptr\n"
+		"%tessc1_out_color_ptr = OpAccessChain %op_v4f32 %out_color %tessc1_invocation_id\n"
+		"%tessc1_out_position_ptr = OpAccessChain %op_v4f32 %out_position %tessc1_invocation_id\n"
+		"OpStore %tessc1_out_color_ptr %tessc1_in_color_val\n"
+		"OpStore %tessc1_out_position_ptr %tessc1_in_position_val\n"
+		"%tessc1_is_first_invocation = OpIEqual %bool %tessc1_invocation_id %c_i32_0\n"
+		"OpSelectionMerge %tessc1_merge_label None\n"
+		"OpBranchConditional %tessc1_is_first_invocation %tessc1_first_invocation %tessc1_merge_label\n"
+		"%tessc1_first_invocation = OpLabel\n"
+		"%tessc1_tess_outer_0 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_0\n"
+		"%tessc1_tess_outer_1 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_1\n"
+		"%tessc1_tess_outer_2 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_2\n"
+		"%tessc1_tess_inner = OpAccessChain %op_f32 %gl_TessLevelInner %c_i32_0\n"
+		"OpStore %tessc1_tess_outer_0 %c_f32_1\n"
+		"OpStore %tessc1_tess_outer_1 %c_f32_1\n"
+		"OpStore %tessc1_tess_outer_2 %c_f32_1\n"
+		"OpStore %tessc1_tess_inner %c_f32_1\n"
+		"OpBranch %tessc1_merge_label\n"
+		"%tessc1_merge_label = OpLabel\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+
+		"%tessc2_main = OpFunction %void None %fun\n"
+		"%tessc2_label = OpLabel\n"
+		"%tessc2_invocation_id = OpLoad %i32 %gl_InvocationID\n"
+		"%tessc2_in_color_ptr = OpAccessChain %ip_v4f32 %in_color %tessc2_invocation_id\n"
+		"%tessc2_in_position_ptr = OpAccessChain %ip_v4f32 %in_position %tessc2_invocation_id\n"
+		"%tessc2_in_color_val = OpLoad %v4f32 %tessc2_in_color_ptr\n"
+		"%tessc2_in_position_val = OpLoad %v4f32 %tessc2_in_position_ptr\n"
+		"%tessc2_out_color_ptr = OpAccessChain %op_v4f32 %out_color %tessc2_invocation_id\n"
+		"%tessc2_out_position_ptr = OpAccessChain %op_v4f32 %out_position %tessc2_invocation_id\n"
+		"%tessc2_transformed_color = OpFSub %v4f32 %cval %tessc2_in_color_val\n"
+		"%tessc2_transformed_color_a = OpVectorInsertDynamic %v4f32 %tessc2_transformed_color %c_f32_1 %c_i32_3\n"
+		"OpStore %tessc2_out_color_ptr %tessc2_transformed_color_a\n"
+		"OpStore %tessc2_out_position_ptr %tessc2_in_position_val\n"
+		"%tessc2_is_first_invocation = OpIEqual %bool %tessc2_invocation_id %c_i32_0\n"
+		"OpSelectionMerge %tessc2_merge_label None\n"
+		"OpBranchConditional %tessc2_is_first_invocation %tessc2_first_invocation %tessc2_merge_label\n"
+		"%tessc2_first_invocation = OpLabel\n"
+		"%tessc2_tess_outer_0 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_0\n"
+		"%tessc2_tess_outer_1 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_1\n"
+		"%tessc2_tess_outer_2 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_2\n"
+		"%tessc2_tess_inner = OpAccessChain %op_f32 %gl_TessLevelInner %c_i32_0\n"
+		"OpStore %tessc2_tess_outer_0 %c_f32_1\n"
+		"OpStore %tessc2_tess_outer_1 %c_f32_1\n"
+		"OpStore %tessc2_tess_outer_2 %c_f32_1\n"
+		"OpStore %tessc2_tess_inner %c_f32_1\n"
+		"OpBranch %tessc2_merge_label\n"
+		"%tessc2_merge_label = OpLabel\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n";
+
+	dst.spirvAsmSources.add("tesse") <<
+		"OpCapability Tessellation\n"
+		"OpCapability ClipDistance\n"
+		"OpCapability CullDistance\n"
+		"OpMemoryModel Logical GLSL450\n"
+		"OpEntryPoint TessellationEvaluation %tesse1_main \"tesse1\" %stream %gl_tessCoord %in_position %out_color %in_color \n"
+		"OpEntryPoint TessellationEvaluation %tesse2_main \"tesse2\" %stream %gl_tessCoord %in_position %out_color %in_color \n"
+		"OpExecutionMode %tesse1_main Triangles\n"
+		"OpExecutionMode %tesse1_main SpacingEqual\n"
+		"OpExecutionMode %tesse1_main VertexOrderCcw\n"
+		"OpExecutionMode %tesse2_main Triangles\n"
+		"OpExecutionMode %tesse2_main SpacingEqual\n"
+		"OpExecutionMode %tesse2_main VertexOrderCcw\n"
+		"OpName %tesse1_main \"tesse1\"\n"
+		"OpName %tesse2_main \"tesse2\"\n"
+		"OpName %per_vertex_out \"gl_PerVertex\"\n"
+		"OpMemberName %per_vertex_out 0 \"gl_Position\"\n"
+		"OpMemberName %per_vertex_out 1 \"gl_PointSize\"\n"
+		"OpMemberName %per_vertex_out 2 \"gl_ClipDistance\"\n"
+		"OpMemberName %per_vertex_out 3 \"gl_CullDistance\"\n"
+		"OpName %stream \"\"\n"
+		"OpName %gl_tessCoord \"gl_TessCoord\"\n"
+		"OpName %in_position \"in_position\"\n"
+		"OpName %out_color \"out_color\"\n"
+		"OpName %in_color \"in_color\"\n"
+		"OpMemberDecorate %per_vertex_out 0 BuiltIn Position\n"
+		"OpMemberDecorate %per_vertex_out 1 BuiltIn PointSize\n"
+		"OpMemberDecorate %per_vertex_out 2 BuiltIn ClipDistance\n"
+		"OpMemberDecorate %per_vertex_out 3 BuiltIn CullDistance\n"
+		"OpDecorate %per_vertex_out Block\n"
+		"OpDecorate %gl_tessCoord BuiltIn TessCoord\n"
+		"OpDecorate %in_position Location 2\n"
+		"OpDecorate %out_color Location 1\n"
+		"OpDecorate %in_color Location 1\n"
+		SPIRV_ASSEMBLY_TYPES
+		SPIRV_ASSEMBLY_CONSTANTS
+		SPIRV_ASSEMBLY_ARRAYS
+		"%cval = OpConstantComposite %v4f32 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_0\n"
+		"%per_vertex_out = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
+		"%op_per_vertex_out = OpTypePointer Output %per_vertex_out\n"
+		"%stream = OpVariable %op_per_vertex_out Output\n"
+		"%gl_tessCoord = OpVariable %ip_v3f32 Input\n"
+		"%in_position = OpVariable %ip_a32v4f32 Input\n"
+		"%out_color = OpVariable %op_v4f32 Output\n"
+		"%in_color = OpVariable %ip_a32v4f32 Input\n"
+
+		"%tesse1_main = OpFunction %void None %fun\n"
+		"%tesse1_label = OpLabel\n"
+		"%tesse1_tc_0_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_0\n"
+		"%tesse1_tc_1_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_1\n"
+		"%tesse1_tc_2_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_2\n"
+		"%tesse1_tc_0 = OpLoad %f32 %tesse1_tc_0_ptr\n"
+		"%tesse1_tc_1 = OpLoad %f32 %tesse1_tc_1_ptr\n"
+		"%tesse1_tc_2 = OpLoad %f32 %tesse1_tc_2_ptr\n"
+		"%tesse1_in_pos_0_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_0\n"
+		"%tesse1_in_pos_1_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_1\n"
+		"%tesse1_in_pos_2_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_2\n"
+		"%tesse1_in_pos_0 = OpLoad %v4f32 %tesse1_in_pos_0_ptr\n"
+		"%tesse1_in_pos_1 = OpLoad %v4f32 %tesse1_in_pos_1_ptr\n"
+		"%tesse1_in_pos_2 = OpLoad %v4f32 %tesse1_in_pos_2_ptr\n"
+		"%tesse1_in_pos_0_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_pos_0 %tesse1_tc_0\n"
+		"%tesse1_in_pos_1_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_pos_1 %tesse1_tc_1\n"
+		"%tesse1_in_pos_2_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_pos_2 %tesse1_tc_2\n"
+		"%tesse1_out_pos_ptr = OpAccessChain %op_v4f32 %stream %c_i32_0\n"
+		"%tesse1_in_pos_0_plus_pos_1 = OpFAdd %v4f32 %tesse1_in_pos_0_weighted %tesse1_in_pos_1_weighted\n"
+		"%tesse1_computed_out = OpFAdd %v4f32 %tesse1_in_pos_0_plus_pos_1 %tesse1_in_pos_2_weighted\n"
+		"OpStore %tesse1_out_pos_ptr %tesse1_computed_out\n"
+		"%tesse1_in_clr_0_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_0\n"
+		"%tesse1_in_clr_1_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_1\n"
+		"%tesse1_in_clr_2_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_2\n"
+		"%tesse1_in_clr_0 = OpLoad %v4f32 %tesse1_in_clr_0_ptr\n"
+		"%tesse1_in_clr_1 = OpLoad %v4f32 %tesse1_in_clr_1_ptr\n"
+		"%tesse1_in_clr_2 = OpLoad %v4f32 %tesse1_in_clr_2_ptr\n"
+		"%tesse1_in_clr_0_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_clr_0 %tesse1_tc_0\n"
+		"%tesse1_in_clr_1_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_clr_1 %tesse1_tc_1\n"
+		"%tesse1_in_clr_2_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_clr_2 %tesse1_tc_2\n"
+		"%tesse1_in_clr_0_plus_col_1 = OpFAdd %v4f32 %tesse1_in_clr_0_weighted %tesse1_in_clr_1_weighted\n"
+		"%tesse1_computed_clr = OpFAdd %v4f32 %tesse1_in_clr_0_plus_col_1 %tesse1_in_clr_2_weighted\n"
+		"OpStore %out_color %tesse1_computed_clr\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n"
+
+		"%tesse2_main = OpFunction %void None %fun\n"
+		"%tesse2_label = OpLabel\n"
+		"%tesse2_tc_0_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_0\n"
+		"%tesse2_tc_1_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_1\n"
+		"%tesse2_tc_2_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_2\n"
+		"%tesse2_tc_0 = OpLoad %f32 %tesse2_tc_0_ptr\n"
+		"%tesse2_tc_1 = OpLoad %f32 %tesse2_tc_1_ptr\n"
+		"%tesse2_tc_2 = OpLoad %f32 %tesse2_tc_2_ptr\n"
+		"%tesse2_in_pos_0_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_0\n"
+		"%tesse2_in_pos_1_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_1\n"
+		"%tesse2_in_pos_2_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_2\n"
+		"%tesse2_in_pos_0 = OpLoad %v4f32 %tesse2_in_pos_0_ptr\n"
+		"%tesse2_in_pos_1 = OpLoad %v4f32 %tesse2_in_pos_1_ptr\n"
+		"%tesse2_in_pos_2 = OpLoad %v4f32 %tesse2_in_pos_2_ptr\n"
+		"%tesse2_in_pos_0_weighted = OpVectorTimesScalar %v4f32 %tesse2_in_pos_0 %tesse2_tc_0\n"
+		"%tesse2_in_pos_1_weighted = OpVectorTimesScalar %v4f32 %tesse2_in_pos_1 %tesse2_tc_1\n"
+		"%tesse2_in_pos_2_weighted = OpVectorTimesScalar %v4f32 %tesse2_in_pos_2 %tesse2_tc_2\n"
+		"%tesse2_out_pos_ptr = OpAccessChain %op_v4f32 %stream %c_i32_0\n"
+		"%tesse2_in_pos_0_plus_pos_1 = OpFAdd %v4f32 %tesse2_in_pos_0_weighted %tesse2_in_pos_1_weighted\n"
+		"%tesse2_computed_out = OpFAdd %v4f32 %tesse2_in_pos_0_plus_pos_1 %tesse2_in_pos_2_weighted\n"
+		"OpStore %tesse2_out_pos_ptr %tesse2_computed_out\n"
+		"%tesse2_in_clr_0_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_0\n"
+		"%tesse2_in_clr_1_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_1\n"
+		"%tesse2_in_clr_2_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_2\n"
+		"%tesse2_in_clr_0 = OpLoad %v4f32 %tesse2_in_clr_0_ptr\n"
+		"%tesse2_in_clr_1 = OpLoad %v4f32 %tesse2_in_clr_1_ptr\n"
+		"%tesse2_in_clr_2 = OpLoad %v4f32 %tesse2_in_clr_2_ptr\n"
+		"%tesse2_in_clr_0_weighted = OpVectorTimesScalar %v4f32 %tesse2_in_clr_0 %tesse2_tc_0\n"
+		"%tesse2_in_clr_1_weighted = OpVectorTimesScalar %v4f32 %tesse2_in_clr_1 %tesse2_tc_1\n"
+		"%tesse2_in_clr_2_weighted = OpVectorTimesScalar %v4f32 %tesse2_in_clr_2 %tesse2_tc_2\n"
+		"%tesse2_in_clr_0_plus_col_1 = OpFAdd %v4f32 %tesse2_in_clr_0_weighted %tesse2_in_clr_1_weighted\n"
+		"%tesse2_computed_clr = OpFAdd %v4f32 %tesse2_in_clr_0_plus_col_1 %tesse2_in_clr_2_weighted\n"
+		"%tesse2_clr_transformed = OpFSub %v4f32 %cval %tesse2_computed_clr\n"
+		"%tesse2_clr_transformed_a = OpVectorInsertDynamic %v4f32 %tesse2_clr_transformed %c_f32_1 %c_i32_3\n"
+		"OpStore %out_color %tesse2_clr_transformed_a\n"
+		"OpReturn\n"
+		"OpFunctionEnd\n";
+}
+
+bool compare16BitFloat (float original, deUint16 returned, RoundingModeFlags flags, tcu::TestLog& log)
+{
+	// We only support RTE, RTZ, or both.
+	DE_ASSERT(static_cast<int>(flags) > 0 && static_cast<int>(flags) < 4);
+
+	const Float32	originalFloat	(original);
+	const Float16	returnedFloat	(returned);
+
+	// Zero are turned into zero under both RTE and RTZ.
+	if (originalFloat.isZero())
+	{
+		if (returnedFloat.isZero())
+			return true;
+
+		log << TestLog::Message << "Error: expected zero but returned " << returned << TestLog::EndMessage;
+		return false;
+	}
+
+	// Inf are always turned into Inf with the same sign, too.
+	if (originalFloat.isInf())
+	{
+		if (returnedFloat.isInf() && originalFloat.signBit() == returnedFloat.signBit())
+			return true;
+
+		log << TestLog::Message << "Error: expected Inf but returned " << returned << TestLog::EndMessage;
+		return false;
+	}
+
+	// NaN are always turned into NaN, too.
+	if (originalFloat.isNaN())
+	{
+		if (returnedFloat.isNaN())
+			return true;
+
+		log << TestLog::Message << "Error: expected NaN but returned " << returned << TestLog::EndMessage;
+		return false;
+	}
+
+	// Check all rounding modes
+	for (int bitNdx = 0; bitNdx < 2; ++bitNdx)
+	{
+		if ((flags & (1u << bitNdx)) == 0)
+			continue;	// This rounding mode is not selected.
+
+		const Float16	expectedFloat	(deFloat32To16Round(original, deRoundingMode(bitNdx)));
+
+		// Denormalized numbers can be flushed to zero.
+		if (expectedFloat.isDenorm() && returnedFloat.isZero())
+			return true;
+
+		// If not matched in the above cases, they should have the same bit pattern.
+		if (expectedFloat.bits() == returnedFloat.bits())
+			return true;
+	}
+
+	log << TestLog::Message << "Error: found unmatched 32-bit and 16-bit floats: " << originalFloat.bits() << " vs " << returned << TestLog::EndMessage;
+	return false;
+}
+
+bool compare32BitFloat (float expected, float returned, tcu::TestLog& log)
+{
+	const Float32	expectedFloat	(expected);
+	const Float32	returnedFloat	(returned);
+
+	// Denormalized numbers can be flushed to zero
+	if (expectedFloat.isDenorm() && returnedFloat.isZero())
+		return true;
+
+	if (expectedFloat.isNaN())
+	{
+		if (returnedFloat.isNaN())
+			return true;
+
+		log << TestLog::Message << "Error: expected NaN but returned " << returned << TestLog::EndMessage;
+		return false;
+	}
+
+	if (returned == expected)
+		return true;
+
+	log << TestLog::Message << "Error: found unmatched 32-bit float: expected " << expectedFloat.bits() << " vs. returned " << returnedFloat.bits() << TestLog::EndMessage;
+	return false;
+}
+
+Move<VkBuffer> createBufferForResource(const DeviceInterface& vk, const VkDevice vkDevice, const Resource& resource, deUint32 queueFamilyIndex)
+{
+	const VkBufferCreateInfo	resourceBufferParams	=
+	{
+		VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,								// sType
+		DE_NULL,															// pNext
+		(VkBufferCreateFlags)0,												// flags
+		(VkDeviceSize)resource.second->getNumBytes(),						// size
+		(VkBufferUsageFlags)getMatchingBufferUsageFlagBit(resource.first),	// usage
+		VK_SHARING_MODE_EXCLUSIVE,											// sharingMode
+		1u,																	// queueFamilyCount
+		&queueFamilyIndex,													// pQueueFamilyIndices
+	};
+
+	return createBuffer(vk, vkDevice, &resourceBufferParams);
+}
+
+TestStatus runAndVerifyDefaultPipeline (Context& context, InstanceContext instance)
+{
+	const InstanceInterface&					vkInstance				= context.getInstanceInterface();
+	const VkPhysicalDevice						vkPhysicalDevice		= context.getPhysicalDevice();
+	const deUint32								queueFamilyIndex		= context.getUniversalQueueFamilyIndex();
+	// Create a dedicated logic device with required extensions enabled for this test case.
+	const Unique<VkDevice>						vkDevice				(createDeviceWithExtensions(vkInstance, vkPhysicalDevice, queueFamilyIndex, context.getDeviceExtensions(), instance.requiredDeviceExtensions));
+	const DeviceDriver							vk						(vkInstance, *vkDevice);
+	const VkQueue								queue					= getDeviceQueue(vk, *vkDevice, queueFamilyIndex, 0);
+	const tcu::UVec2							renderSize				(256, 256);
+	vector<ModuleHandleSp>						modules;
+	map<VkShaderStageFlagBits, VkShaderModule>	moduleByStage;
+	const int									testSpecificSeed		= 31354125;
+	const int									seed					= context.getTestContext().getCommandLine().getBaseSeed() ^ testSpecificSeed;
+	bool										supportsGeometry		= false;
+	bool										supportsTessellation	= false;
+	bool										hasTessellation         = false;
+	const bool									hasPushConstants		= !instance.pushConstants.empty();
+	const deUint32								numResources			= static_cast<deUint32>(instance.resources.inputs.size() + instance.resources.outputs.size());
+	const bool									needInterface			= !instance.interfaces.empty();
+	const VkPhysicalDeviceFeatures&				features				= context.getDeviceFeatures();
+	const de::UniquePtr<Allocator>				allocatorUptr			(createAllocator(vkInstance, vkPhysicalDevice, vk, *vkDevice));
+	Allocator&									allocator				= *allocatorUptr;
+
+	supportsGeometry		= features.geometryShader == VK_TRUE;
+	supportsTessellation	= features.tessellationShader == VK_TRUE;
+	hasTessellation			= (instance.requiredStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) ||
+								(instance.requiredStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT);
+
+	if (hasTessellation && !supportsTessellation)
+	{
+		throw tcu::NotSupportedError(std::string("Tessellation not supported"));
+	}
+
+	if ((instance.requiredStages & VK_SHADER_STAGE_GEOMETRY_BIT) &&
+		!supportsGeometry)
+	{
+		throw tcu::NotSupportedError(std::string("Geometry not supported"));
+	}
+
+	{
+		for (deUint32 featureNdx = 0; featureNdx < instance.requiredDeviceFeatures.size(); ++featureNdx)
+		{
+			const string& feature = instance.requiredDeviceFeatures[featureNdx];
+
+			if (feature == "shaderInt16")
+			{
+				if (features.shaderInt16 != VK_TRUE)
+					throw tcu::NotSupportedError(std::string("Device feature not supported: ") + feature);
+			}
+			else
+			{
+				throw tcu::InternalError(std::string("Unimplemented physical device feature: ") + feature);
+			}
+		}
+	}
+
+	// 16bit storage features
+	{
+		if (!is16BitStorageFeaturesSupported(vkInstance, vkPhysicalDevice, context.getInstanceExtensions(), instance.requestedExtensionFeatures.ext16BitStorage))
+			TCU_THROW(NotSupportedError, "Requested 16bit storage features not supported");
+	}
+
+	de::Random(seed).shuffle(instance.inputColors, instance.inputColors+4);
+	de::Random(seed).shuffle(instance.outputColors, instance.outputColors+4);
+	const Vec4								vertexData[]			=
+	{
+		// Upper left corner:
+		Vec4(-1.0f, -1.0f, 0.0f, 1.0f), instance.inputColors[0].toVec(),
+		Vec4(-0.5f, -1.0f, 0.0f, 1.0f), instance.inputColors[0].toVec(),
+		Vec4(-1.0f, -0.5f, 0.0f, 1.0f), instance.inputColors[0].toVec(),
+
+		// Upper right corner:
+		Vec4(+0.5f, -1.0f, 0.0f, 1.0f), instance.inputColors[1].toVec(),
+		Vec4(+1.0f, -1.0f, 0.0f, 1.0f), instance.inputColors[1].toVec(),
+		Vec4(+1.0f, -0.5f, 0.0f, 1.0f), instance.inputColors[1].toVec(),
+
+		// Lower left corner:
+		Vec4(-1.0f, +0.5f, 0.0f, 1.0f), instance.inputColors[2].toVec(),
+		Vec4(-0.5f, +1.0f, 0.0f, 1.0f), instance.inputColors[2].toVec(),
+		Vec4(-1.0f, +1.0f, 0.0f, 1.0f), instance.inputColors[2].toVec(),
+
+		// Lower right corner:
+		Vec4(+1.0f, +0.5f, 0.0f, 1.0f), instance.inputColors[3].toVec(),
+		Vec4(+1.0f, +1.0f, 0.0f, 1.0f), instance.inputColors[3].toVec(),
+		Vec4(+0.5f, +1.0f, 0.0f, 1.0f), instance.inputColors[3].toVec()
+	};
+	const size_t							singleVertexDataSize	= 2 * sizeof(Vec4);
+	const size_t							vertexCount				= sizeof(vertexData) / singleVertexDataSize;
+
+	Move<VkBuffer>							vertexInputBuffer		;
+	de::MovePtr<Allocation>					vertexInputMemory		;
+	Move<VkBuffer>							fragOutputBuffer		;
+	de::MovePtr<Allocation>					fragOutputMemory		;
+	Move<VkImage>							fragOutputImage			;
+	de::MovePtr<Allocation>					fragOutputImageMemory	;
+	Move<VkImageView>						fragOutputImageView		;
+
+	const VkBufferCreateInfo				vertexBufferParams		=
+	{
+		VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,	//	VkStructureType		sType;
+		DE_NULL,								//	const void*			pNext;
+		0u,										//	VkBufferCreateFlags	flags;
+		(VkDeviceSize)sizeof(vertexData),		//	VkDeviceSize		size;
+		VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,		//	VkBufferUsageFlags	usage;
+		VK_SHARING_MODE_EXCLUSIVE,				//	VkSharingMode		sharingMode;
+		1u,										//	deUint32			queueFamilyCount;
+		&queueFamilyIndex,						//	const deUint32*		pQueueFamilyIndices;
+	};
+	const Unique<VkBuffer>					vertexBuffer			(createBuffer(vk, *vkDevice, &vertexBufferParams));
+	const UniquePtr<Allocation>				vertexBufferMemory		(allocator.allocate(getBufferMemoryRequirements(vk, *vkDevice, *vertexBuffer), MemoryRequirement::HostVisible));
+
+	VK_CHECK(vk.bindBufferMemory(*vkDevice, *vertexBuffer, vertexBufferMemory->getMemory(), vertexBufferMemory->getOffset()));
+
+	const VkDeviceSize						imageSizeBytes			= (VkDeviceSize)(sizeof(deUint32)*renderSize.x()*renderSize.y());
+	const VkBufferCreateInfo				readImageBufferParams	=
+	{
+		VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,		//	VkStructureType		sType;
+		DE_NULL,									//	const void*			pNext;
+		0u,											//	VkBufferCreateFlags	flags;
+		imageSizeBytes,								//	VkDeviceSize		size;
+		VK_BUFFER_USAGE_TRANSFER_DST_BIT,			//	VkBufferUsageFlags	usage;
+		VK_SHARING_MODE_EXCLUSIVE,					//	VkSharingMode		sharingMode;
+		1u,											//	deUint32			queueFamilyCount;
+		&queueFamilyIndex,							//	const deUint32*		pQueueFamilyIndices;
+	};
+	const Unique<VkBuffer>					readImageBuffer			(createBuffer(vk, *vkDevice, &readImageBufferParams));
+	const UniquePtr<Allocation>				readImageBufferMemory	(allocator.allocate(getBufferMemoryRequirements(vk, *vkDevice, *readImageBuffer), MemoryRequirement::HostVisible));
+
+	VK_CHECK(vk.bindBufferMemory(*vkDevice, *readImageBuffer, readImageBufferMemory->getMemory(), readImageBufferMemory->getOffset()));
+
+	VkImageCreateInfo						imageParams				=
+	{
+		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,									//	VkStructureType		sType;
+		DE_NULL,																//	const void*			pNext;
+		0u,																		//	VkImageCreateFlags	flags;
+		VK_IMAGE_TYPE_2D,														//	VkImageType			imageType;
+		VK_FORMAT_R8G8B8A8_UNORM,												//	VkFormat			format;
+		{ renderSize.x(), renderSize.y(), 1 },									//	VkExtent3D			extent;
+		1u,																		//	deUint32			mipLevels;
+		1u,																		//	deUint32			arraySize;
+		VK_SAMPLE_COUNT_1_BIT,													//	deUint32			samples;
+		VK_IMAGE_TILING_OPTIMAL,												//	VkImageTiling		tiling;
+		VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT,	//	VkImageUsageFlags	usage;
+		VK_SHARING_MODE_EXCLUSIVE,												//	VkSharingMode		sharingMode;
+		1u,																		//	deUint32			queueFamilyCount;
+		&queueFamilyIndex,														//	const deUint32*		pQueueFamilyIndices;
+		VK_IMAGE_LAYOUT_UNDEFINED,												//	VkImageLayout		initialLayout;
+	};
+
+	const Unique<VkImage>					image					(createImage(vk, *vkDevice, &imageParams));
+	const UniquePtr<Allocation>				imageMemory				(allocator.allocate(getImageMemoryRequirements(vk, *vkDevice, *image), MemoryRequirement::Any));
+
+	VK_CHECK(vk.bindImageMemory(*vkDevice, *image, imageMemory->getMemory(), imageMemory->getOffset()));
+
+	if (needInterface)
+	{
+		// The pipeline renders four triangles, each with three vertexes.
+		// Test instantialization only provides four data points, each
+		// for one triangle. So we need allocate space of three times of
+		// input buffer's size.
+		const deUint32							inputNumBytes			= deUint32(instance.interfaces.getInputBuffer()->getNumBytes() * 3);
+		// Create an additional buffer and backing memory for one input variable.
+		const VkBufferCreateInfo				vertexInputParams		=
+		{
+			VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,		//	VkStructureType		sType;
+			DE_NULL,									//	const void*			pNext;
+			0u,											//	VkBufferCreateFlags	flags;
+			inputNumBytes,								//	VkDeviceSize		size;
+			VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,			//	VkBufferUsageFlags	usage;
+			VK_SHARING_MODE_EXCLUSIVE,					//	VkSharingMode		sharingMode;
+			1u,											//	deUint32			queueFamilyCount;
+			&queueFamilyIndex,							//	const deUint32*		pQueueFamilyIndices;
+		};
+
+		vertexInputBuffer = createBuffer(vk, *vkDevice, &vertexInputParams);
+		vertexInputMemory = allocator.allocate(getBufferMemoryRequirements(vk, *vkDevice, *vertexInputBuffer), MemoryRequirement::HostVisible);
+		VK_CHECK(vk.bindBufferMemory(*vkDevice, *vertexInputBuffer, vertexInputMemory->getMemory(), vertexInputMemory->getOffset()));
+
+		// Create an additional buffer and backing memory for an output variable.
+		const VkDeviceSize						fragOutputImgSize		= (VkDeviceSize)(instance.interfaces.getOutputType().getNumBytes() * renderSize.x() * renderSize.y());
+		const VkBufferCreateInfo				fragOutputParams		=
+		{
+			VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,		//	VkStructureType		sType;
+			DE_NULL,									//	const void*			pNext;
+			0u,											//	VkBufferCreateFlags	flags;
+			fragOutputImgSize,							//	VkDeviceSize		size;
+			VK_BUFFER_USAGE_TRANSFER_DST_BIT,			//	VkBufferUsageFlags	usage;
+			VK_SHARING_MODE_EXCLUSIVE,					//	VkSharingMode		sharingMode;
+			1u,											//	deUint32			queueFamilyCount;
+			&queueFamilyIndex,							//	const deUint32*		pQueueFamilyIndices;
+		};
+		fragOutputBuffer = createBuffer(vk, *vkDevice, &fragOutputParams);
+		fragOutputMemory = allocator.allocate(getBufferMemoryRequirements(vk, *vkDevice, *fragOutputBuffer), MemoryRequirement::HostVisible);
+		VK_CHECK(vk.bindBufferMemory(*vkDevice, *fragOutputBuffer, fragOutputMemory->getMemory(), fragOutputMemory->getOffset()));
+
+		// Create an additional image and backing memory for attachment.
+		// Reuse the previous imageParams since we only need to change the image format.
+		imageParams.format		= instance.interfaces.getOutputType().getVkFormat();
+		fragOutputImage			= createImage(vk, *vkDevice, &imageParams);
+		fragOutputImageMemory	= allocator.allocate(getImageMemoryRequirements(vk, *vkDevice, *fragOutputImage), MemoryRequirement::Any);
+
+		VK_CHECK(vk.bindImageMemory(*vkDevice, *fragOutputImage, fragOutputImageMemory->getMemory(), fragOutputImageMemory->getOffset()));
+	}
+
+	vector<VkAttachmentDescription>			colorAttDescs			;
+	vector<VkAttachmentReference>			colorAttRefs			;
+	{
+		const VkAttachmentDescription		attDesc					=
+		{
+			0u,												//	VkAttachmentDescriptionFlags	flags;
+			VK_FORMAT_R8G8B8A8_UNORM,						//	VkFormat						format;
+			VK_SAMPLE_COUNT_1_BIT,							//	deUint32						samples;
+			VK_ATTACHMENT_LOAD_OP_CLEAR,					//	VkAttachmentLoadOp				loadOp;
+			VK_ATTACHMENT_STORE_OP_STORE,					//	VkAttachmentStoreOp				storeOp;
+			VK_ATTACHMENT_LOAD_OP_DONT_CARE,				//	VkAttachmentLoadOp				stencilLoadOp;
+			VK_ATTACHMENT_STORE_OP_DONT_CARE,				//	VkAttachmentStoreOp				stencilStoreOp;
+			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,		//	VkImageLayout					initialLayout;
+			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,		//	VkImageLayout					finalLayout;
+		};
+		colorAttDescs.push_back(attDesc);
+
+		const VkAttachmentReference			attRef					=
+		{
+			0u,												//	deUint32		attachment;
+			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,		//	VkImageLayout	layout;
+		};
+		colorAttRefs.push_back(attRef);
+	}
+
+	if (needInterface)
+	{
+		const VkAttachmentDescription		attDesc					=
+		{
+			0u,													//	VkAttachmentDescriptionFlags	flags;
+			instance.interfaces.getOutputType().getVkFormat(),	//	VkFormat						format;
+			VK_SAMPLE_COUNT_1_BIT,								//	deUint32						samples;
+			VK_ATTACHMENT_LOAD_OP_CLEAR,						//	VkAttachmentLoadOp				loadOp;
+			VK_ATTACHMENT_STORE_OP_STORE,						//	VkAttachmentStoreOp				storeOp;
+			VK_ATTACHMENT_LOAD_OP_DONT_CARE,					//	VkAttachmentLoadOp				stencilLoadOp;
+			VK_ATTACHMENT_STORE_OP_DONT_CARE,					//	VkAttachmentStoreOp				stencilStoreOp;
+			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,			//	VkImageLayout					initialLayout;
+			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,			//	VkImageLayout					finalLayout;
+		};
+		colorAttDescs.push_back(attDesc);
+
+		const VkAttachmentReference			attRef					=
+		{
+			1u,												//	deUint32		attachment;
+			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,		//	VkImageLayout	layout;
+		};
+		colorAttRefs.push_back(attRef);
+	}
+
+	VkSubpassDescription					subpassDesc				=
+	{
+		0u,												//	VkSubpassDescriptionFlags		flags;
+		VK_PIPELINE_BIND_POINT_GRAPHICS,				//	VkPipelineBindPoint				pipelineBindPoint;
+		0u,												//	deUint32						inputCount;
+		DE_NULL,										//	const VkAttachmentReference*	pInputAttachments;
+		1u,												//	deUint32						colorCount;
+		colorAttRefs.data(),							//	const VkAttachmentReference*	pColorAttachments;
+		DE_NULL,										//	const VkAttachmentReference*	pResolveAttachments;
+		DE_NULL,										//	const VkAttachmentReference*	pDepthStencilAttachment;
+		0u,												//	deUint32						preserveCount;
+		DE_NULL,										//	const VkAttachmentReference*	pPreserveAttachments;
+
+	};
+	VkRenderPassCreateInfo					renderPassParams		=
+	{
+		VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,		//	VkStructureType					sType;
+		DE_NULL,										//	const void*						pNext;
+		(VkRenderPassCreateFlags)0,
+		1u,												//	deUint32						attachmentCount;
+		colorAttDescs.data(),							//	const VkAttachmentDescription*	pAttachments;
+		1u,												//	deUint32						subpassCount;
+		&subpassDesc,									//	const VkSubpassDescription*		pSubpasses;
+		0u,												//	deUint32						dependencyCount;
+		DE_NULL,										//	const VkSubpassDependency*		pDependencies;
+	};
+
+	if (needInterface)
+	{
+		subpassDesc.colorAttachmentCount += 1;
+		renderPassParams.attachmentCount += 1;
+	}
+
+	const Unique<VkRenderPass>				renderPass				(createRenderPass(vk, *vkDevice, &renderPassParams));
+
+	const VkImageViewCreateInfo				colorAttViewParams		=
+	{
+		VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,		//	VkStructureType				sType;
+		DE_NULL,										//	const void*					pNext;
+		0u,												//	VkImageViewCreateFlags		flags;
+		*image,											//	VkImage						image;
+		VK_IMAGE_VIEW_TYPE_2D,							//	VkImageViewType				viewType;
+		VK_FORMAT_R8G8B8A8_UNORM,						//	VkFormat					format;
+		{
+			VK_COMPONENT_SWIZZLE_R,
+			VK_COMPONENT_SWIZZLE_G,
+			VK_COMPONENT_SWIZZLE_B,
+			VK_COMPONENT_SWIZZLE_A
+		},												//	VkChannelMapping			channels;
+		{
+			VK_IMAGE_ASPECT_COLOR_BIT,						//	VkImageAspectFlags	aspectMask;
+			0u,												//	deUint32			baseMipLevel;
+			1u,												//	deUint32			mipLevels;
+			0u,												//	deUint32			baseArrayLayer;
+			1u,												//	deUint32			arraySize;
+		},												//	VkImageSubresourceRange		subresourceRange;
+	};
+	const Unique<VkImageView>				colorAttView			(createImageView(vk, *vkDevice, &colorAttViewParams));
+
+	vector<VkImageView>						attViews				;
+	attViews.push_back(*colorAttView);
+
+	// Handle resources requested by the test instantiation.
+	const deUint32							numInResources			= static_cast<deUint32>(instance.resources.inputs.size());
+	const deUint32							numOutResources			= static_cast<deUint32>(instance.resources.outputs.size());
+	// These variables should be placed out of the following if block to avoid deallocation after out of scope.
+	vector<AllocationSp>					inResourceMemories		;
+	vector<AllocationSp>					outResourceMemories		;
+	vector<BufferHandleSp>					inResourceBuffers		;
+	vector<BufferHandleSp>					outResourceBuffers		;
+	Move<VkDescriptorPool>					descriptorPool			;
+	Move<VkDescriptorSetLayout>				setLayout				;
+	VkDescriptorSetLayout					rawSetLayout			= DE_NULL;
+	VkDescriptorSet							rawSet					= DE_NULL;
+
+	if (numResources != 0)
+	{
+		vector<VkDescriptorSetLayoutBinding>	setLayoutBindings	;
+		vector<VkDescriptorPoolSize>			poolSizes			;
+
+		setLayoutBindings.reserve(numResources);
+		poolSizes.reserve(numResources);
+
+		// Process all input resources.
+		for (deUint32 inputNdx = 0; inputNdx < numInResources; ++inputNdx)
+		{
+			const Resource&					resource				= instance.resources.inputs[inputNdx];
+			// Create buffer and allocate memory.
+			Move<VkBuffer>					resourceBuffer			= createBufferForResource(vk, *vkDevice, resource, queueFamilyIndex);
+			de::MovePtr<Allocation>			resourceMemory			= allocator.allocate(getBufferMemoryRequirements(vk, *vkDevice, *resourceBuffer), MemoryRequirement::HostVisible);
+
+			VK_CHECK(vk.bindBufferMemory(*vkDevice, *resourceBuffer, resourceMemory->getMemory(), resourceMemory->getOffset()));
+
+			// Copy data to memory.
+			const VkMappedMemoryRange		range					=
+			{
+				VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,				//	VkStructureType	sType;
+				DE_NULL,											//	const void*		pNext;
+				resourceMemory->getMemory(),						//	VkDeviceMemory	mem;
+				0,													//	VkDeviceSize	offset;
+				VK_WHOLE_SIZE,										//	VkDeviceSize	size;
+			};
+
+			deMemcpy(resourceMemory->getHostPtr(), resource.second->data(), resource.second->getNumBytes());
+			VK_CHECK(vk.flushMappedMemoryRanges(*vkDevice, 1u, &range));
+
+			inResourceMemories.push_back(AllocationSp(resourceMemory.release()));
+			inResourceBuffers.push_back(BufferHandleSp(new BufferHandleUp(resourceBuffer)));
+
+			// Prepare descriptor bindings and pool sizes for creating descriptor set layout and pool.
+			const VkDescriptorSetLayoutBinding	binding				=
+			{
+				inputNdx,											// binding
+				resource.first,										// descriptorType
+				1u,													// descriptorCount
+				VK_SHADER_STAGE_ALL_GRAPHICS,						// stageFlags
+				DE_NULL,											// pImmutableSamplers
+			};
+			setLayoutBindings.push_back(binding);
+
+			// Note: the following code doesn't check and unify descriptors of the same type.
+			const VkDescriptorPoolSize		poolSize				=
+			{
+				resource.first,										// type
+				1u,													// descriptorCount
+			};
+			poolSizes.push_back(poolSize);
+		}
+
+		// Process all output resources.
+		for (deUint32 outputNdx = 0; outputNdx < numOutResources; ++outputNdx)
+		{
+			const Resource&					resource				= instance.resources.outputs[outputNdx];
+			// Create buffer and allocate memory.
+			Move<VkBuffer>					resourceBuffer			= createBufferForResource(vk, *vkDevice, resource, queueFamilyIndex);
+			de::MovePtr<Allocation>			resourceMemory			= allocator.allocate(getBufferMemoryRequirements(vk, *vkDevice, *resourceBuffer), MemoryRequirement::HostVisible);
+
+			VK_CHECK(vk.bindBufferMemory(*vkDevice, *resourceBuffer, resourceMemory->getMemory(), resourceMemory->getOffset()));
+
+			// Fill memory with all ones.
+			const VkMappedMemoryRange		range					=
+			{
+				VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,				//	VkStructureType	sType;
+				DE_NULL,											//	const void*		pNext;
+				resourceMemory->getMemory(),						//	VkDeviceMemory	mem;
+				0,													//	VkDeviceSize	offset;
+				VK_WHOLE_SIZE,										//	VkDeviceSize	size;
+			};
+
+			deMemset((deUint8*)resourceMemory->getHostPtr(), 0xff, resource.second->getNumBytes());
+			VK_CHECK(vk.flushMappedMemoryRanges(*vkDevice, 1u, &range));
+
+			outResourceMemories.push_back(AllocationSp(resourceMemory.release()));
+			outResourceBuffers.push_back(BufferHandleSp(new BufferHandleUp(resourceBuffer)));
+
+			// Prepare descriptor bindings and pool sizes for creating descriptor set layout and pool.
+			const VkDescriptorSetLayoutBinding	binding				=
+			{
+				numInResources  + outputNdx,						// binding
+				resource.first,										// descriptorType
+				1u,													// descriptorCount
+				VK_SHADER_STAGE_ALL_GRAPHICS,						// stageFlags
+				DE_NULL,											// pImmutableSamplers
+			};
+			setLayoutBindings.push_back(binding);
+
+			// Note: the following code doesn't check and unify descriptors of the same type.
+			const VkDescriptorPoolSize		poolSize				=
+			{
+				resource.first,										// type
+				1u,													// descriptorCount
+			};
+			poolSizes.push_back(poolSize);
+		}
+
+		// Create descriptor set layout, descriptor pool, and allocate descriptor set.
+		const VkDescriptorSetLayoutCreateInfo	setLayoutParams		=
+		{
+			VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,	// sType
+			DE_NULL,												// pNext
+			(VkDescriptorSetLayoutCreateFlags)0,					// flags
+			numResources,											// bindingCount
+			setLayoutBindings.data(),								// pBindings
+		};
+		setLayout													= createDescriptorSetLayout(vk, *vkDevice, &setLayoutParams);
+		rawSetLayout												= *setLayout;
+
+		const VkDescriptorPoolCreateInfo		poolParams			=
+		{
+			VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,			// sType
+			DE_NULL,												// pNext
+			(VkDescriptorPoolCreateFlags)0,							// flags
+			1u,														// maxSets
+			numResources,											// poolSizeCount
+			poolSizes.data(),										// pPoolSizes
+		};
+		descriptorPool												= createDescriptorPool(vk, *vkDevice, &poolParams);
+
+		const VkDescriptorSetAllocateInfo		setAllocParams		=
+		{
+			VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,			// sType
+			DE_NULL,												// pNext
+			*descriptorPool,										// descriptorPool
+			1u,														// descriptorSetCount
+			&rawSetLayout,											// pSetLayouts
+		};
+		VK_CHECK(vk.allocateDescriptorSets(*vkDevice, &setAllocParams, &rawSet));
+
+		// Update descriptor set.
+		vector<VkWriteDescriptorSet>			writeSpecs			;
+		vector<VkDescriptorBufferInfo>			dBufferInfos		;
+
+		writeSpecs.reserve(numResources);
+		dBufferInfos.reserve(numResources);
+
+		for (deUint32 inputNdx = 0; inputNdx < numInResources; ++inputNdx)
+		{
+			const VkDescriptorBufferInfo		bufInfo				=
+			{
+				**inResourceBuffers[inputNdx],						// buffer
+				0,													// offset
+				VK_WHOLE_SIZE,										// size
+			};
+			dBufferInfos.push_back(bufInfo);
+
+			const VkWriteDescriptorSet			writeSpec			= {
+				VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,				// sType
+				DE_NULL,											// pNext
+				rawSet,												// dstSet
+				inputNdx,											// binding
+				0,													// dstArrayElement
+				1u,													// descriptorCount
+				instance.resources.inputs[inputNdx].first,			// descriptorType
+				DE_NULL,											// pImageInfo
+				&dBufferInfos.back(),								// pBufferInfo
+				DE_NULL,											// pTexelBufferView
+			};
+			writeSpecs.push_back(writeSpec);
+		}
+		for (deUint32 outputNdx = 0; outputNdx < numOutResources; ++outputNdx)
+		{
+			const VkDescriptorBufferInfo		bufInfo				=
+			{
+				**outResourceBuffers[outputNdx],					// buffer
+				0,													// offset
+				VK_WHOLE_SIZE,										// size
+			};
+			dBufferInfos.push_back(bufInfo);
+
+			const VkWriteDescriptorSet			writeSpec			= {
+				VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,				// sType
+				DE_NULL,											// pNext
+				rawSet,												// dstSet
+				numInResources + outputNdx,							// binding
+				0,													// dstArrayElement
+				1u,													// descriptorCount
+				instance.resources.outputs[outputNdx].first,		// descriptorType
+				DE_NULL,											// pImageInfo
+				&dBufferInfos.back(),								// pBufferInfo
+				DE_NULL,											// pTexelBufferView
+			};
+			writeSpecs.push_back(writeSpec);
+		}
+		vk.updateDescriptorSets(*vkDevice, numResources, writeSpecs.data(), 0, DE_NULL);
+	}
+
+	// Pipeline layout
+	VkPipelineLayoutCreateInfo				pipelineLayoutParams	=
+	{
+		VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,			//	VkStructureType					sType;
+		DE_NULL,												//	const void*						pNext;
+		(VkPipelineLayoutCreateFlags)0,
+		0u,														//	deUint32						descriptorSetCount;
+		DE_NULL,												//	const VkDescriptorSetLayout*	pSetLayouts;
+		0u,														//	deUint32						pushConstantRangeCount;
+		DE_NULL,												//	const VkPushConstantRange*		pPushConstantRanges;
+	};
+
+	VkPushConstantRange						pushConstantRange		=
+	{
+		VK_SHADER_STAGE_ALL_GRAPHICS,							// VkShaderStageFlags    stageFlags;
+		0,														// uint32_t              offset;
+		0,														// uint32_t              size;
+	};
+	if (hasPushConstants)
+	{
+		pushConstantRange.size						= static_cast<deUint32>(instance.pushConstants.getBuffer()->getNumBytes());
+		pipelineLayoutParams.pushConstantRangeCount	= 1;
+		pipelineLayoutParams.pPushConstantRanges	= &pushConstantRange;
+	}
+	if (numResources != 0)
+	{
+		// Update pipeline layout with the descriptor set layout.
+		pipelineLayoutParams.setLayoutCount								= 1;
+		pipelineLayoutParams.pSetLayouts								= &rawSetLayout;
+	}
+	const Unique<VkPipelineLayout>			pipelineLayout			(createPipelineLayout(vk, *vkDevice, &pipelineLayoutParams));
+
+	// Pipeline
+	vector<VkPipelineShaderStageCreateInfo>		shaderStageParams;
+	// We need these vectors to make sure that information about specialization constants for each stage can outlive createGraphicsPipeline().
+	vector<vector<VkSpecializationMapEntry> >	specConstantEntries;
+	vector<VkSpecializationInfo>				specializationInfos;
+	createPipelineShaderStages(vk, *vkDevice, instance, context, modules, shaderStageParams);
+
+	// And we don't want the reallocation of these vectors to invalidate pointers pointing to their contents.
+	specConstantEntries.reserve(shaderStageParams.size());
+	specializationInfos.reserve(shaderStageParams.size());
+
+	// Patch the specialization info field in PipelineShaderStageCreateInfos.
+	for (vector<VkPipelineShaderStageCreateInfo>::iterator stageInfo = shaderStageParams.begin(); stageInfo != shaderStageParams.end(); ++stageInfo)
+	{
+		const StageToSpecConstantMap::const_iterator stageIt = instance.specConstants.find(stageInfo->stage);
+
+		if (stageIt != instance.specConstants.end())
+		{
+			const size_t						numSpecConstants	= stageIt->second.size();
+			vector<VkSpecializationMapEntry>	entries;
+			VkSpecializationInfo				specInfo;
+
+			entries.resize(numSpecConstants);
+
+			// Only support 32-bit integers as spec constants now. And their constant IDs are numbered sequentially starting from 0.
+			for (size_t ndx = 0; ndx < numSpecConstants; ++ndx)
+			{
+				entries[ndx].constantID	= (deUint32)ndx;
+				entries[ndx].offset		= deUint32(ndx * sizeof(deInt32));
+				entries[ndx].size		= sizeof(deInt32);
+			}
+
+			specConstantEntries.push_back(entries);
+
+			specInfo.mapEntryCount	= (deUint32)numSpecConstants;
+			specInfo.pMapEntries	= specConstantEntries.back().data();
+			specInfo.dataSize		= numSpecConstants * sizeof(deInt32);
+			specInfo.pData			= stageIt->second.data();
+			specializationInfos.push_back(specInfo);
+
+			stageInfo->pSpecializationInfo = &specializationInfos.back();
+		}
+	}
+	const VkPipelineDepthStencilStateCreateInfo	depthStencilParams		=
+	{
+		VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,	//	VkStructureType		sType;
+		DE_NULL,													//	const void*			pNext;
+		(VkPipelineDepthStencilStateCreateFlags)0,
+		DE_FALSE,													//	deUint32			depthTestEnable;
+		DE_FALSE,													//	deUint32			depthWriteEnable;
+		VK_COMPARE_OP_ALWAYS,										//	VkCompareOp			depthCompareOp;
+		DE_FALSE,													//	deUint32			depthBoundsTestEnable;
+		DE_FALSE,													//	deUint32			stencilTestEnable;
+		{
+			VK_STENCIL_OP_KEEP,											//	VkStencilOp	stencilFailOp;
+			VK_STENCIL_OP_KEEP,											//	VkStencilOp	stencilPassOp;
+			VK_STENCIL_OP_KEEP,											//	VkStencilOp	stencilDepthFailOp;
+			VK_COMPARE_OP_ALWAYS,										//	VkCompareOp	stencilCompareOp;
+			0u,															//	deUint32	stencilCompareMask;
+			0u,															//	deUint32	stencilWriteMask;
+			0u,															//	deUint32	stencilReference;
+		},															//	VkStencilOpState	front;
+		{
+			VK_STENCIL_OP_KEEP,											//	VkStencilOp	stencilFailOp;
+			VK_STENCIL_OP_KEEP,											//	VkStencilOp	stencilPassOp;
+			VK_STENCIL_OP_KEEP,											//	VkStencilOp	stencilDepthFailOp;
+			VK_COMPARE_OP_ALWAYS,										//	VkCompareOp	stencilCompareOp;
+			0u,															//	deUint32	stencilCompareMask;
+			0u,															//	deUint32	stencilWriteMask;
+			0u,															//	deUint32	stencilReference;
+		},															//	VkStencilOpState	back;
+		-1.0f,														//	float				minDepthBounds;
+		+1.0f,														//	float				maxDepthBounds;
+	};
+	const VkViewport						viewport0				=
+	{
+		0.0f,														//	float	originX;
+		0.0f,														//	float	originY;
+		(float)renderSize.x(),										//	float	width;
+		(float)renderSize.y(),										//	float	height;
+		0.0f,														//	float	minDepth;
+		1.0f,														//	float	maxDepth;
+	};
+	const VkRect2D							scissor0				=
+	{
+		{
+			0u,															//	deInt32	x;
+			0u,															//	deInt32	y;
+		},															//	VkOffset2D	offset;
+		{
+			renderSize.x(),												//	deInt32	width;
+			renderSize.y(),												//	deInt32	height;
+		},															//	VkExtent2D	extent;
+	};
+	const VkPipelineViewportStateCreateInfo		viewportParams			=
+	{
+		VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,		//	VkStructureType		sType;
+		DE_NULL,													//	const void*			pNext;
+		(VkPipelineViewportStateCreateFlags)0,
+		1u,															//	deUint32			viewportCount;
+		&viewport0,
+		1u,
+		&scissor0
+	};
+	const VkSampleMask							sampleMask				= ~0u;
+	const VkPipelineMultisampleStateCreateInfo	multisampleParams		=
+	{
+		VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,	//	VkStructureType			sType;
+		DE_NULL,													//	const void*				pNext;
+		(VkPipelineMultisampleStateCreateFlags)0,
+		VK_SAMPLE_COUNT_1_BIT,										//	VkSampleCountFlagBits	rasterSamples;
+		DE_FALSE,													//	deUint32				sampleShadingEnable;
+		0.0f,														//	float					minSampleShading;
+		&sampleMask,												//	const VkSampleMask*		pSampleMask;
+		DE_FALSE,													//	VkBool32				alphaToCoverageEnable;
+		DE_FALSE,													//	VkBool32				alphaToOneEnable;
+	};
+	const VkPipelineRasterizationStateCreateInfo	rasterParams		=
+	{
+		VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,	//	VkStructureType	sType;
+		DE_NULL,													//	const void*		pNext;
+		(VkPipelineRasterizationStateCreateFlags)0,
+		DE_TRUE,													//	deUint32		depthClipEnable;
+		DE_FALSE,													//	deUint32		rasterizerDiscardEnable;
+		VK_POLYGON_MODE_FILL,										//	VkFillMode		fillMode;
+		VK_CULL_MODE_NONE,											//	VkCullMode		cullMode;
+		VK_FRONT_FACE_COUNTER_CLOCKWISE,							//	VkFrontFace		frontFace;
+		VK_FALSE,													//	VkBool32		depthBiasEnable;
+		0.0f,														//	float			depthBias;
+		0.0f,														//	float			depthBiasClamp;
+		0.0f,														//	float			slopeScaledDepthBias;
+		1.0f,														//	float			lineWidth;
+	};
+	const VkPrimitiveTopology topology = hasTessellation? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+	const VkPipelineInputAssemblyStateCreateInfo	inputAssemblyParams	=
+	{
+		VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,	//	VkStructureType		sType;
+		DE_NULL,														//	const void*			pNext;
+		(VkPipelineInputAssemblyStateCreateFlags)0,
+		topology,														//	VkPrimitiveTopology	topology;
+		DE_FALSE,														//	deUint32			primitiveRestartEnable;
+	};
+
+	vector<VkVertexInputBindingDescription>		vertexBindings;
+	vector<VkVertexInputAttributeDescription>	vertexAttribs;
+
+	const VkVertexInputBindingDescription		vertexBinding0			=
+	{
+		0u,									// deUint32					binding;
+		deUint32(singleVertexDataSize),		// deUint32					strideInBytes;
+		VK_VERTEX_INPUT_RATE_VERTEX			// VkVertexInputStepRate	stepRate;
+	};
+	vertexBindings.push_back(vertexBinding0);
+
+	{
+		VkVertexInputAttributeDescription		attr0					=
+		{
+			0u,									// deUint32	location;
+			0u,									// deUint32	binding;
+			VK_FORMAT_R32G32B32A32_SFLOAT,		// VkFormat	format;
+			0u									// deUint32	offsetInBytes;
+		};
+		vertexAttribs.push_back(attr0);
+
+		VkVertexInputAttributeDescription		attr1					=
+		{
+			1u,									// deUint32	location;
+			0u,									// deUint32	binding;
+			VK_FORMAT_R32G32B32A32_SFLOAT,		// VkFormat	format;
+			sizeof(Vec4),						// deUint32	offsetInBytes;
+		};
+		vertexAttribs.push_back(attr1);
+	};
+
+	// If the test instantiation has additional input/output interface variables, we need to create additional bindings.
+	// Right now we only support one additional input varible for the vertex stage, and that will be bound to binding #1
+	// with location #2.
+	if (needInterface)
+	{
+		const VkVertexInputBindingDescription	vertexBinding1			=
+		{
+			1u,													// deUint32					binding;
+			instance.interfaces.getInputType().getNumBytes(),	// deUint32					strideInBytes;
+			VK_VERTEX_INPUT_RATE_VERTEX							// VkVertexInputStepRate	stepRate;
+		};
+		vertexBindings.push_back(vertexBinding1);
+
+		VkVertexInputAttributeDescription		attr					=
+		{
+			2u,													// deUint32	location;
+			1u,													// deUint32	binding;
+			instance.interfaces.getInputType().getVkFormat(),	// VkFormat	format;
+			0,													// deUint32	offsetInBytes;
+		};
+		vertexAttribs.push_back(attr);
+	}
+
+	VkPipelineVertexInputStateCreateInfo		vertexInputStateParams	=
+	{
+		VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,	//	VkStructureType								sType;
+		DE_NULL,													//	const void*									pNext;
+		(VkPipelineVertexInputStateCreateFlags)0,
+		1u,															//	deUint32									bindingCount;
+		vertexBindings.data(),										//	const VkVertexInputBindingDescription*		pVertexBindingDescriptions;
+		2u,															//	deUint32									attributeCount;
+		vertexAttribs.data(),										//	const VkVertexInputAttributeDescription*	pVertexAttributeDescriptions;
+	};
+
+	if (needInterface)
+	{
+		vertexInputStateParams.vertexBindingDescriptionCount += 1;
+		vertexInputStateParams.vertexAttributeDescriptionCount += 1;
+	}
+
+	vector<VkPipelineColorBlendAttachmentState>	attBlendStates			;
+	const VkPipelineColorBlendAttachmentState	attBlendState			=
+	{
+		DE_FALSE,													//	deUint32		blendEnable;
+		VK_BLEND_FACTOR_ONE,										//	VkBlend			srcBlendColor;
+		VK_BLEND_FACTOR_ZERO,										//	VkBlend			destBlendColor;
+		VK_BLEND_OP_ADD,											//	VkBlendOp		blendOpColor;
+		VK_BLEND_FACTOR_ONE,										//	VkBlend			srcBlendAlpha;
+		VK_BLEND_FACTOR_ZERO,										//	VkBlend			destBlendAlpha;
+		VK_BLEND_OP_ADD,											//	VkBlendOp		blendOpAlpha;
+		(VK_COLOR_COMPONENT_R_BIT|
+		 VK_COLOR_COMPONENT_G_BIT|
+		 VK_COLOR_COMPONENT_B_BIT|
+		 VK_COLOR_COMPONENT_A_BIT),									//	VkChannelFlags	channelWriteMask;
+	};
+	attBlendStates.push_back(attBlendState);
+
+	if (needInterface)
+		attBlendStates.push_back(attBlendState);
+
+	VkPipelineColorBlendStateCreateInfo		blendParams				=
+	{
+		VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,	//	VkStructureType								sType;
+		DE_NULL,													//	const void*									pNext;
+		(VkPipelineColorBlendStateCreateFlags)0,
+		DE_FALSE,													//	VkBool32									logicOpEnable;
+		VK_LOGIC_OP_COPY,											//	VkLogicOp									logicOp;
+		1u,															//	deUint32									attachmentCount;
+		attBlendStates.data(),										//	const VkPipelineColorBlendAttachmentState*	pAttachments;
+		{ 0.0f, 0.0f, 0.0f, 0.0f },									//	float										blendConst[4];
+	};
+	if (needInterface)
+	{
+		blendParams.attachmentCount += 1;
+	}
+	const VkPipelineTessellationStateCreateInfo	tessellationState	=
+	{
+		VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
+		DE_NULL,
+		(VkPipelineTessellationStateCreateFlags)0,
+		3u
+	};
+
+	const VkPipelineTessellationStateCreateInfo* tessellationInfo	=	hasTessellation ? &tessellationState: DE_NULL;
+	const VkGraphicsPipelineCreateInfo		pipelineParams			=
+	{
+		VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,		//	VkStructureType									sType;
+		DE_NULL,												//	const void*										pNext;
+		0u,														//	VkPipelineCreateFlags							flags;
+		(deUint32)shaderStageParams.size(),						//	deUint32										stageCount;
+		&shaderStageParams[0],									//	const VkPipelineShaderStageCreateInfo*			pStages;
+		&vertexInputStateParams,								//	const VkPipelineVertexInputStateCreateInfo*		pVertexInputState;
+		&inputAssemblyParams,									//	const VkPipelineInputAssemblyStateCreateInfo*	pInputAssemblyState;
+		tessellationInfo,										//	const VkPipelineTessellationStateCreateInfo*	pTessellationState;
+		&viewportParams,										//	const VkPipelineViewportStateCreateInfo*		pViewportState;
+		&rasterParams,											//	const VkPipelineRasterStateCreateInfo*			pRasterState;
+		&multisampleParams,										//	const VkPipelineMultisampleStateCreateInfo*		pMultisampleState;
+		&depthStencilParams,									//	const VkPipelineDepthStencilStateCreateInfo*	pDepthStencilState;
+		&blendParams,											//	const VkPipelineColorBlendStateCreateInfo*		pColorBlendState;
+		(const VkPipelineDynamicStateCreateInfo*)DE_NULL,		//	const VkPipelineDynamicStateCreateInfo*			pDynamicState;
+		*pipelineLayout,										//	VkPipelineLayout								layout;
+		*renderPass,											//	VkRenderPass									renderPass;
+		0u,														//	deUint32										subpass;
+		DE_NULL,												//	VkPipeline										basePipelineHandle;
+		0u,														//	deInt32											basePipelineIndex;
+	};
+
+	const Unique<VkPipeline>				pipeline				(createGraphicsPipeline(vk, *vkDevice, DE_NULL, &pipelineParams));
+
+	if (needInterface)
+	{
+		const VkImageViewCreateInfo			fragOutputViewParams	=
+		{
+			VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,			//	VkStructureType				sType;
+			DE_NULL,											//	const void*					pNext;
+			0u,													//	VkImageViewCreateFlags		flags;
+			*fragOutputImage,									//	VkImage						image;
+			VK_IMAGE_VIEW_TYPE_2D,								//	VkImageViewType				viewType;
+			instance.interfaces.getOutputType().getVkFormat(),	//	VkFormat					format;
+			{
+				VK_COMPONENT_SWIZZLE_R,
+				VK_COMPONENT_SWIZZLE_G,
+				VK_COMPONENT_SWIZZLE_B,
+				VK_COMPONENT_SWIZZLE_A
+			},													//	VkChannelMapping			channels;
+			{
+				VK_IMAGE_ASPECT_COLOR_BIT,						//	VkImageAspectFlags	aspectMask;
+				0u,												//	deUint32			baseMipLevel;
+				1u,												//	deUint32			mipLevels;
+				0u,												//	deUint32			baseArrayLayer;
+				1u,												//	deUint32			arraySize;
+			},													//	VkImageSubresourceRange		subresourceRange;
+		};
+		fragOutputImageView = createImageView(vk, *vkDevice, &fragOutputViewParams);
+		attViews.push_back(*fragOutputImageView);
+	}
+
+	// Framebuffer
+	VkFramebufferCreateInfo					framebufferParams		=
+	{
+		VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,				//	VkStructureType		sType;
+		DE_NULL,												//	const void*			pNext;
+		(VkFramebufferCreateFlags)0,
+		*renderPass,											//	VkRenderPass		renderPass;
+		1u,														//	deUint32			attachmentCount;
+		attViews.data(),										//	const VkImageView*	pAttachments;
+		(deUint32)renderSize.x(),								//	deUint32			width;
+		(deUint32)renderSize.y(),								//	deUint32			height;
+		1u,														//	deUint32			layers;
+	};
+
+	if (needInterface)
+		framebufferParams.attachmentCount += 1;
+
+	const Unique<VkFramebuffer>				framebuffer				(createFramebuffer(vk, *vkDevice, &framebufferParams));
+
+	const Unique<VkCommandPool>				cmdPool					(createCommandPool(vk, *vkDevice, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
+
+	// Command buffer
+	const Unique<VkCommandBuffer>			cmdBuf					(allocateCommandBuffer(vk, *vkDevice, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
+
+	const VkCommandBufferBeginInfo			cmdBufBeginParams		=
+	{
+		VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,			//	VkStructureType				sType;
+		DE_NULL,												//	const void*					pNext;
+		(VkCommandBufferUsageFlags)0,
+		(const VkCommandBufferInheritanceInfo*)DE_NULL,
+	};
+
+	// Record commands
+	VK_CHECK(vk.beginCommandBuffer(*cmdBuf, &cmdBufBeginParams));
+
+	{
+		const VkMemoryBarrier			vertFlushBarrier	=
+		{
+			VK_STRUCTURE_TYPE_MEMORY_BARRIER,			//	VkStructureType		sType;
+			DE_NULL,									//	const void*			pNext;
+			VK_ACCESS_HOST_WRITE_BIT,					//	VkMemoryOutputFlags	outputMask;
+			VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT,		//	VkMemoryInputFlags	inputMask;
+		};
+		vector<VkImageMemoryBarrier>	colorAttBarriers	;
+
+		VkImageMemoryBarrier			imgBarrier          =
+		{
+			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,		//	VkStructureType			sType;
+			DE_NULL,									//	const void*				pNext;
+			0u,											//	VkMemoryOutputFlags		outputMask;
+			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,		//	VkMemoryInputFlags		inputMask;
+			VK_IMAGE_LAYOUT_UNDEFINED,					//	VkImageLayout			oldLayout;
+			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	//	VkImageLayout			newLayout;
+			queueFamilyIndex,							//	deUint32				srcQueueFamilyIndex;
+			queueFamilyIndex,							//	deUint32				destQueueFamilyIndex;
+			*image,										//	VkImage					image;
+			{
+				VK_IMAGE_ASPECT_COLOR_BIT,					//	VkImageAspect	aspect;
+				0u,											//	deUint32		baseMipLevel;
+				1u,											//	deUint32		mipLevels;
+				0u,											//	deUint32		baseArraySlice;
+				1u,											//	deUint32		arraySize;
+			}											//	VkImageSubresourceRange	subresourceRange;
+		};
+		colorAttBarriers.push_back(imgBarrier);
+		if (needInterface)
+		{
+			imgBarrier.image = *fragOutputImage;
+			colorAttBarriers.push_back(imgBarrier);
+			vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, (VkDependencyFlags)0, 1, &vertFlushBarrier, 0, (const VkBufferMemoryBarrier*)DE_NULL, 2, colorAttBarriers.data());
+		}
+		else
+		{
+			vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, (VkDependencyFlags)0, 1, &vertFlushBarrier, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, colorAttBarriers.data());
+		}
+	}
+
+	{
+		vector<VkClearValue>			clearValue		;
+		clearValue.push_back(makeClearValueColorF32(0.125f, 0.25f, 0.75f, 1.0f));
+		if (needInterface)
+		{
+			clearValue.push_back(makeClearValueColorU32(0, 0, 0, 0));
+		}
+		VkRenderPassBeginInfo			passBeginParams	=
+		{
+			VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,			//	VkStructureType		sType;
+			DE_NULL,											//	const void*			pNext;
+			*renderPass,										//	VkRenderPass		renderPass;
+			*framebuffer,										//	VkFramebuffer		framebuffer;
+			{ { 0, 0 }, { renderSize.x(), renderSize.y() } },	//	VkRect2D			renderArea;
+			1u,													//	deUint32			clearValueCount;
+			clearValue.data(),									//	const VkClearValue*	pClearValues;
+		};
+		if (needInterface)
+		{
+			passBeginParams.clearValueCount += 1;
+		}
+		vk.cmdBeginRenderPass(*cmdBuf, &passBeginParams, VK_SUBPASS_CONTENTS_INLINE);
+	}
+
+	vk.cmdBindPipeline(*cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
+	{
+		const VkDeviceSize bindingOffset = 0;
+		vk.cmdBindVertexBuffers(*cmdBuf, 0u, 1u, &vertexBuffer.get(), &bindingOffset);
+	}
+	if (needInterface)
+	{
+		const VkDeviceSize bindingOffset = 0;
+		vk.cmdBindVertexBuffers(*cmdBuf, 1u, 1u, &vertexInputBuffer.get(), &bindingOffset);
+	}
+	if (hasPushConstants)
+	{
+		const deUint32	size	= static_cast<deUint32>(instance.pushConstants.getBuffer()->getNumBytes());
+		const void*		data	= instance.pushConstants.getBuffer()->data();
+
+		vk.cmdPushConstants(*cmdBuf, *pipelineLayout, VK_SHADER_STAGE_ALL_GRAPHICS, 0, size, data);
+	}
+	if (numResources != 0)
+	{
+		// Bind to set number 0.
+		vk.cmdBindDescriptorSets(*cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0, 1, &rawSet, 0, DE_NULL);
+	}
+	vk.cmdDraw(*cmdBuf, deUint32(vertexCount), 1u /*run pipeline once*/, 0u /*first vertex*/, 0u /*first instanceIndex*/);
+	vk.cmdEndRenderPass(*cmdBuf);
+
+	{
+		vector<VkImageMemoryBarrier>	renderFinishBarrier;
+		VkImageMemoryBarrier			imgBarrier				=
+		{
+			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,		//	VkStructureType			sType;
+			DE_NULL,									//	const void*				pNext;
+			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,		//	VkMemoryOutputFlags		outputMask;
+			VK_ACCESS_TRANSFER_READ_BIT,				//	VkMemoryInputFlags		inputMask;
+			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	//	VkImageLayout			oldLayout;
+			VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,		//	VkImageLayout			newLayout;
+			queueFamilyIndex,							//	deUint32				srcQueueFamilyIndex;
+			queueFamilyIndex,							//	deUint32				destQueueFamilyIndex;
+			*image,										//	VkImage					image;
+			{
+				VK_IMAGE_ASPECT_COLOR_BIT,					//	VkImageAspectFlags	aspectMask;
+				0u,											//	deUint32			baseMipLevel;
+				1u,											//	deUint32			mipLevels;
+				0u,											//	deUint32			baseArraySlice;
+				1u,											//	deUint32			arraySize;
+			}											//	VkImageSubresourceRange	subresourceRange;
+		};
+		renderFinishBarrier.push_back(imgBarrier);
+
+		if (needInterface)
+		{
+			imgBarrier.image = *fragOutputImage;
+			renderFinishBarrier.push_back(imgBarrier);
+			vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 2, renderFinishBarrier.data());
+		}
+		else
+		{
+			vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, renderFinishBarrier.data());
+		}
+	}
+
+	{
+		const VkBufferImageCopy	copyParams	=
+		{
+			(VkDeviceSize)0u,						//	VkDeviceSize			bufferOffset;
+			(deUint32)renderSize.x(),				//	deUint32				bufferRowLength;
+			(deUint32)renderSize.y(),				//	deUint32				bufferImageHeight;
+			{
+				VK_IMAGE_ASPECT_COLOR_BIT,				//	VkImageAspect		aspect;
+				0u,										//	deUint32			mipLevel;
+				0u,										//	deUint32			arrayLayer;
+				1u,										//	deUint32			arraySize;
+			},										//	VkImageSubresourceCopy	imageSubresource;
+			{ 0u, 0u, 0u },							//	VkOffset3D				imageOffset;
+			{ renderSize.x(), renderSize.y(), 1u }	//	VkExtent3D				imageExtent;
+		};
+		vk.cmdCopyImageToBuffer(*cmdBuf, *image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *readImageBuffer, 1u, &copyParams);
+
+		if (needInterface)
+		{
+			vk.cmdCopyImageToBuffer(*cmdBuf, *fragOutputImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *fragOutputBuffer, 1u, &copyParams);
+		}
+	}
+
+	{
+		vector<VkBufferMemoryBarrier> cpFinishBarriers		;
+		VkBufferMemoryBarrier			copyFinishBarrier	=
+		{
+			VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,	//	VkStructureType		sType;
+			DE_NULL,									//	const void*			pNext;
+			VK_ACCESS_TRANSFER_WRITE_BIT,				//	VkMemoryOutputFlags	outputMask;
+			VK_ACCESS_HOST_READ_BIT,					//	VkMemoryInputFlags	inputMask;
+			queueFamilyIndex,							//	deUint32			srcQueueFamilyIndex;
+			queueFamilyIndex,							//	deUint32			destQueueFamilyIndex;
+			*readImageBuffer,							//	VkBuffer			buffer;
+			0u,											//	VkDeviceSize		offset;
+			imageSizeBytes								//	VkDeviceSize		size;
+		};
+		cpFinishBarriers.push_back(copyFinishBarrier);
+
+		if (needInterface)
+		{
+			copyFinishBarrier.buffer	= *fragOutputBuffer;
+			copyFinishBarrier.size		= VK_WHOLE_SIZE;
+			cpFinishBarriers.push_back(copyFinishBarrier);
+
+			vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 2, cpFinishBarriers.data(), 0, (const VkImageMemoryBarrier*)DE_NULL);
+		}
+		else
+		{
+			vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, cpFinishBarriers.data(), 0, (const VkImageMemoryBarrier*)DE_NULL);
+		}
+	}
+
+	VK_CHECK(vk.endCommandBuffer(*cmdBuf));
+
+	// Upload vertex data
+	{
+		const VkMappedMemoryRange	range			=
+		{
+			VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,	//	VkStructureType	sType;
+			DE_NULL,								//	const void*		pNext;
+			vertexBufferMemory->getMemory(),		//	VkDeviceMemory	mem;
+			0,										//	VkDeviceSize	offset;
+			(VkDeviceSize)sizeof(vertexData),		//	VkDeviceSize	size;
+		};
+		void*						vertexBufPtr	= vertexBufferMemory->getHostPtr();
+
+		deMemcpy(vertexBufPtr, &vertexData[0], sizeof(vertexData));
+		VK_CHECK(vk.flushMappedMemoryRanges(*vkDevice, 1u, &range));
+	}
+
+	if (needInterface)
+	{
+		const deUint32				typNumBytes		= instance.interfaces.getInputType().getNumBytes();
+		const deUint32				bufNumBytes		= static_cast<deUint32>(instance.interfaces.getInputBuffer()->getNumBytes());
+
+		// Require that the test instantation provides four output values.
+		DE_ASSERT(bufNumBytes == 4 * typNumBytes);
+
+		// We have four triangles. Because interpolation happens before executing the fragment shader,
+		// we need to provide the same vertex attribute for the same triangle. That means, duplicate each
+		// value three times for all four values.
+
+		const deUint8*				provided		= static_cast<const deUint8*>(instance.interfaces.getInputBuffer()->data());
+		vector<deUint8>				data;
+
+		data.reserve(3 * bufNumBytes);
+
+		for (deUint32 offset = 0; offset < bufNumBytes; offset += typNumBytes)
+			for (deUint32 vertexNdx = 0; vertexNdx < 3; ++vertexNdx)
+				for (deUint32 byteNdx = 0; byteNdx < typNumBytes; ++byteNdx)
+					data.push_back(provided[offset + byteNdx]);
+
+		deMemcpy(vertexInputMemory->getHostPtr(), data.data(), data.size());
+
+		const VkMappedMemoryRange	range			=
+		{
+			VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,	//	VkStructureType	sType;
+			DE_NULL,								//	const void*		pNext;
+			vertexInputMemory->getMemory(),			//	VkDeviceMemory	mem;
+			0,										//	VkDeviceSize	offset;
+			VK_WHOLE_SIZE,							//	VkDeviceSize	size;
+		};
+
+		VK_CHECK(vk.flushMappedMemoryRanges(*vkDevice, 1u, &range));
+	}
+
+	// Submit & wait for completion
+	{
+		const VkFenceCreateInfo	fenceParams	=
+		{
+			VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,	//	VkStructureType		sType;
+			DE_NULL,								//	const void*			pNext;
+			0u,										//	VkFenceCreateFlags	flags;
+		};
+		const Unique<VkFence>	fence		(createFence(vk, *vkDevice, &fenceParams));
+		const VkSubmitInfo		submitInfo	=
+		{
+			VK_STRUCTURE_TYPE_SUBMIT_INFO,
+			DE_NULL,
+			0u,
+			(const VkSemaphore*)DE_NULL,
+			(const VkPipelineStageFlags*)DE_NULL,
+			1u,
+			&cmdBuf.get(),
+			0u,
+			(const VkSemaphore*)DE_NULL,
+		};
+
+		VK_CHECK(vk.queueSubmit(queue, 1u, &submitInfo, *fence));
+		VK_CHECK(vk.waitForFences(*vkDevice, 1u, &fence.get(), DE_TRUE, ~0ull));
+	}
+
+	const void* imagePtr	= readImageBufferMemory->getHostPtr();
+	const tcu::ConstPixelBufferAccess pixelBuffer(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8),
+												  renderSize.x(), renderSize.y(), 1, imagePtr);
+	// Log image
+	{
+		const VkMappedMemoryRange	range		=
+		{
+			VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,	//	VkStructureType	sType;
+			DE_NULL,								//	const void*		pNext;
+			readImageBufferMemory->getMemory(),		//	VkDeviceMemory	mem;
+			0,										//	VkDeviceSize	offset;
+			imageSizeBytes,							//	VkDeviceSize	size;
+		};
+
+		VK_CHECK(vk.invalidateMappedMemoryRanges(*vkDevice, 1u, &range));
+		context.getTestContext().getLog() << TestLog::Image("Result", "Result", pixelBuffer);
+	}
+
+	{ // Make sure all output resources are ready.
+		for (deUint32 outputNdx = 0; outputNdx < numOutResources; ++outputNdx)
+		{
+			const VkMappedMemoryRange	range	=
+			{
+				VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,			//	VkStructureType	sType;
+				DE_NULL,										//	const void*		pNext;
+				outResourceMemories[outputNdx]->getMemory(),	//	VkDeviceMemory	mem;
+				0,												//	VkDeviceSize	offset;
+				VK_WHOLE_SIZE,									//	VkDeviceSize	size;
+			};
+
+			VK_CHECK(vk.invalidateMappedMemoryRanges(*vkDevice, 1u, &range));
+		}
+	}
+
+	const RGBA threshold(1, 1, 1, 1);
+
+	const RGBA upperLeft(pixelBuffer.getPixel(1, 1));
+	if (!tcu::compareThreshold(upperLeft, instance.outputColors[0], threshold))
+		return TestStatus(instance.failResult, instance.getSpecializedFailMessage("Upper left corner mismatch"));
+
+	const RGBA upperRight(pixelBuffer.getPixel(pixelBuffer.getWidth() - 1, 1));
+	if (!tcu::compareThreshold(upperRight, instance.outputColors[1], threshold))
+		return TestStatus(instance.failResult, instance.getSpecializedFailMessage("Upper right corner mismatch"));
+
+	const RGBA lowerLeft(pixelBuffer.getPixel(1, pixelBuffer.getHeight() - 1));
+	if (!tcu::compareThreshold(lowerLeft, instance.outputColors[2], threshold))
+		return TestStatus(instance.failResult, instance.getSpecializedFailMessage("Lower left corner mismatch"));
+
+	const RGBA lowerRight(pixelBuffer.getPixel(pixelBuffer.getWidth() - 1, pixelBuffer.getHeight() - 1));
+	if (!tcu::compareThreshold(lowerRight, instance.outputColors[3], threshold))
+		return TestStatus(instance.failResult, instance.getSpecializedFailMessage("Lower right corner mismatch"));
+
+	// Check that the contents in the ouput variable matches expected.
+	if (needInterface)
+	{
+		const IFDataType&					outputType				= instance.interfaces.getOutputType();
+		const void*							inputData				= instance.interfaces.getInputBuffer()->data();
+		const void*							outputData				= instance.interfaces.getOutputBuffer()->data();
+		vector<std::pair<int, int> >		positions;
+		const tcu::ConstPixelBufferAccess	fragOutputBufferAccess	(outputType.getTextureFormat(), renderSize.x(), renderSize.y(), 1, fragOutputMemory->getHostPtr());
+
+		positions.push_back(std::make_pair(1, 1));
+		positions.push_back(std::make_pair(fragOutputBufferAccess.getWidth() - 1, 1));
+		positions.push_back(std::make_pair(1, fragOutputBufferAccess.getHeight() - 1));
+		positions.push_back(std::make_pair(fragOutputBufferAccess.getWidth() - 1, fragOutputBufferAccess.getHeight() - 1));
+
+		for (deUint32 posNdx = 0; posNdx < positions.size(); ++posNdx)
+		{
+			const int	x		= positions[posNdx].first;
+			const int	y		= positions[posNdx].second;
+			bool		equal	= true;
+
+			if (outputType.elementType == NUMBERTYPE_FLOAT32)
+			{
+				const float*		expected	= static_cast<const float*>(outputData) + posNdx * outputType.numElements;
+				const float*		actual		= static_cast<const float*>(fragOutputBufferAccess.getPixelPtr(x, y));
+
+				for (deUint32 eleNdx = 0; eleNdx < outputType.numElements; ++eleNdx)
+					if (!compare32BitFloat(expected[eleNdx], actual[eleNdx], context.getTestContext().getLog()))
+						equal = false;
+			}
+			else if (outputType.elementType == NUMBERTYPE_INT32)
+			{
+				const deInt32*		expected	= static_cast<const deInt32*>(outputData) + posNdx * outputType.numElements;
+				const deInt32*		actual		= static_cast<const deInt32*>(fragOutputBufferAccess.getPixelPtr(x, y));
+
+				for (deUint32 eleNdx = 0; eleNdx < outputType.numElements; ++eleNdx)
+					if (expected[eleNdx] != actual[eleNdx])
+						equal = false;
+			}
+			else if (outputType.elementType == NUMBERTYPE_UINT32)
+			{
+				const deUint32*		expected	= static_cast<const deUint32*>(outputData) + posNdx * outputType.numElements;
+				const deUint32*		actual		= static_cast<const deUint32*>(fragOutputBufferAccess.getPixelPtr(x, y));
+
+				for (deUint32 eleNdx = 0; eleNdx < outputType.numElements; ++eleNdx)
+					if (expected[eleNdx] != actual[eleNdx])
+						equal = false;
+			}
+			else if (outputType.elementType == NUMBERTYPE_FLOAT16)
+			{
+				const float*		original	= static_cast<const float*>(inputData) + posNdx * outputType.numElements;
+				const deFloat16*	actual		= static_cast<const deFloat16*>(fragOutputBufferAccess.getPixelPtr(x, y));
+
+				for (deUint32 eleNdx = 0; eleNdx < outputType.numElements; ++eleNdx)
+					if (!compare16BitFloat(original[eleNdx], actual[eleNdx], instance.interfaces.getRoundingMode(), context.getTestContext().getLog()))
+						equal = false;
+			}
+			else if (outputType.elementType == NUMBERTYPE_INT16)
+			{
+				const deInt16*		expected	= static_cast<const deInt16*>(outputData) + posNdx * outputType.numElements;
+				const deInt16*		actual		= static_cast<const deInt16*>(fragOutputBufferAccess.getPixelPtr(x, y));
+
+				for (deUint32 eleNdx = 0; eleNdx < outputType.numElements; ++eleNdx)
+					if (expected[eleNdx] != actual[eleNdx])
+						equal = false;
+			}
+			else if (outputType.elementType == NUMBERTYPE_UINT16)
+			{
+				const deUint16*		expected	= static_cast<const deUint16*>(outputData) + posNdx * outputType.numElements;
+				const deUint16*		actual		= static_cast<const deUint16*>(fragOutputBufferAccess.getPixelPtr(x, y));
+
+				for (deUint32 eleNdx = 0; eleNdx < outputType.numElements; ++eleNdx)
+					if (expected[eleNdx] != actual[eleNdx])
+						equal = false;
+			}
+			else {
+				DE_ASSERT(0 && "unhandled type");
+			}
+
+			if (!equal)
+				return TestStatus(instance.failResult, instance.getSpecializedFailMessage("fragment output dat point #" + numberToString(posNdx) + " mismatch"));
+		}
+	}
+
+	// Check the contents in output resources match with expected.
+	for (deUint32 outputNdx = 0; outputNdx < numOutResources; ++outputNdx)
+	{
+		const BufferSp& expected = instance.resources.outputs[outputNdx].second;
+
+		if (instance.resources.verifyIO != DE_NULL)
+		{
+			if (!(*instance.resources.verifyIO)(instance.resources.inputs, outResourceMemories, instance.resources.outputs, context.getTestContext().getLog()))
+				return tcu::TestStatus::fail("Resource returned doesn't match with expected");
+		}
+		else
+		{
+			if (deMemCmp(expected->data(), outResourceMemories[outputNdx]->getHostPtr(), expected->getNumBytes()))
+				return tcu::TestStatus::fail("Resource returned doesn't match bitwisely with expected");
+		}
+	}
+
+	return TestStatus::pass("Rendered output matches input");
+}
+
+void createTestsForAllStages (const std::string&			name,
+							  const RGBA					(&inputColors)[4],
+							  const RGBA					(&outputColors)[4],
+							  const map<string, string>&	testCodeFragments,
+							  const vector<deInt32>&		specConstants,
+							  const PushConstants&			pushConstants,
+							  const GraphicsResources&		resources,
+							  const GraphicsInterfaces&		interfaces,
+							  const vector<string>&			extensions,
+							  const vector<string>&			features,
+							  ExtensionFeatures				extensionFeatures,
+							  tcu::TestCaseGroup*			tests,
+							  const qpTestResult			failResult,
+							  const string&					failMessageTemplate)
+{
+	const ShaderElement		vertFragPipelineStages[]		=
+	{
+		ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
+		ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
+	};
+
+	const ShaderElement		tessPipelineStages[]			=
+	{
+		ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
+		ShaderElement("tessc", "main", VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT),
+		ShaderElement("tesse", "main", VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT),
+		ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
+	};
+
+	const ShaderElement		geomPipelineStages[]				=
+	{
+		ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
+		ShaderElement("geom", "main", VK_SHADER_STAGE_GEOMETRY_BIT),
+		ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
+	};
+
+	StageToSpecConstantMap	specConstantMap;
+
+	specConstantMap[VK_SHADER_STAGE_VERTEX_BIT] = specConstants;
+	addFunctionCaseWithPrograms<InstanceContext>(
+			tests, name + "_vert", "", addShaderCodeCustomVertex, runAndVerifyDefaultPipeline,
+			createInstanceContext(vertFragPipelineStages, inputColors, outputColors, testCodeFragments,
+				specConstantMap, pushConstants, resources, interfaces, extensions, features, extensionFeatures, failResult, failMessageTemplate));
+
+	specConstantMap.clear();
+	specConstantMap[VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT] = specConstants;
+	addFunctionCaseWithPrograms<InstanceContext>(
+			tests, name + "_tessc", "", addShaderCodeCustomTessControl, runAndVerifyDefaultPipeline,
+			createInstanceContext(tessPipelineStages, inputColors, outputColors, testCodeFragments,
+				specConstantMap, pushConstants, resources, interfaces, extensions, features, extensionFeatures, failResult, failMessageTemplate));
+
+	specConstantMap.clear();
+	specConstantMap[VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT] = specConstants;
+	addFunctionCaseWithPrograms<InstanceContext>(
+			tests, name + "_tesse", "", addShaderCodeCustomTessEval, runAndVerifyDefaultPipeline,
+			createInstanceContext(tessPipelineStages, inputColors, outputColors, testCodeFragments,
+				specConstantMap, pushConstants, resources, interfaces, extensions, features, extensionFeatures, failResult, failMessageTemplate));
+
+	specConstantMap.clear();
+	specConstantMap[VK_SHADER_STAGE_GEOMETRY_BIT] = specConstants;
+	addFunctionCaseWithPrograms<InstanceContext>(
+			tests, name + "_geom", "", addShaderCodeCustomGeometry, runAndVerifyDefaultPipeline,
+			createInstanceContext(geomPipelineStages, inputColors, outputColors, testCodeFragments,
+				specConstantMap, pushConstants, resources, interfaces, extensions, features, extensionFeatures, failResult, failMessageTemplate));
+
+	specConstantMap.clear();
+	specConstantMap[VK_SHADER_STAGE_FRAGMENT_BIT] = specConstants;
+	addFunctionCaseWithPrograms<InstanceContext>(
+			tests, name + "_frag", "", addShaderCodeCustomFragment, runAndVerifyDefaultPipeline,
+			createInstanceContext(vertFragPipelineStages, inputColors, outputColors, testCodeFragments,
+				specConstantMap, pushConstants, resources, interfaces, extensions, features, extensionFeatures, failResult, failMessageTemplate));
+}
+
+void addTessCtrlTest(tcu::TestCaseGroup* group, const char* name, const map<string, string>& fragments)
+{
+	RGBA defaultColors[4];
+	getDefaultColors(defaultColors);
+	const ShaderElement pipelineStages[] =
+	{
+		ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
+		ShaderElement("tessc", "main", VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT),
+		ShaderElement("tesse", "main", VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT),
+		ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
+	};
+
+	addFunctionCaseWithPrograms<InstanceContext>(
+			group, name, "", addShaderCodeCustomTessControl,
+			runAndVerifyDefaultPipeline, createInstanceContext(
+				pipelineStages, defaultColors, defaultColors, fragments,
+				StageToSpecConstantMap(), PushConstants(), GraphicsResources(),
+				GraphicsInterfaces(), vector<string>(), vector<string>(),
+				ExtensionFeatures()));
+}
+
+} // SpirVAssembly
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.hpp b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.hpp
new file mode 100644
index 0000000..1f0f020
--- /dev/null
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.hpp
@@ -0,0 +1,553 @@
+#ifndef _VKTSPVASMGRAPHICSSHADERTESTUTIL_HPP
+#define _VKTSPVASMGRAPHICSSHADERTESTUTIL_HPP
+/*-------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Graphics pipeline and helper functions for SPIR-V assembly tests
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuCommandLine.hpp"
+#include "tcuRGBA.hpp"
+
+#include "vkPrograms.hpp"
+#include "vktSpvAsmComputeShaderTestUtil.hpp"
+#include "vktSpvAsmUtils.hpp"
+#include "vktTestCaseUtil.hpp"
+
+#include "deRandom.hpp"
+#include "deSharedPtr.hpp"
+
+#include <map>
+#include <sstream>
+#include <string>
+#include <utility>
+
+namespace vkt
+{
+namespace SpirVAssembly
+{
+
+typedef vk::Unique<VkBuffer>										BufferHandleUp;
+typedef de::SharedPtr<BufferHandleUp>								BufferHandleSp;
+typedef vk::Unique<vk::VkShaderModule>								ModuleHandleUp;
+typedef de::SharedPtr<ModuleHandleUp>								ModuleHandleSp;
+typedef std::pair<std::string, vk::VkShaderStageFlagBits>			EntryToStage;
+typedef std::map<std::string, std::vector<EntryToStage> >			ModuleMap;
+typedef std::map<vk::VkShaderStageFlagBits, std::vector<deInt32> >	StageToSpecConstantMap;
+typedef std::pair<vk::VkDescriptorType, BufferSp>					Resource;
+
+enum NumberType
+{
+	NUMBERTYPE_INT32,
+	NUMBERTYPE_UINT32,
+	NUMBERTYPE_FLOAT32,
+	NUMBERTYPE_END32,		// Marks the end of 32-bit scalar types
+	NUMBERTYPE_INT16,
+	NUMBERTYPE_UINT16,
+	NUMBERTYPE_FLOAT16,
+};
+
+typedef enum RoundingModeFlags_e
+{
+	ROUNDINGMODE_RTE = 0x1,	// Round to nearest even
+	ROUNDINGMODE_RTZ = 0x2,	// Round to zero
+} RoundingModeFlags;
+
+typedef bool (*GraphicsVerifyIOFunc) (const std::vector<Resource>&		inputs,
+									  const std::vector<AllocationSp>&	outputAllocations,
+									  const std::vector<Resource>&		expectedOutputs,
+									  tcu::TestLog&						log);
+
+// Resources used by graphics-pipeline-based tests.
+struct GraphicsResources
+{
+	// Resources used as inputs.
+	std::vector<Resource>	inputs;
+	// Resources used as outputs. The data supplied will be used as
+	// the expected outputs for the corresponding bindings by default.
+	// If other behaviors are needed, please provide a custom verifyIO.
+	std::vector<Resource>	outputs;
+	// If null, a default verification will be performed by comparing the
+	// memory pointed to by outputAllocations  and the contents of
+	// expectedOutputs. Otherwise the function pointed to by verifyIO will
+	// be called. If true is returned, then the test case is assumed to
+	// have passed, if false is returned, then the test case is assumed
+	// to have failed.
+	GraphicsVerifyIOFunc	verifyIO;
+
+							GraphicsResources()
+								: verifyIO	(DE_NULL)
+							{}
+};
+
+// Interface data type.
+struct IFDataType
+{
+						IFDataType			(deUint32 numE, NumberType elementT)
+							: numElements	(numE)
+							, elementType	(elementT)
+						{
+							DE_ASSERT(numE > 0 && numE < 5);
+							DE_ASSERT(elementT != NUMBERTYPE_END32);
+						}
+
+						IFDataType			(const IFDataType& that)
+							: numElements	(that.numElements)
+							, elementType	(that.elementType)
+						{}
+
+	deUint32			getElementNumBytes	(void) const;
+	deUint32			getNumBytes			(void) const { return numElements * getElementNumBytes(); }
+
+	vk::VkFormat		getVkFormat			(void) const;
+
+	tcu::TextureFormat	getTextureFormat	(void) const;
+
+	std::string			str					(void) const;
+
+	bool				elementIs32bit		(void) const { return elementType < NUMBERTYPE_END32; }
+	bool				isVector			(void) const { return numElements > 1; }
+
+	deUint32			numElements;
+	NumberType			elementType;
+};
+
+typedef std::pair<IFDataType, BufferSp>			Interface;
+
+// Interface variables used by graphics-pipeline-based tests.
+class GraphicsInterfaces
+{
+public:
+						GraphicsInterfaces	()
+							: rndMode	(static_cast<RoundingModeFlags>(0))
+						{}
+
+						GraphicsInterfaces	(const GraphicsInterfaces& that)
+							: inputs	(that.inputs)
+							, outputs	(that.outputs)
+							, rndMode	(that.rndMode)
+						{}
+
+	void				setInputOutput		(const Interface& input, const Interface&  output)
+						{
+							inputs.clear();
+							outputs.clear();
+							inputs.push_back(input);
+							outputs.push_back(output);
+						}
+
+	const IFDataType&	getInputType		(void) const
+						{
+							DE_ASSERT(inputs.size() == 1);
+							return inputs.front().first;
+						}
+
+	const IFDataType&	getOutputType		(void) const
+						{
+							DE_ASSERT(outputs.size() == 1);
+							return outputs.front().first;
+						}
+
+	const BufferSp&		getInputBuffer		(void) const
+						{
+							DE_ASSERT(inputs.size() == 1);
+							return inputs.front().second;
+						}
+
+	const BufferSp&		getOutputBuffer		(void) const
+						{
+							DE_ASSERT(outputs.size() == 1);
+							return outputs.front().second;
+						}
+
+	bool				empty				(void) const
+						{
+							return inputs.size() == 0;
+						}
+
+	void				setRoundingMode		(RoundingModeFlags flag)
+						{
+							rndMode = flag;
+						}
+	RoundingModeFlags	getRoundingMode		(void) const
+						{
+							return rndMode;
+						}
+private:
+	// vector<Interface> acts as a null-able Interface here. Canonically we should use
+	// std::unique_ptr, but sadly we cannot leverage C++11 in dEQP. dEQP has its own
+	// de::UniquePtr, but still cumbersome to use in InstanceContext and do copies
+	// at various places.
+	// Public methods should make sure that there are less than two elements in both
+	// members and both members have the same number of elements.
+	std::vector<Interface>	inputs;
+	std::vector<Interface>	outputs;
+	RoundingModeFlags		rndMode;
+
+};
+
+struct PushConstants
+{
+public:
+							PushConstants (void)
+							{}
+
+							PushConstants (const PushConstants& that)
+								: pcs	(that.pcs)
+							{}
+
+	void					setPushConstant	(const BufferSp& pc)
+							{
+								pcs.clear();
+								pcs.push_back(pc);
+							}
+
+	bool					empty (void) const
+							{
+								return pcs.empty();
+							}
+
+	const BufferSp&			getBuffer(void) const
+							{
+								DE_ASSERT(pcs.size() == 1);
+								return pcs[0];
+							}
+
+private:
+	// Right now we only support one field in the push constant block.
+	std::vector<BufferSp>	pcs;
+};
+
+// Returns the corresponding buffer usage flag bit for the given descriptor type.
+VkBufferUsageFlagBits getMatchingBufferUsageFlagBit(VkDescriptorType dType);
+
+// Context for a specific test instantiation. For example, an instantiation
+// may test colors yellow/magenta/cyan/mauve in a tesselation shader
+// with an entry point named 'main_to_the_main'
+struct InstanceContext
+{
+	// Map of modules to what entry_points we care to use from those modules.
+	ModuleMap								moduleMap;
+	tcu::RGBA								inputColors[4];
+	tcu::RGBA								outputColors[4];
+	// Concrete SPIR-V code to test via boilerplate specialization.
+	std::map<std::string, std::string>		testCodeFragments;
+	StageToSpecConstantMap					specConstants;
+	bool									hasTessellation;
+	vk::VkShaderStageFlagBits				requiredStages;
+	std::vector<std::string>				requiredDeviceExtensions;
+	std::vector<std::string>				requiredDeviceFeatures;
+	ExtensionFeatures						requestedExtensionFeatures;
+	PushConstants							pushConstants;
+	// Possible resources used by the graphics pipeline.
+	// If it is not empty, a single descriptor set (number 0) will be allocated
+	// to point to all resources specified. Binding numbers are allocated in
+	// accord with the resources' order in the vector; outputs are allocated
+	// after inputs.
+	GraphicsResources						resources;
+	// Possible interface variables use by the graphics pipeline.
+	// If it is not empty, input/output variables will be set up for shader stages
+	// in the test. Both the input and output variable will take location #2 in the
+	// pipeline for all stages, except that the output variable in the fragment
+	// stage will take location #1.
+	GraphicsInterfaces						interfaces;
+	qpTestResult							failResult;
+	std::string								failMessageTemplate;	//!< ${reason} in the template will be replaced with a detailed failure message
+
+	InstanceContext (const tcu::RGBA							(&inputs)[4],
+					 const tcu::RGBA							(&outputs)[4],
+					 const std::map<std::string, std::string>&	testCodeFragments_,
+					 const StageToSpecConstantMap&				specConstants_,
+					 const PushConstants&						pushConsants_,
+					 const GraphicsResources&					resources_,
+					 const GraphicsInterfaces&					interfaces_,
+					 const std::vector<std::string>&			extensions_,
+					 const std::vector<std::string>&			features_,
+					 ExtensionFeatures							extFeatures_);
+
+	InstanceContext (const InstanceContext& other);
+
+	std::string getSpecializedFailMessage (const std::string& failureReason);
+};
+
+// A description of a shader to be used for a single stage of the graphics pipeline.
+struct ShaderElement
+{
+	// The module that contains this shader entrypoint.
+	std::string					moduleName;
+
+	// The name of the entrypoint.
+	std::string					entryName;
+
+	// Which shader stage this entry point represents.
+	vk::VkShaderStageFlagBits	stage;
+
+	ShaderElement (const std::string& moduleName_, const std::string& entryPoint_, vk::VkShaderStageFlagBits shaderStage_);
+};
+
+template <typename T>
+const std::string numberToString (T number)
+{
+	std::stringstream ss;
+	ss << number;
+	return ss.str();
+}
+
+// Performs a bitwise copy of source to the destination type Dest.
+template <typename Dest, typename Src>
+Dest bitwiseCast(Src source)
+{
+  Dest dest;
+  DE_STATIC_ASSERT(sizeof(source) == sizeof(dest));
+  deMemcpy(&dest, &source, sizeof(dest));
+  return dest;
+}
+
+template<typename T>	T			randomScalar	(de::Random& rnd, T minValue, T maxValue);
+template<> inline		float		randomScalar	(de::Random& rnd, float minValue, float maxValue)		{ return rnd.getFloat(minValue, maxValue);	}
+template<> inline		deInt32		randomScalar	(de::Random& rnd, deInt32 minValue, deInt32 maxValue)	{ return rnd.getInt(minValue, maxValue);	}
+
+
+void getDefaultColors (tcu::RGBA (&colors)[4]);
+
+void getHalfColorsFullAlpha (tcu::RGBA (&colors)[4]);
+
+void getInvertedDefaultColors (tcu::RGBA (&colors)[4]);
+
+// Creates fragments that specialize into a simple pass-through shader (of any kind).
+std::map<std::string, std::string> passthruFragments(void);
+
+void createCombinedModule(vk::SourceCollections& dst, InstanceContext);
+
+// This has two shaders of each stage. The first
+// is a passthrough, the second inverts the color.
+void createMultipleEntries(vk::SourceCollections& dst, InstanceContext);
+
+// Turns a statically sized array of ShaderElements into an instance-context
+// by setting up the mapping of modules to their contained shaders and stages.
+// The inputs and expected outputs are given by inputColors and outputColors
+template<size_t N>
+InstanceContext createInstanceContext (const ShaderElement							(&elements)[N],
+									   const tcu::RGBA								(&inputColors)[4],
+									   const tcu::RGBA								(&outputColors)[4],
+									   const std::map<std::string, std::string>&	testCodeFragments,
+									   const StageToSpecConstantMap&				specConstants,
+									   const PushConstants&							pushConstants,
+									   const GraphicsResources&						resources,
+									   const GraphicsInterfaces&					interfaces,
+									   const std::vector<std::string>&				extensions,
+									   const std::vector<std::string>&				features,
+									   ExtensionFeatures							extensionFeatures,
+									   const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
+									   const std::string&							failMessageTemplate	= std::string())
+{
+	InstanceContext ctx (inputColors, outputColors, testCodeFragments, specConstants, pushConstants, resources, interfaces, extensions, features, extensionFeatures);
+	for (size_t i = 0; i < N; ++i)
+	{
+		ctx.moduleMap[elements[i].moduleName].push_back(std::make_pair(elements[i].entryName, elements[i].stage));
+		ctx.requiredStages = static_cast<VkShaderStageFlagBits>(ctx.requiredStages | elements[i].stage);
+	}
+	ctx.failResult				= failResult;
+	if (!failMessageTemplate.empty())
+		ctx.failMessageTemplate	= failMessageTemplate;
+	return ctx;
+}
+
+// The same as createInstanceContext above, without extensions, spec constants, and resources.
+template<size_t N>
+inline InstanceContext createInstanceContext (const ShaderElement						(&elements)[N],
+											  tcu::RGBA									(&inputColors)[4],
+											  const tcu::RGBA							(&outputColors)[4],
+											  const std::map<std::string, std::string>&	testCodeFragments)
+{
+	return createInstanceContext(elements, inputColors, outputColors, testCodeFragments,
+								 StageToSpecConstantMap(), PushConstants(), GraphicsResources(),
+								 GraphicsInterfaces(), std::vector<std::string>(), std::vector<std::string>(),
+								 ExtensionFeatures());
+}
+
+// The same as createInstanceContext above, but with default colors.
+template<size_t N>
+InstanceContext createInstanceContext (const ShaderElement							(&elements)[N],
+									   const std::map<std::string, std::string>&	testCodeFragments)
+{
+	tcu::RGBA defaultColors[4];
+	getDefaultColors(defaultColors);
+	return createInstanceContext(elements, defaultColors, defaultColors, testCodeFragments);
+}
+
+
+void createTestsForAllStages (const std::string&						name,
+							  const tcu::RGBA							(&inputColors)[4],
+							  const tcu::RGBA							(&outputColors)[4],
+							  const std::map<std::string, std::string>&	testCodeFragments,
+							  const std::vector<deInt32>&				specConstants,
+							  const PushConstants&						pushConstants,
+							  const GraphicsResources&					resources,
+							  const GraphicsInterfaces&					interfaces,
+							  const std::vector<std::string>&			extensions,
+							  const std::vector<std::string>&			features,
+							  ExtensionFeatures							extensionFeatures,
+							  tcu::TestCaseGroup*						tests,
+							  const qpTestResult						failResult			= QP_TEST_RESULT_FAIL,
+							  const std::string&						failMessageTemplate	= std::string());
+
+inline void createTestsForAllStages (const std::string&							name,
+									 const tcu::RGBA							(&inputColors)[4],
+									 const tcu::RGBA							(&outputColors)[4],
+									 const std::map<std::string, std::string>&	testCodeFragments,
+									 tcu::TestCaseGroup*						tests,
+									 const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
+									 const std::string&							failMessageTemplate	= std::string())
+{
+	std::vector<deInt32>		noSpecConstants;
+	PushConstants				noPushConstants;
+	GraphicsResources			noResources;
+	GraphicsInterfaces			noInterfaces;
+	std::vector<std::string>	noExtensions;
+	std::vector<std::string>	noFeatures;
+
+	createTestsForAllStages(
+			name, inputColors, outputColors, testCodeFragments, noSpecConstants, noPushConstants,
+			noResources, noInterfaces, noExtensions, noFeatures, ExtensionFeatures(),
+			tests, failResult, failMessageTemplate);
+}
+
+inline void createTestsForAllStages (const std::string&							name,
+									 const tcu::RGBA							(&inputColors)[4],
+									 const tcu::RGBA							(&outputColors)[4],
+									 const std::map<std::string, std::string>&	testCodeFragments,
+									 const std::vector<deInt32>&				specConstants,
+									 tcu::TestCaseGroup*						tests,
+									 const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
+									 const std::string&							failMessageTemplate	= std::string())
+{
+	PushConstants					noPushConstants;
+	GraphicsResources				noResources;
+	GraphicsInterfaces				noInterfaces;
+	std::vector<std::string>		noExtensions;
+	std::vector<std::string>		noFeatures;
+
+	createTestsForAllStages(
+			name, inputColors, outputColors, testCodeFragments, specConstants, noPushConstants,
+			noResources, noInterfaces, noExtensions, noFeatures, ExtensionFeatures(),
+			tests, failResult, failMessageTemplate);
+}
+
+inline void createTestsForAllStages (const std::string&							name,
+									 const tcu::RGBA							(&inputColors)[4],
+									 const tcu::RGBA							(&outputColors)[4],
+									 const std::map<std::string, std::string>&	testCodeFragments,
+									 const GraphicsResources&					resources,
+									 const std::vector<std::string>&			extensions,
+									 tcu::TestCaseGroup*						tests,
+									 ExtensionFeatures							extensionFeatures	= ExtensionFeatures(),
+									 const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
+									 const std::string&							failMessageTemplate	= std::string())
+{
+	std::vector<deInt32>		noSpecConstants;
+	PushConstants				noPushConstants;
+	GraphicsInterfaces			noInterfaces;
+	std::vector<std::string>	noFeatures;
+
+	createTestsForAllStages(
+			name, inputColors, outputColors, testCodeFragments, noSpecConstants, noPushConstants,
+			resources, noInterfaces, extensions, noFeatures, extensionFeatures,
+			tests, failResult, failMessageTemplate);
+}
+
+inline void createTestsForAllStages (const std::string& name,
+									 const tcu::RGBA							(&inputColors)[4],
+									 const tcu::RGBA							(&outputColors)[4],
+									 const std::map<std::string, std::string>&	testCodeFragments,
+									 const GraphicsInterfaces					interfaces,
+									 const std::vector<std::string>&			extensions,
+									 tcu::TestCaseGroup*						tests,
+									 ExtensionFeatures							extensionFeatures	= ExtensionFeatures(),
+									 const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
+									 const std::string&							failMessageTemplate	= std::string())
+{
+	GraphicsResources			noResources;
+	std::vector<deInt32>		noSpecConstants;
+	std::vector<std::string>	noFeatures;
+	PushConstants				noPushConstants;
+
+	createTestsForAllStages(
+			name, inputColors, outputColors, testCodeFragments, noSpecConstants, noPushConstants,
+			noResources, interfaces, extensions, noFeatures, extensionFeatures,
+			tests, failResult, failMessageTemplate);
+}
+
+inline void createTestsForAllStages (const std::string& name,
+									 const tcu::RGBA							(&inputColors)[4],
+									 const tcu::RGBA							(&outputColors)[4],
+									 const std::map<std::string, std::string>&	testCodeFragments,
+									 const PushConstants&						pushConstants,
+									 const GraphicsResources&					resources,
+									 const std::vector<std::string>&			extensions,
+									 tcu::TestCaseGroup*						tests,
+									 ExtensionFeatures							extensionFeatures	= ExtensionFeatures(),
+									 const qpTestResult							failResult			= QP_TEST_RESULT_FAIL,
+									 const std::string&							failMessageTemplate	= std::string())
+{
+	std::vector<deInt32>			noSpecConstants;
+	GraphicsInterfaces				noInterfaces;
+	std::vector<std::string>		noFeatures;
+
+	createTestsForAllStages(
+			name, inputColors, outputColors, testCodeFragments, noSpecConstants, pushConstants,
+			resources, noInterfaces, extensions, noFeatures, extensionFeatures,
+			tests, failResult, failMessageTemplate);
+}
+
+// Sets up and runs a Vulkan pipeline, then spot-checks the resulting image.
+// Feeds the pipeline a set of colored triangles, which then must occur in the
+// rendered image.  The surface is cleared before executing the pipeline, so
+// whatever the shaders draw can be directly spot-checked.
+tcu::TestStatus runAndVerifyDefaultPipeline (Context& context, InstanceContext instance);
+
+// Adds a new test to group using custom fragments for the tessellation-control
+// stage and passthrough fragments for all other stages.  Uses default colors
+// for input and expected output.
+void addTessCtrlTest(tcu::TestCaseGroup* group, const char* name, const std::map<std::string, std::string>& fragments);
+
+// Given the original 32-bit float value, computes the corresponding 16-bit
+// float value under the given rounding mode flags and compares with the
+// returned 16-bit float value. Returns true if they are considered as equal.
+//
+// The following equivalence criteria are respected:
+// * Positive and negative zeros are considered equivalent.
+// * Denormalized floats are allowed to be flushed to zeros.
+// * Different bit patterns of NaNs are allowed.
+// * For the rest, require exactly the same bit pattern.
+bool compare16BitFloat (float original, deUint16 returned, RoundingModeFlags flags, tcu::TestLog& log);
+
+// Compare the returned 32-bit float against its expected value.
+//
+// The following equivalence criteria are respected:
+// * Denormalized floats are allowed to be flushed to zeros.
+// * Different bit patterns of NaNs/Infs are allowed.
+// * For the rest, use C++ float equivalence check.
+bool compare32BitFloat (float expected, float returned, tcu::TestLog& log);
+
+} // SpirVAssembly
+} // vkt
+
+#endif // _VKTSPVASMGRAPHICSSHADERTESTUTIL_HPP
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmInstructionTests.cpp b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmInstructionTests.cpp
index 5aafc4b..d253359 100644
--- a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmInstructionTests.cpp
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmInstructionTests.cpp
@@ -3,6 +3,7 @@
  * ------------------------
  *
  * Copyright (c) 2015 Google Inc.
+ * Copyright (c) 2016 The Khronos Group Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -49,6 +50,7 @@
 
 #include "vktSpvAsmComputeShaderCase.hpp"
 #include "vktSpvAsmComputeShaderTestUtil.hpp"
+#include "vktSpvAsmGraphicsShaderTestUtil.hpp"
 #include "vktTestCaseUtil.hpp"
 
 #include <cmath>
@@ -56,6 +58,7 @@
 #include <map>
 #include <string>
 #include <sstream>
+#include <utility>
 
 namespace vkt
 {
@@ -79,13 +82,6 @@
 using tcu::StringTemplate;
 using tcu::Vec4;
 
-typedef Unique<VkShaderModule>			ModuleHandleUp;
-typedef de::SharedPtr<ModuleHandleUp>	ModuleHandleSp;
-
-template<typename T>	T			randomScalar	(de::Random& rnd, T minValue, T maxValue);
-template<> inline		float		randomScalar	(de::Random& rnd, float minValue, float maxValue)		{ return rnd.getFloat(minValue, maxValue);	}
-template<> inline		deInt32		randomScalar	(de::Random& rnd, deInt32 minValue, deInt32 maxValue)	{ return rnd.getInt(minValue, maxValue);	}
-
 template<typename T>
 static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, int offset = 0)
 {
@@ -94,6 +90,22 @@
 		typedPtr[offset + ndx] = randomScalar<T>(rnd, minValue, maxValue);
 }
 
+// Filter is a function that returns true if a value should pass, false otherwise.
+template<typename T, typename FilterT>
+static void fillRandomScalars (de::Random& rnd, T minValue, T maxValue, void* dst, int numValues, FilterT filter, int offset = 0)
+{
+	T* const typedPtr = (T*)dst;
+	T value;
+	for (int ndx = 0; ndx < numValues; ndx++)
+	{
+		do
+			value = randomScalar<T>(rnd, minValue, maxValue);
+		while (!filter(value));
+
+		typedPtr[offset + ndx] = value;
+	}
+}
+
 static void floorAll (vector<float>& values)
 {
 	for (size_t i = 0; i < values.size(); i++)
@@ -132,45 +144,6 @@
 //   output_data.elements[x] = -input_data.elements[x];
 // }
 
-static const char* const s_ShaderPreamble =
-	"OpCapability Shader\n"
-	"OpMemoryModel Logical GLSL450\n"
-	"OpEntryPoint GLCompute %main \"main\" %id\n"
-	"OpExecutionMode %main LocalSize 1 1 1\n";
-
-static const char* const s_CommonTypes =
-	"%bool      = OpTypeBool\n"
-	"%void      = OpTypeVoid\n"
-	"%voidf     = OpTypeFunction %void\n"
-	"%u32       = OpTypeInt 32 0\n"
-	"%i32       = OpTypeInt 32 1\n"
-	"%f32       = OpTypeFloat 32\n"
-	"%uvec3     = OpTypeVector %u32 3\n"
-	"%fvec3     = OpTypeVector %f32 3\n"
-	"%uvec3ptr  = OpTypePointer Input %uvec3\n"
-	"%i32ptr    = OpTypePointer Uniform %i32\n"
-	"%f32ptr    = OpTypePointer Uniform %f32\n"
-	"%i32arr    = OpTypeRuntimeArray %i32\n"
-	"%f32arr    = OpTypeRuntimeArray %f32\n";
-
-// Declares two uniform variables (indata, outdata) of type "struct { float[] }". Depends on type "f32arr" (for "float[]").
-static const char* const s_InputOutputBuffer =
-	"%buf     = OpTypeStruct %f32arr\n"
-	"%bufptr  = OpTypePointer Uniform %buf\n"
-	"%indata    = OpVariable %bufptr Uniform\n"
-	"%outdata   = OpVariable %bufptr Uniform\n";
-
-// Declares buffer type and layout for uniform variables indata and outdata. Both of them are SSBO bounded to descriptor set 0.
-// indata is at binding point 0, while outdata is at 1.
-static const char* const s_InputOutputBufferTraits =
-	"OpDecorate %buf BufferBlock\n"
-	"OpDecorate %indata DescriptorSet 0\n"
-	"OpDecorate %indata Binding 0\n"
-	"OpDecorate %outdata DescriptorSet 0\n"
-	"OpDecorate %outdata Binding 1\n"
-	"OpDecorate %f32arr ArrayStride 4\n"
-	"OpMemberDecorate %buf 0 Offset 0\n";
-
 tcu::TestCaseGroup* createOpNopGroup (tcu::TestContext& testCtx)
 {
 	de::MovePtr<tcu::TestCaseGroup>	group			(new tcu::TestCaseGroup(testCtx, "opnop", "Test the OpNop instruction"));
@@ -186,7 +159,7 @@
 		negativeFloats[ndx] = -positiveFloats[ndx];
 
 	spec.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main           \"main\"\n"
@@ -194,9 +167,9 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes)
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes())
 
-		+ string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBuffer()) +
 
 		"%id        = OpVariable %uvec3ptr Input\n"
 		"%zero      = OpConstant %i32 0\n"
@@ -276,7 +249,7 @@
 
 	const StringTemplate			shaderTemplate	(
 
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main           \"main\"\n"
@@ -297,7 +270,7 @@
 		"OpMemberDecorate %buf 0 Offset 0\n"
 		"OpMemberDecorate %buf2 0 Offset 0\n"
 
-		+ string(s_CommonTypes) +
+		+ string(getComputeAsmCommonTypes()) +
 
 		"%buf        = OpTypeStruct %f32arr\n"
 		"%bufptr     = OpTypePointer Uniform %buf\n"
@@ -399,7 +372,7 @@
 
 	const StringTemplate			shaderTemplate	(
 
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main           \"main\"\n"
@@ -419,7 +392,7 @@
 		"OpMemberDecorate %sumbuf 0 Coherent\n"
 		"OpMemberDecorate %sumbuf 0 Offset 0\n"
 
-		+ string(s_CommonTypes) +
+		+ string(getComputeAsmCommonTypes()) +
 
 		"%buf       = OpTypeStruct %i32arr\n"
 		"%bufptr    = OpTypePointer Uniform %buf\n"
@@ -514,7 +487,7 @@
 		negativeFloats[ndx] = -positiveFloats[ndx];
 
 	spec.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"%fname1 = OpString \"negateInputs.comp\"\n"
 		"%fname2 = OpString \"negateInputs\"\n"
@@ -525,11 +498,11 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) +
+		+ string(getComputeAsmInputOutputBufferTraits()) +
 
 		"OpLine %fname1 0 0\n" // At the earliest possible position
 
-		+ string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"OpLine %fname1 0 1\n" // Multiple OpLines in sequence
 		"OpLine %fname2 1 0\n" // Different filenames
@@ -578,7 +551,7 @@
 		negativeFloats[ndx] = -positiveFloats[ndx];
 
 	spec.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"%fname = OpString \"negateInputs.comp\"\n"
 
@@ -588,11 +561,11 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) +
+		+ string(getComputeAsmInputOutputBufferTraits()) +
 
 		"OpNoLine\n" // At the earliest possible position, without preceding OpLine
 
-		+ string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"OpLine %fname 0 1\n"
 		"OpNoLine\n" // Immediately following a preceding OpLine
@@ -659,7 +632,7 @@
 	vector<float>					inputFloats2	(numElements, 0);
 	vector<float>					outputFloats	(numElements, 0);
 	const StringTemplate			shaderTemplate	(
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpName %main           \"main\"\n"
 		"OpName %id             \"gl_GlobalInvocationID\"\n"
@@ -678,7 +651,7 @@
 		"OpDecorate %f32arr ArrayStride 4\n"
 		"OpMemberDecorate %buf 0 Offset 0\n"
 
-		+ string(s_CommonTypes) +
+		+ string(getComputeAsmCommonTypes()) +
 
 		"%buf        = OpTypeStruct %f32arr\n"
 		"%bufptr     = OpTypePointer Uniform %buf\n"
@@ -786,7 +759,7 @@
 	}
 
 	spec.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpName %main           \"main\"\n"
 		"OpName %id             \"gl_GlobalInvocationID\"\n"
@@ -803,7 +776,7 @@
 		"OpDecorate %f32arr ArrayStride 4\n"
 		"OpMemberDecorate %buf 0 Offset 0\n"
 
-		+ string(s_CommonTypes) +
+		+ string(getComputeAsmCommonTypes()) +
 
 		"%buf        = OpTypeStruct %f32arr\n"
 		"%bufptr     = OpTypePointer Uniform %buf\n"
@@ -860,7 +833,7 @@
 		outputFloats1[ndx] = inputFloats1[ndx] + Vec4(0.f, 0.5f, 1.5f, 2.5f);
 
 	spec1.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpName %main           \"main\"\n"
 		"OpName %id             \"gl_GlobalInvocationID\"\n"
@@ -868,7 +841,7 @@
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 		"OpDecorate %vec4arr ArrayStride 16\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) +
 
 		"%vec4       = OpTypeVector %f32 4\n"
 		"%vec4ptr_u  = OpTypePointer Uniform %vec4\n"
@@ -918,7 +891,7 @@
 		outputFloats2[ndx] = inputFloats2[ndx];
 
 	spec2.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpName %main           \"main\"\n"
 		"OpName %id             \"gl_GlobalInvocationID\"\n"
@@ -926,7 +899,7 @@
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 		"OpDecorate %f32arr100 ArrayStride 4\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) +
 
 		"%hundred        = OpConstant %u32 100\n"
 		"%f32arr100      = OpTypeArray %f32 %hundred\n"
@@ -967,7 +940,7 @@
 		outputFloats3[ndx] = inputFloats3[ndx];
 
 	spec3.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpName %main           \"main\"\n"
 		"OpName %id             \"gl_GlobalInvocationID\"\n"
@@ -978,7 +951,7 @@
 		"OpMemberDecorate %buf 2 Offset 32\n"
 		"OpMemberDecorate %buf 3 Offset 48\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) +
 
 		"%vec4      = OpTypeVector %f32 4\n"
 		"%buf       = OpTypeStruct %vec4 %vec4 %vec4 %vec4\n"
@@ -1015,14 +988,14 @@
 		outputFloats4[ndx] = -inputFloats4[ndx];
 
 	spec4.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpName %main           \"main\"\n"
 		"OpName %id             \"gl_GlobalInvocationID\"\n"
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%f32ptr_f  = OpTypePointer Function %f32\n"
 		"%id        = OpVariable %uvec3ptr Input\n"
@@ -1069,21 +1042,21 @@
 		outputFloats[ndx] = inputFloats[ndx] + 7.5f;
 
 	spec.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpName %main           \"main\"\n"
 		"OpName %id             \"gl_GlobalInvocationID\"\n"
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) +
 
 		"%fmat     = OpTypeMatrix %fvec3 3\n"
 		"%three    = OpConstant %u32 3\n"
 		"%farr     = OpTypeArray %f32 %three\n"
 		"%fst      = OpTypeStruct %f32 %f32\n"
 
-		+ string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBuffer()) +
 
 		"%id            = OpVariable %uvec3ptr Input\n"
 		"%zero          = OpConstant %i32 0\n"
@@ -1182,7 +1155,7 @@
 		negativeFloats[ndx] = -positiveFloats[ndx];
 
 	spec.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main            \"main\"\n"
@@ -1193,7 +1166,7 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) +
 
 		"%u32ptr    = OpTypePointer Function %u32\n"
 		"%uintfuint = OpTypeFunction %u32 %u32ptr\n"
@@ -1209,7 +1182,7 @@
 		"%hundred   = OpConstant %u32 100\n"
 		"%thousand  = OpConstant %u32 1000\n"
 
-		+ string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBuffer()) +
 
 		// Main()
 		"%main   = OpFunction %void None %voidf\n"
@@ -1336,7 +1309,7 @@
 		outputFloats[ndx] = inputFloats0[ndx] + inputFloats1[ndx] + inputFloats2[ndx] + inputFloats3[ndx] + inputFloats4[ndx];
 
 	spec.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main \"main\"\n"
@@ -1388,7 +1361,7 @@
 		"OpGroupDecorate %group3 %indata4\n"
 		"OpDecorate %indata4 Binding 4\n"
 
-		+ string(s_CommonTypes) +
+		+ string(getComputeAsmCommonTypes()) +
 
 		"%id   = OpVariable %uvec3ptr Input\n"
 		"%zero = OpConstant %i32 0\n"
@@ -1491,7 +1464,7 @@
 	vector<deInt32>					outputInts3		(numElements, 0);
 	vector<deInt32>					outputInts4		(numElements, 0);
 	const StringTemplate			shaderTemplate	(
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpName %main           \"main\"\n"
 		"OpName %id             \"gl_GlobalInvocationID\"\n"
@@ -1501,7 +1474,7 @@
 		"OpDecorate %sc_1  SpecId 1\n"
 		"OpDecorate %i32arr ArrayStride 4\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) +
 
 		"%buf     = OpTypeStruct %i32arr\n"
 		"%bufptr  = OpTypePointer Uniform %buf\n"
@@ -1598,7 +1571,7 @@
 	ComputeShaderSpec				spec;
 
 	spec.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpName %main           \"main\"\n"
 		"OpName %id             \"gl_GlobalInvocationID\"\n"
@@ -1609,7 +1582,7 @@
 		"OpDecorate %sc_2  SpecId 2\n"
 		"OpDecorate %i32arr ArrayStride 4\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) +
 
 		"%ivec3     = OpTypeVector %i32 3\n"
 		"%buf     = OpTypeStruct %i32arr\n"
@@ -1690,7 +1663,7 @@
 	}
 
 	spec1.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main \"main\"\n"
@@ -1698,7 +1671,7 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%id = OpVariable %uvec3ptr Input\n"
 		"%zero       = OpConstant %i32 0\n"
@@ -1750,14 +1723,14 @@
 	group->addChild(new SpvAsmComputeShaderCase(testCtx, "block", "out-of-order and unreachable blocks for OpPhi", spec1));
 
 	spec2.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpName %main \"main\"\n"
 		"OpName %id \"gl_GlobalInvocationID\"\n"
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%id         = OpVariable %uvec3ptr Input\n"
 		"%zero       = OpConstant %i32 0\n"
@@ -1794,14 +1767,14 @@
 	group->addChild(new SpvAsmComputeShaderCase(testCtx, "induction", "The usual way induction variables are handled in LLVM IR", spec2));
 
 	spec3.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpName %main \"main\"\n"
 		"OpName %id \"gl_GlobalInvocationID\"\n"
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%f32ptr_f   = OpTypePointer Function %f32\n"
 		"%id         = OpVariable %uvec3ptr Input\n"
@@ -1896,7 +1869,7 @@
 	}
 
 	spec.assembly =
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main \"main\"\n"
@@ -1904,12 +1877,12 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) +
 
 		"%u32ptr       = OpTypePointer Function %u32\n"
 		"%u32ptr_input = OpTypePointer Input %u32\n"
 
-		+ string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBuffer()) +
 
 		"%id        = OpVariable %uvec3ptr Input\n"
 		"%zero      = OpConstant %i32 0\n"
@@ -2053,7 +2026,7 @@
 		"OpMemberDecorate %vert_builtin_st 1 BuiltIn PointSize\n"
 		"OpMemberDecorate %vert_builtin_st 2 BuiltIn ClipDistance\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%zero       = OpConstant %i32 0\n"
 		"%one        = OpConstant %u32 1\n"
@@ -2163,7 +2136,7 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%id        = OpVariable %uvec3ptr Input\n"
 		"%zero      = OpConstant %i32 0\n"
@@ -2246,7 +2219,7 @@
 	vector<float>					inputFloats		(numElements, 0);
 	vector<float>					outputFloats	(numElements, 0);
 	const StringTemplate			shaderTemplate	(
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSourceExtension \"${EXTENSION}\"\n"
 
@@ -2255,7 +2228,7 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%id        = OpVariable %uvec3ptr Input\n"
 		"%zero      = OpConstant %i32 0\n"
@@ -2310,7 +2283,7 @@
 	vector<float>					positiveFloats	(numElements, 0);
 	vector<float>					negativeFloats	(numElements, 0);
 	const StringTemplate			shaderTemplate	(
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main           \"main\"\n"
@@ -2318,17 +2291,9 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) +
-		"%uvec2     = OpTypeVector %u32 2\n"
-		"%bvec3     = OpTypeVector %bool 3\n"
-		"%fvec4     = OpTypeVector %f32 4\n"
-		"%fmat33    = OpTypeMatrix %fvec3 3\n"
-		"%const100  = OpConstant %u32 100\n"
-		"%uarr100   = OpTypeArray %i32 %const100\n"
-		"%struct    = OpTypeStruct %f32 %i32 %u32\n"
-		"%pointer   = OpTypePointer Function %i32\n"
-		+ string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
+		"${TYPE}\n"
 		"%null      = OpConstantNull ${TYPE}\n"
 
 		"%id        = OpVariable %uvec3ptr Input\n"
@@ -2390,7 +2355,7 @@
 	vector<float>					positiveFloats	(numElements, 0);
 	vector<float>					negativeFloats	(numElements, 0);
 	const StringTemplate			shaderTemplate	(
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main           \"main\"\n"
@@ -2398,7 +2363,7 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%id        = OpVariable %uvec3ptr Input\n"
 		"%zero      = OpConstant %i32 0\n"
@@ -2552,7 +2517,7 @@
 	de::MovePtr<tcu::TestCaseGroup>	group			(new tcu::TestCaseGroup(testCtx, "opquantize", "Tests the OpQuantizeToF16 instruction"));
 
 	const std::string shader (
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main           \"main\"\n"
@@ -2560,7 +2525,7 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%id        = OpVariable %uvec3ptr Input\n"
 		"%zero      = OpConstant %i32 0\n"
@@ -2738,22 +2703,12 @@
 	return group.release();
 }
 
-// Performs a bitwise copy of source to the destination type Dest.
-template <typename Dest, typename Src>
-Dest bitwiseCast(Src source)
-{
-  Dest dest;
-  DE_STATIC_ASSERT(sizeof(source) == sizeof(dest));
-  deMemcpy(&dest, &source, sizeof(dest));
-  return dest;
-}
-
 tcu::TestCaseGroup* createSpecConstantOpQuantizeToF16Group (tcu::TestContext& testCtx)
 {
 	de::MovePtr<tcu::TestCaseGroup>	group			(new tcu::TestCaseGroup(testCtx, "opspecconstantop_opquantize", "Tests the OpQuantizeToF16 opcode for the OpSpecConstantOp instruction"));
 
 	const std::string shader (
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpName %main           \"main\"\n"
 		"OpName %id             \"gl_GlobalInvocationID\"\n"
@@ -2767,7 +2722,7 @@
 		"OpDecorate %sc_4  SpecId 4\n"
 		"OpDecorate %sc_5  SpecId 5\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%id        = OpVariable %uvec3ptr Input\n"
 		"%zero      = OpConstant %i32 0\n"
@@ -2983,14 +2938,14 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) +
 
 		"%fmat      = OpTypeMatrix %fvec3 3\n"
 		"%ten       = OpConstant %u32 10\n"
 		"%f32arr10  = OpTypeArray %f32 %ten\n"
 		"%fst       = OpTypeStruct %f32 %f32\n"
 
-		+ string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBuffer()) +
 
 		"%id        = OpVariable %uvec3ptr Input\n"
 		"%zero      = OpConstant %i32 0\n"
@@ -3070,7 +3025,7 @@
 	vector<float>					inputFloats		(numElements, 0);
 	vector<float>					outputFloats	(numElements, 0);
 	const StringTemplate			shaderTemplate	(
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main \"main\"\n"
@@ -3078,7 +3033,7 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%u32ptr      = OpTypePointer Function %u32\n"
 
@@ -3172,7 +3127,7 @@
 	vector<float>					inputFloats		(numElements, 0);
 	vector<float>					outputFloats	(numElements, 0);
 	const StringTemplate			shaderTemplate	(
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main \"main\"\n"
@@ -3180,7 +3135,7 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%id       = OpVariable %uvec3ptr Input\n"
 		"%zero     = OpConstant %i32 0\n"
@@ -3266,7 +3221,7 @@
 	vector<float>					inputFloats		(numElements, 0);
 	vector<float>					outputFloats	(numElements, 0);
 	const StringTemplate			shaderTemplate	(
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main \"main\"\n"
@@ -3275,7 +3230,7 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%f32f = OpTypeFunction %f32\n"
 		"%id = OpVariable %uvec3ptr Input\n"
@@ -3344,7 +3299,7 @@
 	vector<float>					inputFloats		(numElements, 0);
 	vector<float>					outputFloats	(numElements, 0);
 	const StringTemplate			shaderTemplate	(
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main           \"main\"\n"
@@ -3352,7 +3307,7 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) + string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
 
 		"%f32ptr_f  = OpTypePointer Function %f32\n"
 
@@ -3415,7 +3370,7 @@
 	vector<float>					positiveFloats	(numElements, 0);
 	vector<float>					negativeFloats	(numElements, 0);
 	const StringTemplate			shaderTemplate	(
-		string(s_ShaderPreamble) +
+		string(getComputeAsmShaderPreamble()) +
 
 		"OpSource GLSL 430\n"
 		"OpName %main           \"main\"\n"
@@ -3423,18 +3378,10 @@
 
 		"OpDecorate %id BuiltIn GlobalInvocationId\n"
 
-		+ string(s_InputOutputBufferTraits) + string(s_CommonTypes) +
-		"%uvec2     = OpTypeVector %u32 2\n"
-		"%fvec4     = OpTypeVector %f32 4\n"
-		"%fmat33    = OpTypeMatrix %fvec3 3\n"
-		"%image     = OpTypeImage %f32 2D 0 0 0 1 Unknown\n"
-		"%sampler   = OpTypeSampler\n"
-		"%simage    = OpTypeSampledImage %image\n"
-		"%const100  = OpConstant %u32 100\n"
-		"%uarr100   = OpTypeArray %i32 %const100\n"
-		"%struct    = OpTypeStruct %f32 %i32 %u32\n"
-		"%pointer   = OpTypePointer Function %i32\n"
-		+ string(s_InputOutputBuffer) +
+		+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
+
+		"${TYPE}\n"
+
 		"%id        = OpVariable %uvec3ptr Input\n"
 		"%zero      = OpConstant %i32 0\n"
 
@@ -3490,2168 +3437,6 @@
 
 		return group.release();
 }
-typedef std::pair<std::string, VkShaderStageFlagBits>	EntryToStage;
-typedef map<string, vector<EntryToStage> >				ModuleMap;
-typedef map<VkShaderStageFlagBits, vector<deInt32> >	StageToSpecConstantMap;
-
-// Context for a specific test instantiation. For example, an instantiation
-// may test colors yellow/magenta/cyan/mauve in a tesselation shader
-// with an entry point named 'main_to_the_main'
-struct InstanceContext
-{
-	// Map of modules to what entry_points we care to use from those modules.
-	ModuleMap				moduleMap;
-	RGBA					inputColors[4];
-	RGBA					outputColors[4];
-	// Concrete SPIR-V code to test via boilerplate specialization.
-	map<string, string>		testCodeFragments;
-	StageToSpecConstantMap	specConstants;
-	bool					hasTessellation;
-	VkShaderStageFlagBits	requiredStages;
-
-	InstanceContext (const RGBA (&inputs)[4], const RGBA (&outputs)[4], const map<string, string>& testCodeFragments_, const StageToSpecConstantMap& specConstants_)
-		: testCodeFragments		(testCodeFragments_)
-		, specConstants			(specConstants_)
-		, hasTessellation		(false)
-		, requiredStages		(static_cast<VkShaderStageFlagBits>(0))
-	{
-		inputColors[0]		= inputs[0];
-		inputColors[1]		= inputs[1];
-		inputColors[2]		= inputs[2];
-		inputColors[3]		= inputs[3];
-
-		outputColors[0]		= outputs[0];
-		outputColors[1]		= outputs[1];
-		outputColors[2]		= outputs[2];
-		outputColors[3]		= outputs[3];
-	}
-
-	InstanceContext (const InstanceContext& other)
-		: moduleMap			(other.moduleMap)
-		, testCodeFragments	(other.testCodeFragments)
-		, specConstants		(other.specConstants)
-		, hasTessellation	(other.hasTessellation)
-		, requiredStages    (other.requiredStages)
-	{
-		inputColors[0]		= other.inputColors[0];
-		inputColors[1]		= other.inputColors[1];
-		inputColors[2]		= other.inputColors[2];
-		inputColors[3]		= other.inputColors[3];
-
-		outputColors[0]		= other.outputColors[0];
-		outputColors[1]		= other.outputColors[1];
-		outputColors[2]		= other.outputColors[2];
-		outputColors[3]		= other.outputColors[3];
-	}
-};
-
-// A description of a shader to be used for a single stage of the graphics pipeline.
-struct ShaderElement
-{
-	// The module that contains this shader entrypoint.
-	string					moduleName;
-
-	// The name of the entrypoint.
-	string					entryName;
-
-	// Which shader stage this entry point represents.
-	VkShaderStageFlagBits	stage;
-
-	ShaderElement (const string& moduleName_, const string& entryPoint_, VkShaderStageFlagBits shaderStage_)
-		: moduleName(moduleName_)
-		, entryName(entryPoint_)
-		, stage(shaderStage_)
-	{
-	}
-};
-
-void getDefaultColors (RGBA (&colors)[4])
-{
-	colors[0] = RGBA::white();
-	colors[1] = RGBA::red();
-	colors[2] = RGBA::green();
-	colors[3] = RGBA::blue();
-}
-
-void getHalfColorsFullAlpha (RGBA (&colors)[4])
-{
-	colors[0] = RGBA(127, 127, 127, 255);
-	colors[1] = RGBA(127, 0,   0,	255);
-	colors[2] = RGBA(0,	  127, 0,	255);
-	colors[3] = RGBA(0,	  0,   127, 255);
-}
-
-void getInvertedDefaultColors (RGBA (&colors)[4])
-{
-	colors[0] = RGBA(0,		0,		0,		255);
-	colors[1] = RGBA(0,		255,	255,	255);
-	colors[2] = RGBA(255,	0,		255,	255);
-	colors[3] = RGBA(255,	255,	0,		255);
-}
-
-// Turns a statically sized array of ShaderElements into an instance-context
-// by setting up the mapping of modules to their contained shaders and stages.
-// The inputs and expected outputs are given by inputColors and outputColors
-template<size_t N>
-InstanceContext createInstanceContext (const ShaderElement (&elements)[N], const RGBA (&inputColors)[4], const RGBA (&outputColors)[4], const map<string, string>& testCodeFragments, const StageToSpecConstantMap& specConstants)
-{
-	InstanceContext ctx (inputColors, outputColors, testCodeFragments, specConstants);
-	for (size_t i = 0; i < N; ++i)
-	{
-		ctx.moduleMap[elements[i].moduleName].push_back(std::make_pair(elements[i].entryName, elements[i].stage));
-		ctx.requiredStages = static_cast<VkShaderStageFlagBits>(ctx.requiredStages | elements[i].stage);
-	}
-	return ctx;
-}
-
-template<size_t N>
-inline InstanceContext createInstanceContext (const ShaderElement (&elements)[N], RGBA (&inputColors)[4], const RGBA (&outputColors)[4], const map<string, string>& testCodeFragments)
-{
-	return createInstanceContext(elements, inputColors, outputColors, testCodeFragments, StageToSpecConstantMap());
-}
-
-// The same as createInstanceContext above, but with default colors.
-template<size_t N>
-InstanceContext createInstanceContext (const ShaderElement (&elements)[N], const map<string, string>& testCodeFragments)
-{
-	RGBA defaultColors[4];
-	getDefaultColors(defaultColors);
-	return createInstanceContext(elements, defaultColors, defaultColors, testCodeFragments);
-}
-
-// For the current InstanceContext, constructs the required modules and shader stage create infos.
-void createPipelineShaderStages (const DeviceInterface& vk, const VkDevice vkDevice, InstanceContext& instance, Context& context, vector<ModuleHandleSp>& modules, vector<VkPipelineShaderStageCreateInfo>& createInfos)
-{
-	for (ModuleMap::const_iterator moduleNdx = instance.moduleMap.begin(); moduleNdx != instance.moduleMap.end(); ++moduleNdx)
-	{
-		const ModuleHandleSp mod(new Unique<VkShaderModule>(createShaderModule(vk, vkDevice, context.getBinaryCollection().get(moduleNdx->first), 0)));
-		modules.push_back(ModuleHandleSp(mod));
-		for (vector<EntryToStage>::const_iterator shaderNdx = moduleNdx->second.begin(); shaderNdx != moduleNdx->second.end(); ++shaderNdx)
-		{
-			const EntryToStage&						stage			= *shaderNdx;
-			const VkPipelineShaderStageCreateInfo	shaderParam		=
-			{
-				VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,	//	VkStructureType			sType;
-				DE_NULL,												//	const void*				pNext;
-				(VkPipelineShaderStageCreateFlags)0,
-				stage.second,											//	VkShaderStageFlagBits	stage;
-				**modules.back(),										//	VkShaderModule			module;
-				stage.first.c_str(),									//	const char*				pName;
-				(const VkSpecializationInfo*)DE_NULL,
-			};
-			createInfos.push_back(shaderParam);
-		}
-	}
-}
-
-#define SPIRV_ASSEMBLY_TYPES																	\
-	"%void = OpTypeVoid\n"																		\
-	"%bool = OpTypeBool\n"																		\
-																								\
-	"%i32 = OpTypeInt 32 1\n"																	\
-	"%u32 = OpTypeInt 32 0\n"																	\
-																								\
-	"%f32 = OpTypeFloat 32\n"																	\
-	"%v3f32 = OpTypeVector %f32 3\n"															\
-	"%v4f32 = OpTypeVector %f32 4\n"															\
-	"%v4bool = OpTypeVector %bool 4\n"															\
-																								\
-	"%v4f32_function = OpTypeFunction %v4f32 %v4f32\n"											\
-	"%fun = OpTypeFunction %void\n"																\
-																								\
-	"%ip_f32 = OpTypePointer Input %f32\n"														\
-	"%ip_i32 = OpTypePointer Input %i32\n"														\
-	"%ip_v3f32 = OpTypePointer Input %v3f32\n"													\
-	"%ip_v4f32 = OpTypePointer Input %v4f32\n"													\
-																								\
-	"%op_f32 = OpTypePointer Output %f32\n"														\
-	"%op_v4f32 = OpTypePointer Output %v4f32\n"													\
-																								\
-	"%fp_f32   = OpTypePointer Function %f32\n"													\
-	"%fp_i32   = OpTypePointer Function %i32\n"													\
-	"%fp_v4f32 = OpTypePointer Function %v4f32\n"
-
-#define SPIRV_ASSEMBLY_CONSTANTS																\
-	"%c_f32_1 = OpConstant %f32 1.0\n"															\
-	"%c_f32_0 = OpConstant %f32 0.0\n"															\
-	"%c_f32_0_5 = OpConstant %f32 0.5\n"														\
-	"%c_f32_n1  = OpConstant %f32 -1.\n"														\
-	"%c_f32_7 = OpConstant %f32 7.0\n"															\
-	"%c_f32_8 = OpConstant %f32 8.0\n"															\
-	"%c_i32_0 = OpConstant %i32 0\n"															\
-	"%c_i32_1 = OpConstant %i32 1\n"															\
-	"%c_i32_2 = OpConstant %i32 2\n"															\
-	"%c_i32_3 = OpConstant %i32 3\n"															\
-	"%c_i32_4 = OpConstant %i32 4\n"															\
-	"%c_u32_0 = OpConstant %u32 0\n"															\
-	"%c_u32_1 = OpConstant %u32 1\n"															\
-	"%c_u32_2 = OpConstant %u32 2\n"															\
-	"%c_u32_3 = OpConstant %u32 3\n"															\
-	"%c_u32_32 = OpConstant %u32 32\n"															\
-	"%c_u32_4 = OpConstant %u32 4\n"															\
-	"%c_u32_31_bits = OpConstant %u32 0x7FFFFFFF\n"												\
-	"%c_v4f32_1_1_1_1 = OpConstantComposite %v4f32 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_1\n"		\
-	"%c_v4f32_1_0_0_1 = OpConstantComposite %v4f32 %c_f32_1 %c_f32_0 %c_f32_0 %c_f32_1\n"		\
-	"%c_v4f32_0_5_0_5_0_5_0_5 = OpConstantComposite %v4f32 %c_f32_0_5 %c_f32_0_5 %c_f32_0_5 %c_f32_0_5\n"
-
-#define SPIRV_ASSEMBLY_ARRAYS																	\
-	"%a1f32 = OpTypeArray %f32 %c_u32_1\n"														\
-	"%a2f32 = OpTypeArray %f32 %c_u32_2\n"														\
-	"%a3v4f32 = OpTypeArray %v4f32 %c_u32_3\n"													\
-	"%a4f32 = OpTypeArray %f32 %c_u32_4\n"														\
-	"%a32v4f32 = OpTypeArray %v4f32 %c_u32_32\n"												\
-	"%ip_a3v4f32 = OpTypePointer Input %a3v4f32\n"												\
-	"%ip_a32v4f32 = OpTypePointer Input %a32v4f32\n"											\
-	"%op_a2f32 = OpTypePointer Output %a2f32\n"													\
-	"%op_a3v4f32 = OpTypePointer Output %a3v4f32\n"												\
-	"%op_a4f32 = OpTypePointer Output %a4f32\n"
-
-// Creates vertex-shader assembly by specializing a boilerplate StringTemplate
-// on fragments, which must (at least) map "testfun" to an OpFunction definition
-// for %test_code that takes and returns a %v4f32.  Boilerplate IDs are prefixed
-// with "BP_" to avoid collisions with fragments.
-//
-// It corresponds roughly to this GLSL:
-//;
-// layout(location = 0) in vec4 position;
-// layout(location = 1) in vec4 color;
-// layout(location = 1) out highp vec4 vtxColor;
-// void main (void) { gl_Position = position; vtxColor = test_func(color); }
-string makeVertexShaderAssembly(const map<string, string>& fragments)
-{
-// \todo [2015-11-23 awoloszyn] Remove OpName once these have stabalized
-	static const char vertexShaderBoilerplate[] =
-		"OpCapability Shader\n"
-		"OpCapability ClipDistance\n"
-		"OpCapability CullDistance\n"
-		"OpMemoryModel Logical GLSL450\n"
-		"OpEntryPoint Vertex %main \"main\" %BP_stream %BP_position %BP_vtx_color %BP_color %BP_gl_VertexIndex %BP_gl_InstanceIndex\n"
-		"${debug:opt}\n"
-		"OpName %main \"main\"\n"
-		"OpName %BP_gl_PerVertex \"gl_PerVertex\"\n"
-		"OpMemberName %BP_gl_PerVertex 0 \"gl_Position\"\n"
-		"OpMemberName %BP_gl_PerVertex 1 \"gl_PointSize\"\n"
-		"OpMemberName %BP_gl_PerVertex 2 \"gl_ClipDistance\"\n"
-		"OpMemberName %BP_gl_PerVertex 3 \"gl_CullDistance\"\n"
-		"OpName %test_code \"testfun(vf4;\"\n"
-		"OpName %BP_stream \"\"\n"
-		"OpName %BP_position \"position\"\n"
-		"OpName %BP_vtx_color \"vtxColor\"\n"
-		"OpName %BP_color \"color\"\n"
-		"OpName %BP_gl_VertexIndex \"gl_VertexIndex\"\n"
-		"OpName %BP_gl_InstanceIndex \"gl_InstanceIndex\"\n"
-		"OpMemberDecorate %BP_gl_PerVertex 0 BuiltIn Position\n"
-		"OpMemberDecorate %BP_gl_PerVertex 1 BuiltIn PointSize\n"
-		"OpMemberDecorate %BP_gl_PerVertex 2 BuiltIn ClipDistance\n"
-		"OpMemberDecorate %BP_gl_PerVertex 3 BuiltIn CullDistance\n"
-		"OpDecorate %BP_gl_PerVertex Block\n"
-		"OpDecorate %BP_position Location 0\n"
-		"OpDecorate %BP_vtx_color Location 1\n"
-		"OpDecorate %BP_color Location 1\n"
-		"OpDecorate %BP_gl_VertexIndex BuiltIn VertexIndex\n"
-		"OpDecorate %BP_gl_InstanceIndex BuiltIn InstanceIndex\n"
-		"${decoration:opt}\n"
-		SPIRV_ASSEMBLY_TYPES
-		SPIRV_ASSEMBLY_CONSTANTS
-		SPIRV_ASSEMBLY_ARRAYS
-		"%BP_gl_PerVertex = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
-		"%BP_op_gl_PerVertex = OpTypePointer Output %BP_gl_PerVertex\n"
-		"%BP_stream = OpVariable %BP_op_gl_PerVertex Output\n"
-		"%BP_position = OpVariable %ip_v4f32 Input\n"
-		"%BP_vtx_color = OpVariable %op_v4f32 Output\n"
-		"%BP_color = OpVariable %ip_v4f32 Input\n"
-		"%BP_gl_VertexIndex = OpVariable %ip_i32 Input\n"
-		"%BP_gl_InstanceIndex = OpVariable %ip_i32 Input\n"
-		"${pre_main:opt}\n"
-		"%main = OpFunction %void None %fun\n"
-		"%BP_label = OpLabel\n"
-		"%BP_pos = OpLoad %v4f32 %BP_position\n"
-		"%BP_gl_pos = OpAccessChain %op_v4f32 %BP_stream %c_i32_0\n"
-		"OpStore %BP_gl_pos %BP_pos\n"
-		"%BP_col = OpLoad %v4f32 %BP_color\n"
-		"%BP_col_transformed = OpFunctionCall %v4f32 %test_code %BP_col\n"
-		"OpStore %BP_vtx_color %BP_col_transformed\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-		"${testfun}\n";
-	return tcu::StringTemplate(vertexShaderBoilerplate).specialize(fragments);
-}
-
-// Creates tess-control-shader assembly by specializing a boilerplate
-// StringTemplate on fragments, which must (at least) map "testfun" to an
-// OpFunction definition for %test_code that takes and returns a %v4f32.
-// Boilerplate IDs are prefixed with "BP_" to avoid collisions with fragments.
-//
-// It roughly corresponds to the following GLSL.
-//
-// #version 450
-// layout(vertices = 3) out;
-// layout(location = 1) in vec4 in_color[];
-// layout(location = 1) out vec4 out_color[];
-//
-// void main() {
-//   out_color[gl_InvocationID] = testfun(in_color[gl_InvocationID]);
-//   gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;
-//   if (gl_InvocationID == 0) {
-//     gl_TessLevelOuter[0] = 1.0;
-//     gl_TessLevelOuter[1] = 1.0;
-//     gl_TessLevelOuter[2] = 1.0;
-//     gl_TessLevelInner[0] = 1.0;
-//   }
-// }
-string makeTessControlShaderAssembly (const map<string, string>& fragments)
-{
-	static const char tessControlShaderBoilerplate[] =
-		"OpCapability Tessellation\n"
-		"OpCapability ClipDistance\n"
-		"OpCapability CullDistance\n"
-		"OpMemoryModel Logical GLSL450\n"
-		"OpEntryPoint TessellationControl %BP_main \"main\" %BP_out_color %BP_gl_InvocationID %BP_in_color %BP_gl_out %BP_gl_in %BP_gl_TessLevelOuter %BP_gl_TessLevelInner\n"
-		"OpExecutionMode %BP_main OutputVertices 3\n"
-		"${debug:opt}\n"
-		"OpName %BP_main \"main\"\n"
-		"OpName %test_code \"testfun(vf4;\"\n"
-		"OpName %BP_out_color \"out_color\"\n"
-		"OpName %BP_gl_InvocationID \"gl_InvocationID\"\n"
-		"OpName %BP_in_color \"in_color\"\n"
-		"OpName %BP_gl_PerVertex \"gl_PerVertex\"\n"
-		"OpMemberName %BP_gl_PerVertex 0 \"gl_Position\"\n"
-		"OpMemberName %BP_gl_PerVertex 1 \"gl_PointSize\"\n"
-		"OpMemberName %BP_gl_PerVertex 2 \"gl_ClipDistance\"\n"
-		"OpMemberName %BP_gl_PerVertex 3 \"gl_CullDistance\"\n"
-		"OpName %BP_gl_out \"gl_out\"\n"
-		"OpName %BP_gl_PVOut \"gl_PerVertex\"\n"
-		"OpMemberName %BP_gl_PVOut 0 \"gl_Position\"\n"
-		"OpMemberName %BP_gl_PVOut 1 \"gl_PointSize\"\n"
-		"OpMemberName %BP_gl_PVOut 2 \"gl_ClipDistance\"\n"
-		"OpMemberName %BP_gl_PVOut 3 \"gl_CullDistance\"\n"
-		"OpName %BP_gl_in \"gl_in\"\n"
-		"OpName %BP_gl_TessLevelOuter \"gl_TessLevelOuter\"\n"
-		"OpName %BP_gl_TessLevelInner \"gl_TessLevelInner\"\n"
-		"OpDecorate %BP_out_color Location 1\n"
-		"OpDecorate %BP_gl_InvocationID BuiltIn InvocationId\n"
-		"OpDecorate %BP_in_color Location 1\n"
-		"OpMemberDecorate %BP_gl_PerVertex 0 BuiltIn Position\n"
-		"OpMemberDecorate %BP_gl_PerVertex 1 BuiltIn PointSize\n"
-		"OpMemberDecorate %BP_gl_PerVertex 2 BuiltIn ClipDistance\n"
-		"OpMemberDecorate %BP_gl_PerVertex 3 BuiltIn CullDistance\n"
-		"OpDecorate %BP_gl_PerVertex Block\n"
-		"OpMemberDecorate %BP_gl_PVOut 0 BuiltIn Position\n"
-		"OpMemberDecorate %BP_gl_PVOut 1 BuiltIn PointSize\n"
-		"OpMemberDecorate %BP_gl_PVOut 2 BuiltIn ClipDistance\n"
-		"OpMemberDecorate %BP_gl_PVOut 3 BuiltIn CullDistance\n"
-		"OpDecorate %BP_gl_PVOut Block\n"
-		"OpDecorate %BP_gl_TessLevelOuter Patch\n"
-		"OpDecorate %BP_gl_TessLevelOuter BuiltIn TessLevelOuter\n"
-		"OpDecorate %BP_gl_TessLevelInner Patch\n"
-		"OpDecorate %BP_gl_TessLevelInner BuiltIn TessLevelInner\n"
-		"${decoration:opt}\n"
-		SPIRV_ASSEMBLY_TYPES
-		SPIRV_ASSEMBLY_CONSTANTS
-		SPIRV_ASSEMBLY_ARRAYS
-		"%BP_out_color = OpVariable %op_a3v4f32 Output\n"
-		"%BP_gl_InvocationID = OpVariable %ip_i32 Input\n"
-		"%BP_in_color = OpVariable %ip_a32v4f32 Input\n"
-		"%BP_gl_PerVertex = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
-		"%BP_a3_gl_PerVertex = OpTypeArray %BP_gl_PerVertex %c_u32_3\n"
-		"%BP_op_a3_gl_PerVertex = OpTypePointer Output %BP_a3_gl_PerVertex\n"
-		"%BP_gl_out = OpVariable %BP_op_a3_gl_PerVertex Output\n"
-		"%BP_gl_PVOut = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
-		"%BP_a32_gl_PVOut = OpTypeArray %BP_gl_PVOut %c_u32_32\n"
-		"%BP_ip_a32_gl_PVOut = OpTypePointer Input %BP_a32_gl_PVOut\n"
-		"%BP_gl_in = OpVariable %BP_ip_a32_gl_PVOut Input\n"
-		"%BP_gl_TessLevelOuter = OpVariable %op_a4f32 Output\n"
-		"%BP_gl_TessLevelInner = OpVariable %op_a2f32 Output\n"
-		"${pre_main:opt}\n"
-
-		"%BP_main = OpFunction %void None %fun\n"
-		"%BP_label = OpLabel\n"
-
-		"%BP_gl_Invoc = OpLoad %i32 %BP_gl_InvocationID\n"
-
-		"%BP_in_col_loc = OpAccessChain %ip_v4f32 %BP_in_color %BP_gl_Invoc\n"
-		"%BP_out_col_loc = OpAccessChain %op_v4f32 %BP_out_color %BP_gl_Invoc\n"
-		"%BP_in_col_val = OpLoad %v4f32 %BP_in_col_loc\n"
-		"%BP_clr_transformed = OpFunctionCall %v4f32 %test_code %BP_in_col_val\n"
-		"OpStore %BP_out_col_loc %BP_clr_transformed\n"
-
-		"%BP_in_pos_loc = OpAccessChain %ip_v4f32 %BP_gl_in %BP_gl_Invoc %c_i32_0\n"
-		"%BP_out_pos_loc = OpAccessChain %op_v4f32 %BP_gl_out %BP_gl_Invoc %c_i32_0\n"
-		"%BP_in_pos_val = OpLoad %v4f32 %BP_in_pos_loc\n"
-		"OpStore %BP_out_pos_loc %BP_in_pos_val\n"
-
-		"%BP_cmp = OpIEqual %bool %BP_gl_Invoc %c_i32_0\n"
-		"OpSelectionMerge %BP_merge_label None\n"
-		"OpBranchConditional %BP_cmp %BP_if_label %BP_merge_label\n"
-		"%BP_if_label = OpLabel\n"
-		"%BP_gl_TessLevelOuterPos_0 = OpAccessChain %op_f32 %BP_gl_TessLevelOuter %c_i32_0\n"
-		"%BP_gl_TessLevelOuterPos_1 = OpAccessChain %op_f32 %BP_gl_TessLevelOuter %c_i32_1\n"
-		"%BP_gl_TessLevelOuterPos_2 = OpAccessChain %op_f32 %BP_gl_TessLevelOuter %c_i32_2\n"
-		"%BP_gl_TessLevelInnerPos_0 = OpAccessChain %op_f32 %BP_gl_TessLevelInner %c_i32_0\n"
-		"OpStore %BP_gl_TessLevelOuterPos_0 %c_f32_1\n"
-		"OpStore %BP_gl_TessLevelOuterPos_1 %c_f32_1\n"
-		"OpStore %BP_gl_TessLevelOuterPos_2 %c_f32_1\n"
-		"OpStore %BP_gl_TessLevelInnerPos_0 %c_f32_1\n"
-		"OpBranch %BP_merge_label\n"
-		"%BP_merge_label = OpLabel\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-		"${testfun}\n";
-	return tcu::StringTemplate(tessControlShaderBoilerplate).specialize(fragments);
-}
-
-// Creates tess-evaluation-shader assembly by specializing a boilerplate
-// StringTemplate on fragments, which must (at least) map "testfun" to an
-// OpFunction definition for %test_code that takes and returns a %v4f32.
-// Boilerplate IDs are prefixed with "BP_" to avoid collisions with fragments.
-//
-// It roughly corresponds to the following glsl.
-//
-// #version 450
-//
-// layout(triangles, equal_spacing, ccw) in;
-// layout(location = 1) in vec4 in_color[];
-// layout(location = 1) out vec4 out_color;
-//
-// #define interpolate(val)
-//   vec4(gl_TessCoord.x) * val[0] + vec4(gl_TessCoord.y) * val[1] +
-//          vec4(gl_TessCoord.z) * val[2]
-//
-// void main() {
-//   gl_Position = vec4(gl_TessCoord.x) * gl_in[0].gl_Position +
-//                  vec4(gl_TessCoord.y) * gl_in[1].gl_Position +
-//                  vec4(gl_TessCoord.z) * gl_in[2].gl_Position;
-//   out_color = testfun(interpolate(in_color));
-// }
-string makeTessEvalShaderAssembly(const map<string, string>& fragments)
-{
-	static const char tessEvalBoilerplate[] =
-		"OpCapability Tessellation\n"
-		"OpCapability ClipDistance\n"
-		"OpCapability CullDistance\n"
-		"OpMemoryModel Logical GLSL450\n"
-		"OpEntryPoint TessellationEvaluation %BP_main \"main\" %BP_stream %BP_gl_TessCoord %BP_gl_in %BP_out_color %BP_in_color\n"
-		"OpExecutionMode %BP_main Triangles\n"
-		"OpExecutionMode %BP_main SpacingEqual\n"
-		"OpExecutionMode %BP_main VertexOrderCcw\n"
-		"${debug:opt}\n"
-		"OpName %BP_main \"main\"\n"
-		"OpName %test_code \"testfun(vf4;\"\n"
-		"OpName %BP_gl_PerVertexOut \"gl_PerVertex\"\n"
-		"OpMemberName %BP_gl_PerVertexOut 0 \"gl_Position\"\n"
-		"OpMemberName %BP_gl_PerVertexOut 1 \"gl_PointSize\"\n"
-		"OpMemberName %BP_gl_PerVertexOut 2 \"gl_ClipDistance\"\n"
-		"OpMemberName %BP_gl_PerVertexOut 3 \"gl_CullDistance\"\n"
-		"OpName %BP_stream \"\"\n"
-		"OpName %BP_gl_TessCoord \"gl_TessCoord\"\n"
-		"OpName %BP_gl_PerVertexIn \"gl_PerVertex\"\n"
-		"OpMemberName %BP_gl_PerVertexIn 0 \"gl_Position\"\n"
-		"OpMemberName %BP_gl_PerVertexIn 1 \"gl_PointSize\"\n"
-		"OpMemberName %BP_gl_PerVertexIn 2 \"gl_ClipDistance\"\n"
-		"OpMemberName %BP_gl_PerVertexIn 3 \"gl_CullDistance\"\n"
-		"OpName %BP_gl_in \"gl_in\"\n"
-		"OpName %BP_out_color \"out_color\"\n"
-		"OpName %BP_in_color \"in_color\"\n"
-		"OpMemberDecorate %BP_gl_PerVertexOut 0 BuiltIn Position\n"
-		"OpMemberDecorate %BP_gl_PerVertexOut 1 BuiltIn PointSize\n"
-		"OpMemberDecorate %BP_gl_PerVertexOut 2 BuiltIn ClipDistance\n"
-		"OpMemberDecorate %BP_gl_PerVertexOut 3 BuiltIn CullDistance\n"
-		"OpDecorate %BP_gl_PerVertexOut Block\n"
-		"OpDecorate %BP_gl_TessCoord BuiltIn TessCoord\n"
-		"OpMemberDecorate %BP_gl_PerVertexIn 0 BuiltIn Position\n"
-		"OpMemberDecorate %BP_gl_PerVertexIn 1 BuiltIn PointSize\n"
-		"OpMemberDecorate %BP_gl_PerVertexIn 2 BuiltIn ClipDistance\n"
-		"OpMemberDecorate %BP_gl_PerVertexIn 3 BuiltIn CullDistance\n"
-		"OpDecorate %BP_gl_PerVertexIn Block\n"
-		"OpDecorate %BP_out_color Location 1\n"
-		"OpDecorate %BP_in_color Location 1\n"
-		"${decoration:opt}\n"
-		SPIRV_ASSEMBLY_TYPES
-		SPIRV_ASSEMBLY_CONSTANTS
-		SPIRV_ASSEMBLY_ARRAYS
-		"%BP_gl_PerVertexOut = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
-		"%BP_op_gl_PerVertexOut = OpTypePointer Output %BP_gl_PerVertexOut\n"
-		"%BP_stream = OpVariable %BP_op_gl_PerVertexOut Output\n"
-		"%BP_gl_TessCoord = OpVariable %ip_v3f32 Input\n"
-		"%BP_gl_PerVertexIn = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
-		"%BP_a32_gl_PerVertexIn = OpTypeArray %BP_gl_PerVertexIn %c_u32_32\n"
-		"%BP_ip_a32_gl_PerVertexIn = OpTypePointer Input %BP_a32_gl_PerVertexIn\n"
-		"%BP_gl_in = OpVariable %BP_ip_a32_gl_PerVertexIn Input\n"
-		"%BP_out_color = OpVariable %op_v4f32 Output\n"
-		"%BP_in_color = OpVariable %ip_a32v4f32 Input\n"
-		"${pre_main:opt}\n"
-		"%BP_main = OpFunction %void None %fun\n"
-		"%BP_label = OpLabel\n"
-		"%BP_gl_TC_0 = OpAccessChain %ip_f32 %BP_gl_TessCoord %c_u32_0\n"
-		"%BP_gl_TC_1 = OpAccessChain %ip_f32 %BP_gl_TessCoord %c_u32_1\n"
-		"%BP_gl_TC_2 = OpAccessChain %ip_f32 %BP_gl_TessCoord %c_u32_2\n"
-		"%BP_gl_in_gl_Pos_0 = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_0 %c_i32_0\n"
-		"%BP_gl_in_gl_Pos_1 = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_1 %c_i32_0\n"
-		"%BP_gl_in_gl_Pos_2 = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_2 %c_i32_0\n"
-
-		"%BP_gl_OPos = OpAccessChain %op_v4f32 %BP_stream %c_i32_0\n"
-		"%BP_in_color_0 = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_0\n"
-		"%BP_in_color_1 = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_1\n"
-		"%BP_in_color_2 = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_2\n"
-
-		"%BP_TC_W_0 = OpLoad %f32 %BP_gl_TC_0\n"
-		"%BP_TC_W_1 = OpLoad %f32 %BP_gl_TC_1\n"
-		"%BP_TC_W_2 = OpLoad %f32 %BP_gl_TC_2\n"
-		"%BP_v4f32_TC_0 = OpCompositeConstruct %v4f32 %BP_TC_W_0 %BP_TC_W_0 %BP_TC_W_0 %BP_TC_W_0\n"
-		"%BP_v4f32_TC_1 = OpCompositeConstruct %v4f32 %BP_TC_W_1 %BP_TC_W_1 %BP_TC_W_1 %BP_TC_W_1\n"
-		"%BP_v4f32_TC_2 = OpCompositeConstruct %v4f32 %BP_TC_W_2 %BP_TC_W_2 %BP_TC_W_2 %BP_TC_W_2\n"
-
-		"%BP_gl_IP_0 = OpLoad %v4f32 %BP_gl_in_gl_Pos_0\n"
-		"%BP_gl_IP_1 = OpLoad %v4f32 %BP_gl_in_gl_Pos_1\n"
-		"%BP_gl_IP_2 = OpLoad %v4f32 %BP_gl_in_gl_Pos_2\n"
-
-		"%BP_IP_W_0 = OpFMul %v4f32 %BP_v4f32_TC_0 %BP_gl_IP_0\n"
-		"%BP_IP_W_1 = OpFMul %v4f32 %BP_v4f32_TC_1 %BP_gl_IP_1\n"
-		"%BP_IP_W_2 = OpFMul %v4f32 %BP_v4f32_TC_2 %BP_gl_IP_2\n"
-
-		"%BP_pos_sum_0 = OpFAdd %v4f32 %BP_IP_W_0 %BP_IP_W_1\n"
-		"%BP_pos_sum_1 = OpFAdd %v4f32 %BP_pos_sum_0 %BP_IP_W_2\n"
-
-		"OpStore %BP_gl_OPos %BP_pos_sum_1\n"
-
-		"%BP_IC_0 = OpLoad %v4f32 %BP_in_color_0\n"
-		"%BP_IC_1 = OpLoad %v4f32 %BP_in_color_1\n"
-		"%BP_IC_2 = OpLoad %v4f32 %BP_in_color_2\n"
-
-		"%BP_IC_W_0 = OpFMul %v4f32 %BP_v4f32_TC_0 %BP_IC_0\n"
-		"%BP_IC_W_1 = OpFMul %v4f32 %BP_v4f32_TC_1 %BP_IC_1\n"
-		"%BP_IC_W_2 = OpFMul %v4f32 %BP_v4f32_TC_2 %BP_IC_2\n"
-
-		"%BP_col_sum_0 = OpFAdd %v4f32 %BP_IC_W_0 %BP_IC_W_1\n"
-		"%BP_col_sum_1 = OpFAdd %v4f32 %BP_col_sum_0 %BP_IC_W_2\n"
-
-		"%BP_clr_transformed = OpFunctionCall %v4f32 %test_code %BP_col_sum_1\n"
-
-		"OpStore %BP_out_color %BP_clr_transformed\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-		"${testfun}\n";
-	return tcu::StringTemplate(tessEvalBoilerplate).specialize(fragments);
-}
-
-// Creates geometry-shader assembly by specializing a boilerplate StringTemplate
-// on fragments, which must (at least) map "testfun" to an OpFunction definition
-// for %test_code that takes and returns a %v4f32.  Boilerplate IDs are prefixed
-// with "BP_" to avoid collisions with fragments.
-//
-// Derived from this GLSL:
-//
-// #version 450
-// layout(triangles) in;
-// layout(triangle_strip, max_vertices = 3) out;
-//
-// layout(location = 1) in vec4 in_color[];
-// layout(location = 1) out vec4 out_color;
-//
-// void main() {
-//   gl_Position = gl_in[0].gl_Position;
-//   out_color = test_fun(in_color[0]);
-//   EmitVertex();
-//   gl_Position = gl_in[1].gl_Position;
-//   out_color = test_fun(in_color[1]);
-//   EmitVertex();
-//   gl_Position = gl_in[2].gl_Position;
-//   out_color = test_fun(in_color[2]);
-//   EmitVertex();
-//   EndPrimitive();
-// }
-string makeGeometryShaderAssembly(const map<string, string>& fragments)
-{
-	static const char geometryShaderBoilerplate[] =
-		"OpCapability Geometry\n"
-		"OpCapability ClipDistance\n"
-		"OpCapability CullDistance\n"
-		"OpMemoryModel Logical GLSL450\n"
-		"OpEntryPoint Geometry %BP_main \"main\" %BP_out_gl_position %BP_gl_in %BP_out_color %BP_in_color\n"
-		"OpExecutionMode %BP_main Triangles\n"
-		"OpExecutionMode %BP_main OutputTriangleStrip\n"
-		"OpExecutionMode %BP_main OutputVertices 3\n"
-		"${debug:opt}\n"
-		"OpName %BP_main \"main\"\n"
-		"OpName %BP_per_vertex_in \"gl_PerVertex\"\n"
-		"OpMemberName %BP_per_vertex_in 0 \"gl_Position\"\n"
-		"OpMemberName %BP_per_vertex_in 1 \"gl_PointSize\"\n"
-		"OpMemberName %BP_per_vertex_in 2 \"gl_ClipDistance\"\n"
-		"OpMemberName %BP_per_vertex_in 3 \"gl_CullDistance\"\n"
-		"OpName %BP_gl_in \"gl_in\"\n"
-		"OpName %BP_out_color \"out_color\"\n"
-		"OpName %BP_in_color \"in_color\"\n"
-		"OpName %test_code \"testfun(vf4;\"\n"
-		"OpDecorate %BP_out_gl_position BuiltIn Position\n"
-		"OpMemberDecorate %BP_per_vertex_in 0 BuiltIn Position\n"
-		"OpMemberDecorate %BP_per_vertex_in 1 BuiltIn PointSize\n"
-		"OpMemberDecorate %BP_per_vertex_in 2 BuiltIn ClipDistance\n"
-		"OpMemberDecorate %BP_per_vertex_in 3 BuiltIn CullDistance\n"
-		"OpDecorate %BP_per_vertex_in Block\n"
-		"OpDecorate %BP_out_color Location 1\n"
-		"OpDecorate %BP_in_color Location 1\n"
-		"${decoration:opt}\n"
-		SPIRV_ASSEMBLY_TYPES
-		SPIRV_ASSEMBLY_CONSTANTS
-		SPIRV_ASSEMBLY_ARRAYS
-		"%BP_per_vertex_in = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
-		"%BP_a3_per_vertex_in = OpTypeArray %BP_per_vertex_in %c_u32_3\n"
-		"%BP_ip_a3_per_vertex_in = OpTypePointer Input %BP_a3_per_vertex_in\n"
-
-		"%BP_gl_in = OpVariable %BP_ip_a3_per_vertex_in Input\n"
-		"%BP_out_color = OpVariable %op_v4f32 Output\n"
-		"%BP_in_color = OpVariable %ip_a3v4f32 Input\n"
-		"%BP_out_gl_position = OpVariable %op_v4f32 Output\n"
-		"${pre_main:opt}\n"
-
-		"%BP_main = OpFunction %void None %fun\n"
-		"%BP_label = OpLabel\n"
-		"%BP_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_0 %c_i32_0\n"
-		"%BP_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_1 %c_i32_0\n"
-		"%BP_gl_in_2_gl_position = OpAccessChain %ip_v4f32 %BP_gl_in %c_i32_2 %c_i32_0\n"
-
-		"%BP_in_position_0 = OpLoad %v4f32 %BP_gl_in_0_gl_position\n"
-		"%BP_in_position_1 = OpLoad %v4f32 %BP_gl_in_1_gl_position\n"
-		"%BP_in_position_2 = OpLoad %v4f32 %BP_gl_in_2_gl_position \n"
-
-		"%BP_in_color_0_ptr = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_0\n"
-		"%BP_in_color_1_ptr = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_1\n"
-		"%BP_in_color_2_ptr = OpAccessChain %ip_v4f32 %BP_in_color %c_i32_2\n"
-
-		"%BP_in_color_0 = OpLoad %v4f32 %BP_in_color_0_ptr\n"
-		"%BP_in_color_1 = OpLoad %v4f32 %BP_in_color_1_ptr\n"
-		"%BP_in_color_2 = OpLoad %v4f32 %BP_in_color_2_ptr\n"
-
-		"%BP_transformed_in_color_0 = OpFunctionCall %v4f32 %test_code %BP_in_color_0\n"
-		"%BP_transformed_in_color_1 = OpFunctionCall %v4f32 %test_code %BP_in_color_1\n"
-		"%BP_transformed_in_color_2 = OpFunctionCall %v4f32 %test_code %BP_in_color_2\n"
-
-
-		"OpStore %BP_out_gl_position %BP_in_position_0\n"
-		"OpStore %BP_out_color %BP_transformed_in_color_0\n"
-		"OpEmitVertex\n"
-
-		"OpStore %BP_out_gl_position %BP_in_position_1\n"
-		"OpStore %BP_out_color %BP_transformed_in_color_1\n"
-		"OpEmitVertex\n"
-
-		"OpStore %BP_out_gl_position %BP_in_position_2\n"
-		"OpStore %BP_out_color %BP_transformed_in_color_2\n"
-		"OpEmitVertex\n"
-
-		"OpEndPrimitive\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-		"${testfun}\n";
-	return tcu::StringTemplate(geometryShaderBoilerplate).specialize(fragments);
-}
-
-// Creates fragment-shader assembly by specializing a boilerplate StringTemplate
-// on fragments, which must (at least) map "testfun" to an OpFunction definition
-// for %test_code that takes and returns a %v4f32.  Boilerplate IDs are prefixed
-// with "BP_" to avoid collisions with fragments.
-//
-// Derived from this GLSL:
-//
-// layout(location = 1) in highp vec4 vtxColor;
-// layout(location = 0) out highp vec4 fragColor;
-// highp vec4 testfun(highp vec4 x) { return x; }
-// void main(void) { fragColor = testfun(vtxColor); }
-//
-// with modifications including passing vtxColor by value and ripping out
-// testfun() definition.
-string makeFragmentShaderAssembly(const map<string, string>& fragments)
-{
-	static const char fragmentShaderBoilerplate[] =
-		"OpCapability Shader\n"
-		"OpMemoryModel Logical GLSL450\n"
-		"OpEntryPoint Fragment %BP_main \"main\" %BP_vtxColor %BP_fragColor\n"
-		"OpExecutionMode %BP_main OriginUpperLeft\n"
-		"${debug:opt}\n"
-		"OpName %BP_main \"main\"\n"
-		"OpName %BP_fragColor \"fragColor\"\n"
-		"OpName %BP_vtxColor \"vtxColor\"\n"
-		"OpName %test_code \"testfun(vf4;\"\n"
-		"OpDecorate %BP_fragColor Location 0\n"
-		"OpDecorate %BP_vtxColor Location 1\n"
-		"${decoration:opt}\n"
-		SPIRV_ASSEMBLY_TYPES
-		SPIRV_ASSEMBLY_CONSTANTS
-		SPIRV_ASSEMBLY_ARRAYS
-		"%BP_fragColor = OpVariable %op_v4f32 Output\n"
-		"%BP_vtxColor = OpVariable %ip_v4f32 Input\n"
-		"${pre_main:opt}\n"
-		"%BP_main = OpFunction %void None %fun\n"
-		"%BP_label_main = OpLabel\n"
-		"%BP_tmp1 = OpLoad %v4f32 %BP_vtxColor\n"
-		"%BP_tmp2 = OpFunctionCall %v4f32 %test_code %BP_tmp1\n"
-		"OpStore %BP_fragColor %BP_tmp2\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-		"${testfun}\n";
-	return tcu::StringTemplate(fragmentShaderBoilerplate).specialize(fragments);
-}
-
-// Creates fragments that specialize into a simple pass-through shader (of any kind).
-map<string, string> passthruFragments(void)
-{
-	map<string, string> fragments;
-	fragments["testfun"] =
-		// A %test_code function that returns its argument unchanged.
-		"%test_code = OpFunction %v4f32 None %v4f32_function\n"
-		"%param1 = OpFunctionParameter %v4f32\n"
-		"%label_testfun = OpLabel\n"
-		"OpReturnValue %param1\n"
-		"OpFunctionEnd\n";
-	return fragments;
-}
-
-// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
-// Vertex shader gets custom code from context, the rest are pass-through.
-void addShaderCodeCustomVertex(vk::SourceCollections& dst, InstanceContext context)
-{
-	map<string, string> passthru = passthruFragments();
-	dst.spirvAsmSources.add("vert") << makeVertexShaderAssembly(context.testCodeFragments);
-	dst.spirvAsmSources.add("frag") << makeFragmentShaderAssembly(passthru);
-}
-
-// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
-// Tessellation control shader gets custom code from context, the rest are
-// pass-through.
-void addShaderCodeCustomTessControl(vk::SourceCollections& dst, InstanceContext context)
-{
-	map<string, string> passthru = passthruFragments();
-	dst.spirvAsmSources.add("vert") << makeVertexShaderAssembly(passthru);
-	dst.spirvAsmSources.add("tessc") << makeTessControlShaderAssembly(context.testCodeFragments);
-	dst.spirvAsmSources.add("tesse") << makeTessEvalShaderAssembly(passthru);
-	dst.spirvAsmSources.add("frag") << makeFragmentShaderAssembly(passthru);
-}
-
-// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
-// Tessellation evaluation shader gets custom code from context, the rest are
-// pass-through.
-void addShaderCodeCustomTessEval(vk::SourceCollections& dst, InstanceContext context)
-{
-	map<string, string> passthru = passthruFragments();
-	dst.spirvAsmSources.add("vert") << makeVertexShaderAssembly(passthru);
-	dst.spirvAsmSources.add("tessc") << makeTessControlShaderAssembly(passthru);
-	dst.spirvAsmSources.add("tesse") << makeTessEvalShaderAssembly(context.testCodeFragments);
-	dst.spirvAsmSources.add("frag") << makeFragmentShaderAssembly(passthru);
-}
-
-// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
-// Geometry shader gets custom code from context, the rest are pass-through.
-void addShaderCodeCustomGeometry(vk::SourceCollections& dst, InstanceContext context)
-{
-	map<string, string> passthru = passthruFragments();
-	dst.spirvAsmSources.add("vert") << makeVertexShaderAssembly(passthru);
-	dst.spirvAsmSources.add("geom") << makeGeometryShaderAssembly(context.testCodeFragments);
-	dst.spirvAsmSources.add("frag") << makeFragmentShaderAssembly(passthru);
-}
-
-// Adds shader assembly text to dst.spirvAsmSources for all shader kinds.
-// Fragment shader gets custom code from context, the rest are pass-through.
-void addShaderCodeCustomFragment(vk::SourceCollections& dst, InstanceContext context)
-{
-	map<string, string> passthru = passthruFragments();
-	dst.spirvAsmSources.add("vert") << makeVertexShaderAssembly(passthru);
-	dst.spirvAsmSources.add("frag") << makeFragmentShaderAssembly(context.testCodeFragments);
-}
-
-void createCombinedModule(vk::SourceCollections& dst, InstanceContext)
-{
-	// \todo [2015-12-07 awoloszyn] Make tessellation / geometry conditional
-	// \todo [2015-12-07 awoloszyn] Remove OpName and OpMemberName at some point
-	dst.spirvAsmSources.add("module") <<
-		"OpCapability Shader\n"
-		"OpCapability ClipDistance\n"
-		"OpCapability CullDistance\n"
-		"OpCapability Geometry\n"
-		"OpCapability Tessellation\n"
-		"OpMemoryModel Logical GLSL450\n"
-
-		"OpEntryPoint Vertex %vert_main \"main\" %vert_Position %vert_vtxColor %vert_color %vert_vtxPosition %vert_vertex_id %vert_instance_id\n"
-		"OpEntryPoint Geometry %geom_main \"main\" %geom_out_gl_position %geom_gl_in %geom_out_color %geom_in_color\n"
-		"OpEntryPoint TessellationControl %tessc_main \"main\" %tessc_out_color %tessc_gl_InvocationID %tessc_in_color %tessc_out_position %tessc_in_position %tessc_gl_TessLevelOuter %tessc_gl_TessLevelInner\n"
-		"OpEntryPoint TessellationEvaluation %tesse_main \"main\" %tesse_stream %tesse_gl_tessCoord %tesse_in_position %tesse_out_color %tesse_in_color \n"
-		"OpEntryPoint Fragment %frag_main \"main\" %frag_vtxColor %frag_fragColor\n"
-
-		"OpExecutionMode %geom_main Triangles\n"
-		"OpExecutionMode %geom_main OutputTriangleStrip\n"
-		"OpExecutionMode %geom_main OutputVertices 3\n"
-
-		"OpExecutionMode %tessc_main OutputVertices 3\n"
-
-		"OpExecutionMode %tesse_main Triangles\n"
-		"OpExecutionMode %tesse_main SpacingEqual\n"
-		"OpExecutionMode %tesse_main VertexOrderCcw\n"
-
-		"OpExecutionMode %frag_main OriginUpperLeft\n"
-
-		"OpName %vert_main \"main\"\n"
-		"OpName %vert_vtxPosition \"vtxPosition\"\n"
-		"OpName %vert_Position \"position\"\n"
-		"OpName %vert_vtxColor \"vtxColor\"\n"
-		"OpName %vert_color \"color\"\n"
-		"OpName %vert_vertex_id \"gl_VertexIndex\"\n"
-		"OpName %vert_instance_id \"gl_InstanceIndex\"\n"
-		"OpName %geom_main \"main\"\n"
-		"OpName %geom_per_vertex_in \"gl_PerVertex\"\n"
-		"OpMemberName %geom_per_vertex_in 0 \"gl_Position\"\n"
-		"OpMemberName %geom_per_vertex_in 1 \"gl_PointSize\"\n"
-		"OpMemberName %geom_per_vertex_in 2 \"gl_ClipDistance\"\n"
-		"OpMemberName %geom_per_vertex_in 3 \"gl_CullDistance\"\n"
-		"OpName %geom_gl_in \"gl_in\"\n"
-		"OpName %geom_out_color \"out_color\"\n"
-		"OpName %geom_in_color \"in_color\"\n"
-		"OpName %tessc_main \"main\"\n"
-		"OpName %tessc_out_color \"out_color\"\n"
-		"OpName %tessc_gl_InvocationID \"gl_InvocationID\"\n"
-		"OpName %tessc_in_color \"in_color\"\n"
-		"OpName %tessc_out_position \"out_position\"\n"
-		"OpName %tessc_in_position \"in_position\"\n"
-		"OpName %tessc_gl_TessLevelOuter \"gl_TessLevelOuter\"\n"
-		"OpName %tessc_gl_TessLevelInner \"gl_TessLevelInner\"\n"
-		"OpName %tesse_main \"main\"\n"
-		"OpName %tesse_per_vertex_out \"gl_PerVertex\"\n"
-		"OpMemberName %tesse_per_vertex_out 0 \"gl_Position\"\n"
-		"OpMemberName %tesse_per_vertex_out 1 \"gl_PointSize\"\n"
-		"OpMemberName %tesse_per_vertex_out 2 \"gl_ClipDistance\"\n"
-		"OpMemberName %tesse_per_vertex_out 3 \"gl_CullDistance\"\n"
-		"OpName %tesse_stream \"\"\n"
-		"OpName %tesse_gl_tessCoord \"gl_TessCoord\"\n"
-		"OpName %tesse_in_position \"in_position\"\n"
-		"OpName %tesse_out_color \"out_color\"\n"
-		"OpName %tesse_in_color \"in_color\"\n"
-		"OpName %frag_main \"main\"\n"
-		"OpName %frag_fragColor \"fragColor\"\n"
-		"OpName %frag_vtxColor \"vtxColor\"\n"
-
-		"; Vertex decorations\n"
-		"OpDecorate %vert_vtxPosition Location 2\n"
-		"OpDecorate %vert_Position Location 0\n"
-		"OpDecorate %vert_vtxColor Location 1\n"
-		"OpDecorate %vert_color Location 1\n"
-		"OpDecorate %vert_vertex_id BuiltIn VertexIndex\n"
-		"OpDecorate %vert_instance_id BuiltIn InstanceIndex\n"
-
-		"; Geometry decorations\n"
-		"OpDecorate %geom_out_gl_position BuiltIn Position\n"
-		"OpMemberDecorate %geom_per_vertex_in 0 BuiltIn Position\n"
-		"OpMemberDecorate %geom_per_vertex_in 1 BuiltIn PointSize\n"
-		"OpMemberDecorate %geom_per_vertex_in 2 BuiltIn ClipDistance\n"
-		"OpMemberDecorate %geom_per_vertex_in 3 BuiltIn CullDistance\n"
-		"OpDecorate %geom_per_vertex_in Block\n"
-		"OpDecorate %geom_out_color Location 1\n"
-		"OpDecorate %geom_in_color Location 1\n"
-
-		"; Tessellation Control decorations\n"
-		"OpDecorate %tessc_out_color Location 1\n"
-		"OpDecorate %tessc_gl_InvocationID BuiltIn InvocationId\n"
-		"OpDecorate %tessc_in_color Location 1\n"
-		"OpDecorate %tessc_out_position Location 2\n"
-		"OpDecorate %tessc_in_position Location 2\n"
-		"OpDecorate %tessc_gl_TessLevelOuter Patch\n"
-		"OpDecorate %tessc_gl_TessLevelOuter BuiltIn TessLevelOuter\n"
-		"OpDecorate %tessc_gl_TessLevelInner Patch\n"
-		"OpDecorate %tessc_gl_TessLevelInner BuiltIn TessLevelInner\n"
-
-		"; Tessellation Evaluation decorations\n"
-		"OpMemberDecorate %tesse_per_vertex_out 0 BuiltIn Position\n"
-		"OpMemberDecorate %tesse_per_vertex_out 1 BuiltIn PointSize\n"
-		"OpMemberDecorate %tesse_per_vertex_out 2 BuiltIn ClipDistance\n"
-		"OpMemberDecorate %tesse_per_vertex_out 3 BuiltIn CullDistance\n"
-		"OpDecorate %tesse_per_vertex_out Block\n"
-		"OpDecorate %tesse_gl_tessCoord BuiltIn TessCoord\n"
-		"OpDecorate %tesse_in_position Location 2\n"
-		"OpDecorate %tesse_out_color Location 1\n"
-		"OpDecorate %tesse_in_color Location 1\n"
-
-		"; Fragment decorations\n"
-		"OpDecorate %frag_fragColor Location 0\n"
-		"OpDecorate %frag_vtxColor Location 1\n"
-
-		SPIRV_ASSEMBLY_TYPES
-		SPIRV_ASSEMBLY_CONSTANTS
-		SPIRV_ASSEMBLY_ARRAYS
-
-		"; Vertex Variables\n"
-		"%vert_vtxPosition = OpVariable %op_v4f32 Output\n"
-		"%vert_Position = OpVariable %ip_v4f32 Input\n"
-		"%vert_vtxColor = OpVariable %op_v4f32 Output\n"
-		"%vert_color = OpVariable %ip_v4f32 Input\n"
-		"%vert_vertex_id = OpVariable %ip_i32 Input\n"
-		"%vert_instance_id = OpVariable %ip_i32 Input\n"
-
-		"; Geometry Variables\n"
-		"%geom_per_vertex_in = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
-		"%geom_a3_per_vertex_in = OpTypeArray %geom_per_vertex_in %c_u32_3\n"
-		"%geom_ip_a3_per_vertex_in = OpTypePointer Input %geom_a3_per_vertex_in\n"
-		"%geom_gl_in = OpVariable %geom_ip_a3_per_vertex_in Input\n"
-		"%geom_out_color = OpVariable %op_v4f32 Output\n"
-		"%geom_in_color = OpVariable %ip_a3v4f32 Input\n"
-		"%geom_out_gl_position = OpVariable %op_v4f32 Output\n"
-
-		"; Tessellation Control Variables\n"
-		"%tessc_out_color = OpVariable %op_a3v4f32 Output\n"
-		"%tessc_gl_InvocationID = OpVariable %ip_i32 Input\n"
-		"%tessc_in_color = OpVariable %ip_a32v4f32 Input\n"
-		"%tessc_out_position = OpVariable %op_a3v4f32 Output\n"
-		"%tessc_in_position = OpVariable %ip_a32v4f32 Input\n"
-		"%tessc_gl_TessLevelOuter = OpVariable %op_a4f32 Output\n"
-		"%tessc_gl_TessLevelInner = OpVariable %op_a2f32 Output\n"
-
-		"; Tessellation Evaluation Decorations\n"
-		"%tesse_per_vertex_out = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
-		"%tesse_op_per_vertex_out = OpTypePointer Output %tesse_per_vertex_out\n"
-		"%tesse_stream = OpVariable %tesse_op_per_vertex_out Output\n"
-		"%tesse_gl_tessCoord = OpVariable %ip_v3f32 Input\n"
-		"%tesse_in_position = OpVariable %ip_a32v4f32 Input\n"
-		"%tesse_out_color = OpVariable %op_v4f32 Output\n"
-		"%tesse_in_color = OpVariable %ip_a32v4f32 Input\n"
-
-		"; Fragment Variables\n"
-		"%frag_fragColor = OpVariable %op_v4f32 Output\n"
-		"%frag_vtxColor = OpVariable %ip_v4f32 Input\n"
-
-		"; Vertex Entry\n"
-		"%vert_main = OpFunction %void None %fun\n"
-		"%vert_label = OpLabel\n"
-		"%vert_tmp_position = OpLoad %v4f32 %vert_Position\n"
-		"OpStore %vert_vtxPosition %vert_tmp_position\n"
-		"%vert_tmp_color = OpLoad %v4f32 %vert_color\n"
-		"OpStore %vert_vtxColor %vert_tmp_color\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-
-		"; Geometry Entry\n"
-		"%geom_main = OpFunction %void None %fun\n"
-		"%geom_label = OpLabel\n"
-		"%geom_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %geom_gl_in %c_i32_0 %c_i32_0\n"
-		"%geom_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %geom_gl_in %c_i32_1 %c_i32_0\n"
-		"%geom_gl_in_2_gl_position = OpAccessChain %ip_v4f32 %geom_gl_in %c_i32_2 %c_i32_0\n"
-		"%geom_in_position_0 = OpLoad %v4f32 %geom_gl_in_0_gl_position\n"
-		"%geom_in_position_1 = OpLoad %v4f32 %geom_gl_in_1_gl_position\n"
-		"%geom_in_position_2 = OpLoad %v4f32 %geom_gl_in_2_gl_position \n"
-		"%geom_in_color_0_ptr = OpAccessChain %ip_v4f32 %geom_in_color %c_i32_0\n"
-		"%geom_in_color_1_ptr = OpAccessChain %ip_v4f32 %geom_in_color %c_i32_1\n"
-		"%geom_in_color_2_ptr = OpAccessChain %ip_v4f32 %geom_in_color %c_i32_2\n"
-		"%geom_in_color_0 = OpLoad %v4f32 %geom_in_color_0_ptr\n"
-		"%geom_in_color_1 = OpLoad %v4f32 %geom_in_color_1_ptr\n"
-		"%geom_in_color_2 = OpLoad %v4f32 %geom_in_color_2_ptr\n"
-		"OpStore %geom_out_gl_position %geom_in_position_0\n"
-		"OpStore %geom_out_color %geom_in_color_0\n"
-		"OpEmitVertex\n"
-		"OpStore %geom_out_gl_position %geom_in_position_1\n"
-		"OpStore %geom_out_color %geom_in_color_1\n"
-		"OpEmitVertex\n"
-		"OpStore %geom_out_gl_position %geom_in_position_2\n"
-		"OpStore %geom_out_color %geom_in_color_2\n"
-		"OpEmitVertex\n"
-		"OpEndPrimitive\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-
-		"; Tessellation Control Entry\n"
-		"%tessc_main = OpFunction %void None %fun\n"
-		"%tessc_label = OpLabel\n"
-		"%tessc_invocation_id = OpLoad %i32 %tessc_gl_InvocationID\n"
-		"%tessc_in_color_ptr = OpAccessChain %ip_v4f32 %tessc_in_color %tessc_invocation_id\n"
-		"%tessc_in_position_ptr = OpAccessChain %ip_v4f32 %tessc_in_position %tessc_invocation_id\n"
-		"%tessc_in_color_val = OpLoad %v4f32 %tessc_in_color_ptr\n"
-		"%tessc_in_position_val = OpLoad %v4f32 %tessc_in_position_ptr\n"
-		"%tessc_out_color_ptr = OpAccessChain %op_v4f32 %tessc_out_color %tessc_invocation_id\n"
-		"%tessc_out_position_ptr = OpAccessChain %op_v4f32 %tessc_out_position %tessc_invocation_id\n"
-		"OpStore %tessc_out_color_ptr %tessc_in_color_val\n"
-		"OpStore %tessc_out_position_ptr %tessc_in_position_val\n"
-		"%tessc_is_first_invocation = OpIEqual %bool %tessc_invocation_id %c_i32_0\n"
-		"OpSelectionMerge %tessc_merge_label None\n"
-		"OpBranchConditional %tessc_is_first_invocation %tessc_first_invocation %tessc_merge_label\n"
-		"%tessc_first_invocation = OpLabel\n"
-		"%tessc_tess_outer_0 = OpAccessChain %op_f32 %tessc_gl_TessLevelOuter %c_i32_0\n"
-		"%tessc_tess_outer_1 = OpAccessChain %op_f32 %tessc_gl_TessLevelOuter %c_i32_1\n"
-		"%tessc_tess_outer_2 = OpAccessChain %op_f32 %tessc_gl_TessLevelOuter %c_i32_2\n"
-		"%tessc_tess_inner = OpAccessChain %op_f32 %tessc_gl_TessLevelInner %c_i32_0\n"
-		"OpStore %tessc_tess_outer_0 %c_f32_1\n"
-		"OpStore %tessc_tess_outer_1 %c_f32_1\n"
-		"OpStore %tessc_tess_outer_2 %c_f32_1\n"
-		"OpStore %tessc_tess_inner %c_f32_1\n"
-		"OpBranch %tessc_merge_label\n"
-		"%tessc_merge_label = OpLabel\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-
-		"; Tessellation Evaluation Entry\n"
-		"%tesse_main = OpFunction %void None %fun\n"
-		"%tesse_label = OpLabel\n"
-		"%tesse_tc_0_ptr = OpAccessChain %ip_f32 %tesse_gl_tessCoord %c_u32_0\n"
-		"%tesse_tc_1_ptr = OpAccessChain %ip_f32 %tesse_gl_tessCoord %c_u32_1\n"
-		"%tesse_tc_2_ptr = OpAccessChain %ip_f32 %tesse_gl_tessCoord %c_u32_2\n"
-		"%tesse_tc_0 = OpLoad %f32 %tesse_tc_0_ptr\n"
-		"%tesse_tc_1 = OpLoad %f32 %tesse_tc_1_ptr\n"
-		"%tesse_tc_2 = OpLoad %f32 %tesse_tc_2_ptr\n"
-		"%tesse_in_pos_0_ptr = OpAccessChain %ip_v4f32 %tesse_in_position %c_i32_0\n"
-		"%tesse_in_pos_1_ptr = OpAccessChain %ip_v4f32 %tesse_in_position %c_i32_1\n"
-		"%tesse_in_pos_2_ptr = OpAccessChain %ip_v4f32 %tesse_in_position %c_i32_2\n"
-		"%tesse_in_pos_0 = OpLoad %v4f32 %tesse_in_pos_0_ptr\n"
-		"%tesse_in_pos_1 = OpLoad %v4f32 %tesse_in_pos_1_ptr\n"
-		"%tesse_in_pos_2 = OpLoad %v4f32 %tesse_in_pos_2_ptr\n"
-		"%tesse_in_pos_0_weighted = OpVectorTimesScalar %v4f32 %tesse_in_pos_0 %tesse_tc_0\n"
-		"%tesse_in_pos_1_weighted = OpVectorTimesScalar %v4f32 %tesse_in_pos_1 %tesse_tc_1\n"
-		"%tesse_in_pos_2_weighted = OpVectorTimesScalar %v4f32 %tesse_in_pos_2 %tesse_tc_2\n"
-		"%tesse_out_pos_ptr = OpAccessChain %op_v4f32 %tesse_stream %c_i32_0\n"
-		"%tesse_in_pos_0_plus_pos_1 = OpFAdd %v4f32 %tesse_in_pos_0_weighted %tesse_in_pos_1_weighted\n"
-		"%tesse_computed_out = OpFAdd %v4f32 %tesse_in_pos_0_plus_pos_1 %tesse_in_pos_2_weighted\n"
-		"OpStore %tesse_out_pos_ptr %tesse_computed_out\n"
-		"%tesse_in_clr_0_ptr = OpAccessChain %ip_v4f32 %tesse_in_color %c_i32_0\n"
-		"%tesse_in_clr_1_ptr = OpAccessChain %ip_v4f32 %tesse_in_color %c_i32_1\n"
-		"%tesse_in_clr_2_ptr = OpAccessChain %ip_v4f32 %tesse_in_color %c_i32_2\n"
-		"%tesse_in_clr_0 = OpLoad %v4f32 %tesse_in_clr_0_ptr\n"
-		"%tesse_in_clr_1 = OpLoad %v4f32 %tesse_in_clr_1_ptr\n"
-		"%tesse_in_clr_2 = OpLoad %v4f32 %tesse_in_clr_2_ptr\n"
-		"%tesse_in_clr_0_weighted = OpVectorTimesScalar %v4f32 %tesse_in_clr_0 %tesse_tc_0\n"
-		"%tesse_in_clr_1_weighted = OpVectorTimesScalar %v4f32 %tesse_in_clr_1 %tesse_tc_1\n"
-		"%tesse_in_clr_2_weighted = OpVectorTimesScalar %v4f32 %tesse_in_clr_2 %tesse_tc_2\n"
-		"%tesse_in_clr_0_plus_col_1 = OpFAdd %v4f32 %tesse_in_clr_0_weighted %tesse_in_clr_1_weighted\n"
-		"%tesse_computed_clr = OpFAdd %v4f32 %tesse_in_clr_0_plus_col_1 %tesse_in_clr_2_weighted\n"
-		"OpStore %tesse_out_color %tesse_computed_clr\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-
-		"; Fragment Entry\n"
-		"%frag_main = OpFunction %void None %fun\n"
-		"%frag_label_main = OpLabel\n"
-		"%frag_tmp1 = OpLoad %v4f32 %frag_vtxColor\n"
-		"OpStore %frag_fragColor %frag_tmp1\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n";
-}
-
-// This has two shaders of each stage. The first
-// is a passthrough, the second inverts the color.
-void createMultipleEntries(vk::SourceCollections& dst, InstanceContext)
-{
-	dst.spirvAsmSources.add("vert") <<
-	// This module contains 2 vertex shaders. One that is a passthrough
-	// and a second that inverts the color of the output (1.0 - color).
-		"OpCapability Shader\n"
-		"OpMemoryModel Logical GLSL450\n"
-		"OpEntryPoint Vertex %main \"vert1\" %Position %vtxColor %color %vtxPosition %vertex_id %instance_id\n"
-		"OpEntryPoint Vertex %main2 \"vert2\" %Position %vtxColor %color %vtxPosition %vertex_id %instance_id\n"
-
-		"OpName %main \"vert1\"\n"
-		"OpName %main2 \"vert2\"\n"
-		"OpName %vtxPosition \"vtxPosition\"\n"
-		"OpName %Position \"position\"\n"
-		"OpName %vtxColor \"vtxColor\"\n"
-		"OpName %color \"color\"\n"
-		"OpName %vertex_id \"gl_VertexIndex\"\n"
-		"OpName %instance_id \"gl_InstanceIndex\"\n"
-
-		"OpDecorate %vtxPosition Location 2\n"
-		"OpDecorate %Position Location 0\n"
-		"OpDecorate %vtxColor Location 1\n"
-		"OpDecorate %color Location 1\n"
-		"OpDecorate %vertex_id BuiltIn VertexIndex\n"
-		"OpDecorate %instance_id BuiltIn InstanceIndex\n"
-		SPIRV_ASSEMBLY_TYPES
-		SPIRV_ASSEMBLY_CONSTANTS
-		SPIRV_ASSEMBLY_ARRAYS
-		"%cval = OpConstantComposite %v4f32 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_0\n"
-		"%vtxPosition = OpVariable %op_v4f32 Output\n"
-		"%Position = OpVariable %ip_v4f32 Input\n"
-		"%vtxColor = OpVariable %op_v4f32 Output\n"
-		"%color = OpVariable %ip_v4f32 Input\n"
-		"%vertex_id = OpVariable %ip_i32 Input\n"
-		"%instance_id = OpVariable %ip_i32 Input\n"
-
-		"%main = OpFunction %void None %fun\n"
-		"%label = OpLabel\n"
-		"%tmp_position = OpLoad %v4f32 %Position\n"
-		"OpStore %vtxPosition %tmp_position\n"
-		"%tmp_color = OpLoad %v4f32 %color\n"
-		"OpStore %vtxColor %tmp_color\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-
-		"%main2 = OpFunction %void None %fun\n"
-		"%label2 = OpLabel\n"
-		"%tmp_position2 = OpLoad %v4f32 %Position\n"
-		"OpStore %vtxPosition %tmp_position2\n"
-		"%tmp_color2 = OpLoad %v4f32 %color\n"
-		"%tmp_color3 = OpFSub %v4f32 %cval %tmp_color2\n"
-		"%tmp_color4 = OpVectorInsertDynamic %v4f32 %tmp_color3 %c_f32_1 %c_i32_3\n"
-		"OpStore %vtxColor %tmp_color4\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n";
-
-	dst.spirvAsmSources.add("frag") <<
-		// This is a single module that contains 2 fragment shaders.
-		// One that passes color through and the other that inverts the output
-		// color (1.0 - color).
-		"OpCapability Shader\n"
-		"OpMemoryModel Logical GLSL450\n"
-		"OpEntryPoint Fragment %main \"frag1\" %vtxColor %fragColor\n"
-		"OpEntryPoint Fragment %main2 \"frag2\" %vtxColor %fragColor\n"
-		"OpExecutionMode %main OriginUpperLeft\n"
-		"OpExecutionMode %main2 OriginUpperLeft\n"
-
-		"OpName %main \"frag1\"\n"
-		"OpName %main2 \"frag2\"\n"
-		"OpName %fragColor \"fragColor\"\n"
-		"OpName %vtxColor \"vtxColor\"\n"
-		"OpDecorate %fragColor Location 0\n"
-		"OpDecorate %vtxColor Location 1\n"
-		SPIRV_ASSEMBLY_TYPES
-		SPIRV_ASSEMBLY_CONSTANTS
-		SPIRV_ASSEMBLY_ARRAYS
-		"%cval = OpConstantComposite %v4f32 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_0\n"
-		"%fragColor = OpVariable %op_v4f32 Output\n"
-		"%vtxColor = OpVariable %ip_v4f32 Input\n"
-
-		"%main = OpFunction %void None %fun\n"
-		"%label_main = OpLabel\n"
-		"%tmp1 = OpLoad %v4f32 %vtxColor\n"
-		"OpStore %fragColor %tmp1\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-
-		"%main2 = OpFunction %void None %fun\n"
-		"%label_main2 = OpLabel\n"
-		"%tmp2 = OpLoad %v4f32 %vtxColor\n"
-		"%tmp3 = OpFSub %v4f32 %cval %tmp2\n"
-		"%tmp4 = OpVectorInsertDynamic %v4f32 %tmp3 %c_f32_1 %c_i32_3\n"
-		"OpStore %fragColor %tmp4\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n";
-
-	dst.spirvAsmSources.add("geom") <<
-		"OpCapability Geometry\n"
-		"OpCapability ClipDistance\n"
-		"OpCapability CullDistance\n"
-		"OpMemoryModel Logical GLSL450\n"
-		"OpEntryPoint Geometry %geom1_main \"geom1\" %out_gl_position %gl_in %out_color %in_color\n"
-		"OpEntryPoint Geometry %geom2_main \"geom2\" %out_gl_position %gl_in %out_color %in_color\n"
-		"OpExecutionMode %geom1_main Triangles\n"
-		"OpExecutionMode %geom2_main Triangles\n"
-		"OpExecutionMode %geom1_main OutputTriangleStrip\n"
-		"OpExecutionMode %geom2_main OutputTriangleStrip\n"
-		"OpExecutionMode %geom1_main OutputVertices 3\n"
-		"OpExecutionMode %geom2_main OutputVertices 3\n"
-		"OpName %geom1_main \"geom1\"\n"
-		"OpName %geom2_main \"geom2\"\n"
-		"OpName %per_vertex_in \"gl_PerVertex\"\n"
-		"OpMemberName %per_vertex_in 0 \"gl_Position\"\n"
-		"OpMemberName %per_vertex_in 1 \"gl_PointSize\"\n"
-		"OpMemberName %per_vertex_in 2 \"gl_ClipDistance\"\n"
-		"OpMemberName %per_vertex_in 3 \"gl_CullDistance\"\n"
-		"OpName %gl_in \"gl_in\"\n"
-		"OpName %out_color \"out_color\"\n"
-		"OpName %in_color \"in_color\"\n"
-		"OpDecorate %out_gl_position BuiltIn Position\n"
-		"OpMemberDecorate %per_vertex_in 0 BuiltIn Position\n"
-		"OpMemberDecorate %per_vertex_in 1 BuiltIn PointSize\n"
-		"OpMemberDecorate %per_vertex_in 2 BuiltIn ClipDistance\n"
-		"OpMemberDecorate %per_vertex_in 3 BuiltIn CullDistance\n"
-		"OpDecorate %per_vertex_in Block\n"
-		"OpDecorate %out_color Location 1\n"
-		"OpDecorate %in_color Location 1\n"
-		SPIRV_ASSEMBLY_TYPES
-		SPIRV_ASSEMBLY_CONSTANTS
-		SPIRV_ASSEMBLY_ARRAYS
-		"%cval = OpConstantComposite %v4f32 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_0\n"
-		"%per_vertex_in = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
-		"%a3_per_vertex_in = OpTypeArray %per_vertex_in %c_u32_3\n"
-		"%ip_a3_per_vertex_in = OpTypePointer Input %a3_per_vertex_in\n"
-		"%gl_in = OpVariable %ip_a3_per_vertex_in Input\n"
-		"%out_color = OpVariable %op_v4f32 Output\n"
-		"%in_color = OpVariable %ip_a3v4f32 Input\n"
-		"%out_gl_position = OpVariable %op_v4f32 Output\n"
-
-		"%geom1_main = OpFunction %void None %fun\n"
-		"%geom1_label = OpLabel\n"
-		"%geom1_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_0 %c_i32_0\n"
-		"%geom1_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_1 %c_i32_0\n"
-		"%geom1_gl_in_2_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_2 %c_i32_0\n"
-		"%geom1_in_position_0 = OpLoad %v4f32 %geom1_gl_in_0_gl_position\n"
-		"%geom1_in_position_1 = OpLoad %v4f32 %geom1_gl_in_1_gl_position\n"
-		"%geom1_in_position_2 = OpLoad %v4f32 %geom1_gl_in_2_gl_position \n"
-		"%geom1_in_color_0_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_0\n"
-		"%geom1_in_color_1_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_1\n"
-		"%geom1_in_color_2_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_2\n"
-		"%geom1_in_color_0 = OpLoad %v4f32 %geom1_in_color_0_ptr\n"
-		"%geom1_in_color_1 = OpLoad %v4f32 %geom1_in_color_1_ptr\n"
-		"%geom1_in_color_2 = OpLoad %v4f32 %geom1_in_color_2_ptr\n"
-		"OpStore %out_gl_position %geom1_in_position_0\n"
-		"OpStore %out_color %geom1_in_color_0\n"
-		"OpEmitVertex\n"
-		"OpStore %out_gl_position %geom1_in_position_1\n"
-		"OpStore %out_color %geom1_in_color_1\n"
-		"OpEmitVertex\n"
-		"OpStore %out_gl_position %geom1_in_position_2\n"
-		"OpStore %out_color %geom1_in_color_2\n"
-		"OpEmitVertex\n"
-		"OpEndPrimitive\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-
-		"%geom2_main = OpFunction %void None %fun\n"
-		"%geom2_label = OpLabel\n"
-		"%geom2_gl_in_0_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_0 %c_i32_0\n"
-		"%geom2_gl_in_1_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_1 %c_i32_0\n"
-		"%geom2_gl_in_2_gl_position = OpAccessChain %ip_v4f32 %gl_in %c_i32_2 %c_i32_0\n"
-		"%geom2_in_position_0 = OpLoad %v4f32 %geom2_gl_in_0_gl_position\n"
-		"%geom2_in_position_1 = OpLoad %v4f32 %geom2_gl_in_1_gl_position\n"
-		"%geom2_in_position_2 = OpLoad %v4f32 %geom2_gl_in_2_gl_position \n"
-		"%geom2_in_color_0_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_0\n"
-		"%geom2_in_color_1_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_1\n"
-		"%geom2_in_color_2_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_2\n"
-		"%geom2_in_color_0 = OpLoad %v4f32 %geom2_in_color_0_ptr\n"
-		"%geom2_in_color_1 = OpLoad %v4f32 %geom2_in_color_1_ptr\n"
-		"%geom2_in_color_2 = OpLoad %v4f32 %geom2_in_color_2_ptr\n"
-		"%geom2_transformed_in_color_0 = OpFSub %v4f32 %cval %geom2_in_color_0\n"
-		"%geom2_transformed_in_color_1 = OpFSub %v4f32 %cval %geom2_in_color_1\n"
-		"%geom2_transformed_in_color_2 = OpFSub %v4f32 %cval %geom2_in_color_2\n"
-		"%geom2_transformed_in_color_0_a = OpVectorInsertDynamic %v4f32 %geom2_transformed_in_color_0 %c_f32_1 %c_i32_3\n"
-		"%geom2_transformed_in_color_1_a = OpVectorInsertDynamic %v4f32 %geom2_transformed_in_color_1 %c_f32_1 %c_i32_3\n"
-		"%geom2_transformed_in_color_2_a = OpVectorInsertDynamic %v4f32 %geom2_transformed_in_color_2 %c_f32_1 %c_i32_3\n"
-		"OpStore %out_gl_position %geom2_in_position_0\n"
-		"OpStore %out_color %geom2_transformed_in_color_0_a\n"
-		"OpEmitVertex\n"
-		"OpStore %out_gl_position %geom2_in_position_1\n"
-		"OpStore %out_color %geom2_transformed_in_color_1_a\n"
-		"OpEmitVertex\n"
-		"OpStore %out_gl_position %geom2_in_position_2\n"
-		"OpStore %out_color %geom2_transformed_in_color_2_a\n"
-		"OpEmitVertex\n"
-		"OpEndPrimitive\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n";
-
-	dst.spirvAsmSources.add("tessc") <<
-		"OpCapability Tessellation\n"
-		"OpMemoryModel Logical GLSL450\n"
-		"OpEntryPoint TessellationControl %tessc1_main \"tessc1\" %out_color %gl_InvocationID %in_color %out_position %in_position %gl_TessLevelOuter %gl_TessLevelInner\n"
-		"OpEntryPoint TessellationControl %tessc2_main \"tessc2\" %out_color %gl_InvocationID %in_color %out_position %in_position %gl_TessLevelOuter %gl_TessLevelInner\n"
-		"OpExecutionMode %tessc1_main OutputVertices 3\n"
-		"OpExecutionMode %tessc2_main OutputVertices 3\n"
-		"OpName %tessc1_main \"tessc1\"\n"
-		"OpName %tessc2_main \"tessc2\"\n"
-		"OpName %out_color \"out_color\"\n"
-		"OpName %gl_InvocationID \"gl_InvocationID\"\n"
-		"OpName %in_color \"in_color\"\n"
-		"OpName %out_position \"out_position\"\n"
-		"OpName %in_position \"in_position\"\n"
-		"OpName %gl_TessLevelOuter \"gl_TessLevelOuter\"\n"
-		"OpName %gl_TessLevelInner \"gl_TessLevelInner\"\n"
-		"OpDecorate %out_color Location 1\n"
-		"OpDecorate %gl_InvocationID BuiltIn InvocationId\n"
-		"OpDecorate %in_color Location 1\n"
-		"OpDecorate %out_position Location 2\n"
-		"OpDecorate %in_position Location 2\n"
-		"OpDecorate %gl_TessLevelOuter Patch\n"
-		"OpDecorate %gl_TessLevelOuter BuiltIn TessLevelOuter\n"
-		"OpDecorate %gl_TessLevelInner Patch\n"
-		"OpDecorate %gl_TessLevelInner BuiltIn TessLevelInner\n"
-		SPIRV_ASSEMBLY_TYPES
-		SPIRV_ASSEMBLY_CONSTANTS
-		SPIRV_ASSEMBLY_ARRAYS
-		"%cval = OpConstantComposite %v4f32 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_0\n"
-		"%out_color = OpVariable %op_a3v4f32 Output\n"
-		"%gl_InvocationID = OpVariable %ip_i32 Input\n"
-		"%in_color = OpVariable %ip_a32v4f32 Input\n"
-		"%out_position = OpVariable %op_a3v4f32 Output\n"
-		"%in_position = OpVariable %ip_a32v4f32 Input\n"
-		"%gl_TessLevelOuter = OpVariable %op_a4f32 Output\n"
-		"%gl_TessLevelInner = OpVariable %op_a2f32 Output\n"
-
-		"%tessc1_main = OpFunction %void None %fun\n"
-		"%tessc1_label = OpLabel\n"
-		"%tessc1_invocation_id = OpLoad %i32 %gl_InvocationID\n"
-		"%tessc1_in_color_ptr = OpAccessChain %ip_v4f32 %in_color %tessc1_invocation_id\n"
-		"%tessc1_in_position_ptr = OpAccessChain %ip_v4f32 %in_position %tessc1_invocation_id\n"
-		"%tessc1_in_color_val = OpLoad %v4f32 %tessc1_in_color_ptr\n"
-		"%tessc1_in_position_val = OpLoad %v4f32 %tessc1_in_position_ptr\n"
-		"%tessc1_out_color_ptr = OpAccessChain %op_v4f32 %out_color %tessc1_invocation_id\n"
-		"%tessc1_out_position_ptr = OpAccessChain %op_v4f32 %out_position %tessc1_invocation_id\n"
-		"OpStore %tessc1_out_color_ptr %tessc1_in_color_val\n"
-		"OpStore %tessc1_out_position_ptr %tessc1_in_position_val\n"
-		"%tessc1_is_first_invocation = OpIEqual %bool %tessc1_invocation_id %c_i32_0\n"
-		"OpSelectionMerge %tessc1_merge_label None\n"
-		"OpBranchConditional %tessc1_is_first_invocation %tessc1_first_invocation %tessc1_merge_label\n"
-		"%tessc1_first_invocation = OpLabel\n"
-		"%tessc1_tess_outer_0 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_0\n"
-		"%tessc1_tess_outer_1 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_1\n"
-		"%tessc1_tess_outer_2 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_2\n"
-		"%tessc1_tess_inner = OpAccessChain %op_f32 %gl_TessLevelInner %c_i32_0\n"
-		"OpStore %tessc1_tess_outer_0 %c_f32_1\n"
-		"OpStore %tessc1_tess_outer_1 %c_f32_1\n"
-		"OpStore %tessc1_tess_outer_2 %c_f32_1\n"
-		"OpStore %tessc1_tess_inner %c_f32_1\n"
-		"OpBranch %tessc1_merge_label\n"
-		"%tessc1_merge_label = OpLabel\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-
-		"%tessc2_main = OpFunction %void None %fun\n"
-		"%tessc2_label = OpLabel\n"
-		"%tessc2_invocation_id = OpLoad %i32 %gl_InvocationID\n"
-		"%tessc2_in_color_ptr = OpAccessChain %ip_v4f32 %in_color %tessc2_invocation_id\n"
-		"%tessc2_in_position_ptr = OpAccessChain %ip_v4f32 %in_position %tessc2_invocation_id\n"
-		"%tessc2_in_color_val = OpLoad %v4f32 %tessc2_in_color_ptr\n"
-		"%tessc2_in_position_val = OpLoad %v4f32 %tessc2_in_position_ptr\n"
-		"%tessc2_out_color_ptr = OpAccessChain %op_v4f32 %out_color %tessc2_invocation_id\n"
-		"%tessc2_out_position_ptr = OpAccessChain %op_v4f32 %out_position %tessc2_invocation_id\n"
-		"%tessc2_transformed_color = OpFSub %v4f32 %cval %tessc2_in_color_val\n"
-		"%tessc2_transformed_color_a = OpVectorInsertDynamic %v4f32 %tessc2_transformed_color %c_f32_1 %c_i32_3\n"
-		"OpStore %tessc2_out_color_ptr %tessc2_transformed_color_a\n"
-		"OpStore %tessc2_out_position_ptr %tessc2_in_position_val\n"
-		"%tessc2_is_first_invocation = OpIEqual %bool %tessc2_invocation_id %c_i32_0\n"
-		"OpSelectionMerge %tessc2_merge_label None\n"
-		"OpBranchConditional %tessc2_is_first_invocation %tessc2_first_invocation %tessc2_merge_label\n"
-		"%tessc2_first_invocation = OpLabel\n"
-		"%tessc2_tess_outer_0 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_0\n"
-		"%tessc2_tess_outer_1 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_1\n"
-		"%tessc2_tess_outer_2 = OpAccessChain %op_f32 %gl_TessLevelOuter %c_i32_2\n"
-		"%tessc2_tess_inner = OpAccessChain %op_f32 %gl_TessLevelInner %c_i32_0\n"
-		"OpStore %tessc2_tess_outer_0 %c_f32_1\n"
-		"OpStore %tessc2_tess_outer_1 %c_f32_1\n"
-		"OpStore %tessc2_tess_outer_2 %c_f32_1\n"
-		"OpStore %tessc2_tess_inner %c_f32_1\n"
-		"OpBranch %tessc2_merge_label\n"
-		"%tessc2_merge_label = OpLabel\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n";
-
-	dst.spirvAsmSources.add("tesse") <<
-		"OpCapability Tessellation\n"
-		"OpCapability ClipDistance\n"
-		"OpCapability CullDistance\n"
-		"OpMemoryModel Logical GLSL450\n"
-		"OpEntryPoint TessellationEvaluation %tesse1_main \"tesse1\" %stream %gl_tessCoord %in_position %out_color %in_color \n"
-		"OpEntryPoint TessellationEvaluation %tesse2_main \"tesse2\" %stream %gl_tessCoord %in_position %out_color %in_color \n"
-		"OpExecutionMode %tesse1_main Triangles\n"
-		"OpExecutionMode %tesse1_main SpacingEqual\n"
-		"OpExecutionMode %tesse1_main VertexOrderCcw\n"
-		"OpExecutionMode %tesse2_main Triangles\n"
-		"OpExecutionMode %tesse2_main SpacingEqual\n"
-		"OpExecutionMode %tesse2_main VertexOrderCcw\n"
-		"OpName %tesse1_main \"tesse1\"\n"
-		"OpName %tesse2_main \"tesse2\"\n"
-		"OpName %per_vertex_out \"gl_PerVertex\"\n"
-		"OpMemberName %per_vertex_out 0 \"gl_Position\"\n"
-		"OpMemberName %per_vertex_out 1 \"gl_PointSize\"\n"
-		"OpMemberName %per_vertex_out 2 \"gl_ClipDistance\"\n"
-		"OpMemberName %per_vertex_out 3 \"gl_CullDistance\"\n"
-		"OpName %stream \"\"\n"
-		"OpName %gl_tessCoord \"gl_TessCoord\"\n"
-		"OpName %in_position \"in_position\"\n"
-		"OpName %out_color \"out_color\"\n"
-		"OpName %in_color \"in_color\"\n"
-		"OpMemberDecorate %per_vertex_out 0 BuiltIn Position\n"
-		"OpMemberDecorate %per_vertex_out 1 BuiltIn PointSize\n"
-		"OpMemberDecorate %per_vertex_out 2 BuiltIn ClipDistance\n"
-		"OpMemberDecorate %per_vertex_out 3 BuiltIn CullDistance\n"
-		"OpDecorate %per_vertex_out Block\n"
-		"OpDecorate %gl_tessCoord BuiltIn TessCoord\n"
-		"OpDecorate %in_position Location 2\n"
-		"OpDecorate %out_color Location 1\n"
-		"OpDecorate %in_color Location 1\n"
-		SPIRV_ASSEMBLY_TYPES
-		SPIRV_ASSEMBLY_CONSTANTS
-		SPIRV_ASSEMBLY_ARRAYS
-		"%cval = OpConstantComposite %v4f32 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_0\n"
-		"%per_vertex_out = OpTypeStruct %v4f32 %f32 %a1f32 %a1f32\n"
-		"%op_per_vertex_out = OpTypePointer Output %per_vertex_out\n"
-		"%stream = OpVariable %op_per_vertex_out Output\n"
-		"%gl_tessCoord = OpVariable %ip_v3f32 Input\n"
-		"%in_position = OpVariable %ip_a32v4f32 Input\n"
-		"%out_color = OpVariable %op_v4f32 Output\n"
-		"%in_color = OpVariable %ip_a32v4f32 Input\n"
-
-		"%tesse1_main = OpFunction %void None %fun\n"
-		"%tesse1_label = OpLabel\n"
-		"%tesse1_tc_0_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_0\n"
-		"%tesse1_tc_1_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_1\n"
-		"%tesse1_tc_2_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_2\n"
-		"%tesse1_tc_0 = OpLoad %f32 %tesse1_tc_0_ptr\n"
-		"%tesse1_tc_1 = OpLoad %f32 %tesse1_tc_1_ptr\n"
-		"%tesse1_tc_2 = OpLoad %f32 %tesse1_tc_2_ptr\n"
-		"%tesse1_in_pos_0_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_0\n"
-		"%tesse1_in_pos_1_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_1\n"
-		"%tesse1_in_pos_2_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_2\n"
-		"%tesse1_in_pos_0 = OpLoad %v4f32 %tesse1_in_pos_0_ptr\n"
-		"%tesse1_in_pos_1 = OpLoad %v4f32 %tesse1_in_pos_1_ptr\n"
-		"%tesse1_in_pos_2 = OpLoad %v4f32 %tesse1_in_pos_2_ptr\n"
-		"%tesse1_in_pos_0_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_pos_0 %tesse1_tc_0\n"
-		"%tesse1_in_pos_1_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_pos_1 %tesse1_tc_1\n"
-		"%tesse1_in_pos_2_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_pos_2 %tesse1_tc_2\n"
-		"%tesse1_out_pos_ptr = OpAccessChain %op_v4f32 %stream %c_i32_0\n"
-		"%tesse1_in_pos_0_plus_pos_1 = OpFAdd %v4f32 %tesse1_in_pos_0_weighted %tesse1_in_pos_1_weighted\n"
-		"%tesse1_computed_out = OpFAdd %v4f32 %tesse1_in_pos_0_plus_pos_1 %tesse1_in_pos_2_weighted\n"
-		"OpStore %tesse1_out_pos_ptr %tesse1_computed_out\n"
-		"%tesse1_in_clr_0_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_0\n"
-		"%tesse1_in_clr_1_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_1\n"
-		"%tesse1_in_clr_2_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_2\n"
-		"%tesse1_in_clr_0 = OpLoad %v4f32 %tesse1_in_clr_0_ptr\n"
-		"%tesse1_in_clr_1 = OpLoad %v4f32 %tesse1_in_clr_1_ptr\n"
-		"%tesse1_in_clr_2 = OpLoad %v4f32 %tesse1_in_clr_2_ptr\n"
-		"%tesse1_in_clr_0_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_clr_0 %tesse1_tc_0\n"
-		"%tesse1_in_clr_1_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_clr_1 %tesse1_tc_1\n"
-		"%tesse1_in_clr_2_weighted = OpVectorTimesScalar %v4f32 %tesse1_in_clr_2 %tesse1_tc_2\n"
-		"%tesse1_in_clr_0_plus_col_1 = OpFAdd %v4f32 %tesse1_in_clr_0_weighted %tesse1_in_clr_1_weighted\n"
-		"%tesse1_computed_clr = OpFAdd %v4f32 %tesse1_in_clr_0_plus_col_1 %tesse1_in_clr_2_weighted\n"
-		"OpStore %out_color %tesse1_computed_clr\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n"
-
-		"%tesse2_main = OpFunction %void None %fun\n"
-		"%tesse2_label = OpLabel\n"
-		"%tesse2_tc_0_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_0\n"
-		"%tesse2_tc_1_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_1\n"
-		"%tesse2_tc_2_ptr = OpAccessChain %ip_f32 %gl_tessCoord %c_u32_2\n"
-		"%tesse2_tc_0 = OpLoad %f32 %tesse2_tc_0_ptr\n"
-		"%tesse2_tc_1 = OpLoad %f32 %tesse2_tc_1_ptr\n"
-		"%tesse2_tc_2 = OpLoad %f32 %tesse2_tc_2_ptr\n"
-		"%tesse2_in_pos_0_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_0\n"
-		"%tesse2_in_pos_1_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_1\n"
-		"%tesse2_in_pos_2_ptr = OpAccessChain %ip_v4f32 %in_position %c_i32_2\n"
-		"%tesse2_in_pos_0 = OpLoad %v4f32 %tesse2_in_pos_0_ptr\n"
-		"%tesse2_in_pos_1 = OpLoad %v4f32 %tesse2_in_pos_1_ptr\n"
-		"%tesse2_in_pos_2 = OpLoad %v4f32 %tesse2_in_pos_2_ptr\n"
-		"%tesse2_in_pos_0_weighted = OpVectorTimesScalar %v4f32 %tesse2_in_pos_0 %tesse2_tc_0\n"
-		"%tesse2_in_pos_1_weighted = OpVectorTimesScalar %v4f32 %tesse2_in_pos_1 %tesse2_tc_1\n"
-		"%tesse2_in_pos_2_weighted = OpVectorTimesScalar %v4f32 %tesse2_in_pos_2 %tesse2_tc_2\n"
-		"%tesse2_out_pos_ptr = OpAccessChain %op_v4f32 %stream %c_i32_0\n"
-		"%tesse2_in_pos_0_plus_pos_1 = OpFAdd %v4f32 %tesse2_in_pos_0_weighted %tesse2_in_pos_1_weighted\n"
-		"%tesse2_computed_out = OpFAdd %v4f32 %tesse2_in_pos_0_plus_pos_1 %tesse2_in_pos_2_weighted\n"
-		"OpStore %tesse2_out_pos_ptr %tesse2_computed_out\n"
-		"%tesse2_in_clr_0_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_0\n"
-		"%tesse2_in_clr_1_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_1\n"
-		"%tesse2_in_clr_2_ptr = OpAccessChain %ip_v4f32 %in_color %c_i32_2\n"
-		"%tesse2_in_clr_0 = OpLoad %v4f32 %tesse2_in_clr_0_ptr\n"
-		"%tesse2_in_clr_1 = OpLoad %v4f32 %tesse2_in_clr_1_ptr\n"
-		"%tesse2_in_clr_2 = OpLoad %v4f32 %tesse2_in_clr_2_ptr\n"
-		"%tesse2_in_clr_0_weighted = OpVectorTimesScalar %v4f32 %tesse2_in_clr_0 %tesse2_tc_0\n"
-		"%tesse2_in_clr_1_weighted = OpVectorTimesScalar %v4f32 %tesse2_in_clr_1 %tesse2_tc_1\n"
-		"%tesse2_in_clr_2_weighted = OpVectorTimesScalar %v4f32 %tesse2_in_clr_2 %tesse2_tc_2\n"
-		"%tesse2_in_clr_0_plus_col_1 = OpFAdd %v4f32 %tesse2_in_clr_0_weighted %tesse2_in_clr_1_weighted\n"
-		"%tesse2_computed_clr = OpFAdd %v4f32 %tesse2_in_clr_0_plus_col_1 %tesse2_in_clr_2_weighted\n"
-		"%tesse2_clr_transformed = OpFSub %v4f32 %cval %tesse2_computed_clr\n"
-		"%tesse2_clr_transformed_a = OpVectorInsertDynamic %v4f32 %tesse2_clr_transformed %c_f32_1 %c_i32_3\n"
-		"OpStore %out_color %tesse2_clr_transformed_a\n"
-		"OpReturn\n"
-		"OpFunctionEnd\n";
-}
-
-// Sets up and runs a Vulkan pipeline, then spot-checks the resulting image.
-// Feeds the pipeline a set of colored triangles, which then must occur in the
-// rendered image.  The surface is cleared before executing the pipeline, so
-// whatever the shaders draw can be directly spot-checked.
-TestStatus runAndVerifyDefaultPipeline (Context& context, InstanceContext instance)
-{
-	const VkDevice								vkDevice				= context.getDevice();
-	const DeviceInterface&						vk						= context.getDeviceInterface();
-	const VkQueue								queue					= context.getUniversalQueue();
-	const deUint32								queueFamilyIndex		= context.getUniversalQueueFamilyIndex();
-	const tcu::UVec2							renderSize				(256, 256);
-	vector<ModuleHandleSp>						modules;
-	map<VkShaderStageFlagBits, VkShaderModule>	moduleByStage;
-	const int									testSpecificSeed		= 31354125;
-	const int									seed					= context.getTestContext().getCommandLine().getBaseSeed() ^ testSpecificSeed;
-	bool										supportsGeometry		= false;
-	bool										supportsTessellation	= false;
-	bool										hasTessellation         = false;
-
-	const VkPhysicalDeviceFeatures&				features				= context.getDeviceFeatures();
-	supportsGeometry		= features.geometryShader == VK_TRUE;
-	supportsTessellation	= features.tessellationShader == VK_TRUE;
-	hasTessellation			= (instance.requiredStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) ||
-								(instance.requiredStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT);
-
-	if (hasTessellation && !supportsTessellation)
-	{
-		throw tcu::NotSupportedError(std::string("Tessellation not supported"));
-	}
-
-	if ((instance.requiredStages & VK_SHADER_STAGE_GEOMETRY_BIT) &&
-		!supportsGeometry)
-	{
-		throw tcu::NotSupportedError(std::string("Geometry not supported"));
-	}
-
-	de::Random(seed).shuffle(instance.inputColors, instance.inputColors+4);
-	de::Random(seed).shuffle(instance.outputColors, instance.outputColors+4);
-	const Vec4								vertexData[]			=
-	{
-		// Upper left corner:
-		Vec4(-1.0f, -1.0f, 0.0f, 1.0f), instance.inputColors[0].toVec(),
-		Vec4(-0.5f, -1.0f, 0.0f, 1.0f), instance.inputColors[0].toVec(),
-		Vec4(-1.0f, -0.5f, 0.0f, 1.0f), instance.inputColors[0].toVec(),
-
-		// Upper right corner:
-		Vec4(+0.5f, -1.0f, 0.0f, 1.0f), instance.inputColors[1].toVec(),
-		Vec4(+1.0f, -1.0f, 0.0f, 1.0f), instance.inputColors[1].toVec(),
-		Vec4(+1.0f, -0.5f, 0.0f, 1.0f), instance.inputColors[1].toVec(),
-
-		// Lower left corner:
-		Vec4(-1.0f, +0.5f, 0.0f, 1.0f), instance.inputColors[2].toVec(),
-		Vec4(-0.5f, +1.0f, 0.0f, 1.0f), instance.inputColors[2].toVec(),
-		Vec4(-1.0f, +1.0f, 0.0f, 1.0f), instance.inputColors[2].toVec(),
-
-		// Lower right corner:
-		Vec4(+1.0f, +0.5f, 0.0f, 1.0f), instance.inputColors[3].toVec(),
-		Vec4(+1.0f, +1.0f, 0.0f, 1.0f), instance.inputColors[3].toVec(),
-		Vec4(+0.5f, +1.0f, 0.0f, 1.0f), instance.inputColors[3].toVec()
-	};
-	const size_t							singleVertexDataSize	= 2 * sizeof(Vec4);
-	const size_t							vertexCount				= sizeof(vertexData) / singleVertexDataSize;
-
-	const VkBufferCreateInfo				vertexBufferParams		=
-	{
-		VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,	//	VkStructureType		sType;
-		DE_NULL,								//	const void*			pNext;
-		0u,										//	VkBufferCreateFlags	flags;
-		(VkDeviceSize)sizeof(vertexData),		//	VkDeviceSize		size;
-		VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,		//	VkBufferUsageFlags	usage;
-		VK_SHARING_MODE_EXCLUSIVE,				//	VkSharingMode		sharingMode;
-		1u,										//	deUint32			queueFamilyCount;
-		&queueFamilyIndex,						//	const deUint32*		pQueueFamilyIndices;
-	};
-	const Unique<VkBuffer>					vertexBuffer			(createBuffer(vk, vkDevice, &vertexBufferParams));
-	const UniquePtr<Allocation>				vertexBufferMemory		(context.getDefaultAllocator().allocate(getBufferMemoryRequirements(vk, vkDevice, *vertexBuffer), MemoryRequirement::HostVisible));
-
-	VK_CHECK(vk.bindBufferMemory(vkDevice, *vertexBuffer, vertexBufferMemory->getMemory(), vertexBufferMemory->getOffset()));
-
-	const VkDeviceSize						imageSizeBytes			= (VkDeviceSize)(sizeof(deUint32)*renderSize.x()*renderSize.y());
-	const VkBufferCreateInfo				readImageBufferParams	=
-	{
-		VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,		//	VkStructureType		sType;
-		DE_NULL,									//	const void*			pNext;
-		0u,											//	VkBufferCreateFlags	flags;
-		imageSizeBytes,								//	VkDeviceSize		size;
-		VK_BUFFER_USAGE_TRANSFER_DST_BIT,			//	VkBufferUsageFlags	usage;
-		VK_SHARING_MODE_EXCLUSIVE,					//	VkSharingMode		sharingMode;
-		1u,											//	deUint32			queueFamilyCount;
-		&queueFamilyIndex,							//	const deUint32*		pQueueFamilyIndices;
-	};
-	const Unique<VkBuffer>					readImageBuffer			(createBuffer(vk, vkDevice, &readImageBufferParams));
-	const UniquePtr<Allocation>				readImageBufferMemory	(context.getDefaultAllocator().allocate(getBufferMemoryRequirements(vk, vkDevice, *readImageBuffer), MemoryRequirement::HostVisible));
-
-	VK_CHECK(vk.bindBufferMemory(vkDevice, *readImageBuffer, readImageBufferMemory->getMemory(), readImageBufferMemory->getOffset()));
-
-	const VkImageCreateInfo					imageParams				=
-	{
-		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,									//	VkStructureType		sType;
-		DE_NULL,																//	const void*			pNext;
-		0u,																		//	VkImageCreateFlags	flags;
-		VK_IMAGE_TYPE_2D,														//	VkImageType			imageType;
-		VK_FORMAT_R8G8B8A8_UNORM,												//	VkFormat			format;
-		{ renderSize.x(), renderSize.y(), 1 },									//	VkExtent3D			extent;
-		1u,																		//	deUint32			mipLevels;
-		1u,																		//	deUint32			arraySize;
-		VK_SAMPLE_COUNT_1_BIT,													//	deUint32			samples;
-		VK_IMAGE_TILING_OPTIMAL,												//	VkImageTiling		tiling;
-		VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT|VK_IMAGE_USAGE_TRANSFER_SRC_BIT,	//	VkImageUsageFlags	usage;
-		VK_SHARING_MODE_EXCLUSIVE,												//	VkSharingMode		sharingMode;
-		1u,																		//	deUint32			queueFamilyCount;
-		&queueFamilyIndex,														//	const deUint32*		pQueueFamilyIndices;
-		VK_IMAGE_LAYOUT_UNDEFINED,												//	VkImageLayout		initialLayout;
-	};
-
-	const Unique<VkImage>					image					(createImage(vk, vkDevice, &imageParams));
-	const UniquePtr<Allocation>				imageMemory				(context.getDefaultAllocator().allocate(getImageMemoryRequirements(vk, vkDevice, *image), MemoryRequirement::Any));
-
-	VK_CHECK(vk.bindImageMemory(vkDevice, *image, imageMemory->getMemory(), imageMemory->getOffset()));
-
-	const VkAttachmentDescription			colorAttDesc			=
-	{
-		0u,												//	VkAttachmentDescriptionFlags	flags;
-		VK_FORMAT_R8G8B8A8_UNORM,						//	VkFormat						format;
-		VK_SAMPLE_COUNT_1_BIT,							//	deUint32						samples;
-		VK_ATTACHMENT_LOAD_OP_CLEAR,					//	VkAttachmentLoadOp				loadOp;
-		VK_ATTACHMENT_STORE_OP_STORE,					//	VkAttachmentStoreOp				storeOp;
-		VK_ATTACHMENT_LOAD_OP_DONT_CARE,				//	VkAttachmentLoadOp				stencilLoadOp;
-		VK_ATTACHMENT_STORE_OP_DONT_CARE,				//	VkAttachmentStoreOp				stencilStoreOp;
-		VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,		//	VkImageLayout					initialLayout;
-		VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,		//	VkImageLayout					finalLayout;
-	};
-	const VkAttachmentReference				colorAttRef				=
-	{
-		0u,												//	deUint32		attachment;
-		VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,		//	VkImageLayout	layout;
-	};
-	const VkSubpassDescription				subpassDesc				=
-	{
-		0u,												//	VkSubpassDescriptionFlags		flags;
-		VK_PIPELINE_BIND_POINT_GRAPHICS,				//	VkPipelineBindPoint				pipelineBindPoint;
-		0u,												//	deUint32						inputCount;
-		DE_NULL,										//	const VkAttachmentReference*	pInputAttachments;
-		1u,												//	deUint32						colorCount;
-		&colorAttRef,									//	const VkAttachmentReference*	pColorAttachments;
-		DE_NULL,										//	const VkAttachmentReference*	pResolveAttachments;
-		DE_NULL,										//	const VkAttachmentReference*	pDepthStencilAttachment;
-		0u,												//	deUint32						preserveCount;
-		DE_NULL,										//	const VkAttachmentReference*	pPreserveAttachments;
-
-	};
-	const VkRenderPassCreateInfo			renderPassParams		=
-	{
-		VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,		//	VkStructureType					sType;
-		DE_NULL,										//	const void*						pNext;
-		(VkRenderPassCreateFlags)0,
-		1u,												//	deUint32						attachmentCount;
-		&colorAttDesc,									//	const VkAttachmentDescription*	pAttachments;
-		1u,												//	deUint32						subpassCount;
-		&subpassDesc,									//	const VkSubpassDescription*		pSubpasses;
-		0u,												//	deUint32						dependencyCount;
-		DE_NULL,										//	const VkSubpassDependency*		pDependencies;
-	};
-	const Unique<VkRenderPass>				renderPass				(createRenderPass(vk, vkDevice, &renderPassParams));
-
-	const VkImageViewCreateInfo				colorAttViewParams		=
-	{
-		VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,		//	VkStructureType				sType;
-		DE_NULL,										//	const void*					pNext;
-		0u,												//	VkImageViewCreateFlags		flags;
-		*image,											//	VkImage						image;
-		VK_IMAGE_VIEW_TYPE_2D,							//	VkImageViewType				viewType;
-		VK_FORMAT_R8G8B8A8_UNORM,						//	VkFormat					format;
-		{
-			VK_COMPONENT_SWIZZLE_R,
-			VK_COMPONENT_SWIZZLE_G,
-			VK_COMPONENT_SWIZZLE_B,
-			VK_COMPONENT_SWIZZLE_A
-		},												//	VkChannelMapping			channels;
-		{
-			VK_IMAGE_ASPECT_COLOR_BIT,						//	VkImageAspectFlags	aspectMask;
-			0u,												//	deUint32			baseMipLevel;
-			1u,												//	deUint32			mipLevels;
-			0u,												//	deUint32			baseArrayLayer;
-			1u,												//	deUint32			arraySize;
-		},												//	VkImageSubresourceRange		subresourceRange;
-	};
-	const Unique<VkImageView>				colorAttView			(createImageView(vk, vkDevice, &colorAttViewParams));
-
-
-	// Pipeline layout
-	const VkPipelineLayoutCreateInfo		pipelineLayoutParams	=
-	{
-		VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,			//	VkStructureType					sType;
-		DE_NULL,												//	const void*						pNext;
-		(VkPipelineLayoutCreateFlags)0,
-		0u,														//	deUint32						descriptorSetCount;
-		DE_NULL,												//	const VkDescriptorSetLayout*	pSetLayouts;
-		0u,														//	deUint32						pushConstantRangeCount;
-		DE_NULL,												//	const VkPushConstantRange*		pPushConstantRanges;
-	};
-	const Unique<VkPipelineLayout>			pipelineLayout			(createPipelineLayout(vk, vkDevice, &pipelineLayoutParams));
-
-	// Pipeline
-	vector<VkPipelineShaderStageCreateInfo>		shaderStageParams;
-	// We need these vectors to make sure that information about specialization constants for each stage can outlive createGraphicsPipeline().
-	vector<vector<VkSpecializationMapEntry> >	specConstantEntries;
-	vector<VkSpecializationInfo>				specializationInfos;
-	createPipelineShaderStages(vk, vkDevice, instance, context, modules, shaderStageParams);
-
-	// And we don't want the reallocation of these vectors to invalidate pointers pointing to their contents.
-	specConstantEntries.reserve(shaderStageParams.size());
-	specializationInfos.reserve(shaderStageParams.size());
-
-	// Patch the specialization info field in PipelineShaderStageCreateInfos.
-	for (vector<VkPipelineShaderStageCreateInfo>::iterator stageInfo = shaderStageParams.begin(); stageInfo != shaderStageParams.end(); ++stageInfo)
-	{
-		const StageToSpecConstantMap::const_iterator stageIt = instance.specConstants.find(stageInfo->stage);
-
-		if (stageIt != instance.specConstants.end())
-		{
-			const size_t						numSpecConstants	= stageIt->second.size();
-			vector<VkSpecializationMapEntry>	entries;
-			VkSpecializationInfo				specInfo;
-
-			entries.resize(numSpecConstants);
-
-			// Only support 32-bit integers as spec constants now. And their constant IDs are numbered sequentially starting from 0.
-			for (size_t ndx = 0; ndx < numSpecConstants; ++ndx)
-			{
-				entries[ndx].constantID	= (deUint32)ndx;
-				entries[ndx].offset		= deUint32(ndx * sizeof(deInt32));
-				entries[ndx].size		= sizeof(deInt32);
-			}
-
-			specConstantEntries.push_back(entries);
-
-			specInfo.mapEntryCount	= (deUint32)numSpecConstants;
-			specInfo.pMapEntries	= specConstantEntries.back().data();
-			specInfo.dataSize		= numSpecConstants * sizeof(deInt32);
-			specInfo.pData			= stageIt->second.data();
-			specializationInfos.push_back(specInfo);
-
-			stageInfo->pSpecializationInfo = &specializationInfos.back();
-		}
-	}
-	const VkPipelineDepthStencilStateCreateInfo	depthStencilParams		=
-	{
-		VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,	//	VkStructureType		sType;
-		DE_NULL,													//	const void*			pNext;
-		(VkPipelineDepthStencilStateCreateFlags)0,
-		DE_FALSE,													//	deUint32			depthTestEnable;
-		DE_FALSE,													//	deUint32			depthWriteEnable;
-		VK_COMPARE_OP_ALWAYS,										//	VkCompareOp			depthCompareOp;
-		DE_FALSE,													//	deUint32			depthBoundsTestEnable;
-		DE_FALSE,													//	deUint32			stencilTestEnable;
-		{
-			VK_STENCIL_OP_KEEP,											//	VkStencilOp	stencilFailOp;
-			VK_STENCIL_OP_KEEP,											//	VkStencilOp	stencilPassOp;
-			VK_STENCIL_OP_KEEP,											//	VkStencilOp	stencilDepthFailOp;
-			VK_COMPARE_OP_ALWAYS,										//	VkCompareOp	stencilCompareOp;
-			0u,															//	deUint32	stencilCompareMask;
-			0u,															//	deUint32	stencilWriteMask;
-			0u,															//	deUint32	stencilReference;
-		},															//	VkStencilOpState	front;
-		{
-			VK_STENCIL_OP_KEEP,											//	VkStencilOp	stencilFailOp;
-			VK_STENCIL_OP_KEEP,											//	VkStencilOp	stencilPassOp;
-			VK_STENCIL_OP_KEEP,											//	VkStencilOp	stencilDepthFailOp;
-			VK_COMPARE_OP_ALWAYS,										//	VkCompareOp	stencilCompareOp;
-			0u,															//	deUint32	stencilCompareMask;
-			0u,															//	deUint32	stencilWriteMask;
-			0u,															//	deUint32	stencilReference;
-		},															//	VkStencilOpState	back;
-		-1.0f,														//	float				minDepthBounds;
-		+1.0f,														//	float				maxDepthBounds;
-	};
-	const VkViewport						viewport0				=
-	{
-		0.0f,														//	float	originX;
-		0.0f,														//	float	originY;
-		(float)renderSize.x(),										//	float	width;
-		(float)renderSize.y(),										//	float	height;
-		0.0f,														//	float	minDepth;
-		1.0f,														//	float	maxDepth;
-	};
-	const VkRect2D							scissor0				=
-	{
-		{
-			0u,															//	deInt32	x;
-			0u,															//	deInt32	y;
-		},															//	VkOffset2D	offset;
-		{
-			renderSize.x(),												//	deInt32	width;
-			renderSize.y(),												//	deInt32	height;
-		},															//	VkExtent2D	extent;
-	};
-	const VkPipelineViewportStateCreateInfo		viewportParams			=
-	{
-		VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,		//	VkStructureType		sType;
-		DE_NULL,													//	const void*			pNext;
-		(VkPipelineViewportStateCreateFlags)0,
-		1u,															//	deUint32			viewportCount;
-		&viewport0,
-		1u,
-		&scissor0
-	};
-	const VkSampleMask							sampleMask				= ~0u;
-	const VkPipelineMultisampleStateCreateInfo	multisampleParams		=
-	{
-		VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,	//	VkStructureType			sType;
-		DE_NULL,													//	const void*				pNext;
-		(VkPipelineMultisampleStateCreateFlags)0,
-		VK_SAMPLE_COUNT_1_BIT,										//	VkSampleCountFlagBits	rasterSamples;
-		DE_FALSE,													//	deUint32				sampleShadingEnable;
-		0.0f,														//	float					minSampleShading;
-		&sampleMask,												//	const VkSampleMask*		pSampleMask;
-		DE_FALSE,													//	VkBool32				alphaToCoverageEnable;
-		DE_FALSE,													//	VkBool32				alphaToOneEnable;
-	};
-	const VkPipelineRasterizationStateCreateInfo	rasterParams		=
-	{
-		VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,	//	VkStructureType	sType;
-		DE_NULL,													//	const void*		pNext;
-		(VkPipelineRasterizationStateCreateFlags)0,
-		DE_TRUE,													//	deUint32		depthClipEnable;
-		DE_FALSE,													//	deUint32		rasterizerDiscardEnable;
-		VK_POLYGON_MODE_FILL,										//	VkFillMode		fillMode;
-		VK_CULL_MODE_NONE,											//	VkCullMode		cullMode;
-		VK_FRONT_FACE_COUNTER_CLOCKWISE,							//	VkFrontFace		frontFace;
-		VK_FALSE,													//	VkBool32		depthBiasEnable;
-		0.0f,														//	float			depthBias;
-		0.0f,														//	float			depthBiasClamp;
-		0.0f,														//	float			slopeScaledDepthBias;
-		1.0f,														//	float			lineWidth;
-	};
-	const VkPrimitiveTopology topology = hasTessellation? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
-	const VkPipelineInputAssemblyStateCreateInfo	inputAssemblyParams	=
-	{
-		VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,	//	VkStructureType		sType;
-		DE_NULL,														//	const void*			pNext;
-		(VkPipelineInputAssemblyStateCreateFlags)0,
-		topology,														//	VkPrimitiveTopology	topology;
-		DE_FALSE,														//	deUint32			primitiveRestartEnable;
-	};
-	const VkVertexInputBindingDescription		vertexBinding0 =
-	{
-		0u,									// deUint32					binding;
-		deUint32(singleVertexDataSize),		// deUint32					strideInBytes;
-		VK_VERTEX_INPUT_RATE_VERTEX			// VkVertexInputStepRate	stepRate;
-	};
-	const VkVertexInputAttributeDescription		vertexAttrib0[2] =
-	{
-		{
-			0u,									// deUint32	location;
-			0u,									// deUint32	binding;
-			VK_FORMAT_R32G32B32A32_SFLOAT,		// VkFormat	format;
-			0u									// deUint32	offsetInBytes;
-		},
-		{
-			1u,									// deUint32	location;
-			0u,									// deUint32	binding;
-			VK_FORMAT_R32G32B32A32_SFLOAT,		// VkFormat	format;
-			sizeof(Vec4),						// deUint32	offsetInBytes;
-		}
-	};
-
-	const VkPipelineVertexInputStateCreateInfo	vertexInputStateParams	=
-	{
-		VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,	//	VkStructureType								sType;
-		DE_NULL,													//	const void*									pNext;
-		(VkPipelineVertexInputStateCreateFlags)0,
-		1u,															//	deUint32									bindingCount;
-		&vertexBinding0,											//	const VkVertexInputBindingDescription*		pVertexBindingDescriptions;
-		2u,															//	deUint32									attributeCount;
-		vertexAttrib0,												//	const VkVertexInputAttributeDescription*	pVertexAttributeDescriptions;
-	};
-	const VkPipelineColorBlendAttachmentState	attBlendParams			=
-	{
-		DE_FALSE,													//	deUint32		blendEnable;
-		VK_BLEND_FACTOR_ONE,										//	VkBlend			srcBlendColor;
-		VK_BLEND_FACTOR_ZERO,										//	VkBlend			destBlendColor;
-		VK_BLEND_OP_ADD,											//	VkBlendOp		blendOpColor;
-		VK_BLEND_FACTOR_ONE,										//	VkBlend			srcBlendAlpha;
-		VK_BLEND_FACTOR_ZERO,										//	VkBlend			destBlendAlpha;
-		VK_BLEND_OP_ADD,											//	VkBlendOp		blendOpAlpha;
-		(VK_COLOR_COMPONENT_R_BIT|
-		 VK_COLOR_COMPONENT_G_BIT|
-		 VK_COLOR_COMPONENT_B_BIT|
-		 VK_COLOR_COMPONENT_A_BIT),									//	VkChannelFlags	channelWriteMask;
-	};
-	const VkPipelineColorBlendStateCreateInfo	blendParams				=
-	{
-		VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,	//	VkStructureType								sType;
-		DE_NULL,													//	const void*									pNext;
-		(VkPipelineColorBlendStateCreateFlags)0,
-		DE_FALSE,													//	VkBool32									logicOpEnable;
-		VK_LOGIC_OP_COPY,											//	VkLogicOp									logicOp;
-		1u,															//	deUint32									attachmentCount;
-		&attBlendParams,											//	const VkPipelineColorBlendAttachmentState*	pAttachments;
-		{ 0.0f, 0.0f, 0.0f, 0.0f },									//	float										blendConst[4];
-	};
-	const VkPipelineTessellationStateCreateInfo	tessellationState	=
-	{
-		VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO,
-		DE_NULL,
-		(VkPipelineTessellationStateCreateFlags)0,
-		3u
-	};
-
-	const VkPipelineTessellationStateCreateInfo* tessellationInfo	=	hasTessellation ? &tessellationState: DE_NULL;
-	const VkGraphicsPipelineCreateInfo		pipelineParams			=
-	{
-		VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,		//	VkStructureType									sType;
-		DE_NULL,												//	const void*										pNext;
-		0u,														//	VkPipelineCreateFlags							flags;
-		(deUint32)shaderStageParams.size(),						//	deUint32										stageCount;
-		&shaderStageParams[0],									//	const VkPipelineShaderStageCreateInfo*			pStages;
-		&vertexInputStateParams,								//	const VkPipelineVertexInputStateCreateInfo*		pVertexInputState;
-		&inputAssemblyParams,									//	const VkPipelineInputAssemblyStateCreateInfo*	pInputAssemblyState;
-		tessellationInfo,										//	const VkPipelineTessellationStateCreateInfo*	pTessellationState;
-		&viewportParams,										//	const VkPipelineViewportStateCreateInfo*		pViewportState;
-		&rasterParams,											//	const VkPipelineRasterStateCreateInfo*			pRasterState;
-		&multisampleParams,										//	const VkPipelineMultisampleStateCreateInfo*		pMultisampleState;
-		&depthStencilParams,									//	const VkPipelineDepthStencilStateCreateInfo*	pDepthStencilState;
-		&blendParams,											//	const VkPipelineColorBlendStateCreateInfo*		pColorBlendState;
-		(const VkPipelineDynamicStateCreateInfo*)DE_NULL,		//	const VkPipelineDynamicStateCreateInfo*			pDynamicState;
-		*pipelineLayout,										//	VkPipelineLayout								layout;
-		*renderPass,											//	VkRenderPass									renderPass;
-		0u,														//	deUint32										subpass;
-		DE_NULL,												//	VkPipeline										basePipelineHandle;
-		0u,														//	deInt32											basePipelineIndex;
-	};
-
-	const Unique<VkPipeline>				pipeline				(createGraphicsPipeline(vk, vkDevice, DE_NULL, &pipelineParams));
-
-	// Framebuffer
-	const VkFramebufferCreateInfo			framebufferParams		=
-	{
-		VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,				//	VkStructureType		sType;
-		DE_NULL,												//	const void*			pNext;
-		(VkFramebufferCreateFlags)0,
-		*renderPass,											//	VkRenderPass		renderPass;
-		1u,														//	deUint32			attachmentCount;
-		&*colorAttView,											//	const VkImageView*	pAttachments;
-		(deUint32)renderSize.x(),								//	deUint32			width;
-		(deUint32)renderSize.y(),								//	deUint32			height;
-		1u,														//	deUint32			layers;
-	};
-	const Unique<VkFramebuffer>				framebuffer				(createFramebuffer(vk, vkDevice, &framebufferParams));
-
-	const Unique<VkCommandPool>				cmdPool					(createCommandPool(vk, vkDevice, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex));
-
-	// Command buffer
-	const Unique<VkCommandBuffer>			cmdBuf					(allocateCommandBuffer(vk, vkDevice, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY));
-
-	const VkCommandBufferBeginInfo			cmdBufBeginParams		=
-	{
-		VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,			//	VkStructureType				sType;
-		DE_NULL,												//	const void*					pNext;
-		(VkCommandBufferUsageFlags)0,
-		(const VkCommandBufferInheritanceInfo*)DE_NULL,
-	};
-
-	// Record commands
-	VK_CHECK(vk.beginCommandBuffer(*cmdBuf, &cmdBufBeginParams));
-
-	{
-		const VkMemoryBarrier		vertFlushBarrier	=
-		{
-			VK_STRUCTURE_TYPE_MEMORY_BARRIER,			//	VkStructureType		sType;
-			DE_NULL,									//	const void*			pNext;
-			VK_ACCESS_HOST_WRITE_BIT,					//	VkMemoryOutputFlags	outputMask;
-			VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT,		//	VkMemoryInputFlags	inputMask;
-		};
-		const VkImageMemoryBarrier	colorAttBarrier		=
-		{
-			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,		//	VkStructureType			sType;
-			DE_NULL,									//	const void*				pNext;
-			0u,											//	VkMemoryOutputFlags		outputMask;
-			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,		//	VkMemoryInputFlags		inputMask;
-			VK_IMAGE_LAYOUT_UNDEFINED,					//	VkImageLayout			oldLayout;
-			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	//	VkImageLayout			newLayout;
-			queueFamilyIndex,							//	deUint32				srcQueueFamilyIndex;
-			queueFamilyIndex,							//	deUint32				destQueueFamilyIndex;
-			*image,										//	VkImage					image;
-			{
-				VK_IMAGE_ASPECT_COLOR_BIT,					//	VkImageAspect	aspect;
-				0u,											//	deUint32		baseMipLevel;
-				1u,											//	deUint32		mipLevels;
-				0u,											//	deUint32		baseArraySlice;
-				1u,											//	deUint32		arraySize;
-			}											//	VkImageSubresourceRange	subresourceRange;
-		};
-		vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, (VkDependencyFlags)0, 1, &vertFlushBarrier, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &colorAttBarrier);
-	}
-
-	{
-		const VkClearValue			clearValue		= makeClearValueColorF32(0.125f, 0.25f, 0.75f, 1.0f);
-		const VkRenderPassBeginInfo	passBeginParams	=
-		{
-			VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,			//	VkStructureType		sType;
-			DE_NULL,											//	const void*			pNext;
-			*renderPass,										//	VkRenderPass		renderPass;
-			*framebuffer,										//	VkFramebuffer		framebuffer;
-			{ { 0, 0 }, { renderSize.x(), renderSize.y() } },	//	VkRect2D			renderArea;
-			1u,													//	deUint32			clearValueCount;
-			&clearValue,										//	const VkClearValue*	pClearValues;
-		};
-		vk.cmdBeginRenderPass(*cmdBuf, &passBeginParams, VK_SUBPASS_CONTENTS_INLINE);
-	}
-
-	vk.cmdBindPipeline(*cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
-	{
-		const VkDeviceSize bindingOffset = 0;
-		vk.cmdBindVertexBuffers(*cmdBuf, 0u, 1u, &vertexBuffer.get(), &bindingOffset);
-	}
-	vk.cmdDraw(*cmdBuf, deUint32(vertexCount), 1u /*run pipeline once*/, 0u /*first vertex*/, 0u /*first instanceIndex*/);
-	vk.cmdEndRenderPass(*cmdBuf);
-
-	{
-		const VkImageMemoryBarrier	renderFinishBarrier	=
-		{
-			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,		//	VkStructureType			sType;
-			DE_NULL,									//	const void*				pNext;
-			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,		//	VkMemoryOutputFlags		outputMask;
-			VK_ACCESS_TRANSFER_READ_BIT,				//	VkMemoryInputFlags		inputMask;
-			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	//	VkImageLayout			oldLayout;
-			VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,		//	VkImageLayout			newLayout;
-			queueFamilyIndex,							//	deUint32				srcQueueFamilyIndex;
-			queueFamilyIndex,							//	deUint32				destQueueFamilyIndex;
-			*image,										//	VkImage					image;
-			{
-				VK_IMAGE_ASPECT_COLOR_BIT,					//	VkImageAspectFlags	aspectMask;
-				0u,											//	deUint32			baseMipLevel;
-				1u,											//	deUint32			mipLevels;
-				0u,											//	deUint32			baseArraySlice;
-				1u,											//	deUint32			arraySize;
-			}											//	VkImageSubresourceRange	subresourceRange;
-		};
-		vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &renderFinishBarrier);
-	}
-
-	{
-		const VkBufferImageCopy	copyParams	=
-		{
-			(VkDeviceSize)0u,						//	VkDeviceSize			bufferOffset;
-			(deUint32)renderSize.x(),				//	deUint32				bufferRowLength;
-			(deUint32)renderSize.y(),				//	deUint32				bufferImageHeight;
-			{
-				VK_IMAGE_ASPECT_COLOR_BIT,				//	VkImageAspect		aspect;
-				0u,										//	deUint32			mipLevel;
-				0u,										//	deUint32			arrayLayer;
-				1u,										//	deUint32			arraySize;
-			},										//	VkImageSubresourceCopy	imageSubresource;
-			{ 0u, 0u, 0u },							//	VkOffset3D				imageOffset;
-			{ renderSize.x(), renderSize.y(), 1u }	//	VkExtent3D				imageExtent;
-		};
-		vk.cmdCopyImageToBuffer(*cmdBuf, *image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *readImageBuffer, 1u, &copyParams);
-	}
-
-	{
-		const VkBufferMemoryBarrier	copyFinishBarrier	=
-		{
-			VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,	//	VkStructureType		sType;
-			DE_NULL,									//	const void*			pNext;
-			VK_ACCESS_TRANSFER_WRITE_BIT,				//	VkMemoryOutputFlags	outputMask;
-			VK_ACCESS_HOST_READ_BIT,					//	VkMemoryInputFlags	inputMask;
-			queueFamilyIndex,							//	deUint32			srcQueueFamilyIndex;
-			queueFamilyIndex,							//	deUint32			destQueueFamilyIndex;
-			*readImageBuffer,							//	VkBuffer			buffer;
-			0u,											//	VkDeviceSize		offset;
-			imageSizeBytes								//	VkDeviceSize		size;
-		};
-		vk.cmdPipelineBarrier(*cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 1, &copyFinishBarrier, 0, (const VkImageMemoryBarrier*)DE_NULL);
-	}
-
-	VK_CHECK(vk.endCommandBuffer(*cmdBuf));
-
-	// Upload vertex data
-	{
-		const VkMappedMemoryRange	range			=
-		{
-			VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,	//	VkStructureType	sType;
-			DE_NULL,								//	const void*		pNext;
-			vertexBufferMemory->getMemory(),		//	VkDeviceMemory	mem;
-			0,										//	VkDeviceSize	offset;
-			(VkDeviceSize)sizeof(vertexData),		//	VkDeviceSize	size;
-		};
-		void*						vertexBufPtr	= vertexBufferMemory->getHostPtr();
-
-		deMemcpy(vertexBufPtr, &vertexData[0], sizeof(vertexData));
-		VK_CHECK(vk.flushMappedMemoryRanges(vkDevice, 1u, &range));
-	}
-
-	// Submit & wait for completion
-	{
-		const Unique<VkFence>	fence		(createFence(vk, vkDevice));
-		const VkSubmitInfo		submitInfo	=
-		{
-			VK_STRUCTURE_TYPE_SUBMIT_INFO,
-			DE_NULL,
-			0u,
-			(const VkSemaphore*)DE_NULL,
-			(const VkPipelineStageFlags*)DE_NULL,
-			1u,
-			&cmdBuf.get(),
-			0u,
-			(const VkSemaphore*)DE_NULL,
-		};
-
-		VK_CHECK(vk.queueSubmit(queue, 1u, &submitInfo, *fence));
-		VK_CHECK(vk.waitForFences(vkDevice, 1u, &fence.get(), DE_TRUE, ~0ull));
-	}
-
-	const void* imagePtr	= readImageBufferMemory->getHostPtr();
-	const tcu::ConstPixelBufferAccess pixelBuffer(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8),
-												  renderSize.x(), renderSize.y(), 1, imagePtr);
-	// Log image
-	{
-		const VkMappedMemoryRange	range		=
-		{
-			VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,	//	VkStructureType	sType;
-			DE_NULL,								//	const void*		pNext;
-			readImageBufferMemory->getMemory(),		//	VkDeviceMemory	mem;
-			0,										//	VkDeviceSize	offset;
-			imageSizeBytes,							//	VkDeviceSize	size;
-		};
-
-		VK_CHECK(vk.invalidateMappedMemoryRanges(vkDevice, 1u, &range));
-		context.getTestContext().getLog() << TestLog::Image("Result", "Result", pixelBuffer);
-	}
-
-	const RGBA threshold(1, 1, 1, 1);
-	const RGBA upperLeft(pixelBuffer.getPixel(1, 1));
-	if (!tcu::compareThreshold(upperLeft, instance.outputColors[0], threshold))
-		return TestStatus::fail("Upper left corner mismatch");
-
-	const RGBA upperRight(pixelBuffer.getPixel(pixelBuffer.getWidth() - 1, 1));
-	if (!tcu::compareThreshold(upperRight, instance.outputColors[1], threshold))
-		return TestStatus::fail("Upper right corner mismatch");
-
-	const RGBA lowerLeft(pixelBuffer.getPixel(1, pixelBuffer.getHeight() - 1));
-	if (!tcu::compareThreshold(lowerLeft, instance.outputColors[2], threshold))
-		return TestStatus::fail("Lower left corner mismatch");
-
-	const RGBA lowerRight(pixelBuffer.getPixel(pixelBuffer.getWidth() - 1, pixelBuffer.getHeight() - 1));
-	if (!tcu::compareThreshold(lowerRight, instance.outputColors[3], threshold))
-		return TestStatus::fail("Lower right corner mismatch");
-
-	return TestStatus::pass("Rendered output matches input");
-}
-
-void createTestsForAllStages (const std::string& name, const RGBA (&inputColors)[4], const RGBA (&outputColors)[4], const map<string, string>& testCodeFragments, const vector<deInt32>& specConstants, tcu::TestCaseGroup* tests)
-{
-	const ShaderElement		vertFragPipelineStages[]		=
-	{
-		ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
-		ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
-	};
-
-	const ShaderElement		tessPipelineStages[]			=
-	{
-		ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
-		ShaderElement("tessc", "main", VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT),
-		ShaderElement("tesse", "main", VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT),
-		ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
-	};
-
-	const ShaderElement		geomPipelineStages[]				=
-	{
-		ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
-		ShaderElement("geom", "main", VK_SHADER_STAGE_GEOMETRY_BIT),
-		ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
-	};
-
-	StageToSpecConstantMap	specConstantMap;
-
-	specConstantMap[VK_SHADER_STAGE_VERTEX_BIT] = specConstants;
-	addFunctionCaseWithPrograms<InstanceContext>(tests, name + "_vert", "", addShaderCodeCustomVertex, runAndVerifyDefaultPipeline,
-												 createInstanceContext(vertFragPipelineStages, inputColors, outputColors, testCodeFragments, specConstantMap));
-
-	specConstantMap.clear();
-	specConstantMap[VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT] = specConstants;
-	addFunctionCaseWithPrograms<InstanceContext>(tests, name + "_tessc", "", addShaderCodeCustomTessControl, runAndVerifyDefaultPipeline,
-												 createInstanceContext(tessPipelineStages, inputColors, outputColors, testCodeFragments, specConstantMap));
-
-	specConstantMap.clear();
-	specConstantMap[VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT] = specConstants;
-	addFunctionCaseWithPrograms<InstanceContext>(tests, name + "_tesse", "", addShaderCodeCustomTessEval, runAndVerifyDefaultPipeline,
-												 createInstanceContext(tessPipelineStages, inputColors, outputColors, testCodeFragments, specConstantMap));
-
-	specConstantMap.clear();
-	specConstantMap[VK_SHADER_STAGE_GEOMETRY_BIT] = specConstants;
-	addFunctionCaseWithPrograms<InstanceContext>(tests, name + "_geom", "", addShaderCodeCustomGeometry, runAndVerifyDefaultPipeline,
-												 createInstanceContext(geomPipelineStages, inputColors, outputColors, testCodeFragments, specConstantMap));
-
-	specConstantMap.clear();
-	specConstantMap[VK_SHADER_STAGE_FRAGMENT_BIT] = specConstants;
-	addFunctionCaseWithPrograms<InstanceContext>(tests, name + "_frag", "", addShaderCodeCustomFragment, runAndVerifyDefaultPipeline,
-												 createInstanceContext(vertFragPipelineStages, inputColors, outputColors, testCodeFragments, specConstantMap));
-}
-
-inline void createTestsForAllStages (const std::string& name, const RGBA (&inputColors)[4], const RGBA (&outputColors)[4], const map<string, string>& testCodeFragments, tcu::TestCaseGroup* tests)
-{
-	vector<deInt32> noSpecConstants;
-	createTestsForAllStages(name, inputColors, outputColors, testCodeFragments, noSpecConstants, tests);
-}
 
 } // anonymous
 
@@ -7047,7 +4832,6 @@
 	createTestsForAllStages("vec4float32", defaultColors, defaultColors, fragments, opUndefTests.get());
 
 	fragments["pre_main"] =
-		"%v2f32 = OpTypeVector %f32 2\n"
 		"%m2x2f32 = OpTypeMatrix %v2f32 2\n";
 	fragments["testfun"] =
 		"%test_code = OpFunction %v4f32 None %v4f32_function\n"
@@ -7431,7 +5215,9 @@
 
 	getDefaultColors(defaultColors);
 	getInvertedDefaultColors(invertedColors);
-	addFunctionCaseWithPrograms<InstanceContext>(moduleTests.get(), "same_module", "", createCombinedModule, runAndVerifyDefaultPipeline, createInstanceContext(combinedPipeline, map<string, string>()));
+	addFunctionCaseWithPrograms<InstanceContext>(
+			moduleTests.get(), "same_module", "", createCombinedModule, runAndVerifyDefaultPipeline,
+			createInstanceContext(combinedPipeline, map<string, string>()));
 
 	const char* numbers[] =
 	{
@@ -7455,11 +5241,15 @@
 		// If there are an odd number, the color should be flipped.
 		if ((permutation.vertexPermutation + permutation.geometryPermutation + permutation.tesscPermutation + permutation.tessePermutation + permutation.fragmentPermutation) % 2 == 0)
 		{
-			addFunctionCaseWithPrograms<InstanceContext>(moduleTests.get(), name, "", createMultipleEntries, runAndVerifyDefaultPipeline, createInstanceContext(pipeline, defaultColors, defaultColors, map<string, string>()));
+			addFunctionCaseWithPrograms<InstanceContext>(
+					moduleTests.get(), name, "", createMultipleEntries, runAndVerifyDefaultPipeline,
+					createInstanceContext(pipeline, defaultColors, defaultColors, map<string, string>()));
 		}
 		else
 		{
-			addFunctionCaseWithPrograms<InstanceContext>(moduleTests.get(), name, "", createMultipleEntries, runAndVerifyDefaultPipeline, createInstanceContext(pipeline, defaultColors, invertedColors, map<string, string>()));
+			addFunctionCaseWithPrograms<InstanceContext>(
+					moduleTests.get(), name, "", createMultipleEntries, runAndVerifyDefaultPipeline,
+					createInstanceContext(pipeline, defaultColors, invertedColors, map<string, string>()));
 		}
 	}
 	return moduleTests.release();
@@ -7696,26 +5486,6 @@
 	return testGroup.release();
 }
 
-// Adds a new test to group using custom fragments for the tessellation-control
-// stage and passthrough fragments for all other stages.  Uses default colors
-// for input and expected output.
-void addTessCtrlTest(tcu::TestCaseGroup* group, const char* name, const map<string, string>& fragments)
-{
-	RGBA defaultColors[4];
-	getDefaultColors(defaultColors);
-	const ShaderElement pipelineStages[] =
-	{
-		ShaderElement("vert", "main", VK_SHADER_STAGE_VERTEX_BIT),
-		ShaderElement("tessc", "main", VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT),
-		ShaderElement("tesse", "main", VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT),
-		ShaderElement("frag", "main", VK_SHADER_STAGE_FRAGMENT_BIT),
-	};
-
-	addFunctionCaseWithPrograms<InstanceContext>(group, name, "", addShaderCodeCustomTessControl,
-												 runAndVerifyDefaultPipeline, createInstanceContext(
-													 pipelineStages, defaultColors, defaultColors, fragments, StageToSpecConstantMap()));
-}
-
 // A collection of tests putting OpControlBarrier in places GLSL forbids but SPIR-V allows.
 tcu::TestCaseGroup* createBarrierTests(tcu::TestContext& testCtx)
 {
@@ -8011,22 +5781,22 @@
 			|| to == INTEGER_TYPE_SIGNED_64 || to == INTEGER_TYPE_UNSIGNED_64);
 }
 
-ConvertTestFeatures getUsedFeatures (IntegerType from, IntegerType to)
+ComputeTestFeatures getConversionUsedFeatures (IntegerType from, IntegerType to)
 {
 	if (usesInt16(from, to))
 	{
 		if (usesInt64(from, to))
 		{
-			return CONVERT_TEST_USES_INT16_INT64;
+			return COMPUTE_TEST_USES_INT16_INT64;
 		}
 		else
 		{
-			return CONVERT_TEST_USES_INT16;
+			return COMPUTE_TEST_USES_INT16;
 		}
 	}
 	else
 	{
-		return CONVERT_TEST_USES_INT64;
+		return COMPUTE_TEST_USES_INT64;
 	}
 }
 
@@ -8035,7 +5805,7 @@
 	ConvertCase (IntegerType from, IntegerType to, deInt64 number)
 	: m_fromType		(from)
 	, m_toType			(to)
-	, m_features		(getUsedFeatures(from, to))
+	, m_features		(getConversionUsedFeatures(from, to))
 	, m_name			(getTestName(from, to))
 	, m_inputBuffer		(getBuffer(from, number))
 	, m_outputBuffer	(getBuffer(to, number))
@@ -8043,15 +5813,15 @@
 		m_asmTypes["inputType"]		= getAsmTypeDeclaration(from);
 		m_asmTypes["outputType"]	= getAsmTypeDeclaration(to);
 
-		if (m_features == CONVERT_TEST_USES_INT16)
+		if (m_features == COMPUTE_TEST_USES_INT16)
 		{
 			m_asmTypes["int_capabilities"] = "OpCapability Int16\n";
 		}
-		else if (m_features == CONVERT_TEST_USES_INT64)
+		else if (m_features == COMPUTE_TEST_USES_INT64)
 		{
 			m_asmTypes["int_capabilities"] = "OpCapability Int64\n";
 		}
-		else if (m_features == CONVERT_TEST_USES_INT16_INT64)
+		else if (m_features == COMPUTE_TEST_USES_INT16_INT64)
 		{
 			m_asmTypes["int_capabilities"] = string("OpCapability Int16\n") +
 													"OpCapability Int64\n";
@@ -8064,7 +5834,7 @@
 
 	IntegerType				m_fromType;
 	IntegerType				m_toType;
-	ConvertTestFeatures		m_features;
+	ComputeTestFeatures		m_features;
 	string					m_name;
 	map<string, string>		m_asmTypes;
 	BufferSp				m_inputBuffer;
@@ -8175,7 +5945,7 @@
 		spec.outputs.push_back(test->m_outputBuffer);
 		spec.numWorkGroups = IVec3(1, 1, 1);
 
-		group->addChild(new ConvertTestCase(testCtx, test->m_name.c_str(), "Convert integers with OpSConvert.", spec, test->m_features));
+		group->addChild(new SpvAsmComputeShaderCase(testCtx, test->m_name.c_str(), "Convert integers with OpSConvert.", spec, test->m_features));
 	}
 
 	return group.release();
@@ -8213,30 +5983,22 @@
 		spec.outputs.push_back(test->m_outputBuffer);
 		spec.numWorkGroups = IVec3(1, 1, 1);
 
-		group->addChild(new ConvertTestCase(testCtx, test->m_name.c_str(), "Convert integers with OpUConvert.", spec, test->m_features));
+		group->addChild(new SpvAsmComputeShaderCase(testCtx, test->m_name.c_str(), "Convert integers with OpUConvert.", spec, test->m_features));
 	}
 	return group.release();
 }
 
-enum NumberType
-{
-	TYPE_INT,
-	TYPE_UINT,
-	TYPE_FLOAT,
-	TYPE_END,
-};
-
 const string getNumberTypeName (const NumberType type)
 {
-	if (type == TYPE_INT)
+	if (type == NUMBERTYPE_INT32)
 	{
 		return "int";
 	}
-	else if (type == TYPE_UINT)
+	else if (type == NUMBERTYPE_UINT32)
 	{
 		return "uint";
 	}
-	else if (type == TYPE_FLOAT)
+	else if (type == NUMBERTYPE_FLOAT32)
 	{
 		return "float";
 	}
@@ -8252,14 +6014,6 @@
 	return rnd.getInt(std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
 }
 
-template <typename T>
-const string numberToString (T number)
-{
-	std::stringstream ss;
-	ss << number;
-	return ss.str();
-}
-
 const string repeatString (const string& str, int times)
 {
 	string filler;
@@ -8272,15 +6026,15 @@
 
 const string getRandomConstantString (const NumberType type, de::Random& rnd)
 {
-	if (type == TYPE_INT)
+	if (type == NUMBERTYPE_INT32)
 	{
 		return numberToString<deInt32>(getInt(rnd));
 	}
-	else if (type == TYPE_UINT)
+	else if (type == NUMBERTYPE_UINT32)
 	{
 		return numberToString<deUint32>(rnd.getUint32());
 	}
-	else if (type == TYPE_FLOAT)
+	else if (type == NUMBERTYPE_FLOAT32)
 	{
 		return numberToString<float>(rnd.getFloat());
 	}
@@ -8391,7 +6145,7 @@
 	createArrayCompositeCases(testCases, rnd, type);
 	createStructCompositeCases(testCases, rnd, type);
 	// Matrix only supports float types
-	if (type == TYPE_FLOAT)
+	if (type == NUMBERTYPE_FLOAT32)
 	{
 		createMatrixCompositeCases(testCases, rnd, type);
 	}
@@ -8401,9 +6155,9 @@
 {
 	switch (type)
 	{
-		case TYPE_INT:		return "OpTypeInt 32 1";
-		case TYPE_UINT:		return "OpTypeInt 32 0";
-		case TYPE_FLOAT:	return "OpTypeFloat 32";
+		case NUMBERTYPE_INT32:		return "OpTypeInt 32 1";
+		case NUMBERTYPE_UINT32:		return "OpTypeInt 32 0";
+		case NUMBERTYPE_FLOAT32:	return "OpTypeFloat 32";
 		default:			DE_ASSERT(false); return "";
 	}
 }
@@ -8498,7 +6252,7 @@
 	de::MovePtr<tcu::TestCaseGroup>	group	(new tcu::TestCaseGroup(testCtx, "opcompositeinsert", "Test the OpCompositeInsert instruction"));
 	de::Random						rnd		(deStringHash(group->getName()));
 
-	for (int type = TYPE_INT; type != TYPE_END; ++type)
+	for (int type = NUMBERTYPE_INT32; type != NUMBERTYPE_END32; ++type)
 	{
 		NumberType						numberType		= NumberType(type);
 		const string					typeName		= getNumberTypeName(numberType);
@@ -8516,21 +6270,21 @@
 
 			switch (numberType)
 			{
-				case TYPE_INT:
+				case NUMBERTYPE_INT32:
 				{
 					deInt32 number = getInt(rnd);
 					spec.inputs.push_back(createCompositeBuffer<deInt32>(number));
 					spec.outputs.push_back(createCompositeBuffer<deInt32>(number));
 					break;
 				}
-				case TYPE_UINT:
+				case NUMBERTYPE_UINT32:
 				{
 					deUint32 number = rnd.getUint32();
 					spec.inputs.push_back(createCompositeBuffer<deUint32>(number));
 					spec.outputs.push_back(createCompositeBuffer<deUint32>(number));
 					break;
 				}
-				case TYPE_FLOAT:
+				case NUMBERTYPE_FLOAT32:
 				{
 					float number = rnd.getFloat();
 					spec.inputs.push_back(createCompositeBuffer<float>(number));
@@ -8676,7 +6430,7 @@
 	de::MovePtr<tcu::TestCaseGroup>	group			(new tcu::TestCaseGroup(testCtx, "opinboundsaccesschain", "Test the OpInBoundsAccessChain instruction"));
 	de::Random						rnd				(deStringHash(group->getName()));
 
-	for (int type = TYPE_INT; type != TYPE_END; ++type)
+	for (int type = NUMBERTYPE_INT32; type != NUMBERTYPE_END32; ++type)
 	{
 		NumberType						numberType	= NumberType(type);
 		const string					typeName	= getNumberTypeName(numberType);
@@ -8700,21 +6454,21 @@
 
 			switch (numberType)
 			{
-				case TYPE_INT:
+				case NUMBERTYPE_INT32:
 				{
 					deInt32 number = getInt(rnd);
 					spec.inputs.push_back(createCompositeBuffer<deInt32>(number));
 					spec.outputs.push_back(createCompositeBuffer<deInt32>(number));
 					break;
 				}
-				case TYPE_UINT:
+				case NUMBERTYPE_UINT32:
 				{
 					deUint32 number = rnd.getUint32();
 					spec.inputs.push_back(createCompositeBuffer<deUint32>(number));
 					spec.outputs.push_back(createCompositeBuffer<deUint32>(number));
 					break;
 				}
-				case TYPE_FLOAT:
+				case NUMBERTYPE_FLOAT32:
 				{
 					float number = rnd.getFloat();
 					spec.inputs.push_back(createCompositeBuffer<float>(number));
@@ -8856,7 +6610,7 @@
 	de::MovePtr<tcu::TestCaseGroup>	group	(new tcu::TestCaseGroup(testCtx, "shader_default_output", "Test shader default output."));
 	de::Random						rnd		(deStringHash(group->getName()));
 
-	for (int type = TYPE_INT; type != TYPE_END; ++type)
+	for (int type = NUMBERTYPE_INT32; type != NUMBERTYPE_END32; ++type)
 	{
 		NumberType						numberType	= NumberType(type);
 		const string					typeName	= getNumberTypeName(numberType);
@@ -8873,7 +6627,7 @@
 
 			switch (numberType)
 			{
-				case TYPE_INT:
+				case NUMBERTYPE_INT32:
 				{
 					deInt32 number = getInt(rnd);
 					spec.inputs.push_back(createCompositeBuffer<deInt32>(number));
@@ -8881,7 +6635,7 @@
 					params["constValue"] = numberToString(number);
 					break;
 				}
-				case TYPE_UINT:
+				case NUMBERTYPE_UINT32:
 				{
 					deUint32 number = rnd.getUint32();
 					spec.inputs.push_back(createCompositeBuffer<deUint32>(number));
@@ -8889,7 +6643,7 @@
 					params["constValue"] = numberToString(number);
 					break;
 				}
-				case TYPE_FLOAT:
+				case NUMBERTYPE_FLOAT32:
 				{
 					float number = rnd.getFloat();
 					spec.inputs.push_back(createCompositeBuffer<float>(number));
@@ -8921,6 +6675,41 @@
 	return group.release();
 }
 
+tcu::TestCaseGroup* createOpNopTests (tcu::TestContext& testCtx)
+{
+	de::MovePtr<tcu::TestCaseGroup>	testGroup (new tcu::TestCaseGroup(testCtx, "opnop", "Test OpNop"));
+	RGBA							defaultColors[4];
+	map<string, string>				opNopFragments;
+
+	getDefaultColors(defaultColors);
+
+	opNopFragments["testfun"]		=
+		"%test_code = OpFunction %v4f32 None %v4f32_function\n"
+		"%param1 = OpFunctionParameter %v4f32\n"
+		"%label_testfun = OpLabel\n"
+		"OpNop\n"
+		"OpNop\n"
+		"OpNop\n"
+		"OpNop\n"
+		"OpNop\n"
+		"OpNop\n"
+		"OpNop\n"
+		"OpNop\n"
+		"%a = OpVectorExtractDynamic %f32 %param1 %c_i32_0\n"
+		"%b = OpFAdd %f32 %a %a\n"
+		"OpNop\n"
+		"%c = OpFSub %f32 %b %a\n"
+		"%ret = OpVectorInsertDynamic %v4f32 %param1 %c %c_i32_0\n"
+		"OpNop\n"
+		"OpNop\n"
+		"OpReturnValue %ret\n"
+		"OpFunctionEnd\n";
+
+	createTestsForAllStages("opnop", defaultColors, defaultColors, opNopFragments, testGroup.get());
+
+	return testGroup.release();
+}
+
 tcu::TestCaseGroup* createInstructionTests (tcu::TestContext& testCtx)
 {
 	de::MovePtr<tcu::TestCaseGroup> instructionTests	(new tcu::TestCaseGroup(testCtx, "instruction", "Instructions with special opcodes/operands"));
@@ -8959,37 +6748,7 @@
 	computeTests->addChild(createOpInBoundsAccessChainGroup(testCtx));
 	computeTests->addChild(createShaderDefaultOutputGroup(testCtx));
 
-	RGBA defaultColors[4];
-	getDefaultColors(defaultColors);
-
-	de::MovePtr<tcu::TestCaseGroup> opnopTests (new tcu::TestCaseGroup(testCtx, "opnop", "Test OpNop"));
-	map<string, string> opNopFragments;
-	opNopFragments["testfun"] =
-		"%test_code = OpFunction %v4f32 None %v4f32_function\n"
-		"%param1 = OpFunctionParameter %v4f32\n"
-		"%label_testfun = OpLabel\n"
-		"OpNop\n"
-		"OpNop\n"
-		"OpNop\n"
-		"OpNop\n"
-		"OpNop\n"
-		"OpNop\n"
-		"OpNop\n"
-		"OpNop\n"
-		"%a = OpVectorExtractDynamic %f32 %param1 %c_i32_0\n"
-		"%b = OpFAdd %f32 %a %a\n"
-		"OpNop\n"
-		"%c = OpFSub %f32 %b %a\n"
-		"%ret = OpVectorInsertDynamic %v4f32 %param1 %c %c_i32_0\n"
-		"OpNop\n"
-		"OpNop\n"
-		"OpReturnValue %ret\n"
-		"OpFunctionEnd\n"
-		;
-	createTestsForAllStages("opnop", defaultColors, defaultColors, opNopFragments, opnopTests.get());
-
-
-	graphicsTests->addChild(opnopTests.release());
+	graphicsTests->addChild(createOpNopTests(testCtx));
 	graphicsTests->addChild(createOpSourceTests(testCtx));
 	graphicsTests->addChild(createOpSourceContinuedTests(testCtx));
 	graphicsTests->addChild(createOpLineTests(testCtx));
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmUtils.cpp b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmUtils.cpp
new file mode 100644
index 0000000..c4e225a
--- /dev/null
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmUtils.cpp
@@ -0,0 +1,151 @@
+/*-------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Utilities for Vulkan SPIR-V assembly tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vktSpvAsmUtils.hpp"
+
+#include "deMemory.h"
+#include "deSTLUtil.hpp"
+#include "vkQueryUtil.hpp"
+#include "vkRefUtil.hpp"
+
+namespace vkt
+{
+namespace SpirVAssembly
+{
+
+using namespace vk;
+
+namespace
+{
+
+VkPhysicalDeviceFeatures filterDefaultDeviceFeatures (const VkPhysicalDeviceFeatures& deviceFeatures)
+{
+	VkPhysicalDeviceFeatures enabledDeviceFeatures = deviceFeatures;
+
+	// Disable robustness by default, as it has an impact on performance on some HW.
+	enabledDeviceFeatures.robustBufferAccess = false;
+
+	return enabledDeviceFeatures;
+}
+
+} // anonymous
+
+bool is16BitStorageFeaturesSupported (const InstanceInterface& vki, VkPhysicalDevice device, const std::vector<std::string>& instanceExtensions, Extension16BitStorageFeatures toCheck)
+{
+	VkPhysicalDevice16BitStorageFeaturesKHR	extensionFeatures	=
+	{
+		VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR,	// sType
+		DE_NULL,														// pNext
+		false,															// storageUniformBufferBlock16
+		false,															// storageUniform16
+		false,															// storagePushConstant16
+		false,															// storageInputOutput16
+	};
+	VkPhysicalDeviceFeatures2KHR			features;
+
+	deMemset(&features, 0, sizeof(features));
+	features.sType	= VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
+	features.pNext	= &extensionFeatures;
+
+	// Call the getter only if supported. Otherwise above "zero" defaults are used
+	if (de::contains(instanceExtensions.begin(), instanceExtensions.end(), "VK_KHR_get_physical_device_properties2"))
+	{
+		vki.getPhysicalDeviceFeatures2KHR(device, &features);
+	}
+
+	if ((toCheck & EXT16BITSTORAGEFEATURES_UNIFORM_BUFFER_BLOCK) != 0 && extensionFeatures.storageUniformBufferBlock16 == VK_FALSE)
+		return false;
+
+	if ((toCheck & EXT16BITSTORAGEFEATURES_UNIFORM) != 0 && extensionFeatures.storageUniform16 == VK_FALSE)
+		return false;
+
+	if ((toCheck & EXT16BITSTORAGEFEATURES_PUSH_CONSTANT) != 0 && extensionFeatures.storagePushConstant16 == VK_FALSE)
+		return false;
+
+	if ((toCheck & EXT16BITSTORAGEFEATURES_INPUT_OUTPUT) != 0 && extensionFeatures.storageInputOutput16 == VK_FALSE)
+		return false;
+
+	return true;
+}
+
+Move<VkDevice> createDeviceWithExtensions (const InstanceInterface&			vki,
+										   VkPhysicalDevice					physicalDevice,
+										   const deUint32					queueFamilyIndex,
+										   const std::vector<std::string>&	supportedExtensions,
+										   const std::vector<std::string>&	requiredExtensions)
+{
+	std::vector<const char*>		extensions			(requiredExtensions.size());
+
+	for (deUint32 extNdx = 0; extNdx < requiredExtensions.size(); ++extNdx)
+	{
+		const std::string&	ext = requiredExtensions[extNdx];
+
+		// Check that all required extensions are supported first.
+		if (!de::contains(supportedExtensions.begin(), supportedExtensions.end(), ext))
+		{
+			TCU_THROW(NotSupportedError, (std::string("Device extension not supported: ") + ext).c_str());
+		}
+
+		extensions[extNdx] = ext.c_str();
+	}
+
+	const float						queuePriorities[]	= { 1.0f };
+	const VkDeviceQueueCreateInfo	queueInfos[]		=
+	{
+		{
+			VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+			DE_NULL,
+			(VkDeviceQueueCreateFlags)0,
+			queueFamilyIndex,
+			DE_LENGTH_OF_ARRAY(queuePriorities),
+			&queuePriorities[0]
+		}
+	};
+	const VkPhysicalDeviceFeatures	features			= filterDefaultDeviceFeatures(getPhysicalDeviceFeatures(vki, physicalDevice));
+	const VkDeviceCreateInfo		deviceParams		=
+	{
+		VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+		DE_NULL,
+		(VkDeviceCreateFlags)0,
+		DE_LENGTH_OF_ARRAY(queueInfos),
+		&queueInfos[0],
+		0u,
+		DE_NULL,
+		(deUint32)extensions.size(),
+		extensions.empty() ? DE_NULL : &extensions[0],
+		&features
+	};
+
+	return vk::createDevice(vki, physicalDevice, &deviceParams);
+}
+
+Allocator* createAllocator (const InstanceInterface& instanceInterface, const VkPhysicalDevice physicalDevice, const DeviceInterface& deviceInterface, const VkDevice device)
+{
+	const VkPhysicalDeviceMemoryProperties memoryProperties = getPhysicalDeviceMemoryProperties(instanceInterface, physicalDevice);
+
+	// \todo [2015-07-24 jarkko] support allocator selection/configuration from command line (or compile time)
+	return new SimpleAllocator(deviceInterface, device, memoryProperties);
+}
+
+} // SpirVAssembly
+} // vkt
diff --git a/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmUtils.hpp b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmUtils.hpp
new file mode 100644
index 0000000..78c363f
--- /dev/null
+++ b/external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmUtils.hpp
@@ -0,0 +1,84 @@
+#ifndef _VKTSPVASMUTILS_HPP
+#define _VKTSPVASMUTILS_HPP
+/*-------------------------------------------------------------------------
+ * Vulkan Conformance Tests
+ * ------------------------
+ *
+ * Copyright (c) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Utilities for Vulkan SPIR-V assembly tests
+ *//*--------------------------------------------------------------------*/
+
+#include "vkDefs.hpp"
+#include "vkMemUtil.hpp"
+#include "vkRef.hpp"
+#include "vkTypeUtil.hpp"
+
+#include <string>
+#include <vector>
+
+namespace vkt
+{
+namespace SpirVAssembly
+{
+
+enum Extension16BitStorageFeatureBits
+{
+	EXT16BITSTORAGEFEATURES_UNIFORM_BUFFER_BLOCK	= (1u << 1),
+	EXT16BITSTORAGEFEATURES_UNIFORM					= (1u << 2),
+	EXT16BITSTORAGEFEATURES_PUSH_CONSTANT			= (1u << 3),
+	EXT16BITSTORAGEFEATURES_INPUT_OUTPUT			= (1u << 4),
+};
+typedef deUint32 Extension16BitStorageFeatures;
+
+struct ExtensionFeatures
+{
+	Extension16BitStorageFeatures	ext16BitStorage;
+
+	ExtensionFeatures	(void)
+		: ext16BitStorage	(0)
+	{}
+	explicit ExtensionFeatures	(Extension16BitStorageFeatures	ext16BitStorage_)
+		: ext16BitStorage	(ext16BitStorage_)
+	{}
+};
+
+// Returns true if the given 16bit storage extension features in `toCheck` are all supported.
+bool is16BitStorageFeaturesSupported (const vk::InstanceInterface&	vkInstance,
+									  vk::VkPhysicalDevice			device,
+									  const std::vector<std::string>& instanceExtensions,
+									  Extension16BitStorageFeatures	toCheck);
+
+// Creates a Vulkan logical device with the requiredExtensions enabled and all other extensions disabled.
+// The logical device will be created from the given instance and physical device. A single queue will
+// be created from the given queue family.
+vk::Move<vk::VkDevice> createDeviceWithExtensions (const vk::InstanceInterface&		vki,
+												   vk::VkPhysicalDevice				physicalDevice,
+												   deUint32							queueFamilyIndex,
+												   const std::vector<std::string>&	supportedExtensions,
+												   const std::vector<std::string>&	requiredExtensions);
+
+// Creates a SimpleAllocator on the given device.
+vk::Allocator* createAllocator (const vk::InstanceInterface& instanceInterface,
+								const vk::VkPhysicalDevice physicalDevice,
+								const vk::DeviceInterface& deviceInterface,
+								const vk::VkDevice device);
+
+} // SpirVAssembly
+} // vkt
+
+#endif // _VKTSPVASMUTILS_HPP
diff --git a/external/vulkancts/scripts/src/vulkan.h.in b/external/vulkancts/scripts/src/vulkan.h.in
index 11238cb..8f18a78 100644
--- a/external/vulkancts/scripts/src/vulkan.h.in
+++ b/external/vulkancts/scripts/src/vulkan.h.in
@@ -224,6 +224,7 @@
     VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = 1000059007,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = 1000059008,
     VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR = 1000080000,
+    VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR = 1000083000,
     VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR = 1000084000,
     VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR = 1000085000,
     VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE = 1000092000,
@@ -3953,6 +3954,19 @@
     const VkWriteDescriptorSet*                 pDescriptorWrites);
 #endif
 
+#define VK_KHR_16bit_storage 1
+#define VK_KHR_16BIT_STORAGE_SPEC_VERSION 1
+#define VK_KHR_16BIT_STORAGE_EXTENSION_NAME "VK_KHR_16bit_storage"
+
+typedef struct VkPhysicalDevice16BitStorageFeaturesKHR {
+    VkStructureType    sType;
+    const void*        pNext;
+    VkBool32           storageUniformBufferBlock16;
+    VkBool32           storageUniform16;
+    VkBool32           storagePushConstant16;
+    VkBool32           storageInputOutput16;
+} VkPhysicalDevice16BitStorageFeaturesKHR;
+
 #define VK_KHR_incremental_present 1
 #define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1
 #define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present"