1 /*------------------------------------------------------------------------- 2 * OpenGL Conformance Test Suite 3 * ----------------------------- 4 * 5 * Copyright (c) 2014 Intel Corporation 6 * Copyright (c) 2016 The Khronos Group Inc. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 */ /*! 21 * \file 22 * \brief 23 */ /*-------------------------------------------------------------------*/ 24 25 #include "glcShaderIntegerMixTests.hpp" 26 #include "deMath.h" 27 #include "deRandom.hpp" 28 #include "deString.h" 29 #include "deStringUtil.hpp" 30 #include "gluContextInfo.hpp" 31 #include "gluDrawUtil.hpp" 32 #include "gluPixelTransfer.hpp" 33 #include "gluShaderProgram.hpp" 34 #include "glw.h" 35 #include "glwFunctions.hpp" 36 #include "tcuCommandLine.hpp" 37 #include "tcuStringTemplate.hpp" 38 #include "tcuSurface.hpp" 39 #include "tcuTestLog.hpp" 40 41 namespace deqp 42 { 43 44 using tcu::TestLog; 45 46 class ShaderIntegerMixCase : public TestCase 47 { 48 public: 49 ShaderIntegerMixCase(Context& context, const char* name, const char* description, glu::GLSLVersion glslVersion) 50 : TestCase(context, name, description), m_glslVersion(glslVersion) 51 { 52 } 53 54 ~ShaderIntegerMixCase() 55 { 56 // empty 57 } 58 59 IterateResult iterate() 60 { 61 qpTestResult result = test(); 62 63 m_testCtx.setTestResult(result, qpGetTestResultName(result)); 64 65 return STOP; 66 } 67 68 protected: 69 glu::GLSLVersion m_glslVersion; 70 71 virtual qpTestResult test() = 0; 72 }; 73 74 class ShaderIntegerMixDefineCase : public ShaderIntegerMixCase 75 { 76 public: 77 ShaderIntegerMixDefineCase(Context& context, const char* name, const char* description, 78 glu::GLSLVersion glslVersion) 79 : ShaderIntegerMixCase(context, name, description, glslVersion) 80 { 81 // empty 82 } 83 84 ~ShaderIntegerMixDefineCase() 85 { 86 // empty 87 } 88 89 protected: 90 virtual qpTestResult test() 91 { 92 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 93 bool pass = true; 94 95 static const char source_template[] = "${VERSION_DECL}\n" 96 "#extension GL_EXT_shader_integer_mix: require\n" 97 "\n" 98 "#if !defined GL_EXT_shader_integer_mix\n" 99 "# error GL_EXT_shader_integer_mix is not defined\n" 100 "#elif GL_EXT_shader_integer_mix != 1\n" 101 "# error GL_EXT_shader_integer_mix is not equal to 1\n" 102 "#endif\n" 103 "\n" 104 "void main(void) { ${BODY} }\n"; 105 106 static const struct 107 { 108 GLenum target; 109 const char* body; 110 } shader_targets[] = { 111 { GL_VERTEX_SHADER, "gl_Position = vec4(0);" }, { GL_FRAGMENT_SHADER, "" }, 112 }; 113 114 const glu::GLSLVersion v = glslVersionIsES(m_glslVersion) ? glu::GLSL_VERSION_300_ES : glu::GLSL_VERSION_330; 115 116 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_integer_mix")) 117 return QP_TEST_RESULT_NOT_SUPPORTED; 118 119 for (int i = 0; i < DE_LENGTH_OF_ARRAY(shader_targets); i++) 120 { 121 std::map<std::string, std::string> args; 122 123 args["VERSION_DECL"] = glu::getGLSLVersionDeclaration(v); 124 args["BODY"] = shader_targets[i].body; 125 126 std::string code = tcu::StringTemplate(source_template).specialize(args); 127 128 GLuint shader = gl.createShader(shader_targets[i].target); 129 char const* strings[1] = { code.c_str() }; 130 gl.shaderSource(shader, 1, strings, 0); 131 gl.compileShader(shader); 132 133 GLint compileSuccess = 0; 134 gl.getShaderiv(shader, GL_COMPILE_STATUS, &compileSuccess); 135 gl.deleteShader(shader); 136 137 if (!compileSuccess) 138 pass = false; 139 } 140 141 return pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL; 142 } 143 }; 144 145 class ShaderIntegerMixPrototypesCase : public ShaderIntegerMixCase 146 { 147 public: 148 ShaderIntegerMixPrototypesCase(Context& context, const char* name, const char* description, 149 glu::GLSLVersion glslVersion, bool _use_extension, bool _is_negative_testing) 150 : ShaderIntegerMixCase(context, name, description, glslVersion) 151 , use_extension(_use_extension) 152 , is_negative_testing(_is_negative_testing) 153 { 154 // empty 155 } 156 157 ~ShaderIntegerMixPrototypesCase() 158 { 159 // empty 160 } 161 162 protected: 163 bool use_extension; 164 bool is_negative_testing; 165 166 virtual qpTestResult test() 167 { 168 TestLog& log = m_testCtx.getLog(); 169 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 170 bool pass = true; 171 172 static const char source_template[] = "${VERSION_DECL}\n" 173 "${EXTENSION_ENABLE}\n" 174 "\n" 175 "void main()\n" 176 "{\n" 177 " mix(ivec2(1), ivec2(2), bvec2(true));\n" 178 " mix(ivec3(1), ivec3(2), bvec3(true));\n" 179 " mix(ivec4(1), ivec4(2), bvec4(true));\n" 180 " mix(uvec2(1), uvec2(2), bvec2(true));\n" 181 " mix(uvec3(1), uvec3(2), bvec3(true));\n" 182 " mix(uvec4(1), uvec4(2), bvec4(true));\n" 183 " mix(bvec2(1), bvec2(0), bvec2(true));\n" 184 " mix(bvec3(1), bvec3(0), bvec3(true));\n" 185 " mix(bvec4(1), bvec4(0), bvec4(true));\n" 186 " ${BODY}\n" 187 "}\n"; 188 189 static const struct 190 { 191 GLenum target; 192 const char* body; 193 } shader_targets[] = { 194 { GL_VERTEX_SHADER, "gl_Position = vec4(0);" }, { GL_FRAGMENT_SHADER, "" }, 195 }; 196 197 glu::GLSLVersion v; 198 const char* extension_enable; 199 200 if (use_extension) 201 { 202 v = glslVersionIsES(m_glslVersion) ? glu::GLSL_VERSION_300_ES : glu::GLSL_VERSION_330; 203 extension_enable = "#extension GL_EXT_shader_integer_mix: enable"; 204 205 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_integer_mix")) 206 return QP_TEST_RESULT_NOT_SUPPORTED; 207 } 208 else if (is_negative_testing) 209 { 210 v = glslVersionIsES(m_glslVersion) ? glu::GLSL_VERSION_300_ES : glu::GLSL_VERSION_330; 211 extension_enable = ""; 212 } 213 else 214 { 215 v = m_glslVersion; 216 extension_enable = ""; 217 if (glslVersionIsES(m_glslVersion)) 218 { 219 if (m_glslVersion < glu::GLSL_VERSION_310_ES) 220 return QP_TEST_RESULT_NOT_SUPPORTED; 221 } 222 else 223 { 224 if (m_glslVersion < glu::GLSL_VERSION_450) 225 return QP_TEST_RESULT_NOT_SUPPORTED; 226 } 227 } 228 229 for (int i = 0; i < DE_LENGTH_OF_ARRAY(shader_targets); i++) 230 { 231 std::map<std::string, std::string> args; 232 233 args["VERSION_DECL"] = glu::getGLSLVersionDeclaration(v); 234 args["EXTENSION_ENABLE"] = extension_enable; 235 args["BODY"] = shader_targets[i].body; 236 237 std::string code = tcu::StringTemplate(source_template).specialize(args); 238 239 GLuint shader = gl.createShader(shader_targets[i].target); 240 char const* strings[1] = { code.c_str() }; 241 gl.shaderSource(shader, 1, strings, 0); 242 gl.compileShader(shader); 243 244 GLint compileSuccess = 0; 245 gl.getShaderiv(shader, GL_COMPILE_STATUS, &compileSuccess); 246 247 if (is_negative_testing) 248 { 249 if (compileSuccess) 250 { 251 TCU_FAIL("The shader compilation was expected to fail, but it was successful."); 252 pass = false; 253 } 254 } 255 else if (!compileSuccess) 256 { 257 GLchar infoLog[1000]; 258 259 gl.getShaderInfoLog(shader, sizeof(infoLog), NULL, infoLog); 260 log.writeKernelSource(strings[0]); 261 log.writeCompileInfo("shader", "", false, infoLog); 262 263 pass = false; 264 } 265 266 gl.deleteShader(shader); 267 } 268 269 return pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL; 270 } 271 }; 272 273 class ShaderIntegerMixRenderCase : public ShaderIntegerMixCase 274 { 275 public: 276 ShaderIntegerMixRenderCase(Context& context, const char* name, const char* description, 277 glu::GLSLVersion glslVersion, const char* _type) 278 : ShaderIntegerMixCase(context, name, description, glslVersion), type(_type) 279 { 280 // empty 281 } 282 283 ~ShaderIntegerMixRenderCase() 284 { 285 // empty 286 } 287 288 protected: 289 // Type used for mix() parameters in this test case. 290 const char* type; 291 292 static const unsigned width = 8 * 8; 293 static const unsigned height = 8 * 8; 294 295 virtual qpTestResult test() 296 { 297 static const char vs_template[] = "${VERSION_DECL}\n" 298 "${EXTENSION_ENABLE}\n" 299 "\n" 300 "in vec2 vertex;\n" 301 "in ivec4 vs_in_a;\n" 302 "in ivec4 vs_in_b;\n" 303 "in ivec4 vs_in_sel;\n" 304 "\n" 305 "flat out ivec4 fs_in_a;\n" 306 "flat out ivec4 fs_in_b;\n" 307 "flat out ivec4 fs_in_sel;\n" 308 "flat out ivec4 fs_in_result;\n" 309 "\n" 310 "void main()\n" 311 "{\n" 312 " fs_in_a = vs_in_a;\n" 313 " fs_in_b = vs_in_b;\n" 314 " fs_in_sel = vs_in_sel;\n" 315 "\n" 316 " ${TYPE} a = ${TYPE}(vs_in_a);\n" 317 " ${TYPE} b = ${TYPE}(vs_in_b);\n" 318 " bvec4 sel = bvec4(vs_in_sel);\n" 319 " fs_in_result = ivec4(mix(a, b, sel));\n" 320 "\n" 321 " gl_Position = vec4(vertex, 0, 1);\n" 322 " gl_PointSize = 4.;\n" 323 "}\n"; 324 325 static const char fs_template[] = "${VERSION_DECL}\n" 326 "${EXTENSION_ENABLE}\n" 327 "\n" 328 "out ivec4 o;\n" 329 "\n" 330 "flat in ivec4 fs_in_a;\n" 331 "flat in ivec4 fs_in_b;\n" 332 "flat in ivec4 fs_in_sel;\n" 333 "flat in ivec4 fs_in_result;\n" 334 "\n" 335 "uniform bool use_vs_data;\n" 336 "\n" 337 "void main()\n" 338 "{\n" 339 " if (use_vs_data)\n" 340 " o = fs_in_result;\n" 341 " else {\n" 342 " ${TYPE} a = ${TYPE}(fs_in_a);\n" 343 " ${TYPE} b = ${TYPE}(fs_in_b);\n" 344 " bvec4 sel = bvec4(fs_in_sel);\n" 345 " o = ivec4(mix(a, b, sel));\n" 346 " }\n" 347 "}\n"; 348 349 TestLog& log = m_testCtx.getLog(); 350 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 351 bool pass = true; 352 const char* extension_enable; 353 bool is_es = glslVersionIsES(m_glslVersion); 354 355 if ((is_es && (m_glslVersion < glu::GLSL_VERSION_310_ES)) || !is_es) 356 { 357 /* For versions that do not support this feature in Core it must be exposed via an extension. */ 358 if (!m_context.getContextInfo().isExtensionSupported("GL_EXT_shader_integer_mix")) 359 { 360 return QP_TEST_RESULT_NOT_SUPPORTED; 361 } 362 363 extension_enable = "#extension GL_EXT_shader_integer_mix: enable"; 364 } 365 else 366 { 367 extension_enable = ""; 368 } 369 370 /* Generate the specialization of the shader for the specific 371 * type being tested. 372 */ 373 std::map<std::string, std::string> args; 374 375 args["VERSION_DECL"] = glu::getGLSLVersionDeclaration(m_glslVersion); 376 args["EXTENSION_ENABLE"] = extension_enable; 377 args["TYPE"] = type; 378 379 std::string vs_code = tcu::StringTemplate(vs_template).specialize(args); 380 381 std::string fs_code = tcu::StringTemplate(fs_template).specialize(args); 382 383 glu::ShaderProgram prog(m_context.getRenderContext(), 384 glu::makeVtxFragSources(vs_code.c_str(), fs_code.c_str())); 385 386 if (!prog.isOk()) 387 { 388 log << prog; 389 TCU_FAIL("Compile failed"); 390 } 391 392 if (!glslVersionIsES(m_glslVersion)) 393 glEnable(GL_PROGRAM_POINT_SIZE); 394 395 /* Generate an integer FBO for rendering. 396 */ 397 GLuint fbo; 398 GLuint tex; 399 400 glGenTextures(1, &tex); 401 glBindTexture(GL_TEXTURE_2D, tex); 402 glTexImage2D(GL_TEXTURE_2D, 0 /* level */, GL_RGBA32I, width, height, 0 /* border */, GL_RGBA_INTEGER, GL_INT, 403 NULL /* data */); 404 glBindTexture(GL_TEXTURE_2D, 0); 405 406 glGenFramebuffers(1, &fbo); 407 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); 408 glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); 409 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0 /* level */); 410 411 GLU_EXPECT_NO_ERROR(gl.getError(), "Creation of rendering FBO failed."); 412 413 if (glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) 414 TCU_FAIL("Framebuffer not complete."); 415 416 glViewport(0, 0, width, height); 417 418 /* Fill a VBO with some vertex data. 419 */ 420 deUint32 pointIndices[256]; 421 float vertex[DE_LENGTH_OF_ARRAY(pointIndices) * 2]; 422 deInt32 a[DE_LENGTH_OF_ARRAY(pointIndices) * 4]; 423 deInt32 b[DE_LENGTH_OF_ARRAY(a)]; 424 deInt32 sel[DE_LENGTH_OF_ARRAY(a)]; 425 tcu::IVec4 expected[DE_LENGTH_OF_ARRAY(pointIndices)]; 426 427 for (int i = 0; i < DE_LENGTH_OF_ARRAY(pointIndices); i++) 428 { 429 pointIndices[i] = deUint16(i); 430 431 const int x = (i / 16) * (width / 16) + (4 / 2); 432 const int y = (i % 16) * (height / 16) + (4 / 2); 433 434 vertex[(i * 2) + 0] = float(x) * 2.0f / float(width) - 1.0f; 435 vertex[(i * 2) + 1] = float(y) * 2.0f / float(height) - 1.0f; 436 437 a[(i * 4) + 0] = i; 438 a[(i * 4) + 1] = i * 5; 439 a[(i * 4) + 2] = i * 7; 440 a[(i * 4) + 3] = i * 11; 441 442 b[(i * 4) + 0] = ~a[(i * 4) + 3]; 443 b[(i * 4) + 1] = ~a[(i * 4) + 2]; 444 b[(i * 4) + 2] = ~a[(i * 4) + 1]; 445 b[(i * 4) + 3] = ~a[(i * 4) + 0]; 446 447 sel[(i * 4) + 0] = (i >> 0) & 1; 448 sel[(i * 4) + 1] = (i >> 1) & 1; 449 sel[(i * 4) + 2] = (i >> 2) & 1; 450 sel[(i * 4) + 3] = (i >> 3) & 1; 451 452 expected[i] = tcu::IVec4( 453 sel[(i * 4) + 0] ? b[(i * 4) + 0] : a[(i * 4) + 0], sel[(i * 4) + 1] ? b[(i * 4) + 1] : a[(i * 4) + 1], 454 sel[(i * 4) + 2] ? b[(i * 4) + 2] : a[(i * 4) + 2], sel[(i * 4) + 3] ? b[(i * 4) + 3] : a[(i * 4) + 3]); 455 } 456 457 /* Mask off all but the least significant bit for boolean 458 * types. 459 */ 460 if (type[0] == 'b') 461 { 462 for (int i = 0; i < DE_LENGTH_OF_ARRAY(a); i++) 463 { 464 a[i] &= 1; 465 b[i] &= 1; 466 467 expected[i / 4][0] &= 1; 468 expected[i / 4][1] &= 1; 469 expected[i / 4][2] &= 1; 470 expected[i / 4][3] &= 1; 471 } 472 } 473 474 glu::VertexArrayBinding vertexArrays[] = { 475 glu::va::Float("vertex", 2, DE_LENGTH_OF_ARRAY(pointIndices), 0, vertex), 476 glu::va::Int32("vs_in_a", 4, DE_LENGTH_OF_ARRAY(pointIndices), 0, a), 477 glu::va::Int32("vs_in_b", 4, DE_LENGTH_OF_ARRAY(pointIndices), 0, b), 478 glu::va::Int32("vs_in_sel", 4, DE_LENGTH_OF_ARRAY(pointIndices), 0, sel) 479 }; 480 481 /* Render and verify the results. Rendering happens twice. 482 * The first time, use_vs_data is false, and the mix() result 483 * from the fragment shader is used. The second time, 484 * use_vs_data is true, and the mix() result from the vertex 485 * shader is used. 486 */ 487 const GLint loc = gl.getUniformLocation(prog.getProgram(), "use_vs_data"); 488 gl.useProgram(prog.getProgram()); 489 490 static const GLint clear[] = { 1, 2, 3, 4 }; 491 glClearBufferiv(GL_COLOR, 0, clear); 492 493 gl.uniform1i(loc, 0); 494 glu::draw(m_context.getRenderContext(), prog.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), vertexArrays, 495 glu::pr::Points(DE_LENGTH_OF_ARRAY(pointIndices), pointIndices)); 496 497 for (int i = 0; i < DE_LENGTH_OF_ARRAY(pointIndices); i++) 498 { 499 const int x = int((vertex[(i * 2) + 0] + 1.0f) * float(width) / 2.0f); 500 const int y = int((vertex[(i * 2) + 1] + 1.0f) * float(height) / 2.0f); 501 502 pass = probe_pixel(log, "Fragment", x, y, expected[i]) && pass; 503 } 504 505 gl.uniform1i(loc, 1); 506 glu::draw(m_context.getRenderContext(), prog.getProgram(), DE_LENGTH_OF_ARRAY(vertexArrays), vertexArrays, 507 glu::pr::Points(DE_LENGTH_OF_ARRAY(pointIndices), pointIndices)); 508 509 for (int i = 0; i < DE_LENGTH_OF_ARRAY(pointIndices); i++) 510 { 511 const int x = int((vertex[(i * 2) + 0] + 1.0f) * float(width) / 2.0f); 512 const int y = int((vertex[(i * 2) + 1] + 1.0f) * float(height) / 2.0f); 513 514 pass = probe_pixel(log, "Vertex", x, y, expected[i]) && pass; 515 } 516 517 glDeleteFramebuffers(1, &fbo); 518 glDeleteTextures(1, &tex); 519 520 return pass ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL; 521 } 522 523 bool probe_pixel(TestLog& log, const char* stage, int x, int y, const tcu::IVec4& expected) 524 { 525 tcu::IVec4 pixel; 526 527 glReadPixels(x, y, 1, 1, GL_RGBA_INTEGER, GL_INT, &pixel); 528 529 if (expected != pixel) 530 { 531 log << TestLog::Message << stage << " shader failed at pixel (" << x << ", " << y << "). " 532 << "Got " << pixel << ", expected " << expected << ")." << TestLog::EndMessage; 533 return false; 534 } 535 536 return true; 537 } 538 }; 539 540 ShaderIntegerMixTests::ShaderIntegerMixTests(Context& context, glu::GLSLVersion glslVersion) 541 : TestCaseGroup(context, "shader_integer_mix", "Shader Integer Mix tests"), m_glslVersion(glslVersion) 542 { 543 // empty 544 } 545 546 ShaderIntegerMixTests::~ShaderIntegerMixTests() 547 { 548 // empty 549 } 550 551 void ShaderIntegerMixTests::init(void) 552 { 553 addChild(new ShaderIntegerMixDefineCase(m_context, "define", "Verify GL_EXT_shader_integer_mix is defined to 1.", 554 m_glslVersion)); 555 addChild(new ShaderIntegerMixPrototypesCase(m_context, "prototypes-extension", 556 "Verify availability of all function signatures with the extension.", 557 m_glslVersion, true, false)); 558 addChild(new ShaderIntegerMixPrototypesCase( 559 m_context, "prototypes", "Verify availability of all function signatures with the proper GLSL version.", 560 m_glslVersion, false, false)); 561 addChild(new ShaderIntegerMixPrototypesCase( 562 m_context, "prototypes-negative", 563 "Verify compilation fails if the GLSL version does not support shader_integer_mix", m_glslVersion, false, 564 true)); 565 566 static const char* types_to_test[] = { "ivec4", "uvec4", "bvec4" }; 567 568 for (int i = 0; i < DE_LENGTH_OF_ARRAY(types_to_test); i++) 569 { 570 std::stringstream name; 571 572 name << "mix-" << types_to_test[i]; 573 574 std::stringstream description; 575 576 description << "Verify functionality of mix() with " << types_to_test[i] << " parameters."; 577 578 addChild(new ShaderIntegerMixRenderCase(m_context, name.str().c_str(), description.str().c_str(), m_glslVersion, 579 types_to_test[i])); 580 } 581 } 582 583 } // namespace deqp 584