1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.0 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
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 Primitive restart tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es3fPrimitiveRestartTests.hpp"
25#include "gluShaderProgram.hpp"
26#include "gluPixelTransfer.hpp"
27#include "tcuTestLog.hpp"
28#include "tcuSurface.hpp"
29#include "tcuImageCompare.hpp"
30#include "tcuRenderTarget.hpp"
31#include "deRandom.hpp"
32#include "deMath.h"
33#include "deString.h"
34
35#include "glw.h"
36
37using tcu::Vec4;
38
39namespace deqp
40{
41namespace gles3
42{
43namespace Functional
44{
45
46static const int		MAX_RENDER_WIDTH				= 256;
47static const int		MAX_RENDER_HEIGHT				= 256;
48
49static const deUint32	MAX_UNSIGNED_BYTE				= (1<<8) - 1;
50static const deUint32	MAX_UNSIGNED_SHORT				= (1<<16) - 1;
51static const deUint32	MAX_UNSIGNED_INT				= (deUint32)((1ULL << 32) - 1);
52
53static const deUint8	RESTART_INDEX_UNSIGNED_BYTE		= (deUint8)MAX_UNSIGNED_BYTE;
54static const deUint16	RESTART_INDEX_UNSIGNED_SHORT	= (deUint16)MAX_UNSIGNED_SHORT;
55static const deUint32	RESTART_INDEX_UNSIGNED_INT		= MAX_UNSIGNED_INT;
56
57class PrimitiveRestartCase : public TestCase
58{
59public:
60	enum PrimitiveType
61	{
62		PRIMITIVE_POINTS = 0,
63		PRIMITIVE_LINE_STRIP,
64		PRIMITIVE_LINE_LOOP,
65		PRIMITIVE_LINES,
66		PRIMITIVE_TRIANGLE_STRIP,
67		PRIMITIVE_TRIANGLE_FAN,
68		PRIMITIVE_TRIANGLES,
69
70		PRIMITIVE_LAST
71	};
72
73	enum IndexType
74	{
75		INDEX_UNSIGNED_BYTE = 0,
76		INDEX_UNSIGNED_SHORT,
77		INDEX_UNSIGNED_INT,
78
79		INDEX_LAST
80	};
81
82	enum Function
83	{
84		FUNCTION_DRAW_ELEMENTS = 0,
85		FUNCTION_DRAW_ELEMENTS_INSTANCED,
86		FUNCTION_DRAW_RANGE_ELEMENTS,
87
88		FUNCTION_LAST
89	};
90
91							PrimitiveRestartCase	(Context& context, const char* name, const char* description, PrimitiveType primType, IndexType indexType, Function function, bool beginWithRestart, bool endWithRestart, bool duplicateRestarts);
92							~PrimitiveRestartCase	(void);
93
94	void					init					(void);
95	void					deinit					(void);
96	IterateResult			iterate					(void);
97
98private:
99							PrimitiveRestartCase	(const PrimitiveRestartCase& other);
100	PrimitiveRestartCase&	operator=				(const PrimitiveRestartCase& other);
101
102	void					draw					(int startNdx, int count);
103
104	void					renderWithRestart		(void);
105	void					renderWithoutRestart	(void);
106
107	// Helper functions for handling the appropriate index vector (according to m_indexType).
108	void					addIndex				(deUint32 index);
109	deUint32				getIndex				(int indexNdx);
110	int						getNumIndices			(void);
111	void*					getIndexPtr				(int indexNdx);
112
113	// \note Only one of the following index vectors is used (according to m_indexType).
114	std::vector<deUint8>	m_indicesUB;
115	std::vector<deUint16>	m_indicesUS;
116	std::vector<deUint32>	m_indicesUI;
117
118	std::vector<float>		m_positions;
119
120	PrimitiveType			m_primType;
121	IndexType				m_indexType;
122	Function				m_function;
123
124	bool					m_beginWithRestart;		// Whether there will be restart indices at the beginning of the index array.
125	bool					m_endWithRestart;		// Whether there will be restart indices at the end of the index array.
126	bool					m_duplicateRestarts;	// Whether two consecutive restarts are used instead of one.
127
128	glu::ShaderProgram*		m_program;
129};
130
131PrimitiveRestartCase::PrimitiveRestartCase (Context& context, const char* name, const char* description, PrimitiveType primType, IndexType indexType, Function function, bool beginWithRestart, bool endWithRestart, bool duplicateRestarts)
132	: TestCase				(context, name, description)
133	, m_primType			(primType)
134	, m_indexType			(indexType)
135	, m_function			(function)
136	, m_beginWithRestart	(beginWithRestart)
137	, m_endWithRestart		(endWithRestart)
138	, m_duplicateRestarts	(duplicateRestarts)
139	, m_program				(DE_NULL)
140{
141}
142
143PrimitiveRestartCase::~PrimitiveRestartCase (void)
144{
145	PrimitiveRestartCase::deinit();
146}
147
148void PrimitiveRestartCase::deinit (void)
149{
150	delete m_program;
151	m_program = DE_NULL;
152}
153
154void PrimitiveRestartCase::addIndex (deUint32 index)
155{
156	if (m_indexType == INDEX_UNSIGNED_BYTE)
157	{
158		DE_ASSERT(de::inRange(index, (deUint32)0, MAX_UNSIGNED_BYTE));
159		m_indicesUB.push_back((deUint8)index);
160	}
161	else if (m_indexType == INDEX_UNSIGNED_SHORT)
162	{
163		DE_ASSERT(de::inRange(index, (deUint32)0, MAX_UNSIGNED_SHORT));
164		m_indicesUS.push_back((deUint16)index);
165	}
166	else if (m_indexType == INDEX_UNSIGNED_INT)
167	{
168		DE_ASSERT(de::inRange(index, (deUint32)0, MAX_UNSIGNED_INT));
169		m_indicesUI.push_back((deUint32)index);
170	}
171	else
172		DE_ASSERT(DE_FALSE);
173}
174
175deUint32 PrimitiveRestartCase::getIndex (int indexNdx)
176{
177	switch (m_indexType)
178	{
179		case INDEX_UNSIGNED_BYTE:	return (deUint32)m_indicesUB[indexNdx];
180		case INDEX_UNSIGNED_SHORT:	return (deUint32)m_indicesUS[indexNdx];
181		case INDEX_UNSIGNED_INT:	return m_indicesUI[indexNdx];
182		default:
183			DE_ASSERT(DE_FALSE);
184			return 0;
185	}
186}
187
188int PrimitiveRestartCase::getNumIndices (void)
189{
190	switch (m_indexType)
191	{
192		case INDEX_UNSIGNED_BYTE:	return (int)m_indicesUB.size();
193		case INDEX_UNSIGNED_SHORT:	return (int)m_indicesUS.size();
194		case INDEX_UNSIGNED_INT:	return (int)m_indicesUI.size();
195		default:
196			DE_ASSERT(DE_FALSE);
197			return 0;
198	}
199}
200
201// Pointer to the index value at index indexNdx.
202void* PrimitiveRestartCase::getIndexPtr (int indexNdx)
203{
204	switch (m_indexType)
205	{
206		case INDEX_UNSIGNED_BYTE:	return (void*)&m_indicesUB[indexNdx];
207		case INDEX_UNSIGNED_SHORT:	return (void*)&m_indicesUS[indexNdx];
208		case INDEX_UNSIGNED_INT:	return (void*)&m_indicesUI[indexNdx];
209		default:
210			DE_ASSERT(DE_FALSE);
211			return DE_NULL;
212	}
213}
214
215void PrimitiveRestartCase::init (void)
216{
217	// Create shader program.
218
219	static const char* vertShaderSource =
220		"#version 300 es\n"
221		"in highp vec4 a_position;\n"
222		"\n"
223		"void main()\n"
224		"{\n"
225		"	gl_Position = a_position;\n"
226		"}\n";
227
228	static const char* fragShaderSource =
229		"#version 300 es\n"
230		"layout(location = 0) out mediump vec4 o_color;\n"
231		"\n"
232		"void main()\n"
233		"{\n"
234		"	o_color = vec4(1.0f);\n"
235		"}\n";
236
237	DE_ASSERT(!m_program);
238	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertShaderSource, fragShaderSource));
239
240	if(!m_program->isOk())
241	{
242		m_testCtx.getLog() << *m_program;
243		TCU_FAIL("Failed to compile shader");
244	}
245
246	deUint32 restartIndex = m_indexType == INDEX_UNSIGNED_BYTE	? RESTART_INDEX_UNSIGNED_BYTE
247						  : m_indexType == INDEX_UNSIGNED_SHORT	? RESTART_INDEX_UNSIGNED_SHORT
248						  : m_indexType == INDEX_UNSIGNED_INT	? RESTART_INDEX_UNSIGNED_INT
249						  : 0;
250
251	DE_ASSERT(restartIndex != 0);
252
253	DE_ASSERT(getNumIndices() == 0);
254
255	// If testing a case with restart at beginning, add it there.
256	if (m_beginWithRestart)
257	{
258		addIndex(restartIndex);
259		if (m_duplicateRestarts)
260			addIndex(restartIndex);
261	}
262
263	// Generate vertex positions and indices depending on primitive type.
264	// \note At this point, restarts shall not be added to the start or the end of the index vector. Those are special cases, and are done above and after the following if-else chain, respectively.
265
266	if (m_primType == PRIMITIVE_POINTS)
267	{
268		// Generate rows with different numbers of points.
269
270		deUint32	curIndex			= 0;
271		const int	numRows				= 20;
272
273		for (int row = 0; row < numRows; row++)
274		{
275			for (int col = 0; col < row + 1; col++)
276			{
277				float fx = -1.0f + 2.0f * ((float)col + 0.5f) / (float)numRows;
278				float fy = -1.0f + 2.0f * ((float)row + 0.5f) / (float)numRows;
279
280				m_positions.push_back(fx);
281				m_positions.push_back(fy);
282
283				addIndex(curIndex++);
284			}
285
286			if (row < numRows - 1) // Add a restart after all but last row.
287			{
288				addIndex(restartIndex);
289				if (m_duplicateRestarts)
290					addIndex(restartIndex);
291			}
292		}
293	}
294	else if (m_primType == PRIMITIVE_LINE_STRIP || m_primType == PRIMITIVE_LINE_LOOP || m_primType == PRIMITIVE_LINES)
295	{
296		// Generate a numRows x numCols arrangement of line polygons of different vertex counts.
297
298		deUint32	curIndex	= 0;
299		const int	numRows		= 4;
300		const int	numCols		= 4;
301
302		for (int row = 0; row < numRows; row++)
303		{
304			float centerY = -1.0f + 2.0f * ((float)row + 0.5f) / (float)numRows;
305
306			for (int col = 0; col < numCols; col++)
307			{
308				float	centerX		= -1.0f + 2.0f * ((float)col + 0.5f) / (float)numCols;
309				int		numVertices	= row*numCols + col + 1;
310
311				for (int i = 0; i < numVertices; i++)
312				{
313					float fx = centerX + 0.9f * deFloatCos((float)i*2.0f*DE_PI / (float)numVertices) / (float)numCols;
314					float fy = centerY + 0.9f * deFloatSin((float)i*2.0f*DE_PI / (float)numVertices) / (float)numRows;
315
316					m_positions.push_back(fx);
317					m_positions.push_back(fy);
318
319					addIndex(curIndex++);
320				}
321
322				if (col < numCols - 1 || row < numRows - 1) // Add a restart after all but last polygon.
323				{
324					addIndex(restartIndex);
325					if (m_duplicateRestarts)
326						addIndex(restartIndex);
327				}
328			}
329		}
330	}
331	else if (m_primType == PRIMITIVE_TRIANGLE_STRIP)
332	{
333		// Generate a number of horizontal triangle strips of different lengths.
334
335		deUint32	curIndex	= 0;
336		const int	numStrips	= 20;
337
338		for (int stripNdx = 0; stripNdx < numStrips; stripNdx++)
339		{
340			int numVertices = stripNdx + 1;
341
342			for (int i = 0; i < numVertices; i++)
343			{
344				float fx = -0.9f + 1.8f * (float)(i/2*2) / numStrips;
345				float fy = -0.9f + 1.8f * ((float)stripNdx + (i%2 == 0 ? 0.0f : 0.8f)) / numStrips;
346
347				m_positions.push_back(fx);
348				m_positions.push_back(fy);
349
350				addIndex(curIndex++);
351			}
352
353			if (stripNdx < numStrips - 1) // Add a restart after all but last strip.
354			{
355				addIndex(restartIndex);
356				if (m_duplicateRestarts)
357					addIndex(restartIndex);
358			}
359		}
360	}
361	else if (m_primType == PRIMITIVE_TRIANGLE_FAN)
362	{
363		// Generate a numRows x numCols arrangement of triangle fan polygons of different vertex counts.
364
365		deUint32	curIndex	= 0;
366		const int	numRows		= 4;
367		const int	numCols		= 4;
368
369		for (int row = 0; row < numRows; row++)
370		{
371			float centerY = -1.0f + 2.0f * ((float)row + 0.5f) / (float)numRows;
372
373			for (int col = 0; col < numCols; col++)
374			{
375				float	centerX			= -1.0f + 2.0f * ((float)col + 0.5f) / (float)numCols;
376				int		numArcVertices	= row*numCols + col;
377
378				m_positions.push_back(centerX);
379				m_positions.push_back(centerY);
380
381				addIndex(curIndex++);
382
383				for (int i = 0; i < numArcVertices; i++)
384				{
385					float fx = centerX + 0.9f * deFloatCos((float)i*2.0f*DE_PI / (float)numArcVertices) / (float)numCols;
386					float fy = centerY + 0.9f * deFloatSin((float)i*2.0f*DE_PI / (float)numArcVertices) / (float)numRows;
387
388					m_positions.push_back(fx);
389					m_positions.push_back(fy);
390
391					addIndex(curIndex++);
392				}
393
394				if (col < numCols - 1 || row < numRows - 1) // Add a restart after all but last polygon.
395				{
396					addIndex(restartIndex);
397					if (m_duplicateRestarts)
398						addIndex(restartIndex);
399				}
400			}
401		}
402	}
403	else if (m_primType == PRIMITIVE_TRIANGLES)
404	{
405		// Generate a number of rows with (potentially incomplete) triangles.
406
407		deUint32	curIndex	= 0;
408		const int	numRows		= 3*7;
409
410		for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
411		{
412			int numVertices = rowNdx + 1;
413
414			for (int i = 0; i < numVertices; i++)
415			{
416				float fx = -0.9f + 1.8f * ((i/3) + (i%3 == 2 ? 0.8f : 0.0f)) * 3 / numRows;
417				float fy = -0.9f + 1.8f * ((float)rowNdx + (i%3 == 0 ? 0.0f : 0.8f)) / numRows;
418
419				m_positions.push_back(fx);
420				m_positions.push_back(fy);
421
422				addIndex(curIndex++);
423			}
424
425			if (rowNdx < numRows - 1) // Add a restart after all but last row.
426			{
427				addIndex(restartIndex);
428				if (m_duplicateRestarts)
429					addIndex(restartIndex);
430			}
431		}
432	}
433	else
434		DE_ASSERT(DE_FALSE);
435
436	// If testing a case with restart at end, add it there.
437	if (m_endWithRestart)
438	{
439		addIndex(restartIndex);
440		if (m_duplicateRestarts)
441			addIndex(restartIndex);
442	}
443
444	// Special case assertions.
445
446	int numIndices = getNumIndices();
447
448	DE_ASSERT(numIndices > 0);
449	DE_ASSERT(m_beginWithRestart || getIndex(0) != restartIndex);						// We don't want restarts at beginning unless the case is a special case.
450	DE_ASSERT(m_endWithRestart || getIndex(numIndices-1) != restartIndex);			// We don't want restarts at end unless the case is a special case.
451
452	if (!m_duplicateRestarts)
453		for (int i = 1; i < numIndices; i++)
454			DE_ASSERT(getIndex(i) != restartIndex || getIndex(i-1) != restartIndex);	// We don't want duplicate restarts unless the case is a special case.
455}
456
457PrimitiveRestartCase::IterateResult PrimitiveRestartCase::iterate (void)
458{
459	int							width			= deMin32(m_context.getRenderTarget().getWidth(), MAX_RENDER_WIDTH);
460	int							height			= deMin32(m_context.getRenderTarget().getHeight(), MAX_RENDER_HEIGHT);
461
462	int							xOffsetMax		= m_context.getRenderTarget().getWidth() - width;
463	int							yOffsetMax		= m_context.getRenderTarget().getHeight() - height;
464
465	de::Random					rnd				(deStringHash(getName()));
466
467	int							xOffset			= rnd.getInt(0, xOffsetMax);
468	int							yOffset			= rnd.getInt(0, yOffsetMax);
469	tcu::Surface				referenceImg	(width, height);
470	tcu::Surface				resultImg		(width, height);
471
472	glViewport(xOffset, yOffset, width, height);
473	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
474
475	deUint32 program = m_program->getProgram();
476	glUseProgram(program);
477
478	// Setup position attribute.
479
480	int loc = glGetAttribLocation(program, "a_position");
481	glEnableVertexAttribArray(loc);
482	glVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, &m_positions[0]);
483
484	// Render result.
485
486	renderWithRestart();
487	glu::readPixels(m_context.getRenderContext(), xOffset, yOffset, resultImg.getAccess());
488
489	// Render reference (same scene as the real deal, but emulate primitive restart without actually using it).
490
491	renderWithoutRestart();
492	glu::readPixels(m_context.getRenderContext(), xOffset, yOffset, referenceImg.getAccess());
493
494	// Compare.
495
496	bool testOk = tcu::pixelThresholdCompare(m_testCtx.getLog(), "ComparisonResult", "Image comparison result", referenceImg, resultImg, tcu::RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT);
497
498	m_testCtx.setTestResult(testOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
499							testOk ? "Pass"					: "Fail");
500
501	glUseProgram(0);
502
503	return STOP;
504}
505
506// Draw with the appropriate GLES3 draw function.
507void PrimitiveRestartCase::draw (int startNdx, int count)
508{
509	GLenum primTypeGL;
510
511	switch (m_primType)
512	{
513		case PRIMITIVE_POINTS:			primTypeGL = GL_POINTS;			break;
514		case PRIMITIVE_LINE_STRIP:		primTypeGL = GL_LINE_STRIP;		break;
515		case PRIMITIVE_LINE_LOOP:		primTypeGL = GL_LINE_LOOP;		break;
516		case PRIMITIVE_LINES:			primTypeGL = GL_LINES;			break;
517		case PRIMITIVE_TRIANGLE_STRIP:	primTypeGL = GL_TRIANGLE_STRIP;	break;
518		case PRIMITIVE_TRIANGLE_FAN:	primTypeGL = GL_TRIANGLE_FAN;	break;
519		case PRIMITIVE_TRIANGLES:		primTypeGL = GL_TRIANGLES;		break;
520		default:
521			DE_ASSERT(DE_FALSE);
522			primTypeGL = 0;
523	}
524
525	GLenum indexTypeGL;
526
527	switch (m_indexType)
528	{
529		case INDEX_UNSIGNED_BYTE:	indexTypeGL = GL_UNSIGNED_BYTE;		break;
530		case INDEX_UNSIGNED_SHORT:	indexTypeGL = GL_UNSIGNED_SHORT;	break;
531		case INDEX_UNSIGNED_INT:	indexTypeGL = GL_UNSIGNED_INT;		break;
532		default:
533			DE_ASSERT(DE_FALSE);
534			indexTypeGL = 0;
535	}
536
537	deUint32 restartIndex = m_indexType == INDEX_UNSIGNED_BYTE	? RESTART_INDEX_UNSIGNED_BYTE
538						  : m_indexType == INDEX_UNSIGNED_SHORT	? RESTART_INDEX_UNSIGNED_SHORT
539						  : m_indexType == INDEX_UNSIGNED_INT	? RESTART_INDEX_UNSIGNED_INT
540						  : 0;
541
542	DE_ASSERT(restartIndex != 0);
543
544	if (m_function == FUNCTION_DRAW_ELEMENTS)
545		glDrawElements(primTypeGL, (GLsizei)count, indexTypeGL, (GLvoid*)getIndexPtr(startNdx));
546	else if (m_function == FUNCTION_DRAW_ELEMENTS_INSTANCED)
547		glDrawElementsInstanced(primTypeGL, (GLsizei)count, indexTypeGL, (GLvoid*)getIndexPtr(startNdx), 1);
548	else
549	{
550		DE_ASSERT(m_function == FUNCTION_DRAW_RANGE_ELEMENTS);
551
552		// Find the largest non-restart index in the index array (for glDrawRangeElements() end parameter).
553
554		deUint32 max = 0;
555
556		int numIndices = getNumIndices();
557		for (int i = 0; i < numIndices; i++)
558		{
559			deUint32 index = getIndex(i);
560			if (index != restartIndex && index > max)
561				max = index;
562		}
563
564		glDrawRangeElements(primTypeGL, 0, (GLuint)max, (GLsizei)count, indexTypeGL, (GLvoid*)getIndexPtr(startNdx));
565	}
566}
567
568void PrimitiveRestartCase::renderWithRestart (void)
569{
570	GLU_CHECK_MSG("PrimitiveRestartCase::renderWithRestart() begin");
571
572	glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
573	GLU_CHECK_MSG("Enable primitive restart");
574	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
575	GLU_CHECK_MSG("Clear in PrimitiveRestartCase::renderWithRestart()");
576
577	draw(0, getNumIndices());
578
579	GLU_CHECK_MSG("Draw in PrimitiveRestartCase::renderWithRestart()");
580
581	GLU_CHECK_MSG("PrimitiveRestartCase::renderWithRestart() end");
582}
583
584void PrimitiveRestartCase::renderWithoutRestart (void)
585{
586	GLU_CHECK_MSG("PrimitiveRestartCase::renderWithoutRestart() begin");
587
588	deUint32 restartIndex = m_indexType == INDEX_UNSIGNED_BYTE	? RESTART_INDEX_UNSIGNED_BYTE
589						  : m_indexType == INDEX_UNSIGNED_SHORT	? RESTART_INDEX_UNSIGNED_SHORT
590						  : m_indexType == INDEX_UNSIGNED_INT	? RESTART_INDEX_UNSIGNED_INT
591						  : 0;
592
593	DE_ASSERT(restartIndex != 0);
594
595	glDisable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
596	GLU_CHECK_MSG("Disable primitive restart");
597	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
598	GLU_CHECK_MSG("Clear in PrimitiveRestartCase::renderWithoutRestart()");
599
600	// Draw, emulating primitive restart.
601
602	int numIndices = getNumIndices();
603
604	DE_ASSERT(numIndices >= 0);
605
606	int indexArrayStartNdx = 0; // Keep track of the draw start index - first index after a primitive restart, or initially the first index altogether.
607
608	for (int indexArrayNdx = 0; indexArrayNdx <= numIndices; indexArrayNdx++) // \note Goes one "too far" in order to detect end of array as well.
609	{
610		if (indexArrayNdx >= numIndices || getIndex(indexArrayNdx) == restartIndex) // \note Handle end of array the same way as a restart index encounter.
611		{
612			if (indexArrayStartNdx < numIndices)
613			{
614				// Draw from index indexArrayStartNdx to index indexArrayNdx-1 .
615
616				draw(indexArrayStartNdx, indexArrayNdx - indexArrayStartNdx);
617				GLU_CHECK_MSG("Draw in PrimitiveRestartCase::renderWithoutRestart()");
618			}
619
620			indexArrayStartNdx = indexArrayNdx + 1; // Next draw starts just after this restart index.
621		}
622	}
623
624	GLU_CHECK_MSG("PrimitiveRestartCase::renderWithoutRestart() end");
625}
626
627PrimitiveRestartTests::PrimitiveRestartTests (Context& context)
628	: TestCaseGroup(context, "primitive_restart", "Primitive restart tests")
629{
630}
631
632PrimitiveRestartTests::~PrimitiveRestartTests (void)
633{
634}
635
636void PrimitiveRestartTests::init (void)
637{
638	for (int isRestartBeginCaseI = 0; isRestartBeginCaseI <= 1; isRestartBeginCaseI++)
639	for (int isRestartEndCaseI = 0; isRestartEndCaseI <= 1; isRestartEndCaseI++)
640	for (int isDuplicateRestartCaseI = 0; isDuplicateRestartCaseI <= 1; isDuplicateRestartCaseI++)
641	{
642		bool			isRestartBeginCase		= isRestartBeginCaseI != 0;
643		bool			isRestartEndCase		= isRestartEndCaseI != 0;
644		bool			isDuplicateRestartCase	= isDuplicateRestartCaseI != 0;
645
646		std::string		specialCaseGroupName;
647
648		if (isRestartBeginCase)		specialCaseGroupName = "begin_restart";
649		if (isRestartEndCase)		specialCaseGroupName += std::string(specialCaseGroupName.empty() ? "" : "_") + "end_restart";
650		if (isDuplicateRestartCase)	specialCaseGroupName += std::string(specialCaseGroupName.empty() ? "" : "_") + "duplicate_restarts";
651
652		if (specialCaseGroupName.empty())
653			specialCaseGroupName = "basic";
654
655		TestCaseGroup* specialCaseGroup = new TestCaseGroup(m_context, specialCaseGroupName.c_str(), "");
656		addChild(specialCaseGroup);
657
658		for (int primType = 0; primType < (int)PrimitiveRestartCase::PRIMITIVE_LAST; primType++)
659		{
660			const char* primTypeName = primType == (int)PrimitiveRestartCase::PRIMITIVE_POINTS			? "points"
661									 : primType == (int)PrimitiveRestartCase::PRIMITIVE_LINE_STRIP		? "line_strip"
662									 : primType == (int)PrimitiveRestartCase::PRIMITIVE_LINE_LOOP		? "line_loop"
663									 : primType == (int)PrimitiveRestartCase::PRIMITIVE_LINES			? "lines"
664									 : primType == (int)PrimitiveRestartCase::PRIMITIVE_TRIANGLE_STRIP	? "triangle_strip"
665									 : primType == (int)PrimitiveRestartCase::PRIMITIVE_TRIANGLE_FAN	? "triangle_fan"
666									 : primType == (int)PrimitiveRestartCase::PRIMITIVE_TRIANGLES		? "triangles"
667									 : DE_NULL;
668
669			DE_ASSERT(primTypeName != DE_NULL);
670
671			TestCaseGroup* primTypeGroup = new TestCaseGroup(m_context, primTypeName, "");
672			specialCaseGroup->addChild(primTypeGroup);
673
674			for (int indexType = 0; indexType < (int)PrimitiveRestartCase::INDEX_LAST; indexType++)
675			{
676				const char *indexTypeName = indexType == (int)PrimitiveRestartCase::INDEX_UNSIGNED_BYTE		? "unsigned_byte"
677										  : indexType == (int)PrimitiveRestartCase::INDEX_UNSIGNED_SHORT	? "unsigned_short"
678										  : indexType == (int)PrimitiveRestartCase::INDEX_UNSIGNED_INT		? "unsigned_int"
679										  : DE_NULL;
680
681				DE_ASSERT(indexTypeName != DE_NULL);
682
683				TestCaseGroup* indexTypeGroup = new TestCaseGroup(m_context, indexTypeName, "");
684				primTypeGroup->addChild(indexTypeGroup);
685
686				for (int function = 0; function < (int)PrimitiveRestartCase::FUNCTION_LAST; function++)
687				{
688					const char* functionName = function == (int)PrimitiveRestartCase::FUNCTION_DRAW_ELEMENTS			? "draw_elements"
689											 : function == (int)PrimitiveRestartCase::FUNCTION_DRAW_ELEMENTS_INSTANCED	? "draw_elements_instanced"
690											 : function == (int)PrimitiveRestartCase::FUNCTION_DRAW_RANGE_ELEMENTS		? "draw_range_elements"
691											 : DE_NULL;
692
693					DE_ASSERT(functionName != DE_NULL);
694
695					indexTypeGroup->addChild(new PrimitiveRestartCase(m_context,
696																	  functionName,
697																	  "",
698																	  (PrimitiveRestartCase::PrimitiveType)primType,
699																	  (PrimitiveRestartCase::IndexType)indexType,
700																	  (PrimitiveRestartCase::Function)function,
701																	  isRestartBeginCase,
702																	  isRestartEndCase,
703																	  isDuplicateRestartCase));
704				}
705			}
706		}
707	}
708}
709
710} // Functional
711} // gles3
712} // deqp
713