/*------------------------------------------------------------------------
 * Vulkan Conformance Tests
 * ------------------------
 *
 * Copyright (c) 2018 The Khronos Group Inc.
 * Copyright (c) 2018 Google Inc.
 * Copyright (c) 2015 Imagination Technologies Ltd.
 *
 * 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 attachments unused by subpasses
 *//*--------------------------------------------------------------------*/

#include "vktRenderPassUnusedAttachmentTests.hpp"
#include "pipeline/vktPipelineImageUtil.hpp"
#include "vktTestCase.hpp"
#include "vkImageUtil.hpp"
#include "vkMemUtil.hpp"
#include "vkPlatform.hpp"
#include "vkPrograms.hpp"
#include "vkQueryUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkRef.hpp"
#include "vkRefUtil.hpp"
#include "vkTypeUtil.hpp"
#include "vkCmdUtil.hpp"
#include "vkObjUtil.hpp"
#include "tcuImageCompare.hpp"
#include "tcuPlatform.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuTestLog.hpp"
#include "deStringUtil.hpp"
#include "deUniquePtr.hpp"
#include "deRandom.hpp"
#include <cstring>
#include <set>
#include <sstream>
#include <vector>

namespace vkt
{
namespace renderpass
{

using namespace vk;

namespace
{

struct TestParams
{
	VkAttachmentLoadOp	loadOp;
	VkAttachmentStoreOp	storeOp;
	VkAttachmentLoadOp	stencilLoadOp;
	VkAttachmentStoreOp	stencilStoreOp;
	RenderPassType		renderPassType;
};

struct Vertex4RGBA
{
	tcu::Vec4 position;
	tcu::Vec4 color;
};

std::vector<Vertex4RGBA> createQuad (void)
{
	std::vector<Vertex4RGBA>	vertices;

	const float			size				= 0.8f;
	const tcu::Vec4		color				(0.2f, 0.3f, 0.1f, 1.0f);
	const Vertex4RGBA	lowerLeftVertex		= {tcu::Vec4(-size, -size, 0.0f, 1.0f), color};
	const Vertex4RGBA	lowerRightVertex	= {tcu::Vec4(size, -size, 0.0f, 1.0f), color};
	const Vertex4RGBA	upperLeftVertex		= {tcu::Vec4(-size, size, 0.0f, 1.0f), color};
	const Vertex4RGBA	upperRightVertex	= {tcu::Vec4(size, size, 0.0f, 1.0f), color};

	vertices.push_back(lowerLeftVertex);
	vertices.push_back(lowerRightVertex);
	vertices.push_back(upperLeftVertex);
	vertices.push_back(upperLeftVertex);
	vertices.push_back(lowerRightVertex);
	vertices.push_back(upperRightVertex);

	return vertices;
}

template<typename AttachmentDesc, typename AttachmentRef, typename SubpassDesc, typename SubpassDep, typename RenderPassCreateInfo>
Move<VkRenderPass> createRenderPass (const DeviceInterface&	vk,
									 VkDevice				vkDevice,
									 const TestParams		testParams)
{
	const VkImageAspectFlags	aspectMask						= testParams.renderPassType == RENDERPASS_TYPE_LEGACY ? 0 : VK_IMAGE_ASPECT_COLOR_BIT;
	const AttachmentDesc		attachmentDescriptions[]		=
	{
		// Result attachment
		AttachmentDesc (
			DE_NULL,									// const void*						pNext
			(VkAttachmentDescriptionFlags)0,			// VkAttachmentDescriptionFlags		flags
			VK_FORMAT_R8G8B8A8_UNORM,					// VkFormat							format
			VK_SAMPLE_COUNT_1_BIT,						// 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_COLOR_ATTACHMENT_OPTIMAL	// VkImageLayout					finalLayout
		),
		// Unused attachment
		AttachmentDesc (
			DE_NULL,									// const void*						pNext
			(VkAttachmentDescriptionFlags)0,			// VkAttachmentDescriptionFlags		flags
			VK_FORMAT_R8G8B8A8_UNORM,					// VkFormat							format
			VK_SAMPLE_COUNT_1_BIT,						// VkSampleCountFlagBits			samples
			testParams.loadOp,							// VkAttachmentLoadOp				loadOp
			testParams.storeOp,							// VkAttachmentStoreOp				storeOp
			testParams.stencilLoadOp,					// VkAttachmentLoadOp				stencilLoadOp
			testParams.stencilStoreOp,					// VkAttachmentStoreOp				stencilStoreOp
			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	// VkImageLayout					initialLayout
			VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL	// VkImageLayout					finalLayout
		),
		// Input attachment
		AttachmentDesc (
			DE_NULL,									// const void*						pNext
			(VkAttachmentDescriptionFlags)0,			// VkAttachmentDescriptionFlags		flags
			VK_FORMAT_R8G8B8A8_UNORM,					// VkFormat							format
			VK_SAMPLE_COUNT_1_BIT,						// 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_COLOR_ATTACHMENT_OPTIMAL	// VkImageLayout					finalLayout
		)
	};

	// Note: Attachment 1 is not referenced by any subpass.
	const AttachmentRef			resultAttachmentRefSubpass0		(
		DE_NULL,									// const void*			pNext
		2u,											// deUint32				attachment
		VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	// VkImageLayout		layout
		aspectMask									// VkImageAspectFlags	aspectMask
	);

	const AttachmentRef			resultAttachmentRefSubpass1		(
		DE_NULL,									// const void*			pNext
		0u,											// deUint32				attachment
		VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	// VkImageLayout		layout
		aspectMask									// VkImageAspectFlags	aspectMask
	);

	const AttachmentRef			inputAttachmentRefSubpass1		(
		DE_NULL,									// const void*			pNext
		2u,											// deUint32				attachment
		VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,	// VkImageLayout		layout
		aspectMask									// VkImageAspectFlags	aspectMask
	);

	const SubpassDesc			subpassDescriptions[]			=
	{
		SubpassDesc (
			DE_NULL,
			(VkSubpassDescriptionFlags)0,		// VkSubpassDescriptionFlags		flags
			VK_PIPELINE_BIND_POINT_GRAPHICS,	// VkPipelineBindPoint				pipelineBindPoint
			0u,									// deUint32							viewMask
			0u,									// deUint32							inputAttachmentCount
			DE_NULL,							// const VkAttachmentReference*		pInputAttachments
			1u,									// deUint32							colorAttachmentCount
			&resultAttachmentRefSubpass0,		// const VkAttachmentReference*		pColorAttachments
			DE_NULL,							// const VkAttachmentReference*		pResolveAttachments
			DE_NULL,							// const VkAttachmentReference*		pDepthStencilAttachment
			0u,									// deUint32							preserveAttachmentCount
			DE_NULL								// const deUint32*					pPreserveAttachments
		),
		SubpassDesc (
			DE_NULL,
			(VkSubpassDescriptionFlags)0,		// VkSubpassDescriptionFlags		flags
			VK_PIPELINE_BIND_POINT_GRAPHICS,	// VkPipelineBindPoint				pipelineBindPoint
			0u,									// deUint32							viewMask
			1u,									// deUint32							inputAttachmentCount
			&inputAttachmentRefSubpass1,		// const VkAttachmentReference*		pInputAttachments
			1u,									// deUint32							colorAttachmentCount
			&resultAttachmentRefSubpass1,		// const VkAttachmentReference*		pColorAttachments
			DE_NULL,							// const VkAttachmentReference*		pResolveAttachments
			DE_NULL,							// const VkAttachmentReference*		pDepthStencilAttachment
			0u,									// deUint32							preserveAttachmentCount
			DE_NULL								// const deUint32*					pPreserveAttachments
		)
	};

	const SubpassDep			subpassDependency				(
		DE_NULL,										// const void*				pNext
		0u,												// uint32_t					srcSubpass
		1u,												// uint32_t					dstSubpass
		VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,	// VkPipelineStageFlags		srcStageMask
		VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,			// VkPipelineStageFlags		dstStageMask
		VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,			// VkAccessFlags			srcAccessMask
		VK_ACCESS_INPUT_ATTACHMENT_READ_BIT,			// VkAccessFlags			dstAccessMask
		VK_DEPENDENCY_BY_REGION_BIT,					// VkDependencyFlags		dependencyFlags
		0u												// deInt32					viewOffset
	);

	const RenderPassCreateInfo	renderPassInfo					(
		DE_NULL,									// const void*						pNext
		(VkRenderPassCreateFlags)0,					// VkRenderPassCreateFlags			flags
		3u,											// deUint32							attachmentCount
		attachmentDescriptions,						// const VkAttachmentDescription*	pAttachments
		2u,											// deUint32							subpassCount
		subpassDescriptions,						// const VkSubpassDescription*		pSubpasses
		1u,											// deUint32							dependencyCount
		&subpassDependency,							// const VkSubpassDependency*		pDependencies
		0u,											// deUint32							correlatedViewMaskCount
		DE_NULL										// const deUint32*					pCorrelatedViewMasks
	);

	return renderPassInfo.createRenderPass(vk, vkDevice);
}

class UnusedAttachmentTest : public vkt::TestCase
{
public:
										UnusedAttachmentTest	(tcu::TestContext&	testContext,
																 const std::string&	name,
																 const std::string&	description,
																 const TestParams&	testParams);
	virtual								~UnusedAttachmentTest	(void);
	virtual void						initPrograms			(SourceCollections&	sourceCollections) const;
	virtual TestInstance*				createInstance			(Context&			context) const;
private:
	const TestParams					m_testParams;
};

class UnusedAttachmentTestInstance : public vkt::TestInstance
{
public:
										UnusedAttachmentTestInstance	(Context&				context,
																		 const TestParams&		testParams);
	virtual								~UnusedAttachmentTestInstance	(void);
	virtual tcu::TestStatus				iterate							(void);
	template<typename RenderpassSubpass>
	void								createCommandBuffer				(const DeviceInterface&	vk,
																		 VkDevice				vkDevice);
private:
	tcu::TestStatus						verifyImage						(void);

