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