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 * \file gl3GPUShader5Tests.cpp 26 * \brief Implements conformance tests for "GPU Shader 5" functionality. 27 */ /*-------------------------------------------------------------------*/ 28 29 #include "gl3cGPUShader5Tests.hpp" 30 #include "gluContextInfo.hpp" 31 #include "glwFunctions.hpp" 32 #include "tcuMatrix.hpp" 33 #include "tcuTestLog.hpp" 34 35 #include <iomanip> 36 37 #include <deMath.h> 38 #include <tcuMatrixUtil.hpp> 39 #include <tcuVectorUtil.hpp> 40 41 #include <cstdlib> 42 #include <cstring> 43 #include <limits> 44 #include <memory> 45 46 namespace gl3cts 47 { 48 49 /** Constructor 50 * 51 * @param context Test context 52 **/ 53 Utils::programInfo::programInfo(deqp::Context& context) 54 : m_context(context), m_fragment_shader_id(0), m_program_object_id(0), m_vertex_shader_id(0) 55 { 56 /* Nothing to be done here */ 57 } 58 59 /** Destructor 60 * 61 **/ 62 Utils::programInfo::~programInfo() 63 { 64 /* GL entry points */ 65 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 66 67 /* Make sure program object is no longer used by GL */ 68 gl.useProgram(0); 69 70 /* Clean program object */ 71 if (0 != m_program_object_id) 72 { 73 gl.deleteProgram(m_program_object_id); 74 m_program_object_id = 0; 75 } 76 77 /* Clean shaders */ 78 if (0 != m_fragment_shader_id) 79 { 80 gl.deleteShader(m_fragment_shader_id); 81 m_fragment_shader_id = 0; 82 } 83 84 if (0 != m_vertex_shader_id) 85 { 86 gl.deleteShader(m_vertex_shader_id); 87 m_vertex_shader_id = 0; 88 } 89 } 90 91 /** Build program 92 * 93 * @param fragment_shader_code Fragment shader source code 94 * @param vertex_shader_code Vertex shader source code 95 * @param varying_names Array of strings containing names of varyings to be captured with transfrom feedback 96 * @param n_varying_names Number of varyings to be captured with transfrom feedback 97 **/ 98 void Utils::programInfo::build(const glw::GLchar* fragment_shader_code, const glw::GLchar* vertex_shader_code) 99 { 100 /* GL entry points */ 101 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 102 103 /* Create shader objects and compile */ 104 if (0 != fragment_shader_code) 105 { 106 m_fragment_shader_id = gl.createShader(GL_FRAGMENT_SHADER); 107 GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader"); 108 109 compile(m_fragment_shader_id, fragment_shader_code); 110 } 111 112 if (0 != vertex_shader_code) 113 { 114 m_vertex_shader_id = gl.createShader(GL_VERTEX_SHADER); 115 GLU_EXPECT_NO_ERROR(gl.getError(), "CreateShader"); 116 117 compile(m_vertex_shader_id, vertex_shader_code); 118 } 119 120 /* Create program object */ 121 m_program_object_id = gl.createProgram(); 122 GLU_EXPECT_NO_ERROR(gl.getError(), "CreateProgram"); 123 124 /* Link program */ 125 link(); 126 } 127 128 /** Compile shader 129 * 130 * @param shader_id Shader object id 131 * @param shader_code Shader source code 132 **/ 133 void Utils::programInfo::compile(glw::GLuint shader_id, const glw::GLchar* shader_code) const 134 { 135 /* GL entry points */ 136 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 137 138 /* Compilation status */ 139 glw::GLint status = GL_FALSE; 140 141 /* Set source code */ 142 gl.shaderSource(shader_id, 1 /* count */, &shader_code, 0); 143 GLU_EXPECT_NO_ERROR(gl.getError(), "ShaderSource"); 144 145 /* Compile */ 146 gl.compileShader(shader_id); 147 GLU_EXPECT_NO_ERROR(gl.getError(), "CompileShader"); 148 149 /* Get compilation status */ 150 gl.getShaderiv(shader_id, GL_COMPILE_STATUS, &status); 151 GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv"); 152 153 /* Log compilation error */ 154 if (GL_TRUE != status) 155 { 156 glw::GLint length = 0; 157 std::vector<glw::GLchar> message; 158 159 /* Error log length */ 160 gl.getShaderiv(shader_id, GL_INFO_LOG_LENGTH, &length); 161 GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderiv"); 162 163 /* Prepare storage */ 164 message.resize(length); 165 166 /* Get error log */ 167 gl.getShaderInfoLog(shader_id, length, 0, &message[0]); 168 GLU_EXPECT_NO_ERROR(gl.getError(), "GetShaderInfoLog"); 169 170 /* Log */ 171 m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failed to compile shader:\n" 172 << &message[0] << "\nShader source\n" 173 << shader_code << tcu::TestLog::EndMessage; 174 175 TCU_FAIL("Failed to compile shader"); 176 } 177 } 178 179 /** Attach shaders and link program 180 * 181 **/ 182 void Utils::programInfo::link() const 183 { 184 /* GL entry points */ 185 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 186 187 /* Link status */ 188 glw::GLint status = GL_FALSE; 189 190 /* Attach shaders */ 191 if (0 != m_fragment_shader_id) 192 { 193 gl.attachShader(m_program_object_id, m_fragment_shader_id); 194 GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader"); 195 } 196 197 if (0 != m_vertex_shader_id) 198 { 199 gl.attachShader(m_program_object_id, m_vertex_shader_id); 200 GLU_EXPECT_NO_ERROR(gl.getError(), "AttachShader"); 201 } 202 203 /* Link */ 204 gl.linkProgram(m_program_object_id); 205 GLU_EXPECT_NO_ERROR(gl.getError(), "LinkProgram"); 206 207 /* Get link status */ 208 gl.getProgramiv(m_program_object_id, GL_LINK_STATUS, &status); 209 GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv"); 210 211 /* Log link error */ 212 if (GL_TRUE != status) 213 { 214 glw::GLint length = 0; 215 std::vector<glw::GLchar> message; 216 217 /* Get error log length */ 218 gl.getProgramiv(m_program_object_id, GL_INFO_LOG_LENGTH, &length); 219 GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramiv"); 220 221 message.resize(length); 222 223 /* Get error log */ 224 gl.getProgramInfoLog(m_program_object_id, length, 0, &message[0]); 225 GLU_EXPECT_NO_ERROR(gl.getError(), "GetProgramInfoLog"); 226 227 /* Log */ 228 m_context.getTestContext().getLog() << tcu::TestLog::Message << "Failed to link program:\n" 229 << &message[0] << tcu::TestLog::EndMessage; 230 231 TCU_FAIL("Failed to link program"); 232 } 233 } 234 235 /** Set the uniform variable with provided data. 236 * 237 * @param type Type of variable 238 * @param name Name of variable 239 * @param data Data 240 */ 241 void Utils::programInfo::setUniform(Utils::_variable_type type, const glw::GLchar* name, const glw::GLvoid* data) 242 { 243 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 244 245 const glw::GLfloat* f_data = (glw::GLfloat*)data; 246 const glw::GLint* i_data = (glw::GLint*)data; 247 const glw::GLuint* u_data = (glw::GLuint*)data; 248 249 /* Get location */ 250 glw::GLint location = gl.getUniformLocation(m_program_object_id, name); 251 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetUniformLocation() call failed."); 252 253 if (-1 == location) 254 { 255 TCU_FAIL("Uniform variable is unavailable"); 256 } 257 258 /* Set data */ 259 switch (type) 260 { 261 case Utils::VARIABLE_TYPE_FLOAT: 262 gl.uniform1fv(location, 1, f_data); 263 break; 264 case Utils::VARIABLE_TYPE_INT: 265 gl.uniform1iv(location, 1, i_data); 266 break; 267 case Utils::VARIABLE_TYPE_IVEC2: 268 gl.uniform2iv(location, 1, i_data); 269 break; 270 case Utils::VARIABLE_TYPE_IVEC3: 271 gl.uniform3iv(location, 1, i_data); 272 break; 273 case Utils::VARIABLE_TYPE_IVEC4: 274 gl.uniform4iv(location, 1, i_data); 275 break; 276 case Utils::VARIABLE_TYPE_UINT: 277 gl.uniform1uiv(location, 1, u_data); 278 break; 279 case Utils::VARIABLE_TYPE_UVEC2: 280 gl.uniform2uiv(location, 1, u_data); 281 break; 282 case Utils::VARIABLE_TYPE_UVEC3: 283 gl.uniform3uiv(location, 1, u_data); 284 break; 285 case Utils::VARIABLE_TYPE_UVEC4: 286 gl.uniform4uiv(location, 1, u_data); 287 break; 288 case Utils::VARIABLE_TYPE_VEC2: 289 gl.uniform2fv(location, 1, f_data); 290 break; 291 case Utils::VARIABLE_TYPE_VEC3: 292 gl.uniform3fv(location, 1, f_data); 293 break; 294 case Utils::VARIABLE_TYPE_VEC4: 295 gl.uniform4fv(location, 1, f_data); 296 break; 297 default: 298 TCU_FAIL("Invalid enum"); 299 break; 300 } 301 302 GLU_EXPECT_NO_ERROR(gl.getError(), "Uniform"); 303 } 304 305 /** Replace first occurance of <token> with <text> in <string> starting at <search_posistion> 306 * 307 * @param token Token string 308 * @param search_position Position at which find will start, it is updated to position at which replaced text ends 309 * @param text String that will be used as replacement for <token> 310 * @param string String to work on 311 **/ 312 void Utils::replaceToken(const glw::GLchar* token, size_t& search_position, const glw::GLchar* text, 313 std::string& string) 314 { 315 const size_t text_length = strlen(text); 316 const size_t token_length = strlen(token); 317 const size_t token_position = string.find(token, search_position); 318 319 string.replace(token_position, token_length, text, text_length); 320 321 search_position = token_position + text_length; 322 } 323 324 /* Constants used by GPUShader5ImplicitConversionsTest */ 325 const glw::GLsizei GPUShader5ImplicitConversionsTest::m_width = 8; 326 const glw::GLsizei GPUShader5ImplicitConversionsTest::m_height = 8; 327 328 /** Constructor. 329 * 330 * @param context Rendering context. 331 **/ 332 GPUShader5ImplicitConversionsTest::GPUShader5ImplicitConversionsTest(deqp::Context& context) 333 : TestCase(context, "implicit_conversions", 334 "Verifies that implicit conversions are accepted and executed as explicit ones") 335 , m_fbo_id(0) 336 , m_tex_id(0) 337 , m_vao_id(0) 338 339 { 340 /* Left blank intentionally */ 341 } 342 343 /** Constructor. 344 * 345 * @param context Rendering context. 346 * @param name Name of test 347 * @param description Describes test 348 **/ 349 GPUShader5ImplicitConversionsTest::GPUShader5ImplicitConversionsTest(deqp::Context& context, const char* name, 350 const char* description) 351 : TestCase(context, name, description), m_fbo_id(0), m_tex_id(0), m_vao_id(0) 352 353 { 354 /* Left blank intentionally */ 355 } 356 357 /** Deinitializes all GL objects that may have been created during 358 * test execution. 359 **/ 360 void GPUShader5ImplicitConversionsTest::deinit() 361 { 362 if (m_fbo_id != 0) 363 { 364 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 365 gl.deleteFramebuffers(1, &m_fbo_id); 366 367 m_fbo_id = 0; 368 } 369 370 if (m_tex_id != 0) 371 { 372 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 373 gl.deleteTextures(1, &m_tex_id); 374 375 m_tex_id = 0; 376 } 377 378 if (m_vao_id != 0) 379 { 380 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 381 gl.deleteVertexArrays(1, &m_vao_id); 382 383 m_vao_id = 0; 384 } 385 } 386 387 /** Executes test iteration. 388 * 389 * @return Returns STOP. 390 */ 391 tcu::TestNode::IterateResult GPUShader5ImplicitConversionsTest::iterate() 392 { 393 /* Defines data used as u1 and u2 uniforms */ 394 static const glw::GLint uni_data_int_1[4] = { 112, -1122, 111222, -1222111222 }; 395 static const glw::GLint uni_data_int_2[4] = { -112, 1122, -111222, 1222111222 }; 396 static const glw::GLuint uni_data_uint_1[4] = { 0xffff0000, 0x0000ffff, 0x00ffffff, 0xffffffff }; 397 static const glw::GLuint uni_data_uint_2[4] = { 0xfff70000, 0x00007fff, 0x007fffff, 0xfffffff7 }; 398 399 /* Defines test cases */ 400 static const testCase test_cases[] = { 401 { "uint", false, "int", Utils::VARIABLE_TYPE_INT, uni_data_int_1, uni_data_int_2 } /* int >> uint */, 402 { "uint", true, "int", Utils::VARIABLE_TYPE_INT, uni_data_int_1, uni_data_int_1 }, 403 { "float", false, "int", Utils::VARIABLE_TYPE_INT, uni_data_int_1, uni_data_int_2 } /* int >> float */, 404 { "float", true, "int", Utils::VARIABLE_TYPE_INT, uni_data_int_2, uni_data_int_2 }, 405 { "uvec2", false, "ivec2", Utils::VARIABLE_TYPE_IVEC2, uni_data_int_1, uni_data_int_2 } /* ivec2 >> uvec2 */, 406 { "uvec2", true, "ivec2", Utils::VARIABLE_TYPE_IVEC2, uni_data_int_1, uni_data_int_1 }, 407 { "vec2", false, "ivec2", Utils::VARIABLE_TYPE_IVEC2, uni_data_int_1, uni_data_int_2 } /* ivec2 >> vec2 */, 408 { "vec2", true, "ivec2", Utils::VARIABLE_TYPE_IVEC2, uni_data_int_1, uni_data_int_1 }, 409 { "uvec3", false, "ivec3", Utils::VARIABLE_TYPE_IVEC3, uni_data_int_1, uni_data_int_2 } /* ivec3 >> uvec3 */, 410 { "uvec3", true, "ivec3", Utils::VARIABLE_TYPE_IVEC3, uni_data_int_2, uni_data_int_2 }, 411 { "vec3", false, "ivec3", Utils::VARIABLE_TYPE_IVEC3, uni_data_int_1, uni_data_int_2 } /* ivec3 >> vec3 */, 412 { "vec3", true, "ivec3", Utils::VARIABLE_TYPE_IVEC3, uni_data_int_2, uni_data_int_2 }, 413 { "uvec4", false, "ivec4", Utils::VARIABLE_TYPE_IVEC4, uni_data_int_1, uni_data_int_2 } /* ivec4 >> uvec4 */, 414 { "uvec4", true, "ivec4", Utils::VARIABLE_TYPE_IVEC4, uni_data_int_1, uni_data_int_1 }, 415 { "vec4", false, "ivec4", Utils::VARIABLE_TYPE_IVEC4, uni_data_int_1, uni_data_int_2 } /* ivec4 >> vec4 */, 416 { "vec4", true, "ivec4", Utils::VARIABLE_TYPE_IVEC4, uni_data_int_1, uni_data_int_1 }, 417 { "float", false, "uint", Utils::VARIABLE_TYPE_UINT, uni_data_uint_1, uni_data_uint_2 } /* uint >> float */, 418 { "float", true, "uint", Utils::VARIABLE_TYPE_UINT, uni_data_uint_2, uni_data_uint_2 }, 419 { "vec2", false, "uvec2", Utils::VARIABLE_TYPE_UVEC2, uni_data_uint_1, uni_data_uint_2 } /* uvec2 >> vec2 */, 420 { "vec2", true, "uvec2", Utils::VARIABLE_TYPE_UVEC2, uni_data_uint_1, uni_data_uint_1 }, 421 { "vec3", false, "uvec3", Utils::VARIABLE_TYPE_UVEC3, uni_data_uint_1, uni_data_uint_2 } /* uvec3 >> vec3 */, 422 { "vec3", true, "uvec3", Utils::VARIABLE_TYPE_UVEC3, uni_data_uint_2, uni_data_uint_2 }, 423 { "vec4", false, "uvec4", Utils::VARIABLE_TYPE_UVEC4, uni_data_uint_1, uni_data_uint_2 } /* uvec4 >> vec4 */, 424 { "vec4", true, "uvec4", Utils::VARIABLE_TYPE_UVEC4, uni_data_uint_1, uni_data_uint_1 }, 425 }; 426 static const size_t n_test_cases = sizeof(test_cases) / sizeof(test_cases[0]); 427 428 testInit(); 429 430 /* Execute test cases */ 431 for (size_t i = 0; i < n_test_cases; ++i) 432 { 433 const testCase& test_case = test_cases[i]; 434 435 executeTestCase(test_case); 436 } 437 438 /* Set result - exceptions are thrown in case of any error */ 439 m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); 440 441 /* Done */ 442 return STOP; 443 } 444 445 /** Initializes frame buffer and vertex array 446 * 447 **/ 448 void GPUShader5ImplicitConversionsTest::testInit() 449 { 450 /* */ 451 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 452 453 /* Prepare texture for color attachment 0 */ 454 gl.genTextures(1, &m_tex_id); 455 GLU_EXPECT_NO_ERROR(gl.getError(), "GenTextures"); 456 457 gl.bindTexture(GL_TEXTURE_2D, m_tex_id); 458 GLU_EXPECT_NO_ERROR(gl.getError(), "BindTexture"); 459 460 gl.texStorage2D(GL_TEXTURE_2D, 1 /* levels */, GL_RGBA8, m_width, m_height); 461 GLU_EXPECT_NO_ERROR(gl.getError(), "TexStorage2D"); 462 463 /* Prepare FBO with color attachment 0 */ 464 gl.genFramebuffers(1, &m_fbo_id); 465 GLU_EXPECT_NO_ERROR(gl.getError(), "GenFramebuffers"); 466 467 gl.bindFramebuffer(GL_FRAMEBUFFER, m_fbo_id); 468 GLU_EXPECT_NO_ERROR(gl.getError(), "BindFramebuffer"); 469 470 gl.framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_tex_id, 0 /* level */); 471 GLU_EXPECT_NO_ERROR(gl.getError(), "FramebufferTexture2D"); 472 473 /* Set Viewport */ 474 gl.viewport(0 /* x */, 0 /* y */, m_width, m_height); 475 GLU_EXPECT_NO_ERROR(gl.getError(), "Viewport"); 476 477 /* Prepare blank VAO */ 478 gl.genVertexArrays(1, &m_vao_id); 479 GLU_EXPECT_NO_ERROR(gl.getError(), "genVertexArrays"); 480 481 gl.bindVertexArray(m_vao_id); 482 GLU_EXPECT_NO_ERROR(gl.getError(), "bindVertexArray"); 483 } 484 485 /** Verifies if image is filled with <color> 486 * 487 * @param color Color to be checked 488 * @param is_expected Selects if image is expected to be filled with given color or not 489 **/ 490 void GPUShader5ImplicitConversionsTest::verifyImage(glw::GLuint color, bool is_expected) const 491 { 492 /* */ 493 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 494 495 /* Storage for image data */ 496 glw::GLuint result_image[m_width * m_height]; 497 498 /* Get image data */ 499 gl.getTexImage(GL_TEXTURE_2D, 0 /* level */, GL_RGBA, GL_UNSIGNED_BYTE, result_image); 500 GLU_EXPECT_NO_ERROR(gl.getError(), "getTexImage"); 501 502 /* Inspect data */ 503 if (true == is_expected) 504 { 505 for (size_t i = 0; i < m_width * m_height; ++i) 506 { 507 const glw::GLuint pixel_data = result_image[i]; 508 509 if (color != pixel_data) 510 { 511 TCU_FAIL("Found invalid pixel during verification of drawn image"); 512 } 513 } 514 } 515 else 516 { 517 for (size_t i = 0; i < m_width * m_height; ++i) 518 { 519 const glw::GLuint pixel_data = result_image[i]; 520 521 if (color == pixel_data) 522 { 523 TCU_FAIL("Found invalid pixel during verification of drawn image"); 524 } 525 } 526 } 527 } 528 529 /** Executes test case 530 * 531 * @param test_case Defines test case parameters 532 */ 533 void GPUShader5ImplicitConversionsTest::executeTestCase(const testCase& test_case) 534 { 535 static const glw::GLuint white_color = 0xffffffff; 536 537 /* */ 538 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 539 540 /* Run test case */ 541 { 542 /* Get shaders */ 543 const std::string& fs = getFragmentShader(); 544 const std::string& vs = getVertexShader(test_case.m_destination_type, test_case.m_source_type); 545 546 /* Prepare program */ 547 Utils::programInfo program(m_context); 548 549 program.build(fs.c_str(), vs.c_str()); 550 551 gl.useProgram(program.m_program_object_id); 552 GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram"); 553 554 program.setUniform(test_case.m_source_variable_type, "u1", test_case.m_u1_data); 555 program.setUniform(test_case.m_source_variable_type, "u2", test_case.m_u2_data); 556 557 /* Clear FBO */ 558 gl.clearColor(0.5f, 0.5f, 0.5f, 0.5f); 559 GLU_EXPECT_NO_ERROR(gl.getError(), "clearColor"); 560 561 gl.clear(GL_COLOR_BUFFER_BIT); 562 GLU_EXPECT_NO_ERROR(gl.getError(), "clear"); 563 564 /* Draw a triangle strip */ 565 gl.drawArrays(GL_TRIANGLE_STRIP, 0 /* first */, 4 /* count */); 566 GLU_EXPECT_NO_ERROR(gl.getError(), "drawArrays"); 567 } 568 569 /* Verification */ 570 verifyImage(white_color, test_case.m_is_white_expected); 571 } 572 573 /** Get vertex shader source. 574 * 575 * @param destination_type Name of type 576 * @param source_type Name of type 577 * 578 * @return String with source of shader 579 */ 580 std::string GPUShader5ImplicitConversionsTest::getVertexShader(const glw::GLchar* destination_type, 581 const glw::GLchar* source_type) 582 { 583 /* Vertex shader template */ 584 const char* vs_body_template = "#version 150\n" 585 "#extension GL_ARB_gpu_shader5 : require\n" 586 "\n" 587 "uniform SOURCE_TYPE u1;\n" 588 "uniform SOURCE_TYPE u2;\n" 589 "\n" 590 "out vec4 result;\n" 591 "\n" 592 "void main()\n" 593 "{\n" 594 " DESTINATION_TYPE v = ZERO;\n" 595 "\n" 596 " v = DESTINATION_TYPE(u2) - u1;\n" 597 "\n" 598 " result = vec4(0.0, 0.0, 0.0, 0.0);\n" 599 " if (ZERO == v)\n" 600 " {\n" 601 " result = vec4(1.0, 1.0, 1.0, 1.0);\n" 602 " }\n" 603 "\n" 604 " switch (gl_VertexID)\n" 605 " {\n" 606 " case 0: gl_Position = vec4(-1.0, 1.0, 0.0, 1.0); break; \n" 607 " case 1: gl_Position = vec4( 1.0, 1.0, 0.0, 1.0); break; \n" 608 " case 2: gl_Position = vec4(-1.0,-1.0, 0.0, 1.0); break; \n" 609 " case 3: gl_Position = vec4( 1.0,-1.0, 0.0, 1.0); break; \n" 610 " }\n" 611 "}\n" 612 "\n"; 613 614 std::string vs_body = vs_body_template; 615 616 /* Tokens */ 617 size_t search_position = 0; 618 619 Utils::replaceToken("SOURCE_TYPE", search_position, source_type, vs_body); 620 Utils::replaceToken("SOURCE_TYPE", search_position, source_type, vs_body); 621 622 search_position = 0; 623 Utils::replaceToken("DESTINATION_TYPE", search_position, destination_type, vs_body); 624 Utils::replaceToken("DESTINATION_TYPE", search_position, destination_type, vs_body); 625 626 search_position = 0; 627 if (!strcmp(destination_type, "int") || !strcmp(destination_type, "uint")) 628 { 629 Utils::replaceToken("ZERO", search_position, "0", vs_body); 630 Utils::replaceToken("ZERO", search_position, "0", vs_body); 631 } 632 else if (!strcmp(destination_type, "float")) 633 { 634 Utils::replaceToken("ZERO", search_position, "0.0", vs_body); 635 Utils::replaceToken("ZERO", search_position, "0.0", vs_body); 636 } 637 else if (!strcmp(destination_type, "ivec2")) 638 { 639 Utils::replaceToken("ZERO", search_position, "ivec2(0,0)", vs_body); 640 Utils::replaceToken("ZERO", search_position, "ivec2(0,0)", vs_body); 641 } 642 else if (!strcmp(destination_type, "ivec3")) 643 { 644 Utils::replaceToken("ZERO", search_position, "ivec3(0,0,0)", vs_body); 645 Utils::replaceToken("ZERO", search_position, "ivec3(0,0,0)", vs_body); 646 } 647 else if (!strcmp(destination_type, "ivec4")) 648 { 649 Utils::replaceToken("ZERO", search_position, "ivec4(0,0,0,0)", vs_body); 650 Utils::replaceToken("ZERO", search_position, "ivec4(0,0,0,0)", vs_body); 651 } 652 else if (!strcmp(destination_type, "uvec2")) 653 { 654 Utils::replaceToken("ZERO", search_position, "uvec2(0,0)", vs_body); 655 Utils::replaceToken("ZERO", search_position, "uvec2(0,0)", vs_body); 656 } 657 else if (!strcmp(destination_type, "uvec3")) 658 { 659 Utils::replaceToken("ZERO", search_position, "uvec3(0,0,0)", vs_body); 660 Utils::replaceToken("ZERO", search_position, "uvec3(0,0,0)", vs_body); 661 } 662 else if (!strcmp(destination_type, "uvec4")) 663 { 664 Utils::replaceToken("ZERO", search_position, "uvec4(0,0,0,0)", vs_body); 665 Utils::replaceToken("ZERO", search_position, "uvec4(0,0,0,0)", vs_body); 666 } 667 else if (!strcmp(destination_type, "vec2")) 668 { 669 Utils::replaceToken("ZERO", search_position, "vec2(0,0)", vs_body); 670 Utils::replaceToken("ZERO", search_position, "vec2(0,0)", vs_body); 671 } 672 else if (!strcmp(destination_type, "vec3")) 673 { 674 Utils::replaceToken("ZERO", search_position, "vec3(0,0,0)", vs_body); 675 Utils::replaceToken("ZERO", search_position, "vec3(0,0,0)", vs_body); 676 } 677 else if (!strcmp(destination_type, "vec4")) 678 { 679 Utils::replaceToken("ZERO", search_position, "vec4(0,0,0,0)", vs_body); 680 Utils::replaceToken("ZERO", search_position, "vec4(0,0,0,0)", vs_body); 681 } 682 683 return vs_body; 684 } 685 686 /** Get fragment shader source. 687 * 688 * @return String with source of shader 689 */ 690 std::string GPUShader5ImplicitConversionsTest::getFragmentShader() 691 { 692 const char* fs_body_template = "#version 150\n" 693 "\n" 694 "in vec4 result;\n" 695 "out vec4 color;\n" 696 "\n" 697 "void main()\n" 698 "{\n" 699 " color = result;\n" 700 "}\n" 701 "\n"; 702 703 std::string fs_body = fs_body_template; 704 705 return fs_body; 706 } 707 708 /** Constructor. 709 * 710 * @param context Rendering context. 711 * 712 **/ 713 GPUShader5FunctionOverloadingTest::GPUShader5FunctionOverloadingTest(deqp::Context& context) 714 : GPUShader5ImplicitConversionsTest(context, "function_overloading", 715 "Verifies that function overloading is accepted") 716 { 717 /* Left blank intentionally */ 718 } 719 720 /** Executes test iteration. 721 * 722 * @return Returns STOP. 723 */ 724 tcu::TestNode::IterateResult GPUShader5FunctionOverloadingTest::iterate() 725 { 726 /* Defines data used as u1 and u2 uniforms */ 727 static const glw::GLint u1_data_1[4] = { (glw::GLint)0xffff0000, 0x0000ffff, 0x00ffffff, (glw::GLint)0xffffffff }; 728 static const glw::GLint u1_data_2[4] = { -112, 1122, -111222, 1222111222 }; 729 static const glw::GLuint u2_data_1[4] = { 0xffff0000, 0x0000ffff, 0x00ffffff, 0xffffffff }; 730 static const glw::GLuint u2_data_2[4] = { 0xfff70000, 0x00007fff, 0x007fffff, 0xfffffff7 }; 731 732 testInit(); 733 734 /* Execute test case */ 735 execute(u1_data_1, u2_data_1, true); 736 execute(u1_data_2, u2_data_2, false); 737 738 /* Set result - exceptions are thrown in case of any error */ 739 m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); 740 741 /* Done */ 742 return STOP; 743 } 744 745 /** Executes test case 746 * 747 * @param u1_data Pointer to data that will used as u1 uniform 748 * @param u2_data Pointer to data that will used as u2 uniform 749 * @param test_case Defines test case parameters 750 */ 751 void GPUShader5FunctionOverloadingTest::execute(const glw::GLint* u1_data, const glw::GLuint* u2_data, 752 bool is_black_expected) 753 { 754 static const glw::GLuint black_color = 0x00000000; 755 756 /* */ 757 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 758 759 /* Run test case */ 760 { 761 /* Shaders */ 762 const char* fs = "#version 150\n" 763 "\n" 764 "in vec4 result;\n" 765 "out vec4 color;\n" 766 "\n" 767 "void main()\n" 768 "{\n" 769 " color = result;\n" 770 "}\n" 771 "\n"; 772 773 const char* vs = "#version 150\n" 774 "#extension GL_ARB_gpu_shader5 : require\n" 775 "\n" 776 "uniform ivec4 u1;\n" 777 "uniform uvec4 u2;\n" 778 "\n" 779 "out vec4 result;\n" 780 "\n" 781 "vec4 f(in vec4 a, in vec4 b)\n" 782 "{\n" 783 " return a * b;\n" 784 "}\n" 785 "\n" 786 "vec4 f(in uvec4 a, in uvec4 b)\n" 787 "{\n" 788 " return vec4(a - b);\n" 789 "}\n" 790 "\n" 791 "void main()\n" 792 "{\n" 793 " result = f(u1, u2);\n" 794 "\n" 795 " switch (gl_VertexID)\n" 796 " {\n" 797 " case 0: gl_Position = vec4(-1.0, 1.0, 0.0, 1.0); break; \n" 798 " case 1: gl_Position = vec4( 1.0, 1.0, 0.0, 1.0); break; \n" 799 " case 2: gl_Position = vec4(-1.0,-1.0, 0.0, 1.0); break; \n" 800 " case 3: gl_Position = vec4( 1.0,-1.0, 0.0, 1.0); break; \n" 801 " }\n" 802 "}\n" 803 "\n"; 804 805 /* Prepare program */ 806 Utils::programInfo program(m_context); 807 808 program.build(fs, vs); 809 810 gl.useProgram(program.m_program_object_id); 811 GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram"); 812 813 program.setUniform(Utils::VARIABLE_TYPE_IVEC4, "u1", u1_data); 814 program.setUniform(Utils::VARIABLE_TYPE_UVEC4, "u2", u2_data); 815 816 /* Clear FBO */ 817 gl.clearColor(0.5f, 0.5f, 0.5f, 0.5f); 818 GLU_EXPECT_NO_ERROR(gl.getError(), "clearColor"); 819 820 gl.clear(GL_COLOR_BUFFER_BIT); 821 GLU_EXPECT_NO_ERROR(gl.getError(), "clear"); 822 823 /* Draw a triangle strip */ 824 gl.drawArrays(GL_TRIANGLE_STRIP, 0 /* first */, 4 /* count */); 825 GLU_EXPECT_NO_ERROR(gl.getError(), "drawArrays"); 826 } 827 828 /* Verification */ 829 verifyImage(black_color, is_black_expected); 830 } 831 832 /** Constructor. 833 * 834 * @param context Rendering context. 835 * 836 **/ 837 GPUShader5FloatEncodingTest::GPUShader5FloatEncodingTest(deqp::Context& context) 838 : GPUShader5ImplicitConversionsTest(context, "float_encoding", 839 "Verifies that functions encoding floats as bits work as expected") 840 { 841 /* Left blank intentionally */ 842 } 843 844 /** Executes test iteration. 845 * 846 * @return Returns STOP. 847 */ 848 tcu::TestNode::IterateResult GPUShader5FloatEncodingTest::iterate() 849 { 850 /* Defines data used as u1 and u2 uniforms */ 851 static const glw::GLfloat floats[4] = { -1.0f, -1234.0f, 1.0f, 1234.0f }; 852 static const glw::GLint ints[4] = { -1, -1234, 1, 1234 }; 853 static const glw::GLuint uints[4] = { 0xffffffff, 0xfffffb2e, 1, 0x4d2 }; 854 855 /* Defines tested cases */ 856 static const testCase test_cases[] = { 857 { /* float >> int - invalid */ 858 { Utils::VARIABLE_TYPE_INT, "int", ints }, 859 { Utils::VARIABLE_TYPE_FLOAT, "float", floats }, 860 "floatBitsToInt", 861 false }, 862 { /* float >> int - valid */ 863 { Utils::VARIABLE_TYPE_INT, "int", floats }, 864 { Utils::VARIABLE_TYPE_FLOAT, "float", floats }, 865 "floatBitsToInt", 866 true }, 867 { /* vec2 >> ivec2 - invalid */ 868 { Utils::VARIABLE_TYPE_IVEC2, "ivec2", ints }, 869 { Utils::VARIABLE_TYPE_VEC2, "vec2", floats }, 870 "floatBitsToInt", 871 false }, 872 { /* vec2 >> ivec2 - valid */ 873 { Utils::VARIABLE_TYPE_IVEC2, "ivec2", floats }, 874 { Utils::VARIABLE_TYPE_VEC2, "vec2", floats }, 875 "floatBitsToInt", 876 true }, 877 { /* vec3 >> ivec3 - invalid */ 878 { Utils::VARIABLE_TYPE_IVEC3, "ivec3", ints }, 879 { Utils::VARIABLE_TYPE_VEC3, "vec3", floats }, 880 "floatBitsToInt", 881 false }, 882 { /* vec3 >> ivec3 - valid */ 883 { Utils::VARIABLE_TYPE_IVEC3, "ivec3", floats }, 884 { Utils::VARIABLE_TYPE_VEC3, "vec3", floats }, 885 "floatBitsToInt", 886 true }, 887 { /* vec4 >> ivec4 - invalid */ 888 { Utils::VARIABLE_TYPE_IVEC4, "ivec4", ints }, 889 { Utils::VARIABLE_TYPE_VEC4, "vec4", floats }, 890 "floatBitsToInt", 891 false }, 892 { /* vec4 >> ivec4 - valid */ 893 { Utils::VARIABLE_TYPE_IVEC4, "ivec4", floats }, 894 { Utils::VARIABLE_TYPE_VEC4, "vec4", floats }, 895 "floatBitsToInt", 896 true }, 897 { /* float >> uint - invalid */ 898 { Utils::VARIABLE_TYPE_UINT, "uint", uints }, 899 { Utils::VARIABLE_TYPE_FLOAT, "float", floats }, 900 "floatBitsToUint", 901 false }, 902 { /* float >> uint - valid */ 903 { Utils::VARIABLE_TYPE_UINT, "uint", floats }, 904 { Utils::VARIABLE_TYPE_FLOAT, "float", floats }, 905 "floatBitsToUint", 906 true }, 907 { /* vec2 >> uvec2 - invalid */ 908 { Utils::VARIABLE_TYPE_UVEC2, "uvec2", uints }, 909 { Utils::VARIABLE_TYPE_VEC2, "vec2", floats }, 910 "floatBitsToUint", 911 false }, 912 { /* vec2 >> uvec2 - valid */ 913 { Utils::VARIABLE_TYPE_UVEC2, "uvec2", floats }, 914 { Utils::VARIABLE_TYPE_VEC2, "vec2", floats }, 915 "floatBitsToUint", 916 true }, 917 { /* vec3 >> uvec3 - invalid */ 918 { Utils::VARIABLE_TYPE_UVEC3, "uvec3", uints }, 919 { Utils::VARIABLE_TYPE_VEC3, "vec3", floats }, 920 "floatBitsToUint", 921 false }, 922 { /* vec3 >> uvec3 - valid */ 923 { Utils::VARIABLE_TYPE_UVEC3, "uvec3", floats }, 924 { Utils::VARIABLE_TYPE_VEC3, "vec3", floats }, 925 "floatBitsToUint", 926 true }, 927 { /* vec4 >> ivec4 - invalid */ 928 { Utils::VARIABLE_TYPE_UVEC4, "uvec4", uints }, 929 { Utils::VARIABLE_TYPE_VEC4, "vec4", floats }, 930 "floatBitsToUint", 931 false }, 932 { /* vec4 >> uvec4 - valid */ 933 { Utils::VARIABLE_TYPE_UVEC4, "uvec4", floats }, 934 { Utils::VARIABLE_TYPE_VEC4, "vec4", floats }, 935 "floatBitsToUint", 936 true }, 937 { /* int >> float - invalid */ 938 { Utils::VARIABLE_TYPE_FLOAT, "float", floats }, 939 { Utils::VARIABLE_TYPE_INT, "int", ints }, 940 "intBitsToFloat", 941 false }, 942 { /* int >> float - valid */ 943 { Utils::VARIABLE_TYPE_FLOAT, "float", floats }, 944 { Utils::VARIABLE_TYPE_INT, "int", floats }, 945 "intBitsToFloat", 946 true }, 947 { /* ivec2 >> vec2 - invalid */ 948 { Utils::VARIABLE_TYPE_VEC2, "vec2", floats }, 949 { Utils::VARIABLE_TYPE_IVEC2, "ivec2", ints }, 950 "intBitsToFloat", 951 false }, 952 { /* ivec2 >> vec2 - valid */ 953 { Utils::VARIABLE_TYPE_VEC2, "vec2", floats }, 954 { Utils::VARIABLE_TYPE_IVEC2, "ivec2", floats }, 955 "intBitsToFloat", 956 true }, 957 { /* ivec3 >> vec3 - invalid */ 958 { Utils::VARIABLE_TYPE_VEC3, "vec3", floats }, 959 { Utils::VARIABLE_TYPE_IVEC3, "ivec3", ints }, 960 "intBitsToFloat", 961 false }, 962 { /* ivec3 >> vec3 - valid */ 963 { Utils::VARIABLE_TYPE_VEC3, "vec3", floats }, 964 { Utils::VARIABLE_TYPE_IVEC3, "ivec3", floats }, 965 "intBitsToFloat", 966 true }, 967 { /* ivec4 >> vec4 - invalid */ 968 { Utils::VARIABLE_TYPE_VEC4, "vec4", floats }, 969 { Utils::VARIABLE_TYPE_IVEC4, "ivec4", ints }, 970 "intBitsToFloat", 971 false }, 972 { /* ivec4 >> vec4 - valid */ 973 { Utils::VARIABLE_TYPE_VEC4, "vec4", floats }, 974 { Utils::VARIABLE_TYPE_IVEC4, "ivec4", floats }, 975 "intBitsToFloat", 976 true }, 977 { /* uint >> float - invalid */ 978 { Utils::VARIABLE_TYPE_FLOAT, "float", floats }, 979 { Utils::VARIABLE_TYPE_UINT, "uint", uints }, 980 "uintBitsToFloat", 981 false }, 982 { /* uint >> float - valid */ 983 { Utils::VARIABLE_TYPE_FLOAT, "float", floats }, 984 { Utils::VARIABLE_TYPE_UINT, "uint", floats }, 985 "uintBitsToFloat", 986 true }, 987 { /* uvec2 >> vec2 - invalid */ 988 { Utils::VARIABLE_TYPE_VEC2, "vec2", floats }, 989 { Utils::VARIABLE_TYPE_UVEC2, "uvec2", uints }, 990 "uintBitsToFloat", 991 false }, 992 { /* uvec2 >> vec2 - valid */ 993 { Utils::VARIABLE_TYPE_VEC2, "vec2", floats }, 994 { Utils::VARIABLE_TYPE_UVEC2, "uvec2", floats }, 995 "uintBitsToFloat", 996 true }, 997 { /* uvec3 >> vec3 - invalid */ 998 { Utils::VARIABLE_TYPE_VEC3, "vec3", floats }, 999 { Utils::VARIABLE_TYPE_UVEC3, "uvec3", uints }, 1000 "uintBitsToFloat", 1001 false }, 1002 { /* uvec3 >> vec3 - valid */ 1003 { Utils::VARIABLE_TYPE_VEC3, "vec3", floats }, 1004 { Utils::VARIABLE_TYPE_UVEC3, "uvec3", floats }, 1005 "uintBitsToFloat", 1006 true }, 1007 { /* uvec4 >> vec4 - invalid */ 1008 { Utils::VARIABLE_TYPE_VEC4, "vec4", floats }, 1009 { Utils::VARIABLE_TYPE_UVEC4, "uvec4", uints }, 1010 "uintBitsToFloat", 1011 false }, 1012 { /* uvec4 >> vec4 - valid */ 1013 { Utils::VARIABLE_TYPE_VEC4, "vec4", floats }, 1014 { Utils::VARIABLE_TYPE_UVEC4, "uvec4", floats }, 1015 "uintBitsToFloat", 1016 true }, 1017 }; 1018 static const size_t n_test_cases = sizeof(test_cases) / sizeof(test_cases[0]); 1019 1020 testInit(); 1021 1022 /* Execute test case */ 1023 for (size_t i = 0; i < n_test_cases; ++i) 1024 { 1025 const testCase& test_case = test_cases[i]; 1026 1027 execute(test_case); 1028 } 1029 1030 /* Set result - exceptions are thrown in case of any error */ 1031 m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Pass"); 1032 1033 /* Done */ 1034 return STOP; 1035 } 1036 1037 /** Executes test case 1038 * 1039 * @param test_case Tested case 1040 * 1041 * @param test_case Defines test case parameters 1042 */ 1043 void GPUShader5FloatEncodingTest::execute(const testCase& test_case) 1044 { 1045 static const glw::GLuint white_color = 0xffffffff; 1046 1047 /* */ 1048 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 1049 1050 /* Run test case */ 1051 { 1052 /* Shaders */ 1053 const char* fs = "#version 150\n" 1054 "\n" 1055 "in vec4 result;\n" 1056 "out vec4 color;\n" 1057 "\n" 1058 "void main()\n" 1059 "{\n" 1060 " color = result;\n" 1061 "}\n" 1062 "\n"; 1063 1064 const std::string& vs = getVertexShader(test_case); 1065 1066 /* Prepare program */ 1067 Utils::programInfo program(m_context); 1068 1069 program.build(fs, vs.c_str()); 1070 1071 gl.useProgram(program.m_program_object_id); 1072 GLU_EXPECT_NO_ERROR(gl.getError(), "UseProgram"); 1073 1074 program.setUniform(test_case.m_expected_value.m_type, "expected_value", test_case.m_expected_value.m_data); 1075 program.setUniform(test_case.m_value.m_type, "value", test_case.m_value.m_data); 1076 1077 /* Clear FBO */ 1078 gl.clearColor(0.5f, 0.5f, 0.5f, 0.5f); 1079 GLU_EXPECT_NO_ERROR(gl.getError(), "clearColor"); 1080 1081 gl.clear(GL_COLOR_BUFFER_BIT); 1082 GLU_EXPECT_NO_ERROR(gl.getError(), "clear"); 1083 1084 /* Draw a triangle strip */ 1085 gl.drawArrays(GL_TRIANGLE_STRIP, 0 /* first */, 4 /* count */); 1086 GLU_EXPECT_NO_ERROR(gl.getError(), "drawArrays"); 1087 } 1088 1089 /* Verification */ 1090 verifyImage(white_color, test_case.m_is_white_expected); 1091 } 1092 1093 /** Get vertex shader source. 1094 * 1095 * @param test_case Tested case 1096 * 1097 * @return String with source of shader 1098 */ 1099 std::string GPUShader5FloatEncodingTest::getVertexShader(const testCase& test_case) const 1100 { 1101 /* Vertex shader template */ 1102 const char* vs_body_template = "#version 150\n" 1103 "#extension GL_ARB_gpu_shader5 : require\n" 1104 "\n" 1105 "uniform EXPECTED_VALUE_TYPE expected_value;\n" 1106 "uniform VALUE_TYPE value;\n" 1107 "\n" 1108 "out vec4 result;\n" 1109 "\n" 1110 "void main()\n" 1111 "{\n" 1112 " result = vec4(1.0, 1.0, 1.0, 1.0);\n" 1113 "\n" 1114 " EXPECTED_VALUE_TYPE ret_val = TESTED_FUNCTION(value);\n" 1115 "\n" 1116 " if (expected_value != ret_val)\n" 1117 " {\n" 1118 " result = vec4(0.0, 0.0, 0.0, 0.0);\n" 1119 " }\n" 1120 "\n" 1121 " switch (gl_VertexID)\n" 1122 " {\n" 1123 " case 0: gl_Position = vec4(-1.0, 1.0, 0.0, 1.0); break; \n" 1124 " case 1: gl_Position = vec4( 1.0, 1.0, 0.0, 1.0); break; \n" 1125 " case 2: gl_Position = vec4(-1.0,-1.0, 0.0, 1.0); break; \n" 1126 " case 3: gl_Position = vec4( 1.0,-1.0, 0.0, 1.0); break; \n" 1127 " }\n" 1128 "}\n" 1129 "\n"; 1130 1131 std::string vs_body = vs_body_template; 1132 1133 /* Tokens */ 1134 size_t search_position = 0; 1135 1136 Utils::replaceToken("EXPECTED_VALUE_TYPE", search_position, test_case.m_expected_value.m_type_name, vs_body); 1137 Utils::replaceToken("VALUE_TYPE", search_position, test_case.m_value.m_type_name, vs_body); 1138 Utils::replaceToken("EXPECTED_VALUE_TYPE", search_position, test_case.m_expected_value.m_type_name, vs_body); 1139 Utils::replaceToken("TESTED_FUNCTION", search_position, test_case.m_function_name, vs_body); 1140 1141 return vs_body; 1142 } 1143 1144 /** Constructor. 1145 * 1146 * @param context Rendering context. 1147 **/ 1148 GPUShader5Tests::GPUShader5Tests(deqp::Context& context) 1149 : TestCaseGroup(context, "gpu_shader5_gl", "Verifies \"gpu_shader5\" functionality") 1150 { 1151 /* Left blank on purpose */ 1152 } 1153 1154 /** Initializes a texture_storage_multisample test group. 1155 * 1156 **/ 1157 void GPUShader5Tests::init(void) 1158 { 1159 addChild(new GPUShader5ImplicitConversionsTest(m_context)); 1160 addChild(new GPUShader5FunctionOverloadingTest(m_context)); 1161 addChild(new GPUShader5FloatEncodingTest(m_context)); 1162 } 1163 } /* glcts namespace */ 1164