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