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