	const tcu::UVec2					m_renderSize;

	Move<VkImage>						m_colorImage;
	de::MovePtr<Allocation>				m_colorImageAlloc;
	Move<VkImageView>					m_colorAttachmentView;

	Move<VkImage>						m_unusedImage;
	de::MovePtr<Allocation>				m_unusedImageAlloc;
	Move<VkImageView>					m_unusedAttachmentView;

	Move<VkImage>						m_inputImage;
	de::MovePtr<Allocation>				m_inputImageAlloc;
	Move<VkImageView>					m_inputAttachmentView;

	Move<VkDescriptorSetLayout>			m_descriptorSetLayoutSubpass0;
	Move<VkDescriptorSetLayout>			m_descriptorSetLayoutSubpass1;
	Move<VkDescriptorPool>				m_descriptorPool;
	Move<VkDescriptorSet>				m_descriptorSetSubpass1;
	Move<VkRenderPass>					m_renderPass;
	Move<VkFramebuffer>					m_framebuffer;

	Move<VkShaderModule>				m_vertexShaderModule;
	Move<VkShaderModule>				m_fragmentShaderModuleSubpass0;
	Move<VkShaderModule>				m_fragmentShaderModuleSubpass1;

	Move<VkBuffer>						m_vertexBuffer;
	std::vector<Vertex4RGBA>			m_vertices;
	de::MovePtr<Allocation>				m_vertexBufferAlloc;

	Move<VkBuffer>						m_backingBuffer;
	de::MovePtr<Allocation>				m_backingBufferAlloc;

	Move<VkPipelineLayout>				m_pipelineLayoutSubpass0;
	Move<VkPipelineLayout>				m_pipelineLayoutSubpass1;
	Move<VkPipeline>					m_graphicsPipelineSubpass0;
	Move<VkPipeline>					m_graphicsPipelineSubpass1;

