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, ¤t_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, ¤t_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