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