	Move<VkCommandPool>					m_cmdPool;
	Move<VkCommandBuffer>				m_cmdBuffer;
};

UnusedAttachmentTest::UnusedAttachmentTest (tcu::TestContext&	testContext,
											const std::string&	name,
											const std::string&	description,
											const TestParams&	testParams)
	: vkt::TestCase	(testContext, name, description)
	, m_testParams	(testParams)
{
}

UnusedAttachmentTest::~UnusedAttachmentTest (void)
{
}

TestInstance* UnusedAttachmentTest::createInstance (Context& context) const
{
	return new UnusedAttachmentTestInstance(context, m_testParams);
}

void UnusedAttachmentTest::initPrograms (SourceCollections& sourceCollections) const
{
	std::ostringstream fragmentSource;

	sourceCollections.glslSources.add("color_vert") << glu::VertexSource(
		"#version 450\n"
		"layout(location = 0) in highp vec4 position;\n"
		"layout(location = 1) in highp vec4 color;\n"
		"layout(location = 0) out highp vec4 vtxColor;\n"
		"void main (void)\n"
		"{\n"
		"	gl_Position = position;\n"
		"	vtxColor = color;\n"
		"}\n");

	sourceCollections.glslSources.add("color_frag_sb0") << glu::FragmentSource(
		"#version 450\n"
		"layout(location = 0) in highp vec4 vtxColor;\n"
		"layout(location = 0) out highp vec4 fragColor;\n"
		"void main (void)\n"
		"{\n"
		"	fragColor = vtxColor;\n"
		"}\n");

	sourceCollections.glslSources.add("color_frag_sb1") << glu::FragmentSource(
		"#version 450\n"
		"layout(location = 0) in highp vec4 vtxColor;\n"
		"layout(location = 0) out highp vec4 fragColor;\n"
		"layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput inputColor;"
		"void main (void)\n"
		"{\n"
		"	fragColor = subpassLoad(inputColor) + vtxColor;\n"
		"}\n");
}

UnusedAttachmentTestInstance::UnusedAttachmentTestInstance (Context&			context,
															const TestParams&	testParams)
	: vkt::TestInstance	(context)
	, m_renderSize		(32u, 32u)
	, m_vertices		(createQuad())
{
	const DeviceInterface&		vk						= m_context.getDeviceInterface();
	const VkDevice				vkDevice				= m_context.getDevice();
	const deUint32				queueFamilyIndex		= m_context.getUniversalQueueFamilyIndex();
	SimpleAllocator				memAlloc				(vk, vkDevice, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice()));
	const VkComponentMapping	componentMappingRGBA	= { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A };

	// Check for renderpass2 extension if used
	if (testParams.renderPassType == RENDERPASS_TYPE_RENDERPASS2)
		context.requireDeviceExtension("VK_KHR_create_renderpass2");

	// Create color image
	{
		const VkImageCreateInfo	colorImageParams =
		{
			VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,										// VkStructureType			sType;
			DE_NULL,																	// const void*				pNext;
			0u,																			// VkImageCreateFlags		flags;
			VK_IMAGE_TYPE_2D,															// VkImageType				imageType;
			VK_FORMAT_R8G8B8A8_UNORM,													// VkFormat					format;
			{ m_renderSize.x(), m_renderSize.y(), 1u },									// VkExtent3D				extent;
			1u,																			// deUint32					mipLevels;
			1u,																			// deUint32					arrayLayers;
			VK_SAMPLE_COUNT_1_BIT,														// VkSampleCountFlagBits	samples;
			VK_IMAGE_TILING_OPTIMAL,													// VkImageTiling			tiling;
			VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,		// VkImageUsageFlags		usage;
			VK_SHARING_MODE_EXCLUSIVE,													// VkSharingMode			sharingMode;
			1u,																			// deUint32					queueFamilyIndexCount;
			&queueFamilyIndex,															// const deUint32*			pQueueFamilyIndices;
			VK_IMAGE_LAYOUT_UNDEFINED													// VkImageLayout			initialLayout;
		};

		m_colorImage			= createImage(vk, vkDevice, &colorImageParams);

		// Allocate and bind color image memory
		m_colorImageAlloc		= memAlloc.allocate(getImageMemoryRequirements(vk, vkDevice, *m_colorImage), MemoryRequirement::Any);
		VK_CHECK(vk.bindImageMemory(vkDevice, *m_colorImage, m_colorImageAlloc->getMemory(), m_colorImageAlloc->getOffset()));
	}

