• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <climits>
28 
29 #include "deSharedPtr.hpp"
30 #include "rrRenderer.hpp"
31 #include "tcuImageCompare.hpp"
32 #include "tcuRGBA.hpp"
33 #include "tcuTextureUtil.hpp"
34 #include "vkImageUtil.hpp"
35 #include "vkPrograms.hpp"
36 #include "vkCmdUtil.hpp"
37 #include "vkTypeUtil.hpp"
38 #include "vkQueryUtil.hpp"
39 #include "vktDrawBufferObjectUtil.hpp"
40 #include "vktDrawCreateInfoUtil.hpp"
41 #include "vktDrawImageObjectUtil.hpp"
42 #include "vktDrawTestCaseUtil.hpp"
43 
44 namespace vkt
45 {
46 namespace Draw
47 {
48 namespace
49 {
50 
51 static const int	QUAD_GRID_SIZE	= 8;
52 static const int	WIDTH			= 128;
53 static const int	HEIGHT			= 128;
54 
55 struct TestParams
56 {
57 	enum DrawFunction
58 	{
59 		FUNCTION_DRAW = 0,
60 		FUNCTION_DRAW_INDEXED,
61 		FUNCTION_DRAW_INDIRECT,
62 		FUNCTION_DRAW_INDEXED_INDIRECT,
63 
64 		FUNTION_LAST
65 	};
66 
67 	DrawFunction			function;
68 	vk::VkPrimitiveTopology	topology;
69 	deBool					useDynamicRendering;
70 
71 	deBool					testAttribDivisor;
72 	deUint32				attribDivisor;
73 
74 	deBool					testMultiview;
75 };
76 
77 struct VertexPositionAndColor
78 {
VertexPositionAndColorvkt::Draw::__anon9f8fb5ea0111::VertexPositionAndColor79 				VertexPositionAndColor (tcu::Vec4 position_, tcu::Vec4 color_)
80 					: position	(position_)
81 					, color		(color_)
82 				{
83 				}
84 
85 	tcu::Vec4	position;
86 	tcu::Vec4	color;
87 };
88 
operator <<(std::ostream & str,TestParams const & v)89 std::ostream & operator<<(std::ostream & str, TestParams const & v)
90 {
91 	std::ostringstream string;
92 	switch (v.function)
93 	{
94 		case TestParams::FUNCTION_DRAW:
95 			string << "draw";
96 			break;
97 		case TestParams::FUNCTION_DRAW_INDEXED:
98 			string << "draw_indexed";
99 			break;
100 		case TestParams::FUNCTION_DRAW_INDIRECT:
101 			string << "draw_indirect";
102 			break;
103 		case TestParams::FUNCTION_DRAW_INDEXED_INDIRECT:
104 			string << "draw_indexed_indirect";
105 			break;
106 		default:
107 			DE_ASSERT(false);
108 	}
109 
110 	string << "_" << de::toString(v.topology);
111 
112 	if (v.testAttribDivisor)
113 		string << "_attrib_divisor_" << v.attribDivisor;
114 
115 	if (v.testMultiview)
116 		string << "_multiview";
117 
118 	return str << string.str();
119 }
120 
mapVkPrimitiveTopology(vk::VkPrimitiveTopology primitiveTopology)121 rr::PrimitiveType mapVkPrimitiveTopology (vk::VkPrimitiveTopology primitiveTopology)
122 {
123 	switch (primitiveTopology)
124 	{
125 		case vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST:						return rr::PRIMITIVETYPE_POINTS;
126 		case vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST:						return rr::PRIMITIVETYPE_LINES;
127 		case vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:						return rr::PRIMITIVETYPE_LINE_STRIP;
128 		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:					return rr::PRIMITIVETYPE_TRIANGLES;
129 		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:					return rr::PRIMITIVETYPE_TRIANGLE_FAN;
130 		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:					return rr::PRIMITIVETYPE_TRIANGLE_STRIP;
131 		case vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY:		return rr::PRIMITIVETYPE_LINES_ADJACENCY;
132 		case vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY:		return rr::PRIMITIVETYPE_LINE_STRIP_ADJACENCY;
133 		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY:	return rr::PRIMITIVETYPE_TRIANGLES_ADJACENCY;
134 		case vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY:	return rr::PRIMITIVETYPE_TRIANGLE_STRIP_ADJACENCY;
135 		default:
136 			DE_ASSERT(false);
137 	}
138 	return rr::PRIMITIVETYPE_LAST;
139 }
140 
141 template<typename T>
createAndUploadBuffer(const std::vector<T> data,const vk::DeviceInterface & vk,const Context & context,vk::VkBufferUsageFlags usage)142 de::SharedPtr<Buffer> createAndUploadBuffer(const std::vector<T> data, const vk::DeviceInterface& vk, const Context& context, vk::VkBufferUsageFlags usage)
143 {
144 	const vk::VkDeviceSize dataSize = data.size() * sizeof(T);
145 	de::SharedPtr<Buffer> buffer = Buffer::createAndAlloc(vk, context.getDevice(),
146 														  BufferCreateInfo(dataSize, usage),
147 														  context.getDefaultAllocator(),
148 														  vk::MemoryRequirement::HostVisible);
149 
150 	deUint8* ptr = reinterpret_cast<deUint8*>(buffer->getBoundMemory().getHostPtr());
151 
152 	deMemcpy(ptr, &data[0], static_cast<size_t>(dataSize));
153 
154 	vk::flushAlloc(vk, context.getDevice(), buffer->getBoundMemory());
155 	return buffer;
156 }
157 
158 class TestVertShader : public rr::VertexShader
159 {
160 public:
TestVertShader(int numInstances,int firstInstance)161 	TestVertShader (int numInstances, int firstInstance)
162 		: rr::VertexShader	(3, 1)
163 		, m_numInstances	(numInstances)
164 		, m_firstInstance	(firstInstance)
165 	{
166 		m_inputs[0].type	= rr::GENERICVECTYPE_FLOAT;
167 		m_inputs[1].type	= rr::GENERICVECTYPE_FLOAT;
168 		m_inputs[2].type	= rr::GENERICVECTYPE_FLOAT;
169 		m_outputs[0].type	= rr::GENERICVECTYPE_FLOAT;
170 	}
171 
shadeVertices(const rr::VertexAttrib * inputs,rr::VertexPacket * const * packets,const int numPackets) const172 	void shadeVertices (const rr::VertexAttrib* inputs,
173 						rr::VertexPacket* const* packets,
174 						const int numPackets) const
175 	{
176 		for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
177 		{
178 			const int		instanceNdx		= packets[packetNdx]->instanceNdx + m_firstInstance;
179 			const tcu::Vec4	position		= rr::readVertexAttribFloat(inputs[0], packets[packetNdx]->instanceNdx,	packets[packetNdx]->vertexNdx, m_firstInstance);
180 			const tcu::Vec4	color			= rr::readVertexAttribFloat(inputs[1], packets[packetNdx]->instanceNdx,	packets[packetNdx]->vertexNdx, m_firstInstance);
181 			const tcu::Vec4	color2			= rr::readVertexAttribFloat(inputs[2], packets[packetNdx]->instanceNdx, packets[packetNdx]->vertexNdx, m_firstInstance);
182 			packets[packetNdx]->position	= position + tcu::Vec4((float)(packets[packetNdx]->instanceNdx * 2.0 / m_numInstances), 0.0, 0.0, 0.0);
183 			packets[packetNdx]->outputs[0]	= color + tcu::Vec4((float)instanceNdx / (float)m_numInstances, 0.0, 0.0, 1.0) + color2;
184 		}
185 	}
186 
187 private:
188 	const int m_numInstances;
189 	const int m_firstInstance;
190 };
191 
192 class TestFragShader : public rr::FragmentShader
193 {
194 public:
TestFragShader(void)195 	TestFragShader (void)
196 		: rr::FragmentShader(1, 1)
197 	{
198 		m_inputs[0].type	= rr::GENERICVECTYPE_FLOAT;
199 		m_outputs[0].type	= rr::GENERICVECTYPE_FLOAT;
200 	}
201 
shadeFragments(rr::FragmentPacket * packets,const int numPackets,const rr::FragmentShadingContext & context) const202 	void shadeFragments (rr::FragmentPacket* packets,
203 						 const int numPackets,
204 						 const rr::FragmentShadingContext& context) const
205 	{
206 		for (int packetNdx = 0; packetNdx < numPackets; ++packetNdx)
207 		{
208 			rr::FragmentPacket& packet = packets[packetNdx];
209 			for (int fragNdx = 0; fragNdx < rr::NUM_FRAGMENTS_PER_PACKET; ++fragNdx)
210 			{
211 				const tcu::Vec4 color = rr::readVarying<float>(packet, context, 0, fragNdx);
212 				rr::writeFragmentOutput(context, packetNdx, fragNdx, 0, color);
213 			}
214 		}
215 	}
216 };
217 
218 class InstancedDrawInstance : public TestInstance
219 {
220 public:
221 												InstancedDrawInstance	(Context& context, TestParams params);
222 	virtual	tcu::TestStatus						iterate					(void);
223 
224 private:
225 	void										prepareVertexData		(int instanceCount, int firstInstance, int instanceDivisor);
226 
227 	const TestParams							m_params;
228 	const vk::DeviceInterface&					m_vk;
229 
230 	vk::VkFormat								m_colorAttachmentFormat;
231 
232 	vk::Move<vk::VkPipeline>					m_pipeline;
233 	vk::Move<vk::VkPipelineLayout>				m_pipelineLayout;
234 
235 	de::SharedPtr<Image>						m_colorTargetImage;
236 	vk::Move<vk::VkImageView>					m_colorTargetView;
237 
238 	PipelineCreateInfo::VertexInputState		m_vertexInputState;
239 
240 	vk::Move<vk::VkCommandPool>					m_cmdPool;
241 	vk::Move<vk::VkCommandBuffer>				m_cmdBuffer;
242 
243 	vk::Move<vk::VkFramebuffer>					m_framebuffer;
244 	vk::Move<vk::VkRenderPass>					m_renderPass;
245 
246 	// Vertex data
247 	std::vector<VertexPositionAndColor>			m_data;
248 	std::vector<deUint32>						m_indexes;
249 	std::vector<tcu::Vec4>						m_instancedColor;
250 };
251 
252 class InstancedDrawCase : public TestCase
253 {
254 public:
InstancedDrawCase(tcu::TestContext & testCtx,const std::string & name,const std::string & desc,TestParams params)255 	InstancedDrawCase (tcu::TestContext&	testCtx,
256 					   const std::string&	name,
257 					   const std::string&	desc,
258 					   TestParams			params)
259 		: TestCase	(testCtx, name, desc)
260 		, m_params	(params)
261 	{
262 		m_vertexShader = "#version 430\n"
263 				"layout(location = 0) in vec4 in_position;\n"
264 				"layout(location = 1) in vec4 in_color;\n"
265 				"layout(location = 2) in vec4 in_color_2;\n"
266 				"layout(push_constant) uniform TestParams {\n"
267 				"	float firstInstance;\n"
268 				"	float instanceCount;\n"
269 				"} params;\n"
270 				"layout(location = 0) out vec4 out_color;\n"
271 				"out gl_PerVertex {\n"
272 				"    vec4  gl_Position;\n"
273 				"    float gl_PointSize;\n"
274 				"};\n"
275 				"void main() {\n"
276 				"    gl_PointSize = 1.0;\n"
277 				"    gl_Position  = in_position + vec4(float(gl_InstanceIndex - params.firstInstance) * 2.0 / params.instanceCount, 0.0, 0.0, 0.0);\n"
278 				"    out_color    = in_color + vec4(float(gl_InstanceIndex) / params.instanceCount, 0.0, 0.0, 1.0) + in_color_2;\n"
279 				"}\n";
280 
281 		m_fragmentShader = "#version 430\n"
282 				"layout(location = 0) in vec4 in_color;\n"
283 				"layout(location = 0) out vec4 out_color;\n"
284 				"void main()\n"
285 				"{\n"
286 				"    out_color = in_color;\n"
287 				"}\n";
288 	}
289 
checkSupport(Context & context) const290 	virtual void	checkSupport	(Context& context) const
291 	{
292 		if (m_params.testAttribDivisor)
293 		{
294 			context.requireDeviceFunctionality("VK_EXT_vertex_attribute_divisor");
295 
296 			const vk::VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT& vertexAttributeDivisorFeatures = context.getVertexAttributeDivisorFeaturesEXT();
297 
298 			if (m_params.attribDivisor != 1 && !vertexAttributeDivisorFeatures.vertexAttributeInstanceRateDivisor)
299 				TCU_THROW(NotSupportedError, "Implementation does not support vertexAttributeInstanceRateDivisor");
300 
301 			if (m_params.attribDivisor == 0 && !vertexAttributeDivisorFeatures.vertexAttributeInstanceRateZeroDivisor)
302 				TCU_THROW(NotSupportedError, "Implementation does not support vertexAttributeInstanceRateDivisorZero");
303 
304 			if (m_params.testMultiview)
305 			{
306 				context.requireDeviceFunctionality("VK_KHR_multiview");
307 
308 				const vk::VkPhysicalDeviceMultiviewFeatures& multiviewFeatures = context.getMultiviewFeatures();
309 
310 				if (!multiviewFeatures.multiview)
311 					TCU_THROW(NotSupportedError, "Implementation does not support multiview feature");
312 			}
313 		}
314 
315 		if (m_params.useDynamicRendering)
316 			context.requireDeviceFunctionality("VK_KHR_dynamic_rendering");
317 
318 		if (m_params.topology == vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN &&
319 			context.isDeviceFunctionalitySupported("VK_KHR_portability_subset") &&
320 			!context.getPortabilitySubsetFeatures().triangleFans)
321 		{
322 			TCU_THROW(NotSupportedError, "VK_KHR_portability_subset: Triangle fans are not supported by this implementation");
323 		}
324 	}
325 
createInstance(Context & context) const326 	TestInstance*	createInstance	(Context& context) const
327 	{
328 		return new InstancedDrawInstance(context, m_params);
329 	}
330 
initPrograms(vk::SourceCollections & programCollection) const331 	virtual void	initPrograms	(vk::SourceCollections& programCollection) const
332 	{
333 		programCollection.glslSources.add("InstancedDrawVert") << glu::VertexSource(m_vertexShader);
334 		programCollection.glslSources.add("InstancedDrawFrag") << glu::FragmentSource(m_fragmentShader);
335 	}
336 
337 private:
338 	const TestParams	m_params;
339 	std::string			m_vertexShader;
340 	std::string			m_fragmentShader;
341 };
342 
InstancedDrawInstance(Context & context,TestParams params)343 InstancedDrawInstance::InstancedDrawInstance(Context &context, TestParams params)
344 	: TestInstance				(context)
345 	, m_params					(params)
346 	, m_vk						(context.getDeviceInterface())
347 	, m_colorAttachmentFormat	(vk::VK_FORMAT_R8G8B8A8_UNORM)
348 {
349 	const vk::VkDevice device				= m_context.getDevice();
350 	const deUint32 queueFamilyIndex			= m_context.getUniversalQueueFamilyIndex();
351 
352 	const vk::VkPushConstantRange pushConstantRange = {
353 		vk::VK_SHADER_STAGE_VERTEX_BIT,				// VkShaderStageFlags    stageFlags;
354 		0u,											// uint32_t              offset;
355 		(deUint32)sizeof(float) * 2,				// uint32_t              size;
356 	};
357 
358 	const PipelineLayoutCreateInfo pipelineLayoutCreateInfo(0, DE_NULL, 1, &pushConstantRange);
359 	m_pipelineLayout						= vk::createPipelineLayout(m_vk, device, &pipelineLayoutCreateInfo);
360 
361 	deUint32 arrayLayers = m_params.testMultiview ? 2 : 1;
362 	const vk::VkExtent3D targetImageExtent	= { WIDTH, HEIGHT, 1 };
363 	const ImageCreateInfo targetImageCreateInfo(vk::VK_IMAGE_TYPE_2D, m_colorAttachmentFormat, targetImageExtent, 1, arrayLayers, vk::VK_SAMPLE_COUNT_1_BIT,
364 		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);
365 
366 	m_colorTargetImage						= Image::createAndAlloc(m_vk, device, targetImageCreateInfo, m_context.getDefaultAllocator(), m_context.getUniversalQueueFamilyIndex());
367 
368 	const enum vk::VkImageViewType imageViewType = m_params.testMultiview ? vk::VK_IMAGE_VIEW_TYPE_2D_ARRAY : vk::VK_IMAGE_VIEW_TYPE_2D;
369 	ImageSubresourceRange subresourceRange = ImageSubresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT);
370 
371 	if (m_params.testMultiview)
372 		subresourceRange.layerCount = 2;
373 
374 	const ImageViewCreateInfo colorTargetViewInfo(m_colorTargetImage->object(), imageViewType, m_colorAttachmentFormat, subresourceRange);
375 	m_colorTargetView						= vk::createImageView(m_vk, device, &colorTargetViewInfo);
376 
377 	if (!m_params.useDynamicRendering)
378 	{
379 		RenderPassCreateInfo renderPassCreateInfo;
380 		renderPassCreateInfo.addAttachment(AttachmentDescription(m_colorAttachmentFormat,
381 																 vk::VK_SAMPLE_COUNT_1_BIT,
382 																 vk::VK_ATTACHMENT_LOAD_OP_LOAD,
383 																 vk::VK_ATTACHMENT_STORE_OP_STORE,
384 																 vk::VK_ATTACHMENT_LOAD_OP_DONT_CARE,
385 																 vk::VK_ATTACHMENT_STORE_OP_STORE,
386 																 vk::VK_IMAGE_LAYOUT_GENERAL,
387 																 vk::VK_IMAGE_LAYOUT_GENERAL));
388 
389 		const vk::VkAttachmentReference colorAttachmentReference =
390 		{
391 			0,
392 			vk::VK_IMAGE_LAYOUT_GENERAL
393 		};
394 
395 		renderPassCreateInfo.addSubpass(SubpassDescription(vk::VK_PIPELINE_BIND_POINT_GRAPHICS,
396 														   0,
397 														   0,
398 														   DE_NULL,
399 														   1,
400 														   &colorAttachmentReference,
401 														   DE_NULL,
402 														   AttachmentReference(),
403 														   0,
404 														   DE_NULL));
405 
406 		vk::VkRenderPassMultiviewCreateInfo renderPassMultiviewCreateInfo;
407 		// Bit mask that specifies which view rendering is broadcast to
408 		// 0011 = Broadcast to first and second view (layer)
409 		const deUint32 viewMask = 0x3;
410 		// Bit mask that specifices correlation between views
411 		// An implementation may use this for optimizations (concurrent render)
412 		const deUint32 correlationMask = 0x3;
413 
414 		if (m_params.testMultiview)
415 		{
416 			DE_ASSERT(renderPassCreateInfo.subpassCount == 1);
417 
418 			renderPassMultiviewCreateInfo.sType = vk::VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO;
419 			renderPassMultiviewCreateInfo.pNext = DE_NULL;
420 			renderPassMultiviewCreateInfo.subpassCount = renderPassCreateInfo.subpassCount;
421 			renderPassMultiviewCreateInfo.pViewMasks = &viewMask;
422 			renderPassMultiviewCreateInfo.correlationMaskCount = 1u;
423 			renderPassMultiviewCreateInfo.pCorrelationMasks = &correlationMask;
424 			renderPassMultiviewCreateInfo.pViewOffsets = DE_NULL;
425 			renderPassMultiviewCreateInfo.dependencyCount = 0u;
426 
427 			renderPassCreateInfo.pNext = &renderPassMultiviewCreateInfo;
428 		}
429 
430 		m_renderPass = vk::createRenderPass(m_vk, device, &renderPassCreateInfo);
431 
432 		// create framebuffer
433 		std::vector<vk::VkImageView>	colorAttachments { *m_colorTargetView };
434 		const FramebufferCreateInfo		framebufferCreateInfo(*m_renderPass, colorAttachments, WIDTH, HEIGHT, 1);
435 		m_framebuffer = vk::createFramebuffer(m_vk, device, &framebufferCreateInfo);
436 	}
437 
438 	const vk::VkVertexInputBindingDescription vertexInputBindingDescription[2] =
439 	{
440 		{
441 			0u,
442 			(deUint32)sizeof(VertexPositionAndColor),
443 			vk::VK_VERTEX_INPUT_RATE_VERTEX,
444 		},
445 		{
446 			1u,
447 			(deUint32)sizeof(tcu::Vec4),
448 			vk::VK_VERTEX_INPUT_RATE_INSTANCE,
449 		},
450 	};
451 
452 	const vk::VkVertexInputAttributeDescription vertexInputAttributeDescriptions[] =
453 	{
454 		{
455 			0u,
456 			0u,
457 			vk::VK_FORMAT_R32G32B32A32_SFLOAT,
458 			0u
459 		},
460 		{
461 			1u,
462 			0u,
463 			vk::VK_FORMAT_R32G32B32A32_SFLOAT,
464 			(deUint32)sizeof(tcu::Vec4),
465 		},
466 		{
467 			2u,
468 			1u,
469 			vk::VK_FORMAT_R32G32B32A32_SFLOAT,
470 			0,
471 		}
472 	};
473 
474 	m_vertexInputState = PipelineCreateInfo::VertexInputState(2,
475 															  vertexInputBindingDescription,
476 															  DE_LENGTH_OF_ARRAY(vertexInputAttributeDescriptions),
477 															  vertexInputAttributeDescriptions);
478 
479 	const vk::VkVertexInputBindingDivisorDescriptionEXT vertexInputBindingDivisorDescription =
480 	{
481 		1u,
482 		m_params.attribDivisor,
483 	};
484 	if (m_params.testAttribDivisor)
485 		m_vertexInputState.addDivisors(1, &vertexInputBindingDivisorDescription);
486 
487 	const CmdPoolCreateInfo cmdPoolCreateInfo(queueFamilyIndex);
488 	m_cmdPool = vk::createCommandPool(m_vk, device, &cmdPoolCreateInfo);
489 
490 	m_cmdBuffer = vk::allocateCommandBuffer(m_vk, device, *m_cmdPool, vk::VK_COMMAND_BUFFER_LEVEL_PRIMARY);
491 
492 	const vk::Unique<vk::VkShaderModule> vs(createShaderModule(m_vk, device, m_context.getBinaryCollection().get("InstancedDrawVert"), 0));
493 	const vk::Unique<vk::VkShaderModule> fs(createShaderModule(m_vk, device, m_context.getBinaryCollection().get("InstancedDrawFrag"), 0));
494 
495 	const PipelineCreateInfo::ColorBlendState::Attachment vkCbAttachmentState;
496 
497 	vk::VkViewport	viewport	= vk::makeViewport(WIDTH, HEIGHT);
498 	vk::VkRect2D	scissor		= vk::makeRect2D(WIDTH, HEIGHT);
499 
500 	PipelineCreateInfo pipelineCreateInfo(*m_pipelineLayout, *m_renderPass, 0, 0);
501 	pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*vs, "main", vk::VK_SHADER_STAGE_VERTEX_BIT));
502 	pipelineCreateInfo.addShader(PipelineCreateInfo::PipelineShaderStage(*fs, "main", vk::VK_SHADER_STAGE_FRAGMENT_BIT));
503 	pipelineCreateInfo.addState(PipelineCreateInfo::VertexInputState(m_vertexInputState));
504 	pipelineCreateInfo.addState(PipelineCreateInfo::InputAssemblerState(m_params.topology));
505 	pipelineCreateInfo.addState(PipelineCreateInfo::ColorBlendState(1, &vkCbAttachmentState));
506 	pipelineCreateInfo.addState(PipelineCreateInfo::ViewportState(1, std::vector<vk::VkViewport>(1, viewport), std::vector<vk::VkRect2D>(1, scissor)));
507 	pipelineCreateInfo.addState(PipelineCreateInfo::DepthStencilState());
508 	pipelineCreateInfo.addState(PipelineCreateInfo::RasterizerState());
509 	pipelineCreateInfo.addState(PipelineCreateInfo::MultiSampleState());
510 
511 	vk::VkPipelineRenderingCreateInfoKHR renderingFormatCreateInfo
512 	{
513 		vk::VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR,
514 		DE_NULL,
515 		0u,
516 		1u,
517 		&m_colorAttachmentFormat,
518 		vk::VK_FORMAT_UNDEFINED,
519 		vk::VK_FORMAT_UNDEFINED
520 	};
521 
522 	if (m_params.useDynamicRendering)
523 	{
524 		pipelineCreateInfo.pNext = &renderingFormatCreateInfo;
525 
526 		if (m_params.testMultiview)
527 			renderingFormatCreateInfo.viewMask = 3u;
528 	}
529 
530 	m_pipeline = vk::createGraphicsPipeline(m_vk, device, DE_NULL, &pipelineCreateInfo);
531 }
532 
iterate()533 tcu::TestStatus InstancedDrawInstance::iterate()
534 {
535 	const vk::VkQueue		queue					= m_context.getUniversalQueue();
536 	const vk::VkDevice		device					= m_context.getDevice();
537 	static const deUint32	instanceCounts[]		= { 0, 1, 2, 4, 20 };
538 	static const deUint32	firstInstanceIndices[]	= { 0, 1, 3, 4, 20 };
539 	const deUint32			numLayers				= m_params.testMultiview ? 2 : 1;
540 
541 	qpTestResult			res						= QP_TEST_RESULT_PASS;
542 
543 	const vk::VkClearValue clearColor = { { { 0.0f, 0.0f, 0.0f, 1.0f } } };
544 	int firstInstanceIndicesCount = DE_LENGTH_OF_ARRAY(firstInstanceIndices);
545 
546 	// Require 'drawIndirectFirstInstance' feature to run non-zero firstInstance indirect draw tests.
547 	if (m_params.function == TestParams::FUNCTION_DRAW_INDIRECT && !m_context.getDeviceFeatures().drawIndirectFirstInstance)
548 	{
549 		firstInstanceIndicesCount = 1;
550 	}
551 
552 	for (int instanceCountNdx = 0; instanceCountNdx < DE_LENGTH_OF_ARRAY(instanceCounts); instanceCountNdx++)
553 	{
554 		const deUint32 instanceCount = instanceCounts[instanceCountNdx];
555 		for (int firstInstanceIndexNdx = 0; firstInstanceIndexNdx < firstInstanceIndicesCount; firstInstanceIndexNdx++)
556 		{
557 			// Prepare vertex data for at least one instance
558 			const deUint32				prepareCount			= de::max(instanceCount, 1u);
559 			const deUint32				firstInstance			= firstInstanceIndices[firstInstanceIndexNdx];
560 
561 			prepareVertexData(prepareCount, firstInstance, m_params.testAttribDivisor ? m_params.attribDivisor : 1);
562 			const de::SharedPtr<Buffer>	vertexBuffer			= createAndUploadBuffer(m_data, m_vk, m_context, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
563 			const de::SharedPtr<Buffer>	instancedVertexBuffer	= createAndUploadBuffer(m_instancedColor, m_vk, m_context, vk::VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
564 			de::SharedPtr<Buffer>		indexBuffer;
565 			de::SharedPtr<Buffer>		indirectBuffer;
566 			beginCommandBuffer(m_vk, *m_cmdBuffer, 0u);
567 
568 			if (m_params.testMultiview)
569 			{
570 				vk::VkImageMemoryBarrier barrier;
571 				barrier.sType							= vk::VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
572 				barrier.pNext							= DE_NULL;
573 				barrier.srcAccessMask					= 0u;
574 				barrier.dstAccessMask					= vk::VK_ACCESS_TRANSFER_WRITE_BIT;
575 				barrier.oldLayout						= vk::VK_IMAGE_LAYOUT_UNDEFINED;
576 				barrier.newLayout						= vk::VK_IMAGE_LAYOUT_GENERAL;
577 				barrier.srcQueueFamilyIndex				= VK_QUEUE_FAMILY_IGNORED;
578 				barrier.dstQueueFamilyIndex				= VK_QUEUE_FAMILY_IGNORED;
579 				barrier.image							= m_colorTargetImage->object();
580 				barrier.subresourceRange.aspectMask		= vk::VK_IMAGE_ASPECT_COLOR_BIT;
581 				barrier.subresourceRange.baseMipLevel	= 0;
582 				barrier.subresourceRange.levelCount		= 1;
583 				barrier.subresourceRange.baseArrayLayer = 0;
584 				barrier.subresourceRange.layerCount		= numLayers;
585 
586 				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,
587 										0, (const vk::VkBufferMemoryBarrier*)DE_NULL, 1, &barrier);
588 
589 			}
590 			else
591 			{
592 				initialTransitionColor2DImage(m_vk, *m_cmdBuffer, m_colorTargetImage->object(), vk::VK_IMAGE_LAYOUT_GENERAL,
593 											  vk::VK_ACCESS_TRANSFER_WRITE_BIT, vk::VK_PIPELINE_STAGE_TRANSFER_BIT);
594 			}
595 
596 			const ImageSubresourceRange subresourceRange(vk::VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, numLayers);
597 			m_vk.cmdClearColorImage(*m_cmdBuffer, m_colorTargetImage->object(),
598 				vk::VK_IMAGE_LAYOUT_GENERAL, &clearColor.color, 1, &subresourceRange);
599 
600 			const vk::VkMemoryBarrier memBarrier =
601 			{
602 				vk::VK_STRUCTURE_TYPE_MEMORY_BARRIER,
603 				DE_NULL,
604 				vk::VK_ACCESS_TRANSFER_WRITE_BIT,
605 				vk::VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | vk::VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
606 			};
607 
608 			m_vk.cmdPipelineBarrier(*m_cmdBuffer, vk::VK_PIPELINE_STAGE_TRANSFER_BIT,
609 				vk::VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
610 				0, 1, &memBarrier, 0, DE_NULL, 0, DE_NULL);
611 
612 			const vk::VkRect2D renderArea = vk::makeRect2D(WIDTH, HEIGHT);
613 			if (m_params.useDynamicRendering)
614 				beginRendering(m_vk, *m_cmdBuffer, *m_colorTargetView, renderArea, clearColor, vk::VK_IMAGE_LAYOUT_GENERAL, vk::VK_ATTACHMENT_LOAD_OP_LOAD, 0, (m_params.testMultiview) ? 2u : 1u, (m_params.testMultiview) ? 3u : 0u);
615 			else
616 				beginRenderPass(m_vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer, renderArea);
617 
618 			if (m_params.function == TestParams::FUNCTION_DRAW_INDEXED || m_params.function == TestParams::FUNCTION_DRAW_INDEXED_INDIRECT)
619 			{
620 				indexBuffer = createAndUploadBuffer(m_indexes, m_vk, m_context, vk::VK_BUFFER_USAGE_INDEX_BUFFER_BIT);
621 				m_vk.cmdBindIndexBuffer(*m_cmdBuffer, indexBuffer->object(), 0, vk::VK_INDEX_TYPE_UINT32);
622 			}
623 
624 			const vk::VkBuffer vertexBuffers[] =
625 			{
626 				vertexBuffer->object(),
627 				instancedVertexBuffer->object(),
628 			};
629 
630 			const vk::VkDeviceSize vertexBufferOffsets[] =
631 			{
632 				0,	// vertexBufferOffset
633 				0,	// instancedVertexBufferOffset
634 			};
635 
636 			m_vk.cmdBindVertexBuffers(*m_cmdBuffer, 0, DE_LENGTH_OF_ARRAY(vertexBuffers), vertexBuffers, vertexBufferOffsets);
637 
638 			const float pushConstants[] = { (float)firstInstance, (float)instanceCount };
639 			m_vk.cmdPushConstants(*m_cmdBuffer, *m_pipelineLayout, vk::VK_SHADER_STAGE_VERTEX_BIT, 0u, (deUint32)sizeof(pushConstants), pushConstants);
640 
641 			m_vk.cmdBindPipeline(*m_cmdBuffer, vk::VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
642 
643 			switch (m_params.function)
644 			{
645 				case TestParams::FUNCTION_DRAW:
646 					m_vk.cmdDraw(*m_cmdBuffer, (deUint32)m_data.size(), instanceCount, 0u, firstInstance);
647 					break;
648 
649 				case TestParams::FUNCTION_DRAW_INDEXED:
650 					m_vk.cmdDrawIndexed(*m_cmdBuffer, (deUint32)m_indexes.size(), instanceCount, 0u, 0u, firstInstance);
651 					break;
652 
653 				case TestParams::FUNCTION_DRAW_INDIRECT:
654 				{
655 					vk::VkDrawIndirectCommand drawCommand =
656 					{
657 						(deUint32)m_data.size(),	// uint32_t	vertexCount;
658 						instanceCount,				// uint32_t	instanceCount;
659 						0u,							// uint32_t	firstVertex;
660 						firstInstance,				// uint32_t	firstInstance;
661 					};
662 					std::vector<vk::VkDrawIndirectCommand> drawCommands;
663 					drawCommands.push_back(drawCommand);
664 					indirectBuffer = createAndUploadBuffer(drawCommands, m_vk, m_context, vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
665 
666 					m_vk.cmdDrawIndirect(*m_cmdBuffer, indirectBuffer->object(), 0, 1u, 0u);
667 					break;
668 				}
669 				case TestParams::FUNCTION_DRAW_INDEXED_INDIRECT:
670 				{
671 					vk::VkDrawIndexedIndirectCommand drawCommand =
672 					{
673 						(deUint32)m_indexes.size(),	// uint32_t	indexCount;
674 						instanceCount,				// uint32_t	instanceCount;
675 						0u,							// uint32_t	firstIndex;
676 						0,							// int32_t	vertexOffset;
677 						firstInstance,				// uint32_t	firstInstance;
678 					};
679 					std::vector<vk::VkDrawIndexedIndirectCommand> drawCommands;
680 					drawCommands.push_back(drawCommand);
681 					indirectBuffer = createAndUploadBuffer(drawCommands, m_vk, m_context, vk::VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT);
682 
683 					m_vk.cmdDrawIndexedIndirect(*m_cmdBuffer, indirectBuffer->object(), 0, 1u, 0u);
684 					break;
685 				}
686 				default:
687 					DE_ASSERT(false);
688 			}
689 
690 			if (m_params.useDynamicRendering)
691 				endRendering(m_vk, *m_cmdBuffer);
692 			else
693 				endRenderPass(m_vk, *m_cmdBuffer);
694 
695 			endCommandBuffer(m_vk, *m_cmdBuffer);
696 
697 			submitCommandsAndWait(m_vk, device, queue, m_cmdBuffer.get());
698 
699 			// Reference rendering
700 			std::vector<tcu::Vec4>	vetrices;
701 			std::vector<tcu::Vec4>	colors;
702 
703 			for (std::vector<VertexPositionAndColor>::const_iterator it = m_data.begin(); it != m_data.end(); ++it)
704 			{
705 				vetrices.push_back(it->position);
706 				colors.push_back(it->color);
707 			}
708 
709 			tcu::TextureLevel refImage (vk::mapVkFormat(m_colorAttachmentFormat), (int)(0.5 + WIDTH), (int)(0.5 + HEIGHT));
710 
711 			tcu::clear(refImage.getAccess(), tcu::Vec4(0.0f, 0.0f, 0.0f, 1.0f));
712 
713 			const TestVertShader					vertShader(instanceCount, firstInstance);
714 			const TestFragShader					fragShader;
715 			const rr::Program						program			(&vertShader, &fragShader);
716 			const rr::MultisamplePixelBufferAccess	colorBuffer		= rr::MultisamplePixelBufferAccess::fromSinglesampleAccess(refImage.getAccess());
717 			const rr::RenderTarget					renderTarget	(colorBuffer);
718 			const rr::RenderState					renderState		((rr::ViewportState(colorBuffer)), m_context.getDeviceProperties().limits.subPixelPrecisionBits);
719 			const rr::Renderer						renderer;
720 
721 			const rr::VertexAttrib	vertexAttribs[] =
722 			{
723 				rr::VertexAttrib(rr::VERTEXATTRIBTYPE_FLOAT, 4, sizeof(tcu::Vec4), 0, &vetrices[0]),
724 				rr::VertexAttrib(rr::VERTEXATTRIBTYPE_FLOAT, 4, sizeof(tcu::Vec4), 0, &colors[0]),
725 				// The reference renderer treats a divisor of 0 as meaning per-vertex.  Use INT_MAX instead; it should work just as well.
726 				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])
727 			};
728 
729 			if (m_params.function == TestParams::FUNCTION_DRAW || m_params.function == TestParams::FUNCTION_DRAW_INDIRECT)
730 			{
731 				const rr::PrimitiveList	primitives = rr::PrimitiveList(mapVkPrimitiveTopology(m_params.topology), (int)vetrices.size(), 0);
732 				const rr::DrawCommand	command(renderState, renderTarget, program, DE_LENGTH_OF_ARRAY(vertexAttribs), &vertexAttribs[0],
733 												primitives);
734 				renderer.drawInstanced(command, instanceCount);
735 			}
736 			else
737 			{
738 				const rr::DrawIndices indicies(m_indexes.data());
739 
740 				const rr::PrimitiveList	primitives = rr::PrimitiveList(mapVkPrimitiveTopology(m_params.topology), (int)m_indexes.size(), indicies);
741 				const rr::DrawCommand	command(renderState, renderTarget, program, DE_LENGTH_OF_ARRAY(vertexAttribs), &vertexAttribs[0],
742 												primitives);
743 				renderer.drawInstanced(command, instanceCount);
744 			}
745 
746 			const vk::VkOffset3D zeroOffset = { 0, 0, 0 };
747 			for (deUint32 i = 0; i < numLayers; i++)
748 			{
749 				const tcu::ConstPixelBufferAccess renderedFrame = m_colorTargetImage->readSurface(queue, m_context.getDefaultAllocator(),
750 					vk::VK_IMAGE_LAYOUT_GENERAL, zeroOffset, WIDTH, HEIGHT, vk::VK_IMAGE_ASPECT_COLOR_BIT, 0, i);
751 
752 				tcu::TestLog &log		= m_context.getTestContext().getLog();
753 
754 				std::ostringstream resultDesc;
755 				resultDesc << "Image layer " << i << " comparison result. Instance count: " << instanceCount << " first instance index: " << firstInstance;
756 
757 				if (m_params.topology == vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST)
758 				{
759 					const bool ok = tcu::intThresholdPositionDeviationCompare(
760 						log, "Result", resultDesc.str().c_str(), refImage.getAccess(), renderedFrame,
761 						tcu::UVec4(4u),					// color threshold
762 						tcu::IVec3(1, 1, 0),			// position deviation tolerance
763 						true,							// don't check the pixels at the boundary
764 						tcu::COMPARE_LOG_RESULT);
765 
766 					if (!ok)
767 						res = QP_TEST_RESULT_FAIL;
768 				}
769 				else
770 				{
771 					if (!tcu::fuzzyCompare(log, "Result", resultDesc.str().c_str(), refImage.getAccess(), renderedFrame, 0.05f, tcu::COMPARE_LOG_RESULT))
772 						res = QP_TEST_RESULT_FAIL;
773 				}
774 			}
775 		}
776 	}
777 	return tcu::TestStatus(res, qpGetTestResultName(res));
778 }
779 
prepareVertexData(int instanceCount,int firstInstance,int instanceDivisor)780 void InstancedDrawInstance::prepareVertexData(int instanceCount, int firstInstance, int instanceDivisor)
781 {
782 	m_data.clear();
783 	m_indexes.clear();
784 	m_instancedColor.clear();
785 
786 	if (m_params.function == TestParams::FUNCTION_DRAW || m_params.function == TestParams::FUNCTION_DRAW_INDIRECT)
787 	{
788 		for (int y = 0; y < QUAD_GRID_SIZE; y++)
789 		{
790 			for (int x = 0; x < QUAD_GRID_SIZE; x++)
791 			{
792 				const float fx0 = -1.0f + (float)(x+0) / (float)QUAD_GRID_SIZE * 2.0f / (float)instanceCount;
793 				const float fx1 = -1.0f + (float)(x+1) / (float)QUAD_GRID_SIZE * 2.0f / (float)instanceCount;
794 				const float fy0 = -1.0f + (float)(y+0) / (float)QUAD_GRID_SIZE * 2.0f;
795 				const float fy1 = -1.0f + (float)(y+1) / (float)QUAD_GRID_SIZE * 2.0f;
796 
797 				// Vertices of a quad's lower-left triangle: (fx0, fy0), (fx1, fy0) and (fx0, fy1)
798 				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx0, fy0, 1.0f, 1.0f), tcu::RGBA::blue().toVec()));
799 				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx1, fy0, 1.0f, 1.0f), tcu::RGBA::blue().toVec()));
800 				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx0, fy1, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
801 
802 				// Vertices of a quad's upper-right triangle: (fx1, fy1), (fx0, fy1) and (fx1, fy0)
803 				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx1, fy1, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
804 				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx0, fy1, 1.0f, 1.0f), tcu::RGBA::green().toVec()));
805 				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx1, fy0, 1.0f, 1.0f), tcu::RGBA::blue().toVec()));
806 			}
807 		}
808 	}
809 	else
810 	{
811 		for (int y = 0; y < QUAD_GRID_SIZE + 1; y++)
812 		{
813 			for (int x = 0; x < QUAD_GRID_SIZE + 1; x++)
814 			{
815 				const float fx = -1.0f + (float)x / (float)QUAD_GRID_SIZE * 2.0f / (float)instanceCount;
816 				const float fy = -1.0f + (float)y / (float)QUAD_GRID_SIZE * 2.0f;
817 
818 				m_data.push_back(VertexPositionAndColor(tcu::Vec4(fx, fy, 1.0f, 1.0f),
819 														(y % 2 ? tcu::RGBA::blue().toVec() : tcu::RGBA::green().toVec())));
820 			}
821 		}
822 
823 		for (int y = 0; y < QUAD_GRID_SIZE; y++)
824 		{
825 			for (int x = 0; x < QUAD_GRID_SIZE; x++)
826 			{
827 				const int ndx00 = y*(QUAD_GRID_SIZE + 1) + x;
828 				const int ndx10 = y*(QUAD_GRID_SIZE + 1) + x + 1;
829 				const int ndx01 = (y + 1)*(QUAD_GRID_SIZE + 1) + x;
830 				const int ndx11 = (y + 1)*(QUAD_GRID_SIZE + 1) + x + 1;
831 
832 				// Lower-left triangle of a quad.
833 				m_indexes.push_back((deUint16)ndx00);
834 				m_indexes.push_back((deUint16)ndx10);
835 				m_indexes.push_back((deUint16)ndx01);
836 
837 				// Upper-right triangle of a quad.
838 				m_indexes.push_back((deUint16)ndx11);
839 				m_indexes.push_back((deUint16)ndx01);
840 				m_indexes.push_back((deUint16)ndx10);
841 			}
842 		}
843 	}
844 
845 	const int colorCount = instanceDivisor == 0 ? 1 : (instanceCount + firstInstance + instanceDivisor - 1) / instanceDivisor;
846 	for (int i = 0; i < instanceCount + firstInstance; i++)
847 	{
848 		m_instancedColor.push_back(tcu::Vec4(0.0, (float)(1.0 - i * 1.0 / colorCount) / 2, 0.0, 1.0));
849 	}
850 }
851 
852 } // anonymus
853 
InstancedTests(tcu::TestContext & testCtx,bool useDynamicRendering)854 InstancedTests::InstancedTests(tcu::TestContext& testCtx, bool useDynamicRendering)
855 	: TestCaseGroup			(testCtx, "instanced", "Instanced drawing tests")
856 	, m_useDynamicRendering	(useDynamicRendering)
857 {
858 	static const vk::VkPrimitiveTopology	topologies[]			=
859 	{
860 		vk::VK_PRIMITIVE_TOPOLOGY_POINT_LIST,
861 		vk::VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
862 		vk::VK_PRIMITIVE_TOPOLOGY_LINE_STRIP,
863 		vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
864 		vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
865 		vk::VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,
866 	};
867 	static const TestParams::DrawFunction	functions[]				=
868 	{
869 		TestParams::FUNCTION_DRAW,
870 		TestParams::FUNCTION_DRAW_INDEXED,
871 		TestParams::FUNCTION_DRAW_INDIRECT,
872 		TestParams::FUNCTION_DRAW_INDEXED_INDIRECT,
873 	};
874 
875 	static const deBool multiviews[] = { DE_FALSE, DE_TRUE };
876 
877 	static const deUint32 divisors[] = { 0, 1, 2, 4, 20 };
878 
879 	for (int topologyNdx = 0; topologyNdx < DE_LENGTH_OF_ARRAY(topologies); topologyNdx++)
880 	{
881 		for (int functionNdx = 0; functionNdx < DE_LENGTH_OF_ARRAY(functions); functionNdx++)
882 		{
883 			for (int testAttribDivisor = 0; testAttribDivisor < 2; testAttribDivisor++)
884 			{
885 				for (int divisorNdx = 0; divisorNdx < DE_LENGTH_OF_ARRAY(divisors); divisorNdx++)
886 				{
887 					for (int multiviewNdx = 0; multiviewNdx < DE_LENGTH_OF_ARRAY(multiviews); multiviewNdx++)
888 					{
889 						// If we don't have VK_EXT_vertex_attribute_divisor, we only get a divisor or 1.
890 						if (!testAttribDivisor && divisors[divisorNdx] != 1)
891 							continue;
892 
893 						TestParams param;
894 						param.function = functions[functionNdx];
895 						param.topology = topologies[topologyNdx];
896 						param.useDynamicRendering = useDynamicRendering;
897 						param.testAttribDivisor = testAttribDivisor ? DE_TRUE : DE_FALSE;
898 						param.attribDivisor = divisors[divisorNdx];
899 						param.testMultiview = multiviews[multiviewNdx];
900 
901 						// Add multiview tests only when vertex attribute divisor is enabled.
902 						if (param.testMultiview && !testAttribDivisor)
903 							continue;
904 
905 						std::string testName = de::toString(param);
906 
907 						addChild(new InstancedDrawCase(m_testCtx, de::toLower(testName), "Instanced drawing test", param));
908 					}
909 				}
910 			}
911 		}
912 	}
913 }
914 
~InstancedTests()915 InstancedTests::~InstancedTests() {}
916 
917 } // DrawTests
918 } // vkt
919