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 "esextcTessellationShaderQuads.hpp"
25 #include "gluContextInfo.hpp"
26 #include "gluDefs.hpp"
27 #include "glwEnums.hpp"
28 #include "glwFunctions.hpp"
29 #include "tcuTestLog.hpp"
30 #include <algorithm>
31 
32 namespace glcts
33 {
34 
35 /** Constructor
36  *
37  * @param context Test context
38  **/
TessellationShaderQuadsTests(glcts::Context & context,const ExtParameters & extParams)39 TessellationShaderQuadsTests::TessellationShaderQuadsTests(glcts::Context &context, const ExtParameters &extParams)
40     : TestCaseGroupBase(context, extParams, "tessellation_shader_quads_tessellation",
41                         "Verifies quad tessellation functionality")
42 {
43     /* No implementation needed */
44 }
45 
46 /**
47  * Initializes test groups for geometry shader tests
48  **/
init(void)49 void TessellationShaderQuadsTests::init(void)
50 {
51     addChild(new glcts::TessellationShaderQuadsDegenerateCase(m_context, m_extParams));
52     addChild(new glcts::TessellationShaderQuadsInnerTessellationLevelRounding(m_context, m_extParams));
53 }
54 
55 /** Constructor
56  *
57  * @param context Test context
58  **/
TessellationShaderQuadsDegenerateCase(Context & context,const ExtParameters & extParams)59 TessellationShaderQuadsDegenerateCase::TessellationShaderQuadsDegenerateCase(Context &context,
60                                                                              const ExtParameters &extParams)
61     : TestCaseBase(context, extParams, "degenerate_case",
62                    "Verifies that only a single triangle pair covering the outer rectangle"
63                    " is generated, if both clamped inner and outer tessellation levels are "
64                    "set to one.")
65     , m_vao_id(0)
66     , m_utils(DE_NULL)
67 {
68     /* Left blank on purpose */
69 }
70 
71 /** Deinitializes ES objects created for the test. */
deinit()72 void TessellationShaderQuadsDegenerateCase::deinit()
73 {
74     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
75 
76     /* Call base class' deinit() */
77     TestCaseBase::deinit();
78 
79     /* Unbind vertex array object */
80     gl.bindVertexArray(0);
81 
82     /* Deinitialize utils instance */
83     if (m_utils != DE_NULL)
84     {
85         delete m_utils;
86 
87         m_utils = DE_NULL;
88     }
89 
90     /* Delete vertex array object */
91     if (m_vao_id != 0)
92     {
93         gl.deleteVertexArrays(1, &m_vao_id);
94 
95         m_vao_id = 0;
96     }
97 }
98 
99 /** Initializes ES objects necessary to run the test. */
initTest()100 void TessellationShaderQuadsDegenerateCase::initTest()
101 {
102     /* Skip if required extensions are not supported. */
103     if (!m_is_tessellation_shader_supported)
104     {
105         throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
106     }
107 
108     /* Initialize Utils instance */
109     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
110 
111     m_utils = new TessellationShaderUtils(gl, this);
112 
113     /* Initialize vertex array object */
114     gl.genVertexArrays(1, &m_vao_id);
115     GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
116 
117     gl.bindVertexArray(m_vao_id);
118     GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
119 
120     /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
121     glw::GLint gl_max_tess_gen_level_value = 0;
122 
123     gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
124     GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");
125 
126     /* Initialize all test runs */
127     const glw::GLint tess_levels[]   = {-gl_max_tess_gen_level_value / 2, -1, 1};
128     const unsigned int n_tess_levels = sizeof(tess_levels) / sizeof(tess_levels[0]);
129 
130     const _tessellation_shader_vertex_spacing vs_modes[] = {
131         /* NOTE: We do not check "fractional even" vertex spacing since it will always
132          *       clamp to 2 which is out of scope for this test.
133          */
134         TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT, TESSELLATION_SHADER_VERTEX_SPACING_EQUAL,
135         TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD};
136     const unsigned int n_vs_modes = sizeof(vs_modes) / sizeof(vs_modes[0]);
137 
138     /* Iterate through all vertex spacing modes */
139     bool has_failed = false;
140 
141     for (unsigned int n_vs_mode = 0; n_vs_mode < n_vs_modes; ++n_vs_mode)
142     {
143         _tessellation_shader_vertex_spacing vs_mode = vs_modes[n_vs_mode];
144 
145         /* Iterate through all values that should be used for irrelevant tessellation levels */
146         for (unsigned int n_tess_level = 0; n_tess_level < n_tess_levels; ++n_tess_level)
147         {
148             const glw::GLint tess_level = tess_levels[n_tess_level];
149 
150             /* Set up the run descriptor.
151              *
152              * Round outer tesellation levels to 1 if necessary, since otherwise no geometry will
153              * be generated.
154              **/
155             _run run;
156 
157             run.inner[0]       = (float)tess_level;
158             run.inner[1]       = (float)tess_level;
159             run.outer[0]       = (float)((tess_level < 0) ? 1 : tess_level);
160             run.outer[1]       = (float)((tess_level < 0) ? 1 : tess_level);
161             run.outer[2]       = (float)((tess_level < 0) ? 1 : tess_level);
162             run.outer[3]       = (float)((tess_level < 0) ? 1 : tess_level);
163             run.vertex_spacing = vs_mode;
164 
165             /* Retrieve vertex data for both passes */
166             run.n_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
167                 TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, run.inner, run.outer, run.vertex_spacing,
168                 false); /* is_point_mode_enabled */
169 
170             if (run.n_vertices == 0)
171             {
172                 std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
173 
174                 m_testCtx.getLog() << tcu::TestLog::Message
175                                    << "No vertices were generated by tessellator for: "
176                                       "inner tess levels:"
177                                       "["
178                                    << run.inner[0] << ", " << run.inner[1]
179                                    << "]"
180                                       ", outer tess levels:"
181                                       "["
182                                    << run.outer[0] << ", " << run.outer[1] << ", " << run.outer[2] << ", "
183                                    << run.outer[3]
184                                    << "]"
185                                       ", primitive mode: quads, "
186                                       "vertex spacing: "
187                                    << vs_mode_string << tcu::TestLog::EndMessage;
188 
189                 has_failed = true;
190             }
191             else
192             {
193                 /* Retrieve the data buffers */
194                 run.data = m_utils->getDataGeneratedByTessellator(run.inner, false, /* is_point_mode_enabled */
195                                                                   TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS,
196                                                                   TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
197                                                                   run.vertex_spacing, run.outer);
198             }
199 
200             /* Store the run data */
201             m_runs.push_back(run);
202         } /* for (all tessellation levels) */
203     }     /* for (all vertex spacing modes) */
204 
205     if (has_failed)
206     {
207         TCU_FAIL("Zero vertices were generated by tessellator for at least one run which is not "
208                  "a correct behavior");
209     }
210 }
211 
212 /** Executes the test.
213  *
214  *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
215  *
216  *  Note the function throws exception should an error occur!
217  *
218  *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
219  **/
iterate(void)220 tcu::TestNode::IterateResult TessellationShaderQuadsDegenerateCase::iterate(void)
221 {
222     /* Do not execute if required extensions are not supported. */
223     if (!m_is_tessellation_shader_supported)
224     {
225         throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
226     }
227 
228     /* Initialize the test */
229     initTest();
230 
231     /* Iterate through all runs */
232 
233     /* The test fails if any of the runs did not generate exactly 6 coordinates */
234     for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
235     {
236         const _run &run = *run_iterator;
237 
238         if (run.n_vertices != (2 /* triangles */ * 3 /* vertices */))
239         {
240             std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);
241 
242             m_testCtx.getLog() << tcu::TestLog::Message << "Invalid number of coordinates (" << run.n_vertices
243                                << ", instead of 6)"
244                                   " was generated for the following tessellation configuration: "
245                                   "primitive mode:quads, "
246                                   "vertex spacing mode:"
247                                << vs_mode_string << " inner tessellation levels:" << run.inner[0] << ", "
248                                << run.inner[1] << " outer tessellation levels:" << run.outer[0] << ", " << run.outer[1]
249                                << ", " << run.outer[2] << ", " << run.outer[3] << tcu::TestLog::EndMessage;
250 
251             TCU_FAIL("Invalid number of coordinates was generated for at least one run");
252         }
253     } /* for (all runs) */
254 
255     /* All runs should generate exactly the same set of triangles.
256      *
257      * Note: we must not assume any specific vertex ordering, so we cannot
258      *       just do a plain memcmp() here.
259      */
260     const _run &base_run = *m_runs.begin();
261 
262     for (unsigned int n_triangle = 0; n_triangle < base_run.n_vertices / 3 /* vertices per triangle */; n_triangle++)
263     {
264         const float *base_triangle_data = (const float *)(&base_run.data[0]) + 3       /* vertices per triangle */
265                                                                                    * 3 /* components */
266                                                                                    * n_triangle;
267 
268         for (_runs_const_iterator ref_run_iterator = m_runs.begin() + 1; ref_run_iterator != m_runs.end();
269              ref_run_iterator++)
270         {
271             const _run &ref_run = *ref_run_iterator;
272 
273             const float *ref_triangle_data1 = (const float *)(&ref_run.data[0]);
274             const float *ref_triangle_data2 =
275                 (const float *)(&ref_run.data[0]) + 3 /* vertices per triangle */ * 3 /* components */;
276 
277             if (!TessellationShaderUtils::isTriangleDefined(base_triangle_data, ref_triangle_data1) &&
278                 !TessellationShaderUtils::isTriangleDefined(base_triangle_data, ref_triangle_data2))
279             {
280                 std::string base_vs_mode_string =
281                     TessellationShaderUtils::getESTokenForVertexSpacingMode(base_run.vertex_spacing);
282                 std::string ref_vs_mode_string =
283                     TessellationShaderUtils::getESTokenForVertexSpacingMode(ref_run.vertex_spacing);
284 
285                 m_testCtx.getLog() << tcu::TestLog::Message
286                                    << "Reference run does not contain a triangle found in a base run"
287                                       " generated for the following tessellation configuration: "
288                                       "primitive mode:quads, "
289                                       "base vertex spacing mode:"
290                                    << base_vs_mode_string << " base inner tessellation levels:" << base_run.inner[0]
291                                    << ", " << base_run.inner[1]
292                                    << " base outer tessellation levels:" << base_run.outer[0] << ", "
293                                    << base_run.outer[1] << ", " << base_run.outer[2] << ", " << base_run.outer[3]
294                                    << "reference vertex spacing mode:" << ref_vs_mode_string
295                                    << " reference inner tessellation levels:" << ref_run.inner[0] << ", "
296                                    << ref_run.inner[1] << " reference outer tessellation levels:" << ref_run.outer[0]
297                                    << ", " << ref_run.outer[1] << ", " << ref_run.outer[2] << ", " << ref_run.outer[3]
298                                    << tcu::TestLog::EndMessage;
299 
300                 TCU_FAIL("Reference run does not contain a triangle found in a base run");
301             }
302         } /* for (all reference runs) */
303     }     /* for (all triangles) */
304 
305     /* All done */
306     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
307     return STOP;
308 }
309 
310 /** Constructor
311  *
312  * @param context Test context
313  **/
TessellationShaderQuadsInnerTessellationLevelRounding(Context & context,const ExtParameters & extParams)314 TessellationShaderQuadsInnerTessellationLevelRounding::TessellationShaderQuadsInnerTessellationLevelRounding(
315     Context &context, const ExtParameters &extParams)
316     : TestCaseBase(context, extParams, "inner_tessellation_level_rounding",
317                    "Verifies that either inner tessellation level is rounded to 2 or 3,"
318                    " when the tessellator is run in quads primitive mode and "
319                    "corresponding inner tessellation level is set to 1.")
320     , m_vao_id(0)
321     , m_utils(DE_NULL)
322 {
323     /* Left blank on purpose */
324 }
325 
326 /** Deinitializes ES objects created for the test. */
deinit()327 void TessellationShaderQuadsInnerTessellationLevelRounding::deinit()
328 {
329     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
330 
331     /* Call base class' deinit() */
332     TestCaseBase::deinit();
333 
334     /* Unbind vertex array object */
335     gl.bindVertexArray(0);
336 
337     /* Deinitialize utils instance */
338     if (m_utils != DE_NULL)
339     {
340         delete m_utils;
341 
342         m_utils = DE_NULL;
343     }
344 
345     /* Delete vertex array object */
346     if (m_vao_id != 0)
347     {
348         gl.deleteVertexArrays(1, &m_vao_id);
349 
350         m_vao_id = 0;
351     }
352 }
353 
354 /** Takes a vertex data set and returns a vector of unique vec2s found in the set.
355  *
356  *  @param raw_data            Vertex data set to process.
357  *  @param n_raw_data_vertices Amount of 3-component vertices found under @param raw_data.
358  *
359  *  @return As per description.
360  **/
getUniqueTessCoordinatesFromVertexDataSet(const float * raw_data,const unsigned int n_raw_data_vertices)361 std::vector<_vec2> TessellationShaderQuadsInnerTessellationLevelRounding::getUniqueTessCoordinatesFromVertexDataSet(
362     const float *raw_data, const unsigned int n_raw_data_vertices)
363 {
364     std::vector<_vec2> result;
365 
366     for (unsigned int n_vertex = 0; n_vertex < n_raw_data_vertices; n_vertex += 2)
367     {
368         const float *vertex_data = raw_data + n_vertex * 3 /* components */;
369         _vec2 vertex(vertex_data[0], vertex_data[1]);
370 
371         if (std::find(result.begin(), result.end(), vertex) == result.end())
372         {
373             result.push_back(vertex);
374         }
375 
376     } /* for (all vertices) */
377 
378     return result;
379 }
380 
381 /** Initializes ES objects necessary to run the test. */
initTest()382 void TessellationShaderQuadsInnerTessellationLevelRounding::initTest()
383 {
384     /* Skip if required extensions are not supported. */
385     if (!m_is_tessellation_shader_supported)
386     {
387         throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
388     }
389 
390     /* Initialize Utils instance */
391     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
392 
393     m_utils = new TessellationShaderUtils(gl, this);
394 
395     /* Initialize vertex array object */
396     gl.genVertexArrays(1, &m_vao_id);
397     GLU_EXPECT_NO_ERROR(gl.getError(), "Could not generate vertex array object");
398 
399     gl.bindVertexArray(m_vao_id);
400     GLU_EXPECT_NO_ERROR(gl.getError(), "Error binding vertex array object!");
401 
402     /* Retrieve GL_MAX_TESS_GEN_LEVEL_EXT value */
403     glw::GLint gl_max_tess_gen_level_value = 0;
404 
405     gl.getIntegerv(m_glExtTokens.MAX_TESS_GEN_LEVEL, &gl_max_tess_gen_level_value);
406     GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call failed for GL_MAX_TESS_GEN_LEVEL_EXT pname");
407 
408     /* Initialize all test runs */
409     const glw::GLint tess_levels[] = {2, gl_max_tess_gen_level_value / 2, gl_max_tess_gen_level_value};
410 
411     const glw::GLint tess_levels_odd[] = {2 + 1, gl_max_tess_gen_level_value / 2 + 1, gl_max_tess_gen_level_value - 1};
412     const unsigned int n_tess_levels   = sizeof(tess_levels) / sizeof(tess_levels[0]);
413 
414     const _tessellation_shader_vertex_spacing vs_modes[] = {TESSELLATION_SHADER_VERTEX_SPACING_EQUAL,
415                                                             TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN,
416                                                             TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD};
417     const unsigned int n_vs_modes                        = sizeof(vs_modes) / sizeof(vs_modes[0]);
418 
419     /* Iterate through all vertex spacing modes */
420     for (unsigned int n_vs_mode = 0; n_vs_mode < n_vs_modes; ++n_vs_mode)
421     {
422         _tessellation_shader_vertex_spacing vs_mode = vs_modes[n_vs_mode];
423 
424         /* Iterate through all values that should be used for irrelevant tessellation levels */
425         for (unsigned int n_tess_level = 0; n_tess_level < n_tess_levels; ++n_tess_level)
426         {
427             /* We need to test two cases in this test:
428              *
429              * a) inner[0] is set to 1 for set A and, for set B, to the value we're expecting the level to
430              *    round, given the vertex spacing mode. inner[1] is irrelevant.
431              * b) inner[0] is irrelevant. inner[1] is set to 1 for set A and, for set B, to the value we're
432              *    expecting the level to round, given the vertex spacing mode.
433              */
434             for (unsigned int n_inner_tess_level_combination = 0;
435                  n_inner_tess_level_combination < 2; /* inner[0], inner[1] */
436                  ++n_inner_tess_level_combination)
437             {
438                 /* Set up the run descriptor */
439                 glw::GLint tess_level = (vs_mode == TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD) ?
440                                             tess_levels_odd[n_tess_level] :
441                                             tess_levels[n_tess_level];
442                 _run run;
443 
444                 /* Determine inner tessellation level values for two cases */
445                 switch (n_inner_tess_level_combination)
446                 {
447                 case 0:
448                 {
449                     run.set1_inner[0] = 1.0f;
450                     run.set1_inner[1] = (glw::GLfloat)tess_level;
451                     run.set2_inner[1] = (glw::GLfloat)tess_level;
452 
453                     TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
454                         vs_mode, run.set1_inner[0] + 1.0f /* epsilon */, gl_max_tess_gen_level_value,
455                         DE_NULL, /* out_clamped */
456                         run.set2_inner);
457                     break;
458                 } /* case 0: */
459 
460                 case 1:
461                 {
462                     run.set1_inner[0] = (glw::GLfloat)tess_level;
463                     run.set1_inner[1] = 1.0f;
464                     run.set2_inner[0] = (glw::GLfloat)tess_level;
465 
466                     TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(
467                         vs_mode, run.set1_inner[1] + 1.0f /* epsilon */, gl_max_tess_gen_level_value,
468                         DE_NULL, /* out_clamped */
469                         run.set2_inner + 1);
470                     break;
471                 } /* case 1: */
472 
473                 default:
474                     TCU_FAIL("Invalid inner tessellation level combination index");
475                 } /* switch (n_inner_tess_level_combination) */
476 
477                 /* Configure outer tessellation level values */
478                 run.set1_outer[0] = (glw::GLfloat)tess_level;
479                 run.set2_outer[0] = (glw::GLfloat)tess_level;
480                 run.set1_outer[1] = (glw::GLfloat)tess_level;
481                 run.set2_outer[1] = (glw::GLfloat)tess_level;
482                 run.set1_outer[2] = (glw::GLfloat)tess_level;
483                 run.set2_outer[2] = (glw::GLfloat)tess_level;
484                 run.set1_outer[3] = (glw::GLfloat)tess_level;
485                 run.set2_outer[3] = (glw::GLfloat)tess_level;
486 
487                 /* Set up remaining run properties */
488                 run.vertex_spacing = vs_mode;
489 
490                 /* Retrieve vertex data for both passes */
491                 glw::GLint n_set1_vertices = 0;
492                 glw::GLint n_set2_vertices = 0;
493 
494                 n_set1_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
495                     TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, run.set1_inner, run.set1_outer, run.vertex_spacing,
496                     false); /* is_point_mode_enabled */
497                 n_set2_vertices = m_utils->getAmountOfVerticesGeneratedByTessellator(
498                     TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, run.set2_inner, run.set2_outer, run.vertex_spacing,
499                     false); /* is_point_mode_enabled */
500 
501                 if (n_set1_vertices == 0)
502                 {
503                     std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
504 
505                     m_testCtx.getLog() << tcu::TestLog::Message
506                                        << "No vertices were generated by tessellator for: "
507                                           "inner tess levels:"
508                                           "["
509                                        << run.set1_inner[0] << ", " << run.set1_inner[1]
510                                        << "]"
511                                           ", outer tess levels:"
512                                           "["
513                                        << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
514                                        << ", " << run.set1_outer[3]
515                                        << "]"
516                                           ", primitive mode: quads, "
517                                           "vertex spacing: "
518                                        << vs_mode_string << tcu::TestLog::EndMessage;
519 
520                     TCU_FAIL("Zero vertices were generated by tessellator for first test pass");
521                 }
522 
523                 if (n_set2_vertices == 0)
524                 {
525                     std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
526 
527                     m_testCtx.getLog() << tcu::TestLog::Message
528                                        << "No vertices were generated by tessellator for: "
529                                           "inner tess levels:"
530                                           "["
531                                        << run.set2_inner[0] << ", " << run.set2_inner[1]
532                                        << "]"
533                                           ", outer tess levels:"
534                                           "["
535                                        << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
536                                        << ", " << run.set2_outer[3]
537                                        << "]"
538                                           ", primitive mode: quads, "
539                                           "vertex spacing: "
540                                        << vs_mode_string << tcu::TestLog::EndMessage;
541 
542                     TCU_FAIL("Zero vertices were generated by tessellator for second test pass");
543                 }
544 
545                 if (n_set1_vertices != n_set2_vertices)
546                 {
547                     std::string vs_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vs_mode);
548 
549                     m_testCtx.getLog() << tcu::TestLog::Message
550                                        << "Amount of vertices generated by the tessellator differs"
551                                           " for the following inner/outer configs: "
552                                           "inner tess levels:"
553                                           "["
554                                        << run.set1_inner[0] << ", " << run.set1_inner[1]
555                                        << "]"
556                                           ", outer tess levels:"
557                                           "["
558                                        << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
559                                        << ", " << run.set1_outer[3]
560                                        << "]"
561                                           " and inner tess levels:"
562                                           "["
563                                        << run.set2_inner[0] << ", " << run.set2_inner[1]
564                                        << "]"
565                                           ", outer tess levels:"
566                                           "["
567                                        << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
568                                        << ", " << run.set2_outer[3]
569                                        << "]"
570                                           ", primitive mode: quads, vertex spacing: "
571                                        << vs_mode_string << tcu::TestLog::EndMessage;
572 
573                     TCU_FAIL("Amount of vertices generated by tessellator differs between base and references passes");
574                 }
575 
576                 /* Store the amount of vertices in run properties */
577                 run.n_vertices = n_set1_vertices;
578 
579                 /* Retrieve the data buffers */
580                 run.set1_data = m_utils->getDataGeneratedByTessellator(
581                     run.set1_inner, false, /* is_point_mode_enabled */
582                     TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
583                     run.vertex_spacing, run.set1_outer);
584                 run.set2_data = m_utils->getDataGeneratedByTessellator(
585                     run.set2_inner, false, /* is_point_mode_enabled */
586                     TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
587                     run.vertex_spacing, run.set2_outer);
588 
589                 /* Store the run data */
590                 m_runs.push_back(run);
591             } /* for (all inner tess level combinations) */
592         }     /* for (all sets) */
593     }         /* for (all vertex spacing modes) */
594 }
595 
596 /** Executes the test.
597  *
598  *  Sets the test result to QP_TEST_RESULT_FAIL if the test failed, QP_TEST_RESULT_PASS otherwise.
599  *
600  *  Note the function throws exception should an error occur!
601  *
602  *  @return STOP if the test has finished, CONTINUE to indicate iterate() should be called once again.
603  **/
iterate(void)604 tcu::TestNode::IterateResult TessellationShaderQuadsInnerTessellationLevelRounding::iterate(void)
605 {
606     /* Do not execute if required extensions are not supported. */
607     if (!m_is_tessellation_shader_supported)
608     {
609         throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
610     }
611 
612     /* Initialize the test */
613     initTest();
614 
615     /* Iterate through all runs */
616 
617     for (_runs_const_iterator run_iterator = m_runs.begin(); run_iterator != m_runs.end(); run_iterator++)
618     {
619         const _run &run = *run_iterator;
620 
621         if (run.vertex_spacing != TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD)
622         {
623             /* Make sure the vertex data generated for two passes matches */
624             const glw::GLint n_triangles = run.n_vertices / 3 /* vertices per triangle */;
625 
626             std::vector<bool> triangleMatched;
627             triangleMatched.assign(n_triangles, false);
628 
629             for (int n_triangle = 0; n_triangle < n_triangles; ++n_triangle)
630             {
631                 const float *triangle_a = (const float *)(&run.set1_data[0]) + n_triangle * 3 /* vertices */
632                                                                                    * 3;       /* components */
633                 /* Look up matching triangle */
634                 bool triangleFound = false;
635 
636                 for (int n_triangle_b = 0; n_triangle_b < n_triangles; ++n_triangle_b)
637                 {
638                     if (!triangleMatched[n_triangle_b])
639                     {
640                         const float *triangle_b = (const float *)(&run.set2_data[0]) + n_triangle_b * 3 /* vertices */
641                                                                                            * 3;         /* components */
642 
643                         if (TessellationShaderUtils::isTriangleDefined(triangle_a, triangle_b))
644                         {
645                             triangleMatched[n_triangle_b] = true;
646                             triangleFound                 = true;
647                             break;
648                         }
649                     }
650                 }
651 
652                 if (!triangleFound)
653                 {
654                     std::string vs_mode_string =
655                         TessellationShaderUtils::getESTokenForVertexSpacingMode(run.vertex_spacing);
656 
657                     m_testCtx.getLog() << tcu::TestLog::Message
658                                        << "The following triangle, generated in the first pass, was not "
659                                           "generated in the second one. "
660                                           "First pass' configuration: inner tess levels:"
661                                           "["
662                                        << run.set1_inner[0] << ", " << run.set1_inner[1]
663                                        << "]"
664                                           ", outer tess levels:"
665                                           "["
666                                        << run.set1_outer[0] << ", " << run.set1_outer[1] << ", " << run.set1_outer[2]
667                                        << ", " << run.set1_outer[3]
668                                        << "]"
669                                           "; second pass' configuration: inner tess levels:"
670                                           "["
671                                        << run.set2_inner[0] << ", " << run.set2_inner[1]
672                                        << "]"
673                                           ", outer tess levels:"
674                                           "["
675                                        << run.set2_outer[0] << ", " << run.set2_outer[1] << ", " << run.set2_outer[2]
676                                        << ", " << run.set2_outer[3]
677                                        << "]"
678                                           ", primitive mode: quads, vertex spacing: "
679                                        << vs_mode_string << tcu::TestLog::EndMessage;
680 
681                     TCU_FAIL("A triangle from first pass' data set was not found in second pass' data set.");
682                 }
683             } /* for (all vertices) */
684         }     /* if (run.vertex_spacing != TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD) */
685         else
686         {
687             /* For Fractional Odd vertex spacing, we cannot apply the above methodology because of the fact
688              * two of the inner edges (the ones subdivided with tessellation level of 1.0) will have been
689              * subdivided differently between two runs, causing the vertex positions to shift, ultimately
690              * leading to isTriangleDefined() failure.
691              *
692              * Hence, we need to take a bit different approach for this case. If you look at a visualization
693              * of a tessellated quad, for which one of the inner tess levels has been set to 1 (let's assume
694              * inner[0] for the sake of discussion), and then looked at a list of points that were generated by the
695              * tessellator, you'll notice that even though it looks like we only have one span of triangles
696              * horizontally, there are actually three. The two outermost spans on each side (the "outer
697              * tessellation regions") are actually degenerate, because they have epsilon spacing allocated to
698              * them.
699              *
700              * Using the theory above, we look for a so-called "marker triangle". In the inner[0] = 1.0 case,
701              * it's one of the two triangles, one edge of which spans horizontally across the whole domain, and
702              * the other two edges touch the outermost edge of the quad. In the inner[1] = 1.0 case, Xs are flipped
703              * with Ys, but the general idea stays the same.
704              *
705              * So for fractional odd vertex spacing, this test verifies that the two marker triangles capping the
706              * opposite ends of the inner quad tessellation region exist.
707              */
708 
709             /* Convert arrayed triangle-based representation to a vector storing unique tessellation
710              * coordinates.
711              */
712             std::vector<_vec2> set1_tess_coordinates =
713                 getUniqueTessCoordinatesFromVertexDataSet((const float *)(&run.set1_data[0]), run.n_vertices);
714 
715             /* Extract and sort the coordinate components we have from
716              * the minimum to the maximum */
717             std::vector<float> set1_tess_coordinates_x_sorted;
718             std::vector<float> set1_tess_coordinates_y_sorted;
719 
720             for (std::vector<_vec2>::iterator coordinate_iterator = set1_tess_coordinates.begin();
721                  coordinate_iterator != set1_tess_coordinates.end(); coordinate_iterator++)
722             {
723                 const _vec2 &coordinate = *coordinate_iterator;
724 
725                 if (std::find(set1_tess_coordinates_x_sorted.begin(), set1_tess_coordinates_x_sorted.end(),
726                               coordinate.x) == set1_tess_coordinates_x_sorted.end())
727                 {
728                     set1_tess_coordinates_x_sorted.push_back(coordinate.x);
729                 }
730 
731                 if (std::find(set1_tess_coordinates_y_sorted.begin(), set1_tess_coordinates_y_sorted.end(),
732                               coordinate.y) == set1_tess_coordinates_y_sorted.end())
733                 {
734                     set1_tess_coordinates_y_sorted.push_back(coordinate.y);
735                 }
736             } /* for (all tessellation coordinates) */
737 
738             std::sort(set1_tess_coordinates_x_sorted.begin(), set1_tess_coordinates_x_sorted.end());
739             std::sort(set1_tess_coordinates_y_sorted.begin(), set1_tess_coordinates_y_sorted.end());
740 
741             /* Quick checks */
742             DE_ASSERT(set1_tess_coordinates_x_sorted.size() > 2);
743             DE_ASSERT(set1_tess_coordinates_y_sorted.size() > 2);
744 
745             /* NOTE: This code could have been merged for both cases at the expense of code readability. */
746             if (run.set1_inner[0] == 1.0f)
747             {
748                 /* Look for the second horizontal line segment, starting from top and bottom */
749                 const float second_y_from_top = set1_tess_coordinates_y_sorted[1];
750                 const float second_y_from_bottom =
751                     set1_tess_coordinates_y_sorted[set1_tess_coordinates_y_sorted.size() - 2];
752 
753                 /* In this particular case, there should be exactly one triangle spanning
754                  * from U=0 to U=1 at both these heights, with the third coordinate located
755                  * at the "outer" edge.
756                  */
757                 for (int n = 0; n < 2 /* cases */; ++n)
758                 {
759                     float y1_y2 = 0.0f;
760                     float y3    = 0.0f;
761 
762                     if (n == 0)
763                     {
764                         y1_y2 = second_y_from_bottom;
765                         y3    = 1.0f;
766                     }
767                     else
768                     {
769                         y1_y2 = second_y_from_top;
770                         y3    = 0.0f;
771                     }
772 
773                     /* Try to find the triangle */
774                     bool has_found_triangle = false;
775 
776                     DE_ASSERT((run.n_vertices % 3) == 0);
777 
778                     for (unsigned int n_triangle = 0; n_triangle < run.n_vertices / 3; ++n_triangle)
779                     {
780                         const float *vertex1_data = (const float *)(&run.set1_data[0]) + 3       /* vertices */
781                                                                                              * 3 /* components */
782                                                                                              * n_triangle;
783                         const float *vertex2_data = vertex1_data + 3 /* components */;
784                         const float *vertex3_data = vertex2_data + 3 /* components */;
785 
786                         /* Make sure at least two Y coordinates are equal to y1_y2. */
787                         const float *y1_vertex_data = DE_NULL;
788                         const float *y2_vertex_data = DE_NULL;
789                         const float *y3_vertex_data = DE_NULL;
790 
791                         if (vertex1_data[1] == y1_y2)
792                         {
793                             if (vertex2_data[1] == y1_y2 && vertex3_data[1] == y3)
794                             {
795                                 y1_vertex_data = vertex1_data;
796                                 y2_vertex_data = vertex2_data;
797                                 y3_vertex_data = vertex3_data;
798                             }
799                             else if (vertex2_data[1] == y3 && vertex3_data[1] == y1_y2)
800                             {
801                                 y1_vertex_data = vertex1_data;
802                                 y2_vertex_data = vertex3_data;
803                                 y3_vertex_data = vertex2_data;
804                             }
805                         }
806                         else if (vertex2_data[1] == y1_y2 && vertex3_data[1] == y1_y2 && vertex1_data[1] == y3)
807                         {
808                             y1_vertex_data = vertex2_data;
809                             y2_vertex_data = vertex3_data;
810                             y3_vertex_data = vertex1_data;
811                         }
812 
813                         if (y1_vertex_data != DE_NULL && y2_vertex_data != DE_NULL && y3_vertex_data != DE_NULL)
814                         {
815                             /* Vertex 1 and 2 should span across whole domain horizontally */
816                             if ((y1_vertex_data[0] == 0.0f && y2_vertex_data[0] == 1.0f) ||
817                                 (y1_vertex_data[0] == 1.0f && y2_vertex_data[0] == 0.0f))
818                             {
819                                 has_found_triangle = true;
820 
821                                 break;
822                             }
823                         }
824                     } /* for (all triangles) */
825 
826                     if (!has_found_triangle)
827                     {
828                         TCU_FAIL("Could not find a marker triangle");
829                     }
830                 } /* for (both cases) */
831             }     /* if (run.set1_inner[0] == 1.0f) */
832             else
833             {
834                 DE_ASSERT(run.set1_inner[1] == 1.0f);
835 
836                 /* Look for the second vertical line segment, starting from left and right */
837                 const float second_x_from_left = set1_tess_coordinates_x_sorted[1];
838                 const float second_x_from_right =
839                     set1_tess_coordinates_x_sorted[set1_tess_coordinates_x_sorted.size() - 2];
840 
841                 /* In this particular case, there should be exactly one triangle spanning
842                  * from V=0 to V=1 at both these widths, with the third coordinate located
843                  * at the "outer" edge.
844                  */
845                 for (int n = 0; n < 2 /* cases */; ++n)
846                 {
847                     float x1_x2 = 0.0f;
848                     float x3    = 0.0f;
849 
850                     if (n == 0)
851                     {
852                         x1_x2 = second_x_from_right;
853                         x3    = 1.0f;
854                     }
855                     else
856                     {
857                         x1_x2 = second_x_from_left;
858                         x3    = 0.0f;
859                     }
860 
861                     /* Try to find the triangle */
862                     bool has_found_triangle = false;
863 
864                     DE_ASSERT((run.n_vertices % 3) == 0);
865 
866                     for (unsigned int n_triangle = 0; n_triangle < run.n_vertices / 3; ++n_triangle)
867                     {
868                         const float *vertex1_data =
869                             (const float *)(&run.set1_data[0]) + 3 /* vertices */ * 3 /* components */ * n_triangle;
870                         const float *vertex2_data = vertex1_data + 3 /* components */;
871                         const float *vertex3_data = vertex2_data + 3 /* components */;
872 
873                         /* Make sure at least two X coordinates are equal to x1_x2. */
874                         const float *x1_vertex_data = DE_NULL;
875                         const float *x2_vertex_data = DE_NULL;
876                         const float *x3_vertex_data = DE_NULL;
877 
878                         if (vertex1_data[0] == x1_x2)
879                         {
880                             if (vertex2_data[0] == x1_x2 && vertex3_data[0] == x3)
881                             {
882                                 x1_vertex_data = vertex1_data;
883                                 x2_vertex_data = vertex2_data;
884                                 x3_vertex_data = vertex3_data;
885                             }
886                             else if (vertex2_data[0] == x3 && vertex3_data[0] == x1_x2)
887                             {
888                                 x1_vertex_data = vertex1_data;
889                                 x2_vertex_data = vertex3_data;
890                                 x3_vertex_data = vertex2_data;
891                             }
892                         }
893                         else if (vertex2_data[0] == x1_x2 && vertex3_data[0] == x1_x2 && vertex1_data[0] == x3)
894                         {
895                             x1_vertex_data = vertex2_data;
896                             x2_vertex_data = vertex3_data;
897                             x3_vertex_data = vertex1_data;
898                         }
899 
900                         if (x1_vertex_data != DE_NULL && x2_vertex_data != DE_NULL && x3_vertex_data != DE_NULL)
901                         {
902                             /* Vertex 1 and 2 should span across whole domain vertically */
903                             if ((x1_vertex_data[1] == 0.0f && x2_vertex_data[1] == 1.0f) ||
904                                 (x1_vertex_data[1] == 1.0f && x2_vertex_data[1] == 0.0f))
905                             {
906                                 has_found_triangle = true;
907 
908                                 break;
909                             }
910                         }
911                     } /* for (all triangles) */
912 
913                     if (!has_found_triangle)
914                     {
915                         TCU_FAIL("Could not find a marker triangle (implies invalid quad tessellation for the case "
916                                  "considered)");
917                     }
918                 } /* for (both cases) */
919             }
920         }
921     } /* for (all runs) */
922 
923     /* All done */
924     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
925     return STOP;
926 }
927 
928 } /* namespace glcts */
929