1 /*-------------------------------------------------------------------------
2 * OpenGL Conformance Test Suite
3 * -----------------------------
4 *
5 * Copyright (c) 2015-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 /* Includes. */
25 #include "glcClipDistance.hpp"
26 #include "gluContextInfo.hpp"
27 #include "gluDefs.hpp"
28 #include "gluRenderContext.hpp"
29 #include "gluShaderUtil.hpp"
30 #include "gluStrUtil.hpp"
31 #include "tcuStringTemplate.hpp"
32 #include "tcuTestLog.hpp"
33
34 #include <cmath>
35 #include <sstream>
36
37 /* Stringify macro. */
38 #define _STR(s) STR(s)
39 #define STR(s) #s
40
41 /* In OpenGL 3.0 specification GL_CLIP_DISTANCEi is named GL_CLIP_PLANEi */
42 #ifndef GL_CLIP_DISTANCE0
43 #define GL_CLIP_DISTANCE0 GL_CLIP_PLANE0
44 #endif
45
46 /* In OpenGL 3.0 specification GL_MAX_CLIP_DISTANCES is named GL_MAX_CLIP_PLANES */
47 #ifndef GL_MAX_CLIP_DISTANCES
48 #define GL_MAX_CLIP_DISTANCES GL_MAX_CLIP_PLANES
49 #endif
50
51 /******************************** Test Group Implementation ********************************/
52
53 /** @brief Clip distances tests group constructor.
54 *
55 * @param [in] context OpenGL context.
56 */
Tests(deqp::Context & context)57 glcts::ClipDistance::Tests::Tests(deqp::Context &context)
58 : TestCaseGroup(context, "clip_distance", "Clip Distance Test Suite")
59 {
60 /* Intentionally left blank */
61 }
62
63 /** @brief Clip distances tests initializer. */
init()64 void glcts::ClipDistance::Tests::init()
65 {
66 addChild(new glcts::ClipDistance::CoverageTest(m_context));
67 addChild(new glcts::ClipDistance::FunctionalTest(m_context));
68 addChild(new glcts::ClipDistance::NegativeTest(m_context));
69 }
70
71 /****************************** Coverage Tests Base Implementation *****************************/
72
73 /** @brief API base tests constructor.
74 *
75 * @param context Rendering context
76 * @param name Test name
77 * @param description Test description
78 */
ClipDistanceTestBase(deqp::Context & context,const char * name,const char * description)79 glcts::ClipDistance::ClipDistanceTestBase::ClipDistanceTestBase(deqp::Context &context, const char *name,
80 const char *description)
81 : TestCase(context, name, description)
82 , m_extensionSupported(false)
83 , m_isContextES(false)
84 {
85 const glu::RenderContext &renderContext = m_context.getRenderContext();
86 glu::GLSLVersion glslVersion = glu::getContextTypeGLSLVersion(renderContext.getType());
87 m_isContextES = glu::isContextTypeES(renderContext.getType());
88
89 specializationMap["VERSION"] = glu::getGLSLVersionDeclaration(glslVersion);
90 if (m_isContextES)
91 {
92 specializationMap["EXTENSION"] = "#extension GL_EXT_clip_cull_distance : enable";
93 specializationMap["PRECISION"] = "precision highp float;";
94 }
95 else
96 {
97 specializationMap["EXTENSION"] = "";
98 specializationMap["PRECISION"] = "";
99 }
100
101 auto contextType = m_context.getRenderContext().getType();
102 if (m_isContextES)
103 {
104 if (glu::contextSupports(contextType, glu::ApiType::es(3, 0)))
105 {
106 m_extensionSupported = context.getContextInfo().isExtensionSupported("GL_EXT_clip_cull_distance");
107 }
108 }
109 else
110 {
111 /* This test should only be executed if we're running a GL>=3.0 context */
112 if (glu::contextSupports(contextType, glu::ApiType::core(3, 0)))
113 {
114 m_extensionSupported = true;
115 }
116 }
117 }
118
iterate()119 tcu::TestNode::IterateResult glcts::ClipDistance::ClipDistanceTestBase::iterate()
120 {
121 if (!m_extensionSupported)
122 {
123 m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
124 if (!m_isContextES)
125 {
126 /* This test should only be executed if we're running a GL3.0 context */
127 throw tcu::NotSupportedError("GL_ARB_clip_distance is not supported");
128 }
129 else
130 {
131 throw tcu::NotSupportedError("GL_EXT_clip_cull_distance is not supported");
132 }
133 return STOP;
134 }
135
136 test();
137
138 return STOP;
139 }
140
141 /******************************** Coverage Tests Implementation ********************************/
142
143 /** @brief API coverage tests constructor.
144 *
145 * @param [in] context OpenGL context.
146 */
CoverageTest(deqp::Context & context)147 glcts::ClipDistance::CoverageTest::CoverageTest(deqp::Context &context)
148 : ClipDistanceTestBase(context, "coverage", "Clip Distance API Coverage Test")
149 , m_gl_max_clip_distances_value(0)
150 {
151 /* Intentionally left blank. */
152 }
153
154 /** @brief API coverage tests.
155 *
156 */
test()157 void glcts::ClipDistance::CoverageTest::test()
158 {
159 /* Shortcut for GL functionality */
160 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
161
162 /* Running tests. */
163 bool is_ok = true;
164
165 is_ok = is_ok && MaxClipDistancesValueTest(gl);
166 is_ok = is_ok && EnableDisableTest(gl);
167 is_ok = is_ok && MaxClipDistancesValueInVertexShaderTest(gl);
168 is_ok = is_ok && MaxClipDistancesValueInFragmentShaderTest(gl);
169 is_ok = is_ok && ClipDistancesValuePassing(gl);
170
171 /* Result's setup. */
172 if (is_ok)
173 {
174 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
175 }
176 else
177 {
178 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
179 }
180 }
181
182 /* @brief glGet GL_MAX_CLIP_DISTANCES limit coverage test.
183 *
184 * @param [in] gl OpenGL functions' access.
185 *
186 * @return True if passed, false otherwise.
187 */
MaxClipDistancesValueTest(const glw::Functions & gl)188 bool glcts::ClipDistance::CoverageTest::MaxClipDistancesValueTest(const glw::Functions &gl)
189 {
190 /* Check that calling GetIntegerv with GL_MAX_CLIP_DISTANCES doesn't
191 generate any errors and returns a value at least 6 in OpenGL 3.0
192 or 8 in OpenGL 3.1 or OpenGL ES 3.2 and higher (see issues). */
193
194 glw::GLint error_code = GL_NO_ERROR;
195
196 gl.getIntegerv(GL_MAX_CLIP_DISTANCES, &m_gl_max_clip_distances_value);
197
198 error_code = gl.getError();
199
200 if (error_code != GL_NO_ERROR)
201 {
202 m_testCtx.getLog() << tcu::TestLog::Message << "glGetIntegerv(" << STR(GL_MAX_CLIP_DISTANCES)
203 << ") returned error code " << glu::getErrorStr(error_code) << " instead of GL_NO_ERROR."
204 << tcu::TestLog::EndMessage;
205
206 return false;
207 }
208 else
209 {
210 glw::GLint gl_max_clip_distances_minimum_value = 6; /* OpenGL 3.0 Specification minimum value */
211
212 auto contextType = m_context.getRenderContext().getType();
213 /* OpenGL 3.1 specification minimum value, see bug #4803 */
214 /* or OpenGL 3.0 ES and extension GL_EXT_clip_cull_distance minimum value */
215 if (glu::contextSupports(contextType, glu::ApiType(3, 1, glu::PROFILE_CORE)) || m_isContextES)
216 {
217 gl_max_clip_distances_minimum_value = 8;
218 }
219
220 if (m_gl_max_clip_distances_value < gl_max_clip_distances_minimum_value)
221 {
222 m_testCtx.getLog() << tcu::TestLog::Message << "Value of " << STR(GL_MAX_CLIP_DISTANCES) << "is equal to "
223 << m_gl_max_clip_distances_value << " which is less than minimum required ("
224 << gl_max_clip_distances_minimum_value << ")." << tcu::TestLog::EndMessage;
225
226 return false;
227 }
228 }
229
230 return true;
231 }
232
233 /* @brief glEnable / glDisable of GL_CLIP_DISTANCEi coverage test.
234 *
235 * @param [in] gl OpenGL functions' access.
236 *
237 * @return True if passed, false otherwise.
238 */
EnableDisableTest(const glw::Functions & gl)239 bool glcts::ClipDistance::CoverageTest::EnableDisableTest(const glw::Functions &gl)
240 {
241 /* Check that calling Enable and Disable with GL_CLIP_DISTANCEi for all
242 available clip distances does not generate errors.
243 glw::GLint error_code = GL_NO_ERROR; */
244
245 glw::GLint error_code = GL_NO_ERROR;
246
247 /* Test glEnable */
248 for (glw::GLint i = 0; i < m_gl_max_clip_distances_value; ++i)
249 {
250 gl.enable(GL_CLIP_DISTANCE0 + i);
251
252 error_code = gl.getError();
253
254 if (error_code != GL_NO_ERROR)
255 {
256 m_testCtx.getLog() << tcu::TestLog::Message << "glEnable(GL_CLIP_DISTANCE" << i << ") returned error code "
257 << glu::getErrorStr(error_code) << " instead of GL_NO_ERROR."
258 << tcu::TestLog::EndMessage;
259
260 return false;
261 }
262 }
263
264 /* Test glDisable */
265 for (glw::GLint i = 0; i < m_gl_max_clip_distances_value; ++i)
266 {
267 gl.disable(GL_CLIP_DISTANCE0 + i);
268
269 error_code = gl.getError();
270
271 if (error_code != GL_NO_ERROR)
272 {
273 m_testCtx.getLog() << tcu::TestLog::Message << "glDisable(GL_CLIP_DISTANCE" << i << ") returned error code "
274 << glu::getErrorStr(error_code) << " instead of GL_NO_ERROR."
275 << tcu::TestLog::EndMessage;
276
277 return false;
278 }
279 }
280
281 return true;
282 }
283
284 /* @brief gl_MaxClipDistances value test in the vertex shader coverage test.
285 *
286 * @param [in] gl OpenGL functions' access.
287 *
288 * @return True if passed, false otherwise.
289 */
MaxClipDistancesValueInVertexShaderTest(const glw::Functions & gl)290 bool glcts::ClipDistance::CoverageTest::MaxClipDistancesValueInVertexShaderTest(const glw::Functions &gl)
291 {
292 /* Make a program that consist of vertex and fragment shader stages. A
293 vertex shader shall assign the value of gl_MaxClipDistances to transform
294 feedback output variable. Setup gl_Position with passed in attribute.
295 Use blank fragment shader. Check that the shaders compiles and links
296 successfully. Draw a single GL_POINT with screen centered position
297 attribute, a configured transform feedback and GL_RASTERIZER_DISCARD.
298 Query transform feedback value and compare it against
299 GL_MAX_CLIP_DISTANCES. Expect that both values are equal. */
300
301 /* Building program. */
302 std::string vertex_shader = tcu::StringTemplate(m_vertex_shader_code_case_0).specialize(specializationMap);
303 std::string fragment_shader = tcu::StringTemplate(m_fragment_shader_code_case_0).specialize(specializationMap);
304
305 std::string transform_feedback_varying_name = "max_value";
306
307 std::vector<std::string> transform_feedback_varyings(1, transform_feedback_varying_name);
308
309 glcts::ClipDistance::Utility::Program program(gl, vertex_shader, fragment_shader, transform_feedback_varyings);
310
311 if (program.ProgramStatus().program_id == 0)
312 {
313 m_testCtx.getLog() << tcu::TestLog::Message << "Program failed to build.\n Vertex Shader:\n"
314 << vertex_shader << "\nWith Vertex Shader compilation log:\n"
315 << program.VertexShaderStatus().shader_log << "\nWith Fragment Shader:\n"
316 << fragment_shader << "\nWith Fragment Shader compilation log:\n"
317 << program.FragmentShaderStatus().shader_log << "\nWith Program linkage log:\n"
318 << program.ProgramStatus().program_linkage_log << "\n"
319 << tcu::TestLog::EndMessage;
320 return false;
321 }
322
323 program.UseProgram();
324
325 /* Creating and binding empty VAO. */
326 glcts::ClipDistance::Utility::VertexArrayObject vertex_array_object(gl, GL_POINTS);
327
328 /* Creating and binding output VBO */
329 glcts::ClipDistance::Utility::VertexBufferObject<glw::GLint> vertex_buffer_object(gl, GL_TRANSFORM_FEEDBACK_BUFFER,
330 std::vector<glw::GLint>(1, 0));
331
332 /* Draw test. */
333 vertex_array_object.drawWithTransformFeedback(0, 1, true);
334
335 /* Check results. */
336 std::vector<glw::GLint> results = vertex_buffer_object.readBuffer(m_isContextES);
337
338 if (results.size() < 1)
339 {
340 m_testCtx.getLog() << tcu::TestLog::Message << "Results reading error." << tcu::TestLog::EndMessage;
341 return false;
342 }
343
344 if (results[0] != m_gl_max_clip_distances_value)
345 {
346 m_testCtx.getLog() << tcu::TestLog::Message
347 << "Vertex shader's gl_MaxClipDistances constant has improper value equal to " << results[0]
348 << "but " << m_gl_max_clip_distances_value << "is expected. Test failed."
349 << tcu::TestLog::EndMessage;
350 return false;
351 }
352
353 /* Test passed. */
354 return true;
355 }
356
357 /* @brief gl_MaxClipDistances value test in the fragment shader coverage test.
358 *
359 * @param [in] gl OpenGL functions' access.
360 *
361 * @return True if passed, false otherwise.
362 */
MaxClipDistancesValueInFragmentShaderTest(const glw::Functions & gl)363 bool glcts::ClipDistance::CoverageTest::MaxClipDistancesValueInFragmentShaderTest(const glw::Functions &gl)
364 {
365 /* Make a program that consist of vertex and fragment shader stages. In
366 vertex shader setup gl_Position with passed in attribute. Check in
367 fragment shader using "if" statement that gl_MaxClipDistances is equal
368 to GL_MAX_CLIP_DISTANCES passed by uniform. If compared values are not
369 equal, discard the fragment. Output distinguishable color otherwise.
370 Check that the shader program compiles and links successfully. Draw a
371 single GL_POINT with screen centered position attribute and with a
372 configured 1 x 1 pixel size framebuffer. Using glReadPixels function,
373 check that point's fragments were not discarded. */
374
375 /* Creating red-color-only frambuffer. */
376 glcts::ClipDistance::Utility::Framebuffer framebuffer(gl, 1, 1);
377
378 if (!framebuffer.isValid())
379 {
380 m_testCtx.getLog() << tcu::TestLog::Message << "Unable to create framebuffer with size [1,1].\n"
381 << tcu::TestLog::EndMessage;
382 return false;
383 }
384
385 framebuffer.bind();
386 framebuffer.clear();
387
388 /* Building program. */
389 std::string vertex_shader = tcu::StringTemplate(m_vertex_shader_code_case_1).specialize(specializationMap);
390 std::string fragment_shader = tcu::StringTemplate(m_fragment_shader_code_case_1).specialize(specializationMap);
391
392 glcts::ClipDistance::Utility::Program program(gl, vertex_shader, fragment_shader);
393
394 if (program.ProgramStatus().program_id == 0)
395 {
396 m_testCtx.getLog() << tcu::TestLog::Message << "Program failed to build.\n Vertex Shader:\n"
397 << vertex_shader << "\nWith Vertex Shader compilation log:\n"
398 << program.VertexShaderStatus().shader_log << "\nWith Fragment Shader:\n"
399 << fragment_shader << "\nWith Fragment Shader compilation log:\n"
400 << program.FragmentShaderStatus().shader_log << "\nWith Program linkage log:\n"
401 << program.ProgramStatus().program_linkage_log << "\n"
402 << tcu::TestLog::EndMessage;
403
404 return false;
405 }
406
407 program.UseProgram();
408
409 /* Creating empty VAO. */
410 glcts::ClipDistance::Utility::VertexArrayObject vertex_array_object(gl, GL_POINTS);
411
412 /* Draw test. */
413 vertex_array_object.draw(0, 1);
414
415 /* Fetch results. */
416 std::vector<glw::GLfloat> pixels = framebuffer.readPixels();
417
418 if (pixels.size() < 1)
419 {
420 m_testCtx.getLog() << tcu::TestLog::Message << "ReadPixels error.\n" << tcu::TestLog::EndMessage;
421 return false;
422 }
423
424 /* Check results. */
425 glw::GLuint gl_max_clip_distances_value_in_fragment_shader = glw::GLuint(pixels.front());
426
427 if (gl_max_clip_distances_value_in_fragment_shader != glw::GLuint(m_gl_max_clip_distances_value))
428 {
429 m_testCtx.getLog() << tcu::TestLog::Message
430 << "Fragment shader's gl_MaxClipDistances constant has improper value equal to "
431 << gl_max_clip_distances_value_in_fragment_shader << "but " << m_gl_max_clip_distances_value
432 << "is expected. Test failed." << tcu::TestLog::EndMessage;
433 return false;
434 }
435
436 /* Test passed. */
437 return true;
438 }
439
440 /* @brief Vertex shader to fragment shader passing coverage test.
441 *
442 * @param [in] gl OpenGL functions' access.
443 *
444 * @return True if passed, false otherwise.
445 */
ClipDistancesValuePassing(const glw::Functions & gl)446 bool glcts::ClipDistance::CoverageTest::ClipDistancesValuePassing(const glw::Functions &gl)
447 {
448 /* Make a program that consist of vertex and fragment shader stages.
449 Redeclare gl_ClipDistance with size equal to GL_MAX_CLIP_DISTANCES in
450 vertex and fragment shader. In vertex shader, assign values to
451 gl_ClipDistance array using function of clip distance index i:
452
453 f(i) = float(i + 1) / float(gl_MaxClipDistances).
454
455 Setup gl_Position with passed in attribute. Read gl_ClipDistance in the
456 fragment shader and compare them with the same function. Take into
457 account low precision errors. If compared values are not equal, discard
458 the fragment. Output distinguishable color otherwise. Check that the
459 shaders compiles and the program links successfully. Enable all
460 GL_CLIP_DISTANCEs. Draw a single GL_POINT with screen centered position
461 attribute and with a configured 1 x 1 pixel size framebuffer. Using
462 glReadPixels function, check that point's fragments were not discarded. */
463
464 /* Creating red-color-only frambuffer. */
465 glcts::ClipDistance::Utility::Framebuffer framebuffer(gl, 1, 1);
466
467 if (!framebuffer.isValid())
468 {
469 m_testCtx.getLog() << tcu::TestLog::Message << "Unable to create framebuffer with size [1,1].\n"
470 << tcu::TestLog::EndMessage;
471 return false;
472 }
473
474 framebuffer.bind();
475 framebuffer.clear();
476
477 /* Building program. */
478 std::string vertex_shader = tcu::StringTemplate(m_vertex_shader_code_case_2).specialize(specializationMap);
479 std::string fragment_shader = tcu::StringTemplate(m_fragment_shader_code_case_2).specialize(specializationMap);
480
481 glcts::ClipDistance::Utility::Program program(gl, vertex_shader, fragment_shader);
482
483 if (program.ProgramStatus().program_id == 0)
484 {
485 m_testCtx.getLog() << tcu::TestLog::Message << "Program failed to build.\n Vertex Shader:\n"
486 << vertex_shader << "\nWith Vertex Shader compilation log:\n"
487 << program.VertexShaderStatus().shader_log << "\nWith Fragment Shader:\n"
488 << fragment_shader << "\nWith Fragment Shader compilation log:\n"
489 << program.FragmentShaderStatus().shader_log << "\nWith Program linkage log:\n"
490 << program.ProgramStatus().program_linkage_log << "\n"
491 << tcu::TestLog::EndMessage;
492 return false;
493 }
494
495 program.UseProgram();
496
497 /* Creating empty VAO. */
498 glcts::ClipDistance::Utility::VertexArrayObject vertex_array_object(gl, GL_POINTS);
499
500 /* Draw test. */
501 vertex_array_object.draw(0, 1);
502
503 /* Fetch results. */
504 std::vector<glw::GLfloat> pixels = framebuffer.readPixels();
505
506 if (pixels.size() < 1)
507 {
508 m_testCtx.getLog() << tcu::TestLog::Message << "ReadPixels error.\n" << tcu::TestLog::EndMessage;
509 return false;
510 }
511
512 /* Check results. */
513 glw::GLfloat results = pixels.front();
514
515 if (fabs(results - 1.f) > 0.0125)
516 {
517 m_testCtx.getLog() << tcu::TestLog::Message
518 << "Fragment shader values of gl_Clip_distance does not match vertex shader's output value."
519 << tcu::TestLog::EndMessage;
520 return false;
521 }
522
523 /* Test passed. */
524 return true;
525 }
526
527 /** @brief Vertex shader source code to test gl_MaxClipDistances limit value in vertex shader (API Coverage Test). */
528 const glw::GLchar *glcts::ClipDistance::CoverageTest::m_vertex_shader_code_case_0 =
529 "${VERSION}\n"
530 "${EXTENSION}\n"
531 "flat out int max_value;\n"
532 "\n"
533 "void main()\n"
534 "{\n"
535 " max_value = gl_MaxClipDistances;\n"
536 " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
537 "}\n";
538
539 /** @brief Fragment shader source code to test gl_MaxClipDistances limit value in vertex shader (API Coverage Test). */
540 const glw::GLchar *glcts::ClipDistance::CoverageTest::m_fragment_shader_code_case_0 =
541 "${VERSION}\n"
542 "${PRECISION}\n"
543 "out vec4 color;\n"
544 "\n"
545 "void main()\n"
546 "{\n"
547 " color = vec4(0.0, 0.0, 0.0, 1.0);\n"
548 "}\n";
549
550 /** @brief Vertex shader source code to test gl_MaxClipDistances limit value in fragment shader (API Coverage Test). */
551 const glw::GLchar *glcts::ClipDistance::CoverageTest::m_vertex_shader_code_case_1 =
552 "${VERSION}\n"
553 "\n"
554 "void main()\n"
555 "{\n"
556 " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
557 "}\n";
558
559 /** @brief Fragment shader source code to test gl_MaxClipDistances limit value in fragment shader (API Coverage Test). */
560 const glw::GLchar *glcts::ClipDistance::CoverageTest::m_fragment_shader_code_case_1 =
561 "${VERSION}\n"
562 "${EXTENSION}\n"
563 "${PRECISION}\n"
564 "\n"
565 "out highp vec4 color;\n"
566 "\n"
567 "void main()\n"
568 "{\n"
569 " color = vec4(float(gl_MaxClipDistances), 0.0, 0.0, 1.0);\n"
570 "}\n";
571
572 /** @brief Vertex shader source code to test if the gl_ClipDistance[] are passed properly to the fragment shader from vertex shader (API Coverage Test). */
573 const glw::GLchar *glcts::ClipDistance::CoverageTest::m_vertex_shader_code_case_2 =
574 "${VERSION}\n"
575 "${EXTENSION}\n"
576 "\n"
577 "out float gl_ClipDistance[gl_MaxClipDistances];\n"
578 "\n"
579 "void main()\n"
580 "{\n"
581 " for(int i = 0; i < gl_MaxClipDistances; i++)\n"
582 " {\n"
583 " gl_ClipDistance[i] = float(i + 1) / float(gl_MaxClipDistances);\n"
584 " }\n"
585 "\n"
586 " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
587 "}\n";
588
589 /** @brief Fragment shader source code to test if the gl_ClipDistance[] are passed properly to the fragment shader from vertex shader (API Coverage Test). */
590 const glw::GLchar *glcts::ClipDistance::CoverageTest::m_fragment_shader_code_case_2 =
591 "${VERSION}\n"
592 "${EXTENSION}\n"
593 "${PRECISION}\n"
594 "\n"
595 "in float gl_ClipDistance[gl_MaxClipDistances];\n"
596 "\n"
597 "out highp vec4 color;\n"
598 "\n"
599 "void main()\n"
600 "{\n"
601 " for(int i = 0; i < gl_MaxClipDistances; i++)\n"
602 " {\n"
603 " if(abs(gl_ClipDistance[i] - float(i + 1) / float(gl_MaxClipDistances)) > 0.0125)\n"
604 " {\n"
605 " discard;\n"
606 " }\n"
607 " }\n"
608 "\n"
609 " color = vec4(1.0, 0.0, 0.0, 1.0);\n"
610 "}\n";
611
612 /******************************** Functional Tests Implementation ********************************/
613
614 /** @brief Functional test constructor.
615 *
616 * @param [in] context OpenGL context.
617 */
FunctionalTest(deqp::Context & context)618 glcts::ClipDistance::FunctionalTest::FunctionalTest(deqp::Context &context)
619 : ClipDistanceTestBase(context, "functional", "Clip Distance Functional Test")
620 , m_gl_max_clip_distances_value(8) /* Specification minimum required */
621 {
622 /* Intentionally left blank */
623 }
624
625 /** @brief functional test cases.
626 */
test()627 void glcts::ClipDistance::FunctionalTest::test()
628 {
629 /* Shortcut for GL functionality */
630 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
631
632 gl.getIntegerv(GL_MAX_CLIP_DISTANCES, &m_gl_max_clip_distances_value);
633
634 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv call failed.");
635
636 /* Functional test */
637
638 /* For all primitive modes. */
639 for (glw::GLuint i_primitive_type = 0; i_primitive_type < m_primitive_types_count; ++i_primitive_type)
640 {
641 glw::GLenum primitive_type = m_primitive_types[i_primitive_type];
642 glw::GLenum primitive_indices_count = m_primitive_indices[i_primitive_type];
643
644 /* Framebuffer setup. */
645 glw::GLuint framebuffer_size = (primitive_type == GL_POINTS) ? 1 : 32;
646
647 glcts::ClipDistance::Utility::Framebuffer framebuffer(gl, framebuffer_size,
648 framebuffer_size); /* Framebuffer shall be square */
649
650 framebuffer.bind();
651
652 /* For all clip combinations. */
653 for (glw::GLuint i_clip_function = 0;
654 i_clip_function <
655 m_clip_function_count -
656 int(i_primitive_type == GL_POINTS); /* Do not use last clip function with GL_POINTS. */
657 ++i_clip_function)
658 {
659 /* For both redeclaration types (implicit/explicit). */
660 for (glw::GLuint i_redeclaration = 0; i_redeclaration < 2; ++i_redeclaration)
661 {
662 bool redeclaration = (i_redeclaration == 1);
663
664 /* For different clip array sizes. */
665 for (glw::GLuint i_clip_count = 1; i_clip_count <= glw::GLuint(m_gl_max_clip_distances_value);
666 ++i_clip_count)
667 {
668 /* Create and build program. */
669 std::string vs_code, fs_code;
670 glcts::ClipDistance::FunctionalTest::prepareShaderCode(
671 redeclaration, redeclaration, i_clip_count, i_clip_function, primitive_type, vs_code, fs_code);
672
673 glcts::ClipDistance::Utility::Program program(gl, vs_code, fs_code);
674
675 if (program.ProgramStatus().program_id == GL_NONE)
676 {
677 /* Result's setup. */
678 m_testCtx.getLog()
679 << tcu::TestLog::Message
680 << "Functional test have failed when building program.\nVertex shader code:\n"
681 << vs_code << "\nWith Vertex Shader compilation log:\n"
682 << program.VertexShaderStatus().shader_log << "\nWith Fragment Shader:\n"
683 << fs_code << "\nWith Fragment Shader compilation log:\n"
684 << program.FragmentShaderStatus().shader_log << "\nWith Program linkage log:\n"
685 << program.FragmentShaderStatus().shader_log << "\n"
686 << tcu::TestLog::EndMessage;
687
688 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
689
690 return;
691 }
692
693 program.UseProgram();
694
695 /* Framebuffer clear */
696 framebuffer.clear();
697
698 /* Clip setup */
699 gl.enable(GL_CLIP_DISTANCE0 + i_clip_count - 1);
700
701 /* Geometry Setup */
702 glcts::ClipDistance::Utility::VertexArrayObject vertex_array_object(gl, primitive_type);
703
704 glcts::ClipDistance::Utility::VertexBufferObject<glw::GLfloat> *vertex_buffer_object =
705 prepareGeometry(gl, primitive_type);
706
707 if (!vertex_buffer_object->useAsShaderInput(program, "position", 4))
708 {
709 /* Result's setup. */
710 m_testCtx.getLog() << tcu::TestLog::Message
711 << "Functional test have failed when enabling vertex attribute array.\n"
712 << tcu::TestLog::EndMessage;
713
714 m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error");
715
716 delete vertex_buffer_object;
717 return;
718 }
719
720 /* Draw geometry to the framebuffer */
721 vertex_array_object.draw(0, primitive_indices_count);
722
723 /* Check results */
724 std::vector<glw::GLfloat> results = framebuffer.readPixels();
725
726 if (!checkResults(primitive_type, i_clip_function, results))
727 {
728 /* Result's setup. */
729 m_testCtx.getLog()
730 << tcu::TestLog::Message << "Functional test have failed when drawing "
731 << glu::getPrimitiveTypeStr(primitive_type) << ((redeclaration) ? " with " : " without ")
732 << "dynamic redeclaration, when " << i_clip_count
733 << " GL_CLIP_DISTANCES where enabled and set up using function:\n"
734 << m_clip_function[i_clip_function] << tcu::TestLog::EndMessage;
735
736 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
737
738 delete vertex_buffer_object;
739 return;
740 }
741
742 delete vertex_buffer_object;
743 }
744
745 /* Clip clean */
746 for (glw::GLuint i_clip_count = 0; i_clip_count < glw::GLuint(m_gl_max_clip_distances_value);
747 ++i_clip_count)
748 {
749 gl.disable(GL_CLIP_DISTANCE0 + i_clip_count);
750 }
751 }
752 }
753 }
754
755 /* Result's setup. */
756
757 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
758 }
759
760 /** @brief Prepare vertex shader code for functional test.
761 *
762 * @param [in] explicit_redeclaration Use explicit redeclaration with size.
763 * @param [in] dynamic_setter Use dynamic array setter.
764 * @param [in] clip_count Set all first # of gl_ClipDistance-s.
765 * @param [in] clip_function Use #th clip function for gl_ClipDistance[] setup (see m_clip_function[]).
766 * @param [in] primitive_type Primitive mode.
767 * @param [out] vertex_shader Compilation ready vertex shader source code.
768 * @param [out] fragment_shader Compilation ready fragment shader source code.
769 */
prepareShaderCode(bool explicit_redeclaration,bool dynamic_setter,glw::GLuint clip_count,glw::GLuint clip_function,glw::GLenum primitive_type,std::string & vertex_shader,std::string & fragment_shader)770 void glcts::ClipDistance::FunctionalTest::prepareShaderCode(bool explicit_redeclaration, bool dynamic_setter,
771 glw::GLuint clip_count, glw::GLuint clip_function,
772 glw::GLenum primitive_type, std::string &vertex_shader,
773 std::string &fragment_shader)
774 {
775 vertex_shader = m_vertex_shader_code;
776 fragment_shader = m_fragment_shader_code;
777
778 if (explicit_redeclaration)
779 {
780 vertex_shader = glcts::ClipDistance::Utility::preprocessCode(vertex_shader, "CLIP_DISTANCE_REDECLARATION",
781 m_explicit_redeclaration);
782 }
783 else
784 {
785 vertex_shader = glcts::ClipDistance::Utility::preprocessCode(vertex_shader, "CLIP_DISTANCE_REDECLARATION", "");
786 }
787
788 if (dynamic_setter)
789 {
790 vertex_shader =
791 glcts::ClipDistance::Utility::preprocessCode(vertex_shader, "CLIP_DISTANCE_SETUP", m_dynamic_array_setter);
792 }
793 else
794 {
795 std::string static_setters = "";
796
797 for (glw::GLuint i = 0; i < clip_count; ++i)
798 {
799 std::string i_setter = m_static_array_setter;
800 i_setter = glcts::ClipDistance::Utility::preprocessCode(i_setter, "CLIP_INDEX",
801 glcts::ClipDistance::Utility::itoa(i));
802 static_setters.append(i_setter);
803 }
804
805 vertex_shader =
806 glcts::ClipDistance::Utility::preprocessCode(vertex_shader, "CLIP_DISTANCE_SETUP", static_setters);
807 }
808
809 vertex_shader =
810 glcts::ClipDistance::Utility::preprocessCode(vertex_shader, "CLIP_FUNCTION", m_clip_function[clip_function]);
811 vertex_shader = glcts::ClipDistance::Utility::preprocessCode(vertex_shader, "CLIP_COUNT",
812 glcts::ClipDistance::Utility::itoa(clip_count));
813 switch (primitive_type)
814 {
815 case GL_POINTS:
816 vertex_shader = glcts::ClipDistance::Utility::preprocessCode(vertex_shader, "VERTEX_COUNT", "1");
817 break;
818 case GL_LINES:
819 vertex_shader = glcts::ClipDistance::Utility::preprocessCode(vertex_shader, "VERTEX_COUNT", "2");
820 break;
821 case GL_TRIANGLES:
822 vertex_shader = glcts::ClipDistance::Utility::preprocessCode(vertex_shader, "VERTEX_COUNT", "3");
823 break;
824 }
825
826 vertex_shader = tcu::StringTemplate(vertex_shader).specialize(specializationMap);
827 fragment_shader = tcu::StringTemplate(fragment_shader).specialize(specializationMap);
828 }
829
830 /** @brief Prepare geometry for functional test.
831 *
832 * @param [in] gl OpenGL functions' access.
833 * @param [in] primitive_type Primitive mode.
834 *
835 * @return Vertex Buffer Object pointer.
836 */
prepareGeometry(const glw::Functions & gl,const glw::GLenum primitive_type)837 glcts::ClipDistance::Utility::VertexBufferObject<glw::GLfloat> *glcts::ClipDistance::FunctionalTest::prepareGeometry(
838 const glw::Functions &gl, const glw::GLenum primitive_type)
839 {
840 std::vector<glw::GLfloat> data;
841
842 switch (primitive_type)
843 {
844 case GL_POINTS:
845 data.push_back(0.0);
846 data.push_back(0.0);
847 data.push_back(0.0);
848 data.push_back(1.0);
849 break;
850 case GL_LINES:
851 data.push_back(1.0);
852 data.push_back(1.0);
853 data.push_back(0.0);
854 data.push_back(1.0);
855 data.push_back(-1.0);
856 data.push_back(-1.0);
857 data.push_back(0.0);
858 data.push_back(1.0);
859 break;
860 case GL_TRIANGLES:
861 data.push_back(-1.0);
862 data.push_back(-1.0);
863 data.push_back(0.0);
864 data.push_back(1.0);
865 data.push_back(0.0);
866 data.push_back(1.0);
867 data.push_back(0.0);
868 data.push_back(1.0);
869 data.push_back(1.0);
870 data.push_back(-1.0);
871 data.push_back(0.0);
872 data.push_back(1.0);
873 break;
874 default:
875 return NULL;
876 }
877
878 return new glcts::ClipDistance::Utility::VertexBufferObject<glw::GLfloat>(gl, GL_ARRAY_BUFFER, data);
879 }
880
881 /** @brief Check results fetched from framebuffer of functional test.
882 *
883 * @param [in] primitive_type Primitive mode.
884 * @param [in] clip_function Use #th clip function for gl_ClipDistance[] setup (see m_clip_function[]).
885 * @param [in] results Array with framebuffer content.
886 *
887 * @return True if proper result, false otherwise.
888 */
checkResults(glw::GLenum primitive_type,glw::GLuint clip_function,std::vector<glw::GLfloat> & results)889 bool glcts::ClipDistance::FunctionalTest::checkResults(glw::GLenum primitive_type, glw::GLuint clip_function,
890 std::vector<glw::GLfloat> &results)
891 {
892 /* Check for errors */
893 if (results.size() == 0)
894 {
895 return false;
896 }
897
898 /* Calculate surface/line integral */
899 glw::GLfloat integral = 0.f;
900
901 glw::GLuint increment = (glw::GLuint)((primitive_type == GL_LINES) ?
902 glw::GLuint(sqrt(glw::GLfloat(results.size()))) + 1 /* line integral */ :
903 1 /* surface integral */);
904 glw::GLuint base = (glw::GLuint)((primitive_type == GL_LINES) ?
905 glw::GLuint(sqrt(glw::GLfloat(results.size()))) /* line integral */ :
906 results.size() /* surface integral */);
907
908 for (glw::GLuint i_pixels = 0; i_pixels < results.size(); i_pixels += increment)
909 {
910 integral += results[i_pixels];
911 }
912
913 integral /= static_cast<glw::GLfloat>(base);
914
915 /* Check with results' lookup table */
916 glw::GLuint i_primitive_type = (primitive_type == GL_POINTS) ? 0 : ((primitive_type == GL_LINES) ? 1 : 2);
917
918 if (fabs(m_expected_integral[i_primitive_type * m_clip_function_count + clip_function] - integral) >
919 0.01 /* Precision */)
920 {
921 return false;
922 }
923
924 return true;
925 }
926
927 /* @brief Vertex Shader template for functional tests. */
928 const glw::GLchar *glcts::ClipDistance::FunctionalTest::m_vertex_shader_code = "${VERSION}\n"
929 "${EXTENSION}\n"
930 "\n"
931 "CLIP_DISTANCE_REDECLARATION"
932 "\n"
933 "CLIP_FUNCTION"
934 "\n"
935 "in vec4 position;\n"
936 "\n"
937 "void main()\n"
938 "{\n"
939 "CLIP_DISTANCE_SETUP"
940 "\n"
941 " gl_Position = position;\n"
942 "}\n";
943
944 /* @brief Explicit redeclaration key value to preprocess the Vertex Shader template for functional tests. */
945 const glw::GLchar *glcts::ClipDistance::FunctionalTest::m_explicit_redeclaration =
946 "out float gl_ClipDistance[CLIP_COUNT];\n";
947
948 /* @brief Dynamic array setter key value to preprocess the Vertex Shader template for functional tests. */
949 const glw::GLchar *glcts::ClipDistance::FunctionalTest::m_dynamic_array_setter =
950 " for(int i = 0; i < CLIP_COUNT; i++)\n"
951 " {\n"
952 " gl_ClipDistance[i] = f(i);\n"
953 " }\n";
954
955 /* @brief Static array setter key value to preprocess the Vertex Shader template for functional tests. */
956 const glw::GLchar *glcts::ClipDistance::FunctionalTest::m_static_array_setter =
957 " gl_ClipDistance[CLIP_INDEX] = f(CLIP_INDEX);\n";
958
959 /* @brief Clip Distance functions to preprocess the Vertex Shader template for functional tests. */
960 const glw::GLchar *glcts::ClipDistance::FunctionalTest::m_clip_function[] = {
961 "float f(int i)\n"
962 "{\n"
963 " return 0.0;\n"
964 "}\n",
965
966 "float f(int i)\n"
967 "{\n"
968 " return 0.25 + 0.75 * (float(i) + 1.0) * (float(gl_VertexID) + 1.0) / (float(CLIP_COUNT) * "
969 "float(VERTEX_COUNT));\n"
970 "}\n",
971
972 "float f(int i)\n"
973 "{\n"
974 " return - 0.25 - 0.75 * (float(i) + 1.0) * (float(gl_VertexID) + 1.0) / (float(CLIP_COUNT) * "
975 "float(VERTEX_COUNT));\n"
976 "}\n",
977
978 /* This case must be last (it is not rendered for GL_POINTS). */
979 "#define PI 3.1415926535897932384626433832795\n"
980 "\n"
981 "float f(int i)\n"
982 "{\n"
983 " if(i == 0)\n"
984 " {\n"
985 /* This function case generates such series of gl_VertexID:
986 1.0, -1.0 - for VERTEX_COUNT == 2 aka GL_LINES
987 1.0, 0.0, -1.0 - for VERTEX_COUNT == 3 aka GL_TRIANGLES
988 and if needed in future:
989 1.0, 0.0, -1.0, 0.0 - for VERTEX_COUNT == 4 aka GL_QUADS */
990 " return cos( float(gl_VertexID) * PI / ceil( float(VERTEX_COUNT)/2.0 ) );\n"
991 " }\n"
992 "\n"
993 " return 0.25 + 0.75 * (float(i) + 1.0) * (float(gl_VertexID) + 1.0) / (float(CLIP_COUNT) * "
994 "float(VERTEX_COUNT));\n"
995 "}\n"};
996
997 /* @brief Count of Clip Distance functions. */
998 const glw::GLuint glcts::ClipDistance::FunctionalTest::m_clip_function_count =
999 static_cast<glw::GLuint>(sizeof(m_clip_function) / sizeof(m_clip_function[0]));
1000
1001 /* @brief Fragment shader source code for functional tests. */
1002 const glw::GLchar *glcts::ClipDistance::FunctionalTest::m_fragment_shader_code =
1003 "${VERSION}\n"
1004 "${PRECISION}\n"
1005 "\n"
1006 "out highp vec4 color;\n"
1007 "\n"
1008 "void main()\n"
1009 "{\n"
1010 " color = vec4(1.0, 0.0, 0.0, 1.0);\n"
1011 "}\n";
1012
1013 /* @brief Primitive modes to be tested in functional test. */
1014 const glw::GLenum glcts::ClipDistance::FunctionalTest::m_primitive_types[] = {GL_POINTS, GL_LINES, GL_TRIANGLES};
1015
1016 /* @brief Number of primitive indices for each primitive mode. */
1017 const glw::GLenum glcts::ClipDistance::FunctionalTest::m_primitive_indices[] = {1, 2, 3};
1018
1019 /* @brief Primitive modes count. */
1020 const glw::GLuint glcts::ClipDistance::FunctionalTest::m_primitive_types_count =
1021 static_cast<glw::GLuint>(sizeof(m_primitive_types) / sizeof(m_primitive_types[0]));
1022
1023 /* @brief Expected results of testing integral for functional test. */
1024 const glw::GLfloat
1025 glcts::ClipDistance::FunctionalTest::m_expected_integral[m_primitive_types_count * m_clip_function_count] = {
1026 1.0, 1.0, 0.0, 0.0, /* for GL_POINTS */
1027 1.0, 1.0, 0.0, 0.5, /* for GL_LINES */
1028 0.5, 0.5, 0.0, 0.25 /* for GL_TRIANGLES */
1029 };
1030
1031 /******************************** Negative Tests Implementation ********************************/
1032
1033 /** @brief Negative tests constructor.
1034 *
1035 * @param [in] context OpenGL context.
1036 */
NegativeTest(deqp::Context & context)1037 glcts::ClipDistance::NegativeTest::NegativeTest(deqp::Context &context)
1038 : ClipDistanceTestBase(context, "negative", "Clip Distance Negative Tests")
1039 {
1040 /* Intentionally left blank */
1041 }
1042
1043 /** @brief Iterate negative tests
1044 *
1045 * @return Iteration result.
1046 */
test()1047 void glcts::ClipDistance::NegativeTest::test()
1048 {
1049 /* Shortcut for GL functionality */
1050 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
1051
1052 /* Iterate tests */
1053 bool is_ok = true;
1054 bool may_be_ok = true;
1055
1056 is_ok = is_ok && testClipVertexBuildingErrors(gl);
1057 is_ok = is_ok && testMaxClipDistancesBuildingErrors(gl);
1058 may_be_ok = may_be_ok && testClipDistancesRedeclarationBuildingErrors(gl);
1059
1060 /* Result's setup. */
1061 if (is_ok)
1062 {
1063 if (may_be_ok)
1064 {
1065 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1066 }
1067 else
1068 {
1069 m_testCtx.setTestResult(QP_TEST_RESULT_QUALITY_WARNING, "Pass with warning");
1070 }
1071 }
1072 else
1073 {
1074 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
1075 }
1076 }
1077
1078 /** @brief Clip Distance / Clip Vertex negative test sub-case.
1079 *
1080 * @param [in] gl OpenGL functions' access.
1081 *
1082 * @return True if passed, false otherwise.
1083 */
testClipVertexBuildingErrors(const glw::Functions & gl)1084 bool glcts::ClipDistance::NegativeTest::testClipVertexBuildingErrors(const glw::Functions &gl)
1085 {
1086 /* If OpenGL version < 3.1 is available, check that building shader program
1087 fails when vertex shader statically writes to both gl_ClipVertex and
1088 gl_ClipDistance[0]. Validate that the vertex shader which statically
1089 writes to only the gl_ClipVertex or to the gl_ClipDistance[0] builds
1090 without fail. */
1091
1092 /* This test should only be executed if we're running a GL3.0 or less context,
1093 the output variable gl_ClipVertex was removed from OpenGL ES Shading Language. */
1094 if (m_isContextES ||
1095 !glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType(3, 1, glu::PROFILE_CORE)))
1096 {
1097 return true;
1098 }
1099
1100 std::string vertex_shader = tcu::StringTemplate(m_vertex_shader_code_case_0).specialize(specializationMap);
1101 std::string fragment_shader = tcu::StringTemplate(m_fragment_shader_code).specialize(specializationMap);
1102
1103 glcts::ClipDistance::Utility::Program program(gl, vertex_shader, fragment_shader);
1104
1105 if (program.ProgramStatus().program_id)
1106 {
1107 m_testCtx.getLog() << tcu::TestLog::Message
1108 << "Functional test have failed. "
1109 "Building shader which statically writes to both gl_ClipVertex "
1110 "and gl_ClipDistances[] has unexpectedly succeeded."
1111 << tcu::TestLog::EndMessage;
1112
1113 return false;
1114 }
1115
1116 return true;
1117 }
1118
1119 /** @brief Explicit redeclaration negative test sub-case.
1120 *
1121 * @param [in] gl OpenGL functions' access.
1122 *
1123 * @return True if passed, false otherwise.
1124 */
testMaxClipDistancesBuildingErrors(const glw::Functions & gl)1125 bool glcts::ClipDistance::NegativeTest::testMaxClipDistancesBuildingErrors(const glw::Functions &gl)
1126 {
1127 /* Check that building shader program fails when gl_ClipDistance is
1128 redeclared in the shader with size higher than GL_MAX_CLIP_DISTANCES. */
1129
1130 std::string vertex_shader = tcu::StringTemplate(m_vertex_shader_code_case_1).specialize(specializationMap);
1131 std::string fragment_shader = tcu::StringTemplate(m_fragment_shader_code).specialize(specializationMap);
1132
1133 glcts::ClipDistance::Utility::Program program(gl, vertex_shader, fragment_shader);
1134
1135 if (program.ProgramStatus().program_id)
1136 {
1137 m_testCtx.getLog() << tcu::TestLog::Message
1138 << "Functional test have failed. "
1139 "Building shader with explicit redeclaration of gl_ClipDistance[] array with size "
1140 "(gl_MaxClipDistances + 1) has unexpectedly succeeded."
1141 << tcu::TestLog::EndMessage;
1142
1143 return false;
1144 }
1145
1146 return true;
1147 }
1148
1149 /** @brief Implicit redeclaration negative test sub-case.
1150 *
1151 * @param [in] gl OpenGL functions' access.
1152 *
1153 * @return True if passed, false when quality warning occured.
1154 */
testClipDistancesRedeclarationBuildingErrors(const glw::Functions & gl)1155 bool glcts::ClipDistance::NegativeTest::testClipDistancesRedeclarationBuildingErrors(const glw::Functions &gl)
1156 {
1157 /* Check that building shader program fails when gl_ClipDistance is not
1158 redeclared with explicit size and dynamic indexing is used.*/
1159
1160 std::string vertex_shader = tcu::StringTemplate(m_vertex_shader_code_case_2).specialize(specializationMap);
1161 std::string fragment_shader = tcu::StringTemplate(m_fragment_shader_code).specialize(specializationMap);
1162
1163 glcts::ClipDistance::Utility::Program program(gl, vertex_shader, fragment_shader);
1164
1165 if (program.ProgramStatus().program_id)
1166 {
1167 m_testCtx.getLog()
1168 << tcu::TestLog::Message
1169 << "Functional test have passed but with warning. "
1170 "Building shader without explicit redeclaration and with variable indexing has unexpectedly succeeded. "
1171 "This is within the bound of the specification (no error is being), but it may lead to errors."
1172 << tcu::TestLog::EndMessage;
1173
1174 return false;
1175 }
1176
1177 return true;
1178 }
1179
1180 /** @brief Vertex shader source code for gl_ClipVertex negative test. */
1181 const glw::GLchar *glcts::ClipDistance::NegativeTest::m_vertex_shader_code_case_0 =
1182 "${VERSION}\n"
1183 "${EXTENSION}\n"
1184 "\n"
1185 "void main()\n"
1186 "{\n"
1187 " gl_ClipDistance[0] = 0.0;\n"
1188 " gl_ClipVertex = vec4(0.0);\n"
1189 " gl_Position = vec4(1.0);\n"
1190 "}\n";
1191
1192 /** @brief Vertex shader source code for explicit redeclaration negative test. */
1193 const glw::GLchar *glcts::ClipDistance::NegativeTest::m_vertex_shader_code_case_1 =
1194 "${VERSION}\n"
1195 "${EXTENSION}\n"
1196 "\n"
1197 "out float gl_ClipDistance[gl_MaxClipDistances + 1];\n"
1198 "\n"
1199 "void main()\n"
1200 "{\n"
1201 " gl_ClipDistance[0] = 0.0;\n"
1202 " gl_Position = vec4(1.0);\n"
1203 "}\n";
1204
1205 /** @brief Vertex shader source code for impilicit redeclaration negative test. */
1206 const glw::GLchar *glcts::ClipDistance::NegativeTest::m_vertex_shader_code_case_2 =
1207 "${VERSION}\n"
1208 "${EXTENSION}\n"
1209 "\n"
1210 "in int count;\n"
1211 "\n"
1212 "void main()\n"
1213 "{\n"
1214 " for(int i = 0; i < count; i++)\n"
1215 " {\n"
1216 " gl_ClipDistance[i] = 0.0;\n"
1217 " }\n"
1218 "\n"
1219 " gl_Position = vec4(1.0);\n"
1220 "}\n";
1221
1222 /** @brief Simple passthrough fragment shader source code for negative tests. */
1223 const glw::GLchar *glcts::ClipDistance::NegativeTest::m_fragment_shader_code = "${VERSION}\n"
1224 "${PRECISION}\n"
1225 "\n"
1226 "out vec4 color;\n"
1227 "\n"
1228 "void main()\n"
1229 "{\n"
1230 " color = vec4(0.0, 0.0, 0.0, 1.0);\n"
1231 "}\n";
1232
1233 /******************************** Utility Clases Implementations ********************************/
1234
1235 /** @brief Program constructor.
1236 *
1237 * @param [in] gl OpenGL functions' access.
1238 * @param [in] vertex_shader_code Vertex shader source code.
1239 * @param [in] fragment_shader_code Fragment shader source code.
1240 * @param [in] transform_feedback_varyings Transform feedback varying names.
1241 */
Program(const glw::Functions & gl,const std::string & vertex_shader_code,const std::string & fragment_shader_code,std::vector<std::string> transform_feedback_varyings)1242 glcts::ClipDistance::Utility::Program::Program(const glw::Functions &gl, const std::string &vertex_shader_code,
1243 const std::string &fragment_shader_code,
1244 std::vector<std::string> transform_feedback_varyings)
1245 : m_gl(gl)
1246 {
1247 /* Compilation */
1248 const glw::GLchar *vertex_shader_code_c = (const glw::GLchar *)vertex_shader_code.c_str();
1249 const glw::GLchar *fragment_shader_code_c = (const glw::GLchar *)fragment_shader_code.c_str();
1250
1251 m_vertex_shader_status = compileShader(GL_VERTEX_SHADER, &vertex_shader_code_c);
1252 m_fragment_shader_status = compileShader(GL_FRAGMENT_SHADER, &fragment_shader_code_c);
1253
1254 /* Linking */
1255 m_program_status.program_id = 0;
1256 if (m_vertex_shader_status.shader_compilation_status && m_fragment_shader_status.shader_compilation_status)
1257 {
1258 m_program_status = linkShaders(m_vertex_shader_status, m_fragment_shader_status, transform_feedback_varyings);
1259 }
1260
1261 /* Cleaning */
1262 if (m_vertex_shader_status.shader_id)
1263 {
1264 m_gl.deleteShader(m_vertex_shader_status.shader_id);
1265
1266 m_vertex_shader_status.shader_id = 0;
1267 }
1268
1269 if (m_fragment_shader_status.shader_id)
1270 {
1271 m_gl.deleteShader(m_fragment_shader_status.shader_id);
1272
1273 m_fragment_shader_status.shader_id = 0;
1274 }
1275 }
1276
1277 /** @brief Program destructor. */
~Program()1278 glcts::ClipDistance::Utility::Program::~Program()
1279 {
1280 if (m_vertex_shader_status.shader_id)
1281 {
1282 m_gl.deleteShader(m_vertex_shader_status.shader_id);
1283
1284 m_vertex_shader_status.shader_id = 0;
1285 }
1286
1287 if (m_fragment_shader_status.shader_id)
1288 {
1289 m_gl.deleteShader(m_fragment_shader_status.shader_id);
1290
1291 m_fragment_shader_status.shader_id = 0;
1292 }
1293
1294 if (m_program_status.program_id)
1295 {
1296 m_gl.deleteProgram(m_program_status.program_id);
1297
1298 m_program_status.program_id = 0;
1299 }
1300 }
1301
1302 /** @brief Vertex shader compilation status getter.
1303 *
1304 * @return Vertex shader compilation status.
1305 */
1306 const glcts::ClipDistance::Utility::Program::CompilationStatus &glcts::ClipDistance::Utility::Program::
VertexShaderStatus() const1307 VertexShaderStatus() const
1308 {
1309 return m_vertex_shader_status;
1310 }
1311
1312 /** @brief Fragment shader compilation status getter.
1313 *
1314 * @return Fragment shader compilation status.
1315 */
1316 const glcts::ClipDistance::Utility::Program::CompilationStatus &glcts::ClipDistance::Utility::Program::
FragmentShaderStatus() const1317 FragmentShaderStatus() const
1318 {
1319 return m_fragment_shader_status;
1320 }
1321
1322 /** @brief Program building status getter.
1323 *
1324 * @return Program linkage status.
1325 */
ProgramStatus() const1326 const glcts::ClipDistance::Utility::Program::LinkageStatus &glcts::ClipDistance::Utility::Program::ProgramStatus() const
1327 {
1328 return m_program_status;
1329 }
1330
1331 /** @brief Compile shader.
1332 *
1333 * @param [in] shader_type Shader type.
1334 * @param [in] shader_code Shader source code.
1335 *
1336 * @return Compilation status.
1337 */
compileShader(const glw::GLenum shader_type,const glw::GLchar * const * shader_code)1338 glcts::ClipDistance::Utility::Program::CompilationStatus glcts::ClipDistance::Utility::Program::compileShader(
1339 const glw::GLenum shader_type, const glw::GLchar *const *shader_code)
1340 {
1341 CompilationStatus shader = {0, GL_NONE, ""};
1342
1343 if (shader_code != nullptr)
1344 {
1345 try
1346 {
1347 /* Creation */
1348 shader.shader_id = m_gl.createShader(shader_type);
1349
1350 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateShader() call failed.");
1351
1352 /* Compilation */
1353 m_gl.shaderSource(shader.shader_id, 1, shader_code, NULL);
1354
1355 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glShaderSource() call failed.");
1356
1357 m_gl.compileShader(shader.shader_id);
1358
1359 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCompileShader() call failed.");
1360
1361 /* Status */
1362 m_gl.getShaderiv(shader.shader_id, GL_COMPILE_STATUS, &shader.shader_compilation_status);
1363
1364 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderiv() call failed.");
1365
1366 /* Logging */
1367 if (shader.shader_compilation_status == GL_FALSE)
1368 {
1369 glw::GLint log_size = 0;
1370
1371 m_gl.getShaderiv(shader.shader_id, GL_INFO_LOG_LENGTH, &log_size);
1372
1373 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderiv() call failed.");
1374
1375 if (log_size)
1376 {
1377 glw::GLchar *log = new glw::GLchar[log_size];
1378
1379 if (log)
1380 {
1381 memset(log, 0, log_size);
1382
1383 m_gl.getShaderInfoLog(shader.shader_id, log_size, nullptr, log);
1384
1385 shader.shader_log = log;
1386
1387 delete[] log;
1388
1389 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetShaderInfoLog() call failed.");
1390 }
1391 }
1392 }
1393 }
1394 catch (...)
1395 {
1396 if (shader.shader_id)
1397 {
1398 m_gl.deleteShader(shader.shader_id);
1399
1400 shader.shader_id = 0;
1401 }
1402 }
1403 }
1404
1405 return shader;
1406 }
1407
1408 /** @brief Link compiled shaders.
1409 *
1410 * @param [in] vertex_shader Vertex shader compilation status.
1411 * @param [in] fragment_shader Fragment shader compilation status.
1412 * @param [in] transform_feedback_varyings Transform feedback varying names array.
1413 *
1414 * @return Linkage status.
1415 */
linkShaders(const CompilationStatus & vertex_shader,const CompilationStatus & fragment_shader,std::vector<std::string> & transform_feedback_varyings)1416 glcts::ClipDistance::Utility::Program::LinkageStatus glcts::ClipDistance::Utility::Program::linkShaders(
1417 const CompilationStatus &vertex_shader, const CompilationStatus &fragment_shader,
1418 std::vector<std::string> &transform_feedback_varyings)
1419 {
1420 LinkageStatus program = {0, GL_NONE, ""};
1421
1422 if (vertex_shader.shader_id && fragment_shader.shader_id)
1423 {
1424 try
1425 {
1426 /* Creation */
1427 program.program_id = m_gl.createProgram();
1428
1429 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glCreateShader() call failed.");
1430
1431 if (program.program_id)
1432 {
1433 /* Transform Feedback setup */
1434 for (std::vector<std::string>::iterator i = transform_feedback_varyings.begin();
1435 i != transform_feedback_varyings.end(); ++i)
1436 {
1437 const glw::GLchar *varying = i->c_str();
1438
1439 m_gl.transformFeedbackVaryings(program.program_id, 1, &varying, GL_INTERLEAVED_ATTRIBS);
1440
1441 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glTransformFeedbackVaryings() call failed.");
1442 }
1443
1444 /* Linking */
1445 m_gl.attachShader(program.program_id, vertex_shader.shader_id);
1446
1447 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glAttachShader() call failed.");
1448
1449 m_gl.attachShader(program.program_id, fragment_shader.shader_id);
1450
1451 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glAttachShader() call failed.");
1452
1453 m_gl.linkProgram(program.program_id);
1454
1455 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glLinkProgram() call failed.");
1456
1457 /* Status query */
1458 m_gl.getProgramiv(program.program_id, GL_LINK_STATUS, &program.program_linkage_status);
1459
1460 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetProgramiv() call failed.");
1461
1462 /* Logging */
1463 if (program.program_linkage_status == GL_FALSE)
1464 {
1465 glw::GLint log_size = 0;
1466
1467 m_gl.getProgramiv(program.program_id, GL_INFO_LOG_LENGTH, &log_size);
1468
1469 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetProgramiv() call failed.");
1470
1471 if (log_size)
1472 {
1473 glw::GLchar *log = new glw::GLchar[log_size];
1474
1475 if (log)
1476 {
1477 memset(log, 0, log_size);
1478
1479 m_gl.getProgramInfoLog(program.program_id, log_size, nullptr, log);
1480
1481 program.program_linkage_log = log;
1482
1483 delete[] log;
1484
1485 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGetProgramInfoLog() call failed.");
1486 }
1487 }
1488 }
1489
1490 /* Cleanup */
1491 m_gl.detachShader(program.program_id, vertex_shader.shader_id);
1492
1493 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDetachShader() call failed.");
1494
1495 m_gl.detachShader(program.program_id, fragment_shader.shader_id);
1496
1497 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDetachShader() call failed.");
1498
1499 if (program.program_linkage_status == GL_FALSE)
1500 {
1501 m_gl.deleteProgram(program.program_id);
1502
1503 program.program_id = 0;
1504 }
1505 }
1506 }
1507 catch (...)
1508 {
1509 if (program.program_id)
1510 {
1511 m_gl.deleteProgram(program.program_id);
1512
1513 program.program_id = 0;
1514 }
1515 }
1516 }
1517
1518 return program;
1519 }
1520
1521 /** @brief Use program for drawing. */
UseProgram() const1522 void glcts::ClipDistance::Utility::Program::UseProgram() const
1523 {
1524 m_gl.useProgram(ProgramStatus().program_id);
1525 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram call failed.");
1526 }
1527
1528 /** @brief Framebuffer (GL_32R only) constructor.
1529 *
1530 * @param [in] gl OpenGL functions access.
1531 * @param [in] size_x X size of framebuffer.
1532 * @param [in] size_y Y size of framebuffer.
1533 */
Framebuffer(const glw::Functions & gl,const glw::GLsizei size_x,const glw::GLsizei size_y)1534 glcts::ClipDistance::Utility::Framebuffer::Framebuffer(const glw::Functions &gl, const glw::GLsizei size_x,
1535 const glw::GLsizei size_y)
1536 : m_gl(gl)
1537 , m_size_x(size_x)
1538 , m_size_y(size_y)
1539 , m_framebuffer_id(0)
1540 , m_renderbuffer_id(0)
1541 {
1542 m_gl.genFramebuffers(1, &m_framebuffer_id);
1543 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenFramebuffers call failed.");
1544
1545 m_gl.genRenderbuffers(1, &m_renderbuffer_id);
1546 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenRenderbuffers call failed.");
1547
1548 m_gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer_id);
1549 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindFramebuffer call failed.");
1550
1551 m_gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer_id);
1552 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindRenderbuffer call failed.");
1553
1554 m_gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32F, m_size_x, m_size_y);
1555 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glRenderbufferStorage call failed.");
1556
1557 m_gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer_id);
1558 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glFramebufferRenderbuffer call failed.");
1559
1560 if (gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
1561 {
1562 m_gl.deleteFramebuffers(1, &m_framebuffer_id);
1563 m_framebuffer_id = 0;
1564
1565 m_gl.deleteRenderbuffers(1, &m_renderbuffer_id);
1566 m_renderbuffer_id = 0;
1567 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDeleteRenderbuffers call failed.");
1568 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDeleteFramebuffers call failed.");
1569 }
1570 }
1571
1572 /** @brief Framebuffer destructor */
~Framebuffer()1573 glcts::ClipDistance::Utility::Framebuffer::~Framebuffer()
1574 {
1575 if (m_framebuffer_id)
1576 {
1577 m_gl.deleteFramebuffers(1, &m_framebuffer_id);
1578 m_framebuffer_id = 0;
1579 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDeleteFramebuffers call failed.");
1580 }
1581
1582 if (m_renderbuffer_id)
1583 {
1584 m_gl.deleteRenderbuffers(1, &m_renderbuffer_id);
1585 m_renderbuffer_id = 0;
1586 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDeleteRenderbuffers call failed.");
1587 }
1588 }
1589
1590 /** @brief Check frambuffer completness.
1591 *
1592 * @return True if valid, false otherwise.
1593 */
isValid()1594 bool glcts::ClipDistance::Utility::Framebuffer::isValid()
1595 {
1596 if (m_framebuffer_id)
1597 {
1598 return true;
1599 }
1600
1601 return false;
1602 }
1603
1604 /** @brief Bind framebuffer and setup viewport. */
bind()1605 void glcts::ClipDistance::Utility::Framebuffer::bind()
1606 {
1607 m_gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer_id);
1608 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindFramebuffer call failed.");
1609
1610 m_gl.viewport(0, 0, m_size_x, m_size_y);
1611 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glViewport call failed.");
1612 }
1613
1614 /** @brief Read pixels from framebuffer.
1615 *
1616 * @return Vector of read pixels.
1617 */
readPixels()1618 std::vector<glw::GLfloat> glcts::ClipDistance::Utility::Framebuffer::readPixels()
1619 {
1620 std::vector<glw::GLfloat> pixels(m_size_x * m_size_y);
1621
1622 if ((m_size_x > 0) && (m_size_y > 0))
1623 {
1624 m_gl.readPixels(0, 0, m_size_x, m_size_y, GL_RED, GL_FLOAT, pixels.data());
1625 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glReadPixels call failed.");
1626 }
1627
1628 return pixels;
1629 }
1630
1631 /** @brief Clear framebuffer. */
clear()1632 void glcts::ClipDistance::Utility::Framebuffer::clear()
1633 {
1634 if (isValid())
1635 {
1636 m_gl.clearColor(0.f, 0.f, 0.f, 1.f);
1637 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glClearColor call failed.");
1638
1639 m_gl.clear(GL_COLOR_BUFFER_BIT);
1640 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glClear call failed.");
1641 }
1642 }
1643
1644 /** @brief Vertex array object constructor.
1645 *
1646 * @note It silently binds VAO to OpenGL.
1647 *
1648 * @param [in] gl OpenGL functions access.
1649 * @param [in] primitive_type Primitive mode.
1650 */
VertexArrayObject(const glw::Functions & gl,const glw::GLenum primitive_type)1651 glcts::ClipDistance::Utility::VertexArrayObject::VertexArrayObject(const glw::Functions &gl,
1652 const glw::GLenum primitive_type)
1653 : m_gl(gl)
1654 , m_vertex_array_object_id(0)
1655 , m_primitive_type(primitive_type)
1656 {
1657 m_gl.genVertexArrays(1, &m_vertex_array_object_id);
1658 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glGenVertexArrays call failed.");
1659
1660 m_gl.bindVertexArray(m_vertex_array_object_id);
1661 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindVertexArray call failed.");
1662 }
1663
1664 /** @brief Vertex array object destructor. */
~VertexArrayObject()1665 glcts::ClipDistance::Utility::VertexArrayObject::~VertexArrayObject()
1666 {
1667 if (m_vertex_array_object_id)
1668 {
1669 m_gl.deleteVertexArrays(1, &m_vertex_array_object_id);
1670 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDeleteVertexArrays call failed.");
1671 }
1672 }
1673
1674 /** @brief Bind vertex array object. */
bind()1675 void glcts::ClipDistance::Utility::VertexArrayObject::bind()
1676 {
1677 if (m_vertex_array_object_id)
1678 {
1679 m_gl.bindVertexArray(m_vertex_array_object_id);
1680 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBindVertexArray call failed.");
1681 }
1682 }
1683
1684 /** @brief Draw array.
1685 *
1686 * @param [in] first First index to be drawn.
1687 * @param [in] count Count of indices to be drawn.
1688 */
draw(glw::GLuint first,glw::GLuint count)1689 void glcts::ClipDistance::Utility::VertexArrayObject::draw(glw::GLuint first, glw::GLuint count)
1690 {
1691 m_gl.drawArrays(m_primitive_type, first, count);
1692 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays() call failed.");
1693 }
1694
1695 /** @brief Draw array and fetch transform feedback varyings.
1696 *
1697 * @param [in] first First index to be drawn.
1698 * @param [in] count Count of indices to be drawn.
1699 * @param [in] discard_rasterizer Shall we discard rasterizer?
1700 */
drawWithTransformFeedback(glw::GLuint first,glw::GLuint count,bool discard_rasterizer)1701 void glcts::ClipDistance::Utility::VertexArrayObject::drawWithTransformFeedback(glw::GLuint first, glw::GLuint count,
1702 bool discard_rasterizer)
1703 {
1704 if (discard_rasterizer)
1705 {
1706 m_gl.enable(GL_RASTERIZER_DISCARD);
1707 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glEnable call failed.");
1708 }
1709
1710 m_gl.beginTransformFeedback(GL_POINTS);
1711 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glBeginTransformFeedback call failed.");
1712
1713 draw(first, count);
1714
1715 m_gl.endTransformFeedback();
1716 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glEndTransformFeedback call failed.");
1717
1718 if (discard_rasterizer)
1719 {
1720 m_gl.disable(GL_RASTERIZER_DISCARD);
1721 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDisbale call failed.");
1722 }
1723 }
1724
1725 /** @brief Substitute key with value within source code.
1726 *
1727 * @param [in] source Source code to be prerocessed.
1728 * @param [in] key Key to be substituted.
1729 * @param [in] value Value to be inserted.
1730 *
1731 * @return Resulting string.
1732 */
preprocessCode(std::string source,std::string key,std::string value)1733 std::string glcts::ClipDistance::Utility::preprocessCode(std::string source, std::string key, std::string value)
1734 {
1735 std::string destination = source;
1736
1737 while (true)
1738 {
1739 /* Find token in source code. */
1740 size_t position = destination.find(key, 0);
1741
1742 /* No more occurences of this key. */
1743 if (position == std::string::npos)
1744 {
1745 break;
1746 }
1747
1748 /* Replace token with sub_code. */
1749 destination.replace(position, key.size(), value);
1750 }
1751
1752 return destination;
1753 }
1754
1755 /** @brief Convert an integer to a string.
1756 *
1757 * @param [in] i Integer to be converted.
1758 *
1759 * @return String representing integer.
1760 */
itoa(glw::GLint i)1761 std::string glcts::ClipDistance::Utility::itoa(glw::GLint i)
1762 {
1763 std::stringstream stream;
1764
1765 stream << i;
1766
1767 return stream.str();
1768 }
1769