Add tests for FragCoord interaction with multisampling

Components: Vulkan
VK-GL-CTS issue: 89

Test: fragcoord_msaa, frontface, fragdepth, clipping
Change-Id: Ibf342e63f224b88fe0b0cffde59b10b34afd1faf
(cherry picked from commit 69a7a0079a6cc06d556ecb5314f275d2fa01a60d)
diff --git a/android/cts/master/src/vk-excluded-tests.txt b/android/cts/master/src/vk-excluded-tests.txt
index 6ce5b49..6b453cb 100644
--- a/android/cts/master/src/vk-excluded-tests.txt
+++ b/android/cts/master/src/vk-excluded-tests.txt
@@ -328,3 +328,6 @@
 
 #VK-GL-CTS 655
 dEQP-VK.glsl.builtin_var.fragdepth.*
+
+#VK-GL-CTS 656
+dEQP-VK.glsl.builtin_var.fragcoord_msaa.*
diff --git a/external/vulkancts/modules/vulkan/shaderrender/vktShaderRenderBuiltinVarTests.cpp b/external/vulkancts/modules/vulkan/shaderrender/vktShaderRenderBuiltinVarTests.cpp
index 59351e2..b9d0058 100644
--- a/external/vulkancts/modules/vulkan/shaderrender/vktShaderRenderBuiltinVarTests.cpp
+++ b/external/vulkancts/modules/vulkan/shaderrender/vktShaderRenderBuiltinVarTests.cpp
@@ -1220,6 +1220,581 @@
 	return true;
 }
 
