1/*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2015 The Khronos Group Inc.
6 * Copyright (c) 2015 Intel Corporation
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 Draw Indirect Test
23 *//*--------------------------------------------------------------------*/
24
25#include "vktDrawIndirectTest.hpp"
26
27#include "vktTestCaseUtil.hpp"
28#include "vktDrawTestCaseUtil.hpp"
29
30#include "vktDrawBaseClass.hpp"
31
32#include "tcuTestLog.hpp"
33#include "tcuResource.hpp"
34#include "tcuImageCompare.hpp"
35#include "tcuTextureUtil.hpp"
36#include "tcuRGBA.hpp"
37
38#include "vkDefs.hpp"
39
40namespace vkt
41{
42namespace Draw
43{
44namespace
45{
46
47enum
48{
49	VERTEX_OFFSET = 13
50};
51
52struct JunkData
53{
54	JunkData()
55		: varA	(0xcd)
56		, varB	(0xcd)
57	{
58	}
59	const deUint16	varA;
60	const deUint32	varB;
61};
62
63enum DrawType
64{
65	DRAW_TYPE_SEQUENTIAL,
66	DRAW_TYPE_INDEXED,
67
68	DRAWTYPE_LAST
69};
70
71struct DrawTypedTestSpec : public TestSpecBase
72{
73	DrawType drawType;
74};
75
76class IndirectDraw : public DrawTestsBaseClass
77{
78public:
79	typedef DrawTypedTestSpec	TestSpec;
80
81								IndirectDraw	(Context &context, TestSpec testSpec);
82	virtual	tcu::TestStatus		iterate			(void);
83
84	template<typename T> void	addCommand		(const T&);
85
86protected:
87	std::vector<char>		m_indirectBufferContents;
88	de::SharedPtr<Buffer>		m_indirectBuffer;
89	vk::VkDeviceSize		m_offsetInBuffer;
90	deUint32			m_strideInBuffer;
91
92	deUint32			m_drawCount;
93	JunkData			m_junkData;
94
95	const DrawType			m_drawType;
96	deBool				m_isMultiDrawEnabled;
97	deUint32			m_drawIndirectMaxCount;
98
99	de::SharedPtr<Buffer>		m_indexBuffer;
100};
101
102struct FirtsInstanceSupported
103{
104	static deUint32 getFirstInstance	(void)											{ return 2; }
105	static bool		isTestSupported		(const vk::VkPhysicalDeviceFeatures& features)	{ return features.drawIndirectFirstInstance == VK_TRUE; }
106};
107
108struct FirtsInstanceNotSupported
109{
110	static deUint32 getFirstInstance	(void)											{ return 0; }
111	static bool		isTestSupported		(const vk::VkPhysicalDeviceFeatures&)			{ return true; }
112};
113
114template<class FirstInstanceSupport>
115class IndirectDrawInstanced : public IndirectDraw
116{
117public:
118								IndirectDrawInstanced	(Context &context, TestSpec testSpec);
119	virtual tcu::TestStatus		iterate					(void);
120};
121
122IndirectDraw::IndirectDraw (Context &context, TestSpec testSpec)
123	: DrawTestsBaseClass	(context, testSpec.shaders[glu::SHADERTYPE_VERTEX], testSpec.shaders[glu::SHADERTYPE_FRAGMENT], testSpec.topology)
124	, m_drawType			(testSpec.drawType)
125{
126
127	int refVertexIndex = 2;
128
129	if (testSpec.drawType == DRAW_TYPE_INDEXED)
130	{
131		for (int unusedIdx = 0; unusedIdx < VERTEX_OFFSET; unusedIdx++)
132		{
133			m_data.push_back(VertexElementData(tcu::Vec4(-1.0f, 1.0f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), -1));
134		}
135		refVertexIndex += VERTEX_OFFSET;
136	}
137
138	m_data.push_back(VertexElementData(tcu::Vec4( 1.0f, -1.0f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), -1));
139	m_data.push_back(VertexElementData(tcu::Vec4(-1.0f, 1.0f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), -1));
140
141	switch (m_topology)
142	{
143		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
144			m_data.push_back(VertexElementData(tcu::Vec4(-0.3f,	-0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
145			m_data.push_back(VertexElementData(tcu::Vec4(-0.3f,	 0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
146			m_data.push_back(VertexElementData(tcu::Vec4( 0.3f,	-0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
147			m_data.push_back(VertexElementData(tcu::Vec4( 0.3f,	-0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
148			m_data.push_back(VertexElementData(tcu::Vec4( 0.3f,	 0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
149			m_data.push_back(VertexElementData(tcu::Vec4(-0.3f,	 0.3f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), refVertexIndex++));
150			break;
151		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
152			m_data.push_back(VertexElementData(tcu::Vec4(-0.3f,	 0.0f, 1.0f, 1.0f),	 tcu::RGBA::blue().toVec(), refVertexIndex++));
153			m_data.push_back(VertexElementData(tcu::Vec4( 0.3f,	 0.0f, 1.0f, 1.0f),	 tcu::RGBA::blue().toVec(), refVertexIndex++));
154			m_data.push_back(VertexElementData(tcu::Vec4(-0.3f,	-0.3f, 1.0f, 1.0f),	 tcu::RGBA::blue().toVec(), refVertexIndex++));
155			m_data.push_back(VertexElementData(tcu::Vec4( 0.3f,	-0.3f, 1.0f, 1.0f),	 tcu::RGBA::blue().toVec(), refVertexIndex++));
156			m_data.push_back(VertexElementData(tcu::Vec4(-0.3f,	 0.3f, 1.0f, 1.0f),	 tcu::RGBA::blue().toVec(), refVertexIndex++));
157			m_data.push_back(VertexElementData(tcu::Vec4( 0.3f,	 0.3f, 1.0f, 1.0f),	 tcu::RGBA::blue().toVec(), refVertexIndex++));
158			m_data.push_back(VertexElementData(tcu::Vec4(-0.3f,	 0.0f, 1.0f, 1.0f),	 tcu::RGBA::blue().toVec(), refVertexIndex++));
159			m_data.push_back(VertexElementData(tcu::Vec4( 0.3f,	 0.0f, 1.0f, 1.0f),	 tcu::RGBA::blue().toVec(), refVertexIndex++));
160			break;
161		default:
162			DE_FATAL("Unknown topology");
163			break;
164	}
165
166	m_data.push_back(VertexElementData(tcu::Vec4(-1.0f, 1.0f, 1.0f, 1.0f), tcu::RGBA::blue().toVec(), -1));
167
168	initialize();
169
170	if (testSpec.drawType == DRAW_TYPE_INDEXED)
171	{
172		const size_t indexBufferLength = m_data.size() - VERTEX_OFFSET;
173
174		m_indexBuffer = Buffer::createAndAlloc(m_vk, m_context.getDevice(), BufferCreateInfo(sizeof(deUint32) * indexBufferLength, vk::VK_BUFFER_USAGE_INDEX_BUFFER_BIT), m_context.getDefaultAllocator(), vk::MemoryRequirement::HostVisible);
175		deUint32* indices = reinterpret_cast<deUint32*>(m_indexBuffer->getBoundMemory().getHostPtr());
176		for (size_t i = 0; i < indexBufferLength; i++)
177		{
178			indices[i] = static_cast<deUint32>(i);
179		}
180		vk::flushMappedMemoryRange(m_vk, m_context.getDevice(), m_indexBuffer->getBoundMemory().getMemory(), m_indexBuffer->getBoundMemory().getOffset(), sizeof(deUint32) * indexBufferLength);
181	}
182
183	// Check device for multidraw support:
184	if (m_context.getDeviceFeatures().multiDrawIndirect)
185		m_isMultiDrawEnabled = true;
186	else
187		m_isMultiDrawEnabled = false;
188
189	m_drawIndirectMaxCount = m_context.getDeviceProperties().limits.maxDrawIndirectCount;
190
191}
192
193template<>
194void IndirectDraw::addCommand<vk::VkDrawIndirectCommand> (const vk::VkDrawIndirectCommand& command)
195{
196	DE_ASSERT(m_drawType == DRAW_TYPE_SEQUENTIAL);
197
198	const size_t currentSize = m_indirectBufferContents.size();
199
200	m_indirectBufferContents.resize(currentSize + sizeof(command));
201
202	deMemcpy(&m_indirectBufferContents[currentSize], &command, sizeof(command));
203}
204
205template<>
206void IndirectDraw::addCommand<vk::VkDrawIndexedIndirectCommand>(const vk::VkDrawIndexedIndirectCommand& command)
207{
208	DE_ASSERT(m_drawType == DRAW_TYPE_INDEXED);
209
210	const size_t currentSize = m_indirectBufferContents.size();
211
212	m_indirectBufferContents.resize(currentSize + sizeof(command));
213
214	deMemcpy(&m_indirectBufferContents[currentSize], &command, sizeof(command));
215}
216
217tcu::TestStatus IndirectDraw::iterate (void)
218{
219	tcu::TestLog &log = m_context.getTestContext().getLog();
220	const vk::VkQueue queue = m_context.getUniversalQueue();
221
222	if (m_drawType == DRAW_TYPE_SEQUENTIAL)
223	{
224		switch (m_topology)
225		{
226		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
227		{
228			vk::VkDrawIndirectCommand drawCommands[] =
229			{
230				{
231					3,		//vertexCount
232					1,		//instanceCount
233					2,		//firstVertex
234					0		//firstInstance
235				},
236				{ (deUint32)-4, (deUint32)-2, (deUint32)-11, (deUint32)-9 }, // junk (stride)
237				{
238					3,		//vertexCount
239					1,		//instanceCount
240					5,		//firstVertex
241					0		//firstInstance
242				}
243			};
244			addCommand(drawCommands[0]);
245			addCommand(drawCommands[1]);
246			addCommand(drawCommands[2]);
247			break;
248		}
249		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
250		{
251			vk::VkDrawIndirectCommand drawCommands[] =
252			{
253				{
254					4,		//vertexCount
255					1,		//instanceCount
256					2,		//firstVertex
257					0		//firstInstance
258				},
259				{ (deUint32)-4, (deUint32)-2, (deUint32)-11, (deUint32)-9 }, // junk (stride)
260				{
261					4,		//vertexCount
262					1,		//instanceCount
263					6,		//firstVertex
264					0		//firstInstance
265				}
266			};
267			addCommand(drawCommands[0]);
268			addCommand(drawCommands[1]);
269			addCommand(drawCommands[2]);
270			break;
271		}
272		default:
273			TCU_FAIL("impossible");
274		}
275
276		m_strideInBuffer = 2 * (deUint32)sizeof(vk::VkDrawIndirectCommand);
277	}
278	else if (m_drawType == DRAW_TYPE_INDEXED)
279	{
280		switch (m_topology)
281		{
282		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
283		{
284			vk::VkDrawIndexedIndirectCommand drawCommands[] =
285			{
286				{
287					3,					// indexCount
288					1,					// instanceCount
289					2,					// firstIndex
290					VERTEX_OFFSET,		// vertexOffset
291					0,					// firstInstance
292				},
293				{ (deUint32)-4, (deUint32)-2, (deUint32)-11, (deInt32)9, (deUint32)-7 }, // junk (stride)
294				{
295					3,					// indexCount
296					1,					// instanceCount
297					5,					// firstIndex
298					VERTEX_OFFSET,		// vertexOffset
299					0,					// firstInstance
300				}
301			};
302			addCommand(drawCommands[0]);
303			addCommand(drawCommands[1]);
304			addCommand(drawCommands[2]);
305			break;
306		}
307		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
308		{
309			vk::VkDrawIndexedIndirectCommand drawCommands[] =
310			{
311				{
312					4,				// indexCount
313					1,				// instanceCount
314					2,				// firstIndex
315					VERTEX_OFFSET,	// vertexOffset
316					0,				// firstInstance
317				},
318				{ (deUint32)-4, (deUint32)-2, (deUint32)-11, (deInt32)9, (deUint32)-7 }, // junk (stride)
319				{
320					4,				// indexCount
321					1,				// instanceCount
322					6,				// firstIndex
323					VERTEX_OFFSET,	// vertexOffset
324					0,				// firstInstance
325				}
326			};
327			addCommand(drawCommands[0]);
328			addCommand(drawCommands[1]);
329			addCommand(drawCommands[2]);
330			break;
331		}
332		default:
333			TCU_FAIL("impossible");
334		}
335
336		m_strideInBuffer = 2 * (deUint32)sizeof(vk::VkDrawIndexedIndirectCommand);
337	}
338
339	m_drawCount			= 2;
340	m_offsetInBuffer	= sizeof(m_junkData);
341
342	beginRenderPass();
343
344	const vk::VkDeviceSize vertexBufferOffset	= 0;
345	const vk::VkBuffer vertexBuffer				= m_vertexBuffer->object();
346
347	m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
348
349	const vk::VkDeviceSize dataSize = m_indirectBufferContents.size();
350
351	m_indirectBuffer = Buffer::createAndAlloc(	m_vk,
352												m_context.getDevice(),
353												BufferCreateInfo(dataSize + m_offsetInBuffer,
354																 vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT),
355												m_context.getDefaultAllocator(),
356												vk::MemoryRequirement::HostVisible);
357
358	deUint8* ptr = reinterpret_cast<deUint8*>(m_indirectBuffer->getBoundMemory().getHostPtr());
359
360	deMemcpy(ptr, &m_junkData, static_cast<size_t>(m_offsetInBuffer));
361	deMemcpy(ptr + m_offsetInBuffer, &m_indirectBufferContents[0], static_cast<size_t>(dataSize));
362
363	vk::flushMappedMemoryRange(m_vk,
364							   m_context.getDevice(),
365							   m_indirectBuffer->getBoundMemory().getMemory(),
366							   m_indirectBuffer->getBoundMemory().getOffset(),
367							   dataSize + m_offsetInBuffer);
368
369	m_vk.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
370
371	if (m_drawType == DRAW_TYPE_INDEXED)
372	{
373		m_vk.cmdBindIndexBuffer(*m_cmdBuffer, m_indexBuffer->object(), DE_NULL, vk::VK_INDEX_TYPE_UINT32);
374	}
375
376	if (m_isMultiDrawEnabled && m_drawCount <= m_drawIndirectMaxCount)
377	{
378		switch (m_drawType)
379		{
380			case DRAW_TYPE_SEQUENTIAL:
381				m_vk.cmdDrawIndirect(*m_cmdBuffer, m_indirectBuffer->object(), m_offsetInBuffer, m_drawCount, m_strideInBuffer);
382				break;
383			case DRAW_TYPE_INDEXED:
384				m_vk.cmdDrawIndexedIndirect(*m_cmdBuffer, m_indirectBuffer->object(), m_offsetInBuffer, m_drawCount, m_strideInBuffer);
385				break;
386			default:
387				TCU_FAIL("impossible");
388		}
389	}
390	else
391	{
392		for (deUint32 drawNdx = 0; drawNdx < m_drawCount; drawNdx++)
393		{
394			switch (m_drawType)
395			{
396				case DRAW_TYPE_SEQUENTIAL:
397					m_vk.cmdDrawIndirect(*m_cmdBuffer, m_indirectBuffer->object(), m_offsetInBuffer + drawNdx*m_strideInBuffer, 1u, 0u);
398					break;
399				case DRAW_TYPE_INDEXED:
400					m_vk.cmdDrawIndexedIndirect(*m_cmdBuffer, m_indirectBuffer->object(), m_offsetInBuffer + drawNdx*m_strideInBuffer, 1u, 0u);
401					break;
402				default:
403					TCU_FAIL("impossible");
404			}
405		}
406	}
407	m_vk.cmdEndRenderPass(*m_cmdBuffer);
408	m_vk.endCommandBuffer(*m_cmdBuffer);
409
410	vk::VkSubmitInfo submitInfo =
411	{
412		vk::VK_STRUCTURE_TYPE_SUBMIT_INFO,	// VkStructureType			sType;
413		DE_NULL,							// const void*				pNext;
414		0,										// deUint32					waitSemaphoreCount;
415		DE_NULL,								// const VkSemaphore*		pWaitSemaphores;
416		(const vk::VkPipelineStageFlags*)DE_NULL,
417		1,										// deUint32					commandBufferCount;
418		&m_cmdBuffer.get(),					// const VkCommandBuffer*	pCommandBuffers;
419		0,										// deUint32					signalSemaphoreCount;
420		DE_NULL								// const VkSemaphore*		pSignalSemaphores;
421	};
422	VK_CHECK(m_vk.queueSubmit(queue, 1, &submitInfo, DE_NULL));
423
424	VK_CHECK(m_vk.queueWaitIdle(queue));
425
426	// Validation
427	tcu::Texture2D referenceFrame(vk::mapVkFormat(m_colorAttachmentFormat), (int)(0.5 + WIDTH), (int)(0.5 + HEIGHT));
428	referenceFrame.allocLevel(0);
429
430	const deInt32 frameWidth	= referenceFrame.getWidth();
431	const deInt32 frameHeight	= referenceFrame.getHeight();
432
433	tcu::clear(referenceFrame.getLevel(0), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
434
435	ReferenceImageCoordinates refCoords;
436
437	for (int y = 0; y < frameHeight; y++)
438	{
439		const float yCoord = (float)(y / (0.5*frameHeight)) - 1.0f;
440
441		for (int x = 0; x < frameWidth; x++)
442		{
443			const float xCoord = (float)(x / (0.5*frameWidth)) - 1.0f;
444
445			if ((yCoord >= refCoords.bottom	&&
446				 yCoord <= refCoords.top	&&
447				 xCoord >= refCoords.left	&&
448				 xCoord <= refCoords.right))
449				referenceFrame.getLevel(0).setPixel(tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f), x, y);
450		}
451	}
452
453	const vk::VkOffset3D zeroOffset					= { 0, 0, 0 };
454	const tcu::ConstPixelBufferAccess renderedFrame = m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(),
455		vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT);
456
457	qpTestResult res = QP_TEST_RESULT_PASS;
458
459	if (!tcu::fuzzyCompare(log, "Result", "Image comparison result",
460		referenceFrame.getLevel(0), renderedFrame, 0.05f,
461		tcu::COMPARE_LOG_RESULT)) {
462		res = QP_TEST_RESULT_FAIL;
463	}
464
465	return tcu::TestStatus(res, qpGetTestResultName(res));
466
467}
468
469template<class FirstInstanceSupport>
470IndirectDrawInstanced<FirstInstanceSupport>::IndirectDrawInstanced (Context &context, TestSpec testSpec)
471	: IndirectDraw(context, testSpec)
472{
473	if (!FirstInstanceSupport::isTestSupported(m_context.getDeviceFeatures()))
474	{
475		throw tcu::NotSupportedError("Required 'drawIndirectFirstInstance' feature is not supported");
476	}
477}
478
479template<class FirstInstanceSupport>
480tcu::TestStatus IndirectDrawInstanced<FirstInstanceSupport>::iterate (void)
481{
482	tcu::TestLog &log = m_context.getTestContext().getLog();
483	const vk::VkQueue queue = m_context.getUniversalQueue();
484
485	if (m_drawType == DRAW_TYPE_SEQUENTIAL)
486	{
487		switch (m_topology)
488		{
489			case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
490			{
491				vk::VkDrawIndirectCommand drawCmd[] =
492				{
493					{
494						3,											//vertexCount
495						4,											//instanceCount
496						2,											//firstVertex
497						FirstInstanceSupport::getFirstInstance()	//firstInstance
498					},
499					{ (deUint32)-4, (deUint32)-2, (deUint32)-11, (deUint32)-9 }, // junk (stride)
500					{
501						3,											//vertexCount
502						4,											//instanceCount
503						5,											//firstVertex
504						FirstInstanceSupport::getFirstInstance()	//firstInstance
505					}
506				};
507				addCommand(drawCmd[0]);
508				addCommand(drawCmd[1]);
509				addCommand(drawCmd[2]);
510				break;
511			}
512			case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
513			{
514				vk::VkDrawIndirectCommand drawCmd[] =
515				{
516					{
517						4,											//vertexCount
518						4,											//instanceCount
519						2,											//firstVertex
520						FirstInstanceSupport::getFirstInstance()	//firstInstance
521					},
522					{ (deUint32)-4, (deUint32)-2, (deUint32)-11, (deUint32)-9 },
523					{
524						4,											//vertexCount
525						4,											//instanceCount
526						6,											//firstVertex
527						FirstInstanceSupport::getFirstInstance()	//firstInstance
528					}
529				};
530				addCommand(drawCmd[0]);
531				addCommand(drawCmd[1]);
532				addCommand(drawCmd[2]);
533				break;
534			}
535			default:
536				TCU_FAIL("impossible");
537				break;
538		}
539
540		m_strideInBuffer = 2 * (deUint32)sizeof(vk::VkDrawIndirectCommand);
541	}
542	else if (m_drawType == DRAW_TYPE_INDEXED)
543	{
544		switch (m_topology)
545		{
546			case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
547			{
548				vk::VkDrawIndexedIndirectCommand drawCmd[] =
549				{
550					{
551						3,											// indexCount
552						4,											// instanceCount
553						2,											// firstIndex
554						VERTEX_OFFSET,								// vertexOffset
555						FirstInstanceSupport::getFirstInstance()	// firstInstance
556					},
557					{ (deUint32)-4, (deUint32)-2, (deUint32)-11, (deInt32)9, (deUint32)-7 },	// junk (stride)
558					{
559						3,											// indexCount
560						4,											// instanceCount
561						5,											// firstIndex
562						VERTEX_OFFSET,								// vertexOffset
563						FirstInstanceSupport::getFirstInstance()	// firstInstance
564					}
565				};
566				addCommand(drawCmd[0]);
567				addCommand(drawCmd[1]);
568				addCommand(drawCmd[2]);
569				break;
570			}
571			case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
572			{
573				vk::VkDrawIndexedIndirectCommand drawCmd[] =
574				{
575					{
576						4,											// indexCount
577						4,											// instanceCount
578						2,											// firstIndex
579						VERTEX_OFFSET,								// vertexOffset
580						FirstInstanceSupport::getFirstInstance()	// firstInstance
581					},
582					{ (deUint32)-4, (deUint32)-2, (deUint32)-11, (deInt32)9, (deUint32)-7 },	// junk (stride)
583					{
584						4,											// indexCount
585						4,											// instanceCount
586						6,											// firstIndex
587						VERTEX_OFFSET,								// vertexOffset
588						FirstInstanceSupport::getFirstInstance()	// firstInstance
589					}
590				};
591				addCommand(drawCmd[0]);
592				addCommand(drawCmd[1]);
593				addCommand(drawCmd[2]);
594				break;
595			}
596			default:
597				TCU_FAIL("impossible");
598				break;
599		}
600
601		m_strideInBuffer = 2 * (deUint32)sizeof(vk::VkDrawIndexedIndirectCommand);
602	}
603
604	m_drawCount			= 2;
605	m_offsetInBuffer	= sizeof(m_junkData);
606
607	beginRenderPass();
608
609	const vk::VkDeviceSize vertexBufferOffset = 0;
610	const vk::VkBuffer vertexBuffer = m_vertexBuffer->object();
611
612	m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
613
614	const vk::VkDeviceSize dataSize = m_indirectBufferContents.size();
615
616	m_indirectBuffer = Buffer::createAndAlloc(	m_vk,
617												m_context.getDevice(),
618												BufferCreateInfo(dataSize + m_offsetInBuffer,
619																 vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT),
620												m_context.getDefaultAllocator(),
621												vk::MemoryRequirement::HostVisible);
622
623	deUint8* ptr = reinterpret_cast<deUint8*>(m_indirectBuffer->getBoundMemory().getHostPtr());
624
625	deMemcpy(ptr, &m_junkData, static_cast<size_t>(m_offsetInBuffer));
626	deMemcpy((ptr + m_offsetInBuffer), &m_indirectBufferContents[0], static_cast<size_t>(dataSize));
627
628	vk::flushMappedMemoryRange(m_vk,
629							   m_context.getDevice(),
630							   m_indirectBuffer->getBoundMemory().getMemory(),
631							   m_indirectBuffer->getBoundMemory().getOffset(),
632							   dataSize + m_offsetInBuffer);
633
634	m_vk.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
635
636	if (m_drawType == DRAW_TYPE_INDEXED)
637	{
638		m_vk.cmdBindIndexBuffer(*m_cmdBuffer, m_indexBuffer->object(), DE_NULL, vk::VK_INDEX_TYPE_UINT32);
639	}
640
641	if (m_isMultiDrawEnabled && m_drawCount <= m_drawIndirectMaxCount)
642	{
643		switch (m_drawType)
644		{
645			case DRAW_TYPE_SEQUENTIAL:
646				m_vk.cmdDrawIndirect(*m_cmdBuffer, m_indirectBuffer->object(), m_offsetInBuffer, m_drawCount, m_strideInBuffer);
647				break;
648			case DRAW_TYPE_INDEXED:
649				m_vk.cmdDrawIndexedIndirect(*m_cmdBuffer, m_indirectBuffer->object(), m_offsetInBuffer, m_drawCount, m_strideInBuffer);
650				break;
651			default:
652				TCU_FAIL("impossible");
653		}
654	}
655	else
656	{
657		for (deUint32 drawNdx = 0; drawNdx < m_drawCount; drawNdx++)
658		{
659			switch (m_drawType)
660			{
661				case DRAW_TYPE_SEQUENTIAL:
662					m_vk.cmdDrawIndirect(*m_cmdBuffer, m_indirectBuffer->object(), m_offsetInBuffer + drawNdx*m_strideInBuffer, 1u, 0u);
663					break;
664				case DRAW_TYPE_INDEXED:
665					m_vk.cmdDrawIndexedIndirect(*m_cmdBuffer, m_indirectBuffer->object(), m_offsetInBuffer + drawNdx*m_strideInBuffer, 1u, 0u);
666					break;
667				default:
668					TCU_FAIL("impossible");
669			}
670		}
671	}
672	m_vk.cmdEndRenderPass(*m_cmdBuffer);
673	m_vk.endCommandBuffer(*m_cmdBuffer);
674
675	vk::VkSubmitInfo submitInfo =
676	{
677		vk::VK_STRUCTURE_TYPE_SUBMIT_INFO,	// VkStructureType			sType;
678		DE_NULL,							// const void*				pNext;
679		0,										// deUint32					waitSemaphoreCount;
680		DE_NULL,								// const VkSemaphore*		pWaitSemaphores;
681		(const vk::VkPipelineStageFlags*)DE_NULL,
682		1,										// deUint32					commandBufferCount;
683		&m_cmdBuffer.get(),					// const VkCommandBuffer*	pCommandBuffers;
684		0,										// deUint32					signalSemaphoreCount;
685		DE_NULL								// const VkSemaphore*		pSignalSemaphores;
686	};
687	VK_CHECK(m_vk.queueSubmit(queue, 1, &submitInfo, DE_NULL));
688
689	VK_CHECK(m_vk.queueWaitIdle(queue));
690
691	// Validation
692	VK_CHECK(m_vk.queueWaitIdle(queue));
693
694	tcu::Texture2D referenceFrame(vk::mapVkFormat(m_colorAttachmentFormat), (int)(0.5 + WIDTH), (int)(0.5 + HEIGHT));
695
696	referenceFrame.allocLevel(0);
697
698	const deInt32 frameWidth	= referenceFrame.getWidth();
699	const deInt32 frameHeight	= referenceFrame.getHeight();
700
701	tcu::clear(referenceFrame.getLevel(0), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
702
703	ReferenceImageInstancedCoordinates refInstancedCoords;
704
705	for (int y = 0; y < frameHeight; y++)
706	{
707		const float yCoord = (float)(y / (0.5*frameHeight)) - 1.0f;
708
709		for (int x = 0; x < frameWidth; x++)
710		{
711			const float xCoord = (float)(x / (0.5*frameWidth)) - 1.0f;
712
713			if ((yCoord >= refInstancedCoords.bottom	&&
714				 yCoord <= refInstancedCoords.top		&&
715				 xCoord >= refInstancedCoords.left		&&
716				 xCoord <= refInstancedCoords.right))
717				referenceFrame.getLevel(0).setPixel(tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f), x, y);
718		}
719	}
720
721	const vk::VkOffset3D zeroOffset = { 0, 0, 0 };
722	const tcu::ConstPixelBufferAccess renderedFrame = m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(),
723		vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT);
724
725	qpTestResult res = QP_TEST_RESULT_PASS;
726
727	if (!tcu::fuzzyCompare(log, "Result", "Image comparison result",
728		referenceFrame.getLevel(0), renderedFrame, 0.05f,
729		tcu::COMPARE_LOG_RESULT)) {
730		res = QP_TEST_RESULT_FAIL;
731	}
732
733	return tcu::TestStatus(res, qpGetTestResultName(res));
734
735	}
736
737}	// anonymous
738
739IndirectDrawTests::IndirectDrawTests (tcu::TestContext& testCtx)
740	: TestCaseGroup(testCtx, "indirect_draw", "indirect drawing simple geometry")
741{
742	/* Left blank on purpose */
743}
744
745IndirectDrawTests::~IndirectDrawTests (void) {}
746
747
748void IndirectDrawTests::init (void)
749{
750	for (int drawTypeIdx = 0; drawTypeIdx < DRAWTYPE_LAST; drawTypeIdx++)
751	{
752		std::string drawTypeStr;
753		switch (drawTypeIdx)
754		{
755			case DRAW_TYPE_SEQUENTIAL:
756				drawTypeStr = "sequential";
757				break;
758			case DRAW_TYPE_INDEXED:
759				drawTypeStr = "indexed";
760				break;
761			default:
762				TCU_FAIL("impossible");
763		}
764
765		tcu::TestCaseGroup* drawTypeGroup = new tcu::TestCaseGroup(m_testCtx, drawTypeStr.c_str(), ("Draws geometry using " + drawTypeStr + "draw call").c_str());
766		{
767			tcu::TestCaseGroup* indirectDrawGroup = new tcu::TestCaseGroup(m_testCtx, "indirect_draw", "Draws geometry");
768			{
769				IndirectDraw::TestSpec testSpec;
770				testSpec.drawType = static_cast<DrawType>(drawTypeIdx);
771				testSpec.shaders[glu::SHADERTYPE_VERTEX] = "vulkan/draw/VertexFetch.vert";
772				testSpec.shaders[glu::SHADERTYPE_FRAGMENT] = "vulkan/draw/VertexFetch.frag";
773				testSpec.topology = vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
774				indirectDrawGroup->addChild(new InstanceFactory<IndirectDraw>(m_testCtx, "triangle_list", "Draws triangle list", testSpec));
775				testSpec.topology = vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
776				indirectDrawGroup->addChild(new InstanceFactory<IndirectDraw>(m_testCtx, "triangle_strip", "Draws triangle strip", testSpec));
777			}
778			drawTypeGroup->addChild(indirectDrawGroup);
779
780
781			tcu::TestCaseGroup* indirectDrawInstancedGroup = new tcu::TestCaseGroup(m_testCtx, "indirect_draw_instanced", "Draws an instanced geometry");
782			{
783				tcu::TestCaseGroup*	noFirstInstanceGroup = new tcu::TestCaseGroup(m_testCtx, "no_first_instance", "Use 0 as firstInstance");
784				{
785					IndirectDrawInstanced<FirtsInstanceNotSupported>::TestSpec testSpec;
786					testSpec.drawType = static_cast<DrawType>(drawTypeIdx);
787
788					testSpec.shaders[glu::SHADERTYPE_VERTEX] = "vulkan/draw/VertexFetchInstanced.vert";
789					testSpec.shaders[glu::SHADERTYPE_FRAGMENT] = "vulkan/draw/VertexFetch.frag";
790
791					testSpec.topology = vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
792					noFirstInstanceGroup->addChild(new InstanceFactory<IndirectDrawInstanced<FirtsInstanceNotSupported> >(m_testCtx, "triangle_list", "Draws an instanced triangle list", testSpec));
793					testSpec.topology = vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
794					noFirstInstanceGroup->addChild(new InstanceFactory<IndirectDrawInstanced<FirtsInstanceNotSupported> >(m_testCtx, "triangle_strip", "Draws an instanced triangle strip", testSpec));
795				}
796				indirectDrawInstancedGroup->addChild(noFirstInstanceGroup);
797
798				tcu::TestCaseGroup*	firstInstanceGroup = new tcu::TestCaseGroup(m_testCtx, "first_instance", "Use drawIndirectFirstInstance optional feature");
799				{
800					IndirectDrawInstanced<FirtsInstanceSupported>::TestSpec testSpec;
801					testSpec.drawType = static_cast<DrawType>(drawTypeIdx);
802
803					testSpec.shaders[glu::SHADERTYPE_VERTEX] = "vulkan/draw/VertexFetchInstancedFirstInstance.vert";
804					testSpec.shaders[glu::SHADERTYPE_FRAGMENT] = "vulkan/draw/VertexFetch.frag";
805
806					testSpec.topology = vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
807					firstInstanceGroup->addChild(new InstanceFactory<IndirectDrawInstanced<FirtsInstanceSupported> >(m_testCtx, "triangle_list", "Draws an instanced triangle list", testSpec));
808					testSpec.topology = vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
809					firstInstanceGroup->addChild(new InstanceFactory<IndirectDrawInstanced<FirtsInstanceSupported> >(m_testCtx, "triangle_strip", "Draws an instanced triangle strip", testSpec));
810				}
811				indirectDrawInstancedGroup->addChild(firstInstanceGroup);
812			}
813			drawTypeGroup->addChild(indirectDrawInstancedGroup);
814		}
815
816		addChild(drawTypeGroup);
817	}
818}
819
820}	// DrawTests
821}	// vkt
822