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 "esextcTessellationShaderUtils.hpp"
25 #include "deMath.h"
26 #include "glwEnums.hpp"
27 #include "glwFunctions.hpp"
28 #include "tcuTestLog.hpp"
29 #include <sstream>
30 
31 namespace glcts
32 {
33 
34 /** Constructor
35  *
36  *  @param gl         DEQP container for ES entry-points
37  *  @param parentTest Pointer to owning test instance.
38  **/
TessellationShaderUtils(const glw::Functions & gl,glcts::TestCaseBase * parentTest)39 TessellationShaderUtils::TessellationShaderUtils(const glw::Functions &gl, glcts::TestCaseBase *parentTest)
40     : m_gl(gl)
41     , m_bo_id(0)
42     , m_fs_id(0)
43     , m_qo_pg_id(0)
44     , m_vs_id(0)
45     , m_parent_test(parentTest)
46 {
47     init();
48 }
49 
50 /** Destructor */
~TessellationShaderUtils()51 TessellationShaderUtils::~TessellationShaderUtils()
52 {
53     deinit();
54 }
55 
56 /** Captures data generated by the tessellator when a geometry is drawn
57  *  for user-provided vertex counter program.
58  *
59  *  @param program Vertex counter program to use.
60  **/
captureTessellationData(_tessellation_vertex_counter_program & program)61 void TessellationShaderUtils::captureTessellationData(_tessellation_vertex_counter_program &program)
62 {
63     /* Cache current program object ID before we continue */
64     glw::GLint current_po_id = 0;
65 
66     m_gl.getIntegerv(GL_CURRENT_PROGRAM, &current_po_id);
67     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetIntegerv() failed for GL_CURRENT_PROGRAM pname");
68 
69     /* Cache current GL_PATCH_VERTICES_EXT setting before continuing */
70     glw::GLint current_patch_vertices = 0;
71 
72     m_gl.getIntegerv(GL_PATCH_VERTICES, &current_patch_vertices);
73     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetIntegerv() failed for GL_PATCH_VERTICES_EXT pname");
74 
75     /* Activate the program object and the query object */
76     m_gl.useProgram(program.po_id);
77     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
78 
79     m_gl.beginQuery(m_parent_test->m_glExtTokens.PRIMITIVES_GENERATED, m_qo_pg_id);
80     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBeginQuery() called for target GL_PRIMITIVES_GENERATED_EXT failed");
81 
82     /* Disable rasterization, if it's enabled */
83     glw::GLboolean is_rasterization_disabled = m_gl.isEnabled(GL_RASTERIZER_DISCARD);
84 
85     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glIsEnabled(GL_RASTERIZER_DISCARD) failed");
86 
87     if (is_rasterization_disabled)
88     {
89         m_gl.enable(GL_RASTERIZER_DISCARD);
90 
91         GLU_EXPECT_NO_ERROR(m_gl.getError(), "glEnable(GL_RASTERIZER_DISCARD) failed");
92     }
93 
94     /* Update GL_PATCH_VERTICES_EXT */
95     m_gl.patchParameteri(m_parent_test->m_glExtTokens.PATCH_VERTICES, program.n_patch_vertices);
96     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glPatchParameteriEXT() failed");
97 
98     /* Draw the test geometry */
99     m_gl.drawArrays(m_parent_test->m_glExtTokens.PATCHES, 0, /* first */
100                     program.n_patch_vertices);               /* count */
101     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays() failed");
102 
103     /* End the query and retrieve the result */
104     glw::GLuint queryValue = 0;
105 
106     m_gl.endQuery(m_parent_test->m_glExtTokens.PRIMITIVES_GENERATED);
107     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glEndQuery(GL_PRIMITIVES_GENERATED_EXT) failed");
108 
109     m_gl.getQueryObjectuiv(m_qo_pg_id, GL_QUERY_RESULT, &queryValue);
110     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetQueryiv() failed");
111 
112     /* Store amount of primitives under result */
113     program.n_data_vertices = ((unsigned int)queryValue);
114 
115     if (!program.is_point_mode_enabled)
116     {
117         /* Quads get tessellated into triangles, meaning our primitives counter tells how
118          * many triangles were generated for both triangles and quads; isolines get
119          * tessellated into line segments.
120          */
121         if (program.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS ||
122             program.primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES)
123         {
124             program.n_data_vertices *= 3; /* each triangle gets 3 vertices */
125         }
126         else
127         {
128             program.n_data_vertices *= 2; /* each isoline gets 2 vertices */
129         }
130     } /* if (!is_point_mode_enabled) */
131 
132     if (program.n_data_vertices != 0)
133     {
134         /* Now that we now, how many vertices we need to allocate space for, set up TF */
135         glw::GLint bo_size = static_cast<glw::GLint>(sizeof(float) * 3 /* components */ * program.n_data_vertices);
136 
137         m_gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id);
138         GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindBuffer() failed");
139 
140         m_gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, m_bo_id);
141         GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindBufferBase() failed");
142 
143         m_gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bo_size, DE_NULL, /* data */
144                         GL_STATIC_DRAW);
145         GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBufferData() failed");
146 
147         /* Set up TF */
148         glw::GLenum tf_mode =
149             TessellationShaderUtils::getTFModeForPrimitiveMode(program.primitive_mode, program.is_point_mode_enabled);
150 
151         m_gl.beginTransformFeedback(tf_mode);
152         GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBeginTransformFeedback() failed");
153 
154         m_gl.drawArrays(m_parent_test->m_glExtTokens.PATCHES, 0, /* first */
155                         program.n_patch_vertices);               /* count */
156         GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays() failed");
157 
158         m_gl.endTransformFeedback();
159         GLU_EXPECT_NO_ERROR(m_gl.getError(), "glEndTransformFeedback() failed");
160 
161         /* Map the BO and copy the contents */
162         const void *xfb_data = m_gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */
163                                                    bo_size, GL_MAP_READ_BIT);
164         GLU_EXPECT_NO_ERROR(m_gl.getError(), "glMapBufferRange() failed");
165 
166         program.m_data.resize(bo_size);
167 
168         memcpy(&program.m_data[0], xfb_data, bo_size);
169 
170         m_gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
171         GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUnmapBuffer() failed");
172     } /* if (program.n_data_vertices != 0) */
173 
174     /* Bring the rasterization back up, if it was enabled prior to this call */
175     if (!is_rasterization_disabled)
176     {
177         m_gl.disable(GL_RASTERIZER_DISCARD);
178 
179         GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDisable(GL_RASTERIZER_DISCARD) failed");
180     }
181 
182     /* Activate the pre-call program object*/
183     m_gl.useProgram(current_po_id);
184     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
185 
186     /* Bring back pre-call GL_PATCH_VERTICES_EXT setting */
187     m_gl.patchParameteri(m_parent_test->m_glExtTokens.PATCH_VERTICES, current_patch_vertices);
188     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glPatchParameteriEXT() failed");
189 }
190 
191 /** Compiles all requested shaders. Should any of the shaders not compile,
192  *  TestError exception can be thrown if @param should_succeed is set to true.
193  *
194  *  @param n_shaders      Amount of shader IDs passed in @param shaders argument.
195  *  @param shaders        IDs of shader objects to compile.
196  *  @param should_succeed True if the shaders are expected to compile, false if
197  *                        it's fine for them to not to compile successfully.
198  **/
compileShaders(glw::GLint n_shaders,const glw::GLuint * shaders,bool should_succeed)199 void TessellationShaderUtils::compileShaders(glw::GLint n_shaders, const glw::GLuint *shaders, bool should_succeed)
200 {
201     for (glw::GLint n_shader = 0; n_shader < n_shaders; ++n_shader)
202     {
203         glw::GLuint shader = shaders[n_shader];
204 
205         if (shader != 0)
206         {
207             glw::GLint compile_status = GL_FALSE;
208 
209             m_gl.compileShader(shader);
210             GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCompileShader() failed");
211 
212             m_gl.getShaderiv(shader, GL_COMPILE_STATUS, &compile_status);
213             GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderiv() failed");
214 
215             if (should_succeed && compile_status != GL_TRUE)
216             {
217                 std::string info_log      = m_parent_test->getCompilationInfoLog(shader);
218                 std::string shader_source = m_parent_test->getShaderSource(shader);
219 
220                 m_parent_test->m_context.getTestContext().getLog()
221                     << tcu::TestLog::Message << "Compilation failure:\n\n"
222                     << info_log << "\n\n"
223                     << "Source:\n\n"
224                     << shader_source << "\n\n"
225                     << tcu::TestLog::EndMessage;
226                 TCU_FAIL("Shader compilation failed");
227             }
228             else if (!should_succeed && compile_status == GL_TRUE)
229             {
230                 std::string shader_source = m_parent_test->getShaderSource(shader);
231                 m_parent_test->m_context.getTestContext().getLog()
232                     << tcu::TestLog::Message << "Compilation failure expected.\nSource:\n\n"
233                     << shader_source << "\n\n"
234                     << tcu::TestLog::EndMessage;
235                 TCU_FAIL("Shader compiled successfully, even though it was "
236                          "expected to fail.");
237             }
238         }
239     } /* for (all shaders) */
240 }
241 
242 /** Converts input barycentric coordinates to Cartesian coordinate system. The function assumes
243  *  a triangle basis built of the following verticeS: (0.5, 0), (1, 1), (0, 1).
244  *
245  *  @param barycentric_coordinates   Three FP values storing barycentric coordinates of a point. Must
246  *                                   NOT be NULL.
247  *  @param out_cartesian_coordinates Deref will be used to store two result FP values. Must not be NULL.
248  **/
convertBarycentricCoordinatesToCartesian(const float * barycentric_coordinates,float * out_cartesian_coordinates)249 void TessellationShaderUtils::convertBarycentricCoordinatesToCartesian(const float *barycentric_coordinates,
250                                                                        float *out_cartesian_coordinates)
251 {
252     /* Assume output triangle uses the following base:
253      *
254      * (0.5, 0)
255      * (1,   1)
256      * (0,   1)
257      */
258     const float triangle_vertex1_cartesian[2] = {0.5f, 0.0f};
259     const float triangle_vertex2_cartesian[2] = {1.0f, 1.0f};
260     const float triangle_vertex3_cartesian[2] = {0.0f, 1.0f};
261 
262     out_cartesian_coordinates[0] = (float)((double)barycentric_coordinates[0] * (double)triangle_vertex1_cartesian[0] +
263                                            (double)barycentric_coordinates[1] * (double)triangle_vertex2_cartesian[0] +
264                                            (double)barycentric_coordinates[2] * (double)triangle_vertex3_cartesian[0]);
265     out_cartesian_coordinates[1] = barycentric_coordinates[0] * triangle_vertex1_cartesian[1] +
266                                    barycentric_coordinates[1] * triangle_vertex2_cartesian[1] +
267                                    barycentric_coordinates[2] * triangle_vertex3_cartesian[1];
268 }
269 
270 /** Converts input Cartesian coordinates to barycentric coordinate system. The function assumes
271  *  a triangle basis built of the following verticeS: (0.5, 0), (1, 1), (0, 1).
272  *
273  *  @param cartesian_coordinates       Two FP values storing Cartesian coordinates of a point. Must NOT
274  *                                     be NULL.
275  *  @param out_barycentric_coordinates Deref will be used to store three result FP values. Must NOT be NULL.
276  **/
convertCartesianCoordinatesToBarycentric(const float * cartesian_coordinates,float * out_barycentric_coordinates)277 void TessellationShaderUtils::convertCartesianCoordinatesToBarycentric(const float *cartesian_coordinates,
278                                                                        float *out_barycentric_coordinates)
279 {
280     /* Assume input triangle uses the following base:
281      *
282      * (0.5, 0)
283      * (1,   1)
284      * (0,   1)
285      */
286     const float x1 = 0.5f;
287     const float x2 = 1.0f;
288     const float x3 = 0.0f;
289     const float y1 = 0.0f;
290     const float y2 = 1.0f;
291     const float y3 = 1.0f;
292 
293     out_barycentric_coordinates[0] =
294         ((y2 - y3) * (cartesian_coordinates[0] - x3) + (x3 - x2) * (cartesian_coordinates[1] - y3)) /
295         ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
296     out_barycentric_coordinates[1] =
297         ((y3 - y1) * (cartesian_coordinates[0] - x3) + (x1 - x3) * (cartesian_coordinates[1] - y3)) /
298         ((y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3));
299     out_barycentric_coordinates[2] = 1.0f - out_barycentric_coordinates[0] - out_barycentric_coordinates[1];
300 }
301 
302 /** Deinitializes ES objects created for TessellationShaderUtils
303  *  instance.
304  **/
deinit()305 void TessellationShaderUtils::deinit()
306 {
307     if (!m_parent_test->m_is_tessellation_shader_supported)
308     {
309         return;
310     }
311 
312     if (m_bo_id != 0)
313     {
314         m_gl.deleteBuffers(1, &m_bo_id);
315 
316         m_bo_id = 0;
317     }
318 
319     if (m_fs_id != 0)
320     {
321         m_gl.deleteShader(m_fs_id);
322 
323         m_fs_id = 0;
324     }
325 
326     if (m_qo_pg_id != 0)
327     {
328         m_gl.deleteQueries(1, &m_qo_pg_id);
329 
330         m_qo_pg_id = 0;
331     }
332 
333     if (m_vs_id != 0)
334     {
335         m_gl.deleteShader(m_vs_id);
336 
337         m_vs_id = 0;
338     }
339 
340     /* Revert TF buffer object bindings */
341     m_gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* buffer */);
342     m_gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0 /* index */, 0 /* buffer */);
343 
344     /* Disable GL_RASTERIZER_DISCARD mode */
345     m_gl.disable(GL_RASTERIZER_DISCARD);
346 
347     /* Restore GL_PATCH_VERTICES_EXT value */
348     m_gl.patchParameteri(m_parent_test->m_glExtTokens.PATCH_VERTICES, 3);
349 }
350 
351 /** Retrieves generic tessellation control shader source code.
352  *  The shader users user-specified amount of output patch vertices and:
353  *
354  *  - sets gl_Position to gl_in[0].gl_Position if second argument is set to false.
355  *  - sets gl_Position to gl_in[gl_InvocationID].gl_Position otherwise.
356  *
357  *  @param n_patch_vertices                        Amount of output patch vertices
358  *                                                 to use in the shader.
359  *  @param should_use_glInvocationID_indexed_input See above.
360  *
361  *  @return Requested string.
362  */
getGenericTCCode(unsigned int n_patch_vertices,bool should_use_glInvocationID_indexed_input)363 std::string TessellationShaderUtils::getGenericTCCode(unsigned int n_patch_vertices,
364                                                       bool should_use_glInvocationID_indexed_input)
365 {
366     std::string result;
367     const char *tc_body_false = "${VERSION}\n"
368                                 "\n"
369                                 "${TESSELLATION_SHADER_REQUIRE}\n"
370                                 "\n"
371                                 "layout (vertices = MAX_VERTICES) out;\n"
372                                 "\n"
373                                 "uniform vec2 inner_tess_level;\n"
374                                 "uniform vec4 outer_tess_level;\n"
375                                 "\n"
376                                 "void main()\n"
377                                 "{\n"
378                                 "    gl_out[gl_InvocationID].gl_Position = gl_in[0].gl_Position;\n"
379                                 "\n"
380                                 "    gl_TessLevelInner[0] = inner_tess_level.x;\n"
381                                 "    gl_TessLevelInner[1] = inner_tess_level.y;\n"
382                                 "    gl_TessLevelOuter[0] = outer_tess_level.x;\n"
383                                 "    gl_TessLevelOuter[1] = outer_tess_level.y;\n"
384                                 "    gl_TessLevelOuter[2] = outer_tess_level.z;\n"
385                                 "    gl_TessLevelOuter[3] = outer_tess_level.w;\n"
386                                 "}\n";
387 
388     const char *tc_body_true = "${VERSION}\n"
389                                "\n"
390                                "${TESSELLATION_SHADER_REQUIRE}\n"
391                                "\n"
392                                "layout (vertices = MAX_VERTICES) out;\n"
393                                "\n"
394                                "uniform vec2 inner_tess_level;\n"
395                                "uniform vec4 outer_tess_level;\n"
396                                "\n"
397                                "void main()\n"
398                                "{\n"
399                                "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
400                                "\n"
401                                "    gl_TessLevelInner[0] = inner_tess_level.x;\n"
402                                "    gl_TessLevelInner[1] = inner_tess_level.y;\n"
403                                "    gl_TessLevelOuter[0] = outer_tess_level.x;\n"
404                                "    gl_TessLevelOuter[1] = outer_tess_level.y;\n"
405                                "    gl_TessLevelOuter[2] = outer_tess_level.z;\n"
406                                "    gl_TessLevelOuter[3] = outer_tess_level.w;\n"
407                                "}\n";
408 
409     const char *n_patch_vertices_raw_ptr = NULL;
410     std::stringstream n_patch_vertices_sstream;
411     std::string n_patch_vertices_string;
412     std::string token       = "MAX_VERTICES";
413     std::size_t token_index = std::string::npos;
414 
415     n_patch_vertices_sstream << n_patch_vertices;
416     n_patch_vertices_string  = n_patch_vertices_sstream.str();
417     n_patch_vertices_raw_ptr = n_patch_vertices_string.c_str();
418 
419     result = (should_use_glInvocationID_indexed_input) ? tc_body_true : tc_body_false;
420 
421     while ((token_index = result.find(token)) != std::string::npos)
422     {
423         result = result.replace(token_index, token.length(), n_patch_vertices_raw_ptr);
424 
425         token_index = result.find(token);
426     }
427 
428     return result;
429 }
430 
431 /** Retrieves generic tessellation evaluation shader source code.
432  *  The shader users user-specified tessellation properties.
433  *
434  *  @param vertex_spacing Vertex spacing mode to use in the shader.
435  *  @param primitive_mode Primitive mode to use in the shader.
436  *  @param point_mode     true to use point_mode in the shader, false
437  *                        to omit it.
438  *
439  *  @return Requested string.
440  */
getGenericTECode(_tessellation_shader_vertex_spacing vertex_spacing,_tessellation_primitive_mode primitive_mode,_tessellation_shader_vertex_ordering vertex_ordering,bool point_mode)441 std::string TessellationShaderUtils::getGenericTECode(_tessellation_shader_vertex_spacing vertex_spacing,
442                                                       _tessellation_primitive_mode primitive_mode,
443                                                       _tessellation_shader_vertex_ordering vertex_ordering,
444                                                       bool point_mode)
445 {
446     std::string result;
447     const char *te_body = "${VERSION}\n"
448                           "\n"
449                           "${TESSELLATION_SHADER_REQUIRE}\n"
450                           "\n"
451                           "layout (TESSELLATOR_PRIMITIVE_MODE VERTEX_SPACING_MODE VERTEX_ORDERING POINT_MODE) in;\n"
452                           "\n"
453                           "out vec3 result_uvw;\n"
454                           "\n"
455                           "void main()\n"
456                           "{\n"
457                           "    gl_Position = gl_in[0].gl_Position;\n"
458                           "    result_uvw  = gl_TessCoord;\n"
459                           "}\n";
460 
461     const char *point_mode_token           = "POINT_MODE";
462     std::size_t point_mode_token_index     = std::string::npos;
463     std::string primitive_mode_string      = TessellationShaderUtils::getESTokenForPrimitiveMode(primitive_mode);
464     const char *primitive_mode_token       = "TESSELLATOR_PRIMITIVE_MODE";
465     std::size_t primitive_mode_token_index = std::string::npos;
466     std::string vertex_ordering_string;
467     const char *vertex_ordering_token       = "VERTEX_ORDERING";
468     std::size_t vertex_ordering_token_index = std::string::npos;
469     std::string vertex_spacing_mode_string;
470     const char *vertex_spacing_token       = "VERTEX_SPACING_MODE";
471     std::size_t vertex_spacing_token_index = std::string::npos;
472 
473     result = te_body;
474 
475     /* Prepare the vertex ordering token. We need to do this manually, because the default vertex spacing
476      * mode translates to empty string and the shader would fail to compile if we hadn't taken care of the
477      * comma
478      */
479     if (vertex_ordering == TESSELLATION_SHADER_VERTEX_ORDERING_DEFAULT)
480     {
481         vertex_ordering_string = TessellationShaderUtils::getESTokenForVertexOrderingMode(vertex_ordering);
482     }
483     else
484     {
485         std::stringstream helper_sstream;
486 
487         helper_sstream << ", " << TessellationShaderUtils::getESTokenForVertexOrderingMode(vertex_ordering);
488 
489         vertex_ordering_string = helper_sstream.str();
490     }
491 
492     /* Do the same for vertex spacing token */
493     if (vertex_spacing == TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT)
494     {
495         vertex_spacing_mode_string = TessellationShaderUtils::getESTokenForVertexSpacingMode(vertex_spacing);
496     }
497     else
498     {
499         std::stringstream helper_sstream;
500 
501         helper_sstream << ", " << TessellationShaderUtils::getESTokenForVertexSpacingMode(vertex_spacing);
502 
503         vertex_spacing_mode_string = helper_sstream.str();
504     }
505 
506     /* Primitive mode */
507     while ((primitive_mode_token_index = result.find(primitive_mode_token)) != std::string::npos)
508     {
509         result = result.replace(primitive_mode_token_index, strlen(primitive_mode_token), primitive_mode_string);
510 
511         primitive_mode_token_index = result.find(primitive_mode_token);
512     }
513 
514     /* Vertex ordering */
515     while ((vertex_ordering_token_index = result.find(vertex_ordering_token)) != std::string::npos)
516     {
517         result = result.replace(vertex_ordering_token_index, strlen(vertex_ordering_token), vertex_ordering_string);
518 
519         vertex_ordering_token_index = result.find(vertex_ordering_token);
520     }
521 
522     /* Vertex spacing */
523     while ((vertex_spacing_token_index = result.find(vertex_spacing_token)) != std::string::npos)
524     {
525         result = result.replace(vertex_spacing_token_index, strlen(vertex_spacing_token), vertex_spacing_mode_string);
526 
527         vertex_spacing_token_index = result.find(vertex_spacing_token);
528     }
529 
530     /* Point mode */
531     while ((point_mode_token_index = result.find(point_mode_token)) != std::string::npos)
532     {
533         result = result.replace(point_mode_token_index, strlen(point_mode_token), (point_mode) ? ", point_mode" : "");
534 
535         point_mode_token_index = result.find(point_mode_token);
536     }
537 
538     return result;
539 }
540 
541 /** Initializes ES objects that will be needed for non-static calls
542  *
543  *  This function throws TestError exception if an error occurs.
544  *
545  **/
init()546 void TessellationShaderUtils::init()
547 {
548     if (!m_parent_test->m_is_tessellation_shader_supported)
549     {
550         throw tcu::NotSupportedError(TESSELLATION_SHADER_EXTENSION_NOT_SUPPORTED);
551     }
552 
553     /* Create buffer object used to hold XFB data */
554     m_gl.genBuffers(1, &m_bo_id);
555     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenBuffers() failed");
556 
557     /* Create query object */
558     m_gl.genQueries(1, &m_qo_pg_id);
559     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenQueries() failed");
560 
561     /* Initialize shader objects */
562     m_fs_id = m_gl.createShader(GL_FRAGMENT_SHADER);
563     m_vs_id = m_gl.createShader(GL_VERTEX_SHADER);
564 
565     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateShader() failed");
566 
567     /* Initialize bodies of the shaders and try to compile them */
568     const glw::GLuint shaders[]  = {m_fs_id, m_vs_id};
569     const unsigned int n_shaders = DE_LENGTH_OF_ARRAY(shaders);
570 
571     const char *fs_body = "${VERSION}\n"
572                           "\n"
573                           "void main()\n"
574                           "{\n"
575                           "}\n";
576     const char *vs_body = "${VERSION}\n"
577                           "\n"
578                           "void main()\n"
579                           "{\n"
580                           "    gl_Position = vec4(1.0, 0.0, 0.0, 1.0);\n"
581                           "}\n";
582 
583     m_parent_test->shaderSourceSpecialized(m_fs_id, 1 /* count */, &fs_body);
584     m_parent_test->shaderSourceSpecialized(m_vs_id, 1 /* count */, &vs_body);
585     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glShaderSource() failed");
586 
587     compileShaders(n_shaders, shaders, true);
588 }
589 
590 /** Retrieves amount of vertices that will be generated for a draw call
591  *  that uses a program object, which is built of (at least) one tessellation
592  *  stage.
593  *
594  *  NOTE: This function can temporarily unbind active program object.
595  *        This function throws TestError exception if an error occurs.
596  *
597  *  @param primitive_mode           Primitive mode used for the tessellation.
598  *  @param inner_tessellation_level Two FP values that define inner tessellation levels.
599  *                                  Must NOT be NULL.
600  *  @param outer_tessellation_level Four FP values that define outer tessellation levels.
601  *                                  Must NOT be NULL.
602  *  @param vertex_spacing           Vertex spacing mode used for the tessellation.
603  *  @param is_point_mode_enabled    true if point_mode should be enabled for the query,
604  *                                  false otherwise.
605  *
606  *  This function REQUIRES GL_EXT_geometry_shader support.
607  *  This function throws TestError exception, should an error occur.
608  *
609  *  @return Amount of vertices that would be generated by the tessellator unit for
610  *          a particular draw call.
611  **/
getAmountOfVerticesGeneratedByTessellator(_tessellation_primitive_mode primitive_mode,const float * inner_tessellation_level,const float * outer_tessellation_level,_tessellation_shader_vertex_spacing vertex_spacing,bool is_point_mode_enabled)612 unsigned int TessellationShaderUtils::getAmountOfVerticesGeneratedByTessellator(
613     _tessellation_primitive_mode primitive_mode, const float *inner_tessellation_level,
614     const float *outer_tessellation_level, _tessellation_shader_vertex_spacing vertex_spacing,
615     bool is_point_mode_enabled)
616 {
617     unsigned int result = 0;
618 
619     switch (primitive_mode)
620     {
621     case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES:
622     case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
623     case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
624     {
625         /* We refer to a counter program, TC and TE stages are configured as specified
626          * by the caller. Before we issue the draw call, we begin a query introduced in
627          * GL_EXT_geometry_shader that allows us to count how many primitives would have been generated. Doing so
628          * allows us to determine how many vertices were generated during the processed.
629          */
630         const glw::GLint test_n_patch_vertices = 1;
631         _tessellation_vertex_counter_program test_program(m_gl);
632 
633         initTessellationVertexCounterProgram(inner_tessellation_level, outer_tessellation_level, test_n_patch_vertices,
634                                              vertex_spacing, primitive_mode, is_point_mode_enabled, test_program);
635 
636         result = test_program.n_data_vertices;
637         break;
638     }
639 
640     default:
641     {
642         TCU_FAIL("Unrecognized primitive mode");
643     }
644     } /* switch (primitive_mode) */
645 
646     return result;
647 }
648 
649 /** Retrieves data generated by a tessellator for a particular tessellation configuration.
650  *
651  *  @param inner           Two FP values defining inner tessellation values to be used for tessellation.
652  *                         Must not be NULL.
653  *  @param point_mode      true if point mode is to be used for tessellation, false otherwise.
654  *  @param primitive_mode  Primitive mode to be used for tessellation.
655  *  @param vertex_ordering Vertex ordering to be used for tessellation.
656  *  @param vertex_spacing  Vertex spacing to be used for tessellation.
657  *  @param outer           Four FP values defining outer tessellation values to be used for tessellation.
658  *                         Must not be NULL.
659  *
660  *  @return Pointer to buffer containing tessellated coordinates.
661  **/
getDataGeneratedByTessellator(const float * inner,bool point_mode,_tessellation_primitive_mode primitive_mode,_tessellation_shader_vertex_ordering vertex_ordering,_tessellation_shader_vertex_spacing vertex_spacing,const float * outer)662 std::vector<char> TessellationShaderUtils::getDataGeneratedByTessellator(
663     const float *inner, bool point_mode, _tessellation_primitive_mode primitive_mode,
664     _tessellation_shader_vertex_ordering vertex_ordering, _tessellation_shader_vertex_spacing vertex_spacing,
665     const float *outer)
666 {
667     (void)vertex_ordering;
668 
669     glw::GLint test_n_patch_vertices = getPatchVerticesForPrimitiveMode(primitive_mode);
670     _tessellation_vertex_counter_program test_program(m_gl);
671 
672     initTessellationVertexCounterProgram(inner, outer, test_n_patch_vertices, vertex_spacing, primitive_mode,
673                                          point_mode, test_program);
674     return test_program.m_data;
675 }
676 
677 /** Retrieves ESSL token corresponding to particular primitive mode.
678  *  Will throw TestError exception if @param primitive_mode is not
679  *  valid.
680  *
681  *  @param primitive_mode Primitive mode to consider.
682  *
683  *  @return String telling how the primitive mode would be expressed in
684  *          ES SL.
685  **/
getESTokenForPrimitiveMode(_tessellation_primitive_mode primitive_mode)686 std::string TessellationShaderUtils::getESTokenForPrimitiveMode(_tessellation_primitive_mode primitive_mode)
687 {
688     std::string result = "?";
689 
690     switch (primitive_mode)
691     {
692     case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES:
693     {
694         result = "isolines";
695 
696         break;
697     }
698 
699     case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
700     {
701         result = "quads";
702 
703         break;
704     }
705 
706     case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
707     {
708         result = "triangles";
709 
710         break;
711     }
712 
713     default:
714     {
715         TCU_FAIL("Unrecognized tessellation primitive mode");
716     }
717     }
718 
719     return result;
720 }
721 
722 /** Retrieves ESSL token corresponding to particular vertex ordering.
723  *  Will throw TestError exception if @param vertex_ordering is not
724  *  valid.
725  *
726  *  @param vertex_ordering Vertex ordering to consider.
727  *
728  *  @return String telling how the vertex mode would be expressed in
729  *          ES SL.
730  **/
getESTokenForVertexOrderingMode(_tessellation_shader_vertex_ordering vertex_ordering)731 std::string TessellationShaderUtils::getESTokenForVertexOrderingMode(
732     _tessellation_shader_vertex_ordering vertex_ordering)
733 {
734     std::string result;
735 
736     switch (vertex_ordering)
737     {
738     case TESSELLATION_SHADER_VERTEX_ORDERING_CCW:
739     {
740         result = "ccw";
741 
742         break;
743     }
744 
745     case TESSELLATION_SHADER_VERTEX_ORDERING_CW:
746     {
747         result = "cw";
748 
749         break;
750     }
751 
752     case TESSELLATION_SHADER_VERTEX_ORDERING_DEFAULT:
753     {
754         /* Simply return an empty token */
755         result = "";
756 
757         break;
758     }
759 
760     default:
761     {
762         TCU_FAIL("Unrecognized tessellation shader vertex ordering");
763     }
764     } /* switch (vertex_ordering) */
765 
766     return result;
767 }
768 
769 /** Retrieves ESSL token corresponding to particular vertex spacing mode.
770  *  Will throw TestError exception if @param vertex_spacing is not
771  *  valid.
772  *
773  *  @param vertex_spacing Vertex spacing mode to consider.
774  *
775  *  @return String telling how the vertex spacing mode would be expressed in
776  *          ES SL.
777  **/
getESTokenForVertexSpacingMode(_tessellation_shader_vertex_spacing vertex_spacing)778 std::string TessellationShaderUtils::getESTokenForVertexSpacingMode(_tessellation_shader_vertex_spacing vertex_spacing)
779 {
780     std::string result;
781 
782     switch (vertex_spacing)
783     {
784     case TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT:
785     {
786         result = "";
787 
788         break;
789     }
790 
791     case TESSELLATION_SHADER_VERTEX_SPACING_EQUAL:
792     {
793         result = "equal_spacing";
794 
795         break;
796     }
797 
798     case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN:
799     {
800         result = "fractional_even_spacing";
801 
802         break;
803     }
804 
805     case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD:
806     {
807         result = "fractional_odd_spacing";
808 
809         break;
810     }
811 
812     default:
813     {
814         TCU_FAIL("Invalid vertex spacing mode requested");
815     }
816     }
817 
818     return result;
819 }
820 
821 /** Tells how many vertices should be passed in a single patch for
822  *  particular primitive mode to work for tessellation stage.
823  *
824  *  Throws TestError exception if @param primitive_mode is invalid.
825  *
826  *  @param primitive_mode Primitive mode to consider.
827  *
828  *  @return Requested value.
829  **/
getPatchVerticesForPrimitiveMode(_tessellation_primitive_mode primitive_mode)830 glw::GLint TessellationShaderUtils::getPatchVerticesForPrimitiveMode(_tessellation_primitive_mode primitive_mode)
831 {
832     glw::GLint result = 0;
833 
834     switch (primitive_mode)
835     {
836     case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES:
837         result = 4;
838         break;
839     case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
840         result = 4;
841         break;
842     case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
843         result = 3;
844         break;
845 
846     default:
847     {
848         TCU_FAIL("Unrecognized primitive mode");
849     }
850     } /* switch (primitive_mode) */
851 
852     return result;
853 }
854 
855 /** Retrieves tessellation level used by the tessellator, given
856  *  vertex spacing setting.
857  *
858  *  @param vertex_spacing              Vertex spacing used for tessellation
859  *                                     evaluation stage;
860  *  @param level                       Tessellation level as defined in TC
861  *                                     stage OR as configured with
862  *                                     GL_PATCH_DEFAULT_*_LEVEL pnames.
863  *  @param gl_max_tess_gen_level_value GL_MAX_TESS_GEN_LEVEL_EXT pname value,
864  *                                     as reported by the implementation.
865  *  @param out_clamped                 Deref will be used to store clamped (but
866  *                                     not rounded) representation. Can be NULL.
867  *  @param out_clamped_and_rounded     Deref will be used to store clamped and
868  *                                     rounded representation. Can be NULL.
869  **/
getTessellationLevelAfterVertexSpacing(_tessellation_shader_vertex_spacing vertex_spacing,float level,glw::GLint gl_max_tess_gen_level_value,float * out_clamped,float * out_clamped_and_rounded)870 void TessellationShaderUtils::getTessellationLevelAfterVertexSpacing(_tessellation_shader_vertex_spacing vertex_spacing,
871                                                                      float level,
872                                                                      glw::GLint gl_max_tess_gen_level_value,
873                                                                      float *out_clamped, float *out_clamped_and_rounded)
874 {
875     /* Behavior is as per EXT_tessellation_shader spec */
876     switch (vertex_spacing)
877     {
878     case TESSELLATION_SHADER_VERTEX_SPACING_DEFAULT:
879     case TESSELLATION_SHADER_VERTEX_SPACING_EQUAL:
880     {
881         if (level < 1.0f)
882         {
883             level = 1.0f;
884         }
885         else if (level > (float)gl_max_tess_gen_level_value)
886         {
887             level = (float)gl_max_tess_gen_level_value;
888         }
889 
890         if (out_clamped != DE_NULL)
891         {
892             *out_clamped = level;
893         }
894 
895         /* Round *up* to nearest integer */
896         level = (float)((int)(deFloatCeil(level) + 0.5f));
897 
898         if (out_clamped_and_rounded != DE_NULL)
899         {
900             *out_clamped_and_rounded = level;
901         }
902 
903         break;
904     }
905 
906     case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_EVEN:
907     {
908         if (level < 2.0f)
909         {
910             level = 2.0f;
911         }
912         else if (level > (float)gl_max_tess_gen_level_value)
913         {
914             level = (float)gl_max_tess_gen_level_value;
915         }
916 
917         if (out_clamped != DE_NULL)
918         {
919             *out_clamped = level;
920         }
921 
922         /* Round *up* to nearest *even* integer */
923         int level_temp = (int)(deFloatCeil(level) + 0.5f);
924 
925         if ((level_temp % 2) != 0)
926         {
927             level_temp++;
928         }
929 
930         if (out_clamped_and_rounded != DE_NULL)
931         {
932             *out_clamped_and_rounded = (float)level_temp;
933         }
934 
935         break;
936     }
937 
938     case TESSELLATION_SHADER_VERTEX_SPACING_FRACTIONAL_ODD:
939     {
940         if (level < 1.0f)
941         {
942             level = 1.0f;
943         }
944         else if (level > (float)(gl_max_tess_gen_level_value - 1))
945         {
946             level = (float)(gl_max_tess_gen_level_value - 1);
947         }
948 
949         if (out_clamped != DE_NULL)
950         {
951             *out_clamped = level;
952         }
953 
954         /* Round to *up* nearest *odd* integer */
955         int level_temp = (int)(deFloatCeil(level) + 0.5f);
956 
957         if ((level_temp % 2) != 1)
958         {
959             level_temp++;
960         }
961 
962         if (out_clamped_and_rounded != DE_NULL)
963         {
964             *out_clamped_and_rounded = (float)level_temp;
965         }
966 
967         break;
968     }
969 
970     default:
971     {
972         TCU_FAIL("Unrecognized vertex spacing mode");
973     }
974     } /* switch(vertex_spacing) */
975 }
976 
977 /** Returns a vector of _tessellation_levels instances with different level values.
978  *
979  *  @param primitive_mode              Primitive mode to consider.
980  *  @param gl_max_tess_gen_level_value Implementation-specific GL_MAX_TESS_GEN_LEVEL_EXT value.
981  *  @param filter                      Condition which all generated tuples should meet in
982  *                                     order to land in the result vector.
983  *
984  *  @return _tessellation_levels_set instance storing described values.
985  **/
getTessellationLevelSetForPrimitiveMode(_tessellation_primitive_mode primitive_mode,glw::GLint gl_max_tess_gen_level_value,_tessellation_level_set_filter filter)986 _tessellation_levels_set TessellationShaderUtils::getTessellationLevelSetForPrimitiveMode(
987     _tessellation_primitive_mode primitive_mode, glw::GLint gl_max_tess_gen_level_value,
988     _tessellation_level_set_filter filter)
989 {
990     /* As a starter value, use a tessellation level that is different for each
991      * primitive modes, just to make sure the implementation can correctly
992      * handle various tessellation level values */
993     glw::GLint n_min_patch_vertices = getPatchVerticesForPrimitiveMode(primitive_mode);
994 
995     glw::GLint n_half_max_patch_vertices_mul_min = gl_max_tess_gen_level_value / 2;
996     glw::GLint n_max_patch_vertices_mul_min      = gl_max_tess_gen_level_value;
997 
998     if ((n_half_max_patch_vertices_mul_min % n_min_patch_vertices) != 0)
999     {
1000         /* Round to nearest mul-of-min integer */
1001         n_half_max_patch_vertices_mul_min +=
1002             (n_min_patch_vertices - (gl_max_tess_gen_level_value / 2) % n_min_patch_vertices);
1003     }
1004 
1005     if ((n_max_patch_vertices_mul_min % n_min_patch_vertices) != 0)
1006     {
1007         /* Round to previous nearest mul-of-min integer */
1008         n_max_patch_vertices_mul_min -= (gl_max_tess_gen_level_value % n_min_patch_vertices);
1009     }
1010 
1011     /* Prepare the result vector items */
1012     _tessellation_levels_set result;
1013 
1014     if ((filter & TESSELLATION_LEVEL_SET_FILTER_ALL_LEVELS_USE_THE_SAME_VALUE) != 0)
1015     {
1016         /* Prepare the result vector items */
1017         _tessellation_levels item_1;
1018         _tessellation_levels item_2;
1019         _tessellation_levels item_3;
1020 
1021         item_1.inner[0] = float(n_min_patch_vertices);
1022         item_1.inner[1] = float(n_min_patch_vertices + 1);
1023         item_1.outer[0] = float(n_min_patch_vertices + 3);
1024         item_1.outer[1] = float(n_min_patch_vertices + 2);
1025         item_1.outer[2] = float(n_min_patch_vertices + 1);
1026         item_1.outer[3] = float(n_min_patch_vertices);
1027 
1028         item_2.inner[0] = float(n_half_max_patch_vertices_mul_min);
1029         item_2.inner[1] = float(n_half_max_patch_vertices_mul_min - 1);
1030         item_2.outer[0] = float(n_half_max_patch_vertices_mul_min - 3);
1031         item_2.outer[1] = float(n_half_max_patch_vertices_mul_min - 2);
1032         item_2.outer[2] = float(n_half_max_patch_vertices_mul_min - 1);
1033         item_2.outer[3] = float(n_half_max_patch_vertices_mul_min);
1034 
1035         item_3.inner[0] = float(n_max_patch_vertices_mul_min - 1);
1036         item_3.inner[1] = float(n_max_patch_vertices_mul_min - 2);
1037         item_3.outer[0] = float(n_max_patch_vertices_mul_min - 3);
1038         item_3.outer[1] = float(n_max_patch_vertices_mul_min - 4);
1039         item_3.outer[2] = float(n_max_patch_vertices_mul_min - 5);
1040         item_3.outer[3] = float(n_max_patch_vertices_mul_min - 6);
1041 
1042         /* Push the items onto result vector. */
1043         result.push_back(item_1);
1044         result.push_back(item_2);
1045         result.push_back(item_3);
1046     }
1047     else
1048     {
1049         DE_ASSERT((filter & TESSELLATION_LEVEL_SET_FILTER_ALL_COMBINATIONS) != 0 ||
1050                   (filter & TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES) != 0);
1051 
1052         const glw::GLint base_values[]   = {-1, 1, n_half_max_patch_vertices_mul_min, n_max_patch_vertices_mul_min};
1053         const unsigned int n_base_values = DE_LENGTH_OF_ARRAY(base_values);
1054 
1055         const unsigned int n_relevant_inner_tess_levels =
1056             (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) ? 0 :
1057             (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS)    ? 2 :
1058                                                                               1;
1059 
1060         const unsigned int n_relevant_outer_tess_levels =
1061             (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES) ? 2 :
1062             (primitive_mode == TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS)    ? 4 :
1063                                                                               3;
1064 
1065         for (unsigned int n_inner0_base_value = 0;
1066              n_inner0_base_value < ((n_relevant_inner_tess_levels > 0) ? n_base_values : 1); ++n_inner0_base_value)
1067         {
1068             const glw::GLint inner0_value = base_values[n_inner0_base_value];
1069 
1070             for (unsigned int n_inner1_base_value = 0;
1071                  n_inner1_base_value < ((n_relevant_inner_tess_levels > 1) ? n_base_values : 1); ++n_inner1_base_value)
1072             {
1073                 const glw::GLint inner1_value = base_values[n_inner1_base_value];
1074 
1075                 for (unsigned int n_outer0_base_value = 0;
1076                      n_outer0_base_value < ((n_relevant_outer_tess_levels > 0) ? n_base_values : 1);
1077                      ++n_outer0_base_value)
1078                 {
1079                     const glw::GLint outer0_value = base_values[n_outer0_base_value];
1080 
1081                     for (unsigned int n_outer1_base_value = 0;
1082                          n_outer1_base_value < ((n_relevant_outer_tess_levels > 1) ? n_base_values : 1);
1083                          ++n_outer1_base_value)
1084                     {
1085                         const glw::GLint outer1_value = base_values[n_outer1_base_value];
1086 
1087                         for (unsigned int n_outer2_base_value = 0;
1088                              n_outer2_base_value < ((n_relevant_outer_tess_levels > 2) ? n_base_values : 1);
1089                              ++n_outer2_base_value)
1090                         {
1091                             const glw::GLint outer2_value = base_values[n_outer2_base_value];
1092 
1093                             for (unsigned int n_outer3_base_value = 0;
1094                                  n_outer3_base_value < ((n_relevant_outer_tess_levels > 3) ? n_base_values : 1);
1095                                  ++n_outer3_base_value)
1096                             {
1097                                 const glw::GLint outer3_value = base_values[n_outer3_base_value];
1098 
1099                                 /* Skip combinations where any of the relevant outer tessellation level values
1100                                  * is negative. These would cause no tessellation coordinates to be generated
1101                                  * by the tessellator.
1102                                  */
1103                                 if ((n_relevant_outer_tess_levels > 0 && outer0_value < 0) ||
1104                                     (n_relevant_outer_tess_levels > 1 && outer1_value < 0) ||
1105                                     (n_relevant_outer_tess_levels > 2 && outer2_value < 0) ||
1106                                     (n_relevant_outer_tess_levels > 3 && outer3_value < 0))
1107                                 {
1108                                     continue;
1109                                 }
1110 
1111                                 /* If TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES
1112                                  * filter was requested, make sure the values used separately for inner and outer
1113                                  * tess levels are actually different. */
1114                                 if ((filter &
1115                                      TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES) != 0)
1116                                 {
1117                                     DE_ASSERT(n_base_values >= 4 /* outer tess levels supported */);
1118 
1119                                     if ((n_relevant_inner_tess_levels > 1 && inner0_value == inner1_value) ||
1120                                         (n_relevant_outer_tess_levels > 1 && outer0_value == outer1_value) ||
1121                                         (n_relevant_outer_tess_levels > 2 && outer1_value == outer2_value) ||
1122                                         (n_relevant_outer_tess_levels > 3 && outer2_value == outer3_value))
1123                                     {
1124                                         continue;
1125                                     }
1126                                 } /* if ((filter & TESSELLATION_LEVEL_SET_FILTER_INNER_AND_OUTER_LEVELS_USE_DIFFERENT_VALUES) != 0) */
1127 
1128                                 /* If TESSELLATION_LEVEL_SET_FILTER_EXCLUDE_NEGATIVE_BASE_VALUE, make sure
1129                                  * no inner/outer tessellation level we're about to use is negative. */
1130                                 if ((filter & TESSELLATION_LEVEL_SET_FILTER_EXCLUDE_NEGATIVE_BASE_VALUE) != 0)
1131                                 {
1132                                     if ((n_relevant_inner_tess_levels > 0 && inner0_value < 0) ||
1133                                         (n_relevant_inner_tess_levels > 1 && inner1_value < 0) ||
1134                                         (n_relevant_outer_tess_levels > 0 && outer0_value < 0) ||
1135                                         (n_relevant_outer_tess_levels > 1 && outer1_value < 0) ||
1136                                         (n_relevant_outer_tess_levels > 2 && outer2_value < 0) ||
1137                                         (n_relevant_outer_tess_levels > 3 && outer3_value < 0))
1138                                     {
1139                                         continue;
1140                                     }
1141                                 }
1142 
1143                                 /* Construct the tess level combination */
1144                                 _tessellation_levels item;
1145 
1146                                 item.inner[0] = (glw::GLfloat)inner0_value;
1147                                 item.inner[1] = (glw::GLfloat)inner1_value;
1148                                 item.outer[0] = (glw::GLfloat)outer0_value;
1149                                 item.outer[1] = (glw::GLfloat)outer1_value;
1150                                 item.outer[2] = (glw::GLfloat)outer2_value;
1151                                 item.outer[3] = (glw::GLfloat)outer3_value;
1152 
1153                                 /* Store it */
1154                                 result.push_back(item);
1155                             } /* for (all outer[3] base values) */
1156                         }     /* for (all outer[2] base values) */
1157                     }         /* for (all outer[1] base values) */
1158                 }             /* for (all outer[0] base values) */
1159             }                 /* for (all inner[1] base values) */
1160         }                     /* for (all inner[0] base values) */
1161     }
1162 
1163     return result;
1164 }
1165 
1166 /** Retrieves transform feedback mode that should be used for glBeginTransformFeedback()
1167  *  call, if TF is to be active while a tessellated draw call is made.
1168  *
1169  *  This function throws TestError exception if @param primitive_mode is invalid.
1170  *
1171  *  @param primitive_mode Primitive mode to consider
1172  *  @param is_point_mode  true if tessellation is run in point_mode mode, false otherwise.
1173  *
1174  *  @return Corresponding ES enum.
1175  **/
getTFModeForPrimitiveMode(_tessellation_primitive_mode primitive_mode,bool is_point_mode)1176 glw::GLenum TessellationShaderUtils::getTFModeForPrimitiveMode(_tessellation_primitive_mode primitive_mode,
1177                                                                bool is_point_mode)
1178 {
1179     glw::GLenum result = GL_NONE;
1180 
1181     if (is_point_mode)
1182     {
1183         result = GL_POINTS;
1184     }
1185     else
1186     {
1187         switch (primitive_mode)
1188         {
1189         case TESSELLATION_SHADER_PRIMITIVE_MODE_ISOLINES:
1190         {
1191             result = GL_LINES;
1192 
1193             break;
1194         }
1195 
1196         case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
1197         {
1198             result = GL_TRIANGLES;
1199 
1200             break;
1201         }
1202 
1203         case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
1204         {
1205             result = GL_TRIANGLES;
1206 
1207             break;
1208         }
1209 
1210         default:
1211         {
1212             TCU_FAIL("Unrecognized primitive mode");
1213         }
1214         } /* switch (primitive_mode) */
1215     }
1216 
1217     return result;
1218 }
1219 
1220 /** Initializes a counter program.
1221  *
1222  *  This function throws a TestError exception, should an error occur.
1223  *
1224  *  @param inner_tess_level      Two FP values to be used for inner tessellation levels. Must not be NULL.
1225  *  @param outer_tess_level      Four FP values to be used for outer tessellation levels. Must not be NULL.
1226  *  @param n_patch_vertices      Amount of TC stage output patch vertices.
1227  *  @param vertex_spacing        Vertex spacing mode to be used for tessellation.
1228  *  @param primitive_mode        Primitive mode to be used for tessellation.
1229  *  @param is_point_mode_enabled true if the point mode should be enabled for the program, false otherwise.
1230  *  @param result_descriptor     Objects created during initialization will be stored in the referenced descriptor.
1231  *
1232  **/
initTessellationVertexCounterProgram(const float * inner_tess_level,const float * outer_tess_level,glw::GLint n_patch_vertices,_tessellation_shader_vertex_spacing vertex_spacing,_tessellation_primitive_mode primitive_mode,bool is_point_mode_enabled,_tessellation_vertex_counter_program & result_descriptor)1233 void TessellationShaderUtils::initTessellationVertexCounterProgram(
1234     const float *inner_tess_level, const float *outer_tess_level, glw::GLint n_patch_vertices,
1235     _tessellation_shader_vertex_spacing vertex_spacing, _tessellation_primitive_mode primitive_mode,
1236     bool is_point_mode_enabled, _tessellation_vertex_counter_program &result_descriptor)
1237 {
1238     glw::GLint po_id = 0;
1239     glw::GLint tc_id = 0;
1240     glw::GLint te_id = 0;
1241 
1242     /* Generate the shader objects */
1243     tc_id = m_gl.createShader(m_parent_test->m_glExtTokens.TESS_CONTROL_SHADER);
1244     te_id = m_gl.createShader(m_parent_test->m_glExtTokens.TESS_EVALUATION_SHADER);
1245 
1246     GLU_EXPECT_NO_ERROR(m_gl.getError(), "Could not create TC/TE shader objects");
1247 
1248     /* Generate the program object */
1249     po_id = m_gl.createProgram();
1250 
1251     GLU_EXPECT_NO_ERROR(m_gl.getError(), "Could not create a program object");
1252 
1253     /* Initialize the shaders.
1254      *
1255      * Note: it's fine to use CCW ordering here, since it does not affect the amount
1256      *       of primitives generated by the tessellator.
1257      **/
1258     std::string tc_code     = getGenericTCCode(n_patch_vertices, false);
1259     const char *tc_code_ptr = tc_code.c_str();
1260     std::string te_code     = getGenericTECode(vertex_spacing, primitive_mode, TESSELLATION_SHADER_VERTEX_ORDERING_CCW,
1261                                                is_point_mode_enabled);
1262     const char *te_code_ptr = te_code.c_str();
1263 
1264     /* Set up XFB */
1265     const char *varyings[] = {"result_uvw"};
1266 
1267     m_gl.transformFeedbackVaryings(po_id, 1, /* count */
1268                                    varyings, GL_INTERLEAVED_ATTRIBS);
1269     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glTransformFeedbackVaryings() call failed");
1270 
1271     /* Link the program and check that the linking has succeeded */
1272     bool build_success = m_parent_test->buildProgram(po_id, m_fs_id, 0 /* precompiled */, NULL, tc_id, 1, &tc_code_ptr,
1273                                                      te_id, 1, &te_code_ptr, m_vs_id, 0 /* precompiled */, NULL);
1274 
1275     if (!build_success)
1276     {
1277         TCU_FAIL("Compilation and/or linking failed");
1278     }
1279 
1280     /* Set up the inner/outer tess level uniforms */
1281     glw::GLint inner_tess_level_uniform_location = -1;
1282     glw::GLint outer_tess_level_uniform_location = -1;
1283 
1284     inner_tess_level_uniform_location = m_gl.getUniformLocation(po_id, "inner_tess_level");
1285     outer_tess_level_uniform_location = m_gl.getUniformLocation(po_id, "outer_tess_level");
1286 
1287     DE_ASSERT(inner_tess_level_uniform_location != -1);
1288     DE_ASSERT(outer_tess_level_uniform_location != -1);
1289 
1290     m_gl.useProgram(po_id);
1291     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() call failed");
1292 
1293     m_gl.uniform2fv(inner_tess_level_uniform_location, 1, /* count */
1294                     inner_tess_level);
1295     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUniform2fv() call failed");
1296 
1297     m_gl.uniform4fv(outer_tess_level_uniform_location, 1, /* count */
1298                     outer_tess_level);
1299     GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUniform4fv() call failed");
1300 
1301     /* Initialize the test descriptor */
1302     memcpy(result_descriptor.inner_tess_level, inner_tess_level, sizeof(result_descriptor.inner_tess_level));
1303     memcpy(result_descriptor.outer_tess_level, outer_tess_level, sizeof(result_descriptor.outer_tess_level));
1304 
1305     result_descriptor.is_point_mode_enabled             = is_point_mode_enabled;
1306     result_descriptor.n_patch_vertices                  = n_patch_vertices;
1307     result_descriptor.po_id                             = po_id;
1308     result_descriptor.primitive_mode                    = primitive_mode;
1309     result_descriptor.tc_id                             = tc_id;
1310     result_descriptor.te_id                             = te_id;
1311     result_descriptor.tess_level_inner_uniform_location = inner_tess_level_uniform_location;
1312     result_descriptor.tess_level_outer_uniform_location = outer_tess_level_uniform_location;
1313     result_descriptor.vertex_spacing                    = vertex_spacing;
1314 
1315     captureTessellationData(result_descriptor);
1316 }
1317 
1318 /** Tells whether user-provided vertex (expressed in tessellation space) generated
1319  *  during triangle tessellation is an outer edge vertex.
1320  *
1321  *  @param primitive_mode          Primitive mode, for which the tessellated vertex
1322  *                                 data was generated.
1323  *  @param tessellated_vertex_data Vertex data to check. Must define 3 floats.
1324  *
1325  *  @return true if the vertex is a part of an outer edge, false otherwise.
1326  **/
isOuterEdgeVertex(_tessellation_primitive_mode primitive_mode,const float * tessellated_vertex_data)1327 bool TessellationShaderUtils::isOuterEdgeVertex(_tessellation_primitive_mode primitive_mode,
1328                                                 const float *tessellated_vertex_data)
1329 {
1330     const float epsilon = 1e-5f;
1331     bool result         = false;
1332 
1333     switch (primitive_mode)
1334     {
1335     case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES:
1336     {
1337         if (de::abs(tessellated_vertex_data[0]) < epsilon || de::abs(tessellated_vertex_data[0] - 1.0f) < epsilon ||
1338             de::abs(tessellated_vertex_data[1]) < epsilon || de::abs(tessellated_vertex_data[1] - 1.0f) < epsilon ||
1339             de::abs(tessellated_vertex_data[2]) < epsilon || de::abs(tessellated_vertex_data[2] - 1.0f) < epsilon)
1340         {
1341             /* Make sure vertex is inside the triangle */
1342             if (0.0f <= tessellated_vertex_data[0] && tessellated_vertex_data[0] <= 1.0f &&
1343                 0.0f <= tessellated_vertex_data[1] && tessellated_vertex_data[1] <= 1.0f &&
1344                 0.0f <= tessellated_vertex_data[2] && tessellated_vertex_data[2] <= 1.0f)
1345             {
1346                 result = true;
1347             }
1348         }
1349 
1350         break;
1351     } /* case TESSELLATION_SHADER_PRIMITIVE_MODE_TRIANGLES: */
1352 
1353     case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS:
1354     {
1355         if (de::abs(tessellated_vertex_data[0]) < epsilon || de::abs(tessellated_vertex_data[0] - 1.0f) < epsilon ||
1356             de::abs(tessellated_vertex_data[1]) < epsilon || de::abs(tessellated_vertex_data[1] - 1.0f) < epsilon)
1357         {
1358             result = true;
1359         }
1360 
1361         break;
1362     } /* case TESSELLATION_SHADER_PRIMITIVE_MODE_QUADS: */
1363 
1364     default:
1365     {
1366         DE_FATAL("Unrecognized primitive mode");
1367     }
1368     }
1369 
1370     return result;
1371 }
1372 
1373 /** Returns true if two triangles are the same. Takes potentially different
1374  *  vertex ordering into consideration.
1375  *
1376  *  @param triangle_vertex_data Reference 9 FP values defining a triangle
1377  *                              that the triangle defined by @param vertex_data
1378  *                              will be compared against.
1379  *  @param vertex_data          9 FP values defining a triangle, perhaps in
1380  *                              a different order, that @param triangle_vertex_data
1381  *                              will be checked against.
1382  *
1383  *  @return true if the triangles are the same; false otherwise.
1384  *
1385  **/
isTriangleDefined(const float * triangle_vertex_data,const float * vertex_data)1386 bool TessellationShaderUtils::isTriangleDefined(const float *triangle_vertex_data, const float *vertex_data)
1387 {
1388     const float epsilon                  = 1e-5f;
1389     bool has_triangle_vertex1_been_found = false;
1390     bool has_triangle_vertex2_been_found = false;
1391     bool has_triangle_vertex3_been_found = false;
1392     bool result                          = false;
1393 
1394     if ((de::abs(triangle_vertex_data[0] - vertex_data[0]) < epsilon &&
1395          de::abs(triangle_vertex_data[1] - vertex_data[1]) < epsilon &&
1396          de::abs(triangle_vertex_data[2] - vertex_data[2]) < epsilon) ||
1397         (de::abs(triangle_vertex_data[3] - vertex_data[0]) < epsilon &&
1398          de::abs(triangle_vertex_data[4] - vertex_data[1]) < epsilon &&
1399          de::abs(triangle_vertex_data[5] - vertex_data[2]) < epsilon) ||
1400         (de::abs(triangle_vertex_data[6] - vertex_data[0]) < epsilon &&
1401          de::abs(triangle_vertex_data[7] - vertex_data[1]) < epsilon &&
1402          de::abs(triangle_vertex_data[8] - vertex_data[2]) < epsilon))
1403     {
1404         has_triangle_vertex1_been_found = true;
1405     }
1406 
1407     if ((de::abs(triangle_vertex_data[0] - vertex_data[3]) < epsilon &&
1408          de::abs(triangle_vertex_data[1] - vertex_data[4]) < epsilon &&
1409          de::abs(triangle_vertex_data[2] - vertex_data[5]) < epsilon) ||
1410         (de::abs(triangle_vertex_data[3] - vertex_data[3]) < epsilon &&
1411          de::abs(triangle_vertex_data[4] - vertex_data[4]) < epsilon &&
1412          de::abs(triangle_vertex_data[5] - vertex_data[5]) < epsilon) ||
1413         (de::abs(triangle_vertex_data[6] - vertex_data[3]) < epsilon &&
1414          de::abs(triangle_vertex_data[7] - vertex_data[4]) < epsilon &&
1415          de::abs(triangle_vertex_data[8] - vertex_data[5]) < epsilon))
1416     {
1417         has_triangle_vertex2_been_found = true;
1418     }
1419 
1420     if ((de::abs(triangle_vertex_data[0] - vertex_data[6]) < epsilon &&
1421          de::abs(triangle_vertex_data[1] - vertex_data[7]) < epsilon &&
1422          de::abs(triangle_vertex_data[2] - vertex_data[8]) < epsilon) ||
1423         (de::abs(triangle_vertex_data[3] - vertex_data[6]) < epsilon &&
1424          de::abs(triangle_vertex_data[4] - vertex_data[7]) < epsilon &&
1425          de::abs(triangle_vertex_data[5] - vertex_data[8]) < epsilon) ||
1426         (de::abs(triangle_vertex_data[6] - vertex_data[6]) < epsilon &&
1427          de::abs(triangle_vertex_data[7] - vertex_data[7]) < epsilon &&
1428          de::abs(triangle_vertex_data[8] - vertex_data[8]) < epsilon))
1429     {
1430         has_triangle_vertex3_been_found = true;
1431     }
1432 
1433     if (has_triangle_vertex1_been_found && has_triangle_vertex2_been_found && has_triangle_vertex3_been_found)
1434     {
1435         result = true;
1436     }
1437 
1438     return result;
1439 }
1440 
1441 } // namespace glcts
1442