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