	// Create image which is not used by any subpass
	{
		const VkImageCreateInfo	unusedImageParams =
		{
			VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,										// VkStructureType			sType;
			DE_NULL,																	// const void*				pNext;
			0u,																			// VkImageCreateFlags		flags;
			VK_IMAGE_TYPE_2D,															// VkImageType				imageType;
			VK_FORMAT_R8G8B8A8_UNORM,													// VkFormat					format;
			{ m_renderSize.x(), m_renderSize.y(), 1u },									// VkExtent3D				extent;
			1u,																			// deUint32					mipLevels;
			1u,																			// deUint32					arrayLayers;
			VK_SAMPLE_COUNT_1_BIT,														// VkSampleCountFlagBits	samples;
			VK_IMAGE_TILING_OPTIMAL,													// VkImageTiling			tiling;
			VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT
				| VK_IMAGE_USAGE_TRANSFER_DST_BIT,										// VkImageUsageFlags		usage;
			VK_SHARING_MODE_EXCLUSIVE,													// VkSharingMode			sharingMode;
			1u,																			// deUint32					queueFamilyIndexCount;
			&queueFamilyIndex,															// const deUint32*			pQueueFamilyIndices;
			VK_IMAGE_LAYOUT_UNDEFINED													// VkImageLayout			initialLayout;
		};

		m_unusedImage			= createImage(vk, vkDevice, &unusedImageParams);

		// Allocate and bind unused image memory
		VkMemoryRequirements	memoryRequirements	= getImageMemoryRequirements(vk, vkDevice, *m_unusedImage);

		m_unusedImageAlloc		= memAlloc.allocate(memoryRequirements, MemoryRequirement::Any);
		VK_CHECK(vk.bindImageMemory(vkDevice, *m_unusedImage, m_unusedImageAlloc->getMemory(), m_unusedImageAlloc->getOffset()));

		// Clear image with specific value to verify the contents don't change
		{
			const VkImageAspectFlags		aspectMask	= VK_IMAGE_ASPECT_COLOR_BIT;
			Move<VkCommandPool>				cmdPool;
			Move<VkCommandBuffer>			cmdBuffer;

			VkClearValue					clearValue;
			clearValue.color.float32[0] = 0.1f;
			clearValue.color.float32[1] = 0.2f;
			clearValue.color.float32[2] = 0.3f;
			clearValue.color.float32[3] = 0.4f;

			// Create command pool and buffer
			cmdPool		= createCommandPool(vk, vkDevice, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, queueFamilyIndex);
			cmdBuffer	= allocateCommandBuffer(vk, vkDevice, *cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);

			const VkImageMemoryBarrier preImageBarrier =
			{
				VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,	// VkStructureType			sType;
				DE_NULL,								// const void*				pNext;
				0u,										// VkAccessFlags			srcAccessMask;
				VK_ACCESS_TRANSFER_WRITE_BIT,			// VkAccessFlags			dstAccessMask;
				VK_IMAGE_LAYOUT_UNDEFINED,				// VkImageLayout			oldLayout;
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,	// VkImageLayout			newLayout;
				VK_QUEUE_FAMILY_IGNORED,				// deUint32					srcQueueFamilyIndex;
				VK_QUEUE_FAMILY_IGNORED,				// deUint32					dstQueueFamilyIndex;
				*m_unusedImage,							// VkImage					image;
				{										// VkImageSubresourceRange	subresourceRange;
					aspectMask,							// VkImageAspect			aspect;
					0u,									// deUint32					baseMipLevel;
					1u,									// deUint32					mipLevels;
					0u,									// deUint32					baseArraySlice;
					1u									// deUint32					arraySize;
				}
			};

			const VkImageMemoryBarrier postImageBarrier =
			{
				VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,		// VkStructureType			sType;
				DE_NULL,									// const void*				pNext;
				VK_ACCESS_TRANSFER_WRITE_BIT,				// VkAccessFlags			srcAccessMask;
				VK_ACCESS_SHADER_READ_BIT,					// VkAccessFlags			dstAccessMask;
				VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,		// VkImageLayout			oldLayout;
				VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,	// VkImageLayout			newLayout;
				VK_QUEUE_FAMILY_IGNORED,					// deUint32					srcQueueFamilyIndex;
				VK_QUEUE_FAMILY_IGNORED,					// deUint32					dstQueueFamilyIndex;
				*m_unusedImage,								// VkImage					image;
				{											// VkImageSubresourceRange	subresourceRange;
					aspectMask,								// VkImageAspect			aspect;
					0u,										// deUint32					baseMipLevel;
					1u,										// deUint32					mipLevels;
					0u,										// deUint32					baseArraySlice;
					1u										// deUint32					arraySize;
				}
			};

			const VkImageSubresourceRange clearRange	=
			{
				aspectMask,	// VkImageAspectFlags	aspectMask;
				0u,			// deUint32				baseMipLevel;
				1u,			// deUint32				levelCount;
				0u,			// deUint32				baseArrayLayer;
				1u			// deUint32				layerCount;
			};

			// Clear image
			beginCommandBuffer(vk, *cmdBuffer);
			vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &preImageBarrier);
			vk.cmdClearColorImage(*cmdBuffer, *m_unusedImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearValue.color, 1, &clearRange);
			vk.cmdPipelineBarrier(*cmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, (VkDependencyFlags)0, 0, (const VkMemoryBarrier*)DE_NULL, 0, (const VkBufferMemoryBarrier*)DE_NULL, 1, &postImageBarrier);
			endCommandBuffer(vk, *cmdBuffer);

			submitCommandsAndWait(vk, vkDevice, m_context.getUniversalQueue(), cmdBuffer.get());
		}
	}

	// Create input image
	{
		const VkImageCreateInfo	inputImageParams =
		{
			VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,										// VkStructureType			sType;
			DE_NULL,																	// const void*				pNext;
			0u,																			// VkImageCreateFlags		flags;
			VK_IMAGE_TYPE_2D,															// VkImageType				imageType;
			VK_FORMAT_R8G8B8A8_UNORM,													// VkFormat					format;
			{ m_renderSize.x(), m_renderSize.y(), 1u },									// VkExtent3D				extent;
			1u,																			// deUint32					mipLevels;
			1u,																			// deUint32					arrayLayers;
			VK_SAMPLE_COUNT_1_BIT,														// VkSampleCountFlagBits	samples;
			VK_IMAGE_TILING_OPTIMAL,													// VkImageTiling			tiling;
			VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT,	// VkImageUsageFlags		usage;
			VK_SHARING_MODE_EXCLUSIVE,													// VkSharingMode			sharingMode;
			1u,																			// deUint32					queueFamilyIndexCount;
			&queueFamilyIndex,															// const deUint32*			pQueueFamilyIndices;
			VK_IMAGE_LAYOUT_UNDEFINED													// VkImageLayout			initialLayout;
		};

		m_inputImage = createImage(vk, vkDevice, &inputImageParams);

		// Allocate and bind input image memory
		m_inputImageAlloc = memAlloc.allocate(getImageMemoryRequirements(vk, vkDevice, *m_inputImage), MemoryRequirement::Any);
		VK_CHECK(vk.bindImageMemory(vkDevice, *m_inputImage, m_inputImageAlloc->getMemory(), m_inputImageAlloc->getOffset()));
	}

	// Create color attachment view
	{
		const VkImageViewCreateInfo colorAttachmentViewParams =
		{
			VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,			// VkStructureType			sType;
			DE_NULL,											// const void*				pNext;
			0u,													// VkImageViewCreateFlags	flags;
			*m_colorImage,										// VkImage					image;
			VK_IMAGE_VIEW_TYPE_2D,								// VkImageViewType			viewType;
			VK_FORMAT_R8G8B8A8_UNORM,							// VkFormat					format;
			componentMappingRGBA,								// VkChannelMapping			channels;
			{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }		// VkImageSubresourceRange	subresourceRange;
		};

		m_colorAttachmentView = createImageView(vk, vkDevice, &colorAttachmentViewParams);
	}

