• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*-------------------------------------------------------------------------
2  * OpenGL Conformance Test Suite
3  * -----------------------------
4  *
5  * Copyright (c) 2014-2016 The Khronos Group Inc.
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
22  */ /*-------------------------------------------------------------------*/
23 
24 #include "esextcTessellationShaderPoints.hpp"
25 #include "gluContextInfo.hpp"
26 #include "gluDefs.hpp"
27 #include "glwEnums.hpp"
28 #include "glwFunctions.hpp"
29 #include "tcuTestLog.hpp"
30 
31 namespace glcts
32 {
33 
34 const unsigned int TessellationShaderPointsgl_PointSize::m_rt_height =
35 	16; /* note: update shaders if you change this value */
36 const unsigned int TessellationShaderPointsgl_PointSize::m_rt_width =
37 	16; /* note: update shaders if you change this value */
38 
39 /** Constructor
40  *
41  * @param context Test context
42  **/
TessellationShaderPointsTests(glcts::Context & context,const ExtParameters & extParams)43 TessellationShaderPointsTests::TessellationShaderPointsTests(glcts::Context& context, const ExtParameters& extParams)
44 	: TestCaseGroupBase(context, extParams, "tessellation_shader_point_mode", "Verifies point mode functionality")
45 {
46 	/* No implementation needed */
47 }
48 
49 /**
50  * Initializes test groups for geometry shader tests
51  **/
init(void)52 void TessellationShaderPointsTests::init(void)
53 {
54 	addChild(new glcts::TessellationShaderPointsgl_PointSize(m_context, m_extParams));
55 	addChild(new glcts::TessellationShaderPointsVerification(m_context, m_extParams));
56 }
57 
58 /** Constructor
59  *
60  * @param context       Test context
61  * @param name          Test case's name
62  * @param description   Test case's desricption
63  **/
TessellationShaderPointsgl_PointSize(Context & context,const ExtParameters & extParams)64 TessellationShaderPointsgl_PointSize::TessellationShaderPointsgl_PointSize(Context&				context,
65 																		   const ExtParameters& extParams)
66 	: TestCaseBase(context, extParams, "point_rendering", "Verifies point size used to render points is taken from"
67 														  " the right stage")
68 	, m_fbo_id(0)
69 	, m_to_id(0)
70 	, m_vao_id(0)
71 {
72 	/* Left blank on purpose */
73 }
74 
75 /** Deinitializes all ES objects created for the test. */
deinit()76 void TessellationShaderPointsgl_PointSize::deinit()
77 {
78 	/** Call base class' deinit() function */
79 	TestCaseBase::deinit();
80 
81 	if (!m_is_tessellation_shader_supported)
82 	{
83 		return;
84 	}
85 
86 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
87 
88 	if (!glu::isContextTypeES(m_context.getRenderContext().getType()))
89 	{
90 		/* Disable point size */
91 		gl.disable(GL_PROGRAM_POINT_SIZE);
92 	}
93 
94 	/* Reset the program object */
95 	gl.useProgram(0);
96 
97 	/* Revert GL_PATCH_VERTICES_EXT to default value */
98 	gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 3);
99 
100 	/* Unbind vertex array object */
101 	gl.bindVertexArray(0);
102 
103 	/* Deinitialize test-specific objects */
104 	for (_tests_iterator it = m_tests.begin(); it != m_tests.end(); ++it)
105 	{
106 		const _test_descriptor& test = *it;
107 
108 		if (test.fs_id != 0)
109 		{
110 			gl.deleteShader(test.fs_id);
111 		}
112 
113 		if (test.gs_id != 0)
114 		{
115 			gl.deleteShader(test.gs_id);
116 		}
117 
118 		if (test.po_id != 0)
119 		{
120 			gl.deleteProgram(test.po_id);
121 		}
122 
123 		if (test.tes_id != 0)
124 		{
125 			gl.deleteShader(test.tes_id);
126 		}
127 
128 		if (test.tcs_id != 0)
129 		{
130 			gl.deleteShader(test.tcs_id);
131 		}
132 
133 		if (test.vs_id != 0)
134 		{
135 			gl.deleteShader(test.vs_id);
136 		}
137 	}
138 	m_tests.clear();
139 
140 	if (m_fbo_id != 0)
141 	{
142 		gl.deleteFramebuffers(1, &m_fbo_id);
143 
144 		m_fbo_id = 0;
145 	}
146 
147 	if (m_to_id != 0)
148 	{
149 		gl.deleteTextures(1, &m_to_id);
150 
151 		m_to_id = 0;
152 	}
153 
154 	if (m_vao_id != 0)
155 	{
156 		gl.deleteVertexArrays(1, &m_vao_id);
157 
158 		m_vao_id = 0;
159 	}
160 }
161 
162 /** Initializes all ES objects that will be used for the test. */
initTest()163 void TessellationShaderPointsgl_PointSize::initTest()
164 {
165 	/* The test should only execute if maximum point size is at least 2 */
166 	const glw::Functions& gl						 = m_context.getRenderContext().getFunctions();
167 	glw::GLint			  gl_max_point_size_value[2] = { 0 };
168 	const int			  min_max_point_size		 = 2;
169 
170 	if (!glu::isContextTypeES(m_context.getRenderContext().getType()))
171 	{
172 		gl.getIntegerv(GL_POINT_SIZE_RANGE, gl_max_point_size_value);
173 		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() for GL_POINT_SIZE_RANGE failed.");
174 	}
175 	else
176 	{
177 		gl.getIntegerv(GL_ALIASED_POINT_SIZE_RANGE, gl_max_point_size_value);
178 		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() for GL_ALIASED_POINT_SIZE_RANGE failed.");
179 	}
180 
181 	if (gl_max_point_size_value[1] < min_max_point_size)
182 	{
183 		throw tcu::NotSupportedError("Maximum point size is lower than 2.");
184 	}
185 
186 	/* The test requires EXT_tessellation_shader, EXT_tessellation_shader_point_size */
187 	if (!m_is_tessellation_shader_supported || !m_is_tessellation_shader_point_size_supported)
188 	{
189 		throw tcu::NotSupportedError("At least one of the required extensions is not supported.");
190 	}
191 
192 	/* Initialize vertex array object */
193 	gl.genVertexArrays(1, &m_vao_id);
194 	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
195 
196 	gl.bindVertexArray(m_vao_id);
197 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
198 
199 	/* Initialize fs+gs+vs test descriptor */
200 	if (m_is_geometry_shader_extension_supported)
201 	{
202 		_test_descriptor pass_fs_gs_tes_vs;
203 
204 		/* Configure shader bodies */
205 		pass_fs_gs_tes_vs.fs_body = "${VERSION}\n"
206 									"\n"
207 									"precision highp float;\n"
208 									"\n"
209 									"in  vec4 color;\n"
210 									"out vec4 result;\n"
211 									"\n"
212 									"void main()\n"
213 									"{\n"
214 									"    result = color;\n"
215 									"}\n";
216 
217 		pass_fs_gs_tes_vs.gs_body = "${VERSION}\n"
218 									"\n"
219 									"${GEOMETRY_SHADER_REQUIRE}\n"
220 									"${GEOMETRY_POINT_SIZE_REQUIRE}\n"
221 									"\n"
222 									"layout(points)                 in;\n"
223 									"layout(points, max_vertices=5) out;\n"
224 									"\n"
225 									"out vec4 color;\n"
226 									"\n"
227 									"void main()\n"
228 									"{\n"
229 									"    const float point_dx = 2.0 / 16.0 /* rendertarget width */;\n"
230 									"    const float point_dy = 2.0 / 16.0 /* rendertarget_height */;\n"
231 									"\n"
232 									/* Center */
233 									"    color        = vec4(0.1, 0.2, 0.3, 0.4);\n"
234 									"    gl_PointSize = 2.0;\n"
235 									"    gl_Position  = vec4(point_dx + 0.0, point_dy + 0.0, 0.0, 1.0);\n"
236 									"    EmitVertex();\n"
237 									"\n"
238 									/* Top-left corner */
239 									"    color        = vec4(0.2, 0.3, 0.4, 0.5);\n"
240 									"    gl_PointSize = 2.0;\n"
241 									"    gl_Position  = vec4(point_dx - 1.0, -point_dy + 1.0, 0.0, 1.0);\n"
242 									"    EmitVertex();\n"
243 									"\n"
244 									/* Top-right corner */
245 									"    color        = vec4(0.3, 0.4, 0.5, 0.6);\n"
246 									"    gl_PointSize = 2.0;\n"
247 									"    gl_Position  = vec4(-point_dx + 1.0, -point_dy + 1.0, 0.0, 1.0);\n"
248 									"    EmitVertex();\n"
249 									"\n"
250 									/* Bottom-left corner */
251 									"    color        = vec4(0.4, 0.5, 0.6, 0.7);\n"
252 									"    gl_PointSize = 2.0;\n"
253 									"    gl_Position  = vec4(point_dx - 1.0, point_dy - 1.0, 0.0, 1.0);\n"
254 									"    EmitVertex();\n"
255 									"\n"
256 									/* Bottom-right corner */
257 									"    color        = vec4(0.5, 0.6, 0.7, 0.8);\n"
258 									"    gl_PointSize = 2.0;\n"
259 									"    gl_Position  = vec4(-point_dx + 1.0, point_dy - 1.0, 0.0, 1.0);\n"
260 									"    EmitVertex();\n"
261 									"\n"
262 									"}\n";
263 
264 		pass_fs_gs_tes_vs.tes_body = "${VERSION}\n"
265 									 "\n"
266 									 "${TESSELLATION_SHADER_REQUIRE}\n"
267 									 "${TESSELLATION_POINT_SIZE_REQUIRE}\n"
268 									 "\n"
269 									 "layout(isolines, point_mode) in;\n"
270 									 "\n"
271 									 "void main()\n"
272 									 "{\n"
273 									 "    gl_PointSize = 0.1;\n"
274 									 "}\n";
275 
276 		pass_fs_gs_tes_vs.tcs_body = "${VERSION}\n"
277 									 "\n"
278 									 "${TESSELLATION_SHADER_REQUIRE}\n"
279 									 "${TESSELLATION_POINT_SIZE_REQUIRE}\n"
280 									 "\n"
281 									 "layout(vertices=1) out;\n"
282 									 "\n"
283 									 "void main()\n"
284 									 "{\n"
285 									 "    gl_out[gl_InvocationID].gl_Position =\n"
286 									 "        gl_in[gl_InvocationID].gl_Position;\n"
287 									 "    gl_out[gl_InvocationID].gl_PointSize =\n"
288 									 "        gl_in[gl_InvocationID].gl_PointSize;\n"
289 									 "\n"
290 									 "    gl_TessLevelOuter[0] = 1.0;\n"
291 									 "    gl_TessLevelOuter[1] = 1.0;\n"
292 									 "    gl_TessLevelOuter[2] = 1.0;\n"
293 									 "    gl_TessLevelOuter[3] = 1.0;\n"
294 									 "    gl_TessLevelInner[0] = 1.0;\n"
295 									 "    gl_TessLevelInner[1] = 1.0;\n"
296 									 "}\n";
297 
298 		pass_fs_gs_tes_vs.vs_body = "${VERSION}\n"
299 									"\n"
300 									"void main()\n"
301 									"{\n"
302 									"    gl_PointSize = 0.01;\n"
303 									"}\n";
304 
305 		pass_fs_gs_tes_vs.draw_call_count = 1;
306 
307 		/* Store the descriptor in a vector that will be used by iterate() */
308 		m_tests.push_back(pass_fs_gs_tes_vs);
309 	} /* if (m_is_geometry_shader_extension_supported) */
310 
311 	/* Initialize fs+te+vs test descriptor */
312 	if (m_is_tessellation_shader_supported)
313 	{
314 		_test_descriptor pass_fs_tes_vs;
315 
316 		/* Configure shader bodies */
317 		pass_fs_tes_vs.fs_body = "${VERSION}\n"
318 								 "\n"
319 								 "precision highp float;\n"
320 								 "\n"
321 								 "in  vec4 result_color;\n"
322 								 "out vec4 result;\n"
323 								 "\n"
324 								 "void main()\n"
325 								 "{\n"
326 								 "    result = result_color;\n"
327 								 "}\n";
328 
329 		pass_fs_tes_vs.tes_body = "${VERSION}\n"
330 								  "\n"
331 								  "${TESSELLATION_SHADER_REQUIRE}\n"
332 								  "${TESSELLATION_POINT_SIZE_REQUIRE}\n"
333 								  "\n"
334 								  "layout(isolines, point_mode) in;\n"
335 								  "\n"
336 								  "in  vec4 tcColor[];\n"
337 								  "out vec4 result_color;\n"
338 								  "\n"
339 								  "void main()\n"
340 								  "{\n"
341 								  "    gl_PointSize = 2.0;\n"
342 								  "    gl_Position  = gl_in[0].gl_Position;\n"
343 								  "    result_color = tcColor[0];\n"
344 								  "}\n";
345 
346 		pass_fs_tes_vs.tcs_body = "${VERSION}\n"
347 								  "\n"
348 								  "${TESSELLATION_SHADER_REQUIRE}\n"
349 								  "${TESSELLATION_POINT_SIZE_REQUIRE}\n"
350 								  "\n"
351 								  "layout(vertices=1) out;\n"
352 								  "\n"
353 								  "in  vec4 color[];\n"
354 								  "out vec4 tcColor[];\n"
355 								  "\n"
356 								  "void main()\n"
357 								  "{\n"
358 								  "    tcColor[gl_InvocationID] = color[gl_InvocationID];\n"
359 								  "    gl_out[gl_InvocationID].gl_Position =\n"
360 								  "        gl_in[gl_InvocationID].gl_Position;\n"
361 								  "    gl_out[gl_InvocationID].gl_PointSize =\n"
362 								  "        gl_in[gl_InvocationID].gl_PointSize;\n"
363 								  "\n"
364 								  "    gl_TessLevelOuter[0] = 1.0;\n"
365 								  "    gl_TessLevelOuter[1] = 1.0;\n"
366 								  "    gl_TessLevelOuter[2] = 1.0;\n"
367 								  "    gl_TessLevelOuter[3] = 1.0;\n"
368 								  "    gl_TessLevelInner[0] = 1.0;\n"
369 								  "    gl_TessLevelInner[1] = 1.0;\n"
370 								  "}\n";
371 
372 		pass_fs_tes_vs.vs_body = "${VERSION}\n"
373 								 "\n"
374 								 "out vec4 color;\n"
375 								 "\n"
376 								 "void main()\n"
377 								 "{\n"
378 								 "    const float point_dx = 2.0 / 16.0 /* rendertarget width */;\n"
379 								 "    const float point_dy = 2.0 / 16.0 /* rendertarget_height */;\n"
380 								 "\n"
381 								 "    gl_PointSize = 0.1;\n"
382 								 "\n"
383 								 "    switch (gl_VertexID)\n"
384 								 "    {\n"
385 								 "        case 0:\n"
386 								 "        {\n"
387 								 /* Center */
388 								 "            color       = vec4(0.1, 0.2, 0.3, 0.4);\n"
389 								 "            gl_Position = vec4(point_dx + 0.0, point_dy + 0.0, 0.0, 1.0);\n"
390 								 "\n"
391 								 "            break;\n"
392 								 "        }\n"
393 								 "\n"
394 								 "        case 1:\n"
395 								 "        {\n"
396 								 /* Top-left corner */
397 								 "            color       = vec4(0.2, 0.3, 0.4, 0.5);\n"
398 								 "            gl_Position = vec4(point_dx - 1.0, -point_dy + 1.0, 0.0, 1.0);\n"
399 								 "\n"
400 								 "            break;\n"
401 								 "        }\n"
402 								 "\n"
403 								 "        case 2:\n"
404 								 "        {\n"
405 								 /* Top-right corner */
406 								 "            color       = vec4(0.3, 0.4, 0.5, 0.6);\n"
407 								 "            gl_Position = vec4(-point_dx + 1.0, -point_dy + 1.0, 0.0, 1.0);\n"
408 								 "\n"
409 								 "            break;\n"
410 								 "        }\n"
411 								 "\n"
412 								 "        case 3:\n"
413 								 "        {\n"
414 								 /* Bottom-left corner */
415 								 "            color       = vec4(0.4, 0.5, 0.6, 0.7);\n"
416 								 "            gl_Position = vec4(point_dx - 1.0, point_dy - 1.0, 0.0, 1.0);\n"
417 								 "\n"
418 								 "            break;\n"
419 								 "        }\n"
420 								 "\n"
421 								 "        case 4:\n"
422 								 "        {\n"
423 								 /* Bottom-right corner */
424 								 "            color       = vec4(0.5, 0.6, 0.7, 0.8);\n"
425 								 "            gl_Position = vec4(-point_dx + 1.0, point_dy - 1.0, 0.0, 1.0);\n"
426 								 "\n"
427 								 "            break;\n"
428 								 "        }\n"
429 								 "    }\n"
430 								 "}\n";
431 
432 		pass_fs_tes_vs.draw_call_count = 5; /* points in total */
433 
434 		/* Store the descriptor in a vector that will be used by iterate() */
435 		m_tests.push_back(pass_fs_tes_vs);
436 	} /* if (m_is_tessellation_shader_supported) */
437 
438 	/* Set up a color texture we will be rendering to */
439 	gl.genTextures(1, &m_to_id);
440 	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() failed");
441 
442 	gl.bindTexture(GL_TEXTURE_2D, m_to_id);
443 	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() failed");
444 
445 	gl.texStorage2D(GL_TEXTURE_2D, 1 /* levels */, GL_RGBA8, m_rt_width, m_rt_height);
446 	GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() failed");
447 
448 	/* Set up a FBO we'll use for rendering */
449 	gl.genFramebuffers(1, &m_fbo_id);
450 	GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() failed");
451 
452 	gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id);
453 	GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() failed");
454 
455 	gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_id, 0 /* level */);
456 	GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() failed");
457 
458 	if (!glu::isContextTypeES(m_context.getRenderContext().getType()))
459 	{
460 		/* Enable point size */
461 		gl.enable(GL_PROGRAM_POINT_SIZE);
462 	}
463 
464 	/* We're good to execute the test! */
465 }
466 
467 /** Executes the test.
468  *
469  *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
470  *
471  *  Note the function throws exception should an error occur!
472  *
473  *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
474  **/
iterate(void)475 tcu::TestNode::IterateResult TessellationShaderPointsgl_PointSize::iterate(void)
476 {
477 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
478 
479 	initTest();
480 
481 	gl.patchParameteri(m_glExtTokens.PATCH_VERTICES, 1);
482 	GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteriEXT() failed");
483 
484 	/* Iterate through all test descriptors.. */
485 	for (_tests_iterator test_iterator = m_tests.begin(); test_iterator != m_tests.end(); test_iterator++)
486 	{
487 		_test_descriptor& test = *test_iterator;
488 
489 		/* Generate all shader objects we'll need */
490 		if (test.fs_body != NULL)
491 		{
492 			test.fs_id = gl.createShader(GL_FRAGMENT_SHADER);
493 		}
494 
495 		if (test.gs_body != NULL)
496 		{
497 			test.gs_id = gl.createShader(m_glExtTokens.GEOMETRY_SHADER);
498 		}
499 
500 		if (test.tes_body != NULL)
501 		{
502 			test.tes_id = gl.createShader(m_glExtTokens.TESS_EVALUATION_SHADER);
503 		}
504 
505 		if (test.tcs_body != NULL)
506 		{
507 			test.tcs_id = gl.createShader(m_glExtTokens.TESS_CONTROL_SHADER);
508 		}
509 
510 		if (test.vs_body != NULL)
511 		{
512 			test.vs_id = gl.createShader(GL_VERTEX_SHADER);
513 		}
514 		GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call(s) failed");
515 
516 		/* Generate a test program object before we continue */
517 		test.po_id = gl.createProgram();
518 		GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed");
519 
520 		bool link_success =
521 			buildProgram(test.po_id, test.fs_id, test.fs_id ? 1 : 0, &test.fs_body, test.gs_id, test.gs_id ? 1 : 0,
522 						 &test.gs_body, test.tes_id, test.tes_id ? 1 : 0, &test.tes_body, test.tcs_id,
523 						 test.tcs_id ? 1 : 0, &test.tcs_body, test.vs_id, test.vs_id ? 1 : 0, &test.vs_body);
524 
525 		if (!link_success)
526 		{
527 			TCU_FAIL("Program linking failed");
528 		}
529 
530 		/* Prepare for rendering */
531 		gl.clearColor(0.0f /* red */, 0.0f /* green */, 0.0f /* blue */, 0.0f /* alpha */);
532 		GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor() failed");
533 
534 		gl.clear(GL_COLOR_BUFFER_BIT);
535 		GLU_EXPECT_NO_ERROR(gl.getError(), "glClear(GL_COLOR_BUFFER_BIT) failed");
536 
537 		gl.viewport(0 /* x */, 0 /* x */, m_rt_width, m_rt_height);
538 		GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() failed");
539 
540 		gl.useProgram(test.po_id);
541 		GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() failed");
542 
543 		/* Render */
544 		gl.drawArrays(m_glExtTokens.PATCHES, 0 /* first */, test.draw_call_count);
545 		GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() failed");
546 
547 		/* Read back the rendered data */
548 		unsigned char buffer[m_rt_width * m_rt_height * 4 /* components */] = { 0 };
549 
550 		gl.readPixels(0, /* x */
551 					  0, /* y */
552 					  m_rt_width, m_rt_height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
553 		GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() failed");
554 
555 		/* Verify all 5 points were rendered correctly */
556 		const float		   epsilon		= (float)1.0f / 255.0f;
557 		const unsigned int pixel_size   = 4; /* components, GL_UNSIGNED_BYTE */
558 		const float		   point_data[] = {
559 			/* x */ /* y */ /* r */ /* g */ /* b */ /* a */
560 			0.5f, 0.5f, 0.1f, 0.2f, 0.3f, 0.4f,		/* center */
561 			0.0f, 1.0f, 0.2f, 0.3f, 0.4f, 0.5f,		/* top-left */
562 			1.0f, 1.0f, 0.3f, 0.4f, 0.5f, 0.6f,		/* top-right */
563 			0.0f, 0.0f, 0.4f, 0.5f, 0.6f, 0.7f,		/* bottom-left */
564 			1.0f, 0.0f, 0.5f, 0.6f, 0.7f, 0.8f		/* bottom-right */
565 		};
566 		const unsigned int row_size			  = pixel_size * m_rt_width;
567 		const unsigned int n_fields_per_point = 6;
568 		const unsigned int n_points			  = sizeof(point_data) / sizeof(point_data[0]) / n_fields_per_point;
569 
570 		for (unsigned int n_point = 0; n_point < n_points; ++n_point)
571 		{
572 			int   x = (int)(point_data[n_point * n_fields_per_point + 0] * float(m_rt_width - 1) + 0.5f);
573 			int   y = (int)(point_data[n_point * n_fields_per_point + 1] * float(m_rt_height - 1) + 0.5f);
574 			float expected_color_r = point_data[n_point * n_fields_per_point + 2];
575 			float expected_color_g = point_data[n_point * n_fields_per_point + 3];
576 			float expected_color_b = point_data[n_point * n_fields_per_point + 4];
577 			float expected_color_a = point_data[n_point * n_fields_per_point + 5];
578 
579 			const unsigned char* rendered_color_ubyte_ptr = buffer + row_size * y + x * pixel_size;
580 			const float			 rendered_color_r		  = float(rendered_color_ubyte_ptr[0]) / 255.0f;
581 			const float			 rendered_color_g		  = float(rendered_color_ubyte_ptr[1]) / 255.0f;
582 			const float			 rendered_color_b		  = float(rendered_color_ubyte_ptr[2]) / 255.0f;
583 			const float			 rendered_color_a		  = float(rendered_color_ubyte_ptr[3]) / 255.0f;
584 
585 			/* Compare the pixels */
586 			if (de::abs(expected_color_r - rendered_color_r) > epsilon ||
587 				de::abs(expected_color_g - rendered_color_g) > epsilon ||
588 				de::abs(expected_color_b - rendered_color_b) > epsilon ||
589 				de::abs(expected_color_a - rendered_color_a) > epsilon)
590 			{
591 				m_testCtx.getLog() << tcu::TestLog::Message << "Pixel data comparison failed; expected: "
592 								   << "(" << expected_color_r << ", " << expected_color_g << ", " << expected_color_b
593 								   << ", " << expected_color_a << ") rendered: "
594 								   << "(" << rendered_color_r << ", " << rendered_color_g << ", " << rendered_color_b
595 								   << ", " << rendered_color_a << ") epsilon: " << epsilon << tcu::TestLog::EndMessage;
596 
597 				TCU_FAIL("Pixel data comparison failed");
598 			}
599 		} /* for (all points) */
600 	}	 /* for (all tests) */
601 
602 	/* All done */
603 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
604 	return STOP;
605 }
606 
607 /** Constructor
608  *
609  * @param context Test context
610  **/
TessellationShaderPointsVerification(Context & context,const ExtParameters & extParams)611 TessellationShaderPointsVerification::TessellationShaderPointsVerification(Context&				context,
612 																		   const ExtParameters& extParams)
613 	: TestCaseBase(context, extParams, "points_verification",
614 				   "Verifies points generated by the tessellator unit do not duplicate "
615 				   "and that their amount is correct")
616 	, m_utils(DE_NULL)
617 	, m_vao_id(0)
618 {
619 	/* Left blank on purpose */
620 }
621 
622 /* Deinitializes all ES Instances generated for the test */
deinit()623 void TessellationShaderPointsVerification::deinit()
624 {
625 	/* Call base class' deinit() */
626 	TestCaseBase::deinit();
627 
628 	if (!m_is_tessellation_shader_supported)
629 	{
630 		return;
631 	}
632 
633 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
634 
635 	/* Unbind vertex array object */
636 	gl.bindVertexArray(0);
637 
638 	/* Delete utils instances */
639 	if (m_utils != DE_NULL)
640 	{
641 		delete m_utils;
642 
643 		m_utils = DE_NULL;
644 	}
645 
646 	/* Delete vertex array object */
647 	if (m_vao_id != 0)
648 	{
649 		gl.deleteVertexArrays(1, &m_vao_id);
650 
651 		m_vao_id = 0;
652 	}
653 }
654 
655 /** Initializes ES objects necessary to run the test. */
initTest()656 void TessellationShaderPointsVerification::initTest()
657 {
658 	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
659 
660 	/* Skip if required extensions are not supported. */
661 	if (!m_is_tessellation_shader_supported)
662 	{
663 		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
664 	}
665 
666 	/* Initialize and bind vertex array object */
667 	gl.genVertexArrays(1, &m_vao_id);
668 	GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
669 
670 	gl.bindVertexArray(m_vao_id);
671 	GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
672 
673 	/* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
674 	glw::GLint gl_max_tess_gen_level_value = 0;
675 
676 	gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
677 	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");
678 
679 	/* Initialize all test iterations */
680 	const _tessellation_primitive_mode primitive_modes[] = { TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS,
681 															 TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES,
682 															 TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES };
683 	const unsigned int n_primitive_modes = sizeof(primitive_modes) / sizeof(primitive_modes[0]);
684 
685 	const _tessellation_shader_vertex_spacing vertex_spacings[] = {
686 		TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL,
687 		TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN,
688 	};
689 	const unsigned int n_vertex_spacings = sizeof(vertex_spacings) / sizeof(vertex_spacings[0]);
690 
691 	for (unsigned int n_primitive_mode = 0; n_primitive_mode < n_primitive_modes; ++n_primitive_mode)
692 	{
693 		_tessellation_levels_set	 levels_set;
694 		_tessellation_primitive_mode primitive_mode = primitive_modes[n_primitive_mode];
695 
696 		levels_set = TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode(
697 			primitive_mode, gl_max_tess_gen_level_value,
698 			TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES);
699 
700 		for (unsigned int n_vertex_spacing = 0; n_vertex_spacing < n_vertex_spacings; ++n_vertex_spacing)
701 		{
702 			_tessellation_shader_vertex_spacing vertex_spacing = vertex_spacings[n_vertex_spacing];
703 
704 			for (_tessellation_levels_set_const_iterator levels_set_iterator = levels_set.begin();
705 				 levels_set_iterator != levels_set.end(); levels_set_iterator++)
706 			{
707 				const _tessellation_levels& levels = *levels_set_iterator;
708 
709 				/* Skip border cases that this test cannot handle */
710 				if ((primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS ||
711 					 primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES) &&
712 					vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD &&
713 					(levels.inner[0] <= 1 || levels.inner[1] <= 1))
714 				{
715 					continue;
716 				}
717 
718 				/* Initialize a test run descriptor for the iteration-specific properties */
719 				_run run;
720 
721 				memcpy(run.inner, levels.inner, sizeof(run.inner));
722 				memcpy(run.outer, levels.outer, sizeof(run.outer));
723 
724 				run.primitive_mode = primitive_mode;
725 				run.vertex_spacing = vertex_spacing;
726 
727 				m_runs.push_back(run);
728 			} /* for (all level sets) */
729 		}	 /* for (all vertex spacing modes) */
730 	}		  /* for (all primitive modes) */
731 
732 	/* Initialize utils instance.
733 	 */
734 	m_utils = new TessellationShaderUtils(gl, this);
735 }
736 
737 /** Executes the test.
738  *
739  *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
740  *
741  *  Note the function throws exception should an error occur!
742  *
743  *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
744  **/
iterate(void)745 tcu::TestNode::IterateResult TessellationShaderPointsVerification::iterate(void)
746 {
747 	initTest();
748 
749 	/* Do not execute if required extensions are not supported. */
750 	if (!m_is_tessellation_shader_supported)
751 	{
752 		throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
753 	}
754 
755 	/* Iterate through all the test descriptors */
756 	for (std::vector<_run>::const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
757 	{
758 		const _run&		  run = *run_iterator;
759 		std::vector<char> run_data;
760 		unsigned int	  run_n_vertices = 0;
761 
762 		run_data = m_utils->getDataGeneratedByTessellator(run.inner, true, /* point_mode */
763 														  run.primitive_mode, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
764 														  run.vertex_spacing, run.outer);
765 
766 		run_n_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(run.primitive_mode, run.inner, run.outer,
767 																			run.vertex_spacing, true); /* point_mode */
768 
769 		/* First, make sure a valid amount of duplicate vertices was found for a single data set */
770 		verifyCorrectAmountOfDuplicateVertices(run, &run_data[0], run_n_vertices);
771 
772 		/* Now, verify that amount of generated vertices is correct, given
773 		 * tessellation shader stage configuration */
774 		verifyCorrectAmountOfVertices(run, &run_data[0], run_n_vertices);
775 	} /* for (all tests) */
776 
777 	/* All done */
778 	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
779 	return STOP;
780 }
781 
782 /** Verifies that a correct amount of vertices was generated, given test iteration-specific properties.
783  *  Throws a TestError exception in if an incorrect amount of vertices was generated by the tessellator.
784  *
785  *  @param run            Run descriptor.
786  *  @param run_data       Data generated for the run.
787  *  @param run_n_vertices Amount of vertices present at @param run_data.
788  */
verifyCorrectAmountOfVertices(const _run & run,const void * run_data,unsigned int run_n_vertices)789 void TessellationShaderPointsVerification::verifyCorrectAmountOfVertices(const _run& run, const void* run_data,
790 																		 unsigned int run_n_vertices)
791 {
792 	(void)run_data;
793 
794 	const float			  epsilon					   = 1e-5f;
795 	const glw::Functions& gl						   = m_context.getRenderContext().getFunctions();
796 	unsigned int		  n_expected_vertices		   = 0;
797 	float				  post_vs_inner_tess_levels[2] = { 0.0f };
798 	float				  post_vs_outer_tess_levels[4] = { 0.0f };
799 
800 	/* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value*/
801 	glw::GLint gl_max_tess_gen_level_value = 0;
802 
803 	gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
804 	GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");
805 
806 	/* Determine vertex spacing that the tessellator should have used for current primitive mode */
807 	glw::GLfloat						actual_inner_levels[2]  = { 0.0f };
808 	_tessellation_shader_vertex_spacing actual_vs_mode			= run.vertex_spacing;
809 	glw::GLfloat						clamped_inner_levels[2] = { 0.0f };
810 
811 	memcpy(actual_inner_levels, run.inner, sizeof(run.inner));
812 
813 	TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
814 		run.vertex_spacing, actual_inner_levels[0], gl_max_tess_gen_level_value, clamped_inner_levels + 0,
815 		DE_NULL); /* out_clamped_and_rounded */
816 
817 	TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
818 		run.vertex_spacing, actual_inner_levels[1], gl_max_tess_gen_level_value, clamped_inner_levels + 1,
819 		DE_NULL); /* out_clamped_and_rounded */
820 
821 	if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES)
822 	{
823 		/* For isolines tessellation, outer[1] is subdivided as per specified vertex spacing as specified.
824 		 * outer[0] should be subdivided using equal vertex spacing.
825 		 *
826 		 * This is owing to the following language in the spec (* marks important subtleties):
827 		 *
828 		 * The *u==0* and *u==1* edges of the rectangle are subdivided according to the first outer
829 		 * tessellation level. For the purposes of *this* subdivision, the tessellation spacing mode
830 		 * is ignored and treated as "equal_spacing".
831 		 */
832 		TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(TESSELLATION_SHADER_VERTEX_SPACING_EQUAL,
833 																		run.outer[0], gl_max_tess_gen_level_value,
834 																		DE_NULL, /* out_clamped */
835 																		post_vs_outer_tess_levels);
836 
837 		TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
838 			actual_vs_mode, run.outer[1], gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */
839 			post_vs_outer_tess_levels + 1);
840 	}
841 
842 	if (run.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS)
843 	{
844 		/* As per extension spec:
845 		 *
846 		 * if either clamped inner tessellation level is one, that tessellation level
847 		 * is treated as though it were originally specified as 1+epsilon, which would
848 		 * rounded up to result in a two- or three-segment subdivision according to the
849 		 * tessellation spacing.
850 		 *
851 		 **/
852 		if (de::abs(clamped_inner_levels[0] - 1.0f) < epsilon)
853 		{
854 			TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
855 				run.vertex_spacing, clamped_inner_levels[0] + 1.0f, /* epsilon */
856 				gl_max_tess_gen_level_value, DE_NULL,				/* out_clamped */
857 				actual_inner_levels + 0);
858 		}
859 
860 		if (de::abs(clamped_inner_levels[1] - 1.0f) < epsilon)
861 		{
862 			TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
863 				run.vertex_spacing, clamped_inner_levels[1] + 1.0f, /* epsilon */
864 				gl_max_tess_gen_level_value, DE_NULL,				/* out_clamped */
865 				actual_inner_levels + 1);
866 		}
867 	}
868 
869 	/* Retrieve tessellation level values, taking vertex spacing setting into account */
870 	for (int n = 0; n < 2 /* inner tessellation level values */; ++n)
871 	{
872 		TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
873 			actual_vs_mode, actual_inner_levels[n], gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */
874 			post_vs_inner_tess_levels + n);
875 	}
876 
877 	if (run.primitive_mode != TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES)
878 	{
879 		for (int n = 0; n < 4 /* outer tessellation level values */; ++n)
880 		{
881 			TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
882 				actual_vs_mode, run.outer[n], gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */
883 				post_vs_outer_tess_levels + n);
884 		}
885 	}
886 
887 	/* Calculate amount of vertices that should be generated in point mode */
888 	switch (run.primitive_mode)
889 	{
890 	case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES:
891 	{
892 		n_expected_vertices = int(post_vs_outer_tess_levels[0]) * int(post_vs_outer_tess_levels[1] + 1);
893 
894 		break;
895 	}
896 
897 	case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
898 	{
899 		n_expected_vertices = /* outer quad */
900 			int(post_vs_outer_tess_levels[0]) + int(post_vs_outer_tess_levels[1]) + int(post_vs_outer_tess_levels[2]) +
901 			int(post_vs_outer_tess_levels[3]) +
902 			/* inner quad */
903 			(int(post_vs_inner_tess_levels[0]) - 1) * (int(post_vs_inner_tess_levels[1]) - 1);
904 
905 		break;
906 	}
907 
908 	case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
909 	{
910 		/* If the first inner tessellation level and all three outer tessellation
911 		 * levels are exactly one after clamping and rounding, only a single triangle
912 		 * with (u,v,w) coordinates of (0,0,1), (1,0,0), and (0,1,0) is generated.
913 		 */
914 		if (de::abs(run.inner[0] - 1.0f) < epsilon && de::abs(run.outer[0] - 1.0f) < epsilon &&
915 			de::abs(run.outer[1] - 1.0f) < epsilon && de::abs(run.outer[2] - 1.0f) < epsilon)
916 		{
917 			n_expected_vertices = 3;
918 		}
919 		else
920 		{
921 			/* If the inner tessellation level is one and any of the outer tessellation
922 			 * levels is greater than one, the inner tessellation level is treated as
923 			 * though it were originally specified as 1+epsilon and will be rounded up to
924 			 * result in a two- or three-segment subdivision according to the
925 			 * tessellation spacing.
926 			 */
927 			if (de::abs(run.inner[0] - 1.0f) < epsilon &&
928 				(run.outer[0] > 1.0f || run.outer[1] > 1.0f || run.outer[2] > 1.0f))
929 			{
930 				TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
931 					run.vertex_spacing, 2.0f, gl_max_tess_gen_level_value, DE_NULL, /* out_clamped */
932 					post_vs_inner_tess_levels);
933 			}
934 
935 			/* Count vertices making up concentric inner triangles */
936 			n_expected_vertices = (int)post_vs_outer_tess_levels[0] + (int)post_vs_outer_tess_levels[1] +
937 								  (int)post_vs_outer_tess_levels[2];
938 
939 			for (int n = (int)post_vs_inner_tess_levels[0]; n >= 0; n -= 2)
940 			{
941 				/* For the outermost inner triangle, the inner triangle is degenerate -
942 				 * a single point at the center of the triangle -- if <n> is two.
943 				 */
944 				if (n == 2)
945 				{
946 					n_expected_vertices++; /* degenerate vertex */
947 
948 					break;
949 				}
950 
951 				/* If <n> is three, the edges of the inner triangle are not subdivided and is
952 				 * the final triangle in the set of concentric triangles.
953 				 */
954 				if (n == 3)
955 				{
956 					n_expected_vertices += 3 /* vertices per triangle */;
957 
958 					break;
959 				}
960 
961 				/* Otherwise, each edge of the inner triangle is divided into <n>-2 segments,
962 				 * with the <n>-1 vertices of this subdivision produced by intersecting the
963 				 * inner edge with lines perpendicular to the edge running through the <n>-1
964 				 * innermost vertices of the subdivision of the outer edge.
965 				 */
966 				if (n >= 2)
967 				{
968 					n_expected_vertices += (n - 2) * 3 /* triangle edges */;
969 				}
970 				else
971 				{
972 					/* Count in the degenerate point instead */
973 					n_expected_vertices++;
974 				}
975 			} /* for (all inner triangles) */
976 		}
977 
978 		break;
979 	}
980 
981 	default:
982 	{
983 		TCU_FAIL("Unrecognized primitive mode");
984 	}
985 	} /* switch (test.primitive_mode) */
986 
987 	/* Compare two values */
988 	if (run_n_vertices != n_expected_vertices)
989 	{
990 		std::string primitive_mode = TessellationShaderUtils::getESTokenForPrimitiveMode(run.primitive_mode);
991 		std::string vertex_spacing = TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);
992 
993 		m_testCtx.getLog() << tcu::TestLog::Message << run_n_vertices
994 						   << " vertices were generated by the tessellator instead of expected " << n_expected_vertices
995 						   << " for primitive mode [" << primitive_mode << "], vertex spacing mode [" << vertex_spacing
996 						   << "], inner tessellation levels:[" << run.inner[0] << ", " << run.inner[1]
997 						   << "], outer tessellation levels:[" << run.outer[0] << ", " << run.outer[1] << ", "
998 						   << run.outer[2] << ", " << run.outer[3] << "], point mode enabled."
999 						   << tcu::TestLog::EndMessage;
1000 
1001 		TCU_FAIL("Amount of vertices generated in point mode was incorrect");
1002 	} /* if (test.n_vertices != n_expected_vertices) */
1003 }
1004 
1005 /** Verifies a valid amount of duplicate vertices is present in the set of coordinates
1006  *  generated by the tessellator, as described by user-provided test iteration descriptor.
1007  *  Throws a TestError exception if the vertex set does not meet the requirements.
1008  *
1009  *  @param test           Test iteration descriptor.
1010  *  @param run_data       Data generated for the run.
1011  *  @param run_n_vertices Amount of vertices present at @param run_data.
1012  **/
verifyCorrectAmountOfDuplicateVertices(const _run & run,const void * run_data,unsigned int run_n_vertices)1013 void TessellationShaderPointsVerification::verifyCorrectAmountOfDuplicateVertices(const _run& run, const void* run_data,
1014 																				  unsigned int run_n_vertices)
1015 {
1016 	const float  epsilon			  = 1e-5f;
1017 	unsigned int n_duplicate_vertices = 0;
1018 
1019 	for (unsigned int n_vertex_a = 0; n_vertex_a < run_n_vertices; ++n_vertex_a)
1020 	{
1021 		const float* vertex_a = (const float*)run_data + n_vertex_a * 3; /* components */
1022 
1023 		for (unsigned int n_vertex_b = n_vertex_a + 1; n_vertex_b < run_n_vertices; ++n_vertex_b)
1024 		{
1025 			const float* vertex_b = (const float*)run_data + n_vertex_b * 3; /* components */
1026 
1027 			if (de::abs(vertex_a[0] - vertex_b[0]) < epsilon && de::abs(vertex_a[1] - vertex_b[1]) < epsilon &&
1028 				de::abs(vertex_a[2] - vertex_b[2]) < epsilon)
1029 			{
1030 				n_duplicate_vertices++;
1031 			}
1032 		} /* for (all vertices) */
1033 	}	 /* for (all vertices) */
1034 
1035 	if (n_duplicate_vertices != 0)
1036 	{
1037 		std::string vertex_spacing = TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);
1038 
1039 		m_testCtx.getLog() << tcu::TestLog::Message << "Duplicate vertices found for the following tesselelation"
1040 													   " configuration: tessellation level:"
1041 													   "["
1042 						   << run.inner[0] << ", " << run.inner[1] << "], "
1043 																	  "outer tessellation level:"
1044 																	  " ["
1045 						   << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", " << run.outer[3]
1046 						   << "], "
1047 						   << "vertex spacing mode:[" << vertex_spacing.c_str() << "]" << tcu::TestLog::EndMessage;
1048 
1049 		TCU_FAIL("Duplicate vertex found");
1050 	}
1051 }
1052 
1053 } /* namespace glcts */
1054