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_mixed_attachment_samples
   *//*--------------------------------------------------------------------*/
  
  #include "vktPipelineMultisampleMixedAttachmentSamplesTests.hpp"
  #include "vktPipelineSampleLocationsUtil.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 "deMath.h"
  
  #include "tcuVector.hpp"
  #include "tcuTestLog.hpp"
  #include "tcuImageCompare.hpp"
  #include "tcuTextureUtil.hpp"
  #include "tcuRGBA.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::Vec2;
  using tcu::Vec4;
  
  bool compareGreenImage (tcu::TestLog& log, const char* name, const char* description, const tcu::ConstPixelBufferAccess& image)
  {
  	tcu::TextureLevel greenImage(image.getFormat(), image.getWidth(), image.getHeight());
  	tcu::clear(greenImage.getAccess(), tcu::RGBA::green().toIVec());
  	return tcu::intThresholdCompare(log, name, description, greenImage.getAccess(), image, tcu::UVec4(2u), tcu::COMPARE_LOG_RESULT);
  }
  
  VkImageAspectFlags getImageAspectFlags (const VkFormat format)
  {
  	const tcu::TextureFormat tcuFormat = mapVkFormat(format);
  
  	if      (tcuFormat.order == tcu::TextureFormat::DS)		return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
  	else if (tcuFormat.order == tcu::TextureFormat::D)		return VK_IMAGE_ASPECT_DEPTH_BIT;
  	else if (tcuFormat.order == tcu::TextureFormat::S)		return VK_IMAGE_ASPECT_STENCIL_BIT;
  
  	DE_ASSERT(false);
  	return 0u;
  }
  
  struct CompareData
  {
  	Vec4		color;
  	float		depth;
  	deUint32	stencil;
  
  	// Pad to 2*16 bytes, in the shader the base alignment of this structure is 16 due to vec4
  	deUint32	padding[2];
  
  	CompareData() : color(Vec4(0.0f)), depth(0.0f), stencil(0u)
  	{
  		padding[0] = 0u;
  		padding[1] = 0u;
  
  		static_assert(sizeof(CompareData) == (2 * 16), "Wrong structure size, expected 16 bytes");
  	}
  };
  
  //! 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 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;
  		1u,												// 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);
  }
  
  inline bool isDepthFormat (const VkFormat format)
  {
  	return (getImageAspectFlags(format) & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
  }
  
  inline bool isStencilFormat (const VkFormat format)
  {
  	return (getImageAspectFlags(format) & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;
  }
  
  //! 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 deUint32							subpassNdx,
  									   const UVec2&								renderSize,
  									   const VkImageAspectFlags					depthStencilAspect,	//!< Used to determine which D/S tests to turn on
  									   const VkSampleCountFlagBits				numSamples,
  									   const bool								sampleShadingEnable,
  									   const bool								useFragmentShadingRate,
  									   const VkSampleLocationsInfoEXT*			pSampleLocationsInfo = DE_NULL)
  {
  	std::vector<VkVertexInputBindingDescription>	vertexInputBindingDescriptions;
  	std::vector<VkVertexInputAttributeDescription>	vertexInputAttributeDescriptions;
  
  	// Vertex attributes: position and color
  	if (useVertexInput)
  	{
  		vertexInputBindingDescriptions.push_back  (makeVertexInputBindingDescription  (0u, 2 * sizeof(Vec4), VK_VERTEX_INPUT_RATE_VERTEX));
  		vertexInputAttributeDescriptions.push_back(makeVertexInputAttributeDescription(0u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, 0u));
  		vertexInputAttributeDescriptions.push_back(makeVertexInputAttributeDescription(1u, 0u, VK_FORMAT_R32G32B32A32_SFLOAT, 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;
  	};
  
  	VkPipelineSampleLocationsStateCreateInfoEXT pipelineSampleLocationsCreateInfo =
  	{
  		VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT,	// VkStructureType             sType;
  		DE_NULL,															// const void*                 pNext;
  		VK_TRUE,															// VkBool32                    sampleLocationsEnable;
  		VkSampleLocationsInfoEXT(),											// VkSampleLocationsInfoEXT    sampleLocationsInfo;
  	};
  
  	VkPipelineMultisampleStateCreateInfo pipelineMultisampleStateInfo =
  	{
  		VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,	// VkStructureType							sType;
  		DE_NULL,													// const void*								pNext;
  		(VkPipelineMultisampleStateCreateFlags)0,					// VkPipelineMultisampleStateCreateFlags	flags;
  		numSamples,													// VkSampleCountFlagBits					rasterizationSamples;
  		sampleShadingEnable,										// VkBool32									sampleShadingEnable;
  		1.0f,														// float									minSampleShading;
  		DE_NULL,													// const VkSampleMask*						pSampleMask;
  		VK_FALSE,													// VkBool32									alphaToCoverageEnable;
  		VK_FALSE													// VkBool32									alphaToOneEnable;
  	};
  
  	if (pSampleLocationsInfo)
  	{
  		pipelineSampleLocationsCreateInfo.sampleLocationsInfo	= *pSampleLocationsInfo;
  		pipelineMultisampleStateInfo.pNext						= &pipelineSampleLocationsCreateInfo;
  	}
  
  	// Simply increment the buffer
  	const VkStencilOpState stencilOpState = makeStencilOpState(
  		VK_STENCIL_OP_KEEP,						// stencil fail
  		VK_STENCIL_OP_INCREMENT_AND_CLAMP,		// depth & stencil pass
  		VK_STENCIL_OP_KEEP,						// depth only fail
  		VK_COMPARE_OP_ALWAYS,					// compare op
  		~0u,									// compare mask
  		~0u,									// write mask
  		0u);									// reference
  
  	// Always pass the depth test
  	VkPipelineDepthStencilStateCreateInfo pipelineDepthStencilStateInfo =
  	{
  		VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,	// VkStructureType							sType;
  		DE_NULL,													// const void*								pNext;
  		(VkPipelineDepthStencilStateCreateFlags)0,					// VkPipelineDepthStencilStateCreateFlags	flags;
  		(depthStencilAspect & VK_IMAGE_ASPECT_DEPTH_BIT) != 0u,		// VkBool32									depthTestEnable;
  		VK_TRUE,													// VkBool32									depthWriteEnable;
  		VK_COMPARE_OP_ALWAYS,										// VkCompareOp								depthCompareOp;
  		VK_FALSE,													// VkBool32									depthBoundsTestEnable;
  		(depthStencilAspect & VK_IMAGE_ASPECT_STENCIL_BIT) != 0u,	// VkBool32									stencilTestEnable;
  		stencilOpState,												// VkStencilOpState							front;
  		stencilOpState,												// 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;
  		1u,															// 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;
  		}
  	};
  
  	VkPipelineFragmentShadingRateStateCreateInfoKHR shadingRateStateCreateInfo
  	{
  		VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR,								// VkStructureType						sType;
  		DE_NULL,																							// const void*							pNext;
  		{ 2, 2 },																							// VkExtent2D							fragmentSize;
  		{ VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR, VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR },	// VkFragmentShadingRateCombinerOpKHR	combinerOps[2];
  	};
  
  	const VkGraphicsPipelineCreateInfo graphicsPipelineInfo =
  	{
  		VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,					// VkStructureType									sType;
  		useFragmentShadingRate ? &shadingRateStateCreateInfo : 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);
  }
  
  //! Wrap float after an increment
  inline float wrapIncFloat (float a, float min, float max)
  {
  	return deFloatMax(min, deFloatMod(a, max));
  }
  
  //! Generate expected data for color, depth, and stencil samples of a given image.
  //! Samples are ordered starting at pixel (0, 0) - see compute shader source for reference.
  std::vector<CompareData> generateCompareData (const deUint32	seed,
  											  const UVec2&		imageSize,
  											  const deUint32	numCoverageSamples,
  											  const deUint32	numColorSamples,
  											  const deUint32	numDepthStencilSamples)
  {
  	std::vector<CompareData>	allData;
  	de::Random					rng (seed);
  
  	for (deUint32 y		 = 0u; y	  < imageSize.y();		++y)
  	for (deUint32 x		 = 0u; x	  < imageSize.x();		++x)
  	for (deUint32 sample = 0u; sample < numCoverageSamples; ++sample)
  	{
  		CompareData cd;
  
  		if (sample < numColorSamples)
  		{
  			for (int i = 0; i < 3; ++i)
  				cd.color[i]	= 0.1f * static_cast<float>(rng.getInt(1, 10));
  
  			cd.color.w() = 1.0f;
  		}
  
  		if (sample < numDepthStencilSamples)
  		{
  			const deUint32 globalSample = sample + numColorSamples * (x + imageSize.x() * y);
  			cd.depth	= wrapIncFloat(0.05f * static_cast<float>(1 + globalSample), 0.05f, 1.0f);
  			cd.stencil	= 1 + globalSample % numCoverageSamples;
  		}
  
  		allData.push_back(cd);
  	}
  
  	return allData;
  }
  
  //! NDC transformation algorithm for sample locations
  template<typename SampleAccessor>
  std::vector<Vec2> ndcTransformEachSampleInPixel (const UVec2& framebufferSize, const deUint32 numSamplesPerPixel, const SampleAccessor& access)
  {
  	std::vector<Vec2> locations;
  
  	for (deUint32 y			= 0; y			< framebufferSize.y();	++y)
  	for (deUint32 x			= 0; x			< framebufferSize.x();	++x)
  	for (deUint32 sampleNdx	= 0; sampleNdx	< numSamplesPerPixel;	++sampleNdx)
  	{
  		const Vec2& sp = access(x, y, sampleNdx);
  		const float	globalX  = sp.x() + static_cast<float>(x);
  		const float	globalY  = sp.y() + static_cast<float>(y);
  
  		// Transform to [-1, 1] space
  		locations.push_back(Vec2(-1.0f + 2.0f * (globalX / static_cast<float>(framebufferSize.x())),
  								 -1.0f + 2.0f * (globalY / static_cast<float>(framebufferSize.y()))));
  	}
  
  	return locations;
  }
  
  class AccessStandardSampleLocationsArray
  {
  public:
  	AccessStandardSampleLocationsArray (const Vec2* ptr) : m_pData (ptr) {}
  
  	const Vec2& operator ()(const deUint32 x, const deUint32 y, const deUint32 sampleNdx) const
  	{
  		DE_UNREF(x);
  		DE_UNREF(y);
  		return m_pData[sampleNdx];
  	}
  
  private:
  	const Vec2*	m_pData;
  };
  
  class AccessMultisamplePixelGrid
  {
  public:
  	AccessMultisamplePixelGrid (const MultisamplePixelGrid* ptr) : m_pGrid (ptr) {}
  
  	Vec2 operator ()(const deUint32 x, const deUint32 y, const deUint32 sampleNdx) const
  	{
  		const VkSampleLocationEXT& sp = m_pGrid->getSample(x, y, sampleNdx);
  		return Vec2(sp.x, sp.y);
  	}
  
  private:
  	const MultisamplePixelGrid*	m_pGrid;
  };
  
  //! Generate NDC space standard sample locations at each framebuffer pixel
  //! Data is filled starting at pixel (0,0) and for each pixel there are numSamples samples
  std::vector<Vec2> genFramebufferStandardSampleLocations (const VkSampleCountFlagBits numSamples, const UVec2& framebufferSize)
  {
  	static const Vec2 s_location_samples_1[] =
  	{
  		Vec2(0.5f, 0.5f),
  	};
  	static const Vec2 s_location_samples_2[] =
  	{
  		Vec2(0.75f, 0.75f),
  		Vec2(0.25f, 0.25f),
  	};
  	static const Vec2 s_location_samples_4[] =
  	{
  		Vec2(0.375f, 0.125f),
  		Vec2(0.875f, 0.375f),
  		Vec2(0.125f, 0.625f),
  		Vec2(0.625f, 0.875f),
  	};
  	static const Vec2 s_location_samples_8[] =
  	{
  		Vec2(0.5625f, 0.3125f),
  		Vec2(0.4375f, 0.6875f),
  		Vec2(0.8125f, 0.5625f),
  		Vec2(0.3125f, 0.1875f),
  		Vec2(0.1875f, 0.8125f),
  		Vec2(0.0625f, 0.4375f),
  		Vec2(0.6875f, 0.9375f),
  		Vec2(0.9375f, 0.0625f),
  	};
  	static const Vec2 s_location_samples_16[] =
  	{
  		Vec2(0.5625f, 0.5625f),
  		Vec2(0.4375f, 0.3125f),
  		Vec2(0.3125f, 0.6250f),
  		Vec2(0.7500f, 0.4375f),
  		Vec2(0.1875f, 0.3750f),
  		Vec2(0.6250f, 0.8125f),
  		Vec2(0.8125f, 0.6875f),
  		Vec2(0.6875f, 0.1875f),
  		Vec2(0.3750f, 0.8750f),
  		Vec2(0.5000f, 0.0625f),
  		Vec2(0.2500f, 0.1250f),
  		Vec2(0.1250f, 0.7500f),
  		Vec2(0.0000f, 0.5000f),
  		Vec2(0.9375f, 0.2500f),
  		Vec2(0.8750f, 0.9375f),
  		Vec2(0.0625f, 0.0000f),
  	};
  
  	const Vec2*	pSampleLocation = DE_NULL;
  
  	switch (numSamples)
  	{
  		case VK_SAMPLE_COUNT_1_BIT:		pSampleLocation = s_location_samples_1;		break;
  		case VK_SAMPLE_COUNT_2_BIT:		pSampleLocation = s_location_samples_2;		break;
  		case VK_SAMPLE_COUNT_4_BIT:		pSampleLocation = s_location_samples_4;		break;
  		case VK_SAMPLE_COUNT_8_BIT:		pSampleLocation = s_location_samples_8;		break;
  		case VK_SAMPLE_COUNT_16_BIT:	pSampleLocation = s_location_samples_16;	break;
  
  		default:
  			DE_ASSERT(0);
  			return std::vector<Vec2>();
  	}
  
  	return ndcTransformEachSampleInPixel(framebufferSize, static_cast<deUint32>(numSamples), AccessStandardSampleLocationsArray(pSampleLocation));
  }
  
  //! Generate NDC space custom sample locations at each framebuffer pixel, based on the given pixel grid
  std::vector<Vec2> getSampleLocations (const MultisamplePixelGrid& pixelGrid, const UVec2& framebufferSize)
  {
  	return ndcTransformEachSampleInPixel(framebufferSize, pixelGrid.samplesPerPixel(), AccessMultisamplePixelGrid(&pixelGrid));
  }
  
  struct PositionColor
  {
  	tcu::Vec4	position;
  	tcu::Vec4	color;
  
  	PositionColor (const tcu::Vec4& pos, const tcu::Vec4& col) : position(pos), color(col) {}
  };
  
  //! Generate subpixel triangles containing the sample position, based on compare data.
  //! Stencil values are created by overlapping triangles, so the stencil pipeline state must be set up accordingly.
  std::vector<PositionColor> generateSubpixelTriangles (const UVec2&						renderSize,
  													  const std::vector<CompareData>&	compareData,
  													  const std::vector<Vec2>&			sampleLocations)
  {
  	std::vector<PositionColor>	vertices;
  
  	// For each sample location (in the whole framebuffer), create a sub-pixel triangle that contains it.
  	// NDC viewport size is 2.0 in X and Y and NDC pixel width/height depends on the framebuffer resolution.
  	const Vec2			pixelSize	= Vec2(2.0f) / renderSize.cast<float>();
  	const Vec2			offset		= pixelSize / 16.0f;	// 4 bits precision
  
  	// Surround with a roughly centered triangle
  	const float y1 = 0.5f  * offset.y();
  	const float y2 = 0.35f * offset.y();
  	const float x1 = 0.5f  * offset.x();
  
  	DE_ASSERT(compareData.size() == sampleLocations.size());
  
  	for (std::size_t globalSampleNdx = 0; globalSampleNdx < sampleLocations.size(); ++globalSampleNdx)
  	{
  		const Vec2&			loc	= sampleLocations[globalSampleNdx];
  		const CompareData&	cd	= compareData	 [globalSampleNdx];
  
  		// Overdraw at the same position to get the desired stencil
  		// Draw at least once, if stencil is 0
  		for (deUint32 i = 0; i < deMaxu32(1u, cd.stencil); ++i)
  		{
  			vertices.push_back(PositionColor(Vec4(loc.x(),      loc.y() - y1, cd.depth, 1.0f), cd.color));
  			vertices.push_back(PositionColor(Vec4(loc.x() - x1, loc.y() + y2, cd.depth, 1.0f), cd.color));
  			vertices.push_back(PositionColor(Vec4(loc.x() + x1, loc.y() + y2, cd.depth, 1.0f), cd.color));
  		}
  	}
  
  	return vertices;
  }
  
  void reportSampleError (tcu::TestLog& log, const std::string& sampleDesc, UVec2& renderSize, const deUint32 numCoverageSamples, const deUint32 globalSampleNdx)
  {
  	const deUint32 pixelNdx	= globalSampleNdx / numCoverageSamples;
  	const deUint32 x		= pixelNdx % renderSize.x();
  	const deUint32 y		= pixelNdx / renderSize.x();
  	const deUint32 sample	= globalSampleNdx % numCoverageSamples;
  
  	log << tcu::TestLog::Message << "Incorrect " << sampleDesc << " sample (" << sample << ") at pixel (" << x << ", " << y << ")" << tcu::TestLog::EndMessage;
  }
  
  void checkSampleRequirements (Context&						context,
  							  const VkSampleCountFlagBits	numColorSamples,
  							  const VkSampleCountFlagBits	numDepthStencilSamples,
  							  const bool					requireStandardSampleLocations)
  {
  	const VkPhysicalDeviceLimits& limits = context.getDeviceProperties().limits;
  
  	if ((limits.framebufferColorSampleCounts & numColorSamples) == 0u)
  		TCU_THROW(NotSupportedError, "framebufferColorSampleCounts: sample count not supported");
  
  	if ((limits.framebufferDepthSampleCounts & numDepthStencilSamples) == 0u)
  		TCU_THROW(NotSupportedError, "framebufferDepthSampleCounts: sample count not supported");
  
  	if ((limits.framebufferStencilSampleCounts & numDepthStencilSamples) == 0u)
  		TCU_THROW(NotSupportedError, "framebufferStencilSampleCounts: sample count not supported");
  
  	if ((limits.sampledImageColorSampleCounts & numColorSamples) == 0u)
  		TCU_THROW(NotSupportedError, "sampledImageColorSampleCounts: sample count not supported");
  
  	if ((limits.sampledImageDepthSampleCounts & numDepthStencilSamples) == 0u)
  		TCU_THROW(NotSupportedError, "sampledImageDepthSampleCounts: sample count not supported");
  
  	if ((limits.sampledImageStencilSampleCounts & numDepthStencilSamples) == 0u)
  		TCU_THROW(NotSupportedError, "sampledImageStencilSampleCounts: sample count not supported");
  
  	// This is required to output geometry that is covering a specific sample
  	if (requireStandardSampleLocations && !limits.standardSampleLocations)
  		TCU_THROW(NotSupportedError, "standardSampleLocations: not supported");
  }
  
  void checkImageRequirements (Context&						context,
  							 const VkFormat					format,
  							 const VkFormatFeatureFlags		requiredFeatureFlags,
  							 const VkImageUsageFlags		requiredUsageFlags,
  							 const VkSampleCountFlagBits	requiredSampleCount = VK_SAMPLE_COUNT_1_BIT)
  {
  	const InstanceInterface&	vki				= context.getInstanceInterface();
  	const VkPhysicalDevice		physicalDevice	= context.getPhysicalDevice();
  	VkImageFormatProperties		imageProperties;
  
  	const VkFormatProperties formatProperties = getPhysicalDeviceFormatProperties(vki, physicalDevice, format);
  
  	if ((formatProperties.optimalTilingFeatures & requiredFeatureFlags) != requiredFeatureFlags)
  		TCU_THROW(NotSupportedError, (de::toString(format) + ": format features not supported").c_str());
  
  	const VkResult result = vki.getPhysicalDeviceImageFormatProperties(physicalDevice, format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_OPTIMAL, requiredUsageFlags, (VkImageCreateFlags)0, &imageProperties);
  
  	if (result == VK_ERROR_FORMAT_NOT_SUPPORTED)
  		TCU_THROW(NotSupportedError, (de::toString(format) + ": format not supported").c_str());
  
  	if ((imageProperties.sampleCounts & requiredSampleCount) != requiredSampleCount)
  		TCU_THROW(NotSupportedError, (de::toString(format) + ": sample count not supported").c_str());
  }
  
  //! Used after a render pass color output (draw or resolve)
  void recordCopyOutputImageToBuffer (const DeviceInterface&	vk,
  									const VkCommandBuffer	cmdBuffer,
  									const UVec2&			imageSize,
  									const VkImage			srcImage,
  									const VkBuffer			dstBuffer)
  {
  	// Image read barrier after color output
  	{
  		const VkImageMemoryBarrier barrier =
  		{
  			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,									// VkStructureType            sType;
  			DE_NULL,																// const void*                pNext;
  			VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,									// VkAccessFlags              srcAccessMask;
  			VK_ACCESS_TRANSFER_READ_BIT,											// VkAccessFlags              dstAccessMask;
  			VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,									// VkImageLayout              oldLayout;
  			VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,									// VkImageLayout              newLayout;
  			VK_QUEUE_FAMILY_IGNORED,												// uint32_t                   srcQueueFamilyIndex;
  			VK_QUEUE_FAMILY_IGNORED,												// uint32_t                   dstQueueFamilyIndex;
  			srcImage,																// VkImage                    image;
  			makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u),	// VkImageSubresourceRange    subresourceRange;
  		};
  
  		vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0u, DE_NULL, 0u, DE_NULL, 1u, &barrier);
  	}
  	// Resolve image -> host buffer
  	{
  		const VkBufferImageCopy region =
  		{
  			0ull,																// VkDeviceSize                bufferOffset;
  			0u,																	// uint32_t                    bufferRowLength;
  			0u,																	// uint32_t                    bufferImageHeight;
  			makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u),	// VkImageSubresourceLayers    imageSubresource;
  			makeOffset3D(0, 0, 0),												// VkOffset3D                  imageOffset;
  			makeExtent3D(imageSize.x(), imageSize.y(), 1u),						// VkExtent3D                  imageExtent;
  		};
  
  		vk.cmdCopyImageToBuffer(cmdBuffer, srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dstBuffer, 1u, &region);
  	}
  	// Buffer write barrier
  	{
  		const VkBufferMemoryBarrier barrier =
  		{
  			VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,		// VkStructureType    sType;
  			DE_NULL,										// const void*        pNext;
  			VK_ACCESS_TRANSFER_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;
  			dstBuffer,										// VkBuffer           buffer;
  			0ull,											// VkDeviceSize       offset;
  			VK_WHOLE_SIZE,									// VkDeviceSize       size;
  		};
  
  		vk.cmdPipelineBarrier(cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, (VkDependencyFlags)0,
  							  0u, DE_NULL, 1u, &barrier, DE_NULL, 0u);
  	}
  }
  
  namespace VerifySamples
  {
  
  //! The parameters that define a test case
  struct TestParams
  {
  	struct SampleCount
  	{
  		VkSampleCountFlagBits	numCoverageSamples;				//!< VkPipelineMultisampleStateCreateInfo::rasterizationSamples
  		VkSampleCountFlagBits	numColorSamples;				//!< VkAttachmentDescription::samples and VkImageCreateInfo::samples
  		VkSampleCountFlagBits	numDepthStencilSamples;			//!< VkAttachmentDescription::samples and VkImageCreateInfo::samples
  	};
  
  	VkFormat					colorFormat;					//!< Color attachment format
  	VkFormat					depthStencilFormat;				//!< D/S attachment format. Will test both aspects if it's a mixed format
  	bool						useProgrammableSampleLocations;	//!< Try to use VK_EXT_sample_locations if available
  	bool						useFragmentShadingRate;			//!< Try to use VK_KHR_fragment_shading_rate if available
  	std::vector<SampleCount>	perSubpassSamples;				//!< Will use multiple subpasses if more than one element
  
  	TestParams (void)
  		: colorFormat						()
  		, depthStencilFormat				()
  		, useProgrammableSampleLocations	()
  		, useFragmentShadingRate			()
  	{
  	}
  };
  
  //! Common data used by the test
  struct WorkingData
  {
  	struct PerSubpass
  	{
  		deUint32						numVertices;				//!< Number of vertices defined in the vertex buffer
  		Move<VkBuffer>					vertexBuffer;
  		MovePtr<Allocation>				vertexBufferAlloc;
  		Move<VkImage>					colorImage;					//!< Color image
  		Move<VkImageView>				colorImageView;				//!< Color attachment
  		MovePtr<Allocation>				colorImageAlloc;
  		Move<VkImage>					depthStencilImage;			//!< Depth stencil image
  		Move<VkImageView>				depthStencilImageView;		//!< Depth stencil attachment
  		Move<VkImageView>				depthOnlyImageView;			//!< Depth aspect for shader read
  		Move<VkImageView>				stencilOnlyImageView;		//!< Stencil aspect for shader read
  		MovePtr<Allocation>				depthStencilImageAlloc;
  		Move<VkBuffer>					compareBuffer;				//!< Buffer used to verify the images - comparison data
  		MovePtr<Allocation>				compareBufferAlloc;
  		VkDeviceSize					compareBufferSize;
  		Move<VkBuffer>					resultBuffer;				//!< Buffer used to verify the images - results
  		MovePtr<Allocation>				resultBufferAlloc;
  		VkDeviceSize					resultBufferSize;
  		deUint32						numResultElements;			//!< Number of checksums in the result buffer
  		MovePtr<MultisamplePixelGrid>	pixelGrid;					//!< Programmable locations
  
  		PerSubpass (void)
  			: numVertices		()
  			, compareBufferSize	()
  			, resultBufferSize	()
  			, numResultElements	()
  		{
  		}
  	};
  
  	UVec2											renderSize;					//!< Size of the framebuffer
  	VkPhysicalDeviceSampleLocationsPropertiesEXT	sampleLocationsProperties;	//!< Used with VK_EXT_sample_locations
  
  	std::vector<de::SharedPtr<PerSubpass> >			perSubpass;					//!< Test may use more than one set of data
  
  	WorkingData (void)
  		: sampleLocationsProperties ()
  	{
  	}
  };
  
  void addVerificationComputeShader (SourceCollections&			programCollection,
  								   const VkSampleCountFlagBits	numCoverageSamples,
  								   const VkSampleCountFlagBits	numColorSamples,
  								   const VkSampleCountFlagBits	numDepthStencilSamples,
  								   const VkFormat				depthStencilFormat,
  								   const std::string&			nameSuffix)
  {
  		const bool			isColorMS			= (numColorSamples		  != VK_SAMPLE_COUNT_1_BIT);
  		const bool			isDepthStencilMS	= (numDepthStencilSamples != VK_SAMPLE_COUNT_1_BIT);
  		const std::string	colorBit			= de::toString(static_cast<deUint32>(VK_IMAGE_ASPECT_COLOR_BIT)) + "u";
  		const std::string	depthBit			= de::toString(static_cast<deUint32>(VK_IMAGE_ASPECT_DEPTH_BIT)) + "u";
  		const std::string	stencilBit			= de::toString(static_cast<deUint32>(VK_IMAGE_ASPECT_STENCIL_BIT)) + "u";
  
  		std::ostringstream src;
  		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
  			<< "\n"
  			<< "struct CompareData {\n"
  			<< "    vec4  color;\n"
  			<< "    float depth;\n"
  			<< "    uint  stencil;\n"
  			<< "};\n"
  			<< "\n"
  			<< "layout(local_size_x = " << static_cast<deUint32>(numCoverageSamples) << ") in;\n"
  			// Always use this descriptor layout and ignore unused bindings
  			<< "layout(set = 0, binding = 0, std430) writeonly buffer Output {\n"
  			<< "    uint values[];\n"
  			<< "} sb_out;\n"
  			<< "layout(set = 0, binding = 1, std430) readonly buffer InputCompare {\n"
  			<< "    CompareData	data[];\n"
  			<< "} sb_cmp;\n"
  			<< "layout(set = 0, binding = 2) uniform sampler2D" << (isColorMS ? "MS" : "") << "  colorImage;\n"
  			<< "layout(set = 0, binding = 3) uniform sampler2D" << (isDepthStencilMS ? "MS" : "") <<"  depthImage;\n"
  			<< "layout(set = 0, binding = 4) uniform usampler2D" << (isDepthStencilMS ? "MS" : "") <<" stencilImage;\n"
  			<< "\n"
  			<< "void main (void)\n"
  			<< "{\n"
  
  		// Data for each sample in each pixel is laid out linearly (e.g 2 samples):
  		// [pixel(0, 0) sample(0)][pixel(0, 0) sample(1)][pixel(1, 0) sample(0)][pixel(1, 0) sample(1)]...
  
  			<< "    uint  globalIndex = gl_LocalInvocationID.x + gl_WorkGroupSize.x * (gl_WorkGroupID.x + gl_WorkGroupID.y * gl_NumWorkGroups.x);\n"
  			<< "    ivec2 position    = ivec2(gl_WorkGroupID.x, gl_WorkGroupID.y);\n"
  			<< "    int   sampleNdx   = int(gl_LocalInvocationID.x);\n"
  			<< "    uint  result      = 0u;\n"
  			<< "\n"
  			<< "    // Verify color samples\n"
  			<< "    if (sampleNdx < " << static_cast<deUint32>(numColorSamples) << ")\n"
  			<< "    {\n"
  			<< "        vec4 color     = texelFetch(colorImage, position, sampleNdx);\n"	// for non-MS (1 sample) case, sampleNdx = 0 and will instead be LOD = 0
  			<< "        vec4 diff      = abs(color - sb_cmp.data[globalIndex].color);\n"
  			<< "        vec4 threshold = vec4(0.02);\n"
  			<< "\n"
  			<< "        if (all(lessThan(diff, threshold)))\n"
  			<< "            result |= " << colorBit << ";\n"
  			<< "    }\n"
  			<< "    else\n"
  			<< "        result |= " << colorBit << ";\n"	// Pass, if sample doesn't exist
  			<< "\n";
  
  		if (isDepthFormat(depthStencilFormat))
  		{
  			src << "    // Verify depth samples\n"
  				<< "    if (sampleNdx < " << static_cast<deUint32>(numDepthStencilSamples) << ")\n"
  				<< "    {\n"
  				<< "        float depth     = texelFetch(depthImage, position, sampleNdx).r;\n"
  				<< "        float diff      = abs(depth - sb_cmp.data[globalIndex].depth);\n"
  				<< "        float threshold = 0.002;\n"
  				<< "\n"
  				<< "        if (diff < threshold)\n"
  				<< "            result |= " << depthBit << ";\n"
  				<< "    }\n"
  				<< "    else\n"
  				<< "        result |= " << depthBit << ";\n"
  				<< "\n";
  		}
  
  		if (isStencilFormat(depthStencilFormat))
  		{
  			src << "    // Verify stencil samples\n"
  				<< "    if (sampleNdx < " << static_cast<deUint32>(numDepthStencilSamples) << ")\n"
  				<< "    {\n"
  				<< "        uint stencil   = texelFetch(stencilImage, position, sampleNdx).r;\n"
  				<< "        uint diff      = stencil - sb_cmp.data[globalIndex].stencil;\n"
  				<< "\n"
  				<< "        if (diff == 0u)\n"
  				<< "            result |= " << stencilBit << ";\n"
  				<< "    }\n"
  				<< "    else\n"
  				<< "        result |= " << stencilBit << ";\n"
  				<< "\n";
  		}
  
  		src << "    sb_out.values[globalIndex] = result;\n"
  			<< "}\n";
  		programCollection.glslSources.add("comp" + nameSuffix) << glu::ComputeSource(src.str());
  }
  
  //! Get a compact sample count string in format X_Y_Z
  std::string getSampleCountString (const TestParams::SampleCount& samples)
  {
  	std::ostringstream str;
  
  	str << static_cast<deUint32>(samples.numCoverageSamples) << "_"
  		<< static_cast<deUint32>(samples.numColorSamples)	 << "_"
  		<< static_cast<deUint32>(samples.numDepthStencilSamples);
  
  	return str.str();
  }
  
  void initPrograms (SourceCollections& programCollection, const TestParams params)
  {
  	// 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  vec4 in_color;\n"
  			<< "layout(location = 0) out vec4 o_color;\n"
  			<< "\n"
  			<< "out gl_PerVertex {\n"
  			<< "    vec4 gl_Position;\n"
  			<< "};\n"
  			<< "\n"
  			<< "void main(void)\n"
  			<< "{\n"
  			<< "    gl_Position = in_position;\n"
  			<< "    o_color     = in_color;\n"
  			<< "}\n";
  
  		programCollection.glslSources.add("vert") << 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  vec4 in_color;\n"
  			<< "layout(location = 0) out vec4 o_color;\n"
  			<< "\n"
  			<< "void main(void)\n"
  			<< "{\n"
  			<< "    o_color = in_color;\n"
  			<< "}\n";
  
  		programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
  	}
  
  	// Compute shader - image verification
  	for (deUint32 subpassNdx = 0; subpassNdx < static_cast<deUint32>(params.perSubpassSamples.size()); ++subpassNdx)
  	{
  		const TestParams::SampleCount&	samples	= params.perSubpassSamples[subpassNdx];
  		addVerificationComputeShader(programCollection,
  									 samples.numCoverageSamples,
  									 samples.numColorSamples,
  									 samples.numDepthStencilSamples,
  									 params.depthStencilFormat,
  									 "_" + getSampleCountString(samples));
  	}
  }
  
  //! A simple color, depth/stencil draw. Subpasses (if more than one) are independent
  void draw (Context& context, const TestParams& params, WorkingData& wd)
  {
  	const DeviceInterface&	vk				= context.getDeviceInterface();
  	const VkDevice			device			= context.getDevice();
  	const deUint32			numSubpasses	= static_cast<deUint32>(wd.perSubpass.size());
  
  	Move<VkRenderPass>							renderPass;
  	Move<VkFramebuffer>							framebuffer;
  	std::vector<VkSampleLocationsInfoEXT>		perSubpassSampleLocationsInfo;
  	std::vector<VkAttachmentSampleLocationsEXT>	attachmentSampleLocations;
  	std::vector<VkSubpassSampleLocationsEXT>	subpassSampleLocations;
  
  	if (params.useProgrammableSampleLocations)
  	for (deUint32 subpassNdx = 0; subpassNdx < numSubpasses; ++subpassNdx)
  	{
  		perSubpassSampleLocationsInfo.push_back(makeSampleLocationsInfo(*wd.perSubpass[subpassNdx]->pixelGrid));
  	}
  
  	// 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(numSubpasses * 2);
  
  		for (deUint32 subpassNdx = 0; subpassNdx < numSubpasses; ++subpassNdx)
  		{
  			attachments.push_back(wd.perSubpass[subpassNdx]->colorImageView.get());
  			attachments.push_back(wd.perSubpass[subpassNdx]->depthStencilImageView.get());
  
  			attachmentDescriptions.push_back(makeAttachmentDescription(
  				(VkAttachmentDescriptionFlags)0,								// VkAttachmentDescriptionFlags		flags;
  				params.colorFormat,												// VkFormat							format;
  				params.perSubpassSamples[subpassNdx].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;
  			));
  
  			attachmentDescriptions.push_back(makeAttachmentDescription(
  				(VkAttachmentDescriptionFlags)0,								// VkAttachmentDescriptionFlags		flags;
  				params.depthStencilFormat,										// VkFormat							format;
  				params.perSubpassSamples[subpassNdx].numDepthStencilSamples,	// VkSampleCountFlagBits			samples;
  				VK_ATTACHMENT_LOAD_OP_CLEAR,									// VkAttachmentLoadOp				loadOp;
  				VK_ATTACHMENT_STORE_OP_STORE,									// VkAttachmentStoreOp				storeOp;
  				VK_ATTACHMENT_LOAD_OP_CLEAR,									// VkAttachmentLoadOp				stencilLoadOp;
  				VK_ATTACHMENT_STORE_OP_STORE,									// VkAttachmentStoreOp				stencilStoreOp;
  				VK_IMAGE_LAYOUT_UNDEFINED,										// VkImageLayout					initialLayout;
  				VK_IMAGE_LAYOUT_DEPTH_STENCIL_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();
  
  			attachmentReferences.push_back(makeAttachmentReference(static_cast<deUint32>(attachmentReferences.size()),	VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL));
  			const VkAttachmentReference* depthStencilRef = &attachmentReferences.back();
  
  			if (params.useProgrammableSampleLocations)
  			{
  				const VkAttachmentSampleLocationsEXT newAttachmentSampleLocations =
  				{
  					attachmentReferences.back().attachment,			// uint32_t                    attachmentIndex;
  					perSubpassSampleLocationsInfo[subpassNdx],		// VkSampleLocationsInfoEXT    sampleLocationsInfo;
  				};
  				attachmentSampleLocations.push_back(newAttachmentSampleLocations);
  
  				const VkSubpassSampleLocationsEXT newSubpassSampleLocations =
  				{
  					subpassNdx,										// uint32_t                    subpassIndex;
  					perSubpassSampleLocationsInfo[subpassNdx],		// VkSampleLocationsInfoEXT    sampleLocationsInfo;
  				};
  				subpassSampleLocations.push_back(newSubpassSampleLocations);
  			}
  
  			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;
  				depthStencilRef,									// const VkAttachmentReference*    pDepthStencilAttachment;
  				0u,													// uint32_t                        preserveAttachmentCount;
  				DE_NULL,											// const uint32_t*                 pPreserveAttachments;
  			};
  
  			subpasses.push_back(subpassDescription);
  		}
  
  		// Assume there are no dependencies between subpasses
  		const 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), wd.renderSize.x(), wd.renderSize.y());
  	}
  
  	const Unique<VkShaderModule>	vertexModule	(createShaderModule(vk, device, context.getBinaryCollection().get("vert"), 0u));
  	const Unique<VkShaderModule>	fragmentModule	(createShaderModule(vk, device, context.getBinaryCollection().get("frag"), 0u));
  	const Unique<VkPipelineLayout>	pipelineLayout	(makePipelineLayout(vk, device));
  
  	typedef SharedPtr<Unique<VkPipeline> > PipelineSp;
  	std::vector<PipelineSp> pipelines;
  
  	for (deUint32 subpassNdx = 0; subpassNdx < numSubpasses; ++subpassNdx)
  	{
  		const VkSampleLocationsInfoEXT* pSampleLocationsInfo = (params.useProgrammableSampleLocations ? &perSubpassSampleLocationsInfo[subpassNdx] : DE_NULL);
  
  		pipelines.push_back(PipelineSp(new Unique<VkPipeline>(
  			makeGraphicsPipeline(vk, device, *pipelineLayout, *renderPass, *vertexModule, *fragmentModule, /*use vertex input*/ true, subpassNdx,
  								 wd.renderSize, getImageAspectFlags(params.depthStencilFormat), params.perSubpassSamples[subpassNdx].numCoverageSamples,
  								 /*use sample shading*/ true, params.useFragmentShadingRate, pSampleLocationsInfo))));
  	}
  
  	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);
  
  	{
  		std::vector<VkClearValue> clearValues;
  
  		for (deUint32 subpassNdx = 0; subpassNdx < numSubpasses; ++subpassNdx)
  		{
  			clearValues.push_back(makeClearValueColorF32(0.0f, 0.0f, 0.0f, 1.0f));
  			clearValues.push_back(makeClearValueDepthStencil(1.0f, 0u));
  		}
  
  		const VkRect2D renderArea =
  		{
  			{ 0u, 0u },
  			{ wd.renderSize.x(), wd.renderSize.y() }
  		};
  
  		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;
  		};
  
  		if (params.useProgrammableSampleLocations)
  		{
  			const VkRenderPassSampleLocationsBeginInfoEXT renderPassSampleLocationsBeginInfo =
  			{
  				VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT,	// VkStructureType                          sType;
  				DE_NULL,														// const void*                              pNext;
  				static_cast<deUint32>(attachmentSampleLocations.size()),		// uint32_t                                 attachmentInitialSampleLocationsCount;
  				dataOrNullPtr(attachmentSampleLocations),						// const VkAttachmentSampleLocationsEXT*    pAttachmentInitialSampleLocations;
  				static_cast<deUint32>(subpassSampleLocations.size()),			// uint32_t                                 postSubpassSampleLocationsCount;
  				dataOrNullPtr(subpassSampleLocations),							// const VkSubpassSampleLocationsEXT*       pPostSubpassSampleLocations;
  			};
  
  			renderPassBeginInfo.pNext = &renderPassSampleLocationsBeginInfo;
  
  			vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
  		}
  		else
  			vk.cmdBeginRenderPass(*cmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
  	}
  
  	for (deUint32 subpassNdx = 0; subpassNdx < numSubpasses; ++subpassNdx)
  	{
  		if (subpassNdx != 0)
  			vk.cmdNextSubpass(*cmdBuffer, VK_SUBPASS_CONTENTS_INLINE);
  
  		const VkDeviceSize vertexBufferOffset = 0ull;
  		vk.cmdBindVertexBuffers(*cmdBuffer, 0u, 1u, &wd.perSubpass[subpassNdx]->vertexBuffer.get(), &vertexBufferOffset);
  
  		vk.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, **pipelines[subpassNdx]);
  
  		vk.cmdDraw(*cmdBuffer, wd.perSubpass[subpassNdx]->numVertices, 1u, 0u, 0u);
  	}
  
  	vk.cmdEndRenderPass(*cmdBuffer);
  
  	VK_CHECK(vk.endCommandBuffer(*cmdBuffer));
  	submitCommandsAndWait(vk, device, context.getUniversalQueue(), *cmdBuffer);
  }
  
  void dispatchImageCheck (Context& context, const TestParams& params, WorkingData& wd, const deUint32 subpassNdx)
  {
  	const DeviceInterface&		vk			= context.getDeviceInterface();
  	const VkDevice				device		= context.getDevice();
  	WorkingData::PerSubpass&	subpassData	= *wd.perSubpass[subpassNdx];
  
  	const Unique<VkSampler>	defaultSampler	(makeSampler(vk, device));
  
  	// Create descriptor set
  
  	const Unique<VkDescriptorSetLayout> descriptorSetLayout(
  		DescriptorSetLayoutBuilder()
  		.addSingleBinding		(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,			VK_SHADER_STAGE_COMPUTE_BIT)
  		.addSingleBinding		(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,			VK_SHADER_STAGE_COMPUTE_BIT)
  		.addSingleSamplerBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,	VK_SHADER_STAGE_COMPUTE_BIT, &defaultSampler.get())
  		.addSingleSamplerBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,	VK_SHADER_STAGE_COMPUTE_BIT, &defaultSampler.get())
  		.addSingleSamplerBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,	VK_SHADER_STAGE_COMPUTE_BIT, &defaultSampler.get())
  		.build(vk, device));
  
  	const Unique<VkDescriptorPool> descriptorPool(
  		DescriptorPoolBuilder()
  		.addType(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 2u)
  		.addType(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3u)
  		.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u));
  
  	const Unique<VkDescriptorSet> descriptorSet(makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout));
  
  	{
  		const VkDescriptorBufferInfo	compareBufferInfo	= makeDescriptorBufferInfo(*subpassData.compareBuffer, 0ull, subpassData.compareBufferSize);
  		const VkDescriptorBufferInfo	resultBufferInfo	= makeDescriptorBufferInfo(*subpassData.resultBuffer, 0ull, subpassData.resultBufferSize);
  		const VkDescriptorImageInfo		colorImageInfo		= makeDescriptorImageInfo(DE_NULL, *subpassData.colorImageView,			VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
  		const VkDescriptorImageInfo		depthImageInfo		= makeDescriptorImageInfo(DE_NULL, *subpassData.depthOnlyImageView,		VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
  		const VkDescriptorImageInfo		stencilImageInfo	= makeDescriptorImageInfo(DE_NULL, *subpassData.stencilOnlyImageView,	VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
  
  		DescriptorSetUpdateBuilder	builder;
  
  		builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &resultBufferInfo);
  		builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(1u), VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, &compareBufferInfo);
  		builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(2u), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &colorImageInfo);
  
  		if (subpassData.depthOnlyImageView)
  			builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(3u), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &depthImageInfo);
  
  		if (subpassData.stencilOnlyImageView)
  			builder.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(4u), VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, &stencilImageInfo);
  
  		builder.update(vk, device);
  	}
  
  	// Pipeline
  
  	const std::string				shaderName		("comp_" + getSampleCountString(params.perSubpassSamples[subpassNdx]));
  	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, wd.renderSize.x(), wd.renderSize.y(), 1u);
  
  	{
  		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;
  			*subpassData.resultBuffer,						// 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, subpassData.resultBufferAlloc->getMemory(), subpassData.resultBufferAlloc->getOffset(), VK_WHOLE_SIZE);
  }
  
  void createPerSubpassData (Context& context, const TestParams& params, WorkingData& wd, const deUint32 subpassNdx)
  {
  	const DeviceInterface&			vk			= context.getDeviceInterface();
  	const VkDevice					device		= context.getDevice();
  	MovePtr<Allocator>				allocator   = MovePtr<Allocator>(new SimpleAllocator(vk, device, getPhysicalDeviceMemoryProperties(context.getInstanceInterface(), context.getPhysicalDevice())));
  	const TestParams::SampleCount&	samples		= params.perSubpassSamples[subpassNdx];
  	WorkingData::PerSubpass&		subpassData	= *wd.perSubpass[subpassNdx];
  
  	// Create images
  	{
  
  		const VkImageUsageFlags	colorImageUsageFlags		= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT		  | VK_IMAGE_USAGE_SAMPLED_BIT;
  		const VkImageUsageFlags depthStencilImageUsageFlags	= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
  
  		checkImageRequirements (context,
  								params.colorFormat,
  								VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT,
  								colorImageUsageFlags,
  								samples.numColorSamples);
  
  		subpassData.colorImage		= makeImage(vk, device, params.colorFormat, wd.renderSize, samples.numColorSamples, colorImageUsageFlags);
  		subpassData.colorImageAlloc	= bindImage(vk, device, *allocator, *subpassData.colorImage, MemoryRequirement::Any);
  		subpassData.colorImageView	= makeImageView(vk, device, *subpassData.colorImage, VK_IMAGE_VIEW_TYPE_2D, params.colorFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u));
  
  		checkImageRequirements (context,
  								params.depthStencilFormat,
  								VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT,
  								depthStencilImageUsageFlags,
  								samples.numDepthStencilSamples);
  
  		subpassData.depthStencilImage		= makeImage(vk, device, params.depthStencilFormat, wd.renderSize, samples.numDepthStencilSamples, depthStencilImageUsageFlags);
  		subpassData.depthStencilImageAlloc	= bindImage(vk, device, *allocator, *subpassData.depthStencilImage, MemoryRequirement::Any);
  		subpassData.depthStencilImageView	= makeImageView(vk, device, *subpassData.depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, params.depthStencilFormat, makeImageSubresourceRange(getImageAspectFlags(params.depthStencilFormat), 0u, 1u, 0u, 1u));
  
  		if (isDepthFormat(params.depthStencilFormat))
  			subpassData.depthOnlyImageView	= makeImageView(vk, device, *subpassData.depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, params.depthStencilFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u));
  
  		if (isStencilFormat(params.depthStencilFormat))
  			subpassData.stencilOnlyImageView	= makeImageView(vk, device, *subpassData.depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, params.depthStencilFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_STENCIL_BIT, 0u, 1u, 0u, 1u));
  	}
  
  	// Create vertex and comparison buffers
  	{
  		const deUint32					seed		= 123 + 19 * subpassNdx;
  		const std::vector<CompareData>	compareData	= generateCompareData(seed, wd.renderSize, samples.numCoverageSamples, samples.numColorSamples, samples.numDepthStencilSamples);
  
  		subpassData.compareBufferSize	= static_cast<VkDeviceSize>(sizeof(CompareData) * compareData.size());
  		subpassData.compareBuffer		= makeBuffer(vk, device, subpassData.compareBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
  		subpassData.compareBufferAlloc	= bindBuffer(vk, device, *allocator, *subpassData.compareBuffer, MemoryRequirement::HostVisible);
  
  		deMemcpy(subpassData.compareBufferAlloc->getHostPtr(), dataOrNullPtr(compareData), static_cast<std::size_t>(subpassData.compareBufferSize));
  		flushMappedMemoryRange(vk, device, subpassData.compareBufferAlloc->getMemory(), subpassData.compareBufferAlloc->getOffset(), VK_WHOLE_SIZE);
  
  		subpassData.numResultElements	= static_cast<deUint32>(compareData.size());
  		subpassData.resultBufferSize	= static_cast<VkDeviceSize>(sizeof(deUint32) * compareData.size());
  		subpassData.resultBuffer		= makeBuffer(vk, device, subpassData.resultBufferSize, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
  		subpassData.resultBufferAlloc	= bindBuffer(vk, device, *allocator, *subpassData.resultBuffer, MemoryRequirement::HostVisible);
  
  		deMemset(subpassData.resultBufferAlloc->getHostPtr(), 0, static_cast<std::size_t>(subpassData.resultBufferSize));
  		flushMappedMemoryRange(vk, device, subpassData.resultBufferAlloc->getMemory(), subpassData.resultBufferAlloc->getOffset(), VK_WHOLE_SIZE);
  
  		std::vector<PositionColor> vertices;
  
  		if (params.useProgrammableSampleLocations)
  		{
  			subpassData.pixelGrid = MovePtr<MultisamplePixelGrid>(new MultisamplePixelGrid(UVec2(wd.sampleLocationsProperties.maxSampleLocationGridSize.width,
  																								 wd.sampleLocationsProperties.maxSampleLocationGridSize.height),
  																						   samples.numCoverageSamples));
  
  			const deUint32 locationsSeed = 211 + 4 * subpassNdx;
  			fillSampleLocationsRandom(*subpassData.pixelGrid, wd.sampleLocationsProperties.sampleLocationSubPixelBits, locationsSeed);
  			vertices = generateSubpixelTriangles(wd.renderSize, compareData, getSampleLocations(*subpassData.pixelGrid, wd.renderSize));
  		}
  		else
  		{
  			const std::vector<Vec2>	locations = genFramebufferStandardSampleLocations(samples.numCoverageSamples, wd.renderSize);
  			vertices = generateSubpixelTriangles(wd.renderSize, compareData, locations);
  		}
  
  		const VkDeviceSize	vertexBufferSize = static_cast<VkDeviceSize>(sizeof(vertices[0]) * vertices.size());
  		subpassData.numVertices			= static_cast<deUint32>(vertices.size());
  		subpassData.vertexBuffer			= makeBuffer(vk, device, vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
  		subpassData.vertexBufferAlloc	= bindBuffer(vk, device, *allocator, *subpassData.vertexBuffer, MemoryRequirement::HostVisible);
  
  		deMemcpy(subpassData.vertexBufferAlloc->getHostPtr(), dataOrNullPtr(vertices), static_cast<std::size_t>(vertexBufferSize));
  		flushMappedMemoryRange(vk, device, subpassData.vertexBufferAlloc->getMemory(), subpassData.vertexBufferAlloc->getOffset(), VK_WHOLE_SIZE);
  	}
  }
  
  void checkRequirements (Context& context, TestParams params)
  {
  	context.requireDeviceFunctionality("VK_AMD_mixed_attachment_samples");
  
  	if (params.useProgrammableSampleLocations)
  		context.requireDeviceFunctionality("VK_EXT_sample_locations");
  
  	for (deUint32 subpassNdx = 0; subpassNdx < static_cast<deUint32>(params.perSubpassSamples.size()); ++subpassNdx)
  	{
  		const TestParams::SampleCount& samples = params.perSubpassSamples[subpassNdx];
  		checkSampleRequirements(context, samples.numColorSamples, samples.numDepthStencilSamples, !params.useProgrammableSampleLocations);
  	}
  
  	if (params.useFragmentShadingRate)
  	{
  		const auto&	vki				= context.getInstanceInterface();
  		const auto	physicalDevice	= context.getPhysicalDevice();
  
  		context.requireDeviceFunctionality("VK_KHR_fragment_shading_rate");
  
  		if (!context.getFragmentShadingRateFeatures().pipelineFragmentShadingRate)
  			TCU_THROW(NotSupportedError, "pipelineFragmentShadingRate not supported");
  
  		// Fetch information about supported fragment shading rates
  		deUint32 supportedFragmentShadingRateCount = 0;
  		vki.getPhysicalDeviceFragmentShadingRatesKHR(physicalDevice, &supportedFragmentShadingRateCount, DE_NULL);
  
  		std::vector<vk::VkPhysicalDeviceFragmentShadingRateKHR> supportedFragmentShadingRates(supportedFragmentShadingRateCount,
  			{
  				vk::VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_KHR,
  				DE_NULL,
  				vk::VK_SAMPLE_COUNT_1_BIT,
  				{1, 1}
  			});
  		vki.getPhysicalDeviceFragmentShadingRatesKHR(physicalDevice, &supportedFragmentShadingRateCount, supportedFragmentShadingRates.data());
  
  		deUint32 cumulativeNeededSamples = 0;
  		for (const TestParams::SampleCount& samples : params.perSubpassSamples)
  			cumulativeNeededSamples |= samples.numColorSamples;
  
  		bool requiredRateFound = false;
  		for (const auto& rate : supportedFragmentShadingRates)
  		{
  			if ((rate.fragmentSize.width == 2u) &&
  				(rate.fragmentSize.height == 2u) &&
  				(rate.sampleCounts & cumulativeNeededSamples))
  			{
  				requiredRateFound = true;
  				break;
  			}
  		}
  
  		if (!requiredRateFound)
  			TCU_THROW(NotSupportedError, "Required FragmentShadingRate not supported");
  	}
  }
  
  //! Verify the values of all samples in all attachments.
  tcu::TestStatus test (Context& context, const TestParams params)
  {
  	WorkingData wd;
  	wd.renderSize	= UVec2(2, 2);	// Use a very small image, as we will verify all samples for all pixels
  
  	// Query state related to programmable sample locations
  	if (params.useProgrammableSampleLocations)
  	{
  		const InstanceInterface&	vki				= context.getInstanceInterface();
  		const VkPhysicalDevice		physicalDevice	= context.getPhysicalDevice();
  
  		wd.sampleLocationsProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT;
  		wd.sampleLocationsProperties.pNext = DE_NULL;
  
  		VkPhysicalDeviceProperties2 properties =
  		{
  			VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,	    // VkStructureType               sType;
  			&wd.sampleLocationsProperties,							// void*                         pNext;
  			VkPhysicalDeviceProperties(),							// VkPhysicalDeviceProperties    properties;
  		};
  
  		vki.getPhysicalDeviceProperties2(physicalDevice, &properties);
  
  		for (deUint32 subpassNdx = 0; subpassNdx < static_cast<deUint32>(params.perSubpassSamples.size()); ++subpassNdx)
  		{
  			if ((wd.sampleLocationsProperties.sampleLocationSampleCounts & params.perSubpassSamples[subpassNdx].numCoverageSamples) == 0u)
  				TCU_THROW(NotSupportedError, "VkSampleLocationsPropertiesAMD: sample count not supported");
  		}
  	}
  
  	// Create subpass data
  	for (deUint32 subpassNdx = 0; subpassNdx < static_cast<deUint32>(params.perSubpassSamples.size()); ++subpassNdx)
  	{
  		wd.perSubpass.push_back(SharedPtr<WorkingData::PerSubpass>(new WorkingData::PerSubpass()));
  		createPerSubpassData(context, params, wd, subpassNdx);
  	}
  
  	// Draw test geometry
  	draw (context, params, wd);
  
  	// Verify images with a compute shader
  	for (deUint32 subpassNdx = 0; subpassNdx < static_cast<deUint32>(params.perSubpassSamples.size()); ++subpassNdx)
  		dispatchImageCheck (context, params, wd, subpassNdx);
  
  	// Test checksums
  	for (deUint32 subpassNdx = 0; subpassNdx < static_cast<deUint32>(params.perSubpassSamples.size()); ++subpassNdx)
  	{
  		const deUint32*	const	pSampleChecksumBase	= static_cast<deUint32*>(wd.perSubpass[subpassNdx]->resultBufferAlloc->getHostPtr());
  		const bool				hasDepth			= isDepthFormat(params.depthStencilFormat);
  		const bool				hasStencil			= isStencilFormat(params.depthStencilFormat);
  		bool					allOk				= true;
  
  		context.getTestContext().getLog() << tcu::TestLog::Message << "Verify images in subpass " << subpassNdx << tcu::TestLog::EndMessage;
  
  		for (deUint32 globalSampleNdx = 0; globalSampleNdx < wd.perSubpass[subpassNdx]->numResultElements; ++globalSampleNdx)
  		{
  			const TestParams::SampleCount&	samples	 = params.perSubpassSamples[subpassNdx];
  			const deUint32					checksum = pSampleChecksumBase[globalSampleNdx];
  
  			if ((checksum & VK_IMAGE_ASPECT_COLOR_BIT) == 0u)
  			{
  				reportSampleError(context.getTestContext().getLog(), "color", wd.renderSize, samples.numCoverageSamples, globalSampleNdx);
  				allOk = false;
  			}
  
  			if (hasDepth && ((checksum & VK_IMAGE_ASPECT_DEPTH_BIT) == 0u))
  			{
  				reportSampleError(context.getTestContext().getLog(), "depth", wd.renderSize, samples.numCoverageSamples, globalSampleNdx);
  				allOk = false;
  			}
  
  			if (hasStencil && ((checksum & VK_IMAGE_ASPECT_STENCIL_BIT) == 0u))
  			{
  				reportSampleError(context.getTestContext().getLog(), "stencil", wd.renderSize, samples.numCoverageSamples, globalSampleNdx);
  				allOk = false;
  			}
  		}
  
  		if (!allOk)
  			return tcu::TestStatus::fail("Multisampled image has incorrect samples");
  	}
  
  	return tcu::TestStatus::pass("Pass");
  }
  
  } // VerifySamples
  
  namespace ShaderBuiltins
  {
  
  struct TestParams
  {
  	VkSampleCountFlagBits		numCoverageSamples;			//!< VkPipelineMultisampleStateCreateInfo::rasterizationSamples
  	VkSampleCountFlagBits		numColorSamples;			//!< VkAttachmentDescription::samples and VkImageCreateInfo::samples
  	VkSampleCountFlagBits		numDepthStencilSamples;		//!< VkAttachmentDescription::samples and VkImageCreateInfo::samples
  	VkFormat					colorFormat;				//!< Color attachment format
  	VkFormat					depthStencilFormat;			//!< D/S attachment format. Will test both aspects if it's a mixed format
  };
  
  struct WorkingData
  {
  	UVec2						renderSize;					//!< Size of the framebuffer
  	deUint32					numVertices;				//!< Number of vertices defined in the vertex buffer
  	Move<VkBuffer>				vertexBuffer;
  	MovePtr<Allocation>			vertexBufferAlloc;
  	Move<VkImage>				colorImage;					//!< Color image
  	Move<VkImageView>			colorImageView;				//!< Color attachment
  	MovePtr<Allocation>			colorImageAlloc;
  	Move<VkImage>				depthStencilImage;			//!< Depth stencil image
  	Move<VkImageView>			depthStencilImageView;		//!< Depth stencil attachment
  	Move<VkImageView>			depthOnlyImageView;			//!< Depth aspect for shader read
  	Move<VkImageView>			stencilOnlyImageView;		//!< Stencil aspect for shader read
  	MovePtr<Allocation>			depthStencilImageAlloc;
  	Move<VkImage>				resolveImage;				//!< Resolve image
  	Move<VkImageView>			resolveImageView;			//!< Resolve attachment
  	MovePtr<Allocation>			resolveImageAlloc;
  	Move<VkBuffer>				colorBuffer;				//!< Buffer used to copy resolve output
  	MovePtr<Allocation>			colorBufferAlloc;
  	VkDeviceSize				colorBufferSize;
  
  	WorkingData (void)
  		: numVertices		()
  		, colorBufferSize	(0)
  	{
  	}
  };
  
  void initPrograms (SourceCollections& programCollection, const TestParams params)
  {
  	// Vertex shader - no vertex data
  	{
  		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") << glu::VertexSource(src.str());
  	}
  
  	// Fragment shader
  	{
  		std::ostringstream src;
  		src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
  			<< "\n"
  			<< "layout(location = 0) out vec4 o_color;\n"
  			<< "\n"
  			<< "void main(void)\n"
  			<< "{\n"
  			<< "    vec4 col = vec4(0.0, 0.0, 0.0, 1.0);\n"
  			<< "\n";
  
  		if (params.numColorSamples == VK_SAMPLE_COUNT_1_BIT)
  		{
  			const deUint32 expectedMask = ((1u << static_cast<deUint32>(params.numCoverageSamples)) - 1u);
  
  			// Expect all covered samples to be lit, the rest is zero
  			src << "    if (gl_SampleMaskIn[0] == " << expectedMask << ")\n"
  				<< "        col.g = 1.0;\n"
  				<< "    else\n"
  				<< "        col.r = 1.0;\n";
  		}
  		else
  		{
  			// Expect only a matching sample to be lit
  			src << "    if (gl_SampleMaskIn[0] == (1 << gl_SampleID))\n"
  				<< "        col.g = 1.0;\n"
  				<< "    else\n"
  				<< "        col.r = 1.0;\n"
  				<< "\n"
  				<< "    if (gl_SampleID >= " << static_cast<deUint32>(params.numColorSamples) << ")  // number of color samples, should not happen\n"
  				<< "        col.b = 1.0;\n";
  		}
  
  		src << "\n"
  			<< "    o_color = col;\n"
  			<< "}\n";
  
  		programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
  	}
  }
  
  //! A simple color, depth/stencil draw. Single subpass, no vertex input
  void drawResolve (Context& context, const TestParams& params, WorkingData& wd)
  {
  	const DeviceInterface&	vk			= context.getDeviceInterface();
  	const VkDevice			device		= context.getDevice();
  	const bool				needResolve	= (params.numColorSamples != VK_SAMPLE_COUNT_1_BIT);
  
  	Move<VkRenderPass>		renderPass;
  	Move<VkFramebuffer>		framebuffer;
  
  	// Create a render pass and a framebuffer
  	{
  		std::vector<VkImageView>				attachments;
  		std::vector<VkAttachmentDescription>	attachmentDescriptions;
  
  		attachments.push_back(*wd.colorImageView);
  		attachments.push_back(*wd.depthStencilImageView);
  
  		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_TRANSFER_SRC_OPTIMAL					// VkImageLayout					finalLayout;
  		));
  
  		attachmentDescriptions.push_back(makeAttachmentDescription(
  			(VkAttachmentDescriptionFlags)0,						// VkAttachmentDescriptionFlags		flags;
  			params.depthStencilFormat,								// VkFormat							format;
  			params.numDepthStencilSamples,							// VkSampleCountFlagBits			samples;
  			VK_ATTACHMENT_LOAD_OP_CLEAR,							// VkAttachmentLoadOp				loadOp;
  			VK_ATTACHMENT_STORE_OP_STORE,							// VkAttachmentStoreOp				storeOp;
  			VK_ATTACHMENT_LOAD_OP_CLEAR,							// VkAttachmentLoadOp				stencilLoadOp;
  			VK_ATTACHMENT_STORE_OP_STORE,							// VkAttachmentStoreOp				stencilStoreOp;
  			VK_IMAGE_LAYOUT_UNDEFINED,								// VkImageLayout					initialLayout;
  			VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL		// VkImageLayout					finalLayout;
  		));
  
  		if (needResolve)
  		{
  			attachments.push_back(*wd.resolveImageView);
  
  			attachmentDescriptions.push_back(makeAttachmentDescription(
  				(VkAttachmentDescriptionFlags)0,					// VkAttachmentDescriptionFlags		flags;
  				params.colorFormat,									// VkFormat							format;
  				VK_SAMPLE_COUNT_1_BIT,								// VkSampleCountFlagBits			samples;
  				VK_ATTACHMENT_LOAD_OP_DONT_CARE,					// 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_TRANSFER_SRC_OPTIMAL				// VkImageLayout					finalLayout;
  			));
  		}
  
  		const VkAttachmentReference	colorRef		= makeAttachmentReference(0u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
  		const VkAttachmentReference	depthStencilRef	= makeAttachmentReference(1u, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
  		const VkAttachmentReference	resolveRef		= makeAttachmentReference(2u, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
  
  		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;
  			(needResolve ? &resolveRef : DE_NULL),					// const VkAttachmentReference*    pResolveAttachments;
  			&depthStencilRef,										// const VkAttachmentReference*    pDepthStencilAttachment;
  			0u,														// uint32_t                        preserveAttachmentCount;
  			DE_NULL,												// const uint32_t*                 pPreserveAttachments;
  		};
  
  		// Assume there are no dependencies between subpasses
  		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;
  			1u,														// deUint32							subpassCount;
  			&subpassDescription,									// 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), wd.renderSize.x(), wd.renderSize.y());
  	}
  
  	const Unique<VkShaderModule>	vertexModule	(createShaderModule(vk, device, context.getBinaryCollection().get("vert"), 0u));
  	const Unique<VkShaderModule>	fragmentModule	(createShaderModule(vk, device, context.getBinaryCollection().get("frag"), 0u));
  	const Unique<VkPipelineLayout>	pipelineLayout	(makePipelineLayout(vk, device));
  	const bool						useVertexInput	= false;
  	const bool						sampleShading	= (params.numColorSamples != VK_SAMPLE_COUNT_1_BIT);
  	const deUint32					subpassNdx		= 0u;
  	const Unique<VkPipeline>		pipeline		(makeGraphicsPipeline(vk, device, *pipelineLayout, *renderPass, *vertexModule, *fragmentModule, useVertexInput,
  																		  subpassNdx, wd.renderSize, getImageAspectFlags(params.depthStencilFormat),
  																		  params.numCoverageSamples, sampleShading, false));
  
  	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);
  
  	{
  		std::vector<VkClearValue> clearValues;
  		clearValues.push_back(makeClearValueColorF32(0.0f, 0.0f, 0.0f, 1.0f));
  		clearValues.push_back(makeClearValueDepthStencil(1.0f, 0u));
  
  		const VkRect2D renderArea =
  		{
  			{ 0u, 0u },
  			{ wd.renderSize.x(), wd.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.cmdBindPipeline(*cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
  	vk.cmdDraw(*cmdBuffer, 3u, 1u, 0u, 0u);
  
  	vk.cmdEndRenderPass(*cmdBuffer);
  
  	if (needResolve)
  		recordCopyOutputImageToBuffer(vk, *cmdBuffer, wd.renderSize, *wd.resolveImage, *wd.colorBuffer);
  	else
  		recordCopyOutputImageToBuffer(vk, *cmdBuffer, wd.renderSize, *wd.colorImage, *wd.colorBuffer);
  
  	VK_CHECK(vk.endCommandBuffer(*cmdBuffer));
  	submitCommandsAndWait(vk, device, context.getUniversalQueue(), *cmdBuffer);
  }
  
  void checkRequirements (Context& context, TestParams params)
  {
  	context.requireDeviceFunctionality("VK_AMD_mixed_attachment_samples");
  
  	checkSampleRequirements(context, params.numColorSamples, params.numDepthStencilSamples, false /* require standard sample locations */);
  }
  
  //! Verify the values of shader builtins
  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())));
  
  	wd.renderSize	= UVec2(16, 16);
  
  	// Create images and a color buffer
  	{
  
  		const VkImageUsageFlags	colorImageUsageFlags		= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
  		const VkImageUsageFlags depthStencilImageUsageFlags	= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
  
  		checkImageRequirements (context,
  								params.colorFormat,
  								VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT,
  								colorImageUsageFlags,
  								params.numColorSamples);
  
  		wd.colorImage		= makeImage(vk, device, params.colorFormat, wd.renderSize, params.numColorSamples, colorImageUsageFlags);
  		wd.colorImageAlloc	= bindImage(vk, device, *allocator, *wd.colorImage, MemoryRequirement::Any);
  		wd.colorImageView	= makeImageView(vk, device, *wd.colorImage, VK_IMAGE_VIEW_TYPE_2D, params.colorFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u));
  
  		if (params.numColorSamples != VK_SAMPLE_COUNT_1_BIT)
  		{
  			wd.resolveImage			= makeImage(vk, device, params.colorFormat, wd.renderSize, VK_SAMPLE_COUNT_1_BIT, colorImageUsageFlags);
  			wd.resolveImageAlloc	= bindImage(vk, device, *allocator, *wd.resolveImage, MemoryRequirement::Any);
  			wd.resolveImageView		= makeImageView(vk, device, *wd.resolveImage, VK_IMAGE_VIEW_TYPE_2D, params.colorFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u));
  		}
  
  		// Resolve result
  		wd.colorBufferSize	= static_cast<VkDeviceSize>(tcu::getPixelSize(mapVkFormat(params.colorFormat)) * wd.renderSize.x() * wd.renderSize.y());
  		wd.colorBuffer		= makeBuffer(vk, device, wd.colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_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);
  
  		checkImageRequirements (context,
  								params.depthStencilFormat,
  								VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT,
  								depthStencilImageUsageFlags,
  								params.numDepthStencilSamples);
  
  		wd.depthStencilImage		= makeImage(vk, device, params.depthStencilFormat, wd.renderSize, params.numDepthStencilSamples, depthStencilImageUsageFlags);
  		wd.depthStencilImageAlloc	= bindImage(vk, device, *allocator, *wd.depthStencilImage, MemoryRequirement::Any);
  		wd.depthStencilImageView	= makeImageView(vk, device, *wd.depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, params.depthStencilFormat, makeImageSubresourceRange(getImageAspectFlags(params.depthStencilFormat), 0u, 1u, 0u, 1u));
  
  		if (isDepthFormat(params.depthStencilFormat))
  			wd.depthOnlyImageView	= makeImageView(vk, device, *wd.depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, params.depthStencilFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u));
  
  		if (isStencilFormat(params.depthStencilFormat))
  			wd.stencilOnlyImageView	= makeImageView(vk, device, *wd.depthStencilImage, VK_IMAGE_VIEW_TYPE_2D, params.depthStencilFormat, makeImageSubresourceRange(VK_IMAGE_ASPECT_STENCIL_BIT, 0u, 1u, 0u, 1u));
  	}
  
  	// Draw, resolve, and copy to color buffer (see the fragment shader for details)
  	drawResolve(context, params, wd);
  
  	// Verify resolved image
  	{
  		const tcu::ConstPixelBufferAccess image (tcu::ConstPixelBufferAccess(mapVkFormat(params.colorFormat), tcu::IVec3(wd.renderSize.x(), wd.renderSize.y(), 1),wd.colorBufferAlloc->getHostPtr()));
  
  		if (compareGreenImage(context.getTestContext().getLog(), "resolve0", "Resolved test image", image))
  			return tcu::TestStatus::pass("Pass");
  		else
  			return tcu::TestStatus::fail("Some samples were incorrect");
  	}
  }
  
  } // ShaderBuiltins
  
  std::string getSampleCountGroupName(const VkSampleCountFlagBits coverageCount,
  									const VkSampleCountFlagBits colorCount,
  									const VkSampleCountFlagBits depthStencilCount)
  {
  	std::ostringstream str;
  	str << "coverage_"		 << static_cast<deUint32>(coverageCount)
  		<< "_color_"		 << static_cast<deUint32>(colorCount)
  		<< "_depth_stencil_" << static_cast<deUint32>(depthStencilCount);
  	return str.str();
  }
  
  std::string getFormatShortString (const VkFormat format)
  {
  	std::string s(de::toLower(getFormatName(format)));
  	return s.substr(10);
  }
  
  std::string getFormatCaseName (const VkFormat colorFormat,
  							   const VkFormat depthStencilFormat)
  {
  	std::ostringstream str;
  	str << getFormatShortString(colorFormat) << "_" << getFormatShortString(depthStencilFormat);
  	return str.str();
  }
  
  void createMixedAttachmentSamplesTestsInGroup (tcu::TestCaseGroup* rootGroup, bool useFragmentShadingRate)
  {
  	const VkFormat colorFormatRange[] =
  	{
  		VK_FORMAT_R8G8B8A8_UNORM,
  		// If you add more, make sure it is handled in the test/shader
  	};
  
  	const VkFormat depthStencilFormatRange[] =
  	{
  		VK_FORMAT_D16_UNORM,
  		VK_FORMAT_X8_D24_UNORM_PACK32,
  		VK_FORMAT_D32_SFLOAT,
  		VK_FORMAT_S8_UINT,
  		VK_FORMAT_D16_UNORM_S8_UINT,
  		VK_FORMAT_D24_UNORM_S8_UINT,
  		VK_FORMAT_D32_SFLOAT_S8_UINT,
  	};
  
  	// Minimal set of formats to cover depth and stencil
  	const VkFormat depthStencilReducedFormatRange[] =
  	{
  		VK_FORMAT_D16_UNORM,				//!< Must be supported
  		VK_FORMAT_D24_UNORM_S8_UINT,		//!< Either this, or the next one must be supported
  		VK_FORMAT_D32_SFLOAT_S8_UINT,
  	};
  
  	struct SampleCase
  	{
  		VkSampleCountFlagBits	colorSamples;
  		VkSampleCountFlagBits	depthStencilSamples;
  	};
  
  	// Currently supported EQAA cases
  	static const SampleCase	singlePassCases[] =
  	{
  		// Less color than depth/stencil
  		{ VK_SAMPLE_COUNT_1_BIT,	VK_SAMPLE_COUNT_2_BIT  },
  		{ VK_SAMPLE_COUNT_1_BIT,	VK_SAMPLE_COUNT_4_BIT  },
  		{ VK_SAMPLE_COUNT_1_BIT,	VK_SAMPLE_COUNT_8_BIT  },
  		{ VK_SAMPLE_COUNT_1_BIT,	VK_SAMPLE_COUNT_16_BIT },
  		{ VK_SAMPLE_COUNT_2_BIT,	VK_SAMPLE_COUNT_4_BIT  },
  		{ VK_SAMPLE_COUNT_2_BIT,	VK_SAMPLE_COUNT_8_BIT  },
  		{ VK_SAMPLE_COUNT_2_BIT,	VK_SAMPLE_COUNT_16_BIT },
  		{ VK_SAMPLE_COUNT_4_BIT,	VK_SAMPLE_COUNT_8_BIT  },
  		{ VK_SAMPLE_COUNT_4_BIT,	VK_SAMPLE_COUNT_16_BIT },
  		{ VK_SAMPLE_COUNT_8_BIT,	VK_SAMPLE_COUNT_16_BIT },
  	};
  
  	// Multi-subpass cases
  
  	static const SampleCase caseSubpassIncreaseColor_1[] =
  	{
  		{ VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_4_BIT },
  		{ VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT },
  	};
  	static const SampleCase caseSubpassIncreaseColor_2[] =
  	{
  		{ VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_8_BIT },
  		{ VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_8_BIT },
  		{ VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_8_BIT },
  	};
  	static const SampleCase caseSubpassDecreaseColor_1[] =
  	{
  		{ VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT },
  		{ VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_4_BIT },
  	};
  	static const SampleCase caseSubpassDecreaseColor_2[] =
  	{
  		{ VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_8_BIT },
  		{ VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_8_BIT },
  		{ VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_8_BIT },
  	};
  	static const SampleCase caseSubpassIncreaseCoverage_1[] =
  	{
  		{ VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT },
  		{ VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT },
  	};
  	static const SampleCase caseSubpassIncreaseCoverage_2[] =
  	{
  		{ VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT },
  		{ VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT },
  		{ VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_8_BIT },
  	};
  	static const SampleCase caseSubpassDecreaseCoverage_1[] =
  	{
  		{ VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT },
  		{ VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT },
  	};
  	static const SampleCase caseSubpassDecreaseCoverage_2[] =
  	{
  		{ VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_8_BIT },
  		{ VK_SAMPLE_COUNT_2_BIT, VK_SAMPLE_COUNT_4_BIT },
  		{ VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT },
  	};
  
  	static const struct
  	{
  		const char* const	caseName;
  		const deUint32		numSampleCases;
  		const SampleCase*	pSampleCase;
  	} subpassCases[] =
  	{
  		{ "multi_subpass_decrease_color_4",		DE_LENGTH_OF_ARRAY(caseSubpassDecreaseColor_1),		caseSubpassDecreaseColor_1 },
  		{ "multi_subpass_decrease_color_8",		DE_LENGTH_OF_ARRAY(caseSubpassDecreaseColor_2),		caseSubpassDecreaseColor_2 },
  		{ "multi_subpass_decrease_coverage_4",	DE_LENGTH_OF_ARRAY(caseSubpassDecreaseCoverage_1),	caseSubpassDecreaseCoverage_1 },
  		{ "multi_subpass_decrease_coverage_8",	DE_LENGTH_OF_ARRAY(caseSubpassDecreaseCoverage_2),	caseSubpassDecreaseCoverage_2 },
  		{ "multi_subpass_increase_color_4",		DE_LENGTH_OF_ARRAY(caseSubpassIncreaseColor_1),		caseSubpassIncreaseColor_1 },
  		{ "multi_subpass_increase_color_8",		DE_LENGTH_OF_ARRAY(caseSubpassIncreaseColor_2),		caseSubpassIncreaseColor_2 },
  		{ "multi_subpass_increase_coverage_4",	DE_LENGTH_OF_ARRAY(caseSubpassIncreaseCoverage_1),	caseSubpassIncreaseCoverage_1 },
  		{ "multi_subpass_increase_coverage_8",	DE_LENGTH_OF_ARRAY(caseSubpassIncreaseCoverage_2),	caseSubpassIncreaseCoverage_2 },
  	};
  
  	// Test 1: Per-sample expected value check
  	{
  		MovePtr<tcu::TestCaseGroup> standardLocationsGroup		(new tcu::TestCaseGroup(rootGroup->getTestContext(), "verify_standard_locations", ""));
  		MovePtr<tcu::TestCaseGroup> programmableLocationsGroup	(new tcu::TestCaseGroup(rootGroup->getTestContext(), "verify_programmable_locations", ""));
  
  		tcu::TestCaseGroup* locationsGroups[2] =
  		{
  			standardLocationsGroup.get(),
  			programmableLocationsGroup.get()
  		};
  
  		for (deUint32 groupNdx = 0u; groupNdx < DE_LENGTH_OF_ARRAY(locationsGroups); ++groupNdx)
  		{
  			// Single subpass cases
  			for (deUint32 caseNdx = 0u; caseNdx < DE_LENGTH_OF_ARRAY(singlePassCases); ++caseNdx)
  			{
  				VerifySamples::TestParams::SampleCount	samples;
  				samples.numColorSamples					= singlePassCases[caseNdx].colorSamples;
  				samples.numDepthStencilSamples			= singlePassCases[caseNdx].depthStencilSamples;
  				samples.numCoverageSamples				= de::max(samples.numColorSamples, samples.numDepthStencilSamples);
  
  				VerifySamples::TestParams params;
  				params.perSubpassSamples.push_back(samples);
  				params.useProgrammableSampleLocations	= (locationsGroups[groupNdx] == programmableLocationsGroup.get());
  				params.useFragmentShadingRate = useFragmentShadingRate;
  
  				MovePtr<tcu::TestCaseGroup> sampleCaseGroup(new tcu::TestCaseGroup(
  					rootGroup->getTestContext(), getSampleCountGroupName(samples.numCoverageSamples, samples.numColorSamples, samples.numDepthStencilSamples).c_str(), ""));
  
  				for (const VkFormat *pDepthStencilFormat = depthStencilFormatRange; pDepthStencilFormat != DE_ARRAY_END(depthStencilFormatRange); ++pDepthStencilFormat)
  				for (const VkFormat *pColorFormat		 = colorFormatRange;		pColorFormat		!= DE_ARRAY_END(colorFormatRange);		  ++pColorFormat)
  				{
  					params.colorFormat			= *pColorFormat;
  					params.depthStencilFormat	= *pDepthStencilFormat;
  
  					addFunctionCaseWithPrograms(
  						sampleCaseGroup.get(),
  						getFormatCaseName(params.colorFormat, params.depthStencilFormat).c_str(),
  						"",
  						VerifySamples::checkRequirements,
  						VerifySamples::initPrograms,
  						VerifySamples::test, params);
  				}
  
  				locationsGroups[groupNdx]->addChild(sampleCaseGroup.release());
  			}
  
  			// Multi subpass cases
  			for (deUint32 caseNdx = 0u; caseNdx < DE_LENGTH_OF_ARRAY(subpassCases); ++caseNdx)
  			{
  				VerifySamples::TestParams params;
  				params.useProgrammableSampleLocations = (locationsGroups[groupNdx] == programmableLocationsGroup.get());
  				params.useFragmentShadingRate = useFragmentShadingRate;
  
  				for (deUint32 subpassNdx = 0; subpassNdx < subpassCases[caseNdx].numSampleCases; ++subpassNdx)
  				{
  					VerifySamples::TestParams::SampleCount	samples;
  					samples.numColorSamples					= subpassCases[caseNdx].pSampleCase[subpassNdx].colorSamples;
  					samples.numDepthStencilSamples			= subpassCases[caseNdx].pSampleCase[subpassNdx].depthStencilSamples;
  					samples.numCoverageSamples				= de::max(samples.numColorSamples, samples.numDepthStencilSamples);
  					params.perSubpassSamples.push_back(samples);
  				}
  
  				MovePtr<tcu::TestCaseGroup> sampleCaseGroup(new tcu::TestCaseGroup(rootGroup->getTestContext(), subpassCases[caseNdx].caseName, ""));
  
  				for (const VkFormat *pDepthStencilFormat = depthStencilReducedFormatRange;	pDepthStencilFormat != DE_ARRAY_END(depthStencilReducedFormatRange); ++pDepthStencilFormat)
  				for (const VkFormat *pColorFormat		 = colorFormatRange;				pColorFormat		!= DE_ARRAY_END(colorFormatRange);				 ++pColorFormat)
  				{
  					params.colorFormat			= *pColorFormat;
  					params.depthStencilFormat	= *pDepthStencilFormat;
  
  					addFunctionCaseWithPrograms(
  						sampleCaseGroup.get(),
  						getFormatCaseName(params.colorFormat, params.depthStencilFormat).c_str(),
  						"",
  						VerifySamples::checkRequirements,
  						VerifySamples::initPrograms,
  						VerifySamples::test, params);
  				}
  
  				locationsGroups[groupNdx]->addChild(sampleCaseGroup.release());
  			}
  		}
  
  		rootGroup->addChild(standardLocationsGroup.release());
  		rootGroup->addChild(programmableLocationsGroup.release());
  	}
  
  	// Test 2: Shader built-ins check
  	if (!useFragmentShadingRate)
  	{
  		MovePtr<tcu::TestCaseGroup> builtinsGroup (new tcu::TestCaseGroup(rootGroup->getTestContext(), "shader_builtins", ""));
  
  		for (deUint32 caseNdx = 0u; caseNdx < DE_LENGTH_OF_ARRAY(singlePassCases); ++caseNdx)
  		{
  			ShaderBuiltins::TestParams params;
  			params.numColorSamples			= singlePassCases[caseNdx].colorSamples;
  			params.numDepthStencilSamples	= singlePassCases[caseNdx].depthStencilSamples;
  			params.numCoverageSamples		= de::max(params.numColorSamples, params.numDepthStencilSamples);
  
  			MovePtr<tcu::TestCaseGroup> sampleCaseGroup(new tcu::TestCaseGroup(
  				rootGroup->getTestContext(), getSampleCountGroupName(params.numCoverageSamples, params.numColorSamples, params.numDepthStencilSamples).c_str(), ""));
  
  			for (const VkFormat *pDepthStencilFormat = depthStencilReducedFormatRange;  pDepthStencilFormat != DE_ARRAY_END(depthStencilReducedFormatRange); ++pDepthStencilFormat)
  			for (const VkFormat *pColorFormat		 = colorFormatRange;				pColorFormat		!= DE_ARRAY_END(colorFormatRange);				 ++pColorFormat)
  			{
  				params.colorFormat			= *pColorFormat;
  				params.depthStencilFormat	= *pDepthStencilFormat;
  
  				addFunctionCaseWithPrograms(
  					sampleCaseGroup.get(),
  					getFormatCaseName(params.colorFormat, params.depthStencilFormat).c_str(),
  					"",
  					ShaderBuiltins::checkRequirements,
  					ShaderBuiltins::initPrograms,
  					ShaderBuiltins::test,
  					params);
  			}
  
  			builtinsGroup->addChild(sampleCaseGroup.release());
  		}
  
  		rootGroup->addChild(builtinsGroup.release());
  	}
  }
  
  } // anonymous ns
  
  tcu::TestCaseGroup* createMultisampleMixedAttachmentSamplesTests (tcu::TestContext& testCtx, bool useFragmentShadingRate)
  {
  	return createTestGroup(testCtx, "mixed_attachment_samples", "Test a graphics pipeline with varying sample count per color and depth/stencil attachments", createMixedAttachmentSamplesTestsInGroup, useFragmentShadingRate);
  }
  
  } // pipeline
  } // vkt
  