+class BuiltinFragCoordMsaaCaseInstance : public TestInstance
+{
+public:
+	enum
+	{
+		RENDERWIDTH		= 16,
+		RENDERHEIGHT	= 16
+	};
+				BuiltinFragCoordMsaaCaseInstance	(Context& context, VkSampleCountFlagBits sampleCount);
+	TestStatus	iterate								(void);
+private:
+	bool		validateSampleLocations				(const ConstPixelBufferAccess& sampleLocationBuffer) const;
+
+	const tcu::UVec2				m_renderSize;
+	const VkSampleCountFlagBits		m_sampleCount;
+};
+
+BuiltinFragCoordMsaaCaseInstance::BuiltinFragCoordMsaaCaseInstance (Context& context, VkSampleCountFlagBits sampleCount)
+	: TestInstance		(context)
+	, m_renderSize		(RENDERWIDTH, RENDERHEIGHT)
+	, m_sampleCount		(sampleCount)
+{
+	const InstanceInterface&	vki					= m_context.getInstanceInterface();
+	const VkPhysicalDevice		physicalDevice		= m_context.getPhysicalDevice();
+
+	try
+	{
+		VkImageFormatProperties		imageFormatProperties;
+		VkFormatProperties			formatProperties;
+
+		if (m_context.getDeviceFeatures().fragmentStoresAndAtomics == VK_FALSE)
+			throw tcu::NotSupportedError("fragmentStoresAndAtomics not supported");
+
+		imageFormatProperties = getPhysicalDeviceImageFormatProperties(vki, physicalDevice, VK_FORMAT_R32G32B32A32_SFLOAT, VK_IMAGE_TYPE_2D,
+				VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, (VkImageCreateFlags)0);
+
+		if ((imageFormatProperties.sampleCounts & m_sampleCount) == 0)
+			throw tcu::NotSupportedError("Image format and sample count not supported");
+
+		formatProperties = getPhysicalDeviceFormatProperties(vki, physicalDevice, VK_FORMAT_R32G32B32A32_SFLOAT);
+
+		if ((formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) == 0)
+			throw tcu::NotSupportedError("Output format not supported as storage image");
+	}
+	catch (const vk::Error& e)
+	{
+		if (e.getError() == VK_ERROR_FORMAT_NOT_SUPPORTED)
+			throw tcu::NotSupportedError("Image format not supported");
+		else
+			throw;
+
+	}
+}
+
+TestStatus BuiltinFragCoordMsaaCaseInstance::iterate (void)
+{
+	const VkDevice					device				= m_context.getDevice();
+	const DeviceInterface&			vk					= m_context.getDeviceInterface();
+	const VkQueue					queue				= m_context.getUniversalQueue();
+	Allocator&						allocator			= m_context.getDefaultAllocator();
+	const deUint32					queueFamilyIndex	= m_context.getUniversalQueueFamilyIndex();
+	TestLog&						log					= m_context.getTestContext().getLog();
+	Move<VkImage>					outputImage;
+	Move<VkImageView>				outputImageView;
+	MovePtr<Allocation>				outputImageAllocation;
+	Move<VkDescriptorSetLayout>		descriptorSetLayout;
+	Move<VkDescriptorPool>			descriptorPool;
+	Move<VkDescriptorSet>			descriptorSet;
+	Move<VkBuffer>					sampleLocationBuffer;
+	MovePtr<Allocation>				sampleLocationBufferAllocation;
+	Move<VkCommandPool>				cmdPool;
+	Move<VkCommandBuffer>			transferCmdBuffer;
+	Move<VkFence>					fence;
+
+	// Coordinate result image
+	{
+		const VkImageCreateInfo outputImageCreateInfo =
+		{
+			VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,					// VkStructureType			sType
+			DE_NULL,												// const void*				pNext
+			(VkImageCreateFlags)0,									// VkImageCreateFlags		flags
+			VK_IMAGE_TYPE_2D,										// VkImageType				imageType
+			VK_FORMAT_R32G32B32A32_SFLOAT,							// VkFormat					format
+			makeExtent3D(m_sampleCount * m_renderSize.x(), m_renderSize.y(), 1u),	// VkExtent3D				extent3d
+			1u,														// uint32_t					mipLevels
+			1u,														// uint32_t					arrayLayers
+			VK_SAMPLE_COUNT_1_BIT,									// VkSampleCountFlagBits	samples
+			VK_IMAGE_TILING_OPTIMAL,								// VkImageTiling			tiling
+			VK_IMAGE_USAGE_STORAGE_BIT |							// VkImageUsageFlags		usage
+			VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
+			VK_SHARING_MODE_EXCLUSIVE,								// VkSharingMode			sharingMode
+			0u,														// uint32_t					queueFamilyIndexCount
+			DE_NULL,												// const uint32_t*			pQueueFamilyIndices
+			VK_IMAGE_LAYOUT_UNDEFINED								// VkImageLayout			initialLayout
+		};
+
+		outputImage = createImage(vk, device, &outputImageCreateInfo, DE_NULL);
+		outputImageAllocation = allocator.allocate(getImageMemoryRequirements(vk, device, *outputImage), MemoryRequirement::Any);
+		vk.bindImageMemory(device, *outputImage, outputImageAllocation->getMemory(), outputImageAllocation->getOffset());
+
+		VkImageSubresourceRange imageSubresourceRange = makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
+		const VkImageViewCreateInfo outputImageViewCreateInfo =
+		{
+			VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,				// VkStructureType			sType
+			DE_NULL,												// const void*				pNext
+			(VkImageViewCreateFlags)0,								// VkImageViewCreateFlags	flags
+			*outputImage,											// VkImage					image
+			VK_IMAGE_VIEW_TYPE_2D,									// VkImageViewType			viewType
+			VK_FORMAT_R32G32B32A32_SFLOAT,							// VkFormat					format,
+			makeComponentMappingRGBA(),								// VkComponentMapping		components
+			imageSubresourceRange									// VkImageSubresourceRange	imageSubresourceRange
+		};
+
+		outputImageView = createImageView(vk, device, &outputImageViewCreateInfo);
+	}
+
+	// Validation buffer
+	{
+		VkDeviceSize  pixelSize = getPixelSize(mapVkFormat(VK_FORMAT_R32G32B32A32_SFLOAT));
+		const VkBufferCreateInfo sampleLocationBufferCreateInfo =
+		{
+			VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,				// VkStructureType		sType
+			DE_NULL,											// const void*			pNext
+			(VkBufferCreateFlags)0,								// VkBufferCreateFlags	flags
+			m_sampleCount * m_renderSize.x() * m_renderSize.y() * pixelSize,	// VkDeviceSize			size
+			VK_BUFFER_USAGE_TRANSFER_DST_BIT,					// VkBufferUsageFlags	usage
+			VK_SHARING_MODE_EXCLUSIVE,							// VkSharingMode		mode
+			0u,													// uint32_t				queueFamilyIndexCount
+			DE_NULL												// const uint32_t*		pQueueFamilyIndices
+		};
+
+		sampleLocationBuffer = createBuffer(vk, device, &sampleLocationBufferCreateInfo, DE_NULL);
+		sampleLocationBufferAllocation = allocator.allocate(getBufferMemoryRequirements(vk, device, *sampleLocationBuffer), MemoryRequirement::HostVisible);
+		vk.bindBufferMemory(device, *sampleLocationBuffer, sampleLocationBufferAllocation->getMemory(), sampleLocationBufferAllocation->getOffset());
+	}
+
+	// Descriptors
+	{
+		DescriptorSetLayoutBuilder		layoutBuilder;
+		layoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_SHADER_STAGE_FRAGMENT_BIT);
+		descriptorSetLayout = layoutBuilder.build(vk, device);
+		descriptorPool = DescriptorPoolBuilder()
+			.addType(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)
+			.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 1u);
+
+		const VkDescriptorSetAllocateInfo descriptorSetAllocInfo =
+		{
+			VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+			DE_NULL,
+			*descriptorPool,
+			1u,
+			&*descriptorSetLayout
+		};
+
+		descriptorSet = allocateDescriptorSet(vk, device, &descriptorSetAllocInfo);
+
+		const VkDescriptorImageInfo imageInfo =
+		{
+			(VkSampler)DE_NULL,
+			*outputImageView,
+			VK_IMAGE_LAYOUT_GENERAL
+		};
+
+		DescriptorSetUpdateBuilder()
+			.writeSingle(*descriptorSet, DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, &imageInfo)
+			.update(vk, device);
+	}
+
+	// Command Pool
+	{
+		const VkCommandPoolCreateInfo cmdPoolCreateInfo =
+		{
+			VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,			// VkStructureType			sType
+			DE_NULL,											// const void*				pNext
+			VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,	// VkCommandPoolCreateFlags	flags
+			queueFamilyIndex									// uint32_t					queueFamilyIndex
+		};
+
+		cmdPool = createCommandPool(vk, device, &cmdPoolCreateInfo);
+	}
+
+	// Command buffer for data transfers
+	{
+		const VkCommandBufferAllocateInfo cmdBufferAllocInfo =
+		{
+			VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,	// VkStructureType		sType,
+			DE_NULL,										// const void*			pNext
+			*cmdPool,										// VkCommandPool		commandPool
+			VK_COMMAND_BUFFER_LEVEL_PRIMARY,				// VkCommandBufferLevel	level
+			1u												// uint32_t				bufferCount
+		};
+
+		transferCmdBuffer = allocateCommandBuffer(vk, device, &cmdBufferAllocInfo);
+	}
+
+	// Fence for data transfer
+	{
+		const VkFenceCreateInfo fenceCreateInfo =
+		{
+			VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,	// VkStructureType		sType
+			DE_NULL,								// const void*			pNext
+			(VkFenceCreateFlags)0					// VkFenceCreateFlags	flags
+		};
+
+		fence = createFence(vk, device, &fenceCreateInfo);
+	}
+
+	// Transition the output image to LAYOUT_GENERAL
+	{
+		const VkImageMemoryBarrier barrier =
+		{
+			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,		// VkStructureType		sType
+			DE_NULL,									// const void*			pNext
+			0u,											// VkAccessFlags		srcAccessMask
+			VK_ACCESS_SHADER_WRITE_BIT,					// VkAccessFlags		dstAccessMask
+			VK_IMAGE_LAYOUT_UNDEFINED,					// VkImageLayout		oldLayout
+			VK_IMAGE_LAYOUT_GENERAL,					// VkImageLayout		newLayout
+			VK_QUEUE_FAMILY_IGNORED,					// uint32_t				srcQueueFamilyIndex
+			VK_QUEUE_FAMILY_IGNORED,					// uint32_t				dstQueueFamilyIndex
+			*outputImage,								// VkImage				image
+			{
+				VK_IMAGE_ASPECT_COLOR_BIT,			// VkImageAspectFlags	aspectMask
+				0u,									// uint32_t				baseMipLevel
+				1u,									// uint32_t				mipLevels
+				0u,									// uint32_t				baseArray
+				1u									// uint32_t				arraySize
+			}
+		};
+
+		const VkCommandBufferBeginInfo cmdBufferBeginInfo =
+		{
+			VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,		// VkStructureType					sType
+			DE_NULL,											// const void*						pNext
+			VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,		// VkCommandBufferUsageFlags		flags
+			(const VkCommandBufferInheritanceInfo*)DE_NULL		// VkCommandBufferInheritanceInfo	pInheritanceInfo
+		};
+
+		VK_CHECK(vk.beginCommandBuffer(*transferCmdBuffer, &cmdBufferBeginInfo));
+		vk.cmdPipelineBarrier(*transferCmdBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
+				(VkDependencyFlags)0,
+				0, (const VkMemoryBarrier*)DE_NULL,
+				0, (const VkBufferMemoryBarrier*)DE_NULL,
+				1, &barrier);
+
+		VK_CHECK(vk.endCommandBuffer(*transferCmdBuffer));
+
+		const VkSubmitInfo submitInfo =
+		{
+			VK_STRUCTURE_TYPE_SUBMIT_INFO,			// VkStructureType			sType
+			DE_NULL,								// const void*				pNext
+			0u,										// uint32_t					waitSemaphoreCount
+			DE_NULL,								// const VkSemaphore*		pWaitSemaphores
+			(const VkPipelineStageFlags*)DE_NULL,	// const VkPipelineStageFlags*	pWaitDstStageMask
+			1u,										// uint32_t					commandBufferCount
+			&transferCmdBuffer.get(),				// const VkCommandBuffer*	pCommandBuffers
+			0u,										// uint32_t					signalSemaphoreCount
+			DE_NULL									// const VkSemaphore*		pSignalSemaphores
+		};
+
+		vk.resetFences(device, 1, &fence.get());
+		vk.queueSubmit(queue, 1, &submitInfo, *fence);
+		vk.waitForFences(device, 1, &fence.get(), true, ~(0ull));
+	}
+
+	// Perform draw
+	{
+		std::vector<Vec4>				vertices;
+		std::vector<Shader>				shaders;
+
+		vertices.push_back(Vec4( -1.0f,	-1.0f,	0.0f,	1.0f));
+		vertices.push_back(Vec4( -1.0f,	 1.0f,	0.0f,	1.0f));
+		vertices.push_back(Vec4(  1.0f,	-1.0f,	0.0f,	1.0f));
+		vertices.push_back(Vec4(  1.0f,	 1.0f,	0.0f,	1.0f));
+
+		shaders.push_back(Shader(VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get("FragCoordMsaaVert")));
+		shaders.push_back(Shader(VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("FragCoordMsaaFrag")));
+
+		DrawState			drawState(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, m_renderSize.x(), m_renderSize.y());
+		DrawCallData		drawCallData(vertices);
+		VulkanProgram		vulkanProgram(shaders);
+
+		drawState.numSamples				= m_sampleCount;
+		drawState.sampleShadingEnable		= true;
+		vulkanProgram.descriptorSetLayout	= descriptorSetLayout;
+		vulkanProgram.descriptorSet			= descriptorSet;
+
+		VulkanDrawContext	vulkanDrawContext(m_context, drawState, drawCallData, vulkanProgram);
+		vulkanDrawContext.draw();
+
+		log << TestLog::Image(	"result",
+								"result",
+								tcu::ConstPixelBufferAccess(tcu::TextureFormat(
+										vulkanDrawContext.getColorPixels().getFormat()),
+										vulkanDrawContext.getColorPixels().getWidth(),
+										vulkanDrawContext.getColorPixels().getHeight(),
+										1,
+										vulkanDrawContext.getColorPixels().getDataPtr()));
+	}
+
+	// Transfer location image to buffer
+	{
+		const VkImageMemoryBarrier imageBarrier =
+		{
+			VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,			// VkStructureType		sType
+			DE_NULL,										// const void*			pNext
+			VK_ACCESS_SHADER_WRITE_BIT,						// VkAccessFlags		srcAccessMask
+			VK_ACCESS_TRANSFER_READ_BIT,					// VkAccessMask			dstAccessMask
+			VK_IMAGE_LAYOUT_GENERAL,						// VkImageLayout		oldLayout
+			VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,			// VkImageLayout		newLayout
+			VK_QUEUE_FAMILY_IGNORED,						// uint32_t				srcQueueFamilyIndex
+			VK_QUEUE_FAMILY_IGNORED,						// uint32_t				dstQueueFamilyIndex
+			*outputImage,									// VkImage				image
+			{
+				VK_IMAGE_ASPECT_COLOR_BIT,				// VkImageAspectFlags	aspectMask
+				0u,										// uint32_t				baseMipLevel
+				1u,										// uint32_t				mipLevels
+				0u,										// uint32_t				baseArray
+				1u										// uint32_t				arraySize
+			}
+		};
+
+		const VkBufferMemoryBarrier bufferBarrier =
+		{
+			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
+			*sampleLocationBuffer,							// VkBufer				buffer
+			0u,												// VkDeviceSize			offset
+			VK_WHOLE_SIZE									// VkDeviceSize			size
+		};
+
+		const VkBufferImageCopy bufferImageCopy =
+		{
+			0u,									// VkDeviceSize		bufferOffset
+			m_sampleCount * m_renderSize.x(),	// uint32_t			bufferRowLength
+			m_renderSize.y(),					// uint32_t			bufferImageHeight
+			{
+				VK_IMAGE_ASPECT_COLOR_BIT,	// VkImageAspectFlags	aspect
+				0u,							// uint32_t				mipLevel
+				0u,							// uint32_t				baseArrayLayer
+				1u							// uint32_t				layerCount
+			},
+			{ 0, 0, 0 },						// VkOffset3D		imageOffset
+			{
+				m_sampleCount * m_renderSize.x(),	// uint32_t				width
+				m_renderSize.y(),			// uint32_t				height,
+				1u							// uint32_t				depth
+			}
+		};
+
+		const VkCommandBufferBeginInfo	cmdBufferBeginInfo =
+		{
+			VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,	// VkStructureType					sType
+			DE_NULL,										// const void*						pNext
+			VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,	// VkCommandBufferUsageFlags		flags
+			(const VkCommandBufferInheritanceInfo*)DE_NULL	// VkCommandBufferInheritanceInfo	pInheritanceInfo
+		};
+
+		VK_CHECK(vk.beginCommandBuffer(*transferCmdBuffer, &cmdBufferBeginInfo));
+		vk.cmdPipelineBarrier(*transferCmdBuffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
+				(VkDependencyFlags)0,
+				0, (const VkMemoryBarrier*)DE_NULL,
+				0, (const VkBufferMemoryBarrier*)DE_NULL,
+				1, &imageBarrier);
+		vk.cmdCopyImageToBuffer(*transferCmdBuffer, *outputImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *sampleLocationBuffer, 1u, &bufferImageCopy);
+		vk.cmdPipelineBarrier(*transferCmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT,
+				(VkDependencyFlags)0,
+				0, (const VkMemoryBarrier*)DE_NULL,
+				1, &bufferBarrier,
+				0, (const VkImageMemoryBarrier*)DE_NULL);
+		VK_CHECK(vk.endCommandBuffer(*transferCmdBuffer));
+
+		const VkSubmitInfo submitInfo =
+		{
+			VK_STRUCTURE_TYPE_SUBMIT_INFO,			// VkStructureType			sType
+			DE_NULL,								// const void*				pNext
+			0u,										// uint32_t					waitSemaphoreCount
+			DE_NULL,								// const VkSemaphore*		pWaitSemaphores
+			(const VkPipelineStageFlags*)DE_NULL,	// const VkPipelineStageFlags*	pWaitDstStageMask
+			1u,										// uint32_t					commandBufferCount
+			&transferCmdBuffer.get(),				// const VkCommandBuffer*	pCommandBuffers
+			0u,										// uint32_t					signalSemaphoreCount
+			DE_NULL									// const VkSemaphore*		pSignalSemaphores
+		};
+
+		vk.resetFences(device, 1, &fence.get());
+		vk.queueSubmit(queue, 1, &submitInfo, *fence);
+		vk.waitForFences(device, 1, &fence.get(), true, ~(0ull));
+
+		invalidateMappedMemoryRange(vk, device, sampleLocationBufferAllocation->getMemory(), sampleLocationBufferAllocation->getOffset(), VK_WHOLE_SIZE);
+	}
+
+	// Validate result
+	{
+		bool status;
+
+		ConstPixelBufferAccess sampleLocationPixelBuffer(mapVkFormat(VK_FORMAT_R32G32B32A32_SFLOAT), m_sampleCount * m_renderSize.x(),
+				m_renderSize.y(), 1u, sampleLocationBufferAllocation->getHostPtr());
+
+		status = validateSampleLocations(sampleLocationPixelBuffer);
+		if (status)
+			return TestStatus::pass("FragCoordMsaa passed");
+		else
+			return TestStatus::fail("FragCoordMsaa failed");
+	}
+}
+
+static bool pixelOffsetCompare (const Vec2& a, const Vec2& b)
+{
+	return a.x() < b.x();
+}
+
+bool BuiltinFragCoordMsaaCaseInstance::validateSampleLocations (const ConstPixelBufferAccess& sampleLocationBuffer) const
+{
+	const InstanceInterface&	vki					= m_context.getInstanceInterface();
+	TestLog&					log					= m_context.getTestContext().getLog();
+	const VkPhysicalDevice		physicalDevice		= m_context.getPhysicalDevice();
+	deUint32					logSampleCount		= deLog2Floor32(m_sampleCount);
+	VkPhysicalDeviceProperties	physicalDeviceProperties;
+
+	static const Vec2 sampleCount1Bit[] =
+	{
+		Vec2(0.5f, 0.5f)
+	};
+
+	static const Vec2 sampleCount2Bit[] =
+	{
+		Vec2(0.25f, 0.25f), Vec2(0.75f, 0.75f)
+	};
+
+	static const Vec2 sampleCount4Bit[] =
+	{
+		Vec2(0.375f, 0.125f), Vec2(0.875f, 0.375f), Vec2(0.125f, 0.625f), Vec2(0.625f, 0.875f)
+	};
+
+	static const Vec2 sampleCount8Bit[] =
+	{
+		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 sampleCount16Bit[] =
+	{
+		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)
+	};
+
+	static const Vec2* standardSampleLocationTable[] =
+	{
+		sampleCount1Bit,
+		sampleCount2Bit,
+		sampleCount4Bit,
+		sampleCount8Bit,
+		sampleCount16Bit
+	};
+
+	vki.getPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties);
+
+	for (deInt32 rowNdx = 0; rowNdx < (deInt32)m_renderSize.y(); rowNdx++)
+	{
+		for (deInt32 colNdx = 0; colNdx < (deInt32)m_renderSize.x(); colNdx++)
+		{
+			std::vector<Vec2> locations;
+
+			for (deUint32 sampleNdx = 0; sampleNdx < (deUint32)m_sampleCount; sampleNdx++)
+			{
+				const UVec2 pixelAddress	= UVec2(sampleNdx + m_sampleCount * colNdx, rowNdx);
+				const Vec4  pixelData		= sampleLocationBuffer.getPixel(pixelAddress.x(), pixelAddress.y());
+
+				locations.push_back(Vec2(pixelData.x(), pixelData.y()));
+			}
+
+			std::sort(locations.begin(), locations.end(), pixelOffsetCompare);
+			for (std::vector<Vec2>::const_iterator sampleIt = locations.begin(); sampleIt != locations.end(); sampleIt++)
+			{
+				IVec2	sampleFloor(deFloorFloatToInt32((*sampleIt).x()), deFloorFloatToInt32((*sampleIt).y()));
+				IVec2	sampleCeil(deCeilFloatToInt32((*sampleIt).x()), deCeilFloatToInt32((*sampleIt).y()));
+
+				if ( (sampleFloor.x() < colNdx) || (sampleCeil.x() > colNdx + 1) || (sampleFloor.y() < rowNdx) || (sampleCeil.y() > rowNdx + 1) )
+				{
+					log << TestLog::Message << "Pixel (" << colNdx << "," << rowNdx << "): " << *sampleIt << TestLog::EndMessage;
+					return false;
+				}
+			}
+
+			std::vector<Vec2>::iterator last = std::unique(locations.begin(), locations.end());
+			if (last != locations.end())
+			{
+				log << TestLog::Message << "Fail: Sample locations contains non-unique entry" << TestLog::EndMessage;
+				return false;
+			}
+
+			// Check standard sample locations
+			if (logSampleCount < DE_LENGTH_OF_ARRAY(standardSampleLocationTable))
+			{
+				if (physicalDeviceProperties.limits.standardSampleLocations)
+				{
+					for (deUint32 sampleNdx = 0; sampleNdx < (deUint32)m_sampleCount; sampleNdx++)
+					{
+						if (!de::contains(locations.begin(), locations.end(), standardSampleLocationTable[logSampleCount][sampleNdx] + Vec2(float(colNdx), float(rowNdx))))
+						{
+							log << TestLog::Message << "Didn't match sample locations " << standardSampleLocationTable[logSampleCount][sampleNdx] << TestLog::EndMessage;
+							return false;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return true;
+}
+
+class BuiltinFragCoordMsaaTestCase : public TestCase
+{
+public:
+					BuiltinFragCoordMsaaTestCase	(TestContext& testCtx, const char* name, const char* description, VkSampleCountFlagBits sampleCount);
+	virtual			~BuiltinFragCoordMsaaTestCase	(void);
+	void			initPrograms					(SourceCollections& sourceCollections) const;
+	TestInstance*	createInstance					(Context& context) const;
+private:
+	const VkSampleCountFlagBits			m_sampleCount;
+};
+
+BuiltinFragCoordMsaaTestCase::BuiltinFragCoordMsaaTestCase (TestContext& testCtx, const char* name, const char* description, VkSampleCountFlagBits sampleCount)
+	: TestCase			(testCtx, name, description)
+	, m_sampleCount		(sampleCount)
+{
+}
+
+BuiltinFragCoordMsaaTestCase::~BuiltinFragCoordMsaaTestCase (void)
+{
+}
+
+void BuiltinFragCoordMsaaTestCase::initPrograms (SourceCollections& programCollection) const
+{
+	{
+		std::ostringstream vertexSource;
+		vertexSource << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
+			<< "\n"
+			<<  "layout (location = 0) in vec4 position;\n"
+			<< "void main()\n"
+			<< "{\n"
+			<< "	gl_Position = position;\n"
+			<< "}\n";
+		programCollection.glslSources.add("FragCoordMsaaVert") << glu::VertexSource(vertexSource.str());
+	}
+
+	{
+		std::ostringstream fragmentSource;
+		fragmentSource << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
+			<< "\n"
+			<< "layout(location = 0) out mediump vec4 color;\n"
+			<< "layout (set = 0, binding = 0, rgba32f) writeonly uniform image2D storageImage;\n"
+			<< "void main()\n"
+			<< "{\n"
+			<< "	const int sampleNdx = int(gl_SampleID);\n"
+			<< "	ivec2 imageCoord = ivec2(sampleNdx + int(gl_FragCoord.x) * " << m_sampleCount << ", int(gl_FragCoord.y));\n"
+			<< "	imageStore(storageImage, imageCoord, vec4(gl_FragCoord.xy,vec2(0)));\n"
+			<< "	color = vec4(1.0, 0.0, 0.0, 1.0);\n"
+			<< "}\n";
+		programCollection.glslSources.add("FragCoordMsaaFrag") << glu::FragmentSource(fragmentSource.str());
+	}
+}
+
+TestInstance* BuiltinFragCoordMsaaTestCase::createInstance (Context& context) const
+{
+	return new BuiltinFragCoordMsaaCaseInstance(context, m_sampleCount);
+}
+
 class BuiltinFragDepthCase : public TestCase
 {
 public:
@@ -2000,11 +2575,34 @@
 	de::MovePtr<TestCaseGroup> inputVariationsGroup	(new TestCaseGroup(testCtx, "input_variations", "Input type variation tests."));
 	de::MovePtr<TestCaseGroup> frontFacingGroup		(new TestCaseGroup(testCtx, "frontfacing", "Test gl_Frontfacing keyword."));
 	de::MovePtr<TestCaseGroup> fragDepthGroup		(new TestCaseGroup(testCtx, "fragdepth", "Test gl_FragDepth keyword."));
+	de::MovePtr<TestCaseGroup> fragCoordMsaaGroup	(new TestCaseGroup(testCtx, "fragcoord_msaa", "Test interation between gl_FragCoord and msaa"));
 
 	simpleGroup->addChild(new BuiltinGlFragCoordXYZCase(testCtx, "fragcoord_xyz", "FragCoord xyz test"));
 	simpleGroup->addChild(new BuiltinGlFragCoordWCase(testCtx, "fragcoord_w", "FragCoord w test"));
 	simpleGroup->addChild(new BuiltinGlPointCoordCase(testCtx, "pointcoord", "PointCoord test"));
 
+	// FragCoord_msaa
+	{
+		static const struct FragCoordMsaaCaseList
+		{
+			const char*				name;
+			const char*				description;
+			VkSampleCountFlagBits	sampleCount;
+		} fragCoordMsaaCaseList[] =
+		{
+			{ "1_bit",	"Test FragCoord locations with 2 samples", VK_SAMPLE_COUNT_1_BIT },
+			{ "2_bit",	"Test FragCoord locations with 2 samples", VK_SAMPLE_COUNT_2_BIT },
+			{ "4_bit",	"Test FragCoord locations with 4 samples", VK_SAMPLE_COUNT_4_BIT },
+			{ "8_bit",	"Test FragCoord locations with 8 samples", VK_SAMPLE_COUNT_8_BIT },
+			{ "16_bit",	"Test FragCoord locations with 16 samples", VK_SAMPLE_COUNT_16_BIT },
+			{ "32_bit", "Test FragCoord locations with 32 samples", VK_SAMPLE_COUNT_32_BIT },
+			{ "64-bit", "Test FragCoord locaitons with 64 samples", VK_SAMPLE_COUNT_64_BIT }
+		};
+
+		for (deUint32 caseNdx = 0; caseNdx < DE_LENGTH_OF_ARRAY(fragCoordMsaaCaseList); caseNdx++)
+			fragCoordMsaaGroup->addChild(new BuiltinFragCoordMsaaTestCase(testCtx, fragCoordMsaaCaseList[caseNdx].name, fragCoordMsaaCaseList[caseNdx].description, fragCoordMsaaCaseList[caseNdx].sampleCount));
+	}
+
 	// gl_FrontFacing tests
 	{
 		static const struct PrimitiveTable
@@ -2076,6 +2674,7 @@
 
 	builtinGroup->addChild(frontFacingGroup.release());
 	builtinGroup->addChild(fragDepthGroup.release());
+	builtinGroup->addChild(fragCoordMsaaGroup.release());
 	builtinGroup->addChild(simpleGroup.release());
 
 	for (deUint16 shaderType = 0; shaderType <= (SHADER_INPUT_BUILTIN_BIT | SHADER_INPUT_VARYING_BIT | SHADER_INPUT_CONSTANT_BIT); ++shaderType)
diff --git a/external/vulkancts/mustpass/1.0.2/src/excluded-tests.txt b/external/vulkancts/mustpass/1.0.2/src/excluded-tests.txt
index 6ce5b49..6b453cb 100644
--- a/external/vulkancts/mustpass/1.0.2/src/excluded-tests.txt
+++ b/external/vulkancts/mustpass/1.0.2/src/excluded-tests.txt
@@ -328,3 +328,6 @@
 
 #VK-GL-CTS 655
 dEQP-VK.glsl.builtin_var.fragdepth.*
+
+#VK-GL-CTS 656
+dEQP-VK.glsl.builtin_var.fragcoord_msaa.*