	// Create unused attachment view
	{
		const VkImageViewCreateInfo unusedAttachmentViewParams =
		{
			VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,			// VkStructureType			sType;
			DE_NULL,											// const void*				pNext;
			0u,													// VkImageViewCreateFlags	flags;
			*m_unusedImage,										// VkImage					image;
			VK_IMAGE_VIEW_TYPE_2D,								// VkImageViewType			viewType;
			VK_FORMAT_R8G8B8A8_UNORM,							// VkFormat					format;
			componentMappingRGBA,								// VkChannelMapping			channels;
			{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }		// VkImageSubresourceRange	subresourceRange;
		};

		m_unusedAttachmentView = createImageView(vk, vkDevice, &unusedAttachmentViewParams);
	}

	// Create input attachment view
	{
		const VkImageViewCreateInfo inputAttachmentViewParams =
		{
			VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,			// VkStructureType			sType;
			DE_NULL,											// const void*				pNext;
			0u,													// VkImageViewCreateFlags	flags;
			*m_inputImage,										// VkImage					image;
			VK_IMAGE_VIEW_TYPE_2D,								// VkImageViewType			viewType;
			VK_FORMAT_R8G8B8A8_UNORM,							// VkFormat					format;
			componentMappingRGBA,								// VkChannelMapping			channels;
			{ VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u }		// VkImageSubresourceRange	subresourceRange;
		};

		m_inputAttachmentView = createImageView(vk, vkDevice, &inputAttachmentViewParams);
	}

	// Create render pass
	if (testParams.renderPassType == RENDERPASS_TYPE_LEGACY)
		m_renderPass = createRenderPass<AttachmentDescription1, AttachmentReference1, SubpassDescription1, SubpassDependency1, RenderPassCreateInfo1>(vk, vkDevice, testParams);
	else
		m_renderPass = createRenderPass<AttachmentDescription2, AttachmentReference2, SubpassDescription2, SubpassDependency2, RenderPassCreateInfo2>(vk, vkDevice, testParams);

	// Create framebuffer
	{
		const VkImageView				imageViews[]		=
		{
			*m_colorAttachmentView,
			*m_unusedAttachmentView,
			*m_inputAttachmentView
		};

		const VkFramebufferCreateInfo	framebufferParams	=
		{
			VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,	// VkStructureType			sType;
			DE_NULL,									// const void*				pNext;
			0u,											// VkFramebufferCreateFlags	flags;
			*m_renderPass,								// VkRenderPass				renderPass;
			3u,											// deUint32					attachmentCount;
			imageViews,									// const VkImageView*		pAttachments;
			(deUint32)m_renderSize.x(),					// deUint32					width;
			(deUint32)m_renderSize.y(),					// deUint32					height;
			1u											// deUint32					layers;
		};

		m_framebuffer = createFramebuffer(vk, vkDevice, &framebufferParams);
	}

	// Create pipeline layout for subpass 0
	{
		const VkDescriptorSetLayoutCreateInfo	descriptorSetLayoutParams	=
		{
			VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,	// VkStructureType						sType
			DE_NULL,												// const void*							pNext
			0u,														// VkDescriptorSetLayoutCreateFlags		flags
			0u,														// deUint32								bindingCount
			DE_NULL													// const VkDescriptorSetLayoutBinding*	pBindings
		};
		m_descriptorSetLayoutSubpass0 = createDescriptorSetLayout(vk, vkDevice, &descriptorSetLayoutParams);

		const VkPipelineLayoutCreateInfo		pipelineLayoutParams		=
		{
			VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,		// VkStructureType					sType;
			DE_NULL,											// const void*						pNext;
			0u,													// VkPipelineLayoutCreateFlags		flags;
			1u,													// deUint32							setLayoutCount;
			&m_descriptorSetLayoutSubpass0.get(),				// const VkDescriptorSetLayout*		pSetLayouts;
			0u,													// deUint32							pushConstantRangeCount;
			DE_NULL												// const VkPushConstantRange*		pPushConstantRanges;
		};

		m_pipelineLayoutSubpass0 = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
	}

	// Create pipeline layout for subpass 1
	{
		const VkDescriptorSetLayoutBinding		layoutBinding				=
		{
			0u,										// deUint32				binding;
			VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,	// VkDescriptorType		descriptorType;
			1u,										// deUint32				descriptorCount;
			VK_SHADER_STAGE_FRAGMENT_BIT,			// VkShaderStageFlags	stageFlags;
			DE_NULL									// const VkSampler*		pImmutableSamplers;
		};

		const VkDescriptorSetLayoutCreateInfo	descriptorSetLayoutParams	=
		{
			VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,	// VkStructureType						sType
			DE_NULL,												// const void*							pNext
			0u,														// VkDescriptorSetLayoutCreateFlags		flags
			1u,														// deUint32								bindingCount
			&layoutBinding											// const VkDescriptorSetLayoutBinding*	pBindings
		};
		m_descriptorSetLayoutSubpass1 = createDescriptorSetLayout(vk, vkDevice, &descriptorSetLayoutParams);

		const VkPipelineLayoutCreateInfo		pipelineLayoutParams		=
		{
			VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,		// VkStructureType					sType;
			DE_NULL,											// const void*						pNext;
			0u,													// VkPipelineLayoutCreateFlags		flags;
			1u,													// deUint32							setLayoutCount;
			&m_descriptorSetLayoutSubpass1.get(),				// const VkDescriptorSetLayout*		pSetLayouts;
			0u,													// deUint32							pushConstantRangeCount;
			DE_NULL												// const VkPushConstantRange*		pPushConstantRanges;
		};

		m_pipelineLayoutSubpass1 = createPipelineLayout(vk, vkDevice, &pipelineLayoutParams);
	}

