• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
37 using tcu::Vec4;
38 
39 namespace deqp
40 {
41 namespace gles3
42 {
43 namespace Functional
44 {
45 
46 static const int		MAX_RENDER_WIDTH				= 256;
47 static const int		MAX_RENDER_HEIGHT				= 256;
48 
49 static const deUint32	MAX_UNSIGNED_BYTE				= (1<<8) - 1;
50 static const deUint32	MAX_UNSIGNED_SHORT				= (1<<16) - 1;
51 static const deUint32	MAX_UNSIGNED_INT				= (deUint32)((1ULL << 32) - 1);
52 
53 static const deUint8	RESTART_INDEX_UNSIGNED_BYTE		= (deUint8)MAX_UNSIGNED_BYTE;
54 static const deUint16	RESTART_INDEX_UNSIGNED_SHORT	= (deUint16)MAX_UNSIGNED_SHORT;
55 static const deUint32	RESTART_INDEX_UNSIGNED_INT		= MAX_UNSIGNED_INT;
56 
57 class PrimitiveRestartCase : public TestCase
58 {
59 public:
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 
98 private:
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 
PrimitiveRestartCase(Context & context,const char * name,const char * description,PrimitiveType primType,IndexType indexType,Function function,bool beginWithRestart,bool endWithRestart,bool duplicateRestarts)131 PrimitiveRestartCase::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 
~PrimitiveRestartCase(void)143 PrimitiveRestartCase::~PrimitiveRestartCase (void)
144 {
145 	PrimitiveRestartCase::deinit();
146 }
147 
deinit(void)148 void PrimitiveRestartCase::deinit (void)
149 {
150 	delete m_program;
151 	m_program = DE_NULL;
152 }
153 
addIndex(deUint32 index)154 void 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 
getIndex(int indexNdx)175 deUint32 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 
getNumIndices(void)188 int 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.
getIndexPtr(int indexNdx)202 void* 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 
init(void)215 void PrimitiveRestartCase::init (void)
216 {
217 	// Create shader program.
218 	std::string vertShaderSource;
219 	if (m_primType == PRIMITIVE_POINTS)
220 	{
221 		vertShaderSource =
222 			"#version 300 es\n"
223 			"in highp vec4 a_position;\n"
224 			"\n"
225 			"void main()\n"
226 			"{\n"
227 			"	gl_Position = a_position;\n"
228 			"	gl_PointSize = 1.0f;\n"
229 			"}\n";
230 	}
231 	else
232 	{
233 		vertShaderSource =
234 			"#version 300 es\n"
235 			"in highp vec4 a_position;\n"
236 			"\n"
237 			"void main()\n"
238 			"{\n"
239 			"	gl_Position = a_position;\n"
240 			"}\n";
241 	}
242 
243 	static const char* fragShaderSource =
244 		"#version 300 es\n"
245 		"layout(location = 0) out mediump vec4 o_color;\n"
246 		"\n"
247 		"void main()\n"
248 		"{\n"
249 		"	o_color = vec4(1.0f);\n"
250 		"}\n";
251 
252 	DE_ASSERT(!m_program);
253 	m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertShaderSource, fragShaderSource));
254 
255 	if(!m_program->isOk())
256 	{
257 		m_testCtx.getLog() << *m_program;
258 		TCU_FAIL("Failed to compile shader");
259 	}
260 
261 	deUint32 restartIndex = m_indexType == INDEX_UNSIGNED_BYTE	? RESTART_INDEX_UNSIGNED_BYTE
262 						  : m_indexType == INDEX_UNSIGNED_SHORT	? RESTART_INDEX_UNSIGNED_SHORT
263 						  : m_indexType == INDEX_UNSIGNED_INT	? RESTART_INDEX_UNSIGNED_INT
264 						  : 0;
265 
266 	DE_ASSERT(restartIndex != 0);
267 
268 	DE_ASSERT(getNumIndices() == 0);
269 
270 	// If testing a case with restart at beginning, add it there.
271 	if (m_beginWithRestart)
272 	{
273 		addIndex(restartIndex);
274 		if (m_duplicateRestarts)
275 			addIndex(restartIndex);
276 	}
277 
278 	// Generate vertex positions and indices depending on primitive type.
279 	// \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.
280 
281 	if (m_primType == PRIMITIVE_POINTS)
282 	{
283 		// Generate rows with different numbers of points.
284 
285 		deUint32	curIndex			= 0;
286 		const int	numRows				= 20;
287 
288 		for (int row = 0; row < numRows; row++)
289 		{
290 			for (int col = 0; col < row + 1; col++)
291 			{
292 				float fx = -1.0f + 2.0f * ((float)col + 0.5f) / (float)numRows;
293 				float fy = -1.0f + 2.0f * ((float)row + 0.5f) / (float)numRows;
294 
295 				m_positions.push_back(fx);
296 				m_positions.push_back(fy);
297 
298 				addIndex(curIndex++);
299 			}
300 
301 			if (row < numRows - 1) // Add a restart after all but last row.
302 			{
303 				addIndex(restartIndex);
304 				if (m_duplicateRestarts)
305 					addIndex(restartIndex);
306 			}
307 		}
308 	}
309 	else if (m_primType == PRIMITIVE_LINE_STRIP || m_primType == PRIMITIVE_LINE_LOOP || m_primType == PRIMITIVE_LINES)
310 	{
311 		// Generate a numRows x numCols arrangement of line polygons of different vertex counts.
312 
313 		deUint32	curIndex	= 0;
314 		const int	numRows		= 4;
315 		const int	numCols		= 4;
316 
317 		for (int row = 0; row < numRows; row++)
318 		{
319 			float centerY = -1.0f + 2.0f * ((float)row + 0.5f) / (float)numRows;
320 
321 			for (int col = 0; col < numCols; col++)
322 			{
323 				float	centerX		= -1.0f + 2.0f * ((float)col + 0.5f) / (float)numCols;
324 				int		numVertices	= row*numCols + col + 1;
325 
326 				for (int i = 0; i < numVertices; i++)
327 				{
328 					float fx = centerX + 0.9f * deFloatCos((float)i*2.0f*DE_PI / (float)numVertices) / (float)numCols;
329 					float fy = centerY + 0.9f * deFloatSin((float)i*2.0f*DE_PI / (float)numVertices) / (float)numRows;
330 
331 					m_positions.push_back(fx);
332 					m_positions.push_back(fy);
333 
334 					addIndex(curIndex++);
335 				}
336 
337 				if (col < numCols - 1 || row < numRows - 1) // Add a restart after all but last polygon.
338 				{
339 					addIndex(restartIndex);
340 					if (m_duplicateRestarts)
341 						addIndex(restartIndex);
342 				}
343 			}
344 		}
345 	}
346 	else if (m_primType == PRIMITIVE_TRIANGLE_STRIP)
347 	{
348 		// Generate a number of horizontal triangle strips of different lengths.
349 
350 		deUint32	curIndex	= 0;
351 		const int	numStrips	= 20;
352 
353 		for (int stripNdx = 0; stripNdx < numStrips; stripNdx++)
354 		{
355 			int numVertices = stripNdx + 1;
356 
357 			for (int i = 0; i < numVertices; i++)
358 			{
359 				float fx = -0.9f + 1.8f * (float)(i/2*2) / numStrips;
360 				float fy = -0.9f + 1.8f * ((float)stripNdx + (i%2 == 0 ? 0.0f : 0.8f)) / numStrips;
361 
362 				m_positions.push_back(fx);
363 				m_positions.push_back(fy);
364 
365 				addIndex(curIndex++);
366 			}
367 
368 			if (stripNdx < numStrips - 1) // Add a restart after all but last strip.
369 			{
370 				addIndex(restartIndex);
371 				if (m_duplicateRestarts)
372 					addIndex(restartIndex);
373 			}
374 		}
375 	}
376 	else if (m_primType == PRIMITIVE_TRIANGLE_FAN)
377 	{
378 		// Generate a numRows x numCols arrangement of triangle fan polygons of different vertex counts.
379 
380 		deUint32	curIndex	= 0;
381 		const int	numRows		= 4;
382 		const int	numCols		= 4;
383 
384 		for (int row = 0; row < numRows; row++)
385 		{
386 			float centerY = -1.0f + 2.0f * ((float)row + 0.5f) / (float)numRows;
387 
388 			for (int col = 0; col < numCols; col++)
389 			{
390 				float	centerX			= -1.0f + 2.0f * ((float)col + 0.5f) / (float)numCols;
391 				int		numArcVertices	= row*numCols + col;
392 
393 				m_positions.push_back(centerX);
394 				m_positions.push_back(centerY);
395 
396 				addIndex(curIndex++);
397 
398 				for (int i = 0; i < numArcVertices; i++)
399 				{
400 					float fx = centerX + 0.9f * deFloatCos((float)i*2.0f*DE_PI / (float)numArcVertices) / (float)numCols;
401 					float fy = centerY + 0.9f * deFloatSin((float)i*2.0f*DE_PI / (float)numArcVertices) / (float)numRows;
402 
403 					m_positions.push_back(fx);
404 					m_positions.push_back(fy);
405 
406 					addIndex(curIndex++);
407 				}
408 
409 				if (col < numCols - 1 || row < numRows - 1) // Add a restart after all but last polygon.
410 				{
411 					addIndex(restartIndex);
412 					if (m_duplicateRestarts)
413 						addIndex(restartIndex);
414 				}
415 			}
416 		}
417 	}
418 	else if (m_primType == PRIMITIVE_TRIANGLES)
419 	{
420 		// Generate a number of rows with (potentially incomplete) triangles.
421 
422 		deUint32	curIndex	= 0;
423 		const int	numRows		= 3*7;
424 
425 		for (int rowNdx = 0; rowNdx < numRows; rowNdx++)
426 		{
427 			int numVertices = rowNdx + 1;
428 
429 			for (int i = 0; i < numVertices; i++)
430 			{
431 				float fx = -0.9f + 1.8f * ((float)(i/3) + (i%3 == 2 ? 0.8f : 0.0f)) * 3 / numRows;
432 				float fy = -0.9f + 1.8f * ((float)rowNdx + (i%3 == 0 ? 0.0f : 0.8f)) / numRows;
433 
434 				m_positions.push_back(fx);
435 				m_positions.push_back(fy);
436 
437 				addIndex(curIndex++);
438 			}
439 
440 			if (rowNdx < numRows - 1) // Add a restart after all but last row.
441 			{
442 				addIndex(restartIndex);
443 				if (m_duplicateRestarts)
444 					addIndex(restartIndex);
445 			}
446 		}
447 	}
448 	else
449 		DE_ASSERT(DE_FALSE);
450 
451 	// If testing a case with restart at end, add it there.
452 	if (m_endWithRestart)
453 	{
454 		addIndex(restartIndex);
455 		if (m_duplicateRestarts)
456 			addIndex(restartIndex);
457 	}
458 
459 	// Special case assertions.
460 
461 	int numIndices = getNumIndices();
462 
463 	DE_ASSERT(numIndices > 0);
464 	DE_ASSERT(m_beginWithRestart || getIndex(0) != restartIndex);						// We don't want restarts at beginning unless the case is a special case.
465 	DE_ASSERT(m_endWithRestart || getIndex(numIndices-1) != restartIndex);			// We don't want restarts at end unless the case is a special case.
466 
467 	if (!m_duplicateRestarts)
468 		for (int i = 1; i < numIndices; i++)
469 			DE_ASSERT(getIndex(i) != restartIndex || getIndex(i-1) != restartIndex);	// We don't want duplicate restarts unless the case is a special case.
470 }
471 
iterate(void)472 PrimitiveRestartCase::IterateResult PrimitiveRestartCase::iterate (void)
473 {
474 	int							width			= deMin32(m_context.getRenderTarget().getWidth(), MAX_RENDER_WIDTH);
475 	int							height			= deMin32(m_context.getRenderTarget().getHeight(), MAX_RENDER_HEIGHT);
476 
477 	int							xOffsetMax		= m_context.getRenderTarget().getWidth() - width;
478 	int							yOffsetMax		= m_context.getRenderTarget().getHeight() - height;
479 
480 	de::Random					rnd				(deStringHash(getName()));
481 
482 	int							xOffset			= rnd.getInt(0, xOffsetMax);
483 	int							yOffset			= rnd.getInt(0, yOffsetMax);
484 	tcu::Surface				referenceImg	(width, height);
485 	tcu::Surface				resultImg		(width, height);
486 
487 	glViewport(xOffset, yOffset, width, height);
488 	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
489 
490 	deUint32 program = m_program->getProgram();
491 	glUseProgram(program);
492 
493 	// Setup position attribute.
494 
495 	int loc = glGetAttribLocation(program, "a_position");
496 	glEnableVertexAttribArray(loc);
497 	glVertexAttribPointer(loc, 2, GL_FLOAT, GL_FALSE, 0, &m_positions[0]);
498 
499 	// Render result.
500 
501 	renderWithRestart();
502 	glu::readPixels(m_context.getRenderContext(), xOffset, yOffset, resultImg.getAccess());
503 
504 	// Render reference (same scene as the real deal, but emulate primitive restart without actually using it).
505 
506 	renderWithoutRestart();
507 	glu::readPixels(m_context.getRenderContext(), xOffset, yOffset, referenceImg.getAccess());
508 
509 	// Compare.
510 
511 	bool testOk = tcu::pixelThresholdCompare(m_testCtx.getLog(), "ComparisonResult", "Image comparison result", referenceImg, resultImg, tcu::RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT);
512 
513 	m_testCtx.setTestResult(testOk ? QP_TEST_RESULT_PASS	: QP_TEST_RESULT_FAIL,
514 							testOk ? "Pass"					: "Fail");
515 
516 	glUseProgram(0);
517 
518 	return STOP;
519 }
520 
521 // Draw with the appropriate GLES3 draw function.
draw(int startNdx,int count)522 void PrimitiveRestartCase::draw (int startNdx, int count)
523 {
524 	GLenum primTypeGL;
525 
526 	switch (m_primType)
527 	{
528 		case PRIMITIVE_POINTS:			primTypeGL = GL_POINTS;			break;
529 		case PRIMITIVE_LINE_STRIP:		primTypeGL = GL_LINE_STRIP;		break;
530 		case PRIMITIVE_LINE_LOOP:		primTypeGL = GL_LINE_LOOP;		break;
531 		case PRIMITIVE_LINES:			primTypeGL = GL_LINES;			break;
532 		case PRIMITIVE_TRIANGLE_STRIP:	primTypeGL = GL_TRIANGLE_STRIP;	break;
533 		case PRIMITIVE_TRIANGLE_FAN:	primTypeGL = GL_TRIANGLE_FAN;	break;
534 		case PRIMITIVE_TRIANGLES:		primTypeGL = GL_TRIANGLES;		break;
535 		default:
536 			DE_ASSERT(DE_FALSE);
537 			primTypeGL = 0;
538 	}
539 
540 	GLenum indexTypeGL;
541 
542 	switch (m_indexType)
543 	{
544 		case INDEX_UNSIGNED_BYTE:	indexTypeGL = GL_UNSIGNED_BYTE;		break;
545 		case INDEX_UNSIGNED_SHORT:	indexTypeGL = GL_UNSIGNED_SHORT;	break;
546 		case INDEX_UNSIGNED_INT:	indexTypeGL = GL_UNSIGNED_INT;		break;
547 		default:
548 			DE_ASSERT(DE_FALSE);
549 			indexTypeGL = 0;
550 	}
551 
552 	deUint32 restartIndex = m_indexType == INDEX_UNSIGNED_BYTE	? RESTART_INDEX_UNSIGNED_BYTE
553 						  : m_indexType == INDEX_UNSIGNED_SHORT	? RESTART_INDEX_UNSIGNED_SHORT
554 						  : m_indexType == INDEX_UNSIGNED_INT	? RESTART_INDEX_UNSIGNED_INT
555 						  : 0;
556 
557 	DE_ASSERT(restartIndex != 0);
558 
559 	if (m_function == FUNCTION_DRAW_ELEMENTS)
560 		glDrawElements(primTypeGL, (GLsizei)count, indexTypeGL, (GLvoid*)getIndexPtr(startNdx));
561 	else if (m_function == FUNCTION_DRAW_ELEMENTS_INSTANCED)
562 		glDrawElementsInstanced(primTypeGL, (GLsizei)count, indexTypeGL, (GLvoid*)getIndexPtr(startNdx), 1);
563 	else
564 	{
565 		DE_ASSERT(m_function == FUNCTION_DRAW_RANGE_ELEMENTS);
566 
567 		// Find the largest non-restart index in the index array (for glDrawRangeElements() end parameter).
568 
569 		deUint32 max = 0;
570 
571 		int numIndices = getNumIndices();
572 		for (int i = 0; i < numIndices; i++)
573 		{
574 			deUint32 index = getIndex(i);
575 			if (index != restartIndex && index > max)
576 				max = index;
577 		}
578 
579 		glDrawRangeElements(primTypeGL, 0, (GLuint)max, (GLsizei)count, indexTypeGL, (GLvoid*)getIndexPtr(startNdx));
580 	}
581 }
582 
renderWithRestart(void)583 void PrimitiveRestartCase::renderWithRestart (void)
584 {
585 	GLU_CHECK_MSG("PrimitiveRestartCase::renderWithRestart() begin");
586 
587 	glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
588 	GLU_CHECK_MSG("Enable primitive restart");
589 	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
590 	GLU_CHECK_MSG("Clear in PrimitiveRestartCase::renderWithRestart()");
591 
592 	draw(0, getNumIndices());
593 
594 	GLU_CHECK_MSG("Draw in PrimitiveRestartCase::renderWithRestart()");
595 
596 	GLU_CHECK_MSG("PrimitiveRestartCase::renderWithRestart() end");
597 }
598 
renderWithoutRestart(void)599 void PrimitiveRestartCase::renderWithoutRestart (void)
600 {
601 	GLU_CHECK_MSG("PrimitiveRestartCase::renderWithoutRestart() begin");
602 
603 	deUint32 restartIndex = m_indexType == INDEX_UNSIGNED_BYTE	? RESTART_INDEX_UNSIGNED_BYTE
604 						  : m_indexType == INDEX_UNSIGNED_SHORT	? RESTART_INDEX_UNSIGNED_SHORT
605 						  : m_indexType == INDEX_UNSIGNED_INT	? RESTART_INDEX_UNSIGNED_INT
606 						  : 0;
607 
608 	DE_ASSERT(restartIndex != 0);
609 
610 	glDisable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
611 	GLU_CHECK_MSG("Disable primitive restart");
612 	glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
613 	GLU_CHECK_MSG("Clear in PrimitiveRestartCase::renderWithoutRestart()");
614 
615 	// Draw, emulating primitive restart.
616 
617 	int numIndices = getNumIndices();
618 
619 	DE_ASSERT(numIndices >= 0);
620 
621 	int indexArrayStartNdx = 0; // Keep track of the draw start index - first index after a primitive restart, or initially the first index altogether.
622 
623 	for (int indexArrayNdx = 0; indexArrayNdx <= numIndices; indexArrayNdx++) // \note Goes one "too far" in order to detect end of array as well.
624 	{
625 		if (indexArrayNdx >= numIndices || getIndex(indexArrayNdx) == restartIndex) // \note Handle end of array the same way as a restart index encounter.
626 		{
627 			if (indexArrayStartNdx < numIndices)
628 			{
629 				// Draw from index indexArrayStartNdx to index indexArrayNdx-1 .
630 
631 				draw(indexArrayStartNdx, indexArrayNdx - indexArrayStartNdx);
632 				GLU_CHECK_MSG("Draw in PrimitiveRestartCase::renderWithoutRestart()");
633 			}
634 
635 			indexArrayStartNdx = indexArrayNdx + 1; // Next draw starts just after this restart index.
636 		}
637 	}
638 
639 	GLU_CHECK_MSG("PrimitiveRestartCase::renderWithoutRestart() end");
640 }
641 
PrimitiveRestartTests(Context & context)642 PrimitiveRestartTests::PrimitiveRestartTests (Context& context)
643 	: TestCaseGroup(context, "primitive_restart", "Primitive restart tests")
644 {
645 }
646 
~PrimitiveRestartTests(void)647 PrimitiveRestartTests::~PrimitiveRestartTests (void)
648 {
649 }
650 
init(void)651 void PrimitiveRestartTests::init (void)
652 {
653 	for (int isRestartBeginCaseI = 0; isRestartBeginCaseI <= 1; isRestartBeginCaseI++)
654 	for (int isRestartEndCaseI = 0; isRestartEndCaseI <= 1; isRestartEndCaseI++)
655 	for (int isDuplicateRestartCaseI = 0; isDuplicateRestartCaseI <= 1; isDuplicateRestartCaseI++)
656 	{
657 		bool			isRestartBeginCase		= isRestartBeginCaseI != 0;
658 		bool			isRestartEndCase		= isRestartEndCaseI != 0;
659 		bool			isDuplicateRestartCase	= isDuplicateRestartCaseI != 0;
660 
661 		std::string		specialCaseGroupName;
662 
663 		if (isRestartBeginCase)		specialCaseGroupName = "begin_restart";
664 		if (isRestartEndCase)		specialCaseGroupName += std::string(specialCaseGroupName.empty() ? "" : "_") + "end_restart";
665 		if (isDuplicateRestartCase)	specialCaseGroupName += std::string(specialCaseGroupName.empty() ? "" : "_") + "duplicate_restarts";
666 
667 		if (specialCaseGroupName.empty())
668 			specialCaseGroupName = "basic";
669 
670 		TestCaseGroup* specialCaseGroup = new TestCaseGroup(m_context, specialCaseGroupName.c_str(), "");
671 		addChild(specialCaseGroup);
672 
673 		for (int primType = 0; primType < (int)PrimitiveRestartCase::PRIMITIVE_LAST; primType++)
674 		{
675 			const char* primTypeName = primType == (int)PrimitiveRestartCase::PRIMITIVE_POINTS			? "points"
676 									 : primType == (int)PrimitiveRestartCase::PRIMITIVE_LINE_STRIP		? "line_strip"
677 									 : primType == (int)PrimitiveRestartCase::PRIMITIVE_LINE_LOOP		? "line_loop"
678 									 : primType == (int)PrimitiveRestartCase::PRIMITIVE_LINES			? "lines"
679 									 : primType == (int)PrimitiveRestartCase::PRIMITIVE_TRIANGLE_STRIP	? "triangle_strip"
680 									 : primType == (int)PrimitiveRestartCase::PRIMITIVE_TRIANGLE_FAN	? "triangle_fan"
681 									 : primType == (int)PrimitiveRestartCase::PRIMITIVE_TRIANGLES		? "triangles"
682 									 : DE_NULL;
683 
684 			DE_ASSERT(primTypeName != DE_NULL);
685 
686 			TestCaseGroup* primTypeGroup = new TestCaseGroup(m_context, primTypeName, "");
687 			specialCaseGroup->addChild(primTypeGroup);
688 
689 			for (int indexType = 0; indexType < (int)PrimitiveRestartCase::INDEX_LAST; indexType++)
690 			{
691 				const char *indexTypeName = indexType == (int)PrimitiveRestartCase::INDEX_UNSIGNED_BYTE		? "unsigned_byte"
692 										  : indexType == (int)PrimitiveRestartCase::INDEX_UNSIGNED_SHORT	? "unsigned_short"
693 										  : indexType == (int)PrimitiveRestartCase::INDEX_UNSIGNED_INT		? "unsigned_int"
694 										  : DE_NULL;
695 
696 				DE_ASSERT(indexTypeName != DE_NULL);
697 
698 				TestCaseGroup* indexTypeGroup = new TestCaseGroup(m_context, indexTypeName, "");
699 				primTypeGroup->addChild(indexTypeGroup);
700 
701 				for (int function = 0; function < (int)PrimitiveRestartCase::FUNCTION_LAST; function++)
702 				{
703 					const char* functionName = function == (int)PrimitiveRestartCase::FUNCTION_DRAW_ELEMENTS			? "draw_elements"
704 											 : function == (int)PrimitiveRestartCase::FUNCTION_DRAW_ELEMENTS_INSTANCED	? "draw_elements_instanced"
705 											 : function == (int)PrimitiveRestartCase::FUNCTION_DRAW_RANGE_ELEMENTS		? "draw_range_elements"
706 											 : DE_NULL;
707 
708 					DE_ASSERT(functionName != DE_NULL);
709 
710 					indexTypeGroup->addChild(new PrimitiveRestartCase(m_context,
711 																	  functionName,
712 																	  "",
713 																	  (PrimitiveRestartCase::PrimitiveType)primType,
714 																	  (PrimitiveRestartCase::IndexType)indexType,
715 																	  (PrimitiveRestartCase::Function)function,
716 																	  isRestartBeginCase,
717 																	  isRestartEndCase,
718 																	  isDuplicateRestartCase));
719 				}
720 			}
721 		}
722 	}
723 }
724 
725 } // Functional
726 } // gles3
727 } // deqp
728