Home | History | Annotate | Download | only in functional
      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 
    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 
    143 PrimitiveRestartCase::~PrimitiveRestartCase (void)
    144 {
    145 	PrimitiveRestartCase::deinit();
    146 }
    147 
    148 void PrimitiveRestartCase::deinit (void)
    149 {
    150 	delete m_program;
    151 	m_program = DE_NULL;
    152 }
    153 
    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 
    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 
    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.
    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 
    215 void 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 * ((float)(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 
    457 PrimitiveRestartCase::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.
    507 void 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 
    568 void 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 
    584 void 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 
    627 PrimitiveRestartTests::PrimitiveRestartTests (Context& context)
    628 	: TestCaseGroup(context, "primitive_restart", "Primitive restart tests")
    629 {
    630 }
    631 
    632 PrimitiveRestartTests::~PrimitiveRestartTests (void)
    633 {
    634 }
    635 
    636 void 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