• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2016 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief VK_KHR_shader_draw_parameters tests
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vktDrawShaderDrawParametersTests.hpp"
25 
26 #include "vktTestCaseUtil.hpp"
27 #include "vktDrawTestCaseUtil.hpp"
28 #include "vktDrawBaseClass.hpp"
29 
30 #include "vkQueryUtil.hpp"
31 
32 #include "tcuTestLog.hpp"
33 #include "tcuImageCompare.hpp"
34 #include "tcuTextureUtil.hpp"
35 
36 namespace vkt
37 {
38 namespace Draw
39 {
40 namespace
41 {
42 
43 enum TestFlagBits
44 {
45 	TEST_FLAG_INSTANCED			= 1u << 0,
46 	TEST_FLAG_INDEXED			= 1u << 1,
47 	TEST_FLAG_INDIRECT			= 1u << 2,
48 	TEST_FLAG_MULTIDRAW			= 1u << 3,	//!< multiDrawIndirect
49 	TEST_FLAG_FIRST_INSTANCE	= 1u << 4,	//!< drawIndirectFirstInstance
50 };
51 typedef deUint32 TestFlags;
52 
53 struct FlagsTestSpec : public TestSpecBase
54 {
55 	TestFlags	flags;
56 };
57 
addFlags(FlagsTestSpec spec,const TestFlags flags)58 inline FlagsTestSpec addFlags (FlagsTestSpec spec, const TestFlags flags)
59 {
60 	spec.flags |= flags;
61 	return spec;
62 }
63 
64 enum Constants
65 {
66 	// \note Data layout in buffers (junk data and good data is intertwined).
67 	//       Values are largely arbitrary, but we try to avoid "nice" numbers to make sure the test doesn't pass by accident.
68 	NUM_VERTICES			= 4,	//!< number of consecutive good vertices
69 	NDX_FIRST_VERTEX		= 2,	//!< index of first good vertex data
70 	NDX_SECOND_VERTEX		= 9,	//!< index of second good vertex data
71 	NDX_FIRST_INDEX			= 11,	//!< index of a first good index (in index data)
72 	NDX_SECOND_INDEX		= 17,	//!< index of a second good index
73 	OFFSET_FIRST_INDEX		= 1,	//!< offset added to the first index
74 	OFFSET_SECOND_INDEX		= 4,	//!< offset added to the second index
75 	MAX_INSTANCE_COUNT		= 3,	//!< max number of draw instances
76 	MAX_INDIRECT_DRAW_COUNT	= 3,	//!< max drawCount of indirect calls
77 };
78 
79 class DrawTest : public DrawTestsBaseClass
80 {
81 public:
82 	typedef FlagsTestSpec	TestSpec;
83 							DrawTest				(Context &context, TestSpec testSpec);
84 	tcu::TestStatus			iterate					(void);
85 
86 private:
87 	template<typename T, std::size_t N>
88 	void					setIndirectCommand		(const T (&pCmdData)[N]);
89 
90 	void					drawReferenceImage		(const tcu::PixelBufferAccess& refImage) const;
91 
isInstanced(void) const92 	bool					isInstanced				(void) const { return (m_flags & TEST_FLAG_INSTANCED)		!= 0; }
isIndexed(void) const93 	bool					isIndexed				(void) const { return (m_flags & TEST_FLAG_INDEXED)			!= 0; }
isIndirect(void) const94 	bool					isIndirect				(void) const { return (m_flags & TEST_FLAG_INDIRECT)		!= 0; }
isMultiDraw(void) const95 	bool					isMultiDraw				(void) const { return (m_flags & TEST_FLAG_MULTIDRAW)		!= 0; }
isFirstInstance(void) const96 	bool					isFirstInstance			(void) const { return (m_flags & TEST_FLAG_FIRST_INSTANCE)	!= 0; }
97 
98 	const TestFlags			m_flags;
99 	de::SharedPtr<Buffer>	m_indexBuffer;
100 	de::SharedPtr<Buffer>	m_indirectBuffer;
101 };
102 
DrawTest(Context & context,TestSpec testSpec)103 DrawTest::DrawTest (Context &context, TestSpec testSpec)
104 	: DrawTestsBaseClass(context, testSpec.shaders[glu::SHADERTYPE_VERTEX], testSpec.shaders[glu::SHADERTYPE_FRAGMENT], testSpec.topology)
105 	, m_flags			(testSpec.flags)
106 {
107 	DE_ASSERT(m_topology == vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
108 	DE_ASSERT(!isMultiDraw()     || isIndirect());
109 	DE_ASSERT(!isFirstInstance() || (isIndirect() && isInstanced()));
110 
111 	// Requirements
112 	{
113 		if (!de::contains(m_context.getDeviceExtensions().begin(), m_context.getDeviceExtensions().end(), std::string("VK_KHR_shader_draw_parameters")))
114 			TCU_THROW(NotSupportedError, "Missing extension: VK_KHR_shader_draw_parameters");
115 
116 		if (isMultiDraw() && !m_context.getDeviceFeatures().multiDrawIndirect)
117 			TCU_THROW(NotSupportedError, "Missing feature: multiDrawIndirect");
118 
119 		if (isFirstInstance() && !m_context.getDeviceFeatures().drawIndirectFirstInstance)
120 			TCU_THROW(NotSupportedError, "Missing feature: drawIndirectFirstInstance");
121 	}
122 
123 	// Vertex data
124 	{
125 		int refIndex = NDX_FIRST_VERTEX - OFFSET_FIRST_INDEX;
126 
127 		m_data.push_back(VertexElementData(tcu::Vec4( 1.0f, -1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1));
128 		m_data.push_back(VertexElementData(tcu::Vec4(-1.0f,  1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1));
129 
130 		if (!isIndexed())
131 			refIndex = 0;
132 
133 		m_data.push_back(VertexElementData(tcu::Vec4(-0.3f,	-0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++));
134 		m_data.push_back(VertexElementData(tcu::Vec4(-0.3f,	 0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++));
135 		m_data.push_back(VertexElementData(tcu::Vec4( 0.3f,	-0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++));
136 		m_data.push_back(VertexElementData(tcu::Vec4( 0.3f,	 0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++));
137 
138 		m_data.push_back(VertexElementData(tcu::Vec4(-1.0f,  1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1));
139 		m_data.push_back(VertexElementData(tcu::Vec4( 1.0f, -1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1));
140 		m_data.push_back(VertexElementData(tcu::Vec4(-1.0f, -1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1));
141 
142 		if (!isIndexed())
143 			refIndex = 0;
144 
145 		m_data.push_back(VertexElementData(tcu::Vec4(-0.3f,	-0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++));
146 		m_data.push_back(VertexElementData(tcu::Vec4(-0.3f,	 0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++));
147 		m_data.push_back(VertexElementData(tcu::Vec4( 0.3f,	-0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++));
148 		m_data.push_back(VertexElementData(tcu::Vec4( 0.3f,	 0.3f, 1.0f, 1.0f), tcu::Vec4(1.0f), refIndex++));
149 
150 		m_data.push_back(VertexElementData(tcu::Vec4(-1.0f,  1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1));
151 		m_data.push_back(VertexElementData(tcu::Vec4( 1.0f, -1.0f, 1.0f, 1.0f), tcu::Vec4(1.0f), -1));
152 
153 		// Make sure constants are up to date
154 		DE_ASSERT(m_data.size() == NDX_SECOND_VERTEX + NUM_VERTICES + 2);
155 		DE_ASSERT(NDX_SECOND_VERTEX - NDX_FIRST_VERTEX - NUM_VERTICES == 3);
156 	}
157 
158 	if (isIndirect())
159 	{
160 		const std::size_t	indirectBufferSize	= MAX_INDIRECT_DRAW_COUNT * 32;	// space for COUNT commands plus some gratuitous padding
161 							m_indirectBuffer	= Buffer::createAndAlloc(m_vk, m_context.getDevice(), BufferCreateInfo(indirectBufferSize, vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT),
162 												  m_context.getDefaultAllocator(), vk::MemoryRequirement::HostVisible);
163 
164 		deMemset(m_indirectBuffer->getBoundMemory().getHostPtr(), 0, indirectBufferSize);
165 		vk::flushMappedMemoryRange(m_vk, m_context.getDevice(), m_indirectBuffer->getBoundMemory().getMemory(), m_indirectBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE);
166 	}
167 
168 	if (isIndexed())
169 	{
170 		DE_ASSERT(NDX_FIRST_INDEX + NUM_VERTICES <= NDX_SECOND_INDEX);
171 		const std::size_t	indexBufferSize	= sizeof(deUint32) * (NDX_SECOND_INDEX + NUM_VERTICES);
172 							m_indexBuffer	= Buffer::createAndAlloc(m_vk, m_context.getDevice(), BufferCreateInfo(indexBufferSize, vk::VK_BUFFER_USAGE_INDEX_BUFFER_BIT),
173 																	 m_context.getDefaultAllocator(), vk::MemoryRequirement::HostVisible);
174 		deUint32*			indices			= static_cast<deUint32*>(m_indexBuffer->getBoundMemory().getHostPtr());
175 
176 		deMemset(indices, 0, indexBufferSize);
177 
178 		for (int i = 0; i < NUM_VERTICES; i++)
179 		{
180 			indices[NDX_FIRST_INDEX  + i] = static_cast<deUint32>(NDX_FIRST_VERTEX  + i) - OFFSET_FIRST_INDEX;
181 			indices[NDX_SECOND_INDEX + i] = static_cast<deUint32>(NDX_SECOND_VERTEX + i) - OFFSET_SECOND_INDEX;
182 		}
183 
184 		vk::flushMappedMemoryRange(m_vk, m_context.getDevice(), m_indexBuffer->getBoundMemory().getMemory(), m_indexBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE);
185 	}
186 
187 	initialize();
188 }
189 
190 template<typename T, std::size_t N>
setIndirectCommand(const T (& pCmdData)[N])191 void DrawTest::setIndirectCommand (const T (&pCmdData)[N])
192 {
193 	DE_ASSERT(N != 0 && N <= MAX_INDIRECT_DRAW_COUNT);
194 
195 	const std::size_t dataSize = N * sizeof(T);
196 
197 	deMemcpy(m_indirectBuffer->getBoundMemory().getHostPtr(), pCmdData, dataSize);
198 	vk::flushMappedMemoryRange(m_vk, m_context.getDevice(), m_indirectBuffer->getBoundMemory().getMemory(), m_indirectBuffer->getBoundMemory().getOffset(), VK_WHOLE_SIZE);
199 }
200 
201 //! This function must be kept in sync with the shader.
drawReferenceImage(const tcu::PixelBufferAccess & refImage) const202 void DrawTest::drawReferenceImage (const tcu::PixelBufferAccess& refImage) const
203 {
204 	using tcu::Vec2;
205 	using tcu::Vec4;
206 	using tcu::IVec4;
207 
208 	const Vec2	perInstanceOffset[]	= { Vec2(0.0f, 0.0f), Vec2(-0.3f,  0.0f), Vec2(0.0f, 0.3f) };
209 	const Vec2	perDrawOffset[]		= { Vec2(0.0f, 0.0f), Vec2(-0.3f, -0.3f), Vec2(0.3f, 0.3f) };
210 	const Vec4	allColors[]			= { Vec4(1.0f), Vec4(0.0f, 0.0f, 1.0f, 1.0f), Vec4(0.0f, 1.0f, 0.0f, 1.0f) };
211 	const int	numInstances		= isInstanced() ? MAX_INSTANCE_COUNT		: 1;
212 	const int	numIndirectDraws	= isMultiDraw() ? MAX_INDIRECT_DRAW_COUNT	: 1;
213 	const int	rectWidth			= static_cast<int>(WIDTH  * 0.6f / 2.0f);
214 	const int	rectHeight			= static_cast<int>(HEIGHT * 0.6f / 2.0f);
215 
216 	DE_ASSERT(DE_LENGTH_OF_ARRAY(perInstanceOffset) >= numInstances);
217 	DE_ASSERT(DE_LENGTH_OF_ARRAY(allColors) >= numInstances && DE_LENGTH_OF_ARRAY(allColors) >= numIndirectDraws);
218 	DE_ASSERT(DE_LENGTH_OF_ARRAY(perDrawOffset) >= numIndirectDraws);
219 
220 	tcu::clear(refImage, tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
221 
222 	for (int drawNdx     = 0; drawNdx     < numIndirectDraws; ++drawNdx)
223 	for (int instanceNdx = 0; instanceNdx < numInstances;     ++instanceNdx)
224 	{
225 		const Vec2	offset	= perInstanceOffset[instanceNdx] + perDrawOffset[drawNdx];
226 		const Vec4&	color	= allColors[isMultiDraw() ? drawNdx : instanceNdx];
227 		int			x		= static_cast<int>(WIDTH  * (1.0f - 0.3f + offset.x()) / 2.0f);
228 		int			y		= static_cast<int>(HEIGHT * (1.0f - 0.3f + offset.y()) / 2.0f);
229 
230 		tcu::clear(tcu::getSubregion(refImage, x, y, rectWidth, rectHeight), color);
231 	}
232 }
233 
iterate(void)234 tcu::TestStatus DrawTest::iterate (void)
235 {
236 	// Draw
237 	{
238 		beginRenderPass();
239 
240 		const vk::VkDeviceSize	vertexBufferOffset	= 0;
241 		const vk::VkBuffer		vertexBuffer		= m_vertexBuffer->object();
242 
243 		m_vk.cmdBindVertexBuffers	(*m_cmdBuffer, 0, 1, &vertexBuffer, &vertexBufferOffset);
244 		m_vk.cmdBindPipeline		(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
245 
246 		if (isIndexed())
247 			m_vk.cmdBindIndexBuffer(*m_cmdBuffer, m_indexBuffer->object(), 0ull, vk::VK_INDEX_TYPE_UINT32);
248 
249 		const deUint32			numInstances		= isInstanced() ? MAX_INSTANCE_COUNT : 1;
250 
251 		if (isIndirect())
252 		{
253 			if (isIndexed())
254 			{
255 				const vk::VkDrawIndexedIndirectCommand commands[] =
256 				{
257 					// indexCount, instanceCount, firstIndex, vertexOffset, firstInstance
258 					{ NUM_VERTICES,	numInstances,	NDX_FIRST_INDEX,	OFFSET_FIRST_INDEX,		(isFirstInstance() ? 2u : 0u) },
259 					{ NUM_VERTICES,	numInstances,	NDX_SECOND_INDEX,	OFFSET_SECOND_INDEX,	(isFirstInstance() ? 1u : 0u) },
260 					{ NUM_VERTICES,	numInstances,	NDX_FIRST_INDEX,	OFFSET_FIRST_INDEX,		(isFirstInstance() ? 3u : 0u) },
261 				};
262 				setIndirectCommand(commands);
263 			}
264 			else
265 			{
266 				const vk::VkDrawIndirectCommand commands[] =
267 				{
268 					// vertexCount, instanceCount, firstVertex, firstInstance
269 					{ NUM_VERTICES,	numInstances,	NDX_FIRST_VERTEX,	(isFirstInstance() ? 2u : 0u) },
270 					{ NUM_VERTICES,	numInstances,	NDX_SECOND_VERTEX,	(isFirstInstance() ? 1u : 0u) },
271 					{ NUM_VERTICES,	numInstances,	NDX_FIRST_VERTEX,	(isFirstInstance() ? 3u : 0u) },
272 				};
273 				setIndirectCommand(commands);
274 			}
275 		}
276 
277 		if (isIndirect())
278 		{
279 			const deUint32 numIndirectDraws = isMultiDraw() ? MAX_INDIRECT_DRAW_COUNT : 1;
280 
281 			if (isIndexed())
282 				m_vk.cmdDrawIndexedIndirect(*m_cmdBuffer, m_indirectBuffer->object(), 0ull, numIndirectDraws, sizeof(vk::VkDrawIndexedIndirectCommand));
283 			else
284 				m_vk.cmdDrawIndirect(*m_cmdBuffer, m_indirectBuffer->object(), 0ull, numIndirectDraws, sizeof(vk::VkDrawIndirectCommand));
285 		}
286 		else
287 		{
288 			const deUint32 firstInstance = 2;
289 
290 			if (isIndexed())
291 				m_vk.cmdDrawIndexed(*m_cmdBuffer, NUM_VERTICES, numInstances, NDX_FIRST_INDEX, OFFSET_FIRST_INDEX, firstInstance);
292 			else
293 				m_vk.cmdDraw(*m_cmdBuffer, NUM_VERTICES, numInstances, NDX_FIRST_VERTEX, firstInstance);
294 		}
295 
296 		m_vk.cmdEndRenderPass(*m_cmdBuffer);
297 		m_vk.endCommandBuffer(*m_cmdBuffer);
298 	}
299 
300 	// Submit
301 	{
302 		const vk::VkQueue		queue		= m_context.getUniversalQueue();
303 		const vk::VkSubmitInfo	submitInfo	=
304 		{
305 			vk::VK_STRUCTURE_TYPE_SUBMIT_INFO,			// VkStructureType			sType;
306 			DE_NULL,									// const void*				pNext;
307 			0,											// deUint32					waitSemaphoreCount;
308 			DE_NULL,									// const VkSemaphore*		pWaitSemaphores;
309 			(const vk::VkPipelineStageFlags*)DE_NULL,
310 			1,											// deUint32					commandBufferCount;
311 			&m_cmdBuffer.get(),							// const VkCommandBuffer*	pCommandBuffers;
312 			0,											// deUint32					signalSemaphoreCount;
313 			DE_NULL										// const VkSemaphore*		pSignalSemaphores;
314 		};
315 		VK_CHECK(m_vk.queueSubmit(queue, 1, &submitInfo, DE_NULL));
316 		VK_CHECK(m_vk.queueWaitIdle(queue));
317 	}
318 
319 	// Validate
320 	{
321 		tcu::TextureLevel referenceFrame(vk::mapVkFormat(m_colorAttachmentFormat), static_cast<int>(0.5f + WIDTH), static_cast<int>(0.5f + HEIGHT));
322 
323 		drawReferenceImage(referenceFrame.getAccess());
324 
325 		const vk::VkOffset3D				zeroOffset		= { 0, 0, 0 };
326 		const tcu::ConstPixelBufferAccess	renderedFrame	= m_colorTargetImage->readSurface(m_context.getUniversalQueue(), m_context.getDefaultAllocator(),
327 															  vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT);
328 
329 		if (!tcu::fuzzyCompare(m_context.getTestContext().getLog(), "Result", "Image comparison result", referenceFrame.getAccess(), renderedFrame, 0.05f, tcu::COMPARE_LOG_RESULT))
330 			return tcu::TestStatus::fail("Rendered image is incorrect");
331 		else
332 			return tcu::TestStatus::pass("OK");
333 	}
334 }
335 
addDrawCase(tcu::TestCaseGroup * group,const DrawTest::TestSpec testSpec,const TestFlags flags)336 void addDrawCase (tcu::TestCaseGroup* group, const DrawTest::TestSpec testSpec, const TestFlags flags)
337 {
338 	std::ostringstream name;
339 	name << "draw";
340 
341 	if (flags & TEST_FLAG_INDEXED)			name << "_indexed";
342 	if (flags & TEST_FLAG_INDIRECT)			name << "_indirect";
343 	if (flags & TEST_FLAG_INSTANCED)		name << "_instanced";
344 	if (flags & TEST_FLAG_FIRST_INSTANCE)	name << "_first_instance";
345 
346 	group->addChild(new InstanceFactory<DrawTest>(group->getTestContext(), name.str(), "", addFlags(testSpec, flags)));
347 }
348 
349 }	// anonymous
350 
ShaderDrawParametersTests(tcu::TestContext & testCtx)351 ShaderDrawParametersTests::ShaderDrawParametersTests (tcu::TestContext &testCtx)
352 	: TestCaseGroup	(testCtx, "shader_draw_parameters", "VK_KHR_shader_draw_parameters")
353 {
354 }
355 
init(void)356 void ShaderDrawParametersTests::init (void)
357 {
358 	{
359 		DrawTest::TestSpec testSpec;
360 		testSpec.shaders[glu::SHADERTYPE_VERTEX]	= "vulkan/draw/VertexFetchShaderDrawParameters.vert";
361 		testSpec.shaders[glu::SHADERTYPE_FRAGMENT]	= "vulkan/draw/VertexFetch.frag";
362 		testSpec.topology							= vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
363 		testSpec.flags								= 0;
364 
365 		de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(getTestContext(), "base_vertex", ""));
366 		addDrawCase(group.get(), testSpec, 0);
367 		addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED);
368 		addDrawCase(group.get(), testSpec, TEST_FLAG_INDIRECT);
369 		addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED | TEST_FLAG_INDIRECT);
370 		addChild(group.release());
371 	}
372 	{
373 		DrawTest::TestSpec testSpec;
374 		testSpec.shaders[glu::SHADERTYPE_VERTEX]	= "vulkan/draw/VertexFetchShaderDrawParameters.vert";
375 		testSpec.shaders[glu::SHADERTYPE_FRAGMENT]	= "vulkan/draw/VertexFetch.frag";
376 		testSpec.topology							= vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
377 		testSpec.flags								= TEST_FLAG_INSTANCED;
378 
379 		de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(getTestContext(), "base_instance", ""));
380 		addDrawCase(group.get(), testSpec, 0);
381 		addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED);
382 		addDrawCase(group.get(), testSpec, TEST_FLAG_INDIRECT);
383 		addDrawCase(group.get(), testSpec, TEST_FLAG_INDIRECT | TEST_FLAG_FIRST_INSTANCE);
384 		addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED  | TEST_FLAG_INDIRECT);
385 		addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED  | TEST_FLAG_INDIRECT | TEST_FLAG_FIRST_INSTANCE);
386 		addChild(group.release());
387 	}
388 	{
389 		DrawTest::TestSpec testSpec;
390 		testSpec.shaders[glu::SHADERTYPE_VERTEX]	= "vulkan/draw/VertexFetchShaderDrawParametersDrawIndex.vert";
391 		testSpec.shaders[glu::SHADERTYPE_FRAGMENT]	= "vulkan/draw/VertexFetch.frag";
392 		testSpec.topology							= vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
393 		testSpec.flags								= TEST_FLAG_INDIRECT | TEST_FLAG_MULTIDRAW;
394 
395 		de::MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(getTestContext(), "draw_index", ""));
396 		addDrawCase(group.get(), testSpec, 0);
397 		addDrawCase(group.get(), testSpec, TEST_FLAG_INSTANCED);
398 		addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED);
399 		addDrawCase(group.get(), testSpec, TEST_FLAG_INDEXED | TEST_FLAG_INSTANCED);
400 		addChild(group.release());
401 	}
402 }
403 
404 }	// DrawTests
405 }	// vkt
406