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
31namespace glcts
32{
33
34const unsigned int TessellationShaderPointsgl_PointSize::m_rt_height =
35	16; /* note: update shaders if you change this value */
36const 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 **/
43TessellationShaderPointsTests::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 **/
52void 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 **/
64TessellationShaderPointsgl_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. */
76void 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. */
163void 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 **/
475tcu::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 **/
611TessellationShaderPointsVerification::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 */
623void 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. */
656void 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 **/
745tcu::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 */
789void 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 **/
1013void 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