• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /**
25  */ /*!
26  * \file  gl3cCullDistanceTests.cpp
27  * \brief Cull Distance Test Suite Implementation
28  */ /*-------------------------------------------------------------------*/
29 
30 #include "glcCullDistance.hpp"
31 #include "gluContextInfo.hpp"
32 #include "gluDefs.hpp"
33 #include "gluShaderUtil.hpp"
34 #include "gluStrUtil.hpp"
35 #include "glwEnums.hpp"
36 #include "glwFunctions.hpp"
37 #include "tcuStringTemplate.hpp"
38 #include "tcuTestLog.hpp"
39 
40 #include <cmath>
41 #include <sstream>
42 #include <string>
43 #include <vector>
44 
45 #ifndef GL_MAX_CULL_DISTANCES
46 #define GL_MAX_CULL_DISTANCES (0x82F9)
47 #endif
48 #ifndef GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES
49 #define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES (0x82FA)
50 #endif
51 
52 namespace glcts
53 {
54 /** @brief Build OpenGL program
55  *
56  *  @param [in]  gl             OpenGL function bindings
57  *  @param [in]  testCtx        Context
58  *  @param [in]  cs_body        Compute shader source code
59  *  @param [in]  fs_body        Fragment shader source code
60  *  @param [in]  gs_body        Geometric shader source code
61  *  @param [in]  tc_body        Tessellation control shader source code
62  *  @param [in]  te_body        Tessellation evaluation shader source code
63  *  @param [in]  vs_body        Vertex shader source code
64  *  @param [in]  n_tf_varyings  Number of transform feedback varyings
65  *  @param [in]  tf_varyings    Transform feedback varyings names
66  *
67  *  @param [out] out_program    If succeeded output program GL handle, 0 otherwise.
68  */
buildProgram(const glw::Functions & gl,tcu::TestContext & testCtx,const glw::GLchar * cs_body,const glw::GLchar * fs_body,const glw::GLchar * gs_body,const glw::GLchar * tc_body,const glw::GLchar * te_body,const glw::GLchar * vs_body,const glw::GLuint & n_tf_varyings,const glw::GLchar ** tf_varyings,glw::GLuint * out_program)69 void CullDistance::Utilities::buildProgram(const glw::Functions &gl, tcu::TestContext &testCtx,
70                                            const glw::GLchar *cs_body, const glw::GLchar *fs_body,
71                                            const glw::GLchar *gs_body, const glw::GLchar *tc_body,
72                                            const glw::GLchar *te_body, const glw::GLchar *vs_body,
73                                            const glw::GLuint &n_tf_varyings, const glw::GLchar **tf_varyings,
74                                            glw::GLuint *out_program)
75 {
76     glw::GLuint po_id = 0;
77 
78     struct _shaders_configuration
79     {
80         glw::GLenum type;
81         const glw::GLchar *body;
82         glw::GLuint id;
83     } shaders_configuration[] = {{GL_COMPUTE_SHADER, cs_body, 0},         {GL_FRAGMENT_SHADER, fs_body, 0},
84                                  {GL_GEOMETRY_SHADER, gs_body, 0},        {GL_TESS_CONTROL_SHADER, tc_body, 0},
85                                  {GL_TESS_EVALUATION_SHADER, te_body, 0}, {GL_VERTEX_SHADER, vs_body, 0}};
86 
87     const glw::GLuint n_shaders_configuration = sizeof(shaders_configuration) / sizeof(shaders_configuration[0]);
88 
89     /* Guard allocated OpenGL resources */
90     try
91     {
92         /* Create needed programs */
93         po_id = gl.createProgram();
94         GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() call failed.");
95 
96         for (glw::GLuint n_shader_index = 0; n_shader_index < n_shaders_configuration; n_shader_index++)
97         {
98             if (shaders_configuration[n_shader_index].body != nullptr)
99             {
100                 /* Generate shader object */
101                 shaders_configuration[n_shader_index].id = gl.createShader(shaders_configuration[n_shader_index].type);
102                 GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateShader() call failed");
103 
104                 glw::GLint compile_status = GL_FALSE;
105                 const glw::GLuint so_id   = shaders_configuration[n_shader_index].id;
106 
107                 /* Assign shader source code */
108                 gl.shaderSource(shaders_configuration[n_shader_index].id, 1,           /* count */
109                                 &shaders_configuration[n_shader_index].body, nullptr); /* length */
110                 GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed");
111 
112                 gl.compileShader(so_id);
113                 GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed.");
114 
115                 gl.getShaderiv(so_id, GL_COMPILE_STATUS, &compile_status);
116                 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed.");
117 
118                 if (compile_status == GL_FALSE)
119                 {
120                     std::vector<glw::GLchar> log_array(1);
121                     glw::GLint log_length = 0;
122                     std::string log_string("Failed to retrieve log");
123 
124                     /* Retrive compilation log length */
125                     gl.getShaderiv(so_id, GL_INFO_LOG_LENGTH, &log_length);
126                     GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed.");
127 
128                     log_array.resize(log_length + 1, 0);
129 
130                     gl.getShaderInfoLog(so_id, log_length, nullptr, &log_array[0]);
131                     GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderInfoLog() call failed.");
132 
133                     log_string = std::string(&log_array[0]);
134 
135                     testCtx.getLog() << tcu::TestLog::Message << "Shader compilation has failed.\n"
136                                      << "Shader type: " << shaders_configuration[n_shader_index].type << "\n"
137                                      << "Shader compilation error log:\n"
138                                      << log_string << "\n"
139                                      << "Shader source code:\n"
140                                      << shaders_configuration[n_shader_index].body << "\n"
141                                      << tcu::TestLog::EndMessage;
142 
143                     TCU_FAIL("Shader compilation has failed.");
144                 }
145 
146                 /* Also attach the shader to the corresponding program object */
147                 gl.attachShader(po_id, so_id);
148 
149                 GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call failed");
150             } /* if (shaders_configuration[n_shader_index].body != nullptr) */
151         }     /* for (all shader object IDs) */
152 
153         /* Set transform feedback if requested */
154         if (n_tf_varyings > 0)
155         {
156             gl.transformFeedbackVaryings(po_id, n_tf_varyings, tf_varyings, GL_INTERLEAVED_ATTRIBS);
157             GLU_EXPECT_NO_ERROR(gl.getError(), "glTransformFeedbackVaryings() call failed");
158         }
159 
160         /* Try to link the program objects */
161         if (po_id != 0)
162         {
163             glw::GLint link_status = GL_FALSE;
164 
165             gl.linkProgram(po_id);
166             GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed");
167 
168             gl.getProgramiv(po_id, GL_LINK_STATUS, &link_status);
169             GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed");
170 
171             if (link_status == GL_FALSE)
172             {
173                 std::vector<glw::GLchar> log_array(1);
174                 glw::GLsizei log_length = 0;
175                 std::string log_string;
176 
177                 /* Retreive compilation log length */
178                 gl.getProgramiv(po_id, GL_INFO_LOG_LENGTH, &log_length);
179                 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed.");
180 
181                 log_array.resize(log_length + 1, 0);
182 
183                 /* Retreive compilation log */
184                 gl.getProgramInfoLog(po_id, log_length, nullptr, &log_array[0]);
185                 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog() call failed.");
186 
187                 log_string = std::string(&log_array[0]);
188 
189                 /* Log linking error message */
190                 testCtx.getLog() << tcu::TestLog::Message << "Program linking has failed.\n"
191                                  << "Linking error log:\n"
192                                  << log_string << "\n"
193                                  << tcu::TestLog::EndMessage;
194 
195                 /* Log shader source code of shaders involved */
196                 for (glw::GLuint n_shader_index = 0; n_shader_index < n_shaders_configuration; n_shader_index++)
197                 {
198                     if (shaders_configuration[n_shader_index].body != nullptr)
199                     {
200                         testCtx.getLog() << tcu::TestLog::Message << "Shader source code of type "
201                                          << shaders_configuration[n_shader_index].type << " follows:\n"
202                                          << shaders_configuration[n_shader_index].body << "\n"
203                                          << tcu::TestLog::EndMessage;
204                     }
205                 }
206 
207                 TCU_FAIL("Program linking failed");
208             }
209         } /* if (po_id != 0) */
210 
211         /* Delete all shaders we've created */
212         for (glw::GLuint n_shader_index = 0; n_shader_index < n_shaders_configuration; n_shader_index++)
213         {
214             const glw::GLuint so_id = shaders_configuration[n_shader_index].id;
215 
216             if (so_id != 0)
217             {
218                 gl.deleteShader(so_id);
219 
220                 shaders_configuration[n_shader_index].id = 0;
221 
222                 GLU_EXPECT_NO_ERROR(gl.getError(), "glDeleteShader() call failed.");
223             }
224         }
225 
226         /* Store the result progrtam IDs */
227         *out_program = po_id;
228     }
229     catch (...)
230     {
231         /* Delete all shaders we've created */
232         for (glw::GLuint n_shader_index = 0; n_shader_index < n_shaders_configuration; n_shader_index++)
233         {
234             const glw::GLuint so_id = shaders_configuration[n_shader_index].id;
235 
236             if (so_id != 0)
237             {
238                 gl.deleteShader(so_id);
239 
240                 shaders_configuration[n_shader_index].id = 0;
241             }
242         }
243 
244         /* Delete the program object */
245         if (po_id != 0)
246         {
247             gl.deleteProgram(po_id);
248 
249             po_id = 0;
250         }
251 
252         /* Rethrow */
253         throw;
254     }
255 }
256 
257 /** @brief Replace all occurences of a substring in a string by a substring
258  *
259  *  @param [in,out] str    string to be edited
260  *  @param [in]     from   substring to be replaced
261  *  @param [out]    to     new substring
262  */
replaceAll(std::string & str,const std::string & from,const std::string & to)263 void CullDistance::Utilities::replaceAll(std::string &str, const std::string &from, const std::string &to)
264 {
265     for (size_t start_pos = str.find(from, 0); start_pos != std::string::npos; start_pos = str.find(from, start_pos))
266     {
267         str.replace(start_pos, from.length(), to);
268 
269         start_pos += to.length();
270     }
271 
272     return;
273 }
274 
275 /** @brief Convert integer to string representation
276  *
277  *  @param [in] integer     input integer to be converted
278  *
279  *  @return String representation of integer
280  */
intToString(glw::GLint integer)281 std::string CullDistance::Utilities::intToString(glw::GLint integer)
282 {
283     std::stringstream temp_sstream;
284 
285     temp_sstream << integer;
286 
287     return temp_sstream.str();
288 }
289 
290 /****************************** Coverage Tests Base Implementation   *****************************/
291 
292 /** @brief API base tests constructor.
293  *
294  *  @param context Rendering context
295  *  @param name Test name
296  *  @param description Test description
297  */
CullDistanceTestBase(deqp::Context & context,const char * name,const char * description)298 glcts::CullDistance::CullDistanceTestBase::CullDistanceTestBase(deqp::Context &context, const char *name,
299                                                                 const char *description)
300     : TestCase(context, name, description)
301     , m_extensionSupported(false)
302     , m_isContextES(false)
303 {
304     const glu::RenderContext &renderContext = m_context.getRenderContext();
305     m_isContextES                           = glu::isContextTypeES(renderContext.getType());
306     glu::GLSLVersion glslVersion            = glu::getContextTypeGLSLVersion(renderContext.getType());
307 
308     auto contextType = m_context.getRenderContext().getType();
309     if (m_isContextES)
310     {
311         if (glu::contextSupports(contextType, glu::ApiType::es(3, 0)))
312         {
313             m_extensionSupported = context.getContextInfo().isExtensionSupported("GL_EXT_clip_cull_distance");
314         }
315 
316         specializationMap["VERSION"]   = glu::getGLSLVersionDeclaration(glslVersion);
317         specializationMap["EXTENSION"] = "#extension GL_EXT_clip_cull_distance : enable";
318         specializationMap["PRECISION"] = "precision highp float;";
319 
320         specializationMap["CS_VERSION"]   = "#version 310 es";
321         specializationMap["CS_EXTENSION"] = "";
322 
323         specializationMap["TS_EXTENSION"] = "";
324         specializationMap["GS_EXTENSION"] = "";
325         specializationMap["TS_VERSION"]   = specializationMap["VERSION"];
326         specializationMap["GS_VERSION"]   = specializationMap["VERSION"];
327 
328         auto set_version_str = [&](const char *str)
329         {
330             specializationMap["TS_VERSION"] = str;
331             specializationMap["GS_VERSION"] = str;
332             specializationMap["CS_VERSION"] = str;
333             specializationMap["VERSION"]    = str;
334         };
335 
336         if (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)))
337         {
338             set_version_str("#version 320 es");
339         }
340         else
341         {
342             if (m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
343             {
344                 set_version_str("#version 310 es"); // required for GL_EXT_tessellation_shader
345                 specializationMap["TS_EXTENSION"] = "#extension GL_EXT_tessellation_shader : enable\n"
346                                                     "#extension GL_EXT_shader_io_blocks : enable";
347             }
348             if (m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
349             {
350                 set_version_str("#version 310 es"); // required for GL_EXT_geometry_shader
351                 specializationMap["GS_EXTENSION"] = "#extension GL_EXT_geometry_shader : enable";
352             }
353         }
354     }
355     else
356     {
357         m_extensionSupported |= glu::contextSupports(contextType, glu::ApiType::core(4, 5));
358         m_extensionSupported |= m_context.getContextInfo().isExtensionSupported("GL_ARB_cull_distance");
359 
360         specializationMap["VERSION"]   = "#version 150";
361         specializationMap["EXTENSION"] = "#extension GL_ARB_cull_distance : require";
362         specializationMap["PRECISION"] = "";
363 
364         specializationMap["CS_VERSION"]   = "#version 420 core";
365         specializationMap["CS_EXTENSION"] = "#extension GL_ARB_compute_shader : require\n"
366                                             "#extension GL_ARB_shader_image_load_store : require";
367         specializationMap["TS_VERSION"]   = "#version 150";
368         specializationMap["GS_VERSION"]   = "#version 150";
369         specializationMap["TS_EXTENSION"] = "#extension GL_ARB_tessellation_shader : require";
370         specializationMap["GS_EXTENSION"] = "";
371     }
372 }
373 
iterate()374 tcu::TestNode::IterateResult glcts::CullDistance::CullDistanceTestBase::iterate()
375 {
376     if (!m_extensionSupported)
377     {
378         m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
379         if (!m_isContextES)
380         {
381             throw tcu::NotSupportedError("GL_ARB_clip_distance is not supported");
382         }
383         else
384         {
385             throw tcu::NotSupportedError("GL_EXT_clip_cull_distance is not supported");
386         }
387         return STOP;
388     }
389 
390     test();
391 
392     return STOP;
393 }
394 
395 /** Constructor.
396  *
397  *  @param context Rendering context handle.
398  **/
APICoverageTest(deqp::Context & context)399 CullDistance::APICoverageTest::APICoverageTest(deqp::Context &context)
400     : CullDistanceTestBase(context, "coverage", "Cull Distance API Coverage Test")
401     , m_bo_id(0)
402     , m_cs_id(0)
403     , m_cs_to_id(0)
404     , m_fbo_draw_id(0)
405     , m_fbo_draw_to_id(0)
406     , m_fbo_read_id(0)
407     , m_fs_id(0)
408     , m_gs_id(0)
409     , m_po_id(0)
410     , m_tc_id(0)
411     , m_te_id(0)
412     , m_vao_id(0)
413     , m_vs_id(0)
414 {
415     if (m_isContextES)
416     {
417         if (!specializationMap["TS_EXTENSION"].empty())
418         {
419             // fix for base constructor - no need to enable GL_EXT_shader_io_blocks
420             specializationMap["TS_EXTENSION"] = "#extension GL_EXT_tessellation_shader : enable";
421         }
422     }
423 }
424 
425 /** @brief Cull Distance API Coverage Test deinitialization */
deinit()426 void CullDistance::APICoverageTest::deinit()
427 {
428     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
429 
430     if (m_bo_id != 0)
431     {
432         gl.deleteBuffers(1, &m_bo_id);
433 
434         m_bo_id = 0;
435     }
436 
437     if (m_cs_id != 0)
438     {
439         gl.deleteShader(m_cs_id);
440 
441         m_cs_id = 0;
442     }
443 
444     if (m_cs_to_id != 0)
445     {
446         gl.deleteTextures(1, &m_cs_to_id);
447 
448         m_cs_to_id = 0;
449     }
450 
451     if (m_fbo_draw_id != 0)
452     {
453         gl.deleteFramebuffers(1, &m_fbo_draw_id);
454 
455         m_fbo_draw_id = 0;
456     }
457 
458     if (m_fbo_draw_to_id != 0)
459     {
460         gl.deleteTextures(1, &m_fbo_draw_to_id);
461 
462         m_fbo_draw_to_id = 0;
463     }
464 
465     if (m_fbo_read_id != 0)
466     {
467         gl.deleteFramebuffers(1, &m_fbo_read_id);
468 
469         m_fbo_read_id = 0;
470     }
471 
472     if (m_fs_id != 0)
473     {
474         gl.deleteShader(m_fs_id);
475 
476         m_fs_id = 0;
477     }
478 
479     if (m_gs_id != 0)
480     {
481         gl.deleteShader(m_gs_id);
482 
483         m_gs_id = 0;
484     }
485 
486     if (m_po_id != 0)
487     {
488         gl.deleteProgram(m_po_id);
489 
490         m_po_id = 0;
491     }
492 
493     if (m_tc_id != 0)
494     {
495         gl.deleteShader(m_tc_id);
496 
497         m_tc_id = 0;
498     }
499 
500     if (m_te_id != 0)
501     {
502         gl.deleteShader(m_te_id);
503 
504         m_te_id = 0;
505     }
506 
507     if (m_vao_id != 0)
508     {
509         gl.deleteVertexArrays(1, &m_vao_id);
510 
511         m_vao_id = 0;
512     }
513 
514     if (m_vs_id != 0)
515     {
516         gl.deleteShader(m_vs_id);
517 
518         m_vs_id = 0;
519     }
520 
521     /* Restore default pack alignment value */
522     gl.pixelStorei(GL_PACK_ALIGNMENT, 4);
523 }
524 
525 /** Executes test iteration.
526  *
527  *  @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
528  */
test()529 void CullDistance::APICoverageTest::test()
530 {
531     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
532 
533     /* Check that calling GetIntegerv with MAX_CULL_DISTANCES doesn't generate
534      * any errors and returns a value at least 8.
535      *
536      * Check that calling GetIntegerv with MAX_COMBINED_CLIP_AND_CULL_DISTANCES
537      * doesn't generate any errors and returns a value at least 8.
538      *
539      */
540     glw::GLint error_code                                    = GL_NO_ERROR;
541     glw::GLint gl_max_cull_distances_value                   = 0;
542     glw::GLint gl_max_combined_clip_and_cull_distances_value = 0;
543 
544     gl.getIntegerv(GL_MAX_CULL_DISTANCES, &gl_max_cull_distances_value);
545 
546     error_code = gl.getError();
547     if (error_code != GL_NO_ERROR)
548     {
549         m_testCtx.getLog() << tcu::TestLog::Message << "glGetIntegerv() returned error code "
550                            << "[" << glu::getErrorStr(error_code)
551                            << "] for GL_MAX_CULL_DISTANCES"
552                               " query instead of GL_NO_ERROR"
553                            << tcu::TestLog::EndMessage;
554 
555         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
556 
557         return;
558     }
559 
560     gl.getIntegerv(GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES, &gl_max_combined_clip_and_cull_distances_value);
561 
562     error_code = gl.getError();
563     if (error_code != GL_NO_ERROR)
564     {
565         m_testCtx.getLog() << tcu::TestLog::Message << "glGetIntegerv() returned error code "
566                            << "[" << glu::getErrorStr(error_code)
567                            << "] for "
568                               "GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES query "
569                               "instead of GL_NO_ERROR"
570                            << tcu::TestLog::EndMessage;
571 
572         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
573 
574         return;
575     }
576 
577     /* Before we proceed with the two other tests, initialize a buffer & a texture
578      * object we will need to capture data from the programs */
579     static const glw::GLuint bo_size = sizeof(int) * 4 /* components */ * 4 /* result points */;
580 
581     gl.genBuffers(1, &m_bo_id);
582     GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed.");
583 
584     gl.genFramebuffers(1, &m_fbo_draw_id);
585     gl.genFramebuffers(1, &m_fbo_read_id);
586     GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() call(s) failed.");
587 
588     gl.genTextures(1, &m_cs_to_id);
589     gl.genTextures(1, &m_fbo_draw_to_id);
590     GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call failed.");
591 
592     gl.genVertexArrays(1, &m_vao_id);
593     GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed.");
594 
595     gl.bindVertexArray(m_vao_id);
596     GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed.");
597 
598     gl.bindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, m_bo_id);
599     gl.bindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* index */
600                       m_bo_id);
601     GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() or glBindBufferBase() call(s) failed.");
602 
603     gl.bufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bo_size, nullptr, GL_STATIC_DRAW);
604     GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed.");
605 
606     for (glw::GLuint n_to_id = 0; n_to_id < 2; /* CS, FBO */ ++n_to_id)
607     {
608         gl.bindTexture(GL_TEXTURE_2D, (n_to_id == 0) ? m_cs_to_id : m_fbo_draw_to_id);
609         GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() call failed.");
610 
611         gl.texStorage2D(GL_TEXTURE_2D, 1, /* levels */
612                         GL_R32I, 1,       /* width */
613                         1);               /* height */
614         GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() call failed.");
615     }
616 
617     bool bind_img_supported = false;
618     if (m_isContextES)
619     {
620         if (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 1)))
621             bind_img_supported = true;
622     }
623     else
624     {
625         if (glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 3)) ||
626             m_context.getContextInfo().isExtensionSupported("GL_ARB_compute_shader"))
627             bind_img_supported = true;
628     }
629 
630     if (bind_img_supported)
631     {
632         gl.bindImageTexture(0,             /* unit */
633                             m_cs_to_id, 0, /* level */
634                             GL_FALSE,      /* layered */
635                             0,             /* layer */
636                             GL_WRITE_ONLY, GL_R32I);
637         GLU_EXPECT_NO_ERROR(gl.getError(), "glBindImageTexture() call failed.");
638     }
639 
640     gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, m_fbo_draw_id);
641     GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call failed.");
642 
643     gl.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_fbo_draw_to_id, 0); /* level */
644     GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed.");
645 
646     gl.bindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo_read_id);
647     GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call failed.");
648 
649     gl.viewport(0,  /* x */
650                 0,  /* y */
651                 1,  /* width */
652                 1); /* height */
653     GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() call failed.");
654 
655     gl.pixelStorei(GL_PACK_ALIGNMENT, 1);
656     GLU_EXPECT_NO_ERROR(gl.getError(), "glPixelStorei() call failed.");
657 
658     /* There are two new GL constants, where value we need to verify */
659     struct _run
660     {
661         const glw::GLchar *essl_token_value;
662         glw::GLenum gl_enum;
663         glw::GLint gl_value;
664         glw::GLint min_value;
665         const glw::GLchar *name;
666     } runs[] = {{"gl_MaxCullDistances", GL_MAX_CULL_DISTANCES, gl_max_cull_distances_value, 8 /*minimum required */,
667                  "GL_MAX_CULL_DISTANCES"},
668                 {"gl_MaxCombinedClipAndCullDistances", GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES,
669                  gl_max_combined_clip_and_cull_distances_value, 8 /*minimum required */,
670                  "GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES"}};
671 
672     static const glw::GLuint n_runs = sizeof(runs) / sizeof(runs[0]);
673 
674     for (glw::GLuint n_run = 0; n_run < n_runs; ++n_run)
675     {
676         _run &current_run = runs[n_run];
677 
678         static const struct _stage
679         {
680             bool use_cs;
681             bool use_fs;
682             bool use_gs;
683             bool use_tc;
684             bool use_te;
685             bool use_vs;
686 
687             const glw::GLchar *fs_input;
688             const glw::GLchar *gs_input;
689             const glw::GLchar *tc_input;
690             const glw::GLchar *te_input;
691 
692             const glw::GLchar *tf_output_name;
693             const glw::GLenum tf_mode;
694 
695             glw::GLenum draw_call_mode;
696             glw::GLuint n_draw_call_vertices;
697         } stages[]                 = {/* CS only test */
698                       {
699                           /* use_cs|use_fs|use_gs|use_tc|use_te|use_vs */
700                           true, false, false, false, false, false,
701 
702                           NULL,    /* fs_input             */
703                           NULL,    /* gs_input             */
704                           NULL,    /* tc_input             */
705                           NULL,    /* te_input             */
706                           NULL,    /* tf_output_name       */
707                           GL_NONE, /* tf_mode              */
708                           GL_NONE, /* draw_call_mode       */
709                           0,       /* n_draw_call_vertices */
710                       },
711                       /* VS+GS+TC+TE+FS test */
712                       {
713                           /* use_cs|use_fs|use_gs|use_tc|use_te|use_vs */
714                           false, true, true, true, true, true,
715 
716                           "out_gs",     /* fs_input             */
717                           "out_te",     /* gs_input             */
718                           "out_vs",     /* tc_input             */
719                           "out_tc",     /* te_input             */
720                           "out_gs",     /* tf_output_name       */
721                           GL_TRIANGLES, /* tf_mode              */
722                           GL_PATCHES,   /* draw_call_mode       */
723                           3,            /* n_draw_call_vertices */
724                       },
725                       /* VS+GS+FS test */
726                       {
727                           /* use_cs|use_fs|use_gs|use_tc|use_te|use_vs */
728                           false, true, true, false, false, true,
729 
730                           "out_gs",     /* fs_input             */
731                           "out_vs",     /* gs_input             */
732                           NULL,         /* tc_input             */
733                           NULL,         /* te_input             */
734                           "out_gs",     /* tf_output_name       */
735                           GL_TRIANGLES, /* tf_mode              */
736                           GL_POINTS,    /* draw_call_mode       */
737                           1,            /* n_draw_call_vertices */
738                       },
739                       /* VS+TC+TE+FS test */
740                       {
741                           /* use_cs|use_fs|use_gs|use_tc|use_te|use_vs */
742                           false, true, false, true, true, true,
743 
744                           "out_te",   /* fs_input             */
745                           NULL,       /* gs_input             */
746                           "out_vs",   /* tc_input             */
747                           "out_tc",   /* te_input             */
748                           "out_te",   /* tf_output_name       */
749                           GL_POINTS,  /* tf_mode              */
750                           GL_PATCHES, /* draw_call_mode       */
751                           3           /* n_draw_call_vertices */
752                       },
753                       /* VS test */
754                       {
755                           /* use_cs|use_fs|use_gs|use_tc|use_te|use_vs */
756                           false, false, false, false, false, true,
757 
758                           "out_vs",  /* fs_input             */
759                           NULL,      /* gs_input             */
760                           NULL,      /* tc_input             */
761                           NULL,      /* te_input             */
762                           "out_vs",  /* tf_output_name       */
763                           GL_POINTS, /* tf_mode              */
764                           GL_POINTS, /* draw_call_mode       */
765                           1          /* n_draw_call_vertices */
766                       }};
767                         const glw::GLuint n_stages = sizeof(stages) / sizeof(stages[0]);
768 
769         /* Run through all test stages */
770         for (glw::GLuint n_stage = 0; n_stage < n_stages; ++n_stage)
771         {
772             /* Check for OpenGL feature support */
773             if (stages[n_stage].use_cs)
774             {
775                 if (m_isContextES)
776                 {
777                     if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 1)))
778                     {
779                         continue; // no compute shader support
780                     }
781                 }
782                 else
783                 {
784                     if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 3)) &&
785                         !m_context.getContextInfo().isExtensionSupported("GL_ARB_compute_shader"))
786                     {
787                         continue; // no compute shader support
788                     }
789                 }
790             }
791             if (stages[n_stage].use_tc || stages[n_stage].use_te)
792             {
793                 if (m_isContextES)
794                 {
795                     if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) &&
796                         !m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader"))
797                     {
798                         continue; // no tessellation shader support
799                     }
800                 }
801                 else
802                 {
803                     if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0)) &&
804                         !m_context.getContextInfo().isExtensionSupported("GL_ARB_tessellation_shader"))
805                     {
806                         continue; // no tessellation shader support
807                     }
808                 }
809             }
810 
811             if (m_isContextES)
812             {
813                 // OpenGL ES needs at least vs and fs or cs
814                 if (!((stages[n_stage].use_vs && stages[n_stage].use_fs) || stages[n_stage].use_cs))
815                     continue;
816 
817                 if (stages[n_stage].use_gs)
818                 {
819                     if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) &&
820                         !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
821                     {
822                         continue; // no geometry shader support
823                     }
824                 }
825             }
826 
827             /* Check that use of the GLSL built-in constant gl_MaxCullDistance in any
828              * shader stage (including compute shader) does not affect the shader
829              * compilation & program linking process.
830              */
831             static const glw::GLchar *cs_body_template =
832                 "${CS_VERSION}\n"
833                 "\n"
834                 "${EXTENSION}\n"
835                 "\n"
836                 "${CS_EXTENSION}\n"
837                 "\n"
838                 "layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;\n"
839                 "\n"
840                 "layout(r32i) uniform writeonly iimage2D result;\n"
841                 "\n"
842                 "void main()\n"
843                 "{\n"
844                 "    imageStore(result, ivec2(0),ivec4(TOKEN) );\n"
845                 "}\n";
846             std::string cs_body = tcu::StringTemplate(cs_body_template).specialize(specializationMap);
847 
848             static const glw::GLchar *fs_body_template = "${VERSION}\n"
849                                                          "\n"
850                                                          "${EXTENSION}\n"
851                                                          "\n"
852                                                          "${PRECISION}\n"
853                                                          "\n"
854                                                          "flat in  int INPUT_FS_NAME;\n"
855                                                          "out int out_fs;\n"
856                                                          "\n"
857                                                          "void main()\n"
858                                                          "{\n"
859                                                          "    if (INPUT_FS_NAME == TOKEN)\n"
860                                                          "    {\n"
861                                                          "        out_fs = TOKEN;\n"
862                                                          "    }\n"
863                                                          "    else\n"
864                                                          "    {\n"
865                                                          "        out_fs = -1;\n"
866                                                          "    }\n"
867                                                          "}\n";
868             std::string fs_body = tcu::StringTemplate(fs_body_template).specialize(specializationMap);
869 
870             static const glw::GLchar *gs_body_template =
871                 "${GS_VERSION}\n"
872                 "\n"
873                 "${EXTENSION}\n"
874                 "\n"
875                 "${GS_EXTENSION}\n"
876                 "\n"
877                 "flat in  int INPUT_GS_NAME[1];\n"
878                 "flat out int out_gs;\n"
879                 "\n"
880                 "layout(points)                           in;\n"
881                 "layout(triangle_strip, max_vertices = 4) out;\n"
882                 "\n"
883                 "void main()\n"
884                 "{\n"
885                 "    int result_value = (INPUT_GS_NAME[0] == TOKEN) ? TOKEN : -1;\n"
886                 "\n"
887                 /* Draw a full-screen quad */
888                 "    gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n"
889                 "    out_gs      = result_value;\n"
890                 "    EmitVertex();\n"
891                 "\n"
892                 "    gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
893                 "    out_gs      = result_value;\n"
894                 "    EmitVertex();\n"
895                 "\n"
896                 "    gl_Position = vec4(1.0, 1.0, 0.0, 1.0);\n"
897                 "    out_gs      = result_value;\n"
898                 "    EmitVertex();\n"
899                 "\n"
900                 "    gl_Position = vec4(1.0, -1.0, 0.0, 1.0);\n"
901                 "    out_gs      = result_value;\n"
902                 "    EmitVertex();\n"
903                 "    EndPrimitive();\n"
904                 "}\n";
905             std::string gs_body = tcu::StringTemplate(gs_body_template).specialize(specializationMap);
906 
907             static const glw::GLchar *tc_body_template =
908                 "${TS_VERSION}\n"
909                 "${EXTENSION}\n"
910                 "${TS_EXTENSION}\n"
911                 "\n"
912                 "layout(vertices = 1) out;\n"
913                 "\n"
914                 "flat in  int INPUT_TC_NAME[];\n"
915                 "flat out int out_tc       [];\n"
916                 "\n"
917                 "void main()\n"
918                 "{\n"
919                 "    int result_value = (INPUT_TC_NAME[0] == TOKEN) ? TOKEN : -1;\n"
920                 "\n"
921                 "    out_tc[gl_InvocationID]             = result_value;\n"
922                 "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
923                 "    gl_TessLevelInner[0]                = 1.0;\n"
924                 "    gl_TessLevelInner[1]                = 1.0;\n"
925                 "    gl_TessLevelOuter[0]                = 1.0;\n"
926                 "    gl_TessLevelOuter[1]                = 1.0;\n"
927                 "    gl_TessLevelOuter[2]                = 1.0;\n"
928                 "    gl_TessLevelOuter[3]                = 1.0;\n"
929                 "}\n";
930             std::string tc_body = tcu::StringTemplate(tc_body_template).specialize(specializationMap);
931 
932             static const glw::GLchar *te_body_template =
933                 "${TS_VERSION}\n"
934                 "${EXTENSION}\n"
935                 "${TS_EXTENSION}\n"
936                 "\n"
937                 "flat in  int INPUT_TE_NAME[];\n"
938                 "flat out int out_te;\n"
939                 "\n"
940                 "layout(isolines, point_mode) in;\n"
941                 "\n"
942                 "void main()\n"
943                 "{\n"
944                 "    int result_value = (INPUT_TE_NAME[0] == TOKEN) ? TOKEN : 0;\n"
945                 "\n"
946                 "    out_te = result_value;\n"
947                 "\n"
948                 "    gl_Position = vec4(0.0, 0.0, 0.0, 1.);\n"
949                 "}\n";
950             std::string te_body = tcu::StringTemplate(te_body_template).specialize(specializationMap);
951 
952             static const glw::GLchar *vs_body_template = "${VERSION}\n"
953                                                          "\n"
954                                                          "${EXTENSION}\n"
955                                                          "\n"
956                                                          "flat out int out_vs;\n"
957                                                          "\n"
958                                                          "void main()\n"
959                                                          "{\n"
960                                                          "    gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
961                                                          "    out_vs      = TOKEN;\n"
962                                                          "}\n";
963             std::string vs_body = tcu::StringTemplate(vs_body_template).specialize(specializationMap);
964 
965             const _stage &current_stage = stages[n_stage];
966 
967             /* Build shader bodies */
968             struct _shader_body
969             {
970                 std::string *body_ptr;
971                 glw::GLenum gl_type;
972             } shader_bodies[] = {{&cs_body, GL_COMPUTE_SHADER},         {&fs_body, GL_FRAGMENT_SHADER},
973                                  {&gs_body, GL_GEOMETRY_SHADER},        {&tc_body, GL_TESS_CONTROL_SHADER},
974                                  {&te_body, GL_TESS_EVALUATION_SHADER}, {&vs_body, GL_VERTEX_SHADER}};
975             static const glw::GLchar *input_fs_token_string = "INPUT_FS_NAME";
976             static const glw::GLchar *input_gs_token_string = "INPUT_GS_NAME";
977             static const glw::GLchar *input_te_token_string = "INPUT_TE_NAME";
978             static const glw::GLchar *input_tc_token_string = "INPUT_TC_NAME";
979             static const glw::GLuint n_shader_bodies        = sizeof(shader_bodies) / sizeof(shader_bodies[0]);
980 
981             std::size_t token_position             = std::string::npos;
982             static const glw::GLchar *token_string = "TOKEN";
983 
984             for (glw::GLuint n_shader_body = 0; n_shader_body < n_shader_bodies; ++n_shader_body)
985             {
986                 _shader_body &current_body = shader_bodies[n_shader_body];
987 
988                 /* Is this stage actually used? */
989                 if (((current_body.gl_type == GL_COMPUTE_SHADER) && (!current_stage.use_cs)) ||
990                     ((current_body.gl_type == GL_FRAGMENT_SHADER) && (!current_stage.use_fs)) ||
991                     ((current_body.gl_type == GL_TESS_CONTROL_SHADER) && (!current_stage.use_tc)) ||
992                     ((current_body.gl_type == GL_TESS_EVALUATION_SHADER) && (!current_stage.use_te)) ||
993                     ((current_body.gl_type == GL_VERTEX_SHADER) && (!current_stage.use_vs)))
994                 {
995                     /* Skip the iteration. */
996                     continue;
997                 }
998 
999                 /* Iterate over all token and replace them with stage-specific values */
1000                 struct _token_value_pair
1001                 {
1002                     const glw::GLchar *token;
1003                     const glw::GLchar *value;
1004                 } token_value_pairs[] = {
1005                     /* NOTE: The last entry is filled by the switch() block below */
1006                     {token_string, current_run.essl_token_value},
1007                     {NULL, NULL},
1008                 };
1009 
1010                 const size_t n_token_value_pairs = sizeof(token_value_pairs) / sizeof(token_value_pairs[0]);
1011 
1012                 switch (current_body.gl_type)
1013                 {
1014                 case GL_COMPUTE_SHADER:
1015                 case GL_VERTEX_SHADER:
1016                     break;
1017 
1018                 case GL_FRAGMENT_SHADER:
1019                 {
1020                     token_value_pairs[1].token = input_fs_token_string;
1021                     token_value_pairs[1].value = current_stage.fs_input;
1022 
1023                     break;
1024                 }
1025 
1026                 case GL_GEOMETRY_SHADER:
1027                 {
1028                     token_value_pairs[1].token = input_gs_token_string;
1029                     token_value_pairs[1].value = current_stage.gs_input;
1030 
1031                     break;
1032                 }
1033 
1034                 case GL_TESS_CONTROL_SHADER:
1035                 {
1036                     token_value_pairs[1].token = input_tc_token_string;
1037                     token_value_pairs[1].value = current_stage.tc_input;
1038 
1039                     break;
1040                 }
1041 
1042                 case GL_TESS_EVALUATION_SHADER:
1043                 {
1044                     token_value_pairs[1].token = input_te_token_string;
1045                     token_value_pairs[1].value = current_stage.te_input;
1046 
1047                     break;
1048                 }
1049 
1050                 default:
1051                     TCU_FAIL("Unrecognized shader body type");
1052                 }
1053 
1054                 for (glw::GLuint n_pair = 0; n_pair < n_token_value_pairs; ++n_pair)
1055                 {
1056                     const _token_value_pair &current_pair = token_value_pairs[n_pair];
1057 
1058                     if (current_pair.token == NULL || current_pair.value == NULL)
1059                     {
1060                         continue;
1061                     }
1062 
1063                     while ((token_position = current_body.body_ptr->find(current_pair.token)) != std::string::npos)
1064                     {
1065                         current_body.body_ptr->replace(token_position, strlen(current_pair.token), current_pair.value);
1066                     }
1067                 } /* for (all token+value pairs) */
1068             }     /* for (all sader bodies) */
1069 
1070             /* Build the test program */
1071             CullDistance::Utilities::buildProgram(
1072                 gl, m_testCtx, current_stage.use_cs ? cs_body.c_str() : nullptr,
1073                 current_stage.use_fs ? fs_body.c_str() : nullptr, current_stage.use_gs ? gs_body.c_str() : nullptr,
1074                 current_stage.use_tc ? tc_body.c_str() : nullptr, current_stage.use_te ? te_body.c_str() : nullptr,
1075                 current_stage.use_vs ? vs_body.c_str() : nullptr, (current_stage.tf_output_name != NULL) ? 1 : 0,
1076                 (const glw::GLchar **)&current_stage.tf_output_name, &m_po_id);
1077 
1078             /* Bind the test program */
1079             DE_ASSERT(m_po_id != 0);
1080 
1081             gl.useProgram(m_po_id);
1082             GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
1083 
1084             /* Execute the draw call. Transform Feed-back should be enabled for all iterations
1085              * par the CS one, since we use a different tool to capture the result data in the
1086              * latter case.
1087              */
1088             if (!current_stage.use_cs)
1089             {
1090                 gl.beginTransformFeedback(current_stage.tf_mode);
1091                 GLU_EXPECT_NO_ERROR(gl.getError(), "glBeginTransformFeedback() call failed.");
1092 
1093                 gl.drawArrays(current_stage.draw_call_mode, 0,     /* first */
1094                               current_stage.n_draw_call_vertices); /* count */
1095                 GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArrays() call failed.");
1096 
1097                 gl.endTransformFeedback();
1098                 GLU_EXPECT_NO_ERROR(gl.getError(), "glEndTransformFeedback() call failed.");
1099             } /* if (uses_tf) */
1100             else
1101             {
1102                 gl.dispatchCompute(1,  /* num_groups_x */
1103                                    1,  /* num_groups_y */
1104                                    1); /* num_groups_z */
1105                 GLU_EXPECT_NO_ERROR(gl.getError(), "glDispatchCompute() call failed.");
1106             }
1107 
1108             /* Verify the result values */
1109             if (!current_stage.use_cs)
1110             {
1111                 glw::GLint *result_data_ptr = nullptr;
1112 
1113                 /* Retrieve the data captured by Transform Feedback */
1114                 result_data_ptr = (glw::GLint *)gl.mapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */
1115                                                                   sizeof(unsigned int) * 1, GL_MAP_READ_BIT);
1116                 GLU_EXPECT_NO_ERROR(gl.getError(), "glMapBufferRange() call failed.");
1117 
1118                 if (*result_data_ptr != current_run.gl_value)
1119                 {
1120                     m_testCtx.getLog() << tcu::TestLog::Message << current_run.name
1121                                        << " value "
1122                                           "["
1123                                        << *result_data_ptr
1124                                        << "]"
1125                                           " does not match the one reported by glGetIntegerv() "
1126                                           "["
1127                                        << current_run.gl_value << "]" << tcu::TestLog::EndMessage;
1128 
1129                     TCU_FAIL("GL constant value does not match the ES SL equivalent");
1130                 }
1131 
1132                 if (*result_data_ptr < current_run.min_value)
1133                 {
1134                     m_testCtx.getLog() << tcu::TestLog::Message << current_run.name
1135                                        << " value "
1136                                           "["
1137                                        << *result_data_ptr
1138                                        << "]"
1139                                           " does not meet the minimum specification requirements "
1140                                           "["
1141                                        << current_run.min_value << "]" << tcu::TestLog::EndMessage;
1142 
1143                     TCU_FAIL("GL constant value does not meet minimum specification requirements");
1144                 }
1145 
1146                 gl.unmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
1147                 GLU_EXPECT_NO_ERROR(gl.getError(), "glUnmapBuffer() call failed.");
1148             }
1149 
1150             for (glw::GLuint n_stage_internal = 0; n_stage_internal < 2; /* CS, FS write to separate textures */
1151                  ++n_stage_internal)
1152             {
1153                 glw::GLuint to_id = (n_stage_internal == 0) ? m_cs_to_id : m_fbo_draw_to_id;
1154 
1155                 if (((n_stage_internal == 0) && (!current_stage.use_cs)) ||
1156                     ((n_stage_internal == 1) && (!current_stage.use_fs)))
1157                 {
1158                     /* Skip the iteration */
1159                     continue;
1160                 }
1161 
1162                 /* Check the image data the test CS / FS should have written */
1163                 glw::GLint result_value = 0;
1164 
1165                 gl.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, to_id, 0); /* level */
1166                 GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed.");
1167 
1168                 /* NOTE: We're using our custom read framebuffer here, so we'll be reading
1169                  *       from the texture, that the writes have been issued to earlier. */
1170                 gl.finish();
1171                 GLU_EXPECT_NO_ERROR(gl.getError(), "glMemoryBarrier() call failed.");
1172 
1173                 gl.readPixels(0, /* x */
1174                               0, /* y */
1175                               1, /* width */
1176                               1, /* height */
1177                               GL_RED_INTEGER, GL_INT, &result_value);
1178                 GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() call failed.");
1179 
1180                 if (result_value != current_run.gl_value)
1181                 {
1182                     m_testCtx.getLog() << tcu::TestLog::Message << current_run.name
1183                                        << " value accessible to the compute / fragment shader "
1184                                           "["
1185                                        << result_value
1186                                        << "]"
1187                                           " does not match the one reported by glGetIntegerv() "
1188                                           "["
1189                                        << current_run.gl_value << "]" << tcu::TestLog::EndMessage;
1190 
1191                     TCU_FAIL("GL constant value does not match the ES SL equivalent");
1192                 }
1193 
1194                 if (result_value < current_run.min_value)
1195                 {
1196                     m_testCtx.getLog() << tcu::TestLog::Message << current_run.name
1197                                        << " value accessible to the compute / fragment shader "
1198                                           "["
1199                                        << result_value
1200                                        << "]"
1201                                           " does not meet the minimum specification requirements "
1202                                           "["
1203                                        << current_run.min_value << "]" << tcu::TestLog::EndMessage;
1204 
1205                     TCU_FAIL("GL constant value does not meet minimum specification requirements");
1206                 }
1207             }
1208 
1209             /* Clear the data buffer before we continue */
1210             static const glw::GLubyte bo_clear_data[bo_size] = {0};
1211 
1212             gl.bufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, /* offset */
1213                              bo_size, bo_clear_data);
1214             GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferSubData() call failed.");
1215 
1216             /* Clear the texture mip-map before we continue */
1217             glw::GLint clear_values[4] = {0, 0, 0, 0};
1218 
1219             gl.clearBufferiv(GL_COLOR, 0, /* drawbuffer */
1220                              clear_values);
1221             GLU_EXPECT_NO_ERROR(gl.getError(), "glClearBufferiv() call failed.");
1222 
1223             /* Release program before we move on to the next iteration */
1224             if (m_po_id != 0)
1225             {
1226                 gl.deleteProgram(m_po_id);
1227 
1228                 m_po_id = 0;
1229             }
1230         } /* for (all stages) */
1231     }     /* for (both runs) */
1232 
1233     /* All done */
1234     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
1235 
1236     return;
1237 }
1238 
1239 /** Constructor.
1240  *
1241  *  @param context Rendering context handle.
1242  **/
FunctionalTest(deqp::Context & context,_test_item test_item,_primitive_mode primitive_mode,glw::GLint iteration)1243 CullDistance::FunctionalTest::FunctionalTest(deqp::Context &context, _test_item test_item,
1244                                              _primitive_mode primitive_mode, glw::GLint iteration)
1245     : CullDistanceTestBase(context, "functional", "Cull Distance Functional Test")
1246     , m_bo_data()
1247     , m_bo_id(0)
1248     , m_fbo_id(0)
1249     , m_po_id(0)
1250     , m_render_primitives(0)
1251     , m_render_vertices(0)
1252     , m_sub_grid_cell_size(0)
1253     , m_to_id(0)
1254     , m_vao_id(0)
1255     , m_to_height(512)
1256     , m_to_width(512)
1257     , m_to_pixel_data_cache()
1258     , m_test_item(test_item)
1259     , m_primitive_mode(primitive_mode)
1260     , m_iteration(iteration)
1261 {
1262     std::string name = "functional_test_item_" + std::to_string(m_test_item.test_id) + "_primitive_" +
1263                        primitiveModeToString(m_primitive_mode) + "_max_culldist_" + std::to_string(m_iteration);
1264     TestCase::m_name = name;
1265 
1266     if (!m_isContextES)
1267     {
1268         specializationMap["VERSION"]    = "#version 450";
1269         specializationMap["TS_VERSION"] = specializationMap["VERSION"];
1270         specializationMap["GS_VERSION"] = specializationMap["VERSION"];
1271     }
1272 }
1273 
primitiveModeToString(_primitive_mode mode)1274 std::string CullDistance::FunctionalTest::primitiveModeToString(_primitive_mode mode)
1275 {
1276     switch (mode)
1277     {
1278     case PRIMITIVE_MODE_LINES:
1279         return "mode_lines";
1280     case PRIMITIVE_MODE_POINTS:
1281         return "mode_points";
1282     case PRIMITIVE_MODE_TRIANGLES:
1283         return "mode_triangles";
1284     default:
1285         return "default";
1286     }
1287     return "default";
1288 }
1289 
1290 /** @brief Build OpenGL program for functional tests
1291  *
1292  *  @param [in]  clipdistances_array_size   use size of gl_ClipDistance array
1293  *  @param [in]  culldistances_array_size   use size of gl_CullDistance array
1294  *  @param [in]  dynamic_index_writes       use dunamic indexing for setting  the gl_ClipDistance and gl_CullDistance arrays
1295  *  @param [in]  primitive_mode             primitive_mode will be used for rendering
1296  *  @param [in]  redeclare_clipdistances    redeclare gl_ClipDistance
1297  *  @param [in]  redeclare_culldistances    redeclare gl_CullDistance
1298  *  @param [in]  use_core_functionality     use core OpenGL functionality
1299  *  @param [in]  use_gs                     use geometry shader
1300  *  @param [in]  use_ts                     use tessellation shader
1301  *  @param [in]  fetch_culldistance_from_fs fetch check sum of gl_ClipDistance and gl_CullDistance from fragment shader
1302  */
buildPO(glw::GLuint clipdistances_array_size,glw::GLuint culldistances_array_size,bool dynamic_index_writes,_primitive_mode primitive_mode,bool redeclare_clipdistances,bool redeclare_culldistances,bool use_core_functionality,bool use_gs,bool use_ts,bool fetch_culldistance_from_fs)1303 void CullDistance::FunctionalTest::buildPO(glw::GLuint clipdistances_array_size, glw::GLuint culldistances_array_size,
1304                                            bool dynamic_index_writes, _primitive_mode primitive_mode,
1305                                            bool redeclare_clipdistances, bool redeclare_culldistances,
1306                                            bool use_core_functionality, bool use_gs, bool use_ts,
1307                                            bool fetch_culldistance_from_fs)
1308 {
1309     deinitPO();
1310 
1311     /* Form the vertex shader */
1312     glw::GLuint clipdistances_input_size =
1313         clipdistances_array_size > 0 ? clipdistances_array_size : 1; /* Avoid zero-sized array compilation error */
1314     glw::GLuint culldistances_input_size =
1315         culldistances_array_size > 0 ? culldistances_array_size : 1; /* Avoid zero-sized array compilation error */
1316     static const glw::GLchar *dynamic_array_setters =
1317         "\n"
1318         "#if TEMPLATE_N_GL_CLIPDISTANCE_ENTRIES\n"
1319         "     for (int n_clipdistance_entry = 0;\n"
1320         "          n_clipdistance_entry < TEMPLATE_N_GL_CLIPDISTANCE_ENTRIES;\n"
1321         "        ++n_clipdistance_entry)\n"
1322         "     {\n"
1323         "         ASSIGN_CLIP_DISTANCE(n_clipdistance_entry);\n"
1324         "     }\n"
1325         "#endif"
1326         "\n"
1327         "#if TEMPLATE_N_GL_CULLDISTANCE_ENTRIES \n"
1328         "     for (int n_culldistance_entry = 0;\n"
1329         "          n_culldistance_entry < TEMPLATE_N_GL_CULLDISTANCE_ENTRIES;\n"
1330         "        ++n_culldistance_entry)\n"
1331         "     {\n"
1332         "         ASSIGN_CULL_DISTANCE(n_culldistance_entry);\n"
1333         "     }\n"
1334         "#endif\n";
1335 
1336     static const glw::GLchar *fetch_function = "highp float fetch()\n"
1337                                                "{\n"
1338                                                "    highp float sum = 0.0;\n"
1339                                                "\n"
1340                                                "TEMPLATE_SUM_SETTER"
1341                                                "\n"
1342                                                "    return sum / TEMPLATE_SUM_DIVIDER;\n"
1343                                                "}\n"
1344                                                "\n"
1345                                                "#define ASSIGN_RETURN_VALUE fetch()";
1346 
1347     static const glw::GLchar *fs_template = "${VERSION}\n"
1348                                             "${EXTENSION}\n"
1349                                             "${PRECISION}\n"
1350                                             "\n"
1351                                             "TEMPLATE_REDECLARE_CLIPDISTANCE\n"
1352                                             "TEMPLATE_REDECLARE_CULLDISTANCE\n"
1353                                             "\n"
1354                                             "TEMPLATE_ASSIGN_RETURN_VALUE\n"
1355                                             "\n"
1356                                             "out vec4 out_fs;\n"
1357                                             "\n"
1358                                             "/* Fragment shader main function */\n"
1359                                             "void main()\n"
1360                                             "{\n"
1361                                             "    out_fs = vec4(ASSIGN_RETURN_VALUE, 1.0, 1.0, 1.0);\n"
1362                                             "}\n";
1363 
1364     static const glw::GLchar *gs_template = "${GS_VERSION}\n"
1365                                             "${EXTENSION}\n"
1366                                             "${GS_EXTENSION}\n"
1367                                             "\n"
1368                                             "TEMPLATE_LAYOUT_IN\n"
1369                                             "TEMPLATE_LAYOUT_OUT\n"
1370                                             "\n"
1371                                             "TEMPLATE_REDECLARE_CLIPDISTANCE\n"
1372                                             "TEMPLATE_REDECLARE_CULLDISTANCE\n"
1373                                             "\n"
1374                                             "#define ASSIGN_CLIP_DISTANCE(IDX) TEMPLATE_ASSIGN_CLIP_DISTANCE\n"
1375                                             "#define ASSIGN_CULL_DISTANCE(IDX) TEMPLATE_ASSIGN_CULL_DISTANCE\n"
1376                                             "\n"
1377                                             "/* Geometry shader (passthrough) main function */\n"
1378                                             "void main()\n"
1379                                             "{\n"
1380                                             "    for (int n_vertex_index = 0;\n"
1381                                             "             n_vertex_index < gl_in.length();\n"
1382                                             "             n_vertex_index ++)\n"
1383                                             "    {\n"
1384                                             "        gl_Position = gl_in[n_vertex_index].gl_Position;\n"
1385                                             "\n"
1386                                             "        TEMPLATE_ARRAY_SETTERS\n"
1387                                             "\n"
1388                                             "        EmitVertex();\n"
1389                                             "    }\n"
1390                                             "\n"
1391                                             "    EndPrimitive();\n"
1392                                             "}\n";
1393 
1394     static const glw::GLchar *tc_template =
1395         "${TS_VERSION}\n"
1396         "${EXTENSION}\n"
1397         "${TS_EXTENSION}\n"
1398         "\n"
1399         "TEMPLATE_LAYOUT_OUT\n"
1400         "\n"
1401         "out gl_PerVertex {\n"
1402         "TEMPLATE_REDECLARE_CLIPDISTANCE\n"
1403         "TEMPLATE_REDECLARE_CULLDISTANCE\n"
1404         "vec4 gl_Position;\n"
1405         "} gl_out[];\n"
1406         "\n"
1407         "#define ASSIGN_CLIP_DISTANCE(IDX) TEMPLATE_ASSIGN_CLIP_DISTANCE\n"
1408         "#define ASSIGN_CULL_DISTANCE(IDX) TEMPLATE_ASSIGN_CULL_DISTANCE\n"
1409         "\n"
1410         "/* Tesselation control shader main function */\n"
1411         "void main()\n"
1412         "{\n"
1413         "    gl_TessLevelInner[0] = 1.0;\n"
1414         "    gl_TessLevelInner[1] = 1.0;\n"
1415         "    gl_TessLevelOuter[0] = 1.0;\n"
1416         "    gl_TessLevelOuter[1] = 1.0;\n"
1417         "    gl_TessLevelOuter[2] = 1.0;\n"
1418         "    gl_TessLevelOuter[3] = 1.0;\n"
1419         "    /* Clipdistance and culldistance array setters */\n"
1420         "    {\n"
1421         "        TEMPLATE_ARRAY_SETTERS\n"
1422         "    }\n"
1423         "    gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
1424         "}\n";
1425 
1426     static const glw::GLchar *te_template = "${TS_VERSION}\n"
1427                                             "${EXTENSION}\n"
1428                                             "${TS_EXTENSION}\n"
1429                                             "\n"
1430                                             "TEMPLATE_LAYOUT_IN\n"
1431                                             "\n"
1432                                             "in gl_PerVertex {\n"
1433                                             "TEMPLATE_REDECLARE_IN_CLIPDISTANCE\n"
1434                                             "TEMPLATE_REDECLARE_IN_CULLDISTANCE\n"
1435                                             "vec4 gl_Position;\n"
1436                                             "} gl_in[];\n"
1437                                             "\n"
1438                                             "TEMPLATE_REDECLARE_CLIPDISTANCE\n"
1439                                             "TEMPLATE_REDECLARE_CULLDISTANCE\n"
1440                                             "\n"
1441                                             "#define ASSIGN_CLIP_DISTANCE(IDX) TEMPLATE_ASSIGN_CLIP_DISTANCE\n"
1442                                             "#define ASSIGN_CULL_DISTANCE(IDX) TEMPLATE_ASSIGN_CULL_DISTANCE\n"
1443                                             "\n"
1444                                             "/* Tesselation evaluation shader main function */\n"
1445                                             "void main()\n"
1446                                             "{\n"
1447                                             "    /* Clipdistance and culldistance array setters */\n"
1448                                             "    {\n"
1449                                             "        TEMPLATE_ARRAY_SETTERS\n"
1450                                             "    }\n"
1451                                             "    gl_Position = TEMPLATE_OUT_FORMULA;\n"
1452                                             "}\n";
1453 
1454     static const glw::GLchar *vs_template =
1455         "${VERSION}\n"
1456         "${EXTENSION}\n"
1457         "\n"
1458         "TEMPLATE_CLIPDISTANCE_ATTRIB_DECLARATIONS\n"
1459         "TEMPLATE_CULLDISTANCE_ATTRIB_DECLARATIONS\n"
1460         "in vec2  position;\n"
1461         "\n"
1462         "TEMPLATE_REDECLARE_CLIPDISTANCE\n"
1463         "TEMPLATE_REDECLARE_CULLDISTANCE\n"
1464         "\n"
1465         "#define ASSIGN_CLIP_DISTANCE(IDX) TEMPLATE_ASSIGN_CLIP_DISTANCE\n"
1466         "#define ASSIGN_CULL_DISTANCE(IDX) TEMPLATE_ASSIGN_CULL_DISTANCE\n"
1467         "\n"
1468         "/* Vertex shader main function */\n"
1469         "void main()\n"
1470         "{\n"
1471         "    /* Clipdistance and culldistance array setters */\n"
1472         "    {\n"
1473         "        TEMPLATE_ARRAY_SETTERS\n"
1474         "    }\n"
1475         "    gl_Position = vec4(2.0 * position.x - 1.0, 2.0 * position.y - 1.0, 0.0, 1.0);\n"
1476         "}\n";
1477 
1478     std::string *shader_body_string_fs = nullptr;
1479     std::string *shader_body_string_gs = nullptr;
1480     std::string *shader_body_string_tc = nullptr;
1481     std::string *shader_body_string_te = nullptr;
1482     std::string *shader_body_string_vs = nullptr;
1483 
1484     if (!m_isContextES)
1485     {
1486         if (!use_core_functionality)
1487         {
1488             specializationMap["VERSION"]   = "#version 150";
1489             specializationMap["EXTENSION"] = "#extension GL_ARB_cull_distance : require\n"
1490                                              "\n"
1491                                              "#ifndef GL_ARB_cull_distance\n"
1492                                              "    #error GL_ARB_cull_distance is undefined\n"
1493                                              "#endif\n";
1494         }
1495         else
1496         {
1497             specializationMap["VERSION"]   = "#version 450";
1498             specializationMap["EXTENSION"] = "";
1499         }
1500 
1501         specializationMap["TS_VERSION"] = specializationMap["VERSION"];
1502         specializationMap["GS_VERSION"] = specializationMap["VERSION"];
1503     }
1504 
1505     struct _shaders_configuration
1506     {
1507         glw::GLenum type;
1508         const glw::GLchar *shader_template;
1509         std::string body;
1510         const bool use;
1511     } shaders_configuration[] = {{
1512                                      GL_FRAGMENT_SHADER,
1513                                      fs_template,
1514                                      std::string(),
1515                                      true,
1516                                  },
1517                                  {
1518                                      GL_GEOMETRY_SHADER,
1519                                      gs_template,
1520                                      std::string(),
1521                                      use_gs,
1522                                  },
1523                                  {
1524                                      GL_TESS_CONTROL_SHADER,
1525                                      tc_template,
1526                                      std::string(),
1527                                      use_ts,
1528                                  },
1529                                  {
1530                                      GL_TESS_EVALUATION_SHADER,
1531                                      te_template,
1532                                      std::string(),
1533                                      use_ts,
1534                                  },
1535                                  {
1536                                      GL_VERTEX_SHADER,
1537                                      vs_template,
1538                                      std::string(),
1539                                      true,
1540                                  }};
1541 
1542     const glw::GLuint n_shaders_configuration = sizeof(shaders_configuration) / sizeof(shaders_configuration[0]);
1543 
1544     /* Construct shader bodies out of templates */
1545     for (glw::GLuint n_shader_index = 0; n_shader_index < n_shaders_configuration; n_shader_index++)
1546     {
1547         if (shaders_configuration[n_shader_index].use)
1548         {
1549             std::string array_setters;
1550             std::string clipdistance_array_declaration;
1551             std::string culldistance_array_declaration;
1552             std::string clipdistance_in_array_declaration;
1553             std::string culldistance_in_array_declaration;
1554             std::string &shader_source = shaders_configuration[n_shader_index].body;
1555 
1556             /* Copy template into shader body source */
1557             shader_source = tcu::StringTemplate(shaders_configuration[n_shader_index].shader_template)
1558                                 .specialize(specializationMap);
1559 
1560             /* Shader-specific actions */
1561             switch (shaders_configuration[n_shader_index].type)
1562             {
1563             case GL_FRAGMENT_SHADER:
1564             {
1565                 shader_body_string_fs = &shaders_configuration[n_shader_index].body;
1566 
1567                 if (fetch_culldistance_from_fs)
1568                 {
1569                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ASSIGN_RETURN_VALUE"),
1570                                                         std::string(fetch_function));
1571 
1572                     std::string fetch_sum_setters = "";
1573                     for (glw::GLuint i = 0; i < clipdistances_array_size; ++i)
1574                     {
1575                         fetch_sum_setters.append("    sum += abs(gl_ClipDistance[");
1576                         fetch_sum_setters.append(CullDistance::Utilities::intToString(i));
1577                         fetch_sum_setters.append("]) * ");
1578                         fetch_sum_setters.append(CullDistance::Utilities::intToString(i + 1));
1579                         fetch_sum_setters.append(".0;\n");
1580                     }
1581 
1582                     fetch_sum_setters.append("\n");
1583 
1584                     for (glw::GLuint i = 0; i < culldistances_array_size; ++i)
1585                     {
1586                         fetch_sum_setters.append("    sum += abs(gl_CullDistance[");
1587                         fetch_sum_setters.append(CullDistance::Utilities::intToString(i));
1588                         fetch_sum_setters.append("]) * ");
1589                         fetch_sum_setters.append(
1590                             CullDistance::Utilities::intToString(i + 1 + clipdistances_array_size));
1591                         fetch_sum_setters.append(".0;\n");
1592                     }
1593 
1594                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_SUM_SETTER"),
1595                                                         std::string(fetch_sum_setters));
1596                     CullDistance::Utilities::replaceAll(
1597                         shader_source, std::string("TEMPLATE_SUM_DIVIDER"),
1598                         std::string(CullDistance::Utilities::intToString(
1599                                         (clipdistances_array_size + culldistances_array_size) *
1600                                         ((clipdistances_array_size + culldistances_array_size + 1))))
1601                             .append(".0"));
1602                 }
1603                 else
1604                 {
1605                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ASSIGN_RETURN_VALUE"),
1606                                                         std::string("#define ASSIGN_RETURN_VALUE 1.0"));
1607                 }
1608 
1609                 break;
1610             }
1611 
1612             case GL_GEOMETRY_SHADER:
1613             {
1614                 shader_body_string_gs = &shaders_configuration[n_shader_index].body;
1615 
1616                 CullDistance::Utilities::replaceAll(
1617                     shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
1618                     std::string("gl_ClipDistance[IDX] = gl_in[n_vertex_index].gl_ClipDistance[IDX]"));
1619                 CullDistance::Utilities::replaceAll(
1620                     shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
1621                     std::string("gl_CullDistance[IDX] = gl_in[n_vertex_index].gl_CullDistance[IDX]"));
1622 
1623                 switch (primitive_mode)
1624                 {
1625                 case PRIMITIVE_MODE_LINES:
1626                 {
1627                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_IN"),
1628                                                         std::string("layout(lines)                        in;"));
1629                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_OUT"),
1630                                                         std::string("layout(line_strip, max_vertices = 2) out;"));
1631 
1632                     break;
1633                 }
1634                 case PRIMITIVE_MODE_POINTS:
1635                 {
1636                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_IN"),
1637                                                         std::string("layout(points)                   in;"));
1638                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_OUT"),
1639                                                         std::string("layout(points, max_vertices = 1) out;"));
1640 
1641                     break;
1642                 }
1643                 case PRIMITIVE_MODE_TRIANGLES:
1644                 {
1645                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_IN"),
1646                                                         std::string("layout(triangles)                        in;"));
1647                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_OUT"),
1648                                                         std::string("layout(triangle_strip, max_vertices = 3) out;"));
1649 
1650                     break;
1651                 }
1652                 default:
1653                     TCU_FAIL("Unknown primitive mode");
1654                 }
1655 
1656                 break;
1657             }
1658 
1659             case GL_TESS_CONTROL_SHADER:
1660             {
1661                 shader_body_string_tc = &shaders_configuration[n_shader_index].body;
1662 
1663                 CullDistance::Utilities::replaceAll(
1664                     shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
1665                     std::string(
1666                         "gl_out[gl_InvocationID].gl_ClipDistance[IDX] = gl_in[gl_InvocationID].gl_ClipDistance[IDX]"));
1667                 CullDistance::Utilities::replaceAll(
1668                     shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
1669                     std::string(
1670                         "gl_out[gl_InvocationID].gl_CullDistance[IDX] = gl_in[gl_InvocationID].gl_CullDistance[IDX]"));
1671 
1672                 switch (primitive_mode)
1673                 {
1674                 case PRIMITIVE_MODE_LINES:
1675                 {
1676                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_OUT"),
1677                                                         std::string("layout(vertices = 2) out;"));
1678 
1679                     break;
1680                 }
1681                 case PRIMITIVE_MODE_POINTS:
1682                 {
1683                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_OUT"),
1684                                                         std::string("layout(vertices = 1) out;"));
1685 
1686                     break;
1687                 }
1688                 case PRIMITIVE_MODE_TRIANGLES:
1689                 {
1690                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_OUT"),
1691                                                         std::string("layout(vertices = 3) out;"));
1692 
1693                     break;
1694                 }
1695                 default:
1696                     TCU_FAIL("Unknown primitive mode");
1697                 }
1698 
1699                 break;
1700             }
1701 
1702             case GL_TESS_EVALUATION_SHADER:
1703             {
1704                 shader_body_string_te = &shaders_configuration[n_shader_index].body;
1705 
1706                 switch (primitive_mode)
1707                 {
1708                 case PRIMITIVE_MODE_LINES:
1709                 {
1710                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_IN"),
1711                                                         std::string("layout(isolines) in;"));
1712                     CullDistance::Utilities::replaceAll(
1713                         shader_source, std::string("TEMPLATE_OUT_FORMULA"),
1714                         std::string("mix(gl_in[0].gl_Position, gl_in[1].gl_Position, gl_TessCoord.x)"));
1715                     CullDistance::Utilities::replaceAll(
1716                         shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
1717                         std::string("gl_ClipDistance[IDX] = mix(gl_in[0].gl_ClipDistance[IDX], "
1718                                     "gl_in[1].gl_ClipDistance[IDX], gl_TessCoord.x)"));
1719                     CullDistance::Utilities::replaceAll(
1720                         shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
1721                         std::string("gl_CullDistance[IDX] = mix(gl_in[0].gl_CullDistance[IDX], "
1722                                     "gl_in[1].gl_CullDistance[IDX], gl_TessCoord.x)"));
1723 
1724                     break;
1725                 }
1726                 case PRIMITIVE_MODE_POINTS:
1727                 {
1728                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_IN"),
1729                                                         std::string("layout(isolines, point_mode) in;"));
1730                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_OUT_FORMULA"),
1731                                                         std::string("gl_in[0].gl_Position"));
1732                     CullDistance::Utilities::replaceAll(
1733                         shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
1734                         std::string("gl_ClipDistance[IDX] = gl_in[0].gl_ClipDistance[IDX]"));
1735                     CullDistance::Utilities::replaceAll(
1736                         shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
1737                         std::string("gl_CullDistance[IDX] = gl_in[0].gl_CullDistance[IDX]"));
1738 
1739                     break;
1740                 }
1741                 case PRIMITIVE_MODE_TRIANGLES:
1742                 {
1743                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_LAYOUT_IN"),
1744                                                         std::string("layout(triangles) in;"));
1745                     CullDistance::Utilities::replaceAll(
1746                         shader_source, std::string("TEMPLATE_OUT_FORMULA"),
1747                         std::string("vec4(mat3(gl_in[0].gl_Position.xyz, gl_in[1].gl_Position.xyz, "
1748                                     "gl_in[2].gl_Position.xyz) * gl_TessCoord, 1.0)"));
1749                     CullDistance::Utilities::replaceAll(
1750                         shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
1751                         std::string("gl_ClipDistance[IDX] = dot(vec3(gl_in[0].gl_ClipDistance[IDX], "
1752                                     "gl_in[1].gl_ClipDistance[IDX], gl_in[2].gl_ClipDistance[IDX]), gl_TessCoord)"));
1753                     CullDistance::Utilities::replaceAll(
1754                         shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
1755                         std::string("gl_CullDistance[IDX] = dot(vec3(gl_in[0].gl_CullDistance[IDX], "
1756                                     "gl_in[1].gl_CullDistance[IDX], gl_in[2].gl_CullDistance[IDX]), gl_TessCoord)"));
1757 
1758                     break;
1759                 }
1760                 default:
1761                     TCU_FAIL("Unknown primitive mode");
1762                 }
1763 
1764                 break;
1765             }
1766 
1767             case GL_VERTEX_SHADER:
1768             {
1769                 shader_body_string_vs = &shaders_configuration[n_shader_index].body;
1770 
1771                 if (m_isContextES)
1772                 {
1773                     std::ostringstream array_setter_str;
1774                     {
1775                         std::ostringstream sstr;
1776                         for (glw::GLuint n_clipdist = 0; n_clipdist < clipdistances_array_size; n_clipdist++)
1777                         {
1778                             sstr << "in float clipdistance_data" << n_clipdist << ";\n";
1779                             array_setter_str << "gl_ClipDistance[" << n_clipdist << "] = clipdistance_data"
1780                                              << n_clipdist << ";\n";
1781                         }
1782                         CullDistance::Utilities::replaceAll(
1783                             shader_source, std::string("TEMPLATE_CLIPDISTANCE_ATTRIB_DECLARATIONS"), sstr.str());
1784                     }
1785 
1786                     {
1787                         std::ostringstream sstr;
1788                         for (glw::GLuint n_culldist = 0; n_culldist < culldistances_array_size; n_culldist++)
1789                         {
1790                             sstr << "in float culldistance_data" << n_culldist << ";\n";
1791                             array_setter_str << "gl_CullDistance[" << n_culldist << "] = culldistance_data"
1792                                              << n_culldist << ";\n";
1793                         }
1794                         CullDistance::Utilities::replaceAll(
1795                             shader_source, std::string("TEMPLATE_CULLDISTANCE_ATTRIB_DECLARATIONS"), sstr.str());
1796                     }
1797 
1798                     // For OpenGL ES test path input array type is not allowed,
1799                     // thus ASSIGN_CLIP_DISTANCE and ASSIGN_CULL_DISTANCE macros will not be used,
1800                     // template replaced to avoid compilation errors.
1801                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
1802                                                         std::string("gl_ClipDistance[IDX] = 0.0"));
1803                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
1804                                                         std::string("gl_CullDistance[IDX] = 0.0"));
1805                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ARRAY_SETTERS"),
1806                                                         array_setter_str.str());
1807                 }
1808                 else
1809                 {
1810                     CullDistance::Utilities::replaceAll(
1811                         shader_source, std::string("TEMPLATE_CLIPDISTANCE_ATTRIB_DECLARATIONS"),
1812                         "in float clipdistance_data[" + CullDistance::Utilities::intToString(clipdistances_input_size) +
1813                             "];\n");
1814 
1815                     CullDistance::Utilities::replaceAll(
1816                         shader_source, std::string("TEMPLATE_CULLDISTANCE_ATTRIB_DECLARATIONS"),
1817                         "in float culldistance_data[" + CullDistance::Utilities::intToString(culldistances_input_size) +
1818                             "];\n");
1819 
1820                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ASSIGN_CLIP_DISTANCE"),
1821                                                         std::string("gl_ClipDistance[IDX] = clipdistance_data[IDX]"));
1822                     CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ASSIGN_CULL_DISTANCE"),
1823                                                         std::string("gl_CullDistance[IDX] = culldistance_data[IDX]"));
1824                 }
1825 
1826                 break;
1827             }
1828 
1829             default:
1830                 TCU_FAIL("Unknown shader type");
1831             }
1832 
1833             /* Adjust clipdistances declaration */
1834             if (redeclare_clipdistances && clipdistances_array_size > 0)
1835             {
1836                 if (shaders_configuration[n_shader_index].type == GL_FRAGMENT_SHADER)
1837                 {
1838                     if (fetch_culldistance_from_fs)
1839                     {
1840                         clipdistance_array_declaration =
1841                             std::string("in float gl_ClipDistance[") +
1842                             CullDistance::Utilities::intToString(clipdistances_array_size) + std::string("];");
1843                     }
1844                 }
1845                 else if (shaders_configuration[n_shader_index].type == GL_TESS_CONTROL_SHADER)
1846                 {
1847                     clipdistance_array_declaration = std::string("float gl_ClipDistance[") +
1848                                                      CullDistance::Utilities::intToString(clipdistances_array_size) +
1849                                                      std::string("];");
1850                 }
1851                 else
1852                 {
1853                     clipdistance_array_declaration = std::string("out float gl_ClipDistance[") +
1854                                                      CullDistance::Utilities::intToString(clipdistances_array_size) +
1855                                                      std::string("];");
1856                     clipdistance_in_array_declaration = std::string("in float gl_ClipDistance[") +
1857                                                         CullDistance::Utilities::intToString(clipdistances_array_size) +
1858                                                         std::string("];");
1859                 }
1860             }
1861             CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_REDECLARE_CLIPDISTANCE"),
1862                                                 clipdistance_array_declaration);
1863             CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_REDECLARE_IN_CLIPDISTANCE"),
1864                                                 clipdistance_in_array_declaration);
1865 
1866             /* Adjust culldistances declaration */
1867             if (redeclare_culldistances && culldistances_array_size > 0)
1868             {
1869                 if (shaders_configuration[n_shader_index].type == GL_FRAGMENT_SHADER)
1870                 {
1871                     if (fetch_culldistance_from_fs)
1872                     {
1873                         culldistance_array_declaration =
1874                             std::string("in float gl_CullDistance[") +
1875                             CullDistance::Utilities::intToString(culldistances_array_size) + std::string("];");
1876                     }
1877                 }
1878                 else if (shaders_configuration[n_shader_index].type == GL_TESS_CONTROL_SHADER)
1879                 {
1880                     culldistance_array_declaration = std::string("float gl_CullDistance[") +
1881                                                      CullDistance::Utilities::intToString(culldistances_array_size) +
1882                                                      std::string("];");
1883                 }
1884                 else
1885                 {
1886                     culldistance_array_declaration = std::string("out float gl_CullDistance[") +
1887                                                      CullDistance::Utilities::intToString(culldistances_array_size) +
1888                                                      std::string("];");
1889                     culldistance_in_array_declaration = std::string("in float gl_CullDistance[") +
1890                                                         CullDistance::Utilities::intToString(culldistances_array_size) +
1891                                                         std::string("];");
1892                 }
1893             }
1894             CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_REDECLARE_CULLDISTANCE"),
1895                                                 culldistance_array_declaration);
1896             CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_REDECLARE_IN_CULLDISTANCE"),
1897                                                 culldistance_in_array_declaration);
1898 
1899             /* Adjust clip/cull distances setters */
1900             if (dynamic_index_writes)
1901             {
1902                 array_setters = dynamic_array_setters;
1903 
1904                 CullDistance::Utilities::replaceAll(array_setters, std::string("TEMPLATE_N_GL_CLIPDISTANCE_ENTRIES"),
1905                                                     CullDistance::Utilities::intToString(clipdistances_array_size));
1906                 CullDistance::Utilities::replaceAll(array_setters, std::string("TEMPLATE_N_GL_CULLDISTANCE_ENTRIES"),
1907                                                     CullDistance::Utilities::intToString(culldistances_array_size));
1908             }
1909             else
1910             {
1911                 std::stringstream static_array_setters_sstream;
1912 
1913                 static_array_setters_sstream << "\n";
1914 
1915                 for (glw::GLuint clipdistances_array_entry = 0; clipdistances_array_entry < clipdistances_array_size;
1916                      ++clipdistances_array_entry)
1917                 {
1918                     static_array_setters_sstream << "        ASSIGN_CLIP_DISTANCE(" << clipdistances_array_entry
1919                                                  << ");\n";
1920                 }
1921 
1922                 static_array_setters_sstream << "\n";
1923 
1924                 for (glw::GLuint culldistances_array_entry = 0; culldistances_array_entry < culldistances_array_size;
1925                      ++culldistances_array_entry)
1926                 {
1927                     static_array_setters_sstream << "        ASSIGN_CULL_DISTANCE(" << culldistances_array_entry
1928                                                  << ");\n";
1929                 }
1930 
1931                 array_setters = static_array_setters_sstream.str();
1932             }
1933 
1934             CullDistance::Utilities::replaceAll(shader_source, std::string("TEMPLATE_ARRAY_SETTERS"), array_setters);
1935         }
1936     }
1937 
1938     /* Build the geometry shader */
1939     CullDistance::Utilities::buildProgram(
1940         m_context.getRenderContext().getFunctions(), m_testCtx, nullptr, /* Compute shader                    */
1941         shader_body_string_fs != nullptr ? shader_body_string_fs->c_str() :
1942                                            nullptr, /* Fragment shader                   */
1943         shader_body_string_gs != nullptr ? shader_body_string_gs->c_str() :
1944                                            nullptr, /* Geometry shader                   */
1945         shader_body_string_tc != nullptr ? shader_body_string_tc->c_str() :
1946                                            nullptr, /* Tesselation control shader        */
1947         shader_body_string_te != nullptr ? shader_body_string_te->c_str() :
1948                                            nullptr, /* Tesselation evaluation shader     */
1949         shader_body_string_vs != nullptr ? shader_body_string_vs->c_str() :
1950                                            nullptr, /* Vertex shader                     */
1951         0,                                          /* Transform feedback varyings count */
1952         nullptr,                                    /* Transform feedback varyings       */
1953         &m_po_id                                    /* Program object id                 */
1954     );
1955 }
1956 
1957 /** Generates primitive data required to test a case with specified
1958  *  gl_ClipDistance and glCullDistance array sizes for specified
1959  *  primitive mode. Generated primitive data is stored in m_bo_data
1960  *  as well uploaded into buffer specified in m_bo_id buffer.
1961  *  Also the procedure binds vertex attribute locations to
1962  *  program object m_po_id.
1963  *
1964  *  @param clipdistances_array_size gl_ClipDistance array size. Can be 0.
1965  *  @param culldistances_array_size gl_CullDistance array size. Can be 0.
1966  *  @param _primitive_mode          Primitives to be generated. Can be:
1967  *                                  PRIMITIVE_MODE_POINTS,
1968  *                                  PRIMITIVE_MODE_LINES,
1969  *                                  PRIMITIVE_MODE_TRIANGLES.
1970  */
configureVAO(glw::GLuint clipdistances_array_size,glw::GLuint culldistances_array_size,_primitive_mode primitive_mode)1971 void CullDistance::FunctionalTest::configureVAO(glw::GLuint clipdistances_array_size,
1972                                                 glw::GLuint culldistances_array_size, _primitive_mode primitive_mode)
1973 {
1974     /* Detailed test description.
1975      *
1976      * configureVAO() generates primitives layouted in grid. Primitve
1977      * consists of up to 3 vertices and each vertex is accompanied by:
1978      * - array of clipdistances (clipdistances_array_size floats);
1979      * - array of culldistances (culldistances_array_size floats);
1980      * - rendering position coordinates (x and y);
1981      * - check position coordinates (x and y).
1982      *
1983      * The grid has following layout:
1984      *
1985      *     Grid                       |         gl_CullDistance[x]         |
1986      *                                |  0 .. culldistances_array_size - 1 |
1987      *                                |  0th  |  1st  |  2nd  | .......... |
1988      *     ---------------------------+-------+-------+-------+------------+
1989      *     0th  gl_ClipDistance       |Subgrid|Subgrid|Subgrid| .......... |
1990      *     1st  gl_ClipDistance       |Subgrid|Subgrid|Subgrid| .......... |
1991      *     ...                        |  ...  |  ...  |  ...  | .......... |
1992      *     y-th gl_ClipDistance       |Subgrid|Subgrid|Subgrid| .......... |
1993      *     ...                        |  ...  |  ...  |  ...  | .......... |
1994      *     clipdistances_array_size-1 |Subgrid|Subgrid|Subgrid| .......... |
1995      *
1996      * Each grid cell contains subgrid of 3*3 items in size with following
1997      * structure:
1998      *
1999      *     Subgrid        |        x-th gl_CullDistance test           |
2000      *                    |                                            |
2001      *     y-th           | all vertices | 0th vertex   | all vertices |
2002      *     gl_ClipDistance| in primitive | in primitive | in primitive |
2003      *     tests          | dist[x] > 0  | dist[x] < 0  | dist[x] < 0  |
2004      *     ---------------+--------------+--------------+--------------+
2005      *        all vertices| primitive #0 | primitive #1 | primitive #2 |
2006      *        in primitive|              |              |              |
2007      *        dist[y] > 0 |   visible    |   visible    |    culled    |
2008      *     ---------------+--------------+--------------+--------------+
2009      *        0th vertex  | primitive #3 | primitive #4 | primitive #5 |
2010      *        in primitive|  0th vertex  |  0th vertex  |              |
2011      *        dist[y] < 0 |   clipped    |   clipped    |    culled    |
2012      *     ---------------+--------------+--------------+--------------+
2013      *        all vertices| primitive #6 | primitive #7 | primitive #8 |
2014      *        in primitive|              |              |              |
2015      *        dist[y] < 0 |   clipped    |   clipped    |    culled    |
2016      *     ---------------+--------------+--------------+--------------+
2017      *
2018      * Expected rendering result is specified in cell bottom.
2019      * It can be one of the following:
2020      * - "visible" means the primitive is not affected neither by gl_CullDistance
2021      *             nor by gl_ClipDistance and rendered as a whole;
2022      * - "clipped" for the vertex means the vertex is not rendered, while other
2023      *             primitive vertices and some filling fragments are rendered;
2024      * - "clipped" for primitive means none of primitive vertices and fragments
2025      *             are rendered and thus primitive is not rendered and is invisible;
2026      * - "culled"  means, that neither primitive vertices, nor primitive filling
2027      *             fragments are rendered (primitive is invisible).
2028      *
2029      * All subgrid items contain same primitive rendered. Depending on
2030      * test case running it would be either triangle, or line, or point:
2031      *
2032      *     triangle    line        point
2033      *     8x8 box     8x8 box     3x3 box
2034      *     ........    ........    ...
2035      *     .0----2.    .0......    .0.
2036      *     ..\@@@|.    ..\.....    ...
2037      *     ...\@@|.    ...\....
2038      *     ....\@|.    ....\...
2039      *     .....\|.    .....\..
2040      *     ......1.    ......1.
2041      *     ........    ........
2042      *
2043      *     where 0 - is a 0th vertex primitive
2044      *           1 - is a 1st vertex primitive
2045      *           2 - is a 2nd vertex primitive
2046      *
2047      * The culldistances_array_size can be 0. In that case, grid height
2048      * is assumed equal to 1, but 0 glCullDistances is specified.
2049      * Similar handled clipdistances_array_size.
2050      *
2051      * The data generated is used and checked in executeRenderTest().
2052      * After rendering each primitive vertex is tested:
2053      * - if it is rendered, if it have to be rendered (according distance);
2054      * - if it is not rendered, if it have to be not rendered (according distance).
2055      * Due to "top-left" rasterization rule check position is
2056      * different from rendering vertex position.
2057      *
2058      * Also one pixel width guarding box is checked to be clear.
2059      */
2060 
2061     const glw::Functions &gl           = m_context.getRenderContext().getFunctions();
2062     const glw::GLuint n_sub_grid_cells = 3; /* Tested distance is positive for all vertices in the primitive;
2063                                              * Tested distance is negative for 0th vertex in the primitive;
2064                                              * Tested distance is negative for all vertices in the primitive;
2065                                              */
2066     const glw::GLuint sub_grid_cell_size = ((primitive_mode == PRIMITIVE_MODE_LINES)  ? 8 :
2067                                             (primitive_mode == PRIMITIVE_MODE_POINTS) ? 3 :
2068                                                                                         8);
2069 
2070     const glw::GLuint grid_cell_size       = n_sub_grid_cells * sub_grid_cell_size;
2071     const glw::GLuint n_primitive_vertices = ((primitive_mode == PRIMITIVE_MODE_LINES)  ? 2 :
2072                                               (primitive_mode == PRIMITIVE_MODE_POINTS) ? 1 :
2073                                                                                           3);
2074 
2075     const glw::GLuint n_grid_cells_x               = culldistances_array_size != 0 ? culldistances_array_size : 1;
2076     const glw::GLuint n_grid_cells_y               = clipdistances_array_size != 0 ? clipdistances_array_size : 1;
2077     const glw::GLuint n_pervertex_float_attributes = clipdistances_array_size + culldistances_array_size +
2078                                                      2 /* vertex' draw x, y */ + 2 /* vertex' checkpoint x, y */;
2079     const glw::GLuint n_primitives_total     = n_grid_cells_x * n_sub_grid_cells * n_grid_cells_y * n_sub_grid_cells;
2080     const glw::GLuint n_vertices_total       = n_primitives_total * n_primitive_vertices;
2081     const glw::GLuint offsets_line_draw_x[2] = {
2082         1, sub_grid_cell_size - 1}; /* vertex x offsets to subgrid cell origin for line primitive     */
2083     const glw::GLuint offsets_line_draw_y[2] = {
2084         1, sub_grid_cell_size - 1}; /* vertex y offsets to subgrid cell origin for line primitive     */
2085     const glw::GLuint offsets_line_checkpoint_x[2] = {
2086         1, sub_grid_cell_size - 2}; /* pixel x offsets to subgrid cell origin for line primitive      */
2087     const glw::GLuint offsets_line_checkpoint_y[2] = {
2088         1, sub_grid_cell_size - 2}; /* pixel y offsets to subgrid cell origin for line primitive      */
2089     const glw::GLuint offsets_point_draw_x[1] = {
2090         1}; /* vertex x offsets to subgrid cell origin for point primitive    */
2091     const glw::GLuint offsets_point_draw_y[1] = {
2092         1}; /* vertex y offsets to subgrid cell origin for point primitive    */
2093     const glw::GLuint offsets_point_checkpoint_x[1] = {
2094         1}; /* pixel x offsets to subgrid cell origin for point primitive     */
2095     const glw::GLuint offsets_point_checkpoint_y[1] = {
2096         1}; /* pixel y offsets to subgrid cell origin for point primitive     */
2097     const glw::GLuint offsets_triangle_draw_x[3] = {
2098         1, sub_grid_cell_size - 1,
2099         sub_grid_cell_size - 1}; /* vertex x offsets to subgrid cell origin for triangle primitive */
2100     const glw::GLuint offsets_triangle_draw_y[3] = {
2101         1, sub_grid_cell_size - 1, 1}; /* vertex y offsets to subgrid cell origin for triangle primitive */
2102     const glw::GLuint offsets_triangle_checkpoint_x[3] = {
2103         1, sub_grid_cell_size - 2,
2104         sub_grid_cell_size - 2}; /* pixel x offsets to subgrid cell origin for triangle primitive  */
2105     const glw::GLuint offsets_triangle_checkpoint_y[3] = {
2106         1, sub_grid_cell_size - 2, 1}; /* pixel y offsets to subgrid cell origin for triangle primitive  */
2107     const glw::GLfloat offsets_pixel_center_x = (primitive_mode == PRIMITIVE_MODE_POINTS) ? 0.5f : 0;
2108     const glw::GLfloat offsets_pixel_center_y = (primitive_mode == PRIMITIVE_MODE_POINTS) ? 0.5f : 0;
2109     /* Clear data left from previous tests. */
2110     m_bo_data.clear();
2111 
2112     /* No data to render */
2113     m_render_primitives = 0;
2114     m_render_vertices   = 0;
2115 
2116     /* Preallocate space for bo_points_count */
2117     m_bo_data.reserve(n_vertices_total * n_pervertex_float_attributes);
2118 
2119     /* Generate test data for cell_y-th clip distance */
2120     for (glw::GLuint cell_y = 0; cell_y < n_grid_cells_y; cell_y++)
2121     {
2122         /* Generate test data for cell_x-th cull distance */
2123         for (glw::GLuint cell_x = 0; cell_x < n_grid_cells_x; cell_x++)
2124         {
2125             /* Check clip distance sub cases:
2126              * 0. Tested distance is positive for all vertices in the primitive;
2127              * 1. Tested distance is negative for 0th vertex in the primitive;
2128              * 2. Tested distance is negative for all vertices in the primitive;
2129              */
2130             for (glw::GLuint n_sub_cell_y = 0; n_sub_cell_y < n_sub_grid_cells; n_sub_cell_y++)
2131             {
2132                 /* Check cull distance sub cases:
2133                  * 0. Tested distance is positive for all vertices in the primitive;
2134                  * 1. Tested distance is negative for 0th vertex in the primitive;
2135                  * 2. Tested distance is negative for all vertices in the primitive;
2136                  */
2137                 for (glw::GLuint n_sub_cell_x = 0; n_sub_cell_x < n_sub_grid_cells; n_sub_cell_x++)
2138                 {
2139                     /* Generate vertices in primitive */
2140                     for (glw::GLuint n_primitive_vertex = 0; n_primitive_vertex < n_primitive_vertices;
2141                          n_primitive_vertex++)
2142                     {
2143                         /* Fill in clipdistance array for the n_primitive_vertex vertex in primitive */
2144                         for (glw::GLuint n_clipdistance_entry = 0; n_clipdistance_entry < clipdistances_array_size;
2145                              n_clipdistance_entry++)
2146                         {
2147                             glw::GLfloat distance_value = 0.0f;
2148                             bool negative               = true;
2149 
2150                             /* Special approach to tested clipdistance entry. */
2151                             if (n_clipdistance_entry == cell_y)
2152                             {
2153                                 /* The primitive vertex should be affected by the clip distance */
2154                                 switch (n_sub_cell_y)
2155                                 {
2156                                 case 0:
2157                                 {
2158                                     /* subgrid row 0: all primitive vertices have tested distance value positive */
2159                                     negative = false;
2160 
2161                                     break;
2162                                 }
2163                                 case 1:
2164                                 {
2165                                     /* subgrid row 1: tested distance value for 0th primitive vertex is negative,
2166                                      all other primitive vertices have tested distance value positive */
2167                                     negative = (n_primitive_vertex == 0) ? true : false;
2168 
2169                                     break;
2170                                 }
2171                                 case 2:
2172                                 {
2173                                     /* subgrid row 2: tested distance value is negative for all primitive vertices */
2174                                     negative = true;
2175 
2176                                     break;
2177                                 }
2178                                 default:
2179                                     TCU_FAIL("Invalid subgrid cell index");
2180                                 }
2181 
2182                                 distance_value = (negative ? -1.0f : 1.0f) * glw::GLfloat(n_clipdistance_entry + 1);
2183                             }
2184                             else
2185                             {
2186                                 /* For clip distances other than tested: assign positive value to avoid its influence. */
2187                                 distance_value = glw::GLfloat(clipdistances_array_size + n_clipdistance_entry + 1);
2188                             }
2189 
2190                             m_bo_data.push_back(distance_value / glw::GLfloat(clipdistances_array_size));
2191                         } /* for (all gl_ClipDistance[] array values) */
2192 
2193                         /* Fill in culldistance array for the n_primitive_vertex vertex in primitive */
2194                         for (glw::GLuint n_culldistance_entry = 0; n_culldistance_entry < culldistances_array_size;
2195                              n_culldistance_entry++)
2196                         {
2197                             glw::GLfloat distance_value = 0.0f;
2198                             bool negative               = true;
2199 
2200                             /* Special approach to tested culldistance entry. */
2201                             if (n_culldistance_entry == cell_x)
2202                             {
2203                                 /* The primitive vertex should be affected by the cull distance */
2204                                 switch (n_sub_cell_x)
2205                                 {
2206                                 case 0:
2207                                 {
2208                                     /* subgrid column 0: all primitive vertices have tested distance value positive */
2209                                     negative = false;
2210 
2211                                     break;
2212                                 }
2213                                 case 1:
2214                                 {
2215                                     /* subgrid column 1: tested distance value for 0th primitive vertex is negative,
2216                                      all other primitive vertices have tested distance value positive */
2217                                     negative = (n_primitive_vertex == 0) ? true : false;
2218 
2219                                     break;
2220                                 }
2221                                 case 2:
2222                                 {
2223                                     /* subgrid column 2: tested distance value is negative for all primitive vertices */
2224                                     negative = true;
2225 
2226                                     break;
2227                                 }
2228                                 default:
2229                                     TCU_FAIL("Invalid subgrid cell index");
2230                                 }
2231 
2232                                 distance_value = (negative ? -1.0f : 1.0f) * glw::GLfloat(n_culldistance_entry + 1);
2233                             }
2234                             else
2235                             {
2236                                 /* For cull distances other than tested: assign 0th vertex negative value,
2237                                  to check absence of between-distances influence. */
2238                                 if (n_primitive_vertices > 1 && n_primitive_vertex == 0)
2239                                 {
2240                                     distance_value = -glw::GLfloat(culldistances_array_size + n_culldistance_entry + 1);
2241                                 }
2242                                 else
2243                                 {
2244                                     /* This culldistance is out of interest: assign positive value. */
2245                                     distance_value = glw::GLfloat(culldistances_array_size + n_culldistance_entry + 1);
2246                                 }
2247                             }
2248 
2249                             m_bo_data.push_back(distance_value / glw::GLfloat(culldistances_array_size));
2250                         } /* for (all gl_CullDistance[] array values) */
2251 
2252                         /* Generate primitve vertex draw and checkpoint coordinates */
2253                         glw::GLint vertex_draw_pixel_offset_x       = 0;
2254                         glw::GLint vertex_draw_pixel_offset_y       = 0;
2255                         glw::GLint vertex_checkpoint_pixel_offset_x = 0;
2256                         glw::GLint vertex_checkpoint_pixel_offset_y = 0;
2257 
2258                         switch (primitive_mode)
2259                         {
2260                         case PRIMITIVE_MODE_LINES:
2261                         {
2262                             vertex_draw_pixel_offset_x       = offsets_line_draw_x[n_primitive_vertex];
2263                             vertex_draw_pixel_offset_y       = offsets_line_draw_y[n_primitive_vertex];
2264                             vertex_checkpoint_pixel_offset_x = offsets_line_checkpoint_x[n_primitive_vertex];
2265                             vertex_checkpoint_pixel_offset_y = offsets_line_checkpoint_y[n_primitive_vertex];
2266 
2267                             break;
2268                         }
2269 
2270                         case PRIMITIVE_MODE_POINTS:
2271                         {
2272                             vertex_draw_pixel_offset_x       = offsets_point_draw_x[n_primitive_vertex];
2273                             vertex_draw_pixel_offset_y       = offsets_point_draw_y[n_primitive_vertex];
2274                             vertex_checkpoint_pixel_offset_x = offsets_point_checkpoint_x[n_primitive_vertex];
2275                             vertex_checkpoint_pixel_offset_y = offsets_point_checkpoint_y[n_primitive_vertex];
2276 
2277                             break;
2278                         }
2279 
2280                         case PRIMITIVE_MODE_TRIANGLES:
2281                         {
2282                             vertex_draw_pixel_offset_x       = offsets_triangle_draw_x[n_primitive_vertex];
2283                             vertex_draw_pixel_offset_y       = offsets_triangle_draw_y[n_primitive_vertex];
2284                             vertex_checkpoint_pixel_offset_x = offsets_triangle_checkpoint_x[n_primitive_vertex];
2285                             vertex_checkpoint_pixel_offset_y = offsets_triangle_checkpoint_y[n_primitive_vertex];
2286 
2287                             break;
2288                         }
2289 
2290                         default:
2291                             TCU_FAIL("Unknown primitive mode");
2292                         }
2293 
2294                         /* Origin of sub_cell */
2295                         glw::GLint sub_cell_origin_x = cell_x * grid_cell_size + n_sub_cell_x * sub_grid_cell_size;
2296                         glw::GLint sub_cell_origin_y = cell_y * grid_cell_size + n_sub_cell_y * sub_grid_cell_size;
2297                         /* Normalized texture coordinates of vertex draw position. */
2298                         glw::GLfloat x =
2299                             (glw::GLfloat(sub_cell_origin_x + vertex_draw_pixel_offset_x) + offsets_pixel_center_x) /
2300                             glw::GLfloat(m_to_width);
2301                         glw::GLfloat y =
2302                             (glw::GLfloat(sub_cell_origin_y + vertex_draw_pixel_offset_y) + offsets_pixel_center_y) /
2303                             glw::GLfloat(m_to_height);
2304                         /* Normalized texture coordinates of vertex checkpoint position. */
2305                         glw::GLfloat checkpoint_x = glw::GLfloat(sub_cell_origin_x + vertex_checkpoint_pixel_offset_x) /
2306                                                     glw::GLfloat(m_to_width);
2307                         glw::GLfloat checkpoint_y = glw::GLfloat(sub_cell_origin_y + vertex_checkpoint_pixel_offset_y) /
2308                                                     glw::GLfloat(m_to_height);
2309 
2310                         /* Add vertex draw coordinates into buffer. */
2311                         m_bo_data.push_back(x);
2312                         m_bo_data.push_back(y);
2313 
2314                         /* Add vertex checkpoint coordinates into buffer. */
2315                         m_bo_data.push_back(checkpoint_x);
2316                         m_bo_data.push_back(checkpoint_y);
2317                     } /* for (all vertices in primitive) */
2318                 }     /* for (all horizontal sub cells) */
2319             }         /* for (all vertical sub cells) */
2320         }             /* for (all horizontal cells) */
2321     }                 /* for (all vertical cells) */
2322 
2323     /* Quick check: make sure we pushed required amount of data */
2324     DE_ASSERT(m_bo_data.size() == n_vertices_total * n_pervertex_float_attributes);
2325 
2326     /* Save number of primitives to render */
2327     m_render_primitives  = n_primitives_total;
2328     m_render_vertices    = n_vertices_total;
2329     m_sub_grid_cell_size = sub_grid_cell_size;
2330 
2331     /* Copy the data to the buffer object */
2332     gl.bindBuffer(GL_ARRAY_BUFFER, m_bo_id);
2333     GLU_EXPECT_NO_ERROR(gl.getError(), "glBindBuffer() call failed.");
2334 
2335     gl.bufferData(GL_ARRAY_BUFFER, m_bo_data.size() * sizeof(glw::GLfloat), &m_bo_data[0], GL_STATIC_DRAW);
2336     GLU_EXPECT_NO_ERROR(gl.getError(), "glBufferData() call failed.");
2337 
2338     DE_ASSERT(m_po_id != 0);
2339 
2340     /* Bind VAO data to program */
2341     glw::GLint po_position_location = -1;
2342 
2343     /* Retrieve clipdistance and culldistance attribute locations */
2344     gl.bindVertexArray(m_vao_id);
2345     GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed.");
2346 
2347     po_position_location = gl.getAttribLocation(m_po_id, "position");
2348 
2349     GLU_EXPECT_NO_ERROR(gl.getError(), "glGetAttribLocation() call(s) failed.");
2350 
2351     DE_ASSERT(po_position_location != -1);
2352 
2353     glw::GLintptr current_offset = 0;
2354     const glw::GLint stride      = static_cast<glw::GLint>(n_pervertex_float_attributes * sizeof(glw::GLfloat));
2355 
2356     gl.bindVertexArray(m_vao_id);
2357     GLU_EXPECT_NO_ERROR(gl.getError(), "glBindVertexArray() call failed.");
2358 
2359     std::string atrib_name;
2360     for (glw::GLuint n_clipdistance_entry = 0; n_clipdistance_entry < clipdistances_array_size; ++n_clipdistance_entry)
2361     {
2362         if (m_isContextES)
2363             atrib_name = "clipdistance_data" + CullDistance::Utilities::intToString(n_clipdistance_entry);
2364         else
2365             atrib_name = "clipdistance_data[" + CullDistance::Utilities::intToString(n_clipdistance_entry) + "]";
2366 
2367         glw::GLint po_clipdistance_array_location = gl.getAttribLocation(m_po_id, atrib_name.c_str());
2368         DE_ASSERT(po_clipdistance_array_location != -1);
2369 
2370         gl.vertexAttribPointer(po_clipdistance_array_location, 1, /* size */
2371                                GL_FLOAT, GL_FALSE,                /* normalized */
2372                                stride, (const glw::GLvoid *)current_offset);
2373         GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() call failed.");
2374 
2375         gl.enableVertexAttribArray(po_clipdistance_array_location);
2376         GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed.");
2377 
2378         current_offset += sizeof(glw::GLfloat);
2379     } /* for (all clip distance array value attributes) */
2380 
2381     for (glw::GLuint n_culldistance_entry = 0; n_culldistance_entry < culldistances_array_size; ++n_culldistance_entry)
2382     {
2383         if (m_isContextES)
2384             atrib_name = "culldistance_data" + CullDistance::Utilities::intToString(n_culldistance_entry);
2385         else
2386             atrib_name = "culldistance_data[" + CullDistance::Utilities::intToString(n_culldistance_entry) + "]";
2387 
2388         glw::GLint po_culldistance_array_location = gl.getAttribLocation(m_po_id, atrib_name.c_str());
2389 
2390         DE_ASSERT(po_culldistance_array_location != -1);
2391 
2392         gl.vertexAttribPointer(po_culldistance_array_location, 1, /* size */
2393                                GL_FLOAT, GL_FALSE,                /* normalized */
2394                                stride, (const glw::GLvoid *)current_offset);
2395         GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() call failed.");
2396 
2397         gl.enableVertexAttribArray(po_culldistance_array_location);
2398         GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed.");
2399 
2400         current_offset += sizeof(glw::GLfloat);
2401     } /* for (all cull distance array value attributes) */
2402 
2403     gl.vertexAttribPointer(po_position_location, 2, /* size */
2404                            GL_FLOAT, GL_FALSE,      /* normalized */
2405                            stride, (const glw::GLvoid *)current_offset);
2406     GLU_EXPECT_NO_ERROR(gl.getError(), "glVertexAttribPointer() call failed");
2407 
2408     gl.enableVertexAttribArray(po_position_location);
2409     GLU_EXPECT_NO_ERROR(gl.getError(), "glEnableVertexAttribArray() call failed");
2410 }
2411 
2412 /** @brief Cull Distance Functional Test deinitialization */
deinit()2413 void CullDistance::FunctionalTest::deinit()
2414 {
2415     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
2416 
2417     if (m_fbo_id != 0)
2418     {
2419         gl.deleteFramebuffers(1, &m_fbo_id);
2420 
2421         m_fbo_id = 0;
2422     }
2423 
2424     if (m_to_id != 0)
2425     {
2426         gl.deleteTextures(1, &m_to_id);
2427 
2428         m_to_id = 0;
2429     }
2430 
2431     if (m_vao_id != 0)
2432     {
2433         gl.deleteVertexArrays(1, &m_vao_id);
2434 
2435         m_vao_id = 0;
2436     }
2437 
2438     deinitPO();
2439 }
2440 
2441 /** @brief Cull Distance Functional Test deinitialization of OpenGL programs */
deinitPO()2442 void CullDistance::FunctionalTest::deinitPO()
2443 {
2444     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
2445 
2446     if (m_po_id != 0)
2447     {
2448         gl.deleteProgram(m_po_id);
2449 
2450         m_po_id = 0;
2451     }
2452 }
2453 
2454 /** @brief Executes single render test case
2455  *
2456  * @param [in]  clipdistances_array_size    Size of gl_ClipDistance[] array
2457  * @param [in]  culldistances_array_size    Size of gl_CullDistance[] array
2458  * @param [in]  primitive_mode              Type of primitives to be rendered (see enum _primitive_mode)
2459  * @param [in]  use_tesselation             Indicate whether to use tessellation shader
2460  * @param [in]  fetch_culldistance_from_fs  Indicate whether to fetch gl_CullDistance and gl_ClipDistance values from the fragment shader
2461  */
executeRenderTest(glw::GLuint clipdistances_array_size,glw::GLuint culldistances_array_size,_primitive_mode primitive_mode,bool use_tesselation,bool fetch_culldistance_from_fs)2462 void CullDistance::FunctionalTest::executeRenderTest(glw::GLuint clipdistances_array_size,
2463                                                      glw::GLuint culldistances_array_size,
2464                                                      _primitive_mode primitive_mode, bool use_tesselation,
2465                                                      bool fetch_culldistance_from_fs)
2466 {
2467     const glw::Functions &gl                   = m_context.getRenderContext().getFunctions();
2468     glw::GLenum mode                           = GL_NONE;
2469     glw::GLuint n_clipped_vertices_real        = 0;
2470     glw::GLuint n_culled_primitives_real       = 0;
2471     const glw::GLuint primitive_vertices_count = ((primitive_mode == PRIMITIVE_MODE_LINES)  ? 2 :
2472                                                   (primitive_mode == PRIMITIVE_MODE_POINTS) ? 1 :
2473                                                                                               3);
2474     const glw::GLuint stride_in_floats =
2475         clipdistances_array_size + culldistances_array_size + 2 /* position's x, y*/ + 2 /* checkpoint x,y */;
2476 
2477     // Release build does not use them
2478     DE_UNREF(n_clipped_vertices_real);
2479     DE_UNREF(n_culled_primitives_real);
2480 
2481     switch (primitive_mode)
2482     {
2483     case PRIMITIVE_MODE_LINES:
2484     {
2485         mode = GL_LINES;
2486 
2487         break;
2488     }
2489     case PRIMITIVE_MODE_POINTS:
2490     {
2491         mode = GL_POINTS;
2492 
2493         break;
2494     }
2495     case PRIMITIVE_MODE_TRIANGLES:
2496     {
2497         mode = GL_TRIANGLES;
2498 
2499         break;
2500     }
2501     default:
2502         TCU_FAIL("Unknown primitive mode");
2503     }
2504 
2505     if (use_tesselation)
2506     {
2507         mode = GL_PATCHES;
2508 
2509         gl.patchParameteri(GL_PATCH_VERTICES, primitive_vertices_count);
2510         GLU_EXPECT_NO_ERROR(gl.getError(), "glPatchParameteri() call failed.");
2511     }
2512 
2513     gl.clear(GL_COLOR_BUFFER_BIT);
2514     GLU_EXPECT_NO_ERROR(gl.getError(), "glClear() call failed.");
2515 
2516     gl.useProgram(m_po_id);
2517     GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
2518 
2519     for (glw::GLuint n_clipdistance_entry = 0; n_clipdistance_entry < clipdistances_array_size; n_clipdistance_entry++)
2520     {
2521         gl.enable(GL_CLIP_DISTANCE0 + n_clipdistance_entry);
2522         GLU_EXPECT_NO_ERROR(gl.getError(), "gl.enable(GL_CLIP_DISTANCE)() call failed.");
2523     } /* for (all clip distance array value attributes) */
2524 
2525     gl.drawArrays(mode, 0, m_render_vertices);
2526     GLU_EXPECT_NO_ERROR(gl.getError(), "glDrawArray() call(s) failed.");
2527 
2528     for (glw::GLuint n_clipdistance_entry = 0; n_clipdistance_entry < clipdistances_array_size; n_clipdistance_entry++)
2529     {
2530         gl.disable(GL_CLIP_DISTANCE0 + n_clipdistance_entry);
2531         GLU_EXPECT_NO_ERROR(gl.getError(), "gl.disable(GL_CLIP_DISTANCE)() call failed.");
2532     } /* for (all clip distance array value attributes) */
2533 
2534     gl.useProgram(0);
2535     GLU_EXPECT_NO_ERROR(gl.getError(), "glUseProgram() call failed.");
2536 
2537     /* Read generated texture into m_to_pixel_data_cache */
2538     readTexturePixels();
2539 
2540     for (glw::GLint n_primitive_index = 0; n_primitive_index < m_render_primitives; n_primitive_index++)
2541     {
2542         glw::GLuint base_index_of_primitive     = n_primitive_index * primitive_vertices_count * stride_in_floats;
2543         bool primitive_culled                   = false;
2544         glw::GLint primitive_culled_by_distance = -1;
2545 
2546         /* Check the bounding box is clear */
2547         glw::GLuint base_index_of_vertex      = base_index_of_primitive;
2548         glw::GLuint checkpoint_position_index = base_index_of_vertex + clipdistances_array_size +
2549                                                 culldistances_array_size + 2 /* ignore vertex coordinates */;
2550         glw::GLint checkpoint_x = glw::GLint(glw::GLfloat(m_to_width) * m_bo_data[checkpoint_position_index]);
2551         glw::GLint checkpoint_y = glw::GLint(glw::GLfloat(m_to_height) * m_bo_data[checkpoint_position_index + 1]);
2552         glw::GLint origin_x     = checkpoint_x - 1;
2553         glw::GLint origin_y     = checkpoint_y - 1;
2554         for (glw::GLint pixel_offset = 0; pixel_offset < m_sub_grid_cell_size; pixel_offset++)
2555         {
2556             if (readRedPixelValue(origin_x + pixel_offset, origin_y) != 0)
2557             {
2558                 TCU_FAIL("Top edge of bounding box is overwritten");
2559             }
2560 
2561             if (readRedPixelValue(origin_x + m_sub_grid_cell_size - 1, origin_y + pixel_offset) != 0)
2562             {
2563                 TCU_FAIL("Right edge of bounding box is overwritten");
2564             }
2565 
2566             if (readRedPixelValue(origin_x + m_sub_grid_cell_size - 1 - pixel_offset,
2567                                   origin_y + m_sub_grid_cell_size - 1) != 0)
2568             {
2569                 TCU_FAIL("Bottom edge of bounding box is overwritten");
2570             }
2571 
2572             if (readRedPixelValue(origin_x, origin_y + m_sub_grid_cell_size - 1 - pixel_offset) != 0)
2573             {
2574                 TCU_FAIL("Left edge of bounding box is overwritten");
2575             }
2576         }
2577 
2578         /* Determine if primitive has been culled */
2579         for (glw::GLuint n_culldistance_entry = 0; n_culldistance_entry < culldistances_array_size;
2580              n_culldistance_entry++)
2581         {
2582             bool distance_negative_in_all_primitive_vertices = true;
2583 
2584             for (glw::GLuint n_primitive_vertex = 0; n_primitive_vertex < primitive_vertices_count;
2585                  n_primitive_vertex++)
2586             {
2587                 glw::GLint base_index_of_vertex_internal =
2588                     base_index_of_primitive + n_primitive_vertex * stride_in_floats;
2589                 glw::GLint culldistance_array_offset    = base_index_of_vertex_internal + clipdistances_array_size;
2590                 glw::GLfloat *vertex_culldistance_array = &m_bo_data[culldistance_array_offset];
2591 
2592                 if (vertex_culldistance_array[n_culldistance_entry] >= 0)
2593                 {
2594                     /* Primitive is not culled, due to one of its distances is not negative */
2595                     distance_negative_in_all_primitive_vertices = false;
2596 
2597                     /* Skip left vertices for this distance */
2598                     break;
2599                 }
2600             }
2601 
2602             /* The distance is negative in all primitive vertices, so this distance culls the primitive */
2603             if (distance_negative_in_all_primitive_vertices)
2604             {
2605                 primitive_culled             = true;
2606                 primitive_culled_by_distance = n_culldistance_entry;
2607 
2608                 n_culled_primitives_real++;
2609 
2610                 /* Skip left distances from check */
2611                 break;
2612             }
2613         }
2614 
2615         /* Validate culling */
2616         if (primitive_culled)
2617         {
2618             /* Check whether primitive was culled and all its vertices are invisible */
2619             for (glw::GLuint n_primitive_vertex = 0; n_primitive_vertex < primitive_vertices_count;
2620                  n_primitive_vertex++)
2621             {
2622                 glw::GLint base_index_of_vertex_internal =
2623                     base_index_of_primitive + n_primitive_vertex * stride_in_floats;
2624                 glw::GLint checkpoint_position_index_internal = base_index_of_vertex_internal +
2625                                                                 clipdistances_array_size + culldistances_array_size +
2626                                                                 2 /* ignore vertex coordinates */;
2627                 glw::GLint checkpoint_x_internal =
2628                     glw::GLint(glw::GLfloat(m_to_width) * m_bo_data[checkpoint_position_index_internal]);
2629                 glw::GLint checkpoint_y_internal =
2630                     glw::GLint(glw::GLfloat(m_to_height) * m_bo_data[checkpoint_position_index_internal + 1]);
2631                 glw::GLfloat vertex_color_red_value = readRedPixelValue(checkpoint_x_internal, checkpoint_y_internal);
2632 
2633                 /* Make sure vertex is invisible */
2634                 if (vertex_color_red_value != 0)
2635                 {
2636                     m_testCtx.getLog() << tcu::TestLog::Message << "Primitive number [" << n_primitive_index << "] "
2637                                        << "should be culled by distance [" << primitive_culled_by_distance << "]"
2638                                        << "but primitive vertex at (" << checkpoint_x << "," << checkpoint_y
2639                                        << ") is visible." << tcu::TestLog::EndMessage;
2640 
2641                     TCU_FAIL("Primitive is expected to be culled, but one of its vertices is visible.");
2642                 }
2643             }
2644 
2645             /* Primitive is culled, no reason to check clipping */
2646             continue;
2647         }
2648 
2649         bool all_vertices_are_clipped = true;
2650 
2651         for (glw::GLuint n_primitive_vertex = 0; n_primitive_vertex < primitive_vertices_count; n_primitive_vertex++)
2652         {
2653             glw::GLuint base_index_of_vertex_internal = base_index_of_primitive + n_primitive_vertex * stride_in_floats;
2654             glw::GLuint clipdistance_array_index      = base_index_of_vertex_internal;
2655             glw::GLuint checkpoint_position_index_internal = base_index_of_vertex_internal + clipdistances_array_size +
2656                                                              culldistances_array_size +
2657                                                              2 /* ignore vertex coordinates */;
2658             glw::GLint checkpoint_x_internal =
2659                 glw::GLint(glw::GLfloat(m_to_width) * m_bo_data[checkpoint_position_index_internal]);
2660             glw::GLint checkpoint_y_internal =
2661                 glw::GLint(glw::GLfloat(m_to_height) * m_bo_data[checkpoint_position_index_internal + 1]);
2662             glw::GLfloat *vertex_clipdistance_array = &m_bo_data[clipdistance_array_index];
2663             bool vertex_clipped                     = false;
2664             glw::GLint vertex_clipped_by_distance   = 0;
2665             glw::GLfloat vertex_color_red_value     = readRedPixelValue(checkpoint_x_internal, checkpoint_y_internal);
2666 
2667             /* Check whether pixel should be clipped */
2668             for (glw::GLuint n_clipdistance_entry = 0; n_clipdistance_entry < clipdistances_array_size;
2669                  n_clipdistance_entry++)
2670             {
2671                 if (vertex_clipdistance_array[n_clipdistance_entry] < 0)
2672                 {
2673                     vertex_clipped             = true;
2674                     vertex_clipped_by_distance = n_clipdistance_entry;
2675 
2676                     break;
2677                 }
2678             }
2679 
2680             all_vertices_are_clipped &= vertex_clipped;
2681 
2682             /* Validate whether real data same as expected */
2683             if (vertex_clipped)
2684             {
2685                 if (vertex_color_red_value != 0)
2686                 {
2687                     m_testCtx.getLog() << tcu::TestLog::Message << "In primitive number [" << n_primitive_index << "] "
2688                                        << "vertex at (" << checkpoint_x << "," << checkpoint_y << ") "
2689                                        << "should be clipped by distance [" << vertex_clipped_by_distance << "] "
2690                                        << "(distance value [" << vertex_clipdistance_array[vertex_clipped_by_distance]
2691                                        << "])" << tcu::TestLog::EndMessage;
2692 
2693                     TCU_FAIL("Vertex is expected to be clipped and invisible, while it is visible.");
2694                 }
2695                 else
2696                 {
2697                     n_clipped_vertices_real++;
2698                 }
2699             }
2700             else
2701             {
2702                 if (vertex_color_red_value == 0)
2703                 {
2704                     m_testCtx.getLog() << tcu::TestLog::Message << "In primitive number [" << n_primitive_index << "] "
2705                                        << "vertex at (" << checkpoint_x << "," << checkpoint_y << ") "
2706                                        << "should not be clipped." << tcu::TestLog::EndMessage;
2707 
2708                     TCU_FAIL("Vertex is unexpectedly clipped or invisible");
2709                 }
2710             }
2711         }
2712 
2713         if (!all_vertices_are_clipped)
2714         {
2715             /* Check fetched values from the shader (Point 2 of Basic Outline : "Use program that...") */
2716             if (fetch_culldistance_from_fs)
2717             {
2718                 for (glw::GLuint n_primitive_vertex = 0; n_primitive_vertex < primitive_vertices_count;
2719                      n_primitive_vertex++)
2720                 {
2721                     /* Get shader output value */
2722                     glw::GLuint base_index_of_vertex_internal =
2723                         base_index_of_primitive + n_primitive_vertex * stride_in_floats;
2724                     glw::GLuint checkpoint_position_index_internal =
2725                         base_index_of_vertex_internal + clipdistances_array_size + culldistances_array_size +
2726                         2 /* ignore vertex coordinates */;
2727                     glw::GLuint culldistances_index = base_index_of_vertex_internal + clipdistances_array_size;
2728                     glw::GLint checkpoint_x_internal =
2729                         glw::GLint(glw::GLfloat(m_to_width) * m_bo_data[checkpoint_position_index_internal]);
2730                     glw::GLint checkpoint_y_internal =
2731                         glw::GLint(glw::GLfloat(m_to_height) * m_bo_data[checkpoint_position_index_internal + 1]);
2732                     glw::GLfloat vertex_color_red_value =
2733                         readRedPixelValue(checkpoint_x_internal, checkpoint_y_internal);
2734 
2735                     /* Calculate culldistances check sum hash */
2736                     float sum = 0.f;
2737 
2738                     for (glw::GLuint n_clipdistance_entry = 0; n_clipdistance_entry < clipdistances_array_size;
2739                          ++n_clipdistance_entry)
2740                     {
2741                         sum += de::abs(m_bo_data[base_index_of_vertex_internal + n_clipdistance_entry]) *
2742                                float(n_clipdistance_entry + 1);
2743                     }
2744 
2745                     for (glw::GLuint n_culldistance_entry = 0; n_culldistance_entry < culldistances_array_size;
2746                          ++n_culldistance_entry)
2747                     {
2748                         sum += de::abs(m_bo_data[culldistances_index + n_culldistance_entry]) *
2749                                float(n_culldistance_entry + 1 + clipdistances_array_size);
2750                     }
2751 
2752                     /* limit sum and return */
2753                     glw::GLfloat sum_hash =
2754                         glw::GLfloat(sum / glw::GLfloat((clipdistances_array_size + culldistances_array_size) *
2755                                                         (clipdistances_array_size + culldistances_array_size + 1)));
2756 
2757                     sum_hash = (sum_hash < 1.f) ? sum_hash : 1.f;
2758 
2759                     /* Compare against setup value */
2760                     /* precision 4 / 65535 */
2761                     const double precision_error = 0.000061036;
2762                     if (std::abs(vertex_color_red_value - sum_hash) > precision_error)
2763                     {
2764                         m_testCtx.getLog()
2765                             << tcu::TestLog::Message << "Primitive number [" << n_primitive_index << "] "
2766                             << "should have culldistance hash sum " << sum_hash << "but primitive vertex at ("
2767                             << checkpoint_x << "," << checkpoint_y << ") has sum hash equal to "
2768                             << vertex_color_red_value << tcu::TestLog::EndMessage;
2769 
2770                         TCU_FAIL("Culled distances returned from fragment shader dose not match expected values.");
2771                     }
2772                 }
2773             }
2774         }
2775     }
2776 
2777     /* sub_grid cell size is 3*3 */
2778     DE_ASSERT(m_render_primitives % 9 == 0);
2779 
2780     /* Quick check */
2781     switch (primitive_mode)
2782     {
2783     case PRIMITIVE_MODE_LINES:
2784     case PRIMITIVE_MODE_TRIANGLES:
2785     {
2786         /* Validate culled primitives */
2787         if (culldistances_array_size == 0)
2788         {
2789             DE_ASSERT(n_culled_primitives_real == 0);
2790         }
2791         else
2792         {
2793             /* Each 3rd line or triangle should be culled by test design */
2794             DE_ASSERT(glw::GLsizei(n_culled_primitives_real) == m_render_primitives / 3);
2795         }
2796 
2797         /* Validate clipped vertices */
2798         if (clipdistances_array_size == 0)
2799         {
2800             DE_ASSERT(n_clipped_vertices_real == 0);
2801         }
2802         else
2803         {
2804 #if defined(DE_DEBUG) && !defined(DE_COVERAGE_BUILD)
2805             glw::GLint one_third_of_rendered_primitives = (m_render_primitives - n_culled_primitives_real) / 3;
2806             glw::GLint n_clipped_vertices_expected      = /* One third of primitives has 0th vertex clipped */
2807                 one_third_of_rendered_primitives +
2808                 /* One third of primitives clipped completely     */
2809                 one_third_of_rendered_primitives * primitive_vertices_count;
2810 
2811             DE_ASSERT(glw::GLint(n_clipped_vertices_real) == n_clipped_vertices_expected);
2812 #endif
2813         }
2814         break;
2815     }
2816 
2817     case PRIMITIVE_MODE_POINTS:
2818     {
2819         /* Validate culled primitives */
2820         if (culldistances_array_size == 0)
2821         {
2822             DE_ASSERT(n_culled_primitives_real == 0);
2823         }
2824         else
2825         {
2826             /* 2/3 points should be culled by test design */
2827             DE_ASSERT(glw::GLsizei(n_culled_primitives_real) == m_render_primitives * 2 / 3);
2828         }
2829 
2830         /* Validate clipped vertices */
2831         if (clipdistances_array_size == 0)
2832         {
2833             DE_ASSERT(n_clipped_vertices_real == 0);
2834         }
2835         else
2836         {
2837 #if defined(DE_DEBUG) && !defined(DE_COVERAGE_BUILD)
2838             glw::GLint one_third_of_rendered_primitives = (m_render_primitives - n_culled_primitives_real) / 3;
2839 
2840             /* 2/3 of rendered points should be clipped by test design */
2841             DE_ASSERT(glw::GLint(n_clipped_vertices_real) == 2 * one_third_of_rendered_primitives);
2842 #endif
2843         }
2844 
2845         break;
2846     }
2847     default:
2848         TCU_FAIL("Unknown primitive mode");
2849     }
2850 }
2851 
2852 /** Executes test iteration.
2853  *
2854  *  @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
2855  */
test()2856 void CullDistance::FunctionalTest::test()
2857 {
2858     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
2859     bool has_succeeded       = true;
2860 
2861     /* Retrieve important GL constant values */
2862     glw::GLint gl_max_clip_distances_value                   = 0;
2863     glw::GLint gl_max_combined_clip_and_cull_distances_value = 0;
2864     glw::GLint gl_max_cull_distances_value                   = 0;
2865 
2866     gl.getIntegerv(GL_MAX_CLIP_DISTANCES, &gl_max_clip_distances_value);
2867     gl.getIntegerv(GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES, &gl_max_combined_clip_and_cull_distances_value);
2868     gl.getIntegerv(GL_MAX_CULL_DISTANCES, &gl_max_cull_distances_value);
2869     GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv() call(s) failed.");
2870 
2871     if (m_iteration > gl_max_combined_clip_and_cull_distances_value)
2872     {
2873         m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
2874         return;
2875     }
2876 
2877     gl.genTextures(1, &m_to_id);
2878     GLU_EXPECT_NO_ERROR(gl.getError(), "glGenTextures() call failed.");
2879 
2880     gl.bindTexture(GL_TEXTURE_2D, m_to_id);
2881     GLU_EXPECT_NO_ERROR(gl.getError(), "glBindTexture() call failed.");
2882 
2883     gl.texStorage2D(GL_TEXTURE_2D, 1, /* levels */
2884                     GL_R32F, m_to_width, m_to_height);
2885     GLU_EXPECT_NO_ERROR(gl.getError(), "glTexStorage2D() call failed.");
2886 
2887     /* Set up the draw/read FBO */
2888     gl.genFramebuffers(1, &m_fbo_id);
2889     GLU_EXPECT_NO_ERROR(gl.getError(), "glGenFramebuffers() call failed.");
2890 
2891     gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id);
2892     GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer() call failed.");
2893 
2894     gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_to_id, 0); /* level */
2895     GLU_EXPECT_NO_ERROR(gl.getError(), "glFramebufferTexture2D() call failed.");
2896 
2897     /* Prepare a buffer object */
2898     gl.genBuffers(1, &m_bo_id);
2899     GLU_EXPECT_NO_ERROR(gl.getError(), "glGenBuffers() call failed.");
2900 
2901     /* Prepare a VAO. We will configure separately for each iteration. */
2902     gl.genVertexArrays(1, &m_vao_id);
2903     GLU_EXPECT_NO_ERROR(gl.getError(), "glGenVertexArrays() call failed.");
2904 
2905     gl.viewport(0, 0, m_to_width, m_to_height);
2906     GLU_EXPECT_NO_ERROR(gl.getError(), "glViewport() call failed.");
2907 
2908     gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
2909     GLU_EXPECT_NO_ERROR(gl.getError(), "glClearColor() call failed.");
2910 
2911     /* Check for OpenGL feature support */
2912     if (m_test_item.use_passthrough_ts)
2913     {
2914         if (m_isContextES)
2915         {
2916             if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) &&
2917                 (!m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader") ||
2918                  // gl_PerVertex built-in variable
2919                  !m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_io_blocks")))
2920             {
2921                 m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
2922                 return;
2923             }
2924         }
2925         else
2926         {
2927             if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 0)) &&
2928                 !m_context.getContextInfo().isExtensionSupported("GL_ARB_tessellation_shader"))
2929             {
2930                 m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
2931                 return;
2932             }
2933         }
2934     }
2935 
2936     if (m_test_item.use_passthrough_gs)
2937     {
2938         if (m_isContextES)
2939         {
2940             if (!glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2)) &&
2941                 !m_context.getContextInfo().isExtensionSupported("GL_EXT_geometry_shader"))
2942             {
2943                 m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
2944                 return;
2945             }
2946         }
2947     }
2948 
2949     glw::GLuint clipdistances_array_size = 0;
2950     glw::GLuint culldistances_array_size = 0;
2951 
2952     if (m_iteration != 0 && m_iteration <= gl_max_clip_distances_value)
2953     {
2954         clipdistances_array_size = m_iteration;
2955     }
2956 
2957     if ((gl_max_combined_clip_and_cull_distances_value - m_iteration) < gl_max_cull_distances_value)
2958     {
2959         culldistances_array_size = gl_max_combined_clip_and_cull_distances_value - m_iteration;
2960     }
2961     else
2962     {
2963         culldistances_array_size = gl_max_cull_distances_value;
2964     }
2965 
2966     if (clipdistances_array_size == 0 && culldistances_array_size == 0)
2967     {
2968         /* Skip the empty iteration */
2969         m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
2970         return;
2971     }
2972 
2973     if (m_test_item.fetch_culldistances && (m_primitive_mode != PRIMITIVE_MODE_POINTS))
2974     {
2975         m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
2976         return;
2977     }
2978 
2979     /* Create a program to run */
2980     buildPO(clipdistances_array_size, culldistances_array_size, m_test_item.dynamic_index_writes, m_primitive_mode,
2981             m_test_item.redeclare_clipdistances_array, m_test_item.redeclare_culldistances_array,
2982             m_test_item.use_core_functionality, m_test_item.use_passthrough_gs, m_test_item.use_passthrough_ts,
2983             m_test_item.fetch_culldistances);
2984 
2985     /* Initialize VAO data */
2986     configureVAO(clipdistances_array_size, culldistances_array_size, m_primitive_mode);
2987 
2988     /* Run GLSL program and check results */
2989     executeRenderTest(clipdistances_array_size, culldistances_array_size, m_primitive_mode,
2990                       m_test_item.use_passthrough_ts, m_test_item.fetch_culldistances);
2991 
2992     /* All done */
2993     if (has_succeeded)
2994     {
2995         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
2996     }
2997     else
2998     {
2999         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
3000     }
3001 
3002     return;
3003 }
3004 
3005 /** Returns pixel red component read from texture at position x, y.
3006  *
3007  *  @param x x-coordinate to read pixel color component from
3008  *  @param y y-coordinate to read pixel color component from
3009  **/
readRedPixelValue(glw::GLint x,glw::GLint y)3010 glw::GLfloat CullDistance::FunctionalTest::readRedPixelValue(glw::GLint x, glw::GLint y)
3011 {
3012     DE_ASSERT(x >= 0 && (glw::GLuint)x < m_to_width);
3013     DE_ASSERT(y >= 0 && (glw::GLuint)y < m_to_height);
3014 
3015     return m_to_pixel_data_cache[(m_to_width * y + x) * m_to_pixel_data_cache_color_components];
3016 }
3017 
3018 /** Reads texture into m_to_pixel_data_cache.
3019  *  Texture size determined by fields m_to_width, m_to_height
3020  **/
readTexturePixels()3021 void CullDistance::FunctionalTest::readTexturePixels()
3022 {
3023     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3024 
3025     m_to_pixel_data_cache.clear();
3026 
3027     m_to_pixel_data_cache.resize(m_to_width * m_to_height * m_to_pixel_data_cache_color_components);
3028 
3029     /* Read vertex from texture */
3030     gl.readPixels(0,           /* x      */
3031                   0,           /* y      */
3032                   m_to_width,  /* width  */
3033                   m_to_height, /* height */
3034                   GL_RGBA, GL_FLOAT, &m_to_pixel_data_cache[0]);
3035 
3036     GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() call failed.");
3037 }
3038 
3039 /** Constructor.
3040  *
3041  *  @param context Rendering context handle.
3042  **/
NegativeTest(deqp::Context & context)3043 CullDistance::NegativeTest::NegativeTest(deqp::Context &context)
3044     : CullDistanceTestBase(context, "negative", "Cull Distance Negative Test")
3045     , m_fs_id(0)
3046     , m_po_id(0)
3047     , m_temp_buffer(nullptr)
3048     , m_vs_id(0)
3049 {
3050     /* Left blank on purpose */
3051 }
3052 
3053 /** @brief Cull Distance Negative Test deinitialization */
deinit()3054 void CullDistance::NegativeTest::deinit()
3055 {
3056     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3057 
3058     if (m_fs_id != 0)
3059     {
3060         gl.deleteShader(m_fs_id);
3061 
3062         m_fs_id = 0;
3063     }
3064 
3065     if (m_po_id != 0)
3066     {
3067         gl.deleteProgram(m_po_id);
3068 
3069         m_po_id = 0;
3070     }
3071 
3072     if (m_vs_id != 0)
3073     {
3074         gl.deleteShader(m_vs_id);
3075 
3076         m_vs_id = 0;
3077     }
3078 
3079     if (m_temp_buffer != nullptr)
3080     {
3081         delete[] m_temp_buffer;
3082 
3083         m_temp_buffer = nullptr;
3084     }
3085 }
3086 
3087 /** @brief Get string description of test with given parameters
3088  *
3089  *  @param [in] n_test_iteration                    Test iteration number
3090  *  @param [in] should_redeclare_output_variables   Indicate whether test redeclared gl_ClipDistance and gl_CullDistance
3091  *  @param [in] use_dynamic_index_based_writes      Indicate whether test used dynamic index-based setters
3092  *
3093  *  @return String containing description.
3094  */
getTestDescription(int n_test_iteration,bool should_redeclare_output_variables,bool use_dynamic_index_based_writes)3095 std::string CullDistance::NegativeTest::getTestDescription(int n_test_iteration, bool should_redeclare_output_variables,
3096                                                            bool use_dynamic_index_based_writes)
3097 {
3098     std::stringstream stream;
3099 
3100     stream << "Test iteration [" << n_test_iteration << "] which uses a vertex shader that:\n\n"
3101            << ((should_redeclare_output_variables) ?
3102                    "* redeclares gl_ClipDistance and gl_CullDistance arrays\n" :
3103                    "* does not redeclare gl_ClipDistance and gl_CullDistance arrays\n")
3104            << ((use_dynamic_index_based_writes) ? "* uses dynamic index-based writes\n" : "* uses static writes\n");
3105 
3106     return stream.str();
3107 }
3108 
3109 /** Executes test iteration.
3110  *
3111  *  @return Returns STOP when test has finished executing, CONTINUE if more iterations are needed.
3112  */
test()3113 void CullDistance::NegativeTest::test()
3114 {
3115     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
3116 
3117     /* Build the test shaders. */
3118     const glw::GLchar *token_dynamic_index_based_writes = "DYNAMIC_INDEX_BASED_WRITES";
3119     const glw::GLchar *token_insert_static_writes       = "INSERT_STATIC_WRITES";
3120     const glw::GLchar *token_n_gl_clipdistance_entries  = "N_GL_CLIPDISTANCE_ENTRIES";
3121     const glw::GLchar *token_n_gl_culldistance_entries  = "N_GL_CULLDISTANCE_ENTRIES";
3122     const glw::GLchar *token_redeclare_output_variables = "REDECLARE_OUTPUT_VARIABLES";
3123 
3124     const glw::GLchar *fs_body = "${VERSION}\n"
3125                                  "${PRECISION}\n"
3126                                  "void main()\n"
3127                                  "{\n"
3128                                  "}\n";
3129 
3130     const glw::GLchar *vs_body_preamble = "${VERSION}\n"
3131                                           "\n"
3132                                           "${EXTENSION}\n"
3133                                           "\n";
3134 
3135     const glw::GLchar *vs_body_main = "#ifdef REDECLARE_OUTPUT_VARIABLES\n"
3136                                       "    out float gl_ClipDistance[N_GL_CLIPDISTANCE_ENTRIES];\n"
3137                                       "    out float gl_CullDistance[N_GL_CULLDISTANCE_ENTRIES];\n"
3138                                       "#endif\n"
3139                                       "\n"
3140                                       "void main()\n"
3141                                       "{\n"
3142                                       "#ifdef DYNAMIC_INDEX_BASED_WRITES\n"
3143                                       "    for (int n_clipdistance_entry = 0;\n"
3144                                       "             n_clipdistance_entry < N_GL_CLIPDISTANCE_ENTRIES;\n"
3145                                       "           ++n_clipdistance_entry)\n"
3146                                       "    {\n"
3147                                       "        gl_ClipDistance[n_clipdistance_entry] = float(n_clipdistance_entry) / "
3148                                       "float(N_GL_CLIPDISTANCE_ENTRIES);\n"
3149                                       "    }\n"
3150                                       "\n"
3151                                       "    for (int n_culldistance_entry = 0;\n"
3152                                       "             n_culldistance_entry < N_GL_CULLDISTANCE_ENTRIES;\n"
3153                                       "           ++n_culldistance_entry)\n"
3154                                       "    {\n"
3155                                       "        gl_CullDistance[n_culldistance_entry] = float(n_culldistance_entry) / "
3156                                       "float(N_GL_CULLDISTANCE_ENTRIES);\n"
3157                                       "    }\n"
3158                                       "#else\n"
3159                                       "    INSERT_STATIC_WRITES\n"
3160                                       "#endif\n"
3161                                       "}\n";
3162 
3163     /* It only makes sense to run this test if GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES
3164      * is lower than a sum of GL_MAX_CLIP_DISTANCES and GL_MAX_CLIP_CULL_DISTANCES.
3165      */
3166     glw::GLint gl_max_clip_distances_value                   = 0;
3167     glw::GLint gl_max_combined_clip_and_cull_distances_value = 0;
3168     glw::GLint gl_max_cull_distances_value                   = 0;
3169     glw::GLuint n_gl_clipdistance_array_items                = 0;
3170     std::string n_gl_clipdistance_array_items_string;
3171     glw::GLuint n_gl_culldistance_array_items = 0;
3172     std::string n_gl_culldistance_array_items_string;
3173     std::string static_write_shader_body_part;
3174 
3175     gl.getIntegerv(GL_MAX_CLIP_DISTANCES, &gl_max_clip_distances_value);
3176     gl.getIntegerv(GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES, &gl_max_combined_clip_and_cull_distances_value);
3177     gl.getIntegerv(GL_MAX_CULL_DISTANCES, &gl_max_cull_distances_value);
3178 
3179     if (gl_max_clip_distances_value + gl_max_cull_distances_value < gl_max_combined_clip_and_cull_distances_value)
3180     {
3181         m_testCtx.getLog() << tcu::TestLog::Message
3182                            << "GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES is larger than or equal to "
3183                               "the sum of GL_MAX_CLIP_DISTANCES and GL_MAX_CULL_DISTANCES. Skipping."
3184                            << tcu::TestLog::EndMessage;
3185 
3186         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3187 
3188         return;
3189     }
3190 
3191     n_gl_clipdistance_array_items = gl_max_clip_distances_value;
3192     n_gl_culldistance_array_items = gl_max_combined_clip_and_cull_distances_value - gl_max_clip_distances_value + 1;
3193 
3194     /* Determine the number of items we will want the gl_ClipDistance and gl_CullDistance arrays
3195      * to hold for test iterations that will re-declare the built-in output variables.
3196      */
3197     {
3198         std::stringstream temp_sstream;
3199 
3200         temp_sstream << n_gl_clipdistance_array_items;
3201 
3202         n_gl_clipdistance_array_items_string = temp_sstream.str();
3203     }
3204 
3205     {
3206         std::stringstream temp_sstream;
3207 
3208         temp_sstream << n_gl_culldistance_array_items;
3209 
3210         n_gl_culldistance_array_items_string = temp_sstream.str();
3211     }
3212 
3213     /* Form the "static write" shader body part. */
3214     {
3215         std::stringstream temp_sstream;
3216 
3217         temp_sstream << "gl_ClipDistance[" << n_gl_clipdistance_array_items_string.c_str() << "] = 0.0f;\n"
3218                      << "gl_CullDistance[" << n_gl_culldistance_array_items_string.c_str() << "] = 0.0f;\n";
3219 
3220         static_write_shader_body_part = temp_sstream.str();
3221     }
3222 
3223     /* Prepare GL objects before we continue */
3224     glw::GLint compile_status = GL_FALSE;
3225 
3226     m_fs_id = gl.createShader(GL_FRAGMENT_SHADER);
3227     m_po_id = gl.createProgram();
3228     m_vs_id = gl.createShader(GL_VERTEX_SHADER);
3229 
3230     GLU_EXPECT_NO_ERROR(gl.getError(), "glCreateProgram() / glCreateShader() calls failed.");
3231 
3232     gl.attachShader(m_po_id, m_fs_id);
3233     gl.attachShader(m_po_id, m_vs_id);
3234 
3235     GLU_EXPECT_NO_ERROR(gl.getError(), "glAttachShader() call(s) failed.");
3236 
3237     std::string fs_body_spec    = tcu::StringTemplate(fs_body).specialize(specializationMap);
3238     const char *fs_body_raw_ptr = fs_body_spec.c_str();
3239 
3240     gl.shaderSource(m_fs_id, 1,                 /* count */
3241                     &fs_body_raw_ptr, nullptr); /* length */
3242     GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed.");
3243 
3244     gl.compileShader(m_fs_id);
3245     GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed.");
3246 
3247     gl.getShaderiv(m_fs_id, GL_COMPILE_STATUS, &compile_status);
3248     GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed.");
3249 
3250     if (compile_status == GL_FALSE)
3251     {
3252         TCU_FAIL("Fragment shader failed to compile.");
3253     }
3254 
3255     /* Run three separate test iterations. */
3256     struct _test_item
3257     {
3258         bool should_redeclare_output_variables;
3259         bool use_dynamic_index_based_writes;
3260     } test_items[]                  = {/* Negative Test 1 */
3261                       {true, false},
3262 
3263                       /* Negative Test 2 */
3264                       {false, false},
3265 
3266                       /* Negative Test 3 */
3267                       {false, true}};
3268     const unsigned int n_test_items = sizeof(test_items) / sizeof(test_items[0]);
3269 
3270     for (unsigned int n_test_item = 0; n_test_item < n_test_items; ++n_test_item)
3271     {
3272         const _test_item &current_test_item = test_items[n_test_item];
3273 
3274         /* Prepare vertex shader body */
3275         std::size_t token_position = std::string::npos;
3276         std::stringstream vs_body_sstream;
3277         std::string vs_body_string;
3278         std::string vs_body_header = tcu::StringTemplate(vs_body_preamble).specialize(specializationMap);
3279 
3280         vs_body_sstream << vs_body_header << "\n";
3281 
3282         if (current_test_item.should_redeclare_output_variables)
3283         {
3284             vs_body_sstream << "#define " << token_redeclare_output_variables << "\n";
3285         }
3286 
3287         if (current_test_item.use_dynamic_index_based_writes)
3288         {
3289             vs_body_sstream << "#define " << token_dynamic_index_based_writes << "\n";
3290         }
3291 
3292         vs_body_sstream << vs_body_main;
3293 
3294         /* Replace tokens with meaningful values */
3295         vs_body_string = vs_body_sstream.str();
3296 
3297         while ((token_position = vs_body_string.find(token_n_gl_clipdistance_entries)) != std::string::npos)
3298         {
3299             vs_body_string = vs_body_string.replace(token_position, strlen(token_n_gl_clipdistance_entries),
3300                                                     n_gl_clipdistance_array_items_string);
3301         }
3302 
3303         while ((token_position = vs_body_string.find(token_n_gl_culldistance_entries)) != std::string::npos)
3304         {
3305             vs_body_string = vs_body_string.replace(token_position, strlen(token_n_gl_clipdistance_entries),
3306                                                     n_gl_culldistance_array_items_string);
3307         }
3308 
3309         while ((token_position = vs_body_string.find(token_insert_static_writes)) != std::string::npos)
3310         {
3311             vs_body_string = vs_body_string.replace(token_position, strlen(token_insert_static_writes),
3312                                                     static_write_shader_body_part);
3313         }
3314 
3315         /* Try to compile the vertex shader */
3316         glw::GLint compile_status_internal = GL_FALSE;
3317         const char *vs_body_raw_ptr        = vs_body_string.c_str();
3318 
3319         gl.shaderSource(m_vs_id, 1,                 /* count */
3320                         &vs_body_raw_ptr, nullptr); /* length */
3321         GLU_EXPECT_NO_ERROR(gl.getError(), "glShaderSource() call failed.");
3322 
3323         gl.compileShader(m_vs_id);
3324         GLU_EXPECT_NO_ERROR(gl.getError(), "glCompileShader() call failed.");
3325 
3326         gl.getShaderiv(m_vs_id, GL_COMPILE_STATUS, &compile_status_internal);
3327         GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed.");
3328 
3329         if (compile_status_internal == GL_FALSE)
3330         {
3331             glw::GLint buffer_size = 0;
3332 
3333             /* Log the compilation error */
3334             m_testCtx.getLog() << tcu::TestLog::Message
3335                                << getTestDescription(n_test_item, current_test_item.should_redeclare_output_variables,
3336                                                      current_test_item.use_dynamic_index_based_writes)
3337                                << "has failed (as expected) to compile with the following info log:\n\n"
3338                                << tcu::TestLog::EndMessage;
3339 
3340             gl.getShaderiv(m_vs_id, GL_INFO_LOG_LENGTH, &buffer_size);
3341             GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderiv() call failed.");
3342 
3343             m_temp_buffer = new glw::GLchar[buffer_size + 1];
3344 
3345             memset(m_temp_buffer, 0, buffer_size + 1);
3346 
3347             gl.getShaderInfoLog(m_vs_id, buffer_size, nullptr, /* length */
3348                                 m_temp_buffer);
3349             GLU_EXPECT_NO_ERROR(gl.getError(), "glGetShaderInfoLog() call failed.");
3350 
3351             m_testCtx.getLog() << tcu::TestLog::Message << m_temp_buffer << tcu::TestLog::EndMessage;
3352 
3353             delete[] m_temp_buffer;
3354             m_temp_buffer = nullptr;
3355 
3356             /* Move on to the next iteration */
3357             continue;
3358         }
3359 
3360         /* Try to link the program object */
3361         glw::GLint link_status = GL_FALSE;
3362 
3363         gl.linkProgram(m_po_id);
3364         GLU_EXPECT_NO_ERROR(gl.getError(), "glLinkProgram() call failed.");
3365 
3366         gl.getProgramiv(m_po_id, GL_LINK_STATUS, &link_status);
3367         GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed.");
3368 
3369         if (link_status == GL_TRUE)
3370         {
3371             m_testCtx.getLog() << tcu::TestLog::Message
3372                                << getTestDescription(n_test_item, current_test_item.should_redeclare_output_variables,
3373                                                      current_test_item.use_dynamic_index_based_writes)
3374                                << "has linked successfully which is invalid!" << tcu::TestLog::EndMessage;
3375 
3376             TCU_FAIL("Program object has linked successfully, even though the process should have failed.");
3377         }
3378         else
3379         {
3380             glw::GLint buffer_size = 0;
3381 
3382             m_testCtx.getLog() << tcu::TestLog::Message
3383                                << getTestDescription(n_test_item, current_test_item.should_redeclare_output_variables,
3384                                                      current_test_item.use_dynamic_index_based_writes)
3385                                << "has failed (as expected) to link with the following info log:\n\n"
3386                                << tcu::TestLog::EndMessage;
3387 
3388             gl.getProgramiv(m_po_id, GL_INFO_LOG_LENGTH, &buffer_size);
3389             GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramiv() call failed.");
3390 
3391             m_temp_buffer = new glw::GLchar[buffer_size + 1];
3392 
3393             memset(m_temp_buffer, 0, buffer_size + 1);
3394 
3395             gl.getProgramInfoLog(m_po_id, buffer_size, nullptr, /* length */
3396                                  m_temp_buffer);
3397             GLU_EXPECT_NO_ERROR(gl.getError(), "glGetProgramInfoLog() call failed.");
3398 
3399             m_testCtx.getLog() << tcu::TestLog::Message << m_temp_buffer << tcu::TestLog::EndMessage;
3400 
3401             delete[] m_temp_buffer;
3402             m_temp_buffer = nullptr;
3403         }
3404     } /* for (all test items) */
3405 
3406     /* All done */
3407     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
3408 
3409     return;
3410 }
3411 
3412 /** Constructor.
3413  *
3414  *  @param context Rendering context.
3415  */
Tests(deqp::Context & context)3416 CullDistance::Tests::Tests(deqp::Context &context) : TestCaseGroup(context, "cull_distance", "Cull Distance Test Suite")
3417 {
3418 }
3419 
3420 /** Initializes the test group contents. */
init()3421 void CullDistance::Tests::init()
3422 {
3423     addChild(new CullDistance::APICoverageTest(m_context));
3424     addFunctionalTest();
3425     addChild(new CullDistance::NegativeTest(m_context));
3426 }
3427 
addFunctionalTest()3428 void CullDistance::Tests::addFunctionalTest()
3429 {
3430     bool is_core = glu::contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));
3431     const glw::GLint MAX_COMBINED_CLIP_AND_CULL_DISTANCES = 8;
3432 
3433     CullDistance::FunctionalTest::_test_item test_items[] = {
3434         /* Use the basic outline to test the basic functionality of cull distances. */
3435         {
3436             0, true, /* redeclare_clipdistances_array */
3437             true,    /* redeclare_culldistances_array */
3438             false,   /* dynamic_index_writes          */
3439             false,   /* use_passthrough_gs            */
3440             false,   /* use_passthrough_ts            */
3441             is_core, /* use_core_functionality        */
3442             false    /* fetch_culldistances           */
3443         },
3444         /* Use the basic outline but don't redeclare gl_ClipDistance with a size. */
3445         {
3446             1, false, /* redeclare_clipdistances_array */
3447             true,     /* redeclare_culldistances_array */
3448             false,    /* dynamic_index_writes          */
3449             false,    /* use_passthrough_gs            */
3450             false,    /* use_passthrough_ts            */
3451             is_core,  /* use_core_functionality        */
3452             false     /* fetch_culldistances           */
3453         },
3454         /* Use the basic outline but don't redeclare gl_CullDistance with a size. */
3455         {
3456             2, true, /* redeclare_clipdistances_array  */
3457             false,   /* redeclare_culldistances_array  */
3458             false,   /* dynamic_index_writes           */
3459             false,   /* use_passthrough_gs             */
3460             false,   /* use_passthrough_ts             */
3461             is_core, /* use_core_functionality         */
3462             false    /* fetch_culldistances            */
3463         },
3464         /* Use the basic outline but don't redeclare either gl_ClipDistance or
3465          * gl_CullDistance with a size.
3466          */
3467         {
3468             3, false, /* redeclare_clipdistances_array */
3469             false,    /* redeclare_culldistances_array */
3470             false,    /* dynamic_index_writes          */
3471             false,    /* use_passthrough_gs            */
3472             false,    /* use_passthrough_ts            */
3473             is_core,  /* use_core_functionality        */
3474             false     /* fetch_culldistances           */
3475         },
3476         /* Use the basic outline but use dynamic indexing when writing the elements
3477          * of the gl_ClipDistance and gl_CullDistance arrays.
3478          */
3479         {
3480             4, true, /* redeclare_clipdistances_array */
3481             true,    /* redeclare_culldistances_array */
3482             true,    /* dynamic_index_writes          */
3483             false,   /* use_passthrough_gs            */
3484             false,   /* use_passthrough_ts            */
3485             is_core, /* use_core_functionality        */
3486             false    /* fetch_culldistances           */
3487         },
3488         /* Use the basic outline but add a geometry shader to the program that
3489          * simply passes through all written clip and cull distances.
3490          */
3491         {
3492             5, true, /* redeclare_clipdistances_array */
3493             true,    /* redeclare_culldistances_array */
3494             false,   /* dynamic_index_writes          */
3495             true,    /* use_passthrough_gs            */
3496             false,   /* use_passthrough_ts            */
3497             is_core, /* use_core_functionality        */
3498             false    /* fetch_culldistances           */
3499         },
3500         /* Use the basic outline but add a tessellation control and tessellation
3501          * evaluation shader to the program which simply pass through all written
3502          * clip and cull distances.
3503          */
3504         {
3505             6, true, /* redeclare_clipdistances_array */
3506             true,    /* redeclare_culldistances_array */
3507             false,   /* dynamic_index_writes          */
3508             false,   /* use_passthrough_gs            */
3509             true,    /* use_passthrough_ts            */
3510             is_core, /* use_core_functionality        */
3511             false    /* fetch_culldistances           */
3512         },
3513         /* Test that using #extension with GL_ARB_cull_distance allows using the
3514          * feature even with an earlier version of GLSL. Also test that the
3515          * extension name is available as preprocessor #define.
3516          */
3517         {
3518             7, true, /* redeclare_clipdistances_array */
3519             true,    /* redeclare_culldistances_array */
3520             false,   /* dynamic_index_writes          */
3521             false,   /* use_passthrough_gs            */
3522             false,   /* use_passthrough_ts            */
3523             false,   /* use_core_functionality        */
3524             false    /* fetch_culldistances           */
3525         },
3526         /* Use a program that has only a vertex shader and a fragment shader.
3527          * The vertex shader should redeclare gl_ClipDistance with a size that
3528          * fits all enabled cull distances. Also redeclare gl_CullDistance with a
3529          * size. The sum of the two sizes should not be more than MAX_COMBINED_-
3530          * CLIP_AND_CULL_DISTANCES. The fragment shader should output the cull
3531          * distances written by the vertex shader by reading them from the built-in
3532          * array gl_CullDistance.
3533          */
3534         {
3535             8, true, /* redeclare_clipdistances_array */
3536             true,    /* redeclare_culldistances_array */
3537             false,   /* dynamic_index_writes          */
3538             false,   /* use_passthrough_gs            */
3539             false,   /* use_passthrough_ts            */
3540             false,   /* use_core_functionality        */
3541             true     /* fetch_culldistances           */
3542         }};
3543 
3544     const glw::GLuint n_test_items = sizeof(test_items) / sizeof(test_items[0]);
3545     for (glw::GLuint n_test_item = 0; n_test_item < n_test_items; ++n_test_item)
3546     {
3547         const CullDistance::FunctionalTest::_primitive_mode
3548             primitive_modes[CullDistance::FunctionalTest::PRIMITIVE_MODE_COUNT] = {
3549                 CullDistance::FunctionalTest::PRIMITIVE_MODE_LINES, CullDistance::FunctionalTest::PRIMITIVE_MODE_POINTS,
3550                 CullDistance::FunctionalTest::PRIMITIVE_MODE_TRIANGLES};
3551 
3552         for (glw::GLuint primitive_mode_index = 0;
3553              primitive_mode_index < CullDistance::FunctionalTest::PRIMITIVE_MODE_COUNT; ++primitive_mode_index)
3554         {
3555             /* Iterate over a set of gl_ClipDistances[] and gl_CullDistances[] array sizes */
3556             for (glw::GLint n_iteration = 0; n_iteration < MAX_COMBINED_CLIP_AND_CULL_DISTANCES; ++n_iteration)
3557             {
3558                 addChild(new CullDistance::FunctionalTest(m_context, test_items[n_test_item],
3559                                                           primitive_modes[primitive_mode_index], n_iteration));
3560             }
3561         }
3562     }
3563 }
3564 } // namespace glcts
3565