text: /*------------------------------------------------------------------------
   * Vulkan Conformance Tests
   * ------------------------
   *
   * Copyright (c) 2016 The Khronos Group Inc.
   * Copyright (c) 2016 Samsung Electronics Co., 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 Instanced Draw Tests
   *//*--------------------------------------------------------------------*/
  
  #include "vktDrawInstancedTests.hpp"
  
  #include <climits>
  
  #include "deSharedPtr.hpp"
  #include "rrRenderer.hpp"
  #include "tcuImageCompare.hpp"
  #include "tcuRGBA.hpp"
  #include "tcuTextureUtil.hpp"
  #include "vkImageUtil.hpp"
  #include "vkPrograms.hpp"
  #include "vkCmdUtil.hpp"
  #include "vkTypeUtil.hpp"
  #include "vkQueryUtil.hpp"
  #include "vktDrawBufferObjectUtil.hpp"
  #include "vktDrawCreateInfoUtil.hpp"
  #include "vktDrawImageObjectUtil.hpp"
  #include "vktDrawTestCaseUtil.hpp"
  
  namespace vkt
  {
  namespace Draw
  {
  namespace
  {
  
  static const int	QUAD_GRID_SIZE	= 8;
  static const int	WIDTH			= 128;
  static const int	HEIGHT			= 128;
  
  struct TestParams
  {
  	enum DrawFunction
  	{
  		FUNCTION_DRAW = 0,
  		FUNCTION_DRAW_INDEXED,
  		FUNCTION_DRAW_INDIRECT,
  		FUNCTION_DRAW_INDEXED_INDIRECT,
  
  		FUNTION_LAST
  	};
  
  	DrawFunction			function;
  	vk::VkPrimitiveTopology	topology;
  
  	deBool					testAttribDivisor;
  	deUint32				attribDivisor;
  
  	deBool					testMultiview;
  };
  
  struct VertexPositionAndColor
  {
  				VertexPositionAndColor (tcu::Vec4 position_, tcu::Vec4 color_)
  					: position	(position_)
  					, color		(color_)
  				{
  				}
  
  	tcu::Vec4	position;
  	tcu::Vec4	color;
  };
  
  std::ostream & operator<<(std::ostream & str, TestParams const & v)
  {
  	std::ostringstream string;
  	switch (v.function)
  	{
  		case TestParams::FUNCTION_DRAW:
  			string << "draw";
  			break;
  		case TestParams::FUNCTION_DRAW_INDEXED:
  			string << "draw_indexed";
  			break;
  		case TestParams::FUNCTION_DRAW_INDIRECT:
  			string << "draw_indirect";
  			break;
  		case TestParams::FUNCTION_DRAW_INDEXED_INDIRECT:
  			string << "draw_indexed_indirect";
  			break;
  		default:
  			DE_ASSERT(false);
  	}
  
  	string << "_" << de::toString(v.topology);
  
  	if (v.testAttribDivisor)
  		string << "_attrib_divisor_" << v.attribDivisor;
  
  	if (v.testMultiview)
  		string << "_multiview";
  
  	return str << string.str();
  }
  
  rr::PrimitiveType mapVkPrimitiveTopology (vk::VkPrimitiveTopology primitiveTopology)
  {
  	switch (primitiveTopology)
  	{
  		case vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST:						return rr::PRIMITIVETYPE_POINTS;
  		case vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST:						return rr::PRIMITIVETYPE_LINES;
  		case vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:						return rr::PRIMITIVETYPE_LINE_STRIP;
  		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:					return rr::PRIMITIVETYPE_TRIANGLES;
  		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:					return rr::PRIMITIVETYPE_TRIANGLE_FAN;
  		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:					return rr::PRIMITIVETYPE_TRIANGLE_STRIP;
  		case vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:		return rr::PRIMITIVETYPE_LINES_ADJACENCY;
  		case vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:		return rr::PRIMITIVETYPE_LINE_STRIP_ADJACENCY;
  		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:	return rr::PRIMITIVETYPE_TRIANGLES_ADJACENCY;
  		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:	return rr::PRIMITIVETYPE_TRIANGLE_STRIP_ADJACENCY;
  		default:
  			DE_ASSERT(false);
  	}
  	return rr::PRIMITIVETYPE_LAST;
  }
  
  template<typename T>
  de::SharedPtr<Buffer> createAndUploadBuffer(const std::vector<T> data, const vk::DeviceInterface& vk, const Context& context, vk::VkBufferUsageFlags usage)
  {
  	const vk::VkDeviceSize dataSize = data.size() * sizeof(T);
  	de::SharedPtr<Buffer> buffer = Buffer::createAndAlloc(vk, context.getDevice(),
  														  BufferCreateInfo(dataSize, usage),
  														  context.getDefaultAllocator(),
  														  vk::MemoryRequirement::HostVisible);
  
  	deUint8* ptr = reinterpret_cast<deUint8*>(buffer->getBoundMemory().getHostPtr());
  
  	deMemcpy(ptr, &data[0], static_cast<size_t>(dataSize));
  
  	vk::flushAlloc(vk, context.getDevice(), buffer->getBoundMemory());
  	return buffer;
  }
  
  class TestVertShader : public rr::VertexShader
  {
  public:
  	TestVertShader (int numInstances, int firstInstance)
  		: rr::VertexShader	(3, 1)
  		, m_numInstances	(numInstances)
  		, m_firstInstance	(firstInstance)
  	{
  		m_inputs[0].type	= rr::GENERICVECTYPE_FLOAT;
  		m_inputs[1].type	= rr::GENERICVECTYPE_FLOAT;
  		m_inputs[2].type	= rr::GENERICVECTYPE_FLOAT;
  		m_outputs[0].type	= rr::GENERICVECTYPE_FLOAT;
  	}
  
  	void shadeVertices (const rr::VertexAttrib* inputs,
  						rr::VertexPacket* const* packets,
  						const int numPackets) const
  	{
  		for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
  		{
  			const int		instanceNdx		= packets[packetNdx]->instanceNdx + m_firstInstance;
  			const tcu::Vec4	position		= rr::readVertexAttribFloat(inputs[0], packets[packetNdx]->instanceNdx,	packets[packetNdx]->vertexNdx, m_firstInstance);
  			const tcu::Vec4	color			= rr::readVertexAttribFloat(inputs[1], packets[packetNdx]->instanceNdx,	packets[packetNdx]->vertexNdx, m_firstInstance);
  			const tcu::Vec4	color2			= rr::readVertexAttribFloat(inputs[2], packets[packetNdx]->instanceNdx, packets[packetNdx]->vertexNdx, m_firstInstance);
  			packets[packetNdx]->position	= position + tcu::Vec4((float)(packets[packetNdx]->instanceNdx * 2.0 / m_numInstances), 0.0, 0.0, 0.0);
  			packets[packetNdx]->outputs[0]	= color + tcu::Vec4((float)instanceNdx / (float)m_numInstances, 0.0, 0.0, 1.0) + color2;
  		}
  	}
  
  private:
  	const int m_numInstances;
  	const int m_firstInstance;
  };
  
  class TestFragShader : public rr::FragmentShader
  {
  public:
  	TestFragShader (void)
  		: rr::FragmentShader(1, 1)
  	{
  		m_inputs[0].type	= rr::GENERICVECTYPE_FLOAT;
  		m_outputs[0].type	= rr::GENERICVECTYPE_FLOAT;
  	}
  
  	void shadeFragments (rr::FragmentPacket* packets,
  						 const int numPackets,
  						 const rr::FragmentShadingContext& context) const
  	{
  		for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
  		{
  			rr::FragmentPacket& packet = packets[packetNdx];
  			for (int fragNdx = 0; fragNdx < rr::NUM_FRAGMENTS_PER_PACKET; ++fragNdx)
  			{
  				const tcu::Vec4 color = rr::readVarying<float>(packet, context, 0, fragNdx);
  				rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color);
  			}
  		}
  	}
  };
  
  class InstancedDrawInstance : public TestInstance
  {
  public:
  												InstancedDrawInstance	(Context& context, TestParams params);
  	virtual	tcu::TestStatus						iterate					(void);
  
  private:
  	void										prepareVertexData		(int instanceCount, int firstInstance, int instanceDivisor);
  
  	const TestParams							m_params;
  	const vk::DeviceInterface&					m_vk;
  
  	vk::VkFormat								m_colorAttachmentFormat;
  
  	vk::Move<vk::VkPipeline>					m_pipeline;
  	vk::Move<vk::VkPipelineLayout>				m_pipelineLayout;
  
  	de::SharedPtr<Image>						m_colorTargetImage;
  	vk::Move<vk::VkImageView>					m_colorTargetView;
  
  	PipelineCreateInfo::VertexInputState		m_vertexInputState;
  
  	vk::Move<vk::VkCommandPool>					m_cmdPool;
  	vk::Move<vk::VkCommandBuffer>				m_cmdBuffer;
  
  	vk::Move<vk::VkFramebuffer>					m_framebuffer;
  	vk::Move<vk::VkRenderPass>					m_renderPass;
  
  	// Vertex data
  	std::vector<VertexPositionAndColor>			m_data;
  	std::vector<deUint32>						m_indexes;
  	std::vector<tcu::Vec4>						m_instancedColor;
  };
  
  class InstancedDrawCase : public TestCase
  {
  public:
  	InstancedDrawCase (tcu::TestContext&	testCtx,
  					   const std::string&	name,
  					   const std::string&	desc,
  					   TestParams			params)
  		: TestCase	(testCtx, name, desc)
  		, m_params	(params)
  	{
  		m_vertexShader = "#version 430\n"
  				"layout(location = 0) in vec4 in_position;\n"
  				"layout(location = 1) in vec4 in_color;\n"
  				"layout(location = 2) in vec4 in_color_2;\n"
  				"layout(push_constant) uniform TestParams {\n"
  				"	float firstInstance;\n"
  				"	float instanceCount;\n"
  				"} params;\n"
  				"layout(location = 0) out vec4 out_color;\n"
  				"out gl_PerVertex {\n"
  				"    vec4  gl_Position;\n"
  				"    float gl_PointSize;\n"
  				"};\n"
  				"void main() {\n"
  				"    gl_PointSize = 1.0;\n"
  				"    gl_Position  = in_position + vec4(float(gl_InstanceIndex - params.firstInstance) * 2.0 / params.instanceCount, 0.0, 0.0, 0.0);\n"
  				"    out_color    = in_color + vec4(float(gl_InstanceIndex) / params.instanceCount, 0.0, 0.0, 1.0) + in_color_2;\n"
  				"}\n";
  
  		m_fragmentShader = "#version 430\n"
  				"layout(location = 0) in vec4 in_color;\n"
  				"layout(location = 0) out vec4 out_color;\n"
  				"void main()\n"
  				"{\n"
  				"    out_color = in_color;\n"
  				"}\n";
  	}
  
  	virtual void	checkSupport	(Context& context) const
  	{
  		if (m_params.testAttribDivisor)
  		{
  			context.requireDeviceFunctionality("VK_EXT_vertex_attribute_divisor");
  
  			const vk::VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT& vertexAttributeDivisorFeatures = context.getVertexAttributeDivisorFeatures();
  
  			if (m_params.attribDivisor != 1 && !vertexAttributeDivisorFeatures.vertexAttributeInstanceRateDivisor)
  				TCU_THROW(NotSupportedError, "Implementation does not support vertexAttributeInstanceRateDivisor");
  
  			if (m_params.attribDivisor == 0 && !vertexAttributeDivisorFeatures.vertexAttributeInstanceRateZeroDivisor)
  				TCU_THROW(NotSupportedError, "Implementation does not support vertexAttributeInstanceRateDivisorZero");
  
  			if (m_params.testMultiview)
  			{
  				context.requireDeviceFunctionality("VK_KHR_multiview");
  
  				const vk::VkPhysicalDeviceMultiviewFeatures& multiviewFeatures = context.getMultiviewFeatures();
  
  				if (!multiviewFeatures.multiview)
  					TCU_THROW(NotSupportedError, "Implementation does not support multiview feature");
  			}
  		}
  	}
  
  	TestInstance*	createInstance	(Context& context) const
  	{
  		return new InstancedDrawInstance(context, m_params);
  	}
  
  	virtual void	initPrograms	(vk::SourceCollections& programCollection) const
  	{
  		programCollection.glslSources.add("InstancedDrawVert") << glu::VertexSource(m_vertexShader);
  		programCollection.glslSources.add("InstancedDrawFrag") << glu::FragmentSource(m_fragmentShader);
  	}
  
  private:
  	const TestParams	m_params;
  	std::string			m_vertexShader;
  	std::string			m_fragmentShader;
  };
  
  InstancedDrawInstance::InstancedDrawInstance(Context &context, TestParams params)
  	: TestInstance				(context)
  	, m_params					(params)
  	, m_vk						(context.getDeviceInterface())
  	, m_colorAttachmentFormat	(vk::VK_FORMAT_R8G8B8A8_UNORM)
  {
  	const vk::VkDevice device				= m_context.getDevice();
  	const deUint32 queueFamilyIndex			= m_context.getUniversalQueueFamilyIndex();
  
  	const vk::VkPushConstantRange pushConstantRange = {
  		vk::VK_SHADER_STAGE_VERTEX_BIT,				// VkShaderStageFlags    stageFlags;
  		0u,											// uint32_t              offset;
  		(deUint32)sizeof(float) * 2,				// uint32_t              size;
  	};
  
  	const PipelineLayoutCreateInfo pipelineLayoutCreateInfo(0, DE_NULL, 1, &pushConstantRange);
  	m_pipelineLayout						= vk::createPipelineLayout(m_vk, device, &pipelineLayoutCreateInfo);
  
  	deUint32 arrayLayers = m_params.testMultiview ? 2 : 1;
  	const vk::VkExtent3D targetImageExtent	= { WIDTH, HEIGHT, 1 };
  	const ImageCreateInfo targetImageCreateInfo(vk::VK_IMAGE_TYPE_2D, m_colorAttachmentFormat, targetImageExtent, 1, arrayLayers, vk::VK_SAMPLE_COUNT_1_BIT,
  		vk::VK_IMAGE_TILING_OPTIMAL, vk::VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | vk::VK_IMAGE_USAGE_TRANSFER_SRC_BIT | vk::VK_IMAGE_USAGE_TRANSFER_DST_BIT);
  
  	m_colorTargetImage						= Image::createAndAlloc(m_vk, device, targetImageCreateInfo, m_context.getDefaultAllocator(), m_context.getUniversalQueueFamilyIndex());
  
  	const enum vk::VkImageViewType imageViewType = m_params.testMultiview ? vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY : vk::VK_IMAGE_VIEW_TYPE_2D;
  	ImageSubresourceRange subresourceRange = ImageSubresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT);
  
  	if (m_params.testMultiview)
  		subresourceRange.layerCount = 2;
  
  	const ImageViewCreateInfo colorTargetViewInfo(m_colorTargetImage->object(), imageViewType, m_colorAttachmentFormat, subresourceRange);
  	m_colorTargetView						= vk::createImageView(m_vk, device, &colorTargetViewInfo);
  
  	RenderPassCreateInfo renderPassCreateInfo;
  	renderPassCreateInfo.addAttachment(AttachmentDescription(m_colorAttachmentFormat,
  															 vk::VK_SAMPLE_COUNT_1_BIT,
  															 vk::VK_ATTACHMENT_LOAD_OP_LOAD,
  															 vk::VK_ATTACHMENT_STORE_OP_STORE,
  															 vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE,
  															 vk::VK_ATTACHMENT_STORE_OP_STORE,
  															 vk::VK_IMAGE_LAYOUT_GENERAL,
  															 vk::VK_IMAGE_LAYOUT_GENERAL));
  
  	const vk::VkAttachmentReference colorAttachmentReference =
  	{
  		0,
  		vk::VK_IMAGE_LAYOUT_GENERAL
  	};
  
  	renderPassCreateInfo.addSubpass(SubpassDescription(vk::VK_PIPELINE_BIND_POINT_GRAPHICS,
  													   0,
  													   0,
  													   DE_NULL,
  													   1,
  													   &colorAttachmentReference,
  													   DE_NULL,
  													   AttachmentReference(),
  													   0,
  													   DE_NULL));
  
  	vk::VkRenderPassMultiviewCreateInfo renderPassMultiviewCreateInfo;
  	// Bit mask that specifies which view rendering is broadcast to
  	// 0011 = Broadcast to first and second view (layer)
  	const deUint32 viewMask = 0x3;
  	// Bit mask that specifices correlation between views
  	// An implementation may use this for optimizations (concurrent render)
  	const deUint32 correlationMask = 0x3;
  
  	if (m_params.testMultiview)
  	{
  		DE_ASSERT(renderPassCreateInfo.subpassCount == 1);
  
  
  
  		renderPassMultiviewCreateInfo.sType = vk::VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
  		renderPassMultiviewCreateInfo.pNext = DE_NULL;
  		renderPassMultiviewCreateInfo.subpassCount = renderPassCreateInfo.subpassCount;
  		renderPassMultiviewCreateInfo.pViewMasks = &viewMask;
  		renderPassMultiviewCreateInfo.correlationMaskCount = 1u;
  		renderPassMultiviewCreateInfo.pCorrelationMasks = &correlationMask;
  		renderPassMultiviewCreateInfo.pViewOffsets = DE_NULL;
  		renderPassMultiviewCreateInfo.dependencyCount = 0u;
  
  		renderPassCreateInfo.pNext = &renderPassMultiviewCreateInfo;
  	}
  
  	m_renderPass		= vk::createRenderPass(m_vk, device, &renderPassCreateInfo);
  
  	std::vector<vk::VkImageView> colorAttachments(1);
  	colorAttachments[0] = *m_colorTargetView;
  
  	const FramebufferCreateInfo framebufferCreateInfo(*m_renderPass, colorAttachments, WIDTH, HEIGHT, 1);
  
  	m_framebuffer		= vk::createFramebuffer(m_vk, device, &framebufferCreateInfo);
  
  	const vk::VkVertexInputBindingDescription vertexInputBindingDescription[2] =
  	{
  		{
  			0u,
  			(deUint32)sizeof(VertexPositionAndColor),
  			vk::VK_VERTEX_INPUT_RATE_VERTEX,
  		},
  		{
  			1u,
  			(deUint32)sizeof(tcu::Vec4),
  			vk::VK_VERTEX_INPUT_RATE_INSTANCE,
  		},
  	};
  
  	const vk::VkVertexInputAttributeDescription vertexInputAttributeDescriptions[] =
  	{
  		{
  			0u,
  			0u,
  			vk::VK_FORMAT_R32G32B32A32_SFLOAT,
  			0u
  		},
  		{
  			1u,
  			0u,
  			vk::VK_FORMAT_R32G32B32A32_SFLOAT,
  			(deUint32)sizeof(tcu::Vec4),
  		},
  		{
  			2u,
  			1u,
  			vk::VK_FORMAT_R32G32B32A32_SFLOAT,
  			0,
  		}
  	};
  
  	m_vertexInputState = PipelineCreateInfo::VertexInputState(2,
  															  vertexInputBindingDescription,
  															  DE_LENGTH_OF_ARRAY(vertexInputAttributeDescriptions),
  															  vertexInputAttributeDescriptions);
  
  	const vk::VkVertexInputBindingDivisorDescriptionEXT vertexInputBindingDivisorDescription =
  	{
  		1u,
  		m_params.attribDivisor,
  	};
  	if (m_params.testAttribDivisor)
  		m_vertexInputState.addDivisors(1, &vertexInputBindingDivisorDescription);
  
  	const CmdPoolCreateInfo cmdPoolCreateInfo(queueFamilyIndex);
  	m_cmdPool = vk::createCommandPool(m_vk, device, &cmdPoolCreateInfo);
  
  	m_cmdBuffer = vk::allocateCommandBuffer(m_vk, device, *m_cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
  
  	const vk::Unique<vk::VkShaderModule> vs(createShaderModule(m_vk, device, m_context.getBinaryCollection().get("InstancedDrawVert"), 0));
  	const vk::Unique<vk::VkShaderModule> fs(createShaderModule(m_vk, device, m_context.getBinaryCollection().get("InstancedDrawFrag"), 0));
  
  	const PipelineCreateInfo::ColorBlendState::Attachment vkCbAttachmentState;
  
  	vk::VkViewport	viewport	= vk::makeViewport(WIDTH, HEIGHT);
  	vk::VkRect2D	scissor		= vk::makeRect2D(WIDTH, HEIGHT);
  
  	PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, 0);
  	pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vs, "main", vk::VK_SHADER_STAGE_VERTEX_BIT));
  	pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fs, "main", vk::VK_SHADER_STAGE_FRAGMENT_BIT));
  	pipelineCreateInfo.addState(PipelineCreateInfo::VertexInputState(m_vertexInputState));
  	pipelineCreateInfo.addState(PipelineCreateInfo::InputAssemblerState(m_params.topology));
  	pipelineCreateInfo.addState(PipelineCreateInfo::ColorBlendState(1, &vkCbAttachmentState));
  	pipelineCreateInfo.addState(PipelineCreateInfo::ViewportState(1, std::vector<vk::VkViewport>(1, viewport), std::vector<vk::VkRect2D>(1, scissor)));
  	pipelineCreateInfo.addState(PipelineCreateInfo::DepthStencilState());
  	pipelineCreateInfo.addState(PipelineCreateInfo::RasterizerState());
  	pipelineCreateInfo.addState(PipelineCreateInfo::MultiSampleState());
  
  	m_pipeline = vk::createGraphicsPipeline(m_vk, device, DE_NULL, &pipelineCreateInfo);
  }
  
  tcu::TestStatus InstancedDrawInstance::iterate()
  {
  	const vk::VkQueue		queue					= m_context.getUniversalQueue();
  	const vk::VkDevice		device					= m_context.getDevice();
  	static const deUint32	instanceCounts[]		= { 0, 1, 2, 4, 20 };
  	static const deUint32	firstInstanceIndices[]	= { 0, 1, 3, 4, 20 };
  	const deUint32			numLayers				= m_params.testMultiview ? 2 : 1;
  
  	qpTestResult			res						= QP_TEST_RESULT_PASS;
  
  	const vk::VkClearColorValue clearColor = { { 0.0f, 0.0f, 0.0f, 1.0f } };
  	int firstInstanceIndicesCount = 1;
  
  	// Require 'drawIndirectFirstInstance' feature to run non-zero firstInstance indirect draw tests.
  	if (m_context.getDeviceFeatures().drawIndirectFirstInstance)
  		firstInstanceIndicesCount = DE_LENGTH_OF_ARRAY(firstInstanceIndices);
  
  	for (int instanceCountNdx = 0; instanceCountNdx < DE_LENGTH_OF_ARRAY(instanceCounts); instanceCountNdx++)
  	{
  		const deUint32 instanceCount = instanceCounts[instanceCountNdx];
  		for (int firstInstanceIndexNdx = 0; firstInstanceIndexNdx < firstInstanceIndicesCount; firstInstanceIndexNdx++)
  		{
  			// Prepare vertex data for at least one instance
  			const deUint32				prepareCount			= de::max(instanceCount, 1u);
  			const deUint32				firstInstance			= firstInstanceIndices[firstInstanceIndexNdx];
  
  			prepareVertexData(prepareCount, firstInstance, m_params.testAttribDivisor ? m_params.attribDivisor : 1);
  			const de::SharedPtr<Buffer>	vertexBuffer			= createAndUploadBuffer(m_data, m_vk, m_context, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
  			const de::SharedPtr<Buffer>	instancedVertexBuffer	= createAndUploadBuffer(m_instancedColor, m_vk, m_context, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
  			de::SharedPtr<Buffer>		indexBuffer;
  			de::SharedPtr<Buffer>		indirectBuffer;
  			beginCommandBuffer(m_vk, *m_cmdBuffer, 0u);
  
  			if (m_params.testMultiview)
  			{
  				vk::VkImageMemoryBarrier barrier;
  				barrier.sType							= vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
  				barrier.pNext							= DE_NULL;
  				barrier.srcAccessMask					= 0u;
  				barrier.dstAccessMask					= vk::VK_ACCESS_TRANSFER_WRITE_BIT;
  				barrier.oldLayout						= vk::VK_IMAGE_LAYOUT_UNDEFINED;
  				barrier.newLayout						= vk::VK_IMAGE_LAYOUT_GENERAL;
  				barrier.srcQueueFamilyIndex				= VK_QUEUE_FAMILY_IGNORED;
  				barrier.dstQueueFamilyIndex				= VK_QUEUE_FAMILY_IGNORED;
  				barrier.image							= m_colorTargetImage->object();
  				barrier.subresourceRange.aspectMask		= vk::VK_IMAGE_ASPECT_COLOR_BIT;
  				barrier.subresourceRange.baseMipLevel	= 0;
  				barrier.subresourceRange.levelCount		= 1;
  				barrier.subresourceRange.baseArrayLayer = 0;
  				barrier.subresourceRange.layerCount		= numLayers;
  
  				m_vk.cmdPipelineBarrier(*m_cmdBuffer, vk::VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT, (vk::VkDependencyFlags)0, 0, (const vk::VkMemoryBarrier*)DE_NULL,
  										0, (const vk::VkBufferMemoryBarrier*)DE_NULL, 1, &barrier);
  
  			}
  			else
  			{
  				initialTransitionColor2DImage(m_vk, *m_cmdBuffer, m_colorTargetImage->object(), vk::VK_IMAGE_LAYOUT_GENERAL,
  											  vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT);
  			}
  
  			const ImageSubresourceRange subresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, numLayers);
  			m_vk.cmdClearColorImage(*m_cmdBuffer, m_colorTargetImage->object(),
  				vk::VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &subresourceRange);
  
  			const vk::VkMemoryBarrier memBarrier =
  			{
  				vk::VK_STRUCTURE_TYPE_MEMORY_BARRIER,
  				DE_NULL,
  				vk::VK_ACCESS_TRANSFER_WRITE_BIT,
  				vk::VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
  			};
  
  			m_vk.cmdPipelineBarrier(*m_cmdBuffer, vk::VK_PIPELINE_STAGE_TRANSFER_BIT,
  				vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
  				0, 1, &memBarrier, 0, DE_NULL, 0, DE_NULL);
  
  			const vk::VkRect2D renderArea = vk::makeRect2D(WIDTH, HEIGHT);
  			beginRenderPass(m_vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer, renderArea);
  
  			if (m_params.function == TestParams::FUNCTION_DRAW_INDEXED || m_params.function == TestParams::FUNCTION_DRAW_INDEXED_INDIRECT)
  			{
  				indexBuffer = createAndUploadBuffer(m_indexes, m_vk, m_context, vk::VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
  				m_vk.cmdBindIndexBuffer(*m_cmdBuffer, indexBuffer->object(), 0, vk::VK_INDEX_TYPE_UINT32);
  			}
  
  			const vk::VkBuffer vertexBuffers[] =
  			{
  				vertexBuffer->object(),
  				instancedVertexBuffer->object(),
  			};
  
  			const vk::VkDeviceSize vertexBufferOffsets[] =
  			{
  				0,	// vertexBufferOffset
  				0,	// instancedVertexBufferOffset
  			};
  
  			m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, DE_LENGTH_OF_ARRAY(vertexBuffers), vertexBuffers, vertexBufferOffsets);
  
  			const float pushConstants[] = { (float)firstInstance, (float)instanceCount };
  			m_vk.cmdPushConstants(*m_cmdBuffer, *m_pipelineLayout, vk::VK_SHADER_STAGE_VERTEX_BIT, 0u, (deUint32)sizeof(pushConstants), pushConstants);
  
  			m_vk.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
  
  			switch (m_params.function)
  			{
  				case TestParams::FUNCTION_DRAW:
  					m_vk.cmdDraw(*m_cmdBuffer, (deUint32)m_data.size(), instanceCount, 0u, firstInstance);
  					break;
  
  				case TestParams::FUNCTION_DRAW_INDEXED:
  					m_vk.cmdDrawIndexed(*m_cmdBuffer, (deUint32)m_indexes.size(), instanceCount, 0u, 0u, firstInstance);
  					break;
  
  				case TestParams::FUNCTION_DRAW_INDIRECT:
  				{
  					vk::VkDrawIndirectCommand drawCommand =
  					{
  						(deUint32)m_data.size(),	// uint32_t	vertexCount;
  						instanceCount,				// uint32_t	instanceCount;
  						0u,							// uint32_t	firstVertex;
  						firstInstance,				// uint32_t	firstInstance;
  					};
  					std::vector<vk::VkDrawIndirectCommand> drawCommands;
  					drawCommands.push_back(drawCommand);
  					indirectBuffer = createAndUploadBuffer(drawCommands, m_vk, m_context, vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
  
  					m_vk.cmdDrawIndirect(*m_cmdBuffer, indirectBuffer->object(), 0, 1u, 0u);
  					break;
  				}
  				case TestParams::FUNCTION_DRAW_INDEXED_INDIRECT:
  				{
  					vk::VkDrawIndexedIndirectCommand drawCommand =
  					{
  						(deUint32)m_indexes.size(),	// uint32_t	indexCount;
  						instanceCount,				// uint32_t	instanceCount;
  						0u,							// uint32_t	firstIndex;
  						0,							// int32_t	vertexOffset;
  						firstInstance,				// uint32_t	firstInstance;
  					};
  					std::vector<vk::VkDrawIndexedIndirectCommand> drawCommands;
  					drawCommands.push_back(drawCommand);
  					indirectBuffer = createAndUploadBuffer(drawCommands, m_vk, m_context, vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
  
  					m_vk.cmdDrawIndexedIndirect(*m_cmdBuffer, indirectBuffer->object(), 0, 1u, 0u);
  					break;
  				}
  				default:
  					DE_ASSERT(false);
  			}
  
  			endRenderPass(m_vk, *m_cmdBuffer);
  			endCommandBuffer(m_vk, *m_cmdBuffer);
  
  			submitCommandsAndWait(m_vk, device, queue, m_cmdBuffer.get());
  
  			// Reference rendering
  			std::vector<tcu::Vec4>	vetrices;
  			std::vector<tcu::Vec4>	colors;
  
  			for (std::vector<VertexPositionAndColor>::const_iterator it = m_data.begin(); it != m_data.end(); ++it)
  			{
  				vetrices.push_back(it->position);
  				colors.push_back(it->color);
  			}
  
  			tcu::TextureLevel refImage (vk::mapVkFormat(m_colorAttachmentFormat), (int)(0.5 + WIDTH), (int)(0.5 + HEIGHT));
  
  			tcu::clear(refImage.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
  
  			const TestVertShader					vertShader(instanceCount, firstInstance);
  			const TestFragShader					fragShader;
  			const rr::Program						program			(&vertShader, &fragShader);
  			const rr::MultisamplePixelBufferAccess	colorBuffer		= rr::MultisamplePixelBufferAccess::fromSinglesampleAccess(refImage.getAccess());
  			const rr::RenderTarget					renderTarget	(colorBuffer);
  			const rr::RenderState					renderState		((rr::ViewportState(colorBuffer)), m_context.getDeviceProperties().limits.subPixelPrecisionBits);
  			const rr::Renderer						renderer;
  
  			const rr::VertexAttrib	vertexAttribs[] =
  			{
  				rr::VertexAttrib(rr::VERTEXATTRIBTYPE_FLOAT, 4, sizeof(tcu::Vec4), 0, &vetrices[0]),
  				rr::VertexAttrib(rr::VERTEXATTRIBTYPE_FLOAT, 4, sizeof(tcu::Vec4), 0, &colors[0]),
  				// The reference renderer treats a divisor of 0 as meaning per-vertex.  Use INT_MAX instead; it should work just as well.
  				rr::VertexAttrib(rr::VERTEXATTRIBTYPE_FLOAT, 4, sizeof(tcu::Vec4), m_params.testAttribDivisor ? (m_params.attribDivisor == 0 ? INT_MAX : m_params.attribDivisor) : 1, &m_instancedColor[0])
  			};
  
  			if (m_params.function == TestParams::FUNCTION_DRAW || m_params.function == TestParams::FUNCTION_DRAW_INDIRECT)
  			{
  				const rr::PrimitiveList	primitives = rr::PrimitiveList(mapVkPrimitiveTopology(m_params.topology), (int)vetrices.size(), 0);
  				const rr::DrawCommand	command(renderState, renderTarget, program, DE_LENGTH_OF_ARRAY(vertexAttribs), &vertexAttribs[0],
  												primitives);
  				renderer.drawInstanced(command, instanceCount);
  			}
  			else
  			{
  				const rr::DrawIndices indicies(m_indexes.data());
  
  				const rr::PrimitiveList	primitives = rr::PrimitiveList(mapVkPrimitiveTopology(m_params.topology), (int)m_indexes.size(), indicies);
  				const rr::DrawCommand	command(renderState, renderTarget, program, DE_LENGTH_OF_ARRAY(vertexAttribs), &vertexAttribs[0],
  												primitives);
  				renderer.drawInstanced(command, instanceCount);
  			}
  
  			const vk::VkOffset3D zeroOffset = { 0, 0, 0 };
  			for (deUint32 i = 0; i < numLayers; i++)
  			{
  				const tcu::ConstPixelBufferAccess renderedFrame = m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(),
  					vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT, 0, i);
  
  				tcu::TestLog &log		= m_context.getTestContext().getLog();
  
  				std::ostringstream resultDesc;
  				resultDesc << "Image layer " << i << " comparison result. Instance count: " << instanceCount << " first instance index: " << firstInstance;
  
  				if (m_params.topology == vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
  				{
  					const bool ok = tcu::intThresholdPositionDeviationCompare(
  						log, "Result", resultDesc.str().c_str(), refImage.getAccess(), renderedFrame,
  						tcu::UVec4(4u),					// color threshold
  						tcu::IVec3(1, 1, 0),			// position deviation tolerance
  						true,							// don't check the pixels at the boundary
  						tcu::COMPARE_LOG_RESULT);
  
  					if (!ok)
  						res = QP_TEST_RESULT_FAIL;
  				}
  				else
  				{
  					if (!tcu::fuzzyCompare(log, "Result", resultDesc.str().c_str(), refImage.getAccess(), renderedFrame, 0.05f, tcu::COMPARE_LOG_RESULT))
  						res = QP_TEST_RESULT_FAIL;
  				}
  			}
  		}
  	}
  	return tcu::TestStatus(res, qpGetTestResultName(res));
  }
  
  void InstancedDrawInstance::prepareVertexData(int instanceCount, int firstInstance, int instanceDivisor)
  {
  	m_data.clear();
  	m_indexes.clear();
  	m_instancedColor.clear();
  
  	if (m_params.function == TestParams::FUNCTION_DRAW || m_params.function == TestParams::FUNCTION_DRAW_INDIRECT)
  	{
  		for (int y = 0; y < QUAD_GRID_SIZE; y++)
  		{
  			for (int x = 0; x < QUAD_GRID_SIZE; x++)
  			{
  				const float fx0 = -1.0f + (float)(x+0) / (float)QUAD_GRID_SIZE * 2.0f / (float)instanceCount;
  				const float fx1 = -1.0f + (float)(x+1) / (float)QUAD_GRID_SIZE * 2.0f / (float)instanceCount;
  				const float fy0 = -1.0f + (float)(y+0) / (float)QUAD_GRID_SIZE * 2.0f;
  				const float fy1 = -1.0f + (float)(y+1) / (float)QUAD_GRID_SIZE * 2.0f;
  
  				// Vertices of a quad's lower-left triangle: (fx0, fy0), (fx1, fy0) and (fx0, fy1)
  				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx0, fy0, 1.0f, 1.0f), tcu::RGBA::blue().toVec()));
  				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx1, fy0, 1.0f, 1.0f), tcu::RGBA::blue().toVec()));
  				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx0, fy1, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
  
  				// Vertices of a quad's upper-right triangle: (fx1, fy1), (fx0, fy1) and (fx1, fy0)
  				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx1, fy1, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
  				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx0, fy1, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
  				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx1, fy0, 1.0f, 1.0f), tcu::RGBA::blue().toVec()));
  			}
  		}
  	}
  	else
  	{
  		for (int y = 0; y < QUAD_GRID_SIZE + 1; y++)
  		{
  			for (int x = 0; x < QUAD_GRID_SIZE + 1; x++)
  			{
  				const float fx = -1.0f + (float)x / (float)QUAD_GRID_SIZE * 2.0f / (float)instanceCount;
  				const float fy = -1.0f + (float)y / (float)QUAD_GRID_SIZE * 2.0f;
  
  				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx, fy, 1.0f, 1.0f),
  														(y % 2 ? tcu::RGBA::blue().toVec() : tcu::RGBA::green().toVec())));
  			}
  		}
  
  		for (int y = 0; y < QUAD_GRID_SIZE; y++)
  		{
  			for (int x = 0; x < QUAD_GRID_SIZE; x++)
  			{
  				const int ndx00 = y*(QUAD_GRID_SIZE + 1) + x;
  				const int ndx10 = y*(QUAD_GRID_SIZE + 1) + x + 1;
  				const int ndx01 = (y + 1)*(QUAD_GRID_SIZE + 1) + x;
  				const int ndx11 = (y + 1)*(QUAD_GRID_SIZE + 1) + x + 1;
  
  				// Lower-left triangle of a quad.
  				m_indexes.push_back((deUint16)ndx00);
  				m_indexes.push_back((deUint16)ndx10);
  				m_indexes.push_back((deUint16)ndx01);
  
  				// Upper-right triangle of a quad.
  				m_indexes.push_back((deUint16)ndx11);
  				m_indexes.push_back((deUint16)ndx01);
  				m_indexes.push_back((deUint16)ndx10);
  			}
  		}
  	}
  
  	const int colorCount = instanceDivisor == 0 ? 1 : (instanceCount + firstInstance + instanceDivisor - 1) / instanceDivisor;
  	for (int i = 0; i < instanceCount + firstInstance; i++)
  	{
  		m_instancedColor.push_back(tcu::Vec4(0.0, (float)(1.0 - i * 1.0 / colorCount) / 2, 0.0, 1.0));
  	}
  }
  
  } // anonymus
  
  InstancedTests::InstancedTests(tcu::TestContext& testCtx)
  	: TestCaseGroup	(testCtx, "instanced", "Instanced drawing tests")
  {
  	static const vk::VkPrimitiveTopology	topologies[]			=
  	{
  		vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
  		vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
  		vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
  		vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
  		vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
  		vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
  	};
  	static const TestParams::DrawFunction	functions[]				=
  	{
  		TestParams::FUNCTION_DRAW,
  		TestParams::FUNCTION_DRAW_INDEXED,
  		TestParams::FUNCTION_DRAW_INDIRECT,
  		TestParams::FUNCTION_DRAW_INDEXED_INDIRECT,
  	};
  
  	static const deBool multiviews[] = { DE_FALSE, DE_TRUE };
  
  	static const deUint32 divisors[] = { 0, 1, 2, 4, 20 };
  
  	for (int topologyNdx = 0; topologyNdx < DE_LENGTH_OF_ARRAY(topologies); topologyNdx++)
  	{
  		for (int functionNdx = 0; functionNdx < DE_LENGTH_OF_ARRAY(functions); functionNdx++)
  		{
  			for (int testAttribDivisor = 0; testAttribDivisor < 2; testAttribDivisor++)
  			{
  				for (int divisorNdx = 0; divisorNdx < DE_LENGTH_OF_ARRAY(divisors); divisorNdx++)
  				{
  					for (int multiviewNdx = 0; multiviewNdx < DE_LENGTH_OF_ARRAY(multiviews); multiviewNdx++)
  					{
  						// If we don't have VK_EXT_vertex_attribute_divisor, we only get a divisor or 1.
  						if (!testAttribDivisor && divisors[divisorNdx] != 1)
  							continue;
  
  						TestParams param;
  						param.function = functions[functionNdx];
  						param.topology = topologies[topologyNdx];
  						param.testAttribDivisor = testAttribDivisor ? DE_TRUE : DE_FALSE;
  						param.attribDivisor = divisors[divisorNdx];
  						param.testMultiview = multiviews[multiviewNdx];
  
  						// Add multiview tests only when vertex attribute divisor is enabled.
  						if (param.testMultiview && !testAttribDivisor)
  							continue;
  
  						std::string testName = de::toString(param);
  
  						addChild(new InstancedDrawCase(m_testCtx, de::toLower(testName), "Instanced drawing test", param));
  					}
  				}
  			}
  		}
  	}
  }
  
  InstancedTests::~InstancedTests() {}
  
  } // DrawTests
  } // vkt
  