	// Update descriptor set
	{
		const VkDescriptorPoolSize			descriptorPoolSize			=
        {
			VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,	// VkDescriptorType		type;
			1u										// deUint32				descriptorCount;
		};

		const VkDescriptorPoolCreateInfo	descriptorPoolCreateInfo	=
		{
			VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,		// VkStructureType				sType
			DE_NULL,											// const void*					pNext
			VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,	// VkDescriptorPoolCreateFlags	flags
			1u,                                                 // deUint32						maxSets
			1u,													// deUint32						poolSizeCount
			&descriptorPoolSize									// const VkDescriptorPoolSize*	pPoolSizes
		};

		m_descriptorPool = createDescriptorPool(vk, vkDevice, &descriptorPoolCreateInfo);

		const VkDescriptorSetAllocateInfo	descriptorSetAllocateInfo	=
		{
			VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,		// VkStructureType					sType
			DE_NULL,											// const void*						pNext
			*m_descriptorPool,									// VkDescriptorPool					descriptorPool
			1u,													// deUint32							descriptorSetCount
			&m_descriptorSetLayoutSubpass1.get(),				// const VkDescriptorSetLayout*		pSetLayouts
		};

		m_descriptorSetSubpass1 = allocateDescriptorSet(vk, vkDevice, &descriptorSetAllocateInfo);

		const VkDescriptorImageInfo			inputImageInfo				=
		{
			DE_NULL,									// VkSampleri		sampler;
			*m_inputAttachmentView,						// VkImageView		imageView;
			VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL	// VkImageLayout	imageLayout;
		};

		const VkWriteDescriptorSet			descriptorWrite				=
		{
			VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,		// VkStructureType					sType;
			DE_NULL,									// const void*						pNext;
			*m_descriptorSetSubpass1,					// VkDescriptorSet					dstSet;
			0u,											// deUint32							dstBinding;
			0u,											// deUint32							dstArrayElement;
			1u,											// deUint32							descriptorCount;
			VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,		// VkDescriptorType					descriptorType;
			&inputImageInfo,							// const VkDescriptorImageInfo*		pImageInfo;
			DE_NULL,									// const VkDescriptorBufferInfo*	pBufferInfo;
			DE_NULL										// const VkBufferView*				pTexelBufferView;
		};

		vk.updateDescriptorSets(vkDevice, 1u, &descriptorWrite, 0u, DE_NULL);
	}

	m_vertexShaderModule	= createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("color_vert"), 0);
	m_fragmentShaderModuleSubpass0	= createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("color_frag_sb0"), 0);
	m_fragmentShaderModuleSubpass1	= createShaderModule(vk, vkDevice, m_context.getBinaryCollection().get("color_frag_sb1"), 0);

	// Create pipelines
	{
		const VkVertexInputBindingDescription		vertexInputBindingDescription		=
		{
			0u,									// deUint32					binding;
			sizeof(Vertex4RGBA),				// deUint32					strideInBytes;
			VK_VERTEX_INPUT_RATE_VERTEX			// VkVertexInputStepRate	inputRate;
		};

		const VkVertexInputAttributeDescription		vertexInputAttributeDescriptions[2]	=
		{
			{
				0u,								// deUint32		location;
				0u,								// deUint32		binding;
				VK_FORMAT_R32G32B32A32_SFLOAT,	// VkFormat		format;
				0u								// deUint32		offset;
			},
			{
				1u,								// deUint32		location;
				0u,								// deUint32		binding;
				VK_FORMAT_R32G32B32A32_SFLOAT,	// VkFormat		format;
				(deUint32)(sizeof(float) * 4),	// deUint32		offset;
			}
		};

		const VkPipelineVertexInputStateCreateInfo	vertexInputStateParams				=
		{
			VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,	// VkStructureType							sType;
			DE_NULL,													// const void*								pNext;
			0u,															// VkPipelineVertexInputStateCreateFlags	flags;
			1u,															// deUint32									vertexBindingDescriptionCount;
			&vertexInputBindingDescription,								// const VkVertexInputBindingDescription*	pVertexBindingDescriptions;
			2u,															// deUint32									vertexAttributeDescriptionCount;
			vertexInputAttributeDescriptions							// const VkVertexInputAttributeDescription*	pVertexAttributeDescriptions;
		};

		const std::vector<VkViewport>				viewports							(1, makeViewport(m_renderSize));
		const std::vector<VkRect2D>					scissors							(1, makeRect2D(m_renderSize));

		{
			m_graphicsPipelineSubpass0 = makeGraphicsPipeline(vk,									// const DeviceInterface&						vk
															  vkDevice,								// const VkDevice								device
															  *m_pipelineLayoutSubpass0,			// const VkPipelineLayout						pipelineLayout
															  *m_vertexShaderModule,				// const VkShaderModule							vertexShaderModule
															  DE_NULL,								// const VkShaderModule							tessellationControlModule
															  DE_NULL,								// const VkShaderModule							tessellationEvalModule
															  DE_NULL,								// const VkShaderModule							geometryShaderModule
															  *m_fragmentShaderModuleSubpass0,		// const VkShaderModule							fragmentShaderModule
															  *m_renderPass,						// const VkRenderPass							renderPass
															  viewports,							// const std::vector<VkViewport>&				viewports
															  scissors,								// const std::vector<VkRect2D>&					scissors
															  VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,	// const VkPrimitiveTopology					topology
															  0u,									// const deUint32								subpass
															  0u,									// const deUint32								patchControlPoints
															  &vertexInputStateParams);				// const VkPipelineVertexInputStateCreateInfo*	vertexInputStateCreateInfo

			m_graphicsPipelineSubpass1 = makeGraphicsPipeline(vk,									// const DeviceInterface&						vk
															  vkDevice,								// const VkDevice								device
															  *m_pipelineLayoutSubpass1,			// const VkPipelineLayout						pipelineLayout
															  *m_vertexShaderModule,				// const VkShaderModule							vertexShaderModule
															  DE_NULL,								// const VkShaderModule							tessellationControlModule
															  DE_NULL,								// const VkShaderModule							tessellationEvalModule
															  DE_NULL,								// const VkShaderModule							geometryShaderModule
															  *m_fragmentShaderModuleSubpass1,		// const VkShaderModule							fragmentShaderModule
															  *m_renderPass,						// const VkRenderPass							renderPass
															  viewports,							// const std::vector<VkViewport>&				viewports
															  scissors,								// const std::vector<VkRect2D>&					scissors
															  VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,	// const VkPrimitiveTopology					topology
															  1u,									// const deUint32								subpass
															  0u,									// const deUint32								patchControlPoints
															  &vertexInputStateParams);				// const VkPipelineVertexInputStateCreateInfo*	vertexInputStateCreateInfo
		}
	}

	// Create vertex buffer
	{
		const VkBufferCreateInfo vertexBufferParams =
		{
			VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,						// VkStructureType		sType;
			DE_NULL,													// const void*			pNext;
			0u,															// VkBufferCreateFlags	flags;
			(VkDeviceSize)(sizeof(Vertex4RGBA) * m_vertices.size()),	// VkDeviceSize			size;
			VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,							// VkBufferUsageFlags	usage;
			VK_SHARING_MODE_EXCLUSIVE,									// VkSharingMode		sharingMode;
			1u,															// deUint32				queueFamilyIndexCount;
			&queueFamilyIndex											// const deUint32*		pQueueFamilyIndices;
		};

		m_vertexBuffer		= createBuffer(vk, vkDevice, &vertexBufferParams);
		m_vertexBufferAlloc	= memAlloc.allocate(getBufferMemoryRequirements(vk, vkDevice, *m_vertexBuffer), MemoryRequirement::HostVisible);

		VK_CHECK(vk.bindBufferMemory(vkDevice, *m_vertexBuffer, m_vertexBufferAlloc->getMemory(), m_vertexBufferAlloc->getOffset()));

		// Upload vertex data
		deMemcpy(m_vertexBufferAlloc->getHostPtr(), m_vertices.data(), m_vertices.size() * sizeof(Vertex4RGBA));
		flushAlloc(vk, vkDevice, *m_vertexBufferAlloc);
	}

	// Create command pool
	m_cmdPool = createCommandPool(vk, vkDevice, VK_COMMAND_POOL_CREATE_TRANSIENT_BIT, queueFamilyIndex);

	// Create command buffer
	if (testParams.renderPassType == RENDERPASS_TYPE_LEGACY)
		createCommandBuffer<RenderpassSubpass1>(vk, vkDevice);
	else
		createCommandBuffer<RenderpassSubpass2>(vk, vkDevice);
}

