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