1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 3.1 Module 3 * ------------------------------------------------- 4 * 5 * Copyright 2014 The Android Open Source Project 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 Tests for separate shader objects 22 *//*--------------------------------------------------------------------*/ 23 24 #include "es31fSeparateShaderTests.hpp" 25 26 #include "deInt32.h" 27 #include "deString.h" 28 #include "deStringUtil.hpp" 29 #include "deUniquePtr.hpp" 30 #include "deRandom.hpp" 31 #include "deSTLUtil.hpp" 32 #include "tcuCommandLine.hpp" 33 #include "tcuImageCompare.hpp" 34 #include "tcuRenderTarget.hpp" 35 #include "tcuRGBA.hpp" 36 #include "tcuSurface.hpp" 37 #include "tcuStringTemplate.hpp" 38 #include "gluCallLogWrapper.hpp" 39 #include "gluPixelTransfer.hpp" 40 #include "gluRenderContext.hpp" 41 #include "gluShaderProgram.hpp" 42 #include "gluVarType.hpp" 43 #include "glsShaderLibrary.hpp" 44 #include "glwFunctions.hpp" 45 #include "glwDefs.hpp" 46 #include "glwEnums.hpp" 47 48 #include <cstdarg> 49 #include <algorithm> 50 #include <map> 51 #include <sstream> 52 #include <string> 53 #include <set> 54 #include <vector> 55 56 namespace deqp 57 { 58 namespace gles31 59 { 60 namespace Functional 61 { 62 namespace 63 { 64 65 using std::map; 66 using std::set; 67 using std::ostringstream; 68 using std::string; 69 using std::vector; 70 using de::MovePtr; 71 using de::Random; 72 using de::UniquePtr; 73 using tcu::MessageBuilder; 74 using tcu::RenderTarget; 75 using tcu::StringTemplate; 76 using tcu::Surface; 77 using tcu::TestLog; 78 using tcu::ResultCollector; 79 using glu::CallLogWrapper; 80 using glu::DataType; 81 using glu::VariableDeclaration; 82 using glu::Interpolation; 83 using glu::Precision; 84 using glu::Program; 85 using glu::ProgramPipeline; 86 using glu::ProgramSources; 87 using glu::RenderContext; 88 using glu::ShaderProgram; 89 using glu::ShaderType; 90 using glu::Storage; 91 using glu::VarType; 92 using glu::VertexSource; 93 using glu::FragmentSource; 94 using glu::ProgramSeparable; 95 96 using namespace glw; 97 98 #define LOG_CALL(CALL) do \ 99 { \ 100 enableLogging(true); \ 101 CALL; \ 102 enableLogging(false); \ 103 } while (deGetFalse()) 104 105 enum 106 { 107 VIEWPORT_SIZE = 128 108 }; 109 110 DataType randomType (Random& rnd) 111 { 112 using namespace glu; 113 114 if (rnd.getInt(0, 7) == 0) 115 { 116 const int numCols = rnd.getInt(2, 4), numRows = rnd.getInt(2, 4); 117 118 return getDataTypeMatrix(numCols, numRows); 119 } 120 else 121 { 122 static const DataType s_types[] = { TYPE_FLOAT, TYPE_INT, TYPE_UINT }; 123 static const float s_weights[] = { 3.0, 1.0, 1.0 }; 124 const int size = rnd.getInt(1, 4); 125 const DataType scalarType = rnd.chooseWeighted<DataType>( 126 DE_ARRAY_BEGIN(s_types), DE_ARRAY_END(s_types), DE_ARRAY_BEGIN(s_weights)); 127 return getDataTypeVector(scalarType, size); 128 } 129 130 DE_ASSERT(!"Impossible"); 131 return TYPE_INVALID; 132 } 133 134 Interpolation randomInterpolation (Random& rnd) 135 { 136 return Interpolation(rnd.getInt(0, glu::INTERPOLATION_LAST - 1)); 137 } 138 139 enum BindingKind 140 { 141 BINDING_NAME, 142 BINDING_LOCATION, 143 BINDING_LAST 144 }; 145 146 BindingKind randomBinding (Random& rnd) 147 { 148 return rnd.getBool() ? BINDING_LOCATION : BINDING_NAME; 149 } 150 151 void printInputColor (ostringstream& oss, const VariableDeclaration& input) 152 { 153 using namespace glu; 154 155 const DataType basicType = input.varType.getBasicType(); 156 string exp = input.name; 157 158 switch (getDataTypeScalarType(basicType)) 159 { 160 case TYPE_FLOAT: 161 break; 162 163 case TYPE_INT: 164 case TYPE_UINT: 165 { 166 DataType floatType = getDataTypeFloatScalars(basicType); 167 exp = string() + "(" + getDataTypeName(floatType) + "(" + exp + ") / 255.0" + ")"; 168 break; 169 } 170 171 default: 172 DE_ASSERT(!"Impossible"); 173 } 174 175 if (isDataTypeScalarOrVector(basicType)) 176 { 177 switch (getDataTypeScalarSize(basicType)) 178 { 179 case 1: 180 oss << "hsv(vec3(" << exp << ", 1.0, 1.0))"; 181 break; 182 case 2: 183 oss << "hsv(vec3(" << exp << ", 1.0))"; 184 break; 185 case 3: 186 oss << "vec4(" << exp << ", 1.0)"; 187 break; 188 case 4: 189 oss << exp; 190 break; 191 default: 192 DE_ASSERT(!"Impossible"); 193 } 194 } 195 else if (isDataTypeMatrix(basicType)) 196 { 197 int rows = getDataTypeMatrixNumRows(basicType); 198 int columns = getDataTypeMatrixNumColumns(basicType); 199 200 if (rows == columns) 201 oss << "hsv(vec3(determinant(" << exp << ")))"; 202 else 203 { 204 if (rows != 3 && columns >= 3) 205 { 206 exp = "transpose(" + exp + ")"; 207 std::swap(rows, columns); 208 } 209 exp = exp + "[0]"; 210 if (rows > 3) 211 exp = exp + ".xyz"; 212 oss << "hsv(" << exp << ")"; 213 } 214 } 215 else 216 DE_ASSERT(!"Impossible"); 217 } 218 219 // Representation for the varyings between vertex and fragment shaders 220 221 struct VaryingParams 222 { 223 VaryingParams (void) 224 : count (0) 225 , type (glu::TYPE_LAST) 226 , binding (BINDING_LAST) 227 , vtxInterp (glu::INTERPOLATION_LAST) 228 , frgInterp (glu::INTERPOLATION_LAST) {} 229 230 int count; 231 DataType type; 232 BindingKind binding; 233 Interpolation vtxInterp; 234 Interpolation frgInterp; 235 }; 236 237 struct VaryingInterface 238 { 239 vector<VariableDeclaration> vtxOutputs; 240 vector<VariableDeclaration> frgInputs; 241 }; 242 243 // Generate corresponding input and output variable declarations that may vary 244 // in compatible ways. 245 246 Interpolation chooseInterpolation (Interpolation param, DataType type, Random& rnd) 247 { 248 if (glu::getDataTypeScalarType(type) != glu::TYPE_FLOAT) 249 return glu::INTERPOLATION_FLAT; 250 251 if (param == glu::INTERPOLATION_LAST) 252 return randomInterpolation(rnd); 253 254 return param; 255 } 256 257 VaryingInterface genVaryingInterface (const VaryingParams& params, 258 Random& rnd) 259 { 260 using namespace glu; 261 262 VaryingInterface ret; 263 int offset = 0; 264 265 for (int varNdx = 0; varNdx < params.count; ++varNdx) 266 { 267 const BindingKind binding = ((params.binding == BINDING_LAST) 268 ? randomBinding(rnd) : params.binding); 269 const DataType type = ((params.type == TYPE_LAST) 270 ? randomType(rnd) : params.type); 271 const Interpolation vtxInterp = chooseInterpolation(params.vtxInterp, type, rnd); 272 const Interpolation frgInterp = chooseInterpolation(params.frgInterp, type, rnd); 273 const int loc = ((binding == BINDING_LOCATION) ? offset : -1); 274 const string ndxStr = de::toString(varNdx); 275 const string vtxName = ((binding == BINDING_NAME) 276 ? "var" + ndxStr : "vtxVar" + ndxStr); 277 const string frgName = ((binding == BINDING_NAME) 278 ? "var" + ndxStr : "frgVar" + ndxStr); 279 const VarType varType (type, PRECISION_HIGHP); 280 281 offset += getDataTypeNumLocations(type); 282 283 // Over 16 locations aren't necessarily supported, so halt here. 284 if (offset > 16) 285 break; 286 287 ret.vtxOutputs.push_back( 288 VariableDeclaration(varType, vtxName, STORAGE_OUT, vtxInterp, loc)); 289 ret.frgInputs.push_back( 290 VariableDeclaration(varType, frgName, STORAGE_IN, frgInterp, loc)); 291 } 292 293 return ret; 294 } 295 296 // Create vertex output variable declarations that are maximally compatible 297 // with the fragment input variables. 298 299 vector<VariableDeclaration> varyingCompatVtxOutputs (const VaryingInterface& varyings) 300 { 301 vector<VariableDeclaration> outputs = varyings.vtxOutputs; 302 303 for (size_t i = 0; i < outputs.size(); ++i) 304 { 305 outputs[i].interpolation = varyings.frgInputs[i].interpolation; 306 outputs[i].name = varyings.frgInputs[i].name; 307 } 308 309 return outputs; 310 } 311 312 // Shader source generation 313 314 void printFloat (ostringstream& oss, double d) 315 { 316 oss.setf(oss.fixed | oss.internal); 317 oss.precision(4); 318 oss.width(7); 319 oss << d; 320 } 321 322 void printFloatDeclaration (ostringstream& oss, 323 const string& varName, 324 bool uniform, 325 GLfloat value = 0.0) 326 { 327 using namespace glu; 328 329 const VarType varType (TYPE_FLOAT, PRECISION_HIGHP); 330 331 if (uniform) 332 oss << VariableDeclaration(varType, varName, STORAGE_UNIFORM) << ";\n"; 333 else 334 oss << VariableDeclaration(varType, varName, STORAGE_CONST) 335 << " = " << de::floatToString(value, 6) << ";\n"; 336 } 337 338 void printRandomInitializer (ostringstream& oss, DataType type, Random& rnd) 339 { 340 using namespace glu; 341 const int size = getDataTypeScalarSize(type); 342 343 if (size > 0) 344 oss << getDataTypeName(type) << "("; 345 346 for (int i = 0; i < size; ++i) 347 { 348 oss << (i == 0 ? "" : ", "); 349 switch (getDataTypeScalarType(type)) 350 { 351 case TYPE_FLOAT: 352 printFloat(oss, rnd.getInt(0, 16) / 16.0); 353 break; 354 355 case TYPE_INT: 356 case TYPE_UINT: 357 oss << rnd.getInt(0, 255); 358 break; 359 360 case TYPE_BOOL: 361 oss << (rnd.getBool() ? "true" : "false"); 362 break; 363 364 default: 365 DE_ASSERT(!"Impossible"); 366 } 367 } 368 369 if (size > 0) 370 oss << ")"; 371 } 372 373 string genVtxShaderSrc (deUint32 seed, 374 const vector<VariableDeclaration>& outputs, 375 const string& varName, 376 bool uniform, 377 float value = 0.0) 378 { 379 ostringstream oss; 380 Random rnd (seed); 381 enum { NUM_COMPONENTS = 2 }; 382 static const int s_quadrants[][NUM_COMPONENTS] = { {1, 1}, {-1, 1}, {1, -1} }; 383 384 oss << "#version 310 es\n"; 385 386 oss << "const vec2 triangle[3] = vec2[3](\n"; 387 388 for (int vertexNdx = 0; vertexNdx < DE_LENGTH_OF_ARRAY(s_quadrants); ++vertexNdx) 389 { 390 oss << "\tvec2("; 391 392 for (int componentNdx = 0; componentNdx < NUM_COMPONENTS; ++componentNdx) 393 { 394 printFloat(oss, s_quadrants[vertexNdx][componentNdx] * rnd.getInt(4,16) / 16.0); 395 oss << (componentNdx < 1 ? ", " : ""); 396 } 397 398 oss << ")" << (vertexNdx < 2 ? "," : "") << "\n"; 399 } 400 oss << ");\n"; 401 402 403 for (vector<VariableDeclaration>::const_iterator it = outputs.begin(); 404 it != outputs.end(); ++it) 405 { 406 const DataType type = it->varType.getBasicType(); 407 const string typeName = glu::getDataTypeName(type); 408 409 oss << "const " << typeName << " " << it->name << "Inits[3] = " 410 << typeName << "[3](\n"; 411 for (int i = 0; i < 3; ++i) 412 { 413 oss << (i == 0 ? "\t" : ",\n\t"); 414 printRandomInitializer(oss, type, rnd); 415 } 416 oss << ");\n" 417 << *it << ";\n"; 418 } 419 420 printFloatDeclaration(oss, varName, uniform, value); 421 422 oss << "void main (void)\n" 423 << "{\n" 424 << "\tgl_Position = vec4(" << varName << " * triangle[gl_VertexID], 0.0, 1.0);\n"; 425 426 for (vector<VariableDeclaration>::const_iterator it = outputs.begin(); 427 it != outputs.end(); ++it) 428 oss << "\t" << it->name << " = " << it->name << "Inits[gl_VertexID];\n"; 429 430 oss << "}\n"; 431 432 return oss.str(); 433 } 434 435 string genFrgShaderSrc (deUint32 seed, 436 const vector<VariableDeclaration>& inputs, 437 const string& varName, 438 bool uniform, 439 float value = 0.0) 440 { 441 Random rnd (seed); 442 ostringstream oss; 443 444 oss.precision(4); 445 oss.width(7); 446 oss << "#version 310 es\n"; 447 448 oss << "precision highp float;\n";; 449 450 oss << "out vec4 fragColor;\n";; 451 452 printFloatDeclaration(oss, varName, uniform, value); 453 454 for (vector<VariableDeclaration>::const_iterator it = inputs.begin(); 455 it != inputs.end(); ++it) 456 oss << *it << ";\n"; 457 458 // glsl % isn't defined for negative numbers 459 oss << "int imod (int n, int d)" << "\n" 460 << "{" << "\n" 461 << "\t" << "return (n < 0 ? d - 1 - (-1 - n) % d : n % d);" << "\n" 462 << "}" << "\n"; 463 464 oss << "vec4 hsv (vec3 hsv)" 465 << "{" << "\n" 466 << "\tfloat h = hsv.x * 3.0;\n" 467 << "\tfloat r = max(0.0, 1.0 - h) + max(0.0, h - 2.0);\n" 468 << "\tfloat g = max(0.0, 1.0 - abs(h - 1.0));\n" 469 << "\tfloat b = max(0.0, 1.0 - abs(h - 2.0));\n" 470 << "\tvec3 hs = mix(vec3(1.0), vec3(r, g, b), hsv.y);\n" 471 << "\treturn vec4(hsv.z * hs, 1.0);\n" 472 << "}\n"; 473 474 oss << "void main (void)\n" 475 << "{\n"; 476 477 oss << "\t" << "fragColor = vec4(vec3(" << varName << "), 1.0);" << "\n"; 478 479 if (inputs.size() > 0) 480 { 481 oss << "\t" << "switch (imod(int(0.5 * (gl_FragCoord.x - gl_FragCoord.y)), " 482 << inputs.size() << "))" << "\n" 483 << "\t" << "{" << "\n"; 484 485 for (size_t i = 0; i < inputs.size(); ++i) 486 { 487 oss << "\t\t" << "case " << i << ":" << "\n" 488 << "\t\t\t" << "fragColor *= "; 489 490 printInputColor(oss, inputs[i]); 491 492 oss << ";" << "\n" 493 << "\t\t\t" << "break;" << "\n"; 494 } 495 496 oss << "\t\t" << "case " << inputs.size() << ":\n" 497 << "\t\t\t" << "fragColor = vec4(1.0, 0.0, 1.0, 1.0);" << "\n"; 498 oss << "\t\t\t" << "break;" << "\n"; 499 500 oss << "\t\t" << "case -1:\n" 501 << "\t\t\t" << "fragColor = vec4(1.0, 1.0, 0.0, 1.0);" << "\n"; 502 oss << "\t\t\t" << "break;" << "\n"; 503 504 oss << "\t\t" << "default:" << "\n" 505 << "\t\t\t" << "fragColor = vec4(1.0, 1.0, 0.0, 1.0);" << "\n"; 506 507 oss << "\t" << "}\n"; 508 509 } 510 511 oss << "}\n"; 512 513 return oss.str(); 514 } 515 516 // ProgramWrapper 517 518 class ProgramWrapper 519 { 520 public: 521 virtual ~ProgramWrapper (void) {} 522 523 virtual GLuint getProgramName (void) = 0; 524 virtual void writeToLog (TestLog& log) = 0; 525 }; 526 527 class ShaderProgramWrapper : public ProgramWrapper 528 { 529 public: 530 ShaderProgramWrapper (const RenderContext& renderCtx, 531 const ProgramSources& sources) 532 : m_shaderProgram (renderCtx, sources) {} 533 ~ShaderProgramWrapper (void) {} 534 535 GLuint getProgramName (void) { return m_shaderProgram.getProgram(); } 536 ShaderProgram& getShaderProgram (void) { return m_shaderProgram; } 537 void writeToLog (TestLog& log) { log << m_shaderProgram; } 538 539 private: 540 ShaderProgram m_shaderProgram; 541 }; 542 543 class RawProgramWrapper : public ProgramWrapper 544 { 545 public: 546 RawProgramWrapper (const RenderContext& renderCtx, 547 GLuint programName, 548 ShaderType shaderType, 549 const string& source) 550 : m_program (renderCtx, programName) 551 , m_shaderType (shaderType) 552 , m_source (source) {} 553 ~RawProgramWrapper (void) {} 554 555 GLuint getProgramName (void) { return m_program.getProgram(); } 556 Program& getProgram (void) { return m_program; } 557 void writeToLog (TestLog& log); 558 559 private: 560 Program m_program; 561 ShaderType m_shaderType; 562 const string m_source; 563 }; 564 565 void RawProgramWrapper::writeToLog (TestLog& log) 566 { 567 const string info = m_program.getInfoLog(); 568 qpShaderType qpType = glu::getLogShaderType(m_shaderType); 569 570 log << TestLog::ShaderProgram(true, info) 571 << TestLog::Shader(qpType, m_source, 572 true, "[Shader created by glCreateShaderProgramv()]") 573 << TestLog::EndShaderProgram; 574 } 575 576 // ProgramParams 577 578 struct ProgramParams 579 { 580 ProgramParams (deUint32 vtxSeed_, GLfloat vtxScale_, deUint32 frgSeed_, GLfloat frgScale_) 581 : vtxSeed (vtxSeed_) 582 , vtxScale (vtxScale_) 583 , frgSeed (frgSeed_) 584 , frgScale (frgScale_) {} 585 deUint32 vtxSeed; 586 GLfloat vtxScale; 587 deUint32 frgSeed; 588 GLfloat frgScale; 589 }; 590 591 ProgramParams genProgramParams (Random& rnd) 592 { 593 const deUint32 vtxSeed = rnd.getUint32(); 594 const GLfloat vtxScale = rnd.getInt(8, 16) / 16.0f; 595 const deUint32 frgSeed = rnd.getUint32(); 596 const GLfloat frgScale = rnd.getInt(0, 16) / 16.0f; 597 598 return ProgramParams(vtxSeed, vtxScale, frgSeed, frgScale); 599 } 600 601 // TestParams 602 603 struct TestParams 604 { 605 bool initSingle; 606 bool switchVtx; 607 bool switchFrg; 608 bool useUniform; 609 bool useSameName; 610 bool useCreateHelper; 611 bool useProgramUniform; 612 VaryingParams varyings; 613 }; 614 615 deUint32 paramsSeed (const TestParams& params) 616 { 617 deUint32 paramCode = (params.initSingle << 0 | 618 params.switchVtx << 1 | 619 params.switchFrg << 2 | 620 params.useUniform << 3 | 621 params.useSameName << 4 | 622 params.useCreateHelper << 5 | 623 params.useProgramUniform << 6); 624 625 paramCode = deUint32Hash(paramCode) + params.varyings.count; 626 paramCode = deUint32Hash(paramCode) + params.varyings.type; 627 paramCode = deUint32Hash(paramCode) + params.varyings.binding; 628 paramCode = deUint32Hash(paramCode) + params.varyings.vtxInterp; 629 paramCode = deUint32Hash(paramCode) + params.varyings.frgInterp; 630 631 return deUint32Hash(paramCode); 632 } 633 634 string paramsCode (const TestParams& params) 635 { 636 using namespace glu; 637 638 ostringstream oss; 639 640 oss << (params.initSingle ? "1" : "2") 641 << (params.switchVtx ? "v" : "") 642 << (params.switchFrg ? "f" : "") 643 << (params.useProgramUniform ? "p" : "") 644 << (params.useUniform ? "u" : "") 645 << (params.useSameName ? "s" : "") 646 << (params.useCreateHelper ? "c" : "") 647 << de::toString(params.varyings.count) 648 << (params.varyings.binding == BINDING_NAME ? "n" : 649 params.varyings.binding == BINDING_LOCATION ? "l" : 650 params.varyings.binding == BINDING_LAST ? "r" : 651 "") 652 << (params.varyings.vtxInterp == INTERPOLATION_SMOOTH ? "m" : 653 params.varyings.vtxInterp == INTERPOLATION_CENTROID ? "e" : 654 params.varyings.vtxInterp == INTERPOLATION_FLAT ? "a" : 655 params.varyings.vtxInterp == INTERPOLATION_LAST ? "r" : 656 "o") 657 << (params.varyings.frgInterp == INTERPOLATION_SMOOTH ? "m" : 658 params.varyings.frgInterp == INTERPOLATION_CENTROID ? "e" : 659 params.varyings.frgInterp == INTERPOLATION_FLAT ? "a" : 660 params.varyings.frgInterp == INTERPOLATION_LAST ? "r" : 661 "o"); 662 return oss.str(); 663 } 664 665 bool paramsValid (const TestParams& params) 666 { 667 using namespace glu; 668 669 // Final pipeline has a single program? 670 if (params.initSingle) 671 { 672 // Cannot have conflicting names for uniforms or constants 673 if (params.useSameName) 674 return false; 675 676 // CreateShaderProgram would never get called 677 if (!params.switchVtx && !params.switchFrg && params.useCreateHelper) 678 return false; 679 680 // Must switch either all or nothing 681 if (params.switchVtx != params.switchFrg) 682 return false; 683 } 684 685 // ProgramUniform would never get called 686 if (params.useProgramUniform && !params.useUniform) 687 return false; 688 689 // Interpolation is meaningless if we don't use an in/out variable. 690 if (params.varyings.count == 0 && 691 !(params.varyings.vtxInterp == INTERPOLATION_LAST && 692 params.varyings.frgInterp == INTERPOLATION_LAST)) 693 return false; 694 695 // Mismatch by flat / smooth is not allowed. See Khronos bug #12630 696 if ((params.varyings.vtxInterp == INTERPOLATION_FLAT) != (params.varyings.frgInterp == INTERPOLATION_FLAT)) 697 return false; 698 699 return true; 700 } 701 702 void logParams (TestLog& log, const TestParams& params) 703 { 704 // We don't log operational details here since those are shown 705 // in the log messages during execution. 706 MessageBuilder msg = log.message(); 707 708 msg << "Pipeline configuration:\n"; 709 710 msg << "Vertex and fragment shaders have " 711 << (params.useUniform ? "uniform" : "constant") << "s with " 712 << (params.useSameName ? "the same name" : "different names") << ".\n"; 713 714 if (params.varyings.count == 0) 715 msg << "There are no varyings.\n"; 716 else 717 { 718 if (params.varyings.count == 1) 719 msg << "There is one varying.\n"; 720 else 721 msg << "There are " << params.varyings.count << " varyings.\n"; 722 723 if (params.varyings.type == glu::TYPE_LAST) 724 msg << "Varyings are of random types.\n"; 725 else 726 msg << "Varyings are of type '" 727 << glu::getDataTypeName(params.varyings.type) << "'.\n"; 728 729 msg << "Varying outputs and inputs correspond "; 730 switch (params.varyings.binding) 731 { 732 case BINDING_NAME: 733 msg << "by name.\n"; 734 break; 735 case BINDING_LOCATION: 736 msg << "by location.\n"; 737 break; 738 case BINDING_LAST: 739 msg << "randomly either by name or by location.\n"; 740 break; 741 default: 742 DE_ASSERT(!"Impossible"); 743 } 744 745 msg << "In the vertex shader the varyings are qualified "; 746 if (params.varyings.vtxInterp == glu::INTERPOLATION_LAST) 747 msg << "with a random interpolation qualifier.\n"; 748 else 749 msg << "'" << glu::getInterpolationName(params.varyings.vtxInterp) << "'.\n"; 750 751 msg << "In the fragment shader the varyings are qualified "; 752 if (params.varyings.frgInterp == glu::INTERPOLATION_LAST) 753 msg << "with a random interpolation qualifier.\n"; 754 else 755 msg << "'" << glu::getInterpolationName(params.varyings.frgInterp) << "'.\n"; 756 } 757 758 msg << TestLog::EndMessage; 759 760 log.writeMessage(""); 761 } 762 763 TestParams genParams (deUint32 seed) 764 { 765 Random rnd (seed); 766 TestParams params; 767 int tryNdx = 0; 768 769 do 770 { 771 params.initSingle = rnd.getBool(); 772 params.switchVtx = rnd.getBool(); 773 params.switchFrg = rnd.getBool(); 774 params.useUniform = rnd.getBool(); 775 params.useProgramUniform = params.useUniform && rnd.getBool(); 776 params.useCreateHelper = rnd.getBool(); 777 params.useSameName = rnd.getBool(); 778 { 779 int i = rnd.getInt(-1, 3); 780 params.varyings.count = (i == -1 ? 0 : 1 << i); 781 } 782 if (params.varyings.count > 0) 783 { 784 params.varyings.type = glu::TYPE_LAST; 785 params.varyings.binding = BINDING_LAST; 786 params.varyings.vtxInterp = glu::INTERPOLATION_LAST; 787 params.varyings.frgInterp = glu::INTERPOLATION_LAST; 788 } 789 else 790 { 791 params.varyings.type = glu::TYPE_INVALID; 792 params.varyings.binding = BINDING_LAST; 793 params.varyings.vtxInterp = glu::INTERPOLATION_LAST; 794 params.varyings.frgInterp = glu::INTERPOLATION_LAST; 795 } 796 797 tryNdx += 1; 798 } while (!paramsValid(params) && tryNdx < 16); 799 800 DE_ASSERT(paramsValid(params)); 801 802 return params; 803 } 804 805 // Program pipeline wrapper that retains references to component programs. 806 807 struct Pipeline 808 { 809 Pipeline (MovePtr<ProgramPipeline> pipeline_, 810 MovePtr<ProgramWrapper> fullProg_, 811 MovePtr<ProgramWrapper> vtxProg_, 812 MovePtr<ProgramWrapper> frgProg_) 813 : pipeline (pipeline_) 814 , fullProg (fullProg_) 815 , vtxProg (vtxProg_) 816 , frgProg (frgProg_) {} 817 818 ProgramWrapper& getVertexProgram (void) const 819 { 820 return vtxProg ? *vtxProg : *fullProg; 821 } 822 823 ProgramWrapper& getFragmentProgram (void) const 824 { 825 return frgProg ? *frgProg : *fullProg; 826 } 827 828 UniquePtr<ProgramPipeline> pipeline; 829 UniquePtr<ProgramWrapper> fullProg; 830 UniquePtr<ProgramWrapper> vtxProg; 831 UniquePtr<ProgramWrapper> frgProg; 832 }; 833 834 void logPipeline(TestLog& log, const Pipeline& pipeline) 835 { 836 ProgramWrapper& vtxProg = pipeline.getVertexProgram(); 837 ProgramWrapper& frgProg = pipeline.getFragmentProgram(); 838 839 log.writeMessage("// Failed program pipeline:"); 840 if (&vtxProg == &frgProg) 841 { 842 log.writeMessage("// Common program for both vertex and fragment stages:"); 843 vtxProg.writeToLog(log); 844 } 845 else 846 { 847 log.writeMessage("// Vertex stage program:"); 848 vtxProg.writeToLog(log); 849 log.writeMessage("// Fragment stage program:"); 850 frgProg.writeToLog(log); 851 } 852 } 853 854 // Rectangle 855 856 struct Rectangle 857 { 858 Rectangle (int x_, int y_, int width_, int height_) 859 : x (x_) 860 , y (y_) 861 , width (width_) 862 , height (height_) {} 863 int x; 864 int y; 865 int width; 866 int height; 867 }; 868 869 void setViewport (const RenderContext& renderCtx, const Rectangle& rect) 870 { 871 renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height); 872 } 873 874 void readRectangle (const RenderContext& renderCtx, const Rectangle& rect, Surface& dst) 875 { 876 dst.setSize(rect.width, rect.height); 877 glu::readPixels(renderCtx, rect.x, rect.y, dst.getAccess()); 878 } 879 880 Rectangle randomViewport (const RenderContext& ctx, Random& rnd, 881 GLint maxWidth, GLint maxHeight) 882 { 883 const RenderTarget& target = ctx.getRenderTarget(); 884 GLint width = de::min(target.getWidth(), maxWidth); 885 GLint xOff = rnd.getInt(0, target.getWidth() - width); 886 GLint height = de::min(target.getHeight(), maxHeight); 887 GLint yOff = rnd.getInt(0, target.getHeight() - height); 888 889 return Rectangle(xOff, yOff, width, height); 890 } 891 892 // SeparateShaderTest 893 894 class SeparateShaderTest : public TestCase, private CallLogWrapper 895 { 896 public: 897 typedef void (SeparateShaderTest::*TestFunc) 898 (MovePtr<Pipeline>& pipeOut); 899 900 SeparateShaderTest (Context& ctx, 901 const string& name, 902 const string& description, 903 int iterations, 904 const TestParams& params, 905 TestFunc testFunc); 906 907 IterateResult iterate (void); 908 909 void testPipelineRendering (MovePtr<Pipeline>& pipeOut); 910 void testCurrentProgPriority (MovePtr<Pipeline>& pipeOut); 911 void testActiveProgramUniform (MovePtr<Pipeline>& pipeOut); 912 void testPipelineQueryActive (MovePtr<Pipeline>& pipeOut); 913 void testPipelineQueryPrograms (MovePtr<Pipeline>& pipeOut); 914 915 private: 916 TestLog& log (void); 917 const RenderContext& getRenderContext (void); 918 919 void setUniform (ProgramWrapper& program, 920 const string& uniformName, 921 GLfloat value, 922 bool useProgramUni); 923 924 void drawSurface (Surface& dst, 925 deUint32 seed = 0); 926 927 MovePtr<ProgramWrapper> createShaderProgram (const string* vtxSource, 928 const string* frgSource, 929 bool separable); 930 931 MovePtr<ProgramWrapper> createSingleShaderProgram (ShaderType shaderType, 932 const string& src); 933 934 MovePtr<Pipeline> createPipeline (const ProgramParams& pp); 935 936 MovePtr<ProgramWrapper> createReferenceProgram (const ProgramParams& pp); 937 938 int m_iterations; 939 int m_currentIteration; 940 TestParams m_params; 941 TestFunc m_testFunc; 942 Random m_rnd; 943 ResultCollector m_status; 944 VaryingInterface m_varyings; 945 946 // Per-iteration state required for logging on exception 947 MovePtr<ProgramWrapper> m_fullProg; 948 MovePtr<ProgramWrapper> m_vtxProg; 949 MovePtr<ProgramWrapper> m_frgProg; 950 951 }; 952 953 const RenderContext& SeparateShaderTest::getRenderContext (void) 954 { 955 return m_context.getRenderContext(); 956 } 957 958 TestLog& SeparateShaderTest::log (void) 959 { 960 return m_testCtx.getLog(); 961 } 962 963 SeparateShaderTest::SeparateShaderTest (Context& ctx, 964 const string& name, 965 const string& description, 966 int iterations, 967 const TestParams& params, 968 TestFunc testFunc) 969 : TestCase (ctx, name.c_str(), description.c_str()) 970 , CallLogWrapper (ctx.getRenderContext().getFunctions(), log()) 971 , m_iterations (iterations) 972 , m_currentIteration(0) 973 , m_params (params) 974 , m_testFunc (testFunc) 975 , m_rnd (paramsSeed(params)) 976 , m_status (log(), "// ") 977 , m_varyings (genVaryingInterface(params.varyings, m_rnd)) 978 { 979 DE_ASSERT(paramsValid(params)); 980 } 981 982 MovePtr<ProgramWrapper> SeparateShaderTest::createShaderProgram (const string* vtxSource, 983 const string* frgSource, 984 bool separable) 985 { 986 ProgramSources sources; 987 988 if (vtxSource != DE_NULL) 989 sources << VertexSource(*vtxSource); 990 if (frgSource != DE_NULL) 991 sources << FragmentSource(*frgSource); 992 sources << ProgramSeparable(separable); 993 994 MovePtr<ShaderProgramWrapper> wrapper (new ShaderProgramWrapper(getRenderContext(), 995 sources)); 996 if (!wrapper->getShaderProgram().isOk()) 997 { 998 log().writeMessage("Couldn't create shader program"); 999 wrapper->writeToLog(log()); 1000 TCU_FAIL("Couldn't create shader program"); 1001 } 1002 1003 return MovePtr<ProgramWrapper>(wrapper.release()); 1004 } 1005 1006 MovePtr<ProgramWrapper> SeparateShaderTest::createSingleShaderProgram (ShaderType shaderType, 1007 const string& src) 1008 { 1009 const RenderContext& renderCtx = getRenderContext(); 1010 1011 if (m_params.useCreateHelper) 1012 { 1013 const char* const srcStr = src.c_str(); 1014 const GLenum glType = glu::getGLShaderType(shaderType); 1015 const GLuint programName = glCreateShaderProgramv(glType, 1, &srcStr); 1016 1017 if (glGetError() != GL_NO_ERROR || programName == 0) 1018 { 1019 qpShaderType qpType = glu::getLogShaderType(shaderType); 1020 1021 log() << TestLog::Message << "glCreateShaderProgramv() failed" 1022 << TestLog::EndMessage 1023 << TestLog::ShaderProgram(false, "[glCreateShaderProgramv() failed]") 1024 << TestLog::Shader(qpType, src, 1025 false, "[glCreateShaderProgramv() failed]") 1026 << TestLog::EndShaderProgram; 1027 TCU_FAIL("glCreateShaderProgramv() failed"); 1028 } 1029 1030 RawProgramWrapper* const wrapper = new RawProgramWrapper(renderCtx, programName, 1031 shaderType, src); 1032 MovePtr<ProgramWrapper> wrapperPtr(wrapper); 1033 Program& program = wrapper->getProgram(); 1034 1035 if (!program.getLinkStatus()) 1036 { 1037 log().writeMessage("glCreateShaderProgramv() failed at linking"); 1038 wrapper->writeToLog(log()); 1039 TCU_FAIL("glCreateShaderProgram() failed at linking"); 1040 } 1041 return wrapperPtr; 1042 } 1043 else 1044 { 1045 switch (shaderType) 1046 { 1047 case glu::SHADERTYPE_VERTEX: 1048 return createShaderProgram(&src, DE_NULL, true); 1049 case glu::SHADERTYPE_FRAGMENT: 1050 return createShaderProgram(DE_NULL, &src, true); 1051 default: 1052 DE_ASSERT(!"Impossible case"); 1053 } 1054 } 1055 return MovePtr<ProgramWrapper>(); // Shut up compiler warnings. 1056 } 1057 1058 void SeparateShaderTest::setUniform (ProgramWrapper& program, 1059 const string& uniformName, 1060 GLfloat value, 1061 bool useProgramUniform) 1062 { 1063 const GLuint progName = program.getProgramName(); 1064 const GLint location = glGetUniformLocation(progName, uniformName.c_str()); 1065 MessageBuilder msg = log().message(); 1066 1067 msg << "// Set program " << progName << "'s uniform '" << uniformName << "' to " << value; 1068 if (useProgramUniform) 1069 { 1070 msg << " using glProgramUniform1f"; 1071 glProgramUniform1f(progName, location, value); 1072 } 1073 else 1074 { 1075 msg << " using glUseProgram and glUniform1f"; 1076 glUseProgram(progName); 1077 glUniform1f(location, value); 1078 glUseProgram(0); 1079 } 1080 msg << TestLog::EndMessage; 1081 } 1082 1083 MovePtr<Pipeline> SeparateShaderTest::createPipeline(const ProgramParams& pp) 1084 { 1085 const bool useUniform = m_params.useUniform; 1086 const string vtxName = m_params.useSameName ? "scale" : "vtxScale"; 1087 const deUint32 initVtxSeed = m_params.switchVtx ? m_rnd.getUint32() : pp.vtxSeed; 1088 1089 const string frgName = m_params.useSameName ? "scale" : "frgScale"; 1090 const deUint32 initFrgSeed = m_params.switchFrg ? m_rnd.getUint32() : pp.frgSeed; 1091 const string frgSource = genFrgShaderSrc(initFrgSeed, m_varyings.frgInputs, 1092 frgName, useUniform, pp.frgScale); 1093 1094 const RenderContext& renderCtx = getRenderContext(); 1095 MovePtr<ProgramPipeline> pipeline (new ProgramPipeline(renderCtx)); 1096 MovePtr<ProgramWrapper> fullProg; 1097 MovePtr<ProgramWrapper> vtxProg; 1098 MovePtr<ProgramWrapper> frgProg; 1099 1100 // We cannot allow a situation where we have a single program with a 1101 // single uniform, because then the vertex and fragment shader uniforms 1102 // would not be distinct in the final pipeline, and we are going to test 1103 // that altering one uniform will not affect the other. 1104 DE_ASSERT(!(m_params.initSingle && m_params.useSameName && 1105 !m_params.switchVtx && !m_params.switchFrg)); 1106 1107 if (m_params.initSingle) 1108 { 1109 string vtxSource = genVtxShaderSrc(initVtxSeed, 1110 varyingCompatVtxOutputs(m_varyings), 1111 vtxName, useUniform, pp.vtxScale); 1112 fullProg = createShaderProgram(&vtxSource, &frgSource, true); 1113 pipeline->useProgramStages(GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, 1114 fullProg->getProgramName()); 1115 log() << TestLog::Message 1116 << "// Created pipeline " << pipeline->getPipeline() 1117 << " with two-shader program " << fullProg->getProgramName() 1118 << TestLog::EndMessage; 1119 } 1120 else 1121 { 1122 string vtxSource = genVtxShaderSrc(initVtxSeed, m_varyings.vtxOutputs, 1123 vtxName, useUniform, pp.vtxScale); 1124 vtxProg = createSingleShaderProgram(glu::SHADERTYPE_VERTEX, vtxSource); 1125 pipeline->useProgramStages(GL_VERTEX_SHADER_BIT, vtxProg->getProgramName()); 1126 1127 frgProg = createSingleShaderProgram(glu::SHADERTYPE_FRAGMENT, frgSource); 1128 pipeline->useProgramStages(GL_FRAGMENT_SHADER_BIT, frgProg->getProgramName()); 1129 1130 log() << TestLog::Message 1131 << "// Created pipeline " << pipeline->getPipeline() 1132 << " with vertex program " << vtxProg->getProgramName() 1133 << " and fragment program " << frgProg->getProgramName() 1134 << TestLog::EndMessage; 1135 } 1136 1137 m_status.check(pipeline->isValid(), 1138 "Pipeline is invalid after initialization"); 1139 1140 if (m_params.switchVtx) 1141 { 1142 string newSource = genVtxShaderSrc(pp.vtxSeed, m_varyings.vtxOutputs, 1143 vtxName, useUniform, pp.vtxScale); 1144 vtxProg = createSingleShaderProgram(glu::SHADERTYPE_VERTEX, newSource); 1145 pipeline->useProgramStages(GL_VERTEX_SHADER_BIT, vtxProg->getProgramName()); 1146 log() << TestLog::Message 1147 << "// Switched pipeline " << pipeline->getPipeline() 1148 << "'s vertex stage to single-shader program " << vtxProg->getProgramName() 1149 << TestLog::EndMessage; 1150 } 1151 if (m_params.switchFrg) 1152 { 1153 string newSource = genFrgShaderSrc(pp.frgSeed, m_varyings.frgInputs, 1154 frgName, useUniform, pp.frgScale); 1155 frgProg = createSingleShaderProgram(glu::SHADERTYPE_FRAGMENT, newSource); 1156 pipeline->useProgramStages(GL_FRAGMENT_SHADER_BIT, frgProg->getProgramName()); 1157 log() << TestLog::Message 1158 << "// Switched pipeline " << pipeline->getPipeline() 1159 << "'s fragment stage to single-shader program " << frgProg->getProgramName() 1160 << TestLog::EndMessage; 1161 } 1162 1163 if (m_params.switchVtx || m_params.switchFrg) 1164 m_status.check(pipeline->isValid(), 1165 "Pipeline became invalid after changing a stage's program"); 1166 1167 if (m_params.useUniform) 1168 { 1169 ProgramWrapper& vtxStage = *(vtxProg ? vtxProg : fullProg); 1170 ProgramWrapper& frgStage = *(frgProg ? frgProg : fullProg); 1171 1172 setUniform(vtxStage, vtxName, pp.vtxScale, m_params.useProgramUniform); 1173 setUniform(frgStage, frgName, pp.frgScale, m_params.useProgramUniform); 1174 } 1175 else 1176 log().writeMessage("// Programs use constants instead of uniforms"); 1177 1178 return MovePtr<Pipeline>(new Pipeline(pipeline, fullProg, vtxProg, frgProg)); 1179 } 1180 1181 MovePtr<ProgramWrapper> SeparateShaderTest::createReferenceProgram(const ProgramParams& pp) 1182 { 1183 bool useUniform = m_params.useUniform; 1184 const string vtxSrc = genVtxShaderSrc(pp.vtxSeed, 1185 varyingCompatVtxOutputs(m_varyings), 1186 "vtxScale", useUniform, pp.vtxScale); 1187 const string frgSrc = genFrgShaderSrc(pp.frgSeed, m_varyings.frgInputs, 1188 "frgScale", useUniform, pp.frgScale); 1189 MovePtr<ProgramWrapper> program = createShaderProgram(&vtxSrc, &frgSrc, false); 1190 GLuint progName = program->getProgramName(); 1191 1192 log() << TestLog::Message 1193 << "// Created monolithic shader program " << progName 1194 << TestLog::EndMessage; 1195 1196 if (useUniform) 1197 { 1198 setUniform(*program, "vtxScale", pp.vtxScale, false); 1199 setUniform(*program, "frgScale", pp.frgScale, false); 1200 } 1201 1202 return program; 1203 } 1204 1205 void SeparateShaderTest::drawSurface (Surface& dst, deUint32 seed) 1206 { 1207 const RenderContext& renderCtx = getRenderContext(); 1208 Random rnd (seed > 0 ? seed : m_rnd.getUint32()); 1209 Rectangle viewport = randomViewport(renderCtx, rnd, 1210 VIEWPORT_SIZE, VIEWPORT_SIZE); 1211 glClearColor(0.125f, 0.25f, 0.5f, 1.f); 1212 setViewport(renderCtx, viewport); 1213 glClear(GL_COLOR_BUFFER_BIT); 1214 GLU_CHECK_CALL(glDrawArrays(GL_TRIANGLES, 0, 3)); 1215 readRectangle(renderCtx, viewport, dst); 1216 log().writeMessage("// Drew a triangle"); 1217 } 1218 1219 void SeparateShaderTest::testPipelineRendering (MovePtr<Pipeline>& pipeOut) 1220 { 1221 ProgramParams pp = genProgramParams(m_rnd); 1222 Pipeline& pipeline = *(pipeOut = createPipeline(pp)); 1223 GLuint pipeName = pipeline.pipeline->getPipeline(); 1224 UniquePtr<ProgramWrapper> refProgram (createReferenceProgram(pp)); 1225 GLuint refProgName = refProgram->getProgramName(); 1226 Surface refSurface; 1227 Surface pipelineSurface; 1228 GLuint drawSeed = m_rnd.getUint32(); 1229 1230 glUseProgram(refProgName); 1231 log() << TestLog::Message << "// Use program " << refProgName << TestLog::EndMessage; 1232 drawSurface(refSurface, drawSeed); 1233 glUseProgram(0); 1234 1235 glBindProgramPipeline(pipeName); 1236 log() << TestLog::Message << "// Bind pipeline " << pipeName << TestLog::EndMessage; 1237 drawSurface(pipelineSurface, drawSeed); 1238 glBindProgramPipeline(0); 1239 1240 { 1241 const bool result = tcu::fuzzyCompare( 1242 m_testCtx.getLog(), "Program pipeline result", 1243 "Result of comparing a program pipeline with a monolithic program", 1244 refSurface, pipelineSurface, 0.05f, tcu::COMPARE_LOG_RESULT); 1245 1246 m_status.check(result, "Pipeline rendering differs from equivalent monolithic program"); 1247 } 1248 } 1249 1250 void SeparateShaderTest::testCurrentProgPriority (MovePtr<Pipeline>& pipeOut) 1251 { 1252 ProgramParams pipePp = genProgramParams(m_rnd); 1253 ProgramParams programPp = genProgramParams(m_rnd); 1254 Pipeline& pipeline = *(pipeOut = createPipeline(pipePp)); 1255 GLuint pipeName = pipeline.pipeline->getPipeline(); 1256 UniquePtr<ProgramWrapper> program (createReferenceProgram(programPp)); 1257 Surface pipelineSurface; 1258 Surface refSurface; 1259 Surface resultSurface; 1260 deUint32 drawSeed = m_rnd.getUint32(); 1261 1262 LOG_CALL(glBindProgramPipeline(pipeName)); 1263 drawSurface(pipelineSurface, drawSeed); 1264 LOG_CALL(glBindProgramPipeline(0)); 1265 1266 LOG_CALL(glUseProgram(program->getProgramName())); 1267 drawSurface(refSurface, drawSeed); 1268 LOG_CALL(glUseProgram(0)); 1269 1270 LOG_CALL(glUseProgram(program->getProgramName())); 1271 LOG_CALL(glBindProgramPipeline(pipeName)); 1272 drawSurface(resultSurface, drawSeed); 1273 LOG_CALL(glBindProgramPipeline(0)); 1274 LOG_CALL(glUseProgram(0)); 1275 1276 bool result = tcu::pixelThresholdCompare( 1277 m_testCtx.getLog(), "Active program rendering result", 1278 "Active program rendering result", 1279 refSurface, resultSurface, tcu::RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT); 1280 1281 m_status.check(result, "glBindProgramPipeline() affects glUseProgram()"); 1282 if (!result) 1283 log() << TestLog::Image("Pipeline image", "Image produced by pipeline", 1284 pipelineSurface); 1285 } 1286 1287 void SeparateShaderTest::testActiveProgramUniform (MovePtr<Pipeline>& pipeOut) 1288 { 1289 ProgramParams refPp = genProgramParams(m_rnd); 1290 Surface refSurface; 1291 Surface resultSurface; 1292 deUint32 drawSeed = m_rnd.getUint32(); 1293 1294 DE_UNREF(pipeOut); 1295 { 1296 UniquePtr<ProgramWrapper> refProg (createReferenceProgram(refPp)); 1297 GLuint refProgName = refProg->getProgramName(); 1298 1299 glUseProgram(refProgName); 1300 log() << TestLog::Message << "// Use reference program " << refProgName 1301 << TestLog::EndMessage; 1302 drawSurface(refSurface, drawSeed); 1303 glUseProgram(0); 1304 } 1305 1306 { 1307 ProgramParams changePp = genProgramParams(m_rnd); 1308 changePp.vtxSeed = refPp.vtxSeed; 1309 changePp.frgSeed = refPp.frgSeed; 1310 UniquePtr<ProgramWrapper> changeProg (createReferenceProgram(changePp)); 1311 GLuint changeName = changeProg->getProgramName(); 1312 ProgramPipeline pipeline (getRenderContext()); 1313 GLint vtxLoc = glGetUniformLocation(changeName, "vtxScale"); 1314 GLint frgLoc = glGetUniformLocation(changeName, "frgScale"); 1315 1316 LOG_CALL(glBindProgramPipeline(pipeline.getPipeline())); 1317 1318 pipeline.activeShaderProgram(changeName); 1319 log() << TestLog::Message << "// Set active shader program to " << changeName 1320 << TestLog::EndMessage; 1321 1322 glUniform1f(vtxLoc, refPp.vtxScale); 1323 log() << TestLog::Message 1324 << "// Set uniform 'vtxScale' to " << refPp.vtxScale << " using glUniform1f" 1325 << TestLog::EndMessage; 1326 glUniform1f(frgLoc, refPp.frgScale); 1327 log() << TestLog::Message 1328 << "// Set uniform 'frgScale' to " << refPp.frgScale << " using glUniform1f" 1329 << TestLog::EndMessage; 1330 1331 pipeline.activeShaderProgram(0); 1332 LOG_CALL(glBindProgramPipeline(0)); 1333 1334 LOG_CALL(glUseProgram(changeName)); 1335 drawSurface(resultSurface, drawSeed); 1336 LOG_CALL(glUseProgram(0)); 1337 } 1338 1339 bool result = tcu::fuzzyCompare( 1340 m_testCtx.getLog(), "Active program uniform result", 1341 "Active program uniform result", 1342 refSurface, resultSurface, 0.05f, tcu::COMPARE_LOG_RESULT); 1343 1344 m_status.check(result, 1345 "glUniform() did not correctly modify " 1346 "the active program of the bound pipeline"); 1347 } 1348 1349 void SeparateShaderTest::testPipelineQueryPrograms (MovePtr<Pipeline>& pipeOut) 1350 { 1351 ProgramParams pipePp = genProgramParams(m_rnd); 1352 Pipeline& pipeline = *(pipeOut = createPipeline(pipePp)); 1353 GLuint pipeName = pipeline.pipeline->getPipeline(); 1354 GLint queryVtx = 0; 1355 GLint queryFrg = 0; 1356 1357 LOG_CALL(GLU_CHECK_CALL(glGetProgramPipelineiv(pipeName, GL_VERTEX_SHADER, &queryVtx))); 1358 m_status.check(GLuint(queryVtx) == pipeline.getVertexProgram().getProgramName(), 1359 "Program pipeline query reported wrong vertex shader program"); 1360 1361 LOG_CALL(GLU_CHECK_CALL(glGetProgramPipelineiv(pipeName, GL_FRAGMENT_SHADER, &queryFrg))); 1362 m_status.check(GLuint(queryFrg) == pipeline.getFragmentProgram().getProgramName(), 1363 "Program pipeline query reported wrong fragment shader program"); 1364 } 1365 1366 void SeparateShaderTest::testPipelineQueryActive (MovePtr<Pipeline>& pipeOut) 1367 { 1368 ProgramParams pipePp = genProgramParams(m_rnd); 1369 Pipeline& pipeline = *(pipeOut = createPipeline(pipePp)); 1370 GLuint pipeName = pipeline.pipeline->getPipeline(); 1371 GLuint newActive = pipeline.getVertexProgram().getProgramName(); 1372 GLint queryActive = 0; 1373 1374 LOG_CALL(GLU_CHECK_CALL(glGetProgramPipelineiv(pipeName, GL_ACTIVE_PROGRAM, &queryActive))); 1375 m_status.check(queryActive == 0, 1376 "Program pipeline query reported non-zero initial active program"); 1377 1378 pipeline.pipeline->activeShaderProgram(newActive); 1379 log() << TestLog::Message 1380 << "Set pipeline " << pipeName << "'s active shader program to " << newActive 1381 << TestLog::EndMessage; 1382 1383 LOG_CALL(GLU_CHECK_CALL(glGetProgramPipelineiv(pipeName, GL_ACTIVE_PROGRAM, &queryActive))); 1384 m_status.check(GLuint(queryActive) == newActive, 1385 "Program pipeline query reported incorrect active program"); 1386 1387 pipeline.pipeline->activeShaderProgram(0); 1388 } 1389 1390 TestCase::IterateResult SeparateShaderTest::iterate (void) 1391 { 1392 MovePtr<Pipeline> pipeline; 1393 1394 DE_ASSERT(m_iterations > 0); 1395 1396 if (m_currentIteration == 0) 1397 logParams(log(), m_params); 1398 1399 ++m_currentIteration; 1400 1401 try 1402 { 1403 (this->*m_testFunc)(pipeline); 1404 log().writeMessage(""); 1405 } 1406 catch (const tcu::Exception&) 1407 { 1408 if (pipeline) 1409 logPipeline(log(), *pipeline); 1410 throw; 1411 } 1412 1413 if (m_status.getResult() != QP_TEST_RESULT_PASS) 1414 { 1415 if (pipeline) 1416 logPipeline(log(), *pipeline); 1417 } 1418 else if (m_currentIteration < m_iterations) 1419 return CONTINUE; 1420 1421 m_status.setTestContextResult(m_testCtx); 1422 return STOP; 1423 } 1424 1425 // Group construction utilities 1426 1427 enum ParamFlags 1428 { 1429 PARAMFLAGS_SWITCH_FRAGMENT = 1 << 0, 1430 PARAMFLAGS_SWITCH_VERTEX = 1 << 1, 1431 PARAMFLAGS_INIT_SINGLE = 1 << 2, 1432 PARAMFLAGS_LAST = 1 << 3, 1433 PARAMFLAGS_MASK = PARAMFLAGS_LAST - 1 1434 }; 1435 1436 bool areCaseParamFlagsValid (ParamFlags flags) 1437 { 1438 const ParamFlags switchAll = ParamFlags(PARAMFLAGS_SWITCH_VERTEX|PARAMFLAGS_SWITCH_FRAGMENT); 1439 1440 if ((flags & PARAMFLAGS_INIT_SINGLE) != 0) 1441 return (flags & switchAll) == 0 || 1442 (flags & switchAll) == switchAll; 1443 else 1444 return true; 1445 } 1446 1447 bool addRenderTest (TestCaseGroup& group, const string& namePrefix, const string& descPrefix, 1448 int numIterations, ParamFlags flags, TestParams params) 1449 { 1450 ostringstream name; 1451 ostringstream desc; 1452 1453 DE_ASSERT(areCaseParamFlagsValid(flags)); 1454 1455 name << namePrefix; 1456 desc << descPrefix; 1457 1458 params.initSingle = (flags & PARAMFLAGS_INIT_SINGLE) != 0; 1459 params.switchVtx = (flags & PARAMFLAGS_SWITCH_VERTEX) != 0; 1460 params.switchFrg = (flags & PARAMFLAGS_SWITCH_FRAGMENT) != 0; 1461 1462 name << (flags & PARAMFLAGS_INIT_SINGLE ? "single_program" : "separate_programs"); 1463 desc << (flags & PARAMFLAGS_INIT_SINGLE 1464 ? "Single program with two shaders" 1465 : "Separate programs for each shader"); 1466 1467 switch (flags & (PARAMFLAGS_SWITCH_FRAGMENT | PARAMFLAGS_SWITCH_VERTEX)) 1468 { 1469 case 0: 1470 break; 1471 case PARAMFLAGS_SWITCH_FRAGMENT: 1472 name << "_add_fragment"; 1473 desc << ", then add a fragment program"; 1474 break; 1475 case PARAMFLAGS_SWITCH_VERTEX: 1476 name << "_add_vertex"; 1477 desc << ", then add a vertex program"; 1478 break; 1479 case PARAMFLAGS_SWITCH_FRAGMENT | PARAMFLAGS_SWITCH_VERTEX: 1480 name << "_add_both"; 1481 desc << ", then add both vertex and shader programs"; 1482 break; 1483 } 1484 1485 if (!paramsValid(params)) 1486 return false; 1487 1488 group.addChild(new SeparateShaderTest(group.getContext(), name.str(), desc.str(), 1489 numIterations, params, 1490 &SeparateShaderTest::testPipelineRendering)); 1491 1492 return true; 1493 } 1494 1495 void describeInterpolation(const string& stage, Interpolation qual, 1496 ostringstream& name, ostringstream& desc) 1497 { 1498 if (qual == glu::INTERPOLATION_LAST) 1499 { 1500 desc << ", unqualified in " << stage << " shader"; 1501 return; 1502 } 1503 else 1504 { 1505 const string qualName = glu::getInterpolationName(qual); 1506 1507 name << "_" << stage << "_" << qualName; 1508 desc << ", qualified '" << qualName << "' in " << stage << " shader"; 1509 } 1510 } 1511 1512 1513 } // anonymous namespace 1514 1515 TestCaseGroup* createSeparateShaderTests (Context& ctx) 1516 { 1517 TestParams defaultParams; 1518 int numIterations = 4; 1519 TestCaseGroup* group = 1520 new TestCaseGroup(ctx, "separate_shader", "Separate shader tests"); 1521 1522 defaultParams.useUniform = false; 1523 defaultParams.initSingle = false; 1524 defaultParams.switchVtx = false; 1525 defaultParams.switchFrg = false; 1526 defaultParams.useCreateHelper = false; 1527 defaultParams.useProgramUniform = false; 1528 defaultParams.useSameName = false; 1529 defaultParams.varyings.count = 0; 1530 defaultParams.varyings.type = glu::TYPE_INVALID; 1531 defaultParams.varyings.binding = BINDING_NAME; 1532 defaultParams.varyings.vtxInterp = glu::INTERPOLATION_LAST; 1533 defaultParams.varyings.frgInterp = glu::INTERPOLATION_LAST; 1534 1535 TestCaseGroup* stagesGroup = 1536 new TestCaseGroup(ctx, "pipeline", "Pipeline configuration tests"); 1537 group->addChild(stagesGroup); 1538 1539 for (deUint32 flags = 0; flags < PARAMFLAGS_LAST << 2; ++flags) 1540 { 1541 TestParams params = defaultParams; 1542 ostringstream name; 1543 ostringstream desc; 1544 1545 if (!areCaseParamFlagsValid(ParamFlags(flags & PARAMFLAGS_MASK))) 1546 continue; 1547 1548 if (flags & (PARAMFLAGS_LAST << 1)) 1549 { 1550 params.useSameName = true; 1551 name << "same_"; 1552 desc << "Identically named "; 1553 } 1554 else 1555 { 1556 name << "different_"; 1557 desc << "Differently named "; 1558 } 1559 1560 if (flags & PARAMFLAGS_LAST) 1561 { 1562 params.useUniform = true; 1563 name << "uniform_"; 1564 desc << "uniforms, "; 1565 } 1566 else 1567 { 1568 name << "constant_"; 1569 desc << "constants, "; 1570 } 1571 1572 addRenderTest(*stagesGroup, name.str(), desc.str(), numIterations, 1573 ParamFlags(flags & PARAMFLAGS_MASK), params); 1574 } 1575 1576 TestCaseGroup* programUniformGroup = 1577 new TestCaseGroup(ctx, "program_uniform", "ProgramUniform tests"); 1578 group->addChild(programUniformGroup); 1579 1580 for (deUint32 flags = 0; flags < PARAMFLAGS_LAST; ++flags) 1581 { 1582 TestParams params = defaultParams; 1583 1584 if (!areCaseParamFlagsValid(ParamFlags(flags))) 1585 continue; 1586 1587 params.useUniform = true; 1588 params.useProgramUniform = true; 1589 1590 addRenderTest(*programUniformGroup, "", "", numIterations, ParamFlags(flags), params); 1591 } 1592 1593 TestCaseGroup* createShaderProgramGroup = 1594 new TestCaseGroup(ctx, "create_shader_program", "CreateShaderProgram tests"); 1595 group->addChild(createShaderProgramGroup); 1596 1597 for (deUint32 flags = 0; flags < PARAMFLAGS_LAST; ++flags) 1598 { 1599 TestParams params = defaultParams; 1600 1601 if (!areCaseParamFlagsValid(ParamFlags(flags))) 1602 continue; 1603 1604 params.useCreateHelper = true; 1605 1606 addRenderTest(*createShaderProgramGroup, "", "", numIterations, 1607 ParamFlags(flags), params); 1608 } 1609 1610 TestCaseGroup* interfaceGroup = 1611 new TestCaseGroup(ctx, "interface", "Shader interface compatibility tests"); 1612 group->addChild(interfaceGroup); 1613 1614 enum 1615 { 1616 NUM_INTERPOLATIONS = glu::INTERPOLATION_LAST + 1, // INTERPOLATION_LAST is valid 1617 INTERFACEFLAGS_LAST = BINDING_LAST * NUM_INTERPOLATIONS * NUM_INTERPOLATIONS 1618 }; 1619 1620 for (deUint32 flags = 0; flags < INTERFACEFLAGS_LAST; ++flags) 1621 { 1622 deUint32 tmpFlags = flags; 1623 Interpolation frgInterp = Interpolation(tmpFlags % NUM_INTERPOLATIONS); 1624 Interpolation vtxInterp = Interpolation((tmpFlags /= NUM_INTERPOLATIONS) 1625 % NUM_INTERPOLATIONS); 1626 BindingKind binding = BindingKind((tmpFlags /= NUM_INTERPOLATIONS) 1627 % BINDING_LAST); 1628 TestParams params = defaultParams; 1629 ostringstream name; 1630 ostringstream desc; 1631 1632 params.varyings.count = 1; 1633 params.varyings.type = glu::TYPE_FLOAT; 1634 params.varyings.binding = binding; 1635 params.varyings.vtxInterp = vtxInterp; 1636 params.varyings.frgInterp = frgInterp; 1637 1638 switch (binding) 1639 { 1640 case BINDING_LOCATION: 1641 name << "same_location"; 1642 desc << "Varyings have same location, "; 1643 break; 1644 case BINDING_NAME: 1645 name << "same_name"; 1646 desc << "Varyings have same name, "; 1647 break; 1648 default: 1649 DE_ASSERT(!"Impossible"); 1650 } 1651 1652 describeInterpolation("vertex", vtxInterp, name, desc); 1653 describeInterpolation("fragment", frgInterp, name, desc); 1654 1655 if (!paramsValid(params)) 1656 continue; 1657 1658 interfaceGroup->addChild( 1659 new SeparateShaderTest(ctx, name.str(), desc.str(), numIterations, params, 1660 &SeparateShaderTest::testPipelineRendering)); 1661 } 1662 1663 deUint32 baseSeed = ctx.getTestContext().getCommandLine().getBaseSeed(); 1664 Random rnd (deStringHash("separate_shader.random") + baseSeed); 1665 set<string> seen; 1666 TestCaseGroup* randomGroup = new TestCaseGroup( 1667 ctx, "random", "Random pipeline configuration tests"); 1668 group->addChild(randomGroup); 1669 1670 for (deUint32 i = 0; i < 128; ++i) 1671 { 1672 TestParams params; 1673 string code; 1674 deUint32 genIterations = 4096; 1675 1676 do 1677 { 1678 params = genParams(rnd.getUint32()); 1679 code = paramsCode(params); 1680 } while (de::contains(seen, code) && --genIterations > 0); 1681 1682 seen.insert(code); 1683 1684 string name = de::toString(i); // Would be code but baseSeed can change 1685 1686 randomGroup->addChild(new SeparateShaderTest( 1687 ctx, name, name, numIterations, params, 1688 &SeparateShaderTest::testPipelineRendering)); 1689 } 1690 1691 TestCaseGroup* apiGroup = 1692 new TestCaseGroup(ctx, "api", "Program pipeline API tests"); 1693 group->addChild(apiGroup); 1694 1695 { 1696 // More or less random parameters. These shouldn't have much effect, so just 1697 // do a single sample. 1698 TestParams params = defaultParams; 1699 params.useUniform = true; 1700 apiGroup->addChild(new SeparateShaderTest( 1701 ctx, 1702 "current_program_priority", 1703 "Test priority between current program and pipeline binding", 1704 1, params, &SeparateShaderTest::testCurrentProgPriority)); 1705 apiGroup->addChild(new SeparateShaderTest( 1706 ctx, 1707 "active_program_uniform", 1708 "Test that glUniform() affects a pipeline's active program", 1709 1, params, &SeparateShaderTest::testActiveProgramUniform)); 1710 1711 apiGroup->addChild(new SeparateShaderTest( 1712 ctx, 1713 "pipeline_programs", 1714 "Test queries for programs in program pipeline stages", 1715 1, params, &SeparateShaderTest::testPipelineQueryPrograms)); 1716 1717 apiGroup->addChild(new SeparateShaderTest( 1718 ctx, 1719 "pipeline_active", 1720 "Test query for active programs in a program pipeline", 1721 1, params, &SeparateShaderTest::testPipelineQueryActive)); 1722 } 1723 1724 TestCaseGroup* interfaceMismatchGroup = 1725 new TestCaseGroup(ctx, "validation", "Negative program pipeline interface matching"); 1726 group->addChild(interfaceMismatchGroup); 1727 1728 { 1729 gls::ShaderLibrary shaderLibrary (ctx.getTestContext(), ctx.getRenderContext(), ctx.getContextInfo()); 1730 const std::vector<tcu::TestNode*> children = shaderLibrary.loadShaderFile("shaders/separate_shader_validation.test"); 1731 1732 for (int i = 0; i < (int)children.size(); i++) 1733 interfaceMismatchGroup->addChild(children[i]); 1734 } 1735 1736 return group; 1737 } 1738 1739 } // Functional 1740 } // gles31 1741 } // deqp 1742