UnusedAttachmentTestInstance::~UnusedAttachmentTestInstance (void)
{
}

template<typename RenderpassSubpass>
void UnusedAttachmentTestInstance::createCommandBuffer (const DeviceInterface&	vk,
														VkDevice				vkDevice)
{
	const typename RenderpassSubpass::SubpassBeginInfo	subpassBeginInfo	(DE_NULL, VK_SUBPASS_CONTENTS_INLINE);
	const typename RenderpassSubpass::SubpassEndInfo	subpassEndInfo		(DE_NULL);

	const VkClearValue attachmentClearValues[] =
	{
		makeClearValueColorF32(0.5f, 0.5f, 0.5f, 1.0f),	// color
		makeClearValueColorF32(0.5f, 0.5f, 0.5f, 1.0f),	// unused
		makeClearValueColorF32(0.5f, 0.2f, 0.1f, 1.0f)	// input
	};

	const VkDeviceSize vertexBufferOffset = 0;

	m_cmdBuffer = allocateCommandBuffer(vk, vkDevice, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);

	beginCommandBuffer(vk, *m_cmdBuffer, 0u);

	const VkRenderPassBeginInfo renderPassBeginInfo =
	{
		VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,	// VkStructureType		sType;
		DE_NULL,									// const void*			pNext;
		*m_renderPass,								// VkRenderPass			renderPass;
		*m_framebuffer,								// VkFramebuffer		framebuffer;
		makeRect2D(m_renderSize),					// VkRect2D				renderArea;
		3u,											// uint32_t				clearValueCount;
		attachmentClearValues						// const VkClearValue*	pClearValues;
	};
	RenderpassSubpass::cmdBeginRenderPass(vk, *m_cmdBuffer, &renderPassBeginInfo, &subpassBeginInfo);

	vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_graphicsPipelineSubpass0);
	vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, 1, &m_vertexBuffer.get(), &vertexBufferOffset);
	vk.cmdDraw(*m_cmdBuffer, (deUint32)m_vertices.size(), 1, 0, 0);
	vk.cmdNextSubpass(*m_cmdBuffer, VK_SUBPASS_CONTENTS_INLINE);
	vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_graphicsPipelineSubpass1);
	vk.cmdBindDescriptorSets(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelineLayoutSubpass1, 0, 1, &m_descriptorSetSubpass1.get(), 0, DE_NULL);
	vk.cmdDraw(*m_cmdBuffer, (deUint32)m_vertices.size(), 1, 0, 0);

	RenderpassSubpass::cmdEndRenderPass(vk, *m_cmdBuffer, &subpassEndInfo);
	endCommandBuffer(vk, *m_cmdBuffer);
}

tcu::TestStatus UnusedAttachmentTestInstance::iterate (void)
{
	const DeviceInterface&		vk			= m_context.getDeviceInterface();
	const VkDevice				vkDevice	= m_context.getDevice();
	const VkQueue				queue		= m_context.getUniversalQueue();

	submitCommandsAndWait(vk, vkDevice, queue, m_cmdBuffer.get());

	return verifyImage();
}

tcu::TestStatus UnusedAttachmentTestInstance::verifyImage (void)
{
	const DeviceInterface&				vk						= m_context.getDeviceInterface();
	const VkDevice						vkDevice				= m_context.getDevice();
	const VkQueue						queue					= m_context.getUniversalQueue();
	const deUint32						queueFamilyIndex		= m_context.getUniversalQueueFamilyIndex();
	SimpleAllocator						allocator				(vk, vkDevice, getPhysicalDeviceMemoryProperties(m_context.getInstanceInterface(), m_context.getPhysicalDevice()));
	de::UniquePtr<tcu::TextureLevel>	textureLevelResult		(pipeline::readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, allocator, *m_colorImage, VK_FORMAT_R8G8B8A8_UNORM, m_renderSize).release());
	const tcu::ConstPixelBufferAccess&	resultAccess			= textureLevelResult->getAccess();
	de::UniquePtr<tcu::TextureLevel>	textureLevelUnused		(pipeline::readColorAttachment(vk, vkDevice, queue, queueFamilyIndex, allocator, *m_unusedImage, VK_FORMAT_R8G8B8A8_UNORM, m_renderSize).release());
	const tcu::ConstPixelBufferAccess&	unusedAccess			= textureLevelUnused->getAccess();
	tcu::TestLog&						log						= m_context.getTestContext().getLog();

	// Log images
	log << tcu::TestLog::ImageSet("Result", "Result images")
		<< tcu::TestLog::Image("Rendered", "Rendered image", resultAccess)
		<< tcu::TestLog::Image("Unused", "Unused image", unusedAccess)
		<< tcu::TestLog::EndImageSet;

	// Check the unused image data hasn't changed.
	for (int y = 0; y < unusedAccess.getHeight(); y++)
		for (int x = 0; x < unusedAccess.getWidth(); x++)
		{
			const tcu::Vec4 color = unusedAccess.getPixel(x, y);
			const tcu::Vec4 refColor = tcu::Vec4(0.1f, 0.2f, 0.3f, 0.4f);
			for (deUint32 cpnt = 0; cpnt < 4; cpnt++)
				if (de::abs(color[cpnt] - refColor[cpnt]) > 0.01f)
					return tcu::TestStatus::fail("Unused image contents has changed.");
		}

	// Check for rendered result. Just a quick sanity check to see if correct color is found at the center of the quad.
	const tcu::Vec4 resultColor = resultAccess.getPixel(resultAccess.getWidth() / 2, resultAccess.getHeight() / 2);
	const tcu::Vec4 refColor = tcu::Vec4(0.4f, 0.6f, 0.2f, 1.0f);
	for (deUint32 cpnt = 0; cpnt < 4; cpnt++)
		if (de::abs(resultColor[cpnt] - refColor[cpnt]) > 0.01f)
			return tcu::TestStatus::fail("Result image mismatch");

	return tcu::TestStatus::pass("Pass");
}

std::string loadOpToString (VkAttachmentLoadOp loadOp)
{
	switch (loadOp)
	{
		case VK_ATTACHMENT_LOAD_OP_LOAD:
			return "load";
		case VK_ATTACHMENT_LOAD_OP_CLEAR:
			return "clear";
		case VK_ATTACHMENT_LOAD_OP_DONT_CARE:
			return "dontcare";
		default:
			DE_FATAL("unexpected attachment load op");
			return "";
	};
}

std::string storeOpToString (VkAttachmentStoreOp storeOp)
{
	switch (storeOp)
	{
		case VK_ATTACHMENT_STORE_OP_STORE:
			return "store";
		case VK_ATTACHMENT_STORE_OP_DONT_CARE:
			return "dontcare";
		default:
			DE_FATAL("unexpected attachment store op");
			return "";
	};
}

} // anonymous

tcu::TestCaseGroup* createRenderPassUnusedAttachmentTests (tcu::TestContext& testCtx, const RenderPassType renderPassType)
{
	de::MovePtr<tcu::TestCaseGroup>		unusedAttTests		(new tcu::TestCaseGroup(testCtx, "unused_attachment", "Unused attachment tests"));

	const VkAttachmentLoadOp			loadOps[]			=
	{
		VK_ATTACHMENT_LOAD_OP_LOAD,
		VK_ATTACHMENT_LOAD_OP_CLEAR,
		VK_ATTACHMENT_LOAD_OP_DONT_CARE
	};

	const VkAttachmentStoreOp			storeOps[]			=
	{
		VK_ATTACHMENT_STORE_OP_STORE,
		VK_ATTACHMENT_STORE_OP_DONT_CARE
	};

	for (deUint32 loadOpIdx = 0; loadOpIdx < DE_LENGTH_OF_ARRAY(loadOps); loadOpIdx++)
	{
		de::MovePtr<tcu::TestCaseGroup>	loadOpGroup(new tcu::TestCaseGroup(testCtx, (std::string("loadop") + loadOpToString(loadOps[loadOpIdx])).c_str(), ""));

		for (deUint32 storeOpIdx = 0; storeOpIdx < DE_LENGTH_OF_ARRAY(storeOps); storeOpIdx++)
		{
			de::MovePtr<tcu::TestCaseGroup>	storeOpGroup(new tcu::TestCaseGroup(testCtx, (std::string("storeop") + storeOpToString(storeOps[storeOpIdx])).c_str(), ""));

			for (deUint32 stencilLoadOpIdx = 0; stencilLoadOpIdx < DE_LENGTH_OF_ARRAY(loadOps); stencilLoadOpIdx++)
			{
				de::MovePtr<tcu::TestCaseGroup>	stencilLoadOpGroup(new tcu::TestCaseGroup(testCtx, (std::string("stencilloadop") + loadOpToString(loadOps[stencilLoadOpIdx])).c_str(), ""));

				for (deUint32 stencilStoreOpIdx = 0; stencilStoreOpIdx < DE_LENGTH_OF_ARRAY(storeOps); stencilStoreOpIdx++)
				{
					TestParams			params;
					const std::string	testName = std::string("stencilstoreop") + storeOpToString(storeOps[stencilStoreOpIdx]);

					params.loadOp			= loadOps[loadOpIdx];
					params.storeOp			= storeOps[storeOpIdx];
					params.stencilLoadOp	= loadOps[stencilLoadOpIdx];
					params.stencilStoreOp	= storeOps[stencilStoreOpIdx];
					params.renderPassType	= renderPassType;

					stencilLoadOpGroup->addChild(new UnusedAttachmentTest(testCtx, testName, "", params));
				}
				storeOpGroup->addChild(stencilLoadOpGroup.release());
			}
			loadOpGroup->addChild(storeOpGroup.release());
		}
		unusedAttTests->addChild(loadOpGroup.release());
	}

	return unusedAttTests.release();
}

} // renderpass
} // vkt