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