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
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
iterate(void)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.
draw(int startNdx,int count)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
renderWithRestart(void)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
renderWithoutRestart(void)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
PrimitiveRestartTests(Context & context)627 PrimitiveRestartTests::PrimitiveRestartTests (Context& context)
628 : TestCaseGroup(context, "primitive_restart", "Primitive restart tests")
629 {
630 }
631
~PrimitiveRestartTests(void)632 PrimitiveRestartTests::~PrimitiveRestartTests (void)
633 {
634 }
635
init(void)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