text: /*------------------------------------------------------------------------
   * Vulkan Conformance Tests
   * ------------------------
   *
   * Copyright (c) 2019 Advanced Micro Devices, Inc.
   * Copyright (c) 2019 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.
   * 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 Tests for VK_AMD_shader_fragment_mask
   *//*--------------------------------------------------------------------*/
  
  #include "vktPipelineMultisampleShaderFragmentMaskTests.hpp"
  #include "vktPipelineMakeUtil.hpp"
  #include "vktTestCase.hpp"
  #include "vktTestCaseUtil.hpp"
  #include "vktTestGroupUtil.hpp"
  
  #include "vkCmdUtil.hpp"
  #include "vkObjUtil.hpp"
  #include "vkPlatform.hpp"
  #include "vkMemUtil.hpp"
  #include "vkQueryUtil.hpp"
  #include "vkTypeUtil.hpp"
  #include "vkRefUtil.hpp"
  #include "vkBuilderUtil.hpp"
  #include "vkPrograms.hpp"
  #include "vkImageUtil.hpp"
  
  #include "deUniquePtr.hpp"
  #include "deSharedPtr.hpp"
  #include "deRandom.hpp"
  
  #include "tcuVector.hpp"
  #include "tcuTestLog.hpp"
  #include "tcuImageCompare.hpp"
  #include "tcuTestLog.hpp"
  #include "tcuTextureUtil.hpp"
  
  #include <string>
  #include <vector>
  
  namespace vkt
  {
  namespace pipeline
  {
  namespace
  {
  using namespace vk;
  using de::UniquePtr;
  using de::MovePtr;
  using de::SharedPtr;
  using tcu::UVec2;
  using tcu::UVec4;
  using tcu::Vec2;
  using tcu::Vec4;
  
  typedef SharedPtr<Unique<VkImageView> >		ImageViewSp;
  typedef SharedPtr<Unique<VkPipeline> >		PipelineSp;
  
  struct PositionColor
  {
  	tcu::Vec4	        position;
  	VkClearColorValue	color;
  
  	PositionColor (const tcu::Vec4& pos, const tcu::UVec4& col) : position(pos)
      {
          deMemcpy(color.uint32, col.getPtr(), sizeof(color.uint32));
      }
  
  	PositionColor (const tcu::Vec4& pos, const tcu::Vec4&  col) : position(pos)
      {
          deMemcpy(color.float32, col.getPtr(), sizeof(color.float32));
      }
  
  	PositionColor (const PositionColor& rhs)
  		: position	(rhs.position)
          , color     (rhs.color)
  	{
  	}
  };
  
  //! Make a dummy sampler.
  Move<VkSampler> makeSampler (const DeviceInterface& vk, const VkDevice device)
  {
  	const VkSamplerCreateInfo samplerParams =
  	{
  		VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,			// VkStructureType         sType;
  		DE_NULL,										// const void*             pNext;
  		(VkSamplerCreateFlags)0,						// VkSamplerCreateFlags    flags;
  		VK_FILTER_NEAREST,								// VkFilter                magFilter;
  		VK_FILTER_NEAREST,								// VkFilter                minFilter;
  		VK_SAMPLER_MIPMAP_MODE_NEAREST,					// VkSamplerMipmapMode     mipmapMode;
  		VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,			// VkSamplerAddressMode    addressModeU;
  		VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,			// VkSamplerAddressMode    addressModeV;
  		VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,			// VkSamplerAddressMode    addressModeW;
  		0.0f,											// float                   mipLodBias;
  		VK_FALSE,										// VkBool32                anisotropyEnable;
  		1.0f,											// float                   maxAnisotropy;
  		VK_FALSE,										// VkBool32                compareEnable;
  		VK_COMPARE_OP_ALWAYS,							// VkCompareOp             compareOp;
  		0.0f,											// float                   minLod;
  		0.0f,											// float                   maxLod;
  		VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK,		// VkBorderColor           borderColor;
  		VK_FALSE,										// VkBool32                unnormalizedCoordinates;
  	};
  	return createSampler(vk, device, &samplerParams);
  }
  
  Move<VkImage> makeImage (const DeviceInterface&			vk,
  						 const VkDevice					device,
  						 const VkFormat					format,
  						 const UVec2&					size,
  						 const deUint32					layers,
  						 const VkSampleCountFlagBits	samples,
  						 const VkImageUsageFlags		usage)
  {
  	const VkImageCreateInfo imageParams =
  	{
  		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,			// VkStructureType			sType;
  		DE_NULL,										// const void*				pNext;
  		(VkImageCreateFlags)0,							// VkImageCreateFlags		flags;
  		VK_IMAGE_TYPE_2D,								// VkImageType				imageType;
  		format,											// VkFormat					format;
  		makeExtent3D(size.x(), size.y(), 1),			// VkExtent3D				extent;
  		1u,												// deUint32					mipLevels;
  		layers,											// deUint32					arrayLayers;
  		samples,										// VkSampleCountFlagBits	samples;
  		VK_IMAGE_TILING_OPTIMAL,						// VkImageTiling			tiling;
  		usage,											// VkImageUsageFlags		usage;
  		VK_SHARING_MODE_EXCLUSIVE,						// VkSharingMode			sharingMode;
  		0u,												// deUint32					queueFamilyIndexCount;
  		DE_NULL,										// const deUint32*			pQueueFamilyIndices;
  		VK_IMAGE_LAYOUT_UNDEFINED,						// VkImageLayout			initialLayout;
  	};
  	return createImage(vk, device, &imageParams);
  }
  
  //! Create a test-specific MSAA pipeline
  Move<VkPipeline> makeGraphicsPipeline (const DeviceInterface&					vk,
  									   const VkDevice							device,
  									   const VkPipelineLayout					pipelineLayout,
  									   const VkRenderPass						renderPass,
  									   const VkShaderModule						vertexModule,
  									   const VkShaderModule						fragmentModule,
  									   const bool								useVertexInput,
  									   const VkFormat							vertexAttribColorFormat,
  									   const bool								useColorAttachment,
  									   const deUint32							subpassNdx,
  									   const UVec2&								renderSize,
  									   const VkSampleCountFlagBits				numSamples)
  {
  	std::vector<VkVertexInputBindingDescription>	vertexInputBindingDescriptions;
  	std::vector<VkVertexInputAttributeDescription>	vertexInputAttributeDescriptions;
  
  	// Vertex attributes: position and color
  	if (useVertexInput)
  	{
  		vertexInputBindingDescriptions.push_back  (makeVertexInputBindingDescription  (0u, sizeof(PositionColor), VK_VERTEX_INPUT_RATE_VERTEX));
  		vertexInputAttributeDescriptions.push_back(makeVertexInputAttributeDescription(0u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, 0u));
  		vertexInputAttributeDescriptions.push_back(makeVertexInputAttributeDescription(1u, 0u, vertexAttribColorFormat, sizeof(Vec4)));
  	}
  
  	const VkPipelineVertexInputStateCreateInfo vertexInputStateInfo =
  	{
  		VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,		// VkStructureType							sType;
  		DE_NULL,														// const void*								pNext;
  		(VkPipelineVertexInputStateCreateFlags)0,						// VkPipelineVertexInputStateCreateFlags	flags;
  		static_cast<deUint32>(vertexInputBindingDescriptions.size()),	// uint32_t									vertexBindingDescriptionCount;
  		dataOrNullPtr(vertexInputBindingDescriptions),					// const VkVertexInputBindingDescription*	pVertexBindingDescriptions;
  		static_cast<deUint32>(vertexInputAttributeDescriptions.size()),	// uint32_t									vertexAttributeDescriptionCount;
  		dataOrNullPtr(vertexInputAttributeDescriptions),				// const VkVertexInputAttributeDescription*	pVertexAttributeDescriptions;
  	};
  
  	const VkPipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateInfo =
  	{
  		VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,	// VkStructureType							sType;
  		DE_NULL,														// const void*								pNext;
  		(VkPipelineInputAssemblyStateCreateFlags)0,						// VkPipelineInputAssemblyStateCreateFlags	flags;
  		VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,							// VkPrimitiveTopology						topology;
  		VK_FALSE,														// VkBool32									primitiveRestartEnable;
  	};
  
  	const VkViewport viewport =
  	{
  		0.0f, 0.0f,																	// x, y
  		static_cast<float>(renderSize.x()), static_cast<float>(renderSize.y()),		// widht, height
  		0.0f, 1.0f																	// minDepth, maxDepth
  	};
  
  	const VkRect2D scissor =
  	{
  		makeOffset2D(0, 0),
  		makeExtent2D(renderSize.x(), renderSize.y()),
  	};
  
  	const VkPipelineViewportStateCreateInfo pipelineViewportStateInfo =
  	{
  		VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,			// VkStructureType						sType;
  		DE_NULL,														// const void*							pNext;
  		(VkPipelineViewportStateCreateFlags)0,							// VkPipelineViewportStateCreateFlags	flags;
  		1u,																// uint32_t								viewportCount;
  		&viewport,														// const VkViewport*					pViewports;
  		1u,																// uint32_t								scissorCount;
  		&scissor,														// const VkRect2D*						pScissors;
  	};
  
  	const VkPipelineRasterizationStateCreateInfo pipelineRasterizationStateInfo =
  	{
  		VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,	// VkStructureType							sType;
  		DE_NULL,													// const void*								pNext;
  		(VkPipelineRasterizationStateCreateFlags)0,					// VkPipelineRasterizationStateCreateFlags	flags;
  		VK_FALSE,													// VkBool32									depthClampEnable;
  		VK_FALSE,													// VkBool32									rasterizerDiscardEnable;
  		VK_POLYGON_MODE_FILL,										// VkPolygonMode							polygonMode;
  		VK_CULL_MODE_NONE,											// VkCullModeFlags							cullMode;
  		VK_FRONT_FACE_COUNTER_CLOCKWISE,							// VkFrontFace								frontFace;
  		VK_FALSE,													// VkBool32									depthBiasEnable;
  		0.0f,														// float									depthBiasConstantFactor;
  		0.0f,														// float									depthBiasClamp;
  		0.0f,														// float									depthBiasSlopeFactor;
  		1.0f,														// float									lineWidth;
  	};
  
  	const VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateInfo =
  	{
  		VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,	// VkStructureType							sType;
  		DE_NULL,													// const void*								pNext;
  		(VkPipelineMultisampleStateCreateFlags)0,					// VkPipelineMultisampleStateCreateFlags	flags;
  		numSamples,													// VkSampleCountFlagBits					rasterizationSamples;
  		VK_FALSE,													// VkBool32									sampleShadingEnable;
  		1.0f,														// float									minSampleShading;
  		DE_NULL,													// const VkSampleMask*						pSampleMask;
  		VK_FALSE,													// VkBool32									alphaToCoverageEnable;
  		VK_FALSE													// VkBool32									alphaToOneEnable;
  	};
  
  	VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateInfo =
  	{
  		VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,	// VkStructureType							sType;
  		DE_NULL,													// const void*								pNext;
  		(VkPipelineDepthStencilStateCreateFlags)0,					// VkPipelineDepthStencilStateCreateFlags	flags;
  		VK_FALSE,													// VkBool32									depthTestEnable;
  		VK_TRUE,													// VkBool32									depthWriteEnable;
  		VK_COMPARE_OP_ALWAYS,										// VkCompareOp								depthCompareOp;
  		VK_FALSE,													// VkBool32									depthBoundsTestEnable;
  		VK_FALSE,													// VkBool32									stencilTestEnable;
  		VkStencilOpState(),											// VkStencilOpState							front;
  		VkStencilOpState(),											// VkStencilOpState							back;
  		0.0f,														// float									minDepthBounds;
  		1.0f,														// float									maxDepthBounds;
  	};
  
  	const VkColorComponentFlags colorComponentsAll = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
  	const VkPipelineColorBlendAttachmentState defaultBlendAttachmentState =
  	{
  		VK_FALSE,				// VkBool32					blendEnable;
  		VK_BLEND_FACTOR_ONE,	// VkBlendFactor			srcColorBlendFactor;
  		VK_BLEND_FACTOR_ZERO,	// VkBlendFactor			dstColorBlendFactor;
  		VK_BLEND_OP_ADD,		// VkBlendOp				colorBlendOp;
  		VK_BLEND_FACTOR_ONE,	// VkBlendFactor			srcAlphaBlendFactor;
  		VK_BLEND_FACTOR_ZERO,	// VkBlendFactor			dstAlphaBlendFactor;
  		VK_BLEND_OP_ADD,		// VkBlendOp				alphaBlendOp;
  		colorComponentsAll,		// VkColorComponentFlags	colorWriteMask;
  	};
  
  	const VkPipelineColorBlendStateCreateInfo pipelineColorBlendStateInfo =
  	{
  		VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,	// VkStructureType								sType;
  		DE_NULL,													// const void*									pNext;
  		(VkPipelineColorBlendStateCreateFlags)0,					// VkPipelineColorBlendStateCreateFlags			flags;
  		VK_FALSE,													// VkBool32										logicOpEnable;
  		VK_LOGIC_OP_COPY,											// VkLogicOp									logicOp;
  		(useColorAttachment ? 1u : 0u),								// deUint32										attachmentCount;
  		&defaultBlendAttachmentState,								// const VkPipelineColorBlendAttachmentState*	pAttachments;
  		{ 0.0f, 0.0f, 0.0f, 0.0f },									// float										blendConstants[4];
  	};
  
  	const VkPipelineShaderStageCreateInfo pShaderStages[] =
  	{
  		{
  			VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,	// VkStructureType						sType;
  			DE_NULL,												// const void*							pNext;
  			(VkPipelineShaderStageCreateFlags)0,					// VkPipelineShaderStageCreateFlags		flags;
  			VK_SHADER_STAGE_VERTEX_BIT,								// VkShaderStageFlagBits				stage;
  			vertexModule,											// VkShaderModule						module;
  			"main",													// const char*							pName;
  			DE_NULL,												// const VkSpecializationInfo*			pSpecializationInfo;
  		},
  		{
  			VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,	// VkStructureType						sType;
  			DE_NULL,												// const void*							pNext;
  			(VkPipelineShaderStageCreateFlags)0,					// VkPipelineShaderStageCreateFlags		flags;
  			VK_SHADER_STAGE_FRAGMENT_BIT,							// VkShaderStageFlagBits				stage;
  			fragmentModule,											// VkShaderModule						module;
  			"main",													// const char*							pName;
  			DE_NULL,												// const VkSpecializationInfo*			pSpecializationInfo;
  		}
  	};
  
  	const VkGraphicsPipelineCreateInfo	graphicsPipelineInfo =
  	{
  		VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,	// VkStructureType									sType;
  		DE_NULL,											// const void*										pNext;
  		(VkPipelineCreateFlags)0,							// VkPipelineCreateFlags							flags;
  		DE_LENGTH_OF_ARRAY(pShaderStages),					// deUint32											stageCount;
  		pShaderStages,										// const VkPipelineShaderStageCreateInfo*			pStages;
  		&vertexInputStateInfo,								// const VkPipelineVertexInputStateCreateInfo*		pVertexInputState;
  		&pipelineInputAssemblyStateInfo,					// const VkPipelineInputAssemblyStateCreateInfo*	pInputAssemblyState;
  		DE_NULL,											// const VkPipelineTessellationStateCreateInfo*		pTessellationState;
  		&pipelineViewportStateInfo,							// const VkPipelineViewportStateCreateInfo*			pViewportState;
  		&pipelineRasterizationStateInfo,					// const VkPipelineRasterizationStateCreateInfo*	pRasterizationState;
  		&pipelineMultisampleStateInfo,						// const VkPipelineMultisampleStateCreateInfo*		pMultisampleState;
  		&pipelineDepthStencilStateInfo,						// const VkPipelineDepthStencilStateCreateInfo*		pDepthStencilState;
  		&pipelineColorBlendStateInfo,						// const VkPipelineColorBlendStateCreateInfo*		pColorBlendState;
  		DE_NULL,											// const VkPipelineDynamicStateCreateInfo*			pDynamicState;
  		pipelineLayout,										// VkPipelineLayout									layout;
  		renderPass,											// VkRenderPass										renderPass;
  		subpassNdx,											// deUint32											subpass;
  		DE_NULL,											// VkPipeline										basePipelineHandle;
  		-1,													// deInt32											basePipelineIndex;
  	};
  
  	return createGraphicsPipeline(vk, device, DE_NULL, &graphicsPipelineInfo);
  }
  
  std::vector<PositionColor> genShapes (const VkFormat colorFormat)
  {
  	std::vector<PositionColor> vertices;
  
  	if (colorFormat == VK_FORMAT_R8G8B8A8_UNORM)
  	{
  		vertices.push_back(PositionColor(Vec4( 0.0f,  -0.75f, 0.0f, 1.0f), Vec4(0.5f, 0.5f, 0.5f, 1.0f)));
  		vertices.push_back(PositionColor(Vec4(-0.75f,  0.75f, 0.0f, 1.0f), Vec4(1.0f, 0.5f, 0.5f, 1.0f)));
  		vertices.push_back(PositionColor(Vec4( 0.75f,  0.65f, 0.0f, 1.0f), Vec4(0.0f, 0.5f, 1.0f, 1.0f)));
  	}
  	else
  	{
  		vertices.push_back(PositionColor(Vec4( 0.0f,  -0.75f, 0.0f, 1.0f), UVec4(0xabcdu, 0u, 0u, 0u)));
  		vertices.push_back(PositionColor(Vec4(-0.75f,  0.75f, 0.0f, 1.0f), UVec4(0xbcdeu, 0u, 0u, 0u)));
  		vertices.push_back(PositionColor(Vec4( 0.75f,  0.65f, 0.0f, 1.0f), UVec4(0xcdefu, 0u, 0u, 0u)));
  	}
  
  	return vertices;
  }
  
  //! Map color image format to a convenient format used in vertex attributes
  VkFormat getVertexInputColorFormat (const VkFormat colorImageFormat)
  {
  	switch (tcu::getTextureChannelClass(mapVkFormat(colorImageFormat).type))
  	{
  		case tcu::TEXTURECHANNELCLASS_FLOATING_POINT:
  		case tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT:
  		case tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT:
  			return VK_FORMAT_R32G32B32A32_SFLOAT;
  
  		case tcu::TEXTURECHANNELCLASS_SIGNED_INTEGER:
  			return VK_FORMAT_R32G32B32A32_SINT;
  
  		case tcu::TEXTURECHANNELCLASS_UNSIGNED_INTEGER:
  			return VK_FORMAT_R32G32B32A32_UINT;
  
  		default:
  			DE_ASSERT(0);
  			return VK_FORMAT_UNDEFINED;
  	}
  }
  
  enum SampleSource
  {
  	SAMPLE_SOURCE_IMAGE,			//!< texel fetch from an image
  	SAMPLE_SOURCE_SUBPASS_INPUT,	//!< texel fetch from an input attachment
  };
  
  //! The parameters that define a test case
  struct TestParams
  {
  	UVec2					renderSize;
  	deUint32				numLayers;			//!< 1 or N for layered image
  	SampleSource			sampleSource;		//!< source of texel fetch
  	VkSampleCountFlagBits	numColorSamples;
  	VkFormat				colorFormat;		//!< Color attachment format
  
  	TestParams (void)
  		: numLayers			()
  		, numColorSamples	()
  		, colorFormat		()
  	{
  	}
  };
  
  void checkRequirements (Context& context, TestParams params)
  {
  	context.requireDeviceFunctionality("VK_AMD_shader_fragment_mask");
  
  	// In the subpass input case we have to store fetch results into a buffer for subsequent verification in a compute shader.
  	const bool requireFragmentStores = (params.sampleSource == SAMPLE_SOURCE_SUBPASS_INPUT);
  
  	const VkPhysicalDeviceLimits& limits = context.getDeviceProperties().limits;
  
  	if ((limits.framebufferColorSampleCounts & params.numColorSamples) == 0u)
  		TCU_THROW(NotSupportedError, "framebufferColorSampleCounts: sample count not supported");
  
  	if ((isIntFormat(params.colorFormat) || isUintFormat(params.colorFormat)))
  	{
  		if ((limits.sampledImageIntegerSampleCounts & params.numColorSamples) == 0u)
  			TCU_THROW(NotSupportedError, "sampledImageIntegerSampleCounts: sample count not supported");
  	}
  	else
  	{
  		if ((limits.sampledImageColorSampleCounts & params.numColorSamples) == 0u)
  			TCU_THROW(NotSupportedError, "sampledImageColorSampleCounts: sample count not supported");
  	}
  
  	if (requireFragmentStores)
  	{
  		if (!context.getDeviceFeatures().fragmentStoresAndAtomics)
  			TCU_THROW(NotSupportedError, "fragmentStoresAndAtomics: feature not supported");
  	}
  }
  
  //! Common data used by the test
  struct WorkingData
  {
  	deUint32						numVertices;				//!< Number of vertices defined in the vertex buffer
  	Move<VkBuffer>					vertexBuffer;
  	MovePtr<Allocation>				vertexBufferAlloc;
  	Move<VkImage>					colorImage;					//!< Color image
  	MovePtr<Allocation>				colorImageAlloc;
  	Move<VkImageView>				colorImageView;				//!< Color image view spanning all layers
  	Move<VkBuffer>					colorBuffer;				//!< Buffer used to copy image data
  	MovePtr<Allocation>				colorBufferAlloc;
  	VkDeviceSize					colorBufferSize;
  	Move<VkSampler>					defaultSampler;				//!< Dummy sampler, we are using texel fetches
  
  	WorkingData (void)
  		: numVertices		()
  		, colorBufferSize	()
  	{
  	}
  };
  
  void initPrograms (SourceCollections& programCollection, const TestParams params)
  {
  	std::string	colorType;					//!< color pixel type used by image functions
  	std::string	colorBufferType;			//!< packed pixel type as stored in a ssbo
  	std::string colorBufferPack;			//!< a cast or a function call when writing back color format to the ssbo
  	std::string	colorFragInQualifier;		//!< fragment shader color input qualifier
  	std::string samplerPrefix;				//!< u, i, or empty
  
  	switch (params.colorFormat)
  	{
  		case VK_FORMAT_R8G8B8A8_UNORM:
  			colorType				= "vec4";
  			colorBufferType			= "uint";
  			colorBufferPack			= "packUnorm4x8";
  			break;
  
  		case VK_FORMAT_R32_UINT:
  			colorType				= "uint";
  			colorBufferType			= "uint";
  			colorBufferPack			= colorBufferType;
  			colorFragInQualifier	= "flat";
  			samplerPrefix			= "u";
  			break;
  
  		case VK_FORMAT_R32_SINT:
  			colorType				= "int";
  			colorBufferType			= "int";
  			colorBufferPack			= colorBufferType;
  			colorFragInQualifier	= "flat";
  			samplerPrefix			= "i";
  			break;
  
  		default:
  			DE_FATAL("initPrograms not handled for this color format");
  			break;
  	}
  
  	// Vertex shader - position and color
  	{
  		std::ostringstream src;
  		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
  			<< "\n"
  			<< "layout(location = 0) in  vec4 in_position;\n"
  			<< "layout(location = 1) in  " << colorType << " in_color;\n"
  			<< "layout(location = 0) out " << colorType << " o_color;\n"
  			<< "\n"
  			<< "out gl_PerVertex {\n"
  			<< "    vec4 gl_Position;\n"
  			<< "};\n"
  			<< "\n"
  			<< "void main(void)\n"
  			<< "{\n"
  			// Introduce a variance in geometry per instance index which maps to the image layer
  			<< "    float a   = 0.25 * float(gl_InstanceIndex);\n"
  			<< "    mat3 rm   = mat3( cos(a), sin(a), 0.0,\n"
  			<< "                     -sin(a), cos(a), 0.0,\n"
  			<< "                         0.0,    0.0, 1.0);\n"
  			<< "    vec2 rpos = (rm * vec3(in_position.xy, 1.0)).xy;\n"
  			<< "\n"
  			<< "    gl_Position = vec4(rpos, in_position.zw);\n"
  			<< "    o_color     = in_color;\n"
  			<< "}\n";
  
  		programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
  	}
  
  	// Vertex shader - no vertex data, fill viewport with one primitive
  	{
  		std::ostringstream src;
  		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
  			<< "\n"
  			<< "out gl_PerVertex {\n"
  			<< "    vec4 gl_Position;\n"
  			<< "};\n"
  			<< "\n"
  			<< "void main(void)\n"
  			<< "{\n"
  			// Specify an oversized triangle covering the whole viewport.
  			<< "    switch (gl_VertexIndex)\n"
  			<< "    {\n"
  			<< "        case 0:\n"
  			<< "            gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
  			<< "            break;\n"
  			<< "        case 1:\n"
  			<< "            gl_Position = vec4(-1.0,  3.0, 0.0, 1.0);\n"
  			<< "            break;\n"
  			<< "        case 2:\n"
  			<< "            gl_Position = vec4( 3.0, -1.0, 0.0, 1.0);\n"
  			<< "            break;\n"
  			<< "    }\n"
  			<< "}\n";
  
  		programCollection.glslSources.add("vert_full") << glu::VertexSource(src.str());
  	}
  
  	// Fragment shader - output color from VS
  	{
  		std::ostringstream src;
  		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
  			<< "\n"
  			<< "layout(location = 0) in " << colorFragInQualifier << " " << colorType << " in_color;\n"
  			<< "layout(location = 0) out " << colorType << " o_color;\n"
  			<< "\n"
  			<< "void main(void)\n"
  			<< "{\n"
  			<< "    o_color = in_color;\n"
  			<< "}\n";
  
  		programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
  	}
  
  	// Fragment shader - FMASK fetch from an input attachment
  	if (params.sampleSource == SAMPLE_SOURCE_SUBPASS_INPUT)
  	{
  		std::ostringstream src;
  		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
  			<< "#extension GL_AMD_shader_fragment_mask : enable\n"
  			<< "\n"
  			<< "layout(set = 0, binding = 0) uniform " << samplerPrefix << "sampler2DMS" << (params.numLayers > 1 ? "Array" : "") << " u_image;\n"
  			<< "layout(set = 0, binding = 1, std430) writeonly buffer ColorOutput {\n"
  			<< "    " << colorBufferType << " color[];\n"
  			<< "} sb_out;\n"
  			<< "layout(input_attachment_index = " << params.numLayers << ", set = 0, binding = 2) uniform " << samplerPrefix << "subpassInputMS" << " input_attach;\n"
  			<< "\n"
  			<< "void main(void)\n"
  			<< "{\n"
  			<< "    ivec2 p            = ivec2(gl_FragCoord.xy);\n"
  			<< "    int   width        = " << params.renderSize.x() << ";\n"
  			<< "    int   numSamples   = " << static_cast<deUint32>(params.numColorSamples) << ";\n"
  			<< "    int   colorOutNdx  = numSamples * (p.x + width * p.y);\n"
  			<< "\n"
  			<< "    uint mask = fragmentMaskFetchAMD(input_attach);\n"
  			<< "    for (int sampleNdx = 0; sampleNdx < numSamples; ++sampleNdx)\n"
  			<< "    {\n"
  			<< "        int fragNdx = int((mask >> (4 * sampleNdx)) & 0xf);\n"
  			<< "        " << samplerPrefix << "vec4 color = fragmentFetchAMD(input_attach, fragNdx);\n"
  			<< "        sb_out.color[colorOutNdx + sampleNdx] = " << colorBufferPack << "(color);\n"
  			<< "    }\n"
  			<< "}\n";
  
  		programCollection.glslSources.add("frag_fmask_fetch") << glu::FragmentSource(src.str());
  	}
  
  	// Generate compute shaders
  	const struct ComputeShaderParams
  	{
  		const char*		name;
  		bool			isFmaskFetch;
  		bool			enabled;
  	} computeShaders[] =
  	{
  		// name					// FMASK?	// enabled?
  		{ "comp_fetch",			false,		true,													},
  		{ "comp_fmask_fetch",	true,		(params.sampleSource != SAMPLE_SOURCE_SUBPASS_INPUT)	},
  	};
  
  	for (const ComputeShaderParams* pShaderParams = computeShaders; pShaderParams != DE_ARRAY_END(computeShaders); ++pShaderParams)
  	if (pShaderParams->enabled)
  	{
  		const std::string samplingPos = (params.numLayers == 1 ? "ivec2(gl_WorkGroupID.xy)"
  															   : "ivec3(gl_WorkGroupID)");
  		std::ostringstream src;
  		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
  			<< (pShaderParams->isFmaskFetch ? "#extension GL_AMD_shader_fragment_mask : enable\n" : "")
  			<< "#define NUM_SAMPLES " << static_cast<deUint32>(params.numColorSamples) << "\n"
  			<< "\n"
  			<< "layout(local_size_x = NUM_SAMPLES) in;\n"	// one work group per pixel, each sample gets a local invocation
  			<< "\n"
  			<< "layout(set = 0, binding = 0) uniform " << samplerPrefix << "sampler2DMS" << (params.numLayers > 1 ? "Array" : "") << " u_image;\n"
  			<< "layout(set = 0, binding = 1, std430) writeonly buffer ColorOutput {\n"
  			<< "    " << colorBufferType << " color[];\n"
  			<< "} sb_out;\n"
  			<< "\n"
  			<< "void main(void)\n"
  			<< "{\n"
  			<< "    int sampleNdx   = int(gl_LocalInvocationID.x);\n"
  			<< "    int colorOutNdx = NUM_SAMPLES * int(gl_WorkGroupID.x +\n"
  			<< "                                        gl_WorkGroupID.y * gl_NumWorkGroups.x +\n"
  			<< "                                        gl_WorkGroupID.z * gl_NumWorkGroups.x * gl_NumWorkGroups.y);\n"
  			<< "\n";
  		if (pShaderParams->isFmaskFetch)
  		{
  			src << "    uint  mask    = fragmentMaskFetchAMD(u_image, " << samplingPos << ");\n"
  				<< "    int   fragNdx = int((mask >> (4 * sampleNdx)) & 0xf);\n"
  				<< "    " << samplerPrefix << "vec4 color = fragmentFetchAMD(u_image, " << samplingPos << ", fragNdx);\n"
  				<< "    sb_out.color[colorOutNdx + sampleNdx] = " << colorBufferPack << "(color);\n";
  		}
  		else
  		{
  			src << "    " << samplerPrefix << "vec4 color = texelFetch(u_image, " << samplingPos << ", sampleNdx);\n"
  				<< "    sb_out.color[colorOutNdx + sampleNdx] = " << colorBufferPack << "(color);\n";
  		}
  		src << "}\n";
  
  		programCollection.glslSources.add(pShaderParams->name) << glu::ComputeSource(src.str());
  	}
  }
  
  std::vector<VkClearValue> genClearValues (const VkFormat format, const deUint32 count)
  {
  	std::vector<VkClearValue>	clearValues;
  	de::Random					rng (332);
  
  	switch (format)
  	{
  		case VK_FORMAT_R8G8B8A8_UNORM:
  			for (deUint32 i = 0u; i < count; ++i)
  				clearValues.push_back(makeClearValueColorF32(rng.getFloat(), rng.getFloat(), rng.getFloat(), 1.0f));
  			break;
  
  		case VK_FORMAT_R32_UINT:
  		case VK_FORMAT_R32_SINT:
  			for (deUint32 i = 0u; i < count; ++i)
  				clearValues.push_back(makeClearValueColorU32(rng.getUint32(), 0u, 0u, 0u));
  			break;
  
  		default:
  			DE_FATAL("Clear color not defined for this format");
  			break;
  	}
  
  	return clearValues;
  }
  
  //! For subpass load case draw and fetch must happen within the same render pass.
  void drawAndSampleInputAttachment (Context& context, const TestParams& params, WorkingData& wd)
  {
  	DE_ASSERT(params.numLayers == 1u);	// subpass load with single-layer image
  
  	const DeviceInterface&	vk		= context.getDeviceInterface();
  	const VkDevice			device	= context.getDevice();
  
  	Move<VkRenderPass>		renderPass;
  	Move<VkFramebuffer>		framebuffer;
  
  	// Create descriptor set
  	const Unique<VkDescriptorSetLayout> descriptorSetLayout (DescriptorSetLayoutBuilder()
  		.addSingleSamplerBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,	VK_SHADER_STAGE_FRAGMENT_BIT, &wd.defaultSampler.get())
  		.addSingleBinding		(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,			VK_SHADER_STAGE_FRAGMENT_BIT)
  		.addSingleBinding		(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,		VK_SHADER_STAGE_FRAGMENT_BIT)
  		.build(vk, device));
  
  	const Unique<VkDescriptorPool> descriptorPool (DescriptorPoolBuilder()
  		.addType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
  		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
  		.addType(VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT)
  		.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
  
  	const Unique<VkDescriptorSet> descriptorSet (makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
  
  	{
  		const VkDescriptorImageInfo		colorImageInfo	= makeDescriptorImageInfo(DE_NULL, *wd.colorImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
  		const VkDescriptorBufferInfo	bufferInfo		= makeDescriptorBufferInfo(*wd.colorBuffer, 0u, wd.colorBufferSize);
  
  		DescriptorSetUpdateBuilder	builder;
  
  		builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &colorImageInfo);
  		builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,		 &bufferInfo);
  
  		if (params.sampleSource == SAMPLE_SOURCE_SUBPASS_INPUT)
  			builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(2u), VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, &colorImageInfo);
  
  		builder.update(vk, device);
  	}
  
  	// Create a render pass and a framebuffer
  	{
  		std::vector<VkSubpassDescription>		subpasses;
  		std::vector<VkSubpassDependency>		subpassDependencies;
  		std::vector<VkImageView>				attachments;
  		std::vector<VkAttachmentDescription>	attachmentDescriptions;
  		std::vector<VkAttachmentReference>		attachmentReferences;
  
  		// Reserve capacity to avoid invalidating pointers to elements
  		attachmentReferences.reserve(2);	// color image + input attachment
  
  		// Create a MS draw subpass
  		{
  			attachments.push_back(*wd.colorImageView);
  
  			attachmentDescriptions.push_back(makeAttachmentDescription(
  				(VkAttachmentDescriptionFlags)0,								// VkAttachmentDescriptionFlags		flags;
  				params.colorFormat,												// VkFormat							format;
  				params.numColorSamples,											// VkSampleCountFlagBits			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_UNDEFINED,										// VkImageLayout					initialLayout;
  				VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL						// VkImageLayout					finalLayout;
  			));
  
  			attachmentReferences.push_back(makeAttachmentReference(static_cast<deUint32>(attachmentReferences.size()),	VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL));
  			const VkAttachmentReference* colorRef = &attachmentReferences.back();
  
  			const VkSubpassDescription subpassDescription =
  			{
  				(VkSubpassDescriptionFlags)0,						// VkSubpassDescriptionFlags       flags;
  				VK_PIPELINE_BIND_POINT_GRAPHICS,					// VkPipelineBindPoint             pipelineBindPoint;
  				0u,													// uint32_t                        inputAttachmentCount;
  				DE_NULL,											// const VkAttachmentReference*    pInputAttachments;
  				1u,													// uint32_t                        colorAttachmentCount;
  				colorRef,											// const VkAttachmentReference*    pColorAttachments;
  				DE_NULL,											// const VkAttachmentReference*    pResolveAttachments;
  				DE_NULL,											// const VkAttachmentReference*    pDepthStencilAttachment;
  				0u,													// uint32_t                        preserveAttachmentCount;
  				DE_NULL,											// const uint32_t*                 pPreserveAttachments;
  			};
  
  			subpasses.push_back(subpassDescription);
  		}
  
  		// Create a sampling subpass
  		{
  			attachmentReferences.push_back(makeAttachmentReference(0u, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL));
  			const VkAttachmentReference* inputRef = &attachmentReferences.back();
  
  			// No color attachment, side effects only
  			VkSubpassDescription subpassDescription =
  			{
  				(VkSubpassDescriptionFlags)0,						// VkSubpassDescriptionFlags       flags;
  				VK_PIPELINE_BIND_POINT_GRAPHICS,					// VkPipelineBindPoint             pipelineBindPoint;
  				1u,													// uint32_t                        inputAttachmentCount;
  				inputRef,											// const VkAttachmentReference*    pInputAttachments;
  				0u,													// uint32_t                        colorAttachmentCount;
  				DE_NULL,											// const VkAttachmentReference*    pColorAttachments;
  				DE_NULL,											// const VkAttachmentReference*    pResolveAttachments;
  				DE_NULL,											// const VkAttachmentReference*    pDepthStencilAttachment;
  				0u,													// uint32_t                        preserveAttachmentCount;
  				DE_NULL,											// const uint32_t*                 pPreserveAttachments;
  			};
  
  			subpasses.push_back(subpassDescription);
  		}
  
  		// Serialize the subpasses
  		{
  			const VkAccessFlags	dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
  											  | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT
  											  | VK_ACCESS_SHADER_WRITE_BIT;
  			const VkSubpassDependency	dependency	=
  			{
  				0u,																							// uint32_t                srcSubpass;
  				1u,																							// uint32_t                dstSubpass;
  				VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,		// VkPipelineStageFlags    srcStageMask;
  				VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,														// VkPipelineStageFlags    dstStageMask;
  				VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,														// VkAccessFlags           srcAccessMask;
  				dstAccessMask,																				// VkAccessFlags           dstAccessMask;
  				VK_DEPENDENCY_BY_REGION_BIT,																// VkDependencyFlags       dependencyFlags;
  			};
  			subpassDependencies.push_back(dependency);
  		}
  
  		VkRenderPassCreateInfo renderPassInfo =
  		{
  			VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,				// VkStructureType					sType;
  			DE_NULL,												// const void*						pNext;
  			(VkRenderPassCreateFlags)0,								// VkRenderPassCreateFlags			flags;
  			static_cast<deUint32>(attachmentDescriptions.size()),	// deUint32							attachmentCount;
  			dataOrNullPtr(attachmentDescriptions),					// const VkAttachmentDescription*	pAttachments;
  			static_cast<deUint32>(subpasses.size()),				// deUint32							subpassCount;
  			dataOrNullPtr(subpasses),								// const VkSubpassDescription*		pSubpasses;
  			static_cast<deUint32>(subpassDependencies.size()),		// deUint32							dependencyCount;
  			dataOrNullPtr(subpassDependencies),						// const VkSubpassDependency*		pDependencies;
  		};
  
  		renderPass  = createRenderPass(vk, device, &renderPassInfo);
  		framebuffer = makeFramebuffer (vk, device, *renderPass, static_cast<deUint32>(attachments.size()), dataOrNullPtr(attachments), params.renderSize.x(), params.renderSize.y());
  	}
  
  	const Unique<VkShaderModule>	vertexModuleDraw	(createShaderModule(vk, device, context.getBinaryCollection().get("vert"), 0u));
  	const Unique<VkShaderModule>	fragmentModuleDraw	(createShaderModule(vk, device, context.getBinaryCollection().get("frag"), 0u));
  
  	// Create pipelines for MS draw
  	const Unique<VkPipelineLayout>	pipelineLayout		(makePipelineLayout(vk, device, *descriptorSetLayout));
  	const Unique<VkPipeline>		pipelineDraw		(makeGraphicsPipeline(vk, device, *pipelineLayout, *renderPass, *vertexModuleDraw, *fragmentModuleDraw,
  																			  true/*use vertex attribs*/, getVertexInputColorFormat(params.colorFormat), true/*use color attach*/, 0u/*subpass*/,
  																			  params.renderSize, params.numColorSamples));
  
  	// Sampling pass is single-sampled, output to storage buffer
  	const Unique<VkShaderModule>	vertexModuleSample		(createShaderModule(vk, device, context.getBinaryCollection().get("vert_full"), 0u));
  	const Unique<VkShaderModule>	fragmentModuleSample	(createShaderModule(vk, device, context.getBinaryCollection().get("frag_fmask_fetch"), 0u));
  
  	// Sampling pipeline
  	const Unique<VkPipeline>		pipelineSample		(makeGraphicsPipeline(vk, device, *pipelineLayout, *renderPass, *vertexModuleSample, *fragmentModuleSample,
  																			  false/*use vertex attribs*/, VK_FORMAT_UNDEFINED, false/*no color output*/, 1u/*subpass*/,
  																			  params.renderSize, VK_SAMPLE_COUNT_1_BIT));
  
  	const Unique<VkCommandPool>		cmdPool		(createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, context.getUniversalQueueFamilyIndex()));
  	const Unique<VkCommandBuffer>	cmdBuffer	(makeCommandBuffer(vk, device, *cmdPool));
  
  	beginCommandBuffer(vk, *cmdBuffer);
  
  	{
  		// Generate clear values
  		std::vector<VkClearValue> clearValues = genClearValues(params.colorFormat, params.numLayers);
  
  		const VkRect2D renderArea =
  		{
  			{ 0u, 0u },
  			{ params.renderSize.x(), params.renderSize.y() }
  		};
  
  		const VkRenderPassBeginInfo renderPassBeginInfo =
  		{
  			VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,			// VkStructureType         sType;
  			DE_NULL,											// const void*             pNext;
  			*renderPass,										// VkRenderPass            renderPass;
  			*framebuffer,										// VkFramebuffer           framebuffer;
  			renderArea,											// VkRect2D                renderArea;
  			static_cast<deUint32>(clearValues.size()),			// uint32_t                clearValueCount;
  			dataOrNullPtr(clearValues),							// const VkClearValue*     pClearValues;
  		};
  		vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
  	}
  
  	vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
  
  	{
  		const VkDeviceSize vertexBufferOffset = 0ull;
  		vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &wd.vertexBuffer.get(), &vertexBufferOffset);
  	}
  
  	vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineDraw);
  	vk.cmdDraw(*cmdBuffer, wd.numVertices, 1u, 0u, 0u);
  
  	vk.cmdNextSubpass(*cmdBuffer, VK_SUBPASS_CONTENTS_INLINE);
  
  	vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipelineSample);
  	vk.cmdDraw(*cmdBuffer, 3u, 1u, 0u, 0u);	// fill the framebuffer, geometry defined in the VS
  
  	vk.cmdEndRenderPass(*cmdBuffer);
  
  	// Buffer write barrier
  	{
  		const VkBufferMemoryBarrier barrier =
  		{
  			VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,		// VkStructureType    sType;
  			DE_NULL,										// const void*        pNext;
  			VK_ACCESS_SHADER_WRITE_BIT,						// VkAccessFlags      srcAccessMask;
  			VK_ACCESS_HOST_READ_BIT,						// VkAccessFlags      dstAccessMask;
  			VK_QUEUE_FAMILY_IGNORED,						// uint32_t           srcQueueFamilyIndex;
  			VK_QUEUE_FAMILY_IGNORED,						// uint32_t           dstQueueFamilyIndex;
  			*wd.colorBuffer,								// VkBuffer           buffer;
  			0ull,											// VkDeviceSize       offset;
  			VK_WHOLE_SIZE,									// VkDeviceSize       size;
  		};
  
  		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0u, DE_NULL, 1u, &barrier, DE_NULL, 0u);
  	}
  
  	VK_CHECK(vk.endCommandBuffer(*cmdBuffer));
  	submitCommandsAndWait(vk, device, context.getUniversalQueue(), *cmdBuffer);
  
  	invalidateMappedMemoryRange(vk, device, wd.colorBufferAlloc->getMemory(), wd.colorBufferAlloc->getOffset(), VK_WHOLE_SIZE);
  }
  
  //! Only draw a multisampled image
  void draw (Context& context, const TestParams& params, WorkingData& wd)
  {
  	const DeviceInterface&	vk		= context.getDeviceInterface();
  	const VkDevice			device	= context.getDevice();
  
  	std::vector<ImageViewSp>	imageViews;
  	Move<VkRenderPass>			renderPass;
  	Move<VkFramebuffer>			framebuffer;
  
  	// Create color attachments
  	for (deUint32 layerNdx = 0u; layerNdx < params.numLayers; ++layerNdx)
  	{
  		imageViews.push_back(ImageViewSp(new Unique<VkImageView>(
  			makeImageView(vk, device, *wd.colorImage, VK_IMAGE_VIEW_TYPE_2D, params.colorFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, layerNdx, 1u)))));
  	}
  
  	// Create a render pass and a framebuffer
  	{
  		std::vector<VkSubpassDescription>		subpasses;
  		std::vector<VkImageView>				attachments;
  		std::vector<VkAttachmentDescription>	attachmentDescriptions;
  		std::vector<VkAttachmentReference>		attachmentReferences;
  
  		// Reserve capacity to avoid invalidating pointers to elements
  		attachmentReferences.reserve(params.numLayers);
  
  		// Create MS draw subpasses
  		for (deUint32 layerNdx = 0u; layerNdx < params.numLayers; ++layerNdx)
  		{
  			attachments.push_back(**imageViews[layerNdx]);
  
  			attachmentDescriptions.push_back(makeAttachmentDescription(
  				(VkAttachmentDescriptionFlags)0,								// VkAttachmentDescriptionFlags		flags;
  				params.colorFormat,												// VkFormat							format;
  				params.numColorSamples,											// VkSampleCountFlagBits			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_UNDEFINED,										// VkImageLayout					initialLayout;
  				VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL						// VkImageLayout					finalLayout;
  			));
  
  			attachmentReferences.push_back(makeAttachmentReference(static_cast<deUint32>(attachmentReferences.size()),	VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL));
  			const VkAttachmentReference* colorRef = &attachmentReferences.back();
  
  			const VkSubpassDescription subpassDescription =
  			{
  				(VkSubpassDescriptionFlags)0,						// VkSubpassDescriptionFlags       flags;
  				VK_PIPELINE_BIND_POINT_GRAPHICS,					// VkPipelineBindPoint             pipelineBindPoint;
  				0u,													// uint32_t                        inputAttachmentCount;
  				DE_NULL,											// const VkAttachmentReference*    pInputAttachments;
  				1u,													// uint32_t                        colorAttachmentCount;
  				colorRef,											// const VkAttachmentReference*    pColorAttachments;
  				DE_NULL,											// const VkAttachmentReference*    pResolveAttachments;
  				DE_NULL,											// const VkAttachmentReference*    pDepthStencilAttachment;
  				0u,													// uint32_t                        preserveAttachmentCount;
  				DE_NULL,											// const uint32_t*                 pPreserveAttachments;
  			};
  
  			subpasses.push_back(subpassDescription);
  		}
  
  		// All MS image drawing subpasses are independent
  		VkRenderPassCreateInfo renderPassInfo =
  		{
  			VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,				// VkStructureType					sType;
  			DE_NULL,												// const void*						pNext;
  			(VkRenderPassCreateFlags)0,								// VkRenderPassCreateFlags			flags;
  			static_cast<deUint32>(attachmentDescriptions.size()),	// deUint32							attachmentCount;
  			dataOrNullPtr(attachmentDescriptions),					// const VkAttachmentDescription*	pAttachments;
  			static_cast<deUint32>(subpasses.size()),				// deUint32							subpassCount;
  			dataOrNullPtr(subpasses),								// const VkSubpassDescription*		pSubpasses;
  			0u,														// deUint32							dependencyCount;
  			DE_NULL,												// const VkSubpassDependency*		pDependencies;
  		};
  
  		renderPass  = createRenderPass(vk, device, &renderPassInfo);
  		framebuffer = makeFramebuffer (vk, device, *renderPass, static_cast<deUint32>(attachments.size()), dataOrNullPtr(attachments), params.renderSize.x(), params.renderSize.y());
  	}
  
  	std::vector<PipelineSp>			pipelines;
  	const Unique<VkPipelineLayout>	pipelineLayout		(makePipelineLayout(vk, device));
  	const Unique<VkShaderModule>	vertexModuleDraw	(createShaderModule(vk, device, context.getBinaryCollection().get("vert"), 0u));
  	const Unique<VkShaderModule>	fragmentModuleDraw	(createShaderModule(vk, device, context.getBinaryCollection().get("frag"), 0u));
  
  	// Create pipelines for MS draw
  	for (deUint32 layerNdx = 0u; layerNdx < params.numLayers; ++layerNdx)
  	{
  		pipelines.push_back(PipelineSp(new Unique<VkPipeline>(
  			makeGraphicsPipeline(vk, device, *pipelineLayout, *renderPass, *vertexModuleDraw, *fragmentModuleDraw,
  								 true /*use vertex attribs*/, getVertexInputColorFormat(params.colorFormat), true/*use color attachment*/, layerNdx /*subpass*/,
  								 params.renderSize, params.numColorSamples))));
  	}
  
  	const Unique<VkCommandPool>		cmdPool		(createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, context.getUniversalQueueFamilyIndex()));
  	const Unique<VkCommandBuffer>	cmdBuffer	(makeCommandBuffer(vk, device, *cmdPool));
  
  	beginCommandBuffer(vk, *cmdBuffer);
  
  	{
  		// Generate clear values
  		std::vector<VkClearValue> clearValues = genClearValues(params.colorFormat, params.numLayers);
  
  		const VkRect2D renderArea =
  		{
  			{ 0u, 0u },
  			{ params.renderSize.x(), params.renderSize.y() }
  		};
  
  		const VkRenderPassBeginInfo renderPassBeginInfo =
  		{
  			VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,			// VkStructureType         sType;
  			DE_NULL,											// const void*             pNext;
  			*renderPass,										// VkRenderPass            renderPass;
  			*framebuffer,										// VkFramebuffer           framebuffer;
  			renderArea,											// VkRect2D                renderArea;
  			static_cast<deUint32>(clearValues.size()),			// uint32_t                clearValueCount;
  			dataOrNullPtr(clearValues),							// const VkClearValue*     pClearValues;
  		};
  		vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
  	}
  
  	{
  		const VkDeviceSize vertexBufferOffset = 0ull;
  		vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &wd.vertexBuffer.get(), &vertexBufferOffset);
  	}
  
  	for (deUint32 layerNdx = 0u; layerNdx < params.numLayers; ++layerNdx)
  	{
  		if (layerNdx != 0u)
  			vk.cmdNextSubpass(*cmdBuffer, VK_SUBPASS_CONTENTS_INLINE);
  
  		vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, **pipelines[layerNdx]);
  		vk.cmdDraw(*cmdBuffer, wd.numVertices, 1u, 0u, layerNdx);	// pass instance index to slightly change geometry per layer
  	}
  
  	vk.cmdEndRenderPass(*cmdBuffer);
  
  	VK_CHECK(vk.endCommandBuffer(*cmdBuffer));
  	submitCommandsAndWait(vk, device, context.getUniversalQueue(), *cmdBuffer);
  }
  
  //! Sample from an image in a compute shader, storing the result in a color buffer
  void dispatchSampleImage (Context& context, const TestParams& params, WorkingData& wd, const std::string& shaderName)
  {
  	const DeviceInterface&	vk		= context.getDeviceInterface();
  	const VkDevice			device	= context.getDevice();
  
  	// Create descriptor set
  
  	const Unique<VkDescriptorSetLayout> descriptorSetLayout(
  		DescriptorSetLayoutBuilder()
  		.addSingleSamplerBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,	VK_SHADER_STAGE_COMPUTE_BIT, &wd.defaultSampler.get())
  		.addSingleBinding		(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,			VK_SHADER_STAGE_COMPUTE_BIT)
  		.build(vk, device));
  
  	const Unique<VkDescriptorPool> descriptorPool(
  		DescriptorPoolBuilder()
  		.addType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
  		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
  		.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
  
  	const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
  
  	{
  		const VkDescriptorImageInfo		colorImageInfo		= makeDescriptorImageInfo(DE_NULL, *wd.colorImageView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
  		const VkDescriptorBufferInfo	resultBufferInfo	= makeDescriptorBufferInfo(*wd.colorBuffer, 0ull, wd.colorBufferSize);
  
  		DescriptorSetUpdateBuilder	builder;
  
  		builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &colorImageInfo);
  		builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,		  &resultBufferInfo);
  
  		builder.update(vk, device);
  	}
  
  	// Pipeline
  
  	const Unique<VkShaderModule>	shaderModule	(createShaderModule(vk, device, context.getBinaryCollection().get(shaderName), 0u));
  	const Unique<VkPipelineLayout>	pipelineLayout	(makePipelineLayout(vk, device, *descriptorSetLayout));
  	const Unique<VkPipeline>		pipeline		(makeComputePipeline(vk, device, *pipelineLayout, *shaderModule, DE_NULL));
  
  	const Unique<VkCommandPool>		cmdPool		(createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, context.getUniversalQueueFamilyIndex()));
  	const Unique<VkCommandBuffer>	cmdBuffer	(makeCommandBuffer(vk, device, *cmdPool));
  
  	beginCommandBuffer(vk, *cmdBuffer);
  
  	vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline);
  	vk.cmdBindDescriptorSets(*cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, *pipelineLayout, 0u, 1u, &descriptorSet.get(), 0u, DE_NULL);
  
  	vk.cmdDispatch(*cmdBuffer, params.renderSize.x(), params.renderSize.y(), params.numLayers);
  
  	{
  		const VkBufferMemoryBarrier barrier =
  		{
  			VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,		// VkStructureType    sType;
  			DE_NULL,										// const void*        pNext;
  			VK_ACCESS_SHADER_WRITE_BIT,						// VkAccessFlags      srcAccessMask;
  			VK_ACCESS_HOST_READ_BIT,						// VkAccessFlags      dstAccessMask;
  			VK_QUEUE_FAMILY_IGNORED,						// uint32_t           srcQueueFamilyIndex;
  			VK_QUEUE_FAMILY_IGNORED,						// uint32_t           dstQueueFamilyIndex;
  			*wd.colorBuffer,								// VkBuffer           buffer;
  			0ull,											// VkDeviceSize       offset;
  			VK_WHOLE_SIZE,									// VkDeviceSize       size;
  		};
  
  		vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0, 0,
  			(const VkMemoryBarrier*)DE_NULL, 1u, &barrier, 0u, (const VkImageMemoryBarrier*)DE_NULL);
  	}
  
  	VK_CHECK(vk.endCommandBuffer(*cmdBuffer));
  	submitCommandsAndWait(vk, device, context.getUniversalQueue(), *cmdBuffer);
  
  	invalidateMappedMemoryRange(vk, device, wd.colorBufferAlloc->getMemory(), wd.colorBufferAlloc->getOffset(), VK_WHOLE_SIZE);
  }
  
  //! Get a single-sampled image access from a multisampled color buffer with samples packed per pixel
  tcu::ConstPixelBufferAccess getSingleSampledAccess (const void* const imageData, const TestParams& params, const deUint32 sampleNdx, const deUint32 layerNdx)
  {
  	const deUint32		numSamples	= static_cast<deUint32>(params.numColorSamples);
  	const deUint32		pixelSize	= tcu::getPixelSize(mapVkFormat(params.colorFormat));
  	const deUint32		rowSize		= pixelSize * params.renderSize.x();
  	const deUint32		layerSize	= rowSize * params.renderSize.y();
  	const deUint8*		src			= static_cast<const deUint8*>(imageData)
  									+ (layerNdx * numSamples * layerSize)
  									+ (sampleNdx * pixelSize);
  	const tcu::IVec3	size		(params.renderSize.x(), params.renderSize.y(), 1);
  	const tcu::IVec3	pitch		(numSamples * pixelSize,
  									 numSamples * rowSize,
  									 numSamples * layerSize);
  	return tcu::ConstPixelBufferAccess(mapVkFormat(params.colorFormat), size, pitch, src);
  }
  
  tcu::TestStatus test (Context& context, const TestParams params)
  {
  	WorkingData				wd;
  	const DeviceInterface&	vk		  = context.getDeviceInterface();
  	const VkDevice			device	  = context.getDevice();
  	MovePtr<Allocator>		allocator = MovePtr<Allocator>(new SimpleAllocator(vk, device, getPhysicalDeviceMemoryProperties(context.getInstanceInterface(), context.getPhysicalDevice())));
  
  	// Initialize resources
  	{
  		const VkImageUsageFlags	msImageUsage	= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
  												| VK_IMAGE_USAGE_SAMPLED_BIT
  												| (params.sampleSource == SAMPLE_SOURCE_SUBPASS_INPUT ? VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT : (VkImageUsageFlagBits)0);
  		wd.colorImage		= makeImage(vk, device, params.colorFormat, params.renderSize, params.numLayers, params.numColorSamples, msImageUsage);
  		wd.colorImageAlloc	= bindImage(vk, device, *allocator, *wd.colorImage, MemoryRequirement::Any);
  		wd.colorImageView	= makeImageView(vk, device, *wd.colorImage, (params.numLayers == 1u ? VK_IMAGE_VIEW_TYPE_2D : VK_IMAGE_VIEW_TYPE_2D_ARRAY), params.colorFormat,
  											makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, params.numLayers));
  
  		wd.defaultSampler	= makeSampler(vk, device);
  
  		// Color buffer is meant to hold data for all layers and all samples of the image.
  		// Data is tightly packed layer by layer, for each pixel all samples are laid out together starting with sample 0.
  		// E.g.: pixel(0,0)sample(0)sample(1), pixel(1,0)sample(0)sample(1), ...
  		wd.colorBufferSize	= static_cast<VkDeviceSize>(tcu::getPixelSize(mapVkFormat(params.colorFormat))
  														* params.renderSize.x() * params.renderSize.y() * params.numLayers * static_cast<deUint32>(params.numColorSamples));
  		wd.colorBuffer		= makeBuffer(vk, device, wd.colorBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
  		wd.colorBufferAlloc	= bindBuffer(vk, device, *allocator, *wd.colorBuffer, MemoryRequirement::HostVisible);
  
  		deMemset(wd.colorBufferAlloc->getHostPtr(), 0, static_cast<std::size_t>(wd.colorBufferSize));
  		flushMappedMemoryRange(vk, device, wd.colorBufferAlloc->getMemory(), wd.colorBufferAlloc->getOffset(), VK_WHOLE_SIZE);
  
  		const std::vector<PositionColor>	vertices			= genShapes(params.colorFormat);
  		const VkDeviceSize					vertexBufferSize	= static_cast<VkDeviceSize>(sizeof(vertices[0]) * vertices.size());
  
  		wd.numVertices			= static_cast<deUint32>(vertices.size());
  		wd.vertexBuffer			= makeBuffer(vk, device, vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
  		wd.vertexBufferAlloc	= bindBuffer(vk, device, *allocator, *wd.vertexBuffer, MemoryRequirement::HostVisible);
  
  		deMemcpy(wd.vertexBufferAlloc->getHostPtr(), dataOrNullPtr(vertices), static_cast<std::size_t>(vertexBufferSize));
  		flushMappedMemoryRange(vk, device, wd.vertexBufferAlloc->getMemory(), wd.vertexBufferAlloc->getOffset(), VK_WHOLE_SIZE);
  	}
  
  	if (params.sampleSource == SAMPLE_SOURCE_SUBPASS_INPUT)
  	{
  		// Create a multisample image and sample from it
  		drawAndSampleInputAttachment (context, params, wd);
  	}
  	else
  	{
  		// Draw the image, then sample from it in a CS
  		draw				(context, params, wd);
  		dispatchSampleImage (context, params, wd, "comp_fmask_fetch");
  	}
  
  	// Copy the result
  	std::vector<deUint8> fmaskFetchColorBuffer (static_cast<deUint32>(wd.colorBufferSize));
  	deMemcpy(&fmaskFetchColorBuffer[0], wd.colorBufferAlloc->getHostPtr(), static_cast<std::size_t>(wd.colorBufferSize));
  
  	// Clear the color buffer, just to be sure we're getting the new data
  	deMemset(wd.colorBufferAlloc->getHostPtr(), 0, static_cast<std::size_t>(wd.colorBufferSize));
  	flushMappedMemoryRange(vk, device, wd.colorBufferAlloc->getMemory(), wd.colorBufferAlloc->getOffset(), VK_WHOLE_SIZE);
  
  	// Sample image using the standard texel fetch
  	dispatchSampleImage (context, params, wd, "comp_fetch");
  
  	// Verify the images
  	{
  		const void* const fmaskResult	 = dataOrNullPtr(fmaskFetchColorBuffer);
  		const void* const expectedResult = wd.colorBufferAlloc->getHostPtr();
  
  		DE_ASSERT(!isFloatFormat(params.colorFormat));	// we're using int compare
  
  		// Mismatch, do image compare to pinpoint the failure
  		for (deUint32 layerNdx  = 0u; layerNdx  < params.numLayers;								 ++layerNdx)
  		for (deUint32 sampleNdx = 0u; sampleNdx < static_cast<deUint32>(params.numColorSamples); ++sampleNdx)
  		{
  			const std::string					imageName	= "layer_" + de::toString(layerNdx) + "_sample_" + de::toString(sampleNdx);
  			const std::string					imageDesc	= "Layer " + de::toString(layerNdx) + " Sample " + de::toString(sampleNdx);
  			const tcu::ConstPixelBufferAccess	expected	= getSingleSampledAccess(expectedResult, params, sampleNdx, layerNdx);
  			const tcu::ConstPixelBufferAccess	actual		= getSingleSampledAccess(fmaskResult,	 params, sampleNdx, layerNdx);
  			const UVec4							threshold	(0);	// should match exactly
  
  			const bool ok = tcu::intThresholdCompare(context.getTestContext().getLog(), imageName.c_str(), imageDesc.c_str(),
  													 expected, actual, threshold, tcu::COMPARE_LOG_RESULT);
  
  			if (!ok)
  				return tcu::TestStatus::fail("Some texels were incorrect");
  		}
  	}
  
  	return tcu::TestStatus::pass("Pass");
  }
  
  std::string getFormatShortString (const VkFormat format)
  {
  	std::string s(de::toLower(getFormatName(format)));
  	return s.substr(10);
  }
  
  void createShaderFragmentMaskTestsInGroup (tcu::TestCaseGroup* rootGroup)
  {
  	// Per spec, the following formats must support color attachment and sampled image
  	const VkFormat colorFormats[] =
  	{
  		VK_FORMAT_R8G8B8A8_UNORM,
  		VK_FORMAT_R32_UINT,
  		VK_FORMAT_R32_SINT,
  	};
  
  	const VkSampleCountFlagBits	sampleCounts[] =
  	{
  		VK_SAMPLE_COUNT_2_BIT,
  		VK_SAMPLE_COUNT_4_BIT,
  		VK_SAMPLE_COUNT_8_BIT,
  		VK_SAMPLE_COUNT_16_BIT,
  	};
  
  	const struct SourceCase
  	{
  		const char*			name;
  		deUint32			numLayers;
  		SampleSource		sampleSource;
  	} sourceCases[] =
  	{
  		{ "image_2d",		1u,	SAMPLE_SOURCE_IMAGE			},
  		{ "image_2d_array",	3u,	SAMPLE_SOURCE_IMAGE			},
  		{ "subpass_input",	1u,	SAMPLE_SOURCE_SUBPASS_INPUT	},
  	};
  
  	// Test 1: Compare fragments fetched via FMASK and an ordinary texel fetch
  	{
  		for (const VkSampleCountFlagBits* pSampleCount = sampleCounts; pSampleCount != DE_ARRAY_END(sampleCounts); ++pSampleCount)
  		{
  			MovePtr<tcu::TestCaseGroup> sampleCountGroup (new tcu::TestCaseGroup(rootGroup->getTestContext(), ("samples_" + de::toString(*pSampleCount)).c_str(), ""));
  			for (const SourceCase* pSourceCase = sourceCases; pSourceCase != DE_ARRAY_END(sourceCases); ++pSourceCase)
  			{
  				MovePtr<tcu::TestCaseGroup> sourceGroup (new tcu::TestCaseGroup(rootGroup->getTestContext(), pSourceCase->name, ""));
  				for (const VkFormat* pColorFormat = colorFormats; pColorFormat != DE_ARRAY_END(colorFormats); ++pColorFormat)
  				{
  					TestParams params;
  					params.renderSize		= UVec2(32, 32);
  					params.colorFormat		= *pColorFormat;
  					params.numColorSamples	= *pSampleCount;
  					params.numLayers		= pSourceCase->numLayers;
  					params.sampleSource		= pSourceCase->sampleSource;
  
  					addFunctionCaseWithPrograms(sourceGroup.get(), getFormatShortString(*pColorFormat), "", checkRequirements, initPrograms, test, params);
  				}
  				sampleCountGroup->addChild(sourceGroup.release());
  			}
  			rootGroup->addChild(sampleCountGroup.release());
  		}
  	}
  }
  
  } // anonymous ns
  
  tcu::TestCaseGroup* createMultisampleShaderFragmentMaskTests (tcu::TestContext& testCtx)
  {
  	return createTestGroup(testCtx, "shader_fragment_mask", "Access raw texel values in a compressed MSAA surface", createShaderFragmentMaskTestsInGroup);
  }
  
  } // pipeline
  } // vkt
  