1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2016 The Khronos Group Inc.
6 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 *//*!
21 * \file
22 * \brief Instanced Draw Tests
23 *//*--------------------------------------------------------------------*/
24
25#include "vktDrawInstancedTests.hpp"
26
27#include "deSharedPtr.hpp"
28#include "rrRenderer.hpp"
29#include "tcuImageCompare.hpp"
30#include "tcuRGBA.hpp"
31#include "tcuTextureUtil.hpp"
32#include "vkImageUtil.hpp"
33#include "vkPrograms.hpp"
34#include "vktDrawBufferObjectUtil.hpp"
35#include "vktDrawCreateInfoUtil.hpp"
36#include "vktDrawImageObjectUtil.hpp"
37#include "vktDrawTestCaseUtil.hpp"
38
39namespace vkt
40{
41namespace Draw
42{
43namespace
44{
45
46static const int	QUAD_GRID_SIZE	= 8;
47static const int	WIDTH			= 128;
48static const int	HEIGHT			= 128;
49
50struct TestParams
51{
52	enum DrawFunction
53	{
54		FUNCTION_DRAW = 0,
55		FUNCTION_DRAW_INDEXED,
56		FUNCTION_DRAW_INDIRECT,
57		FUNCTION_DRAW_INDEXED_INDIRECT,
58
59		FUNTION_LAST
60	};
61
62	DrawFunction			function;
63	vk::VkPrimitiveTopology	topology;
64};
65
66struct VertexPositionAndColor
67{
68				VertexPositionAndColor (tcu::Vec4 position_, tcu::Vec4 color_)
69					: position	(position_)
70					, color		(color_)
71				{
72				}
73
74	tcu::Vec4	position;
75	tcu::Vec4	color;
76};
77
78std::ostream & operator<<(std::ostream & str, TestParams const & v)
79{
80	std::ostringstream string;
81	switch (v.function)
82	{
83		case TestParams::FUNCTION_DRAW:
84			string << "draw";
85			break;
86		case TestParams::FUNCTION_DRAW_INDEXED:
87			string << "draw_indexed";
88			break;
89		case TestParams::FUNCTION_DRAW_INDIRECT:
90			string << "draw_indirect";
91			break;
92		case TestParams::FUNCTION_DRAW_INDEXED_INDIRECT:
93			string << "draw_indexed_indirect";
94			break;
95		default:
96			DE_ASSERT(false);
97	}
98
99	string << "_" << de::toString(v.topology);
100	return str << string.str();
101}
102
103rr::PrimitiveType mapVkPrimitiveTopology (vk::VkPrimitiveTopology primitiveTopology)
104{
105	switch (primitiveTopology)
106	{
107		case vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST:						return rr::PRIMITIVETYPE_POINTS;
108		case vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST:						return rr::PRIMITIVETYPE_LINES;
109		case vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:						return rr::PRIMITIVETYPE_LINE_STRIP;
110		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:					return rr::PRIMITIVETYPE_TRIANGLES;
111		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:					return rr::PRIMITIVETYPE_TRIANGLE_FAN;
112		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:					return rr::PRIMITIVETYPE_TRIANGLE_STRIP;
113		case vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:		return rr::PRIMITIVETYPE_LINES_ADJACENCY;
114		case vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:		return rr::PRIMITIVETYPE_LINE_STRIP_ADJACENCY;
115		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:	return rr::PRIMITIVETYPE_TRIANGLES_ADJACENCY;
116		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:	return rr::PRIMITIVETYPE_TRIANGLE_STRIP_ADJACENCY;
117		default:
118			DE_ASSERT(false);
119	}
120	return rr::PRIMITIVETYPE_LAST;
121}
122
123template<typename T>
124de::SharedPtr<Buffer> createAndUploadBuffer(const std::vector<T> data, const vk::DeviceInterface& vk, const Context& context, vk::VkBufferUsageFlags usage)
125{
126	const vk::VkDeviceSize dataSize = data.size() * sizeof(T);
127	de::SharedPtr<Buffer> buffer = Buffer::createAndAlloc(vk, context.getDevice(),
128														  BufferCreateInfo(dataSize, usage),
129														  context.getDefaultAllocator(),
130														  vk::MemoryRequirement::HostVisible);
131
132	deUint8* ptr = reinterpret_cast<deUint8*>(buffer->getBoundMemory().getHostPtr());
133
134	deMemcpy(ptr, &data[0], static_cast<size_t>(dataSize));
135
136	vk::flushMappedMemoryRange(vk, context.getDevice(),
137							   buffer->getBoundMemory().getMemory(),
138							   buffer->getBoundMemory().getOffset(),
139							   VK_WHOLE_SIZE);
140	return buffer;
141}
142
143class TestVertShader : public rr::VertexShader
144{
145public:
146	TestVertShader (int numInstances, int firstInstance)
147		: rr::VertexShader	(3, 1)
148		, m_numInstances	(numInstances)
149		, m_firstInstance	(firstInstance)
150	{
151		m_inputs[0].type	= rr::GENERICVECTYPE_FLOAT;
152		m_inputs[1].type	= rr::GENERICVECTYPE_FLOAT;
153		m_inputs[2].type	= rr::GENERICVECTYPE_FLOAT;
154		m_outputs[0].type	= rr::GENERICVECTYPE_FLOAT;
155	}
156
157	void shadeVertices (const rr::VertexAttrib* inputs,
158						rr::VertexPacket* const* packets,
159						const int numPackets) const
160	{
161		for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
162		{
163			const int		instanceNdx		= packets[packetNdx]->instanceNdx + m_firstInstance;
164			const tcu::Vec4	position		= rr::readVertexAttribFloat(inputs[0], instanceNdx,	packets[packetNdx]->vertexNdx);
165			const tcu::Vec4	color			= rr::readVertexAttribFloat(inputs[1], instanceNdx,	packets[packetNdx]->vertexNdx);
166			const tcu::Vec4	color2			= rr::readVertexAttribFloat(inputs[2], instanceNdx, packets[packetNdx]->vertexNdx);
167			packets[packetNdx]->position	= position + tcu::Vec4((float)(packets[packetNdx]->instanceNdx * 2.0 / m_numInstances), 0.0, 0.0, 0.0);
168			packets[packetNdx]->outputs[0]	= color + tcu::Vec4((float)instanceNdx / (float)m_numInstances, 0.0, 0.0, 1.0) + color2;
169		}
170	}
171
172private:
173	const int m_numInstances;
174	const int m_firstInstance;
175};
176
177class TestFragShader : public rr::FragmentShader
178{
179public:
180	TestFragShader (void)
181		: rr::FragmentShader(1, 1)
182	{
183		m_inputs[0].type	= rr::GENERICVECTYPE_FLOAT;
184		m_outputs[0].type	= rr::GENERICVECTYPE_FLOAT;
185	}
186
187	void shadeFragments (rr::FragmentPacket* packets,
188						 const int numPackets,
189						 const rr::FragmentShadingContext& context) const
190	{
191		for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
192		{
193			rr::FragmentPacket& packet = packets[packetNdx];
194			for (int fragNdx = 0; fragNdx < rr::NUM_FRAGMENTS_PER_PACKET; ++fragNdx)
195			{
196				const tcu::Vec4 color = rr::readVarying<float>(packet, context, 0, fragNdx);
197				rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color);
198			}
199		}
200	}
201};
202
203class InstancedDrawInstance : public TestInstance
204{
205public:
206												InstancedDrawInstance	(Context& context, TestParams params);
207	virtual	tcu::TestStatus						iterate					(void);
208
209private:
210	void										prepareVertexData		(int instanceCount, int firstInstance);
211
212	const TestParams							m_params;
213	const vk::DeviceInterface&					m_vk;
214
215	vk::VkFormat								m_colorAttachmentFormat;
216
217	vk::Move<vk::VkPipeline>					m_pipeline;
218	vk::Move<vk::VkPipelineLayout>				m_pipelineLayout;
219
220	de::SharedPtr<Image>						m_colorTargetImage;
221	vk::Move<vk::VkImageView>					m_colorTargetView;
222
223	PipelineCreateInfo::VertexInputState		m_vertexInputState;
224
225	vk::Move<vk::VkCommandPool>					m_cmdPool;
226	vk::Move<vk::VkCommandBuffer>				m_cmdBuffer;
227
228	vk::Move<vk::VkFramebuffer>					m_framebuffer;
229	vk::Move<vk::VkRenderPass>					m_renderPass;
230
231	// Vertex data
232	std::vector<VertexPositionAndColor>			m_data;
233	std::vector<deUint32>						m_indexes;
234	std::vector<tcu::Vec4>						m_instancedColor;
235};
236
237class InstancedDrawCase : public TestCase
238{
239public:
240	InstancedDrawCase (tcu::TestContext&	testCtx,
241					   const std::string&	name,
242					   const std::string&	desc,
243					   TestParams			params)
244		: TestCase	(testCtx, name, desc)
245		, m_params	(params)
246	{
247		m_vertexShader = "#version 430\n"
248				"layout(location = 0) in vec4 in_position;\n"
249				"layout(location = 1) in vec4 in_color;\n"
250				"layout(location = 2) in vec4 in_color_2;\n"
251				"layout(push_constant) uniform TestParams {\n"
252				"	float firstInstance;\n"
253				"	float instanceCount;\n"
254				"} params;\n"
255				"layout(location = 0) out vec4 out_color;\n"
256				"out gl_PerVertex {\n"
257				"    vec4  gl_Position;\n"
258				"    float gl_PointSize;\n"
259				"};\n"
260				"void main() {\n"
261				"    gl_PointSize = 1.0;\n"
262				"    gl_Position  = in_position + vec4(float(gl_InstanceIndex - params.firstInstance) * 2.0 / params.instanceCount, 0.0, 0.0, 0.0);\n"
263				"    out_color    = in_color + vec4(float(gl_InstanceIndex) / params.instanceCount, 0.0, 0.0, 1.0) + in_color_2;\n"
264				"}\n";
265
266		m_fragmentShader = "#version 430\n"
267				"layout(location = 0) in vec4 in_color;\n"
268				"layout(location = 0) out vec4 out_color;\n"
269				"void main()\n"
270				"{\n"
271				"    out_color = in_color;\n"
272				"}\n";
273	}
274
275	TestInstance* createInstance (Context& context) const
276	{
277		return new InstancedDrawInstance(context, m_params);
278	}
279
280	virtual void initPrograms (vk::SourceCollections& programCollection) const
281	{
282		programCollection.glslSources.add("InstancedDrawVert") << glu::VertexSource(m_vertexShader);
283		programCollection.glslSources.add("InstancedDrawFrag") << glu::FragmentSource(m_fragmentShader);
284	}
285
286private:
287	const TestParams	m_params;
288	std::string			m_vertexShader;
289	std::string			m_fragmentShader;
290};
291
292InstancedDrawInstance::InstancedDrawInstance(Context &context, TestParams params)
293	: TestInstance				(context)
294	, m_params					(params)
295	, m_vk						(context.getDeviceInterface())
296	, m_colorAttachmentFormat	(vk::VK_FORMAT_R8G8B8A8_UNORM)
297{
298	const vk::VkDevice device				= m_context.getDevice();
299	const deUint32 queueFamilyIndex			= m_context.getUniversalQueueFamilyIndex();
300
301	const vk::VkPushConstantRange pushConstantRange = {
302		vk::VK_SHADER_STAGE_VERTEX_BIT,				// VkShaderStageFlags    stageFlags;
303		0u,											// uint32_t              offset;
304		(deUint32)sizeof(float) * 2,				// uint32_t              size;
305	};
306
307	const PipelineLayoutCreateInfo pipelineLayoutCreateInfo(0, DE_NULL, 1, &pushConstantRange);
308	m_pipelineLayout						= vk::createPipelineLayout(m_vk, device, &pipelineLayoutCreateInfo);
309
310	const vk::VkExtent3D targetImageExtent	= { WIDTH, HEIGHT, 1 };
311	const ImageCreateInfo targetImageCreateInfo(vk::VK_IMAGE_TYPE_2D, m_colorAttachmentFormat, targetImageExtent, 1, 1, vk::VK_SAMPLE_COUNT_1_BIT,
312		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);
313
314	m_colorTargetImage						= Image::createAndAlloc(m_vk, device, targetImageCreateInfo, m_context.getDefaultAllocator());
315
316	const ImageViewCreateInfo colorTargetViewInfo(m_colorTargetImage->object(), vk::VK_IMAGE_VIEW_TYPE_2D, m_colorAttachmentFormat);
317	m_colorTargetView						= vk::createImageView(m_vk, device, &colorTargetViewInfo);
318
319	RenderPassCreateInfo renderPassCreateInfo;
320	renderPassCreateInfo.addAttachment(AttachmentDescription(m_colorAttachmentFormat,
321															 vk::VK_SAMPLE_COUNT_1_BIT,
322															 vk::VK_ATTACHMENT_LOAD_OP_LOAD,
323															 vk::VK_ATTACHMENT_STORE_OP_STORE,
324															 vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE,
325															 vk::VK_ATTACHMENT_STORE_OP_STORE,
326															 vk::VK_IMAGE_LAYOUT_GENERAL,
327															 vk::VK_IMAGE_LAYOUT_GENERAL));
328
329	const vk::VkAttachmentReference colorAttachmentReference =
330	{
331		0,
332		vk::VK_IMAGE_LAYOUT_GENERAL
333	};
334
335	renderPassCreateInfo.addSubpass(SubpassDescription(vk::VK_PIPELINE_BIND_POINT_GRAPHICS,
336													   0,
337													   0,
338													   DE_NULL,
339													   1,
340													   &colorAttachmentReference,
341													   DE_NULL,
342													   AttachmentReference(),
343													   0,
344													   DE_NULL));
345
346	m_renderPass		= vk::createRenderPass(m_vk, device, &renderPassCreateInfo);
347
348	std::vector<vk::VkImageView> colorAttachments(1);
349	colorAttachments[0] = *m_colorTargetView;
350
351	const FramebufferCreateInfo framebufferCreateInfo(*m_renderPass, colorAttachments, WIDTH, HEIGHT, 1);
352
353	m_framebuffer		= vk::createFramebuffer(m_vk, device, &framebufferCreateInfo);
354
355	const vk::VkVertexInputBindingDescription vertexInputBindingDescription[2] =
356	{
357		{
358			0u,
359			(deUint32)sizeof(VertexPositionAndColor),
360			vk::VK_VERTEX_INPUT_RATE_VERTEX,
361		},
362		{
363			1u,
364			(deUint32)sizeof(tcu::Vec4),
365			vk::VK_VERTEX_INPUT_RATE_INSTANCE,
366		},
367	};
368
369	const vk::VkVertexInputAttributeDescription vertexInputAttributeDescriptions[] =
370	{
371		{
372			0u,
373			0u,
374			vk::VK_FORMAT_R32G32B32A32_SFLOAT,
375			0u
376		},
377		{
378			1u,
379			0u,
380			vk::VK_FORMAT_R32G32B32A32_SFLOAT,
381			(deUint32)sizeof(tcu::Vec4),
382		},
383		{
384			2u,
385			1u,
386			vk::VK_FORMAT_R32G32B32A32_SFLOAT,
387			0,
388		}
389	};
390
391	m_vertexInputState = PipelineCreateInfo::VertexInputState(2,
392															  vertexInputBindingDescription,
393															  DE_LENGTH_OF_ARRAY(vertexInputAttributeDescriptions),
394															  vertexInputAttributeDescriptions);
395
396	const CmdPoolCreateInfo cmdPoolCreateInfo(queueFamilyIndex);
397	m_cmdPool = vk::createCommandPool(m_vk, device, &cmdPoolCreateInfo);
398
399	m_cmdBuffer = vk::allocateCommandBuffer(m_vk, device, *m_cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
400
401	const vk::Unique<vk::VkShaderModule> vs(createShaderModule(m_vk, device, m_context.getBinaryCollection().get("InstancedDrawVert"), 0));
402	const vk::Unique<vk::VkShaderModule> fs(createShaderModule(m_vk, device, m_context.getBinaryCollection().get("InstancedDrawFrag"), 0));
403
404	const PipelineCreateInfo::ColorBlendState::Attachment vkCbAttachmentState;
405
406	vk::VkViewport viewport;
407	viewport.x				= 0;
408	viewport.y				= 0;
409	viewport.width			= static_cast<float>(WIDTH);
410	viewport.height			= static_cast<float>(HEIGHT);
411	viewport.minDepth		= 0.0f;
412	viewport.maxDepth		= 1.0f;
413
414	vk::VkRect2D scissor;
415	scissor.offset.x		= 0;
416	scissor.offset.y		= 0;
417	scissor.extent.width	= WIDTH;
418	scissor.extent.height	= HEIGHT;
419
420	PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, 0);
421	pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vs, "main", vk::VK_SHADER_STAGE_VERTEX_BIT));
422	pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fs, "main", vk::VK_SHADER_STAGE_FRAGMENT_BIT));
423	pipelineCreateInfo.addState(PipelineCreateInfo::VertexInputState(m_vertexInputState));
424	pipelineCreateInfo.addState(PipelineCreateInfo::InputAssemblerState(m_params.topology));
425	pipelineCreateInfo.addState(PipelineCreateInfo::ColorBlendState(1, &vkCbAttachmentState));
426	pipelineCreateInfo.addState(PipelineCreateInfo::ViewportState(1, std::vector<vk::VkViewport>(1, viewport), std::vector<vk::VkRect2D>(1, scissor)));
427	pipelineCreateInfo.addState(PipelineCreateInfo::DepthStencilState());
428	pipelineCreateInfo.addState(PipelineCreateInfo::RasterizerState());
429	pipelineCreateInfo.addState(PipelineCreateInfo::MultiSampleState());
430
431	m_pipeline = vk::createGraphicsPipeline(m_vk, device, DE_NULL, &pipelineCreateInfo);
432}
433
434tcu::TestStatus InstancedDrawInstance::iterate()
435{
436	const vk::VkQueue		queue					= m_context.getUniversalQueue();
437	static const deUint32	instanceCounts[]		= { 0, 1, 2, 4, 20 };
438	static const deUint32	firstInstanceIndices[]	= { 0, 1, 3, 4, 20 };
439
440	qpTestResult			res						= QP_TEST_RESULT_PASS;
441
442	const vk::VkClearColorValue clearColor = { { 0.0f, 0.0f, 0.0f, 1.0f } };
443	const CmdBufferBeginInfo beginInfo;
444	int firstInstanceIndicesCount = 1;
445
446	// Require 'drawIndirectFirstInstance' feature to run non-zero firstInstance indirect draw tests.
447	if (m_context.getDeviceFeatures().drawIndirectFirstInstance)
448		firstInstanceIndicesCount = DE_LENGTH_OF_ARRAY(firstInstanceIndices);
449
450	for (int instanceCountNdx = 0; instanceCountNdx < DE_LENGTH_OF_ARRAY(instanceCounts); instanceCountNdx++)
451	{
452		const deUint32 instanceCount = instanceCounts[instanceCountNdx];
453		for (int firstInstanceIndexNdx = 0; firstInstanceIndexNdx < firstInstanceIndicesCount; firstInstanceIndexNdx++)
454		{
455			// Prepare vertex data for at least one instance
456			const deUint32				prepareCount			= de::max(instanceCount, 1u);
457			const deUint32				firstInstance			= firstInstanceIndices[firstInstanceIndexNdx];
458
459			prepareVertexData(prepareCount, firstInstance);
460			const de::SharedPtr<Buffer>	vertexBuffer			= createAndUploadBuffer(m_data, m_vk, m_context, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
461			const de::SharedPtr<Buffer>	instancedVertexBuffer	= createAndUploadBuffer(m_instancedColor, m_vk, m_context, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
462			de::SharedPtr<Buffer>		indexBuffer;
463			de::SharedPtr<Buffer>		indirectBuffer;
464			m_vk.beginCommandBuffer(*m_cmdBuffer, &beginInfo);
465
466			initialTransitionColor2DImage(m_vk, *m_cmdBuffer, m_colorTargetImage->object(), vk::VK_IMAGE_LAYOUT_GENERAL);
467
468			const ImageSubresourceRange subresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT);
469			m_vk.cmdClearColorImage(*m_cmdBuffer, m_colorTargetImage->object(),
470				vk::VK_IMAGE_LAYOUT_GENERAL, &clearColor, 1, &subresourceRange);
471
472			const vk::VkMemoryBarrier memBarrier =
473			{
474				vk::VK_STRUCTURE_TYPE_MEMORY_BARRIER,
475				DE_NULL,
476				vk::VK_ACCESS_TRANSFER_WRITE_BIT,
477				vk::VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
478			};
479
480			m_vk.cmdPipelineBarrier(*m_cmdBuffer, vk::VK_PIPELINE_STAGE_TRANSFER_BIT,
481				vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
482				0, 1, &memBarrier, 0, DE_NULL, 0, DE_NULL);
483
484			const vk::VkRect2D renderArea = { { 0, 0 }, { WIDTH, HEIGHT } };
485			const RenderPassBeginInfo renderPassBegin(*m_renderPass, *m_framebuffer, renderArea);
486
487			m_vk.cmdBeginRenderPass(*m_cmdBuffer, &renderPassBegin, vk::VK_SUBPASS_CONTENTS_INLINE);
488
489			if (m_params.function == TestParams::FUNCTION_DRAW_INDEXED || m_params.function == TestParams::FUNCTION_DRAW_INDEXED_INDIRECT)
490			{
491				indexBuffer = createAndUploadBuffer(m_indexes, m_vk, m_context, vk::VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
492				m_vk.cmdBindIndexBuffer(*m_cmdBuffer, indexBuffer->object(), 0, vk::VK_INDEX_TYPE_UINT32);
493			}
494
495			const vk::VkBuffer vertexBuffers[] =
496			{
497				vertexBuffer->object(),
498				instancedVertexBuffer->object(),
499			};
500
501			const vk::VkDeviceSize vertexBufferOffsets[] =
502			{
503				0,	// vertexBufferOffset
504				0,	// instancedVertexBufferOffset
505			};
506
507			m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, DE_LENGTH_OF_ARRAY(vertexBuffers), vertexBuffers, vertexBufferOffsets);
508
509			const float pushConstants[] = { (float)firstInstance, (float)instanceCount };
510			m_vk.cmdPushConstants(*m_cmdBuffer, *m_pipelineLayout, vk::VK_SHADER_STAGE_VERTEX_BIT, 0u, (deUint32)sizeof(pushConstants), pushConstants);
511
512			m_vk.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
513
514			switch (m_params.function)
515			{
516				case TestParams::FUNCTION_DRAW:
517					m_vk.cmdDraw(*m_cmdBuffer, (deUint32)m_data.size(), instanceCount, 0u, firstInstance);
518					break;
519
520				case TestParams::FUNCTION_DRAW_INDEXED:
521					m_vk.cmdDrawIndexed(*m_cmdBuffer, (deUint32)m_indexes.size(), instanceCount, 0u, 0u, firstInstance);
522					break;
523
524				case TestParams::FUNCTION_DRAW_INDIRECT:
525				{
526					vk::VkDrawIndirectCommand drawCommand =
527					{
528						(deUint32)m_data.size(),	// uint32_t	vertexCount;
529						instanceCount,				// uint32_t	instanceCount;
530						0u,							// uint32_t	firstVertex;
531						firstInstance,				// uint32_t	firstInstance;
532					};
533					std::vector<vk::VkDrawIndirectCommand> drawCommands;
534					drawCommands.push_back(drawCommand);
535					indirectBuffer = createAndUploadBuffer(drawCommands, m_vk, m_context, vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
536
537					m_vk.cmdDrawIndirect(*m_cmdBuffer, indirectBuffer->object(), 0, 1u, 0u);
538					break;
539				}
540				case TestParams::FUNCTION_DRAW_INDEXED_INDIRECT:
541				{
542					vk::VkDrawIndexedIndirectCommand drawCommand =
543					{
544						(deUint32)m_indexes.size(),	// uint32_t	indexCount;
545						instanceCount,				// uint32_t	instanceCount;
546						0u,							// uint32_t	firstIndex;
547						0,							// int32_t	vertexOffset;
548						firstInstance,				// uint32_t	firstInstance;
549					};
550					std::vector<vk::VkDrawIndexedIndirectCommand> drawCommands;
551					drawCommands.push_back(drawCommand);
552					indirectBuffer = createAndUploadBuffer(drawCommands, m_vk, m_context, vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
553
554					m_vk.cmdDrawIndexedIndirect(*m_cmdBuffer, indirectBuffer->object(), 0, 1u, 0u);
555					break;
556				}
557				default:
558					DE_ASSERT(false);
559			}
560
561			m_vk.cmdEndRenderPass(*m_cmdBuffer);
562			m_vk.endCommandBuffer(*m_cmdBuffer);
563
564			vk::VkSubmitInfo submitInfo =
565			{
566				vk::VK_STRUCTURE_TYPE_SUBMIT_INFO,			// VkStructureType				sType;
567				DE_NULL,									// const void*					pNext;
568				0,											// deUint32						waitSemaphoreCount;
569				DE_NULL,									// const VkSemaphore*			pWaitSemaphores;
570				(const vk::VkPipelineStageFlags*)DE_NULL,	// const VkPipelineStageFlags*	pWaitDstStageMask;
571				1,											// deUint32						commandBufferCount;
572				&m_cmdBuffer.get(),							// const VkCommandBuffer*		pCommandBuffers;
573				0,											// deUint32						signalSemaphoreCount;
574				DE_NULL										// const VkSemaphore*			pSignalSemaphores;
575			};
576			VK_CHECK(m_vk.queueSubmit(queue, 1, &submitInfo, DE_NULL));
577
578			VK_CHECK(m_vk.queueWaitIdle(queue));
579
580			// Reference rendering
581			std::vector<tcu::Vec4>	vetrices;
582			std::vector<tcu::Vec4>	colors;
583
584			for (std::vector<VertexPositionAndColor>::const_iterator it = m_data.begin(); it != m_data.end(); ++it)
585			{
586				vetrices.push_back(it->position);
587				colors.push_back(it->color);
588			}
589
590			tcu::TextureLevel refImage (vk::mapVkFormat(m_colorAttachmentFormat), (int)(0.5 + WIDTH), (int)(0.5 + HEIGHT));
591
592			tcu::clear(refImage.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
593
594			const TestVertShader					vertShader(instanceCount, firstInstance);
595			const TestFragShader					fragShader;
596			const rr::Program						program			(&vertShader, &fragShader);
597			const rr::MultisamplePixelBufferAccess	colorBuffer		= rr::MultisamplePixelBufferAccess::fromSinglesampleAccess(refImage.getAccess());
598			const rr::RenderTarget					renderTarget	(colorBuffer);
599			const rr::RenderState					renderState		((rr::ViewportState(colorBuffer)));
600			const rr::Renderer						renderer;
601
602			const rr::VertexAttrib	vertexAttribs[] =
603			{
604				rr::VertexAttrib(rr::VERTEXATTRIBTYPE_FLOAT, 4, sizeof(tcu::Vec4), 0, &vetrices[0]),
605				rr::VertexAttrib(rr::VERTEXATTRIBTYPE_FLOAT, 4, sizeof(tcu::Vec4), 0, &colors[0]),
606				rr::VertexAttrib(rr::VERTEXATTRIBTYPE_FLOAT, 4, sizeof(tcu::Vec4), 1, &m_instancedColor[0])
607			};
608
609			if (m_params.function == TestParams::FUNCTION_DRAW || m_params.function == TestParams::FUNCTION_DRAW_INDIRECT)
610			{
611				const rr::PrimitiveList	primitives = rr::PrimitiveList(mapVkPrimitiveTopology(m_params.topology), (int)vetrices.size(), 0);
612				const rr::DrawCommand	command(renderState, renderTarget, program, DE_LENGTH_OF_ARRAY(vertexAttribs), &vertexAttribs[0],
613												primitives);
614				renderer.drawInstanced(command, instanceCount);
615			}
616			else
617			{
618				const rr::DrawIndices indicies(m_indexes.data());
619
620				const rr::PrimitiveList	primitives = rr::PrimitiveList(mapVkPrimitiveTopology(m_params.topology), (int)m_indexes.size(), indicies);
621				const rr::DrawCommand	command(renderState, renderTarget, program, DE_LENGTH_OF_ARRAY(vertexAttribs), &vertexAttribs[0],
622												primitives);
623				renderer.drawInstanced(command, instanceCount);
624			}
625
626			const vk::VkOffset3D zeroOffset = { 0, 0, 0 };
627			const tcu::ConstPixelBufferAccess renderedFrame = m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(),
628				vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT);
629
630			tcu::TestLog &log		= m_context.getTestContext().getLog();
631
632			std::ostringstream resultDesc;
633			resultDesc << "Image comparison result. Instance count: " << instanceCount << " first instance index: " << firstInstance;
634
635			if (m_params.topology == vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
636			{
637				const bool ok = tcu::intThresholdPositionDeviationCompare(
638					log, "Result", resultDesc.str().c_str(), refImage.getAccess(), renderedFrame,
639					tcu::UVec4(4u),					// color threshold
640					tcu::IVec3(1, 1, 0),			// position deviation tolerance
641					true,							// don't check the pixels at the boundary
642					tcu::COMPARE_LOG_RESULT);
643
644				if (!ok)
645					res = QP_TEST_RESULT_FAIL;
646			}
647			else
648			{
649				if (!tcu::fuzzyCompare(log, "Result", resultDesc.str().c_str(), refImage.getAccess(), renderedFrame, 0.05f, tcu::COMPARE_LOG_RESULT))
650					res = QP_TEST_RESULT_FAIL;
651			}
652		}
653	}
654	return tcu::TestStatus(res, qpGetTestResultName(res));
655}
656
657void InstancedDrawInstance::prepareVertexData(int instanceCount, int firstInstance)
658{
659	m_data.clear();
660	m_indexes.clear();
661	m_instancedColor.clear();
662
663	if (m_params.function == TestParams::FUNCTION_DRAW || m_params.function == TestParams::FUNCTION_DRAW_INDIRECT)
664	{
665		for (int y = 0; y < QUAD_GRID_SIZE; y++)
666		{
667			for (int x = 0; x < QUAD_GRID_SIZE; x++)
668			{
669				const float fx0 = -1.0f + (float)(x+0) / (float)QUAD_GRID_SIZE * 2.0f / (float)instanceCount;
670				const float fx1 = -1.0f + (float)(x+1) / (float)QUAD_GRID_SIZE * 2.0f / (float)instanceCount;
671				const float fy0 = -1.0f + (float)(y+0) / (float)QUAD_GRID_SIZE * 2.0f;
672				const float fy1 = -1.0f + (float)(y+1) / (float)QUAD_GRID_SIZE * 2.0f;
673
674				// Vertices of a quad's lower-left triangle: (fx0, fy0), (fx1, fy0) and (fx0, fy1)
675				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx0, fy0, 1.0f, 1.0f), tcu::RGBA::blue().toVec()));
676				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx1, fy0, 1.0f, 1.0f), tcu::RGBA::blue().toVec()));
677				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx0, fy1, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
678
679				// Vertices of a quad's upper-right triangle: (fx1, fy1), (fx0, fy1) and (fx1, fy0)
680				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx1, fy1, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
681				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx0, fy1, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
682				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx1, fy0, 1.0f, 1.0f), tcu::RGBA::blue().toVec()));
683			}
684		}
685	}
686	else
687	{
688		for (int y = 0; y < QUAD_GRID_SIZE + 1; y++)
689		{
690			for (int x = 0; x < QUAD_GRID_SIZE + 1; x++)
691			{
692				const float fx = -1.0f + (float)x / (float)QUAD_GRID_SIZE * 2.0f / (float)instanceCount;
693				const float fy = -1.0f + (float)y / (float)QUAD_GRID_SIZE * 2.0f;
694
695				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx, fy, 1.0f, 1.0f),
696														(y % 2 ? tcu::RGBA::blue().toVec() : tcu::RGBA::green().toVec())));
697			}
698		}
699
700		for (int y = 0; y < QUAD_GRID_SIZE; y++)
701		{
702			for (int x = 0; x < QUAD_GRID_SIZE; x++)
703			{
704				const int ndx00 = y*(QUAD_GRID_SIZE + 1) + x;
705				const int ndx10 = y*(QUAD_GRID_SIZE + 1) + x + 1;
706				const int ndx01 = (y + 1)*(QUAD_GRID_SIZE + 1) + x;
707				const int ndx11 = (y + 1)*(QUAD_GRID_SIZE + 1) + x + 1;
708
709				// Lower-left triangle of a quad.
710				m_indexes.push_back((deUint16)ndx00);
711				m_indexes.push_back((deUint16)ndx10);
712				m_indexes.push_back((deUint16)ndx01);
713
714				// Upper-right triangle of a quad.
715				m_indexes.push_back((deUint16)ndx11);
716				m_indexes.push_back((deUint16)ndx01);
717				m_indexes.push_back((deUint16)ndx10);
718			}
719		}
720	}
721
722	for (int i = 0; i < instanceCount + firstInstance; i++)
723	{
724		m_instancedColor.push_back(tcu::Vec4(0.0, (float)(1.0 - i * 1.0 / (instanceCount + firstInstance)) / 2, 0.0, 1.0));
725	}
726}
727
728} // anonymus
729
730InstancedTests::InstancedTests(tcu::TestContext& testCtx)
731	: TestCaseGroup	(testCtx, "instanced", "Instanced drawing tests")
732{
733	static const vk::VkPrimitiveTopology	topologies[]			=
734	{
735		vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
736		vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
737		vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
738		vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
739		vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
740		vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
741	};
742	static const TestParams::DrawFunction	functions[]				=
743	{
744		TestParams::FUNCTION_DRAW,
745		TestParams::FUNCTION_DRAW_INDEXED,
746		TestParams::FUNCTION_DRAW_INDIRECT,
747		TestParams::FUNCTION_DRAW_INDEXED_INDIRECT,
748	};
749
750	for (int topologyNdx = 0; topologyNdx < DE_LENGTH_OF_ARRAY(topologies); topologyNdx++)
751	{
752		for (int functionNdx = 0; functionNdx < DE_LENGTH_OF_ARRAY(functions); functionNdx++)
753		{
754			TestParams param;
755			param.function = functions[functionNdx];
756			param.topology = topologies[topologyNdx];
757
758			std::string testName = de::toString(param);
759
760			addChild(new InstancedDrawCase(m_testCtx, de::toLower(testName), "Instanced drawing test", param));
761		}
762	}
763}
764
765InstancedTests::~InstancedTests() {}
766
767} // DrawTests
768} // vkt
769