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