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