9de1a377bc18ebbb92e818408f3377e6c62e0bc0
[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         return true;
696 }
697
698 void logParams (TestLog& log, const TestParams& params)
699 {
700         // We don't log operational details here since those are shown
701         // in the log messages during execution.
702         MessageBuilder msg = log.message();
703
704         msg << "Pipeline configuration:\n";
705
706         msg << "Vertex and fragment shaders have "
707                 << (params.useUniform ? "uniform" : "constant") << "s with "
708                 << (params.useSameName ? "the same name" : "different names") << ".\n";
709
710         if (params.varyings.count == 0)
711                 msg << "There are no varyings.\n";
712         else
713         {
714                 if (params.varyings.count == 1)
715                         msg << "There is one varying.\n";
716                 else
717                         msg << "There are " << params.varyings.count << " varyings.\n";
718
719                 if (params.varyings.type == glu::TYPE_LAST)
720                         msg << "Varyings are of random types.\n";
721                 else
722                         msg << "Varyings are of type '"
723                                 << glu::getDataTypeName(params.varyings.type) << "'.\n";
724
725                 msg << "Varying outputs and inputs correspond ";
726                 switch (params.varyings.binding)
727                 {
728                         case BINDING_NAME:
729                                 msg << "by name.\n";
730                                 break;
731                         case BINDING_LOCATION:
732                                 msg << "by location.\n";
733                                 break;
734                         case BINDING_LAST:
735                                 msg << "randomly either by name or by location.\n";
736                                 break;
737                         default:
738                                 DE_ASSERT(!"Impossible");
739                 }
740
741                 msg << "In the vertex shader the varyings are qualified ";
742                 if (params.varyings.vtxInterp == glu::INTERPOLATION_LAST)
743                         msg << "with a random interpolation qualifier.\n";
744                 else
745                         msg << "'" << glu::getInterpolationName(params.varyings.vtxInterp) << "'.\n";
746
747                 msg << "In the fragment shader the varyings are qualified ";
748                 if (params.varyings.frgInterp == glu::INTERPOLATION_LAST)
749                         msg << "with a random interpolation qualifier.\n";
750                 else
751                         msg << "'" << glu::getInterpolationName(params.varyings.frgInterp) << "'.\n";
752         }
753
754         msg << TestLog::EndMessage;
755
756         log.writeMessage("");
757 }
758
759 TestParams genParams (deUint32 seed)
760 {
761         Random          rnd             (seed);
762         TestParams      params;
763         int                     tryNdx  = 0;
764
765         do
766         {
767                 params.initSingle                       = rnd.getBool();
768                 params.switchVtx                        = rnd.getBool();
769                 params.switchFrg                        = rnd.getBool();
770                 params.useUniform                       = rnd.getBool();
771                 params.useProgramUniform        = params.useUniform && rnd.getBool();
772                 params.useCreateHelper          = rnd.getBool();
773                 params.useSameName                      = rnd.getBool();
774                 {
775                         int i = rnd.getInt(-1, 3);
776                         params.varyings.count = (i == -1 ? 0 : 1 << i);
777                 }
778                 if (params.varyings.count > 0)
779                 {
780                         params.varyings.type            = glu::TYPE_LAST;
781                         params.varyings.binding         = BINDING_LAST;
782                         params.varyings.vtxInterp       = glu::INTERPOLATION_LAST;
783                         params.varyings.frgInterp       = glu::INTERPOLATION_LAST;
784                 }
785                 else
786                 {
787                         params.varyings.type            = glu::TYPE_INVALID;
788                         params.varyings.binding         = BINDING_LAST;
789                         params.varyings.vtxInterp       = glu::INTERPOLATION_LAST;
790                         params.varyings.frgInterp       = glu::INTERPOLATION_LAST;
791                 }
792
793                 tryNdx += 1;
794         } while (!paramsValid(params) && tryNdx < 16);
795
796         DE_ASSERT(paramsValid(params));
797
798         return params;
799 }
800
801 // Program pipeline wrapper that retains references to component programs.
802
803 struct Pipeline
804 {
805                                                                 Pipeline                        (MovePtr<ProgramPipeline>       pipeline_,
806                                                                                                          MovePtr<ProgramWrapper>        fullProg_,
807                                                                                                          MovePtr<ProgramWrapper>        vtxProg_,
808                                                                                                          MovePtr<ProgramWrapper>        frgProg_)
809                                                                         : pipeline      (pipeline_)
810                                                                         , fullProg      (fullProg_)
811                                                                         , vtxProg       (vtxProg_)
812                                                                         , frgProg       (frgProg_) {}
813
814         ProgramWrapper&                         getVertexProgram        (void) const
815         {
816                 return vtxProg ? *vtxProg : *fullProg;
817         }
818
819         ProgramWrapper&                         getFragmentProgram      (void) const
820         {
821                 return frgProg ? *frgProg : *fullProg;
822         }
823
824         UniquePtr<ProgramPipeline>      pipeline;
825         UniquePtr<ProgramWrapper>       fullProg;
826         UniquePtr<ProgramWrapper>       vtxProg;
827         UniquePtr<ProgramWrapper>       frgProg;
828 };
829
830 void logPipeline(TestLog& log, const Pipeline& pipeline)
831 {
832         ProgramWrapper& vtxProg = pipeline.getVertexProgram();
833         ProgramWrapper& frgProg = pipeline.getFragmentProgram();
834
835         log.writeMessage("// Failed program pipeline:");
836         if (&vtxProg == &frgProg)
837         {
838                 log.writeMessage("// Common program for both vertex and fragment stages:");
839                 vtxProg.writeToLog(log);
840         }
841         else
842         {
843                 log.writeMessage("// Vertex stage program:");
844                 vtxProg.writeToLog(log);
845                 log.writeMessage("// Fragment stage program:");
846                 frgProg.writeToLog(log);
847         }
848 }
849
850 // Rectangle
851
852 struct Rectangle
853 {
854                         Rectangle       (int x_, int y_, int width_, int height_)
855                                 : x                     (x_)
856                                 , y                     (y_)
857                                 , width         (width_)
858                                 , height        (height_) {}
859         int     x;
860         int     y;
861         int     width;
862         int     height;
863 };
864
865 void setViewport (const RenderContext& renderCtx, const Rectangle& rect)
866 {
867         renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height);
868 }
869
870 void readRectangle (const RenderContext& renderCtx, const Rectangle& rect, Surface& dst)
871 {
872         dst.setSize(rect.width, rect.height);
873         glu::readPixels(renderCtx, rect.x, rect.y, dst.getAccess());
874 }
875
876 Rectangle randomViewport (const RenderContext& ctx, Random& rnd,
877                                                   GLint maxWidth, GLint maxHeight)
878 {
879         const RenderTarget&     target  = ctx.getRenderTarget();
880         GLint                           width   = de::min(target.getWidth(), maxWidth);
881         GLint                           xOff    = rnd.getInt(0, target.getWidth() - width);
882         GLint                           height  = de::min(target.getHeight(), maxHeight);
883         GLint                           yOff    = rnd.getInt(0, target.getHeight() - height);
884
885         return Rectangle(xOff, yOff, width, height);
886 }
887
888 // SeparateShaderTest
889
890 class SeparateShaderTest : public TestCase, private CallLogWrapper
891 {
892 public:
893         typedef void                    (SeparateShaderTest::*TestFunc)
894                                                                                                                 (MovePtr<Pipeline>&             pipeOut);
895
896                                                         SeparateShaderTest                      (Context&                               ctx,
897                                                                                                                  const string&                  name,
898                                                                                                                  const string&                  description,
899                                                                                                                  int                                    iterations,
900                                                                                                                  const TestParams&              params,
901                                                                                                                  TestFunc                               testFunc);
902
903         IterateResult                   iterate                                         (void);
904
905         void                                    testPipelineRendering           (MovePtr<Pipeline>&             pipeOut);
906         void                                    testCurrentProgPriority         (MovePtr<Pipeline>&             pipeOut);
907         void                                    testActiveProgramUniform        (MovePtr<Pipeline>&             pipeOut);
908         void                                    testPipelineQueryActive         (MovePtr<Pipeline>&             pipeOut);
909         void                                    testPipelineQueryPrograms       (MovePtr<Pipeline>&             pipeOut);
910
911 private:
912         TestLog&                                log                                                     (void);
913         const RenderContext&    getRenderContext                        (void);
914
915         void                                    setUniform                                      (ProgramWrapper&                program,
916                                                                                                                  const string&                  uniformName,
917                                                                                                                  GLfloat                                value,
918                                                                                                                  bool                                   useProgramUni);
919
920         void                                    drawSurface                                     (Surface&                               dst,
921                                                                                                                  deUint32                               seed = 0);
922
923         MovePtr<ProgramWrapper> createShaderProgram                     (const string*                  vtxSource,
924                                                                                                                  const string*                  frgSource,
925                                                                                                                  bool                                   separable);
926
927         MovePtr<ProgramWrapper> createSingleShaderProgram       (ShaderType                     shaderType,
928                                                                                                                  const string&                  src);
929
930         MovePtr<Pipeline>               createPipeline                          (const ProgramParams&   pp);
931
932         MovePtr<ProgramWrapper> createReferenceProgram          (const ProgramParams&   pp);
933
934         int                                             m_iterations;
935         int                                             m_currentIteration;
936         TestParams                              m_params;
937         TestFunc                                m_testFunc;
938         Random                                  m_rnd;
939         ResultCollector                 m_status;
940         VaryingInterface                m_varyings;
941
942         // Per-iteration state required for logging on exception
943         MovePtr<ProgramWrapper> m_fullProg;
944         MovePtr<ProgramWrapper> m_vtxProg;
945         MovePtr<ProgramWrapper> m_frgProg;
946
947 };
948
949 const RenderContext& SeparateShaderTest::getRenderContext (void)
950 {
951         return m_context.getRenderContext();
952 }
953
954 TestLog& SeparateShaderTest::log (void)
955 {
956         return m_testCtx.getLog();
957 }
958
959 SeparateShaderTest::SeparateShaderTest (Context&                        ctx,
960                                                                                 const string&           name,
961                                                                                 const string&           description,
962                                                                                 int                                     iterations,
963                                                                                 const TestParams&       params,
964                                                                                 TestFunc                        testFunc)
965         : TestCase                      (ctx, name.c_str(), description.c_str())
966         , CallLogWrapper        (ctx.getRenderContext().getFunctions(), log())
967         , m_iterations          (iterations)
968         , m_currentIteration(0)
969         , m_params                      (params)
970         , m_testFunc            (testFunc)
971         , m_rnd                         (paramsSeed(params))
972         , m_status                      (log(), "// ")
973         , m_varyings            (genVaryingInterface(params.varyings, m_rnd))
974 {
975         DE_ASSERT(paramsValid(params));
976 }
977
978 MovePtr<ProgramWrapper> SeparateShaderTest::createShaderProgram (const string*  vtxSource,
979                                                                                                                                  const string*  frgSource,
980                                                                                                                                  bool                   separable)
981 {
982         ProgramSources sources;
983
984         if (vtxSource != DE_NULL)
985                 sources << VertexSource(*vtxSource);
986         if (frgSource != DE_NULL)
987                 sources << FragmentSource(*frgSource);
988         sources << ProgramSeparable(separable);
989
990         MovePtr<ShaderProgramWrapper> wrapper (new ShaderProgramWrapper(getRenderContext(),
991                                                                                                                                         sources));
992         if (!wrapper->getShaderProgram().isOk())
993         {
994                 log().writeMessage("Couldn't create shader program");
995                 wrapper->writeToLog(log());
996                 TCU_FAIL("Couldn't create shader program");
997         }
998
999         return MovePtr<ProgramWrapper>(wrapper.release());
1000 }
1001
1002 MovePtr<ProgramWrapper> SeparateShaderTest::createSingleShaderProgram (ShaderType shaderType,
1003                                                                                                                                            const string& src)
1004 {
1005         const RenderContext&    renderCtx       = getRenderContext();
1006
1007         if (m_params.useCreateHelper)
1008         {
1009                 const char*     const   srcStr          = src.c_str();
1010                 const GLenum            glType          = glu::getGLShaderType(shaderType);
1011                 const GLuint            programName     = glCreateShaderProgramv(glType, 1, &srcStr);
1012
1013                 if (glGetError() != GL_NO_ERROR || programName == 0)
1014                 {
1015                         qpShaderType qpType = glu::getLogShaderType(shaderType);
1016
1017                         log() << TestLog::Message << "glCreateShaderProgramv() failed"
1018                                   << TestLog::EndMessage
1019                                   << TestLog::ShaderProgram(false, "[glCreateShaderProgramv() failed]")
1020                                   << TestLog::Shader(qpType, src,
1021                                                                          false, "[glCreateShaderProgramv() failed]")
1022                                   << TestLog::EndShaderProgram;
1023                         TCU_FAIL("glCreateShaderProgramv() failed");
1024                 }
1025
1026                 RawProgramWrapper* const        wrapper = new RawProgramWrapper(renderCtx, programName,
1027                                                                                                                                         shaderType, src);
1028                 MovePtr<ProgramWrapper>         wrapperPtr(wrapper);
1029                 Program&                                        program = wrapper->getProgram();
1030
1031                 if (!program.getLinkStatus())
1032                 {
1033                         log().writeMessage("glCreateShaderProgramv() failed at linking");
1034                         wrapper->writeToLog(log());
1035                         TCU_FAIL("glCreateShaderProgram() failed at linking");
1036                 }
1037                 return wrapperPtr;
1038         }
1039         else
1040         {
1041                 switch (shaderType)
1042                 {
1043                         case glu::SHADERTYPE_VERTEX:
1044                                 return createShaderProgram(&src, DE_NULL, true);
1045                         case glu::SHADERTYPE_FRAGMENT:
1046                                 return createShaderProgram(DE_NULL, &src, true);
1047                         default:
1048                                 DE_ASSERT(!"Impossible case");
1049                 }
1050         }
1051         return MovePtr<ProgramWrapper>(); // Shut up compiler warnings.
1052 }
1053
1054 void SeparateShaderTest::setUniform (ProgramWrapper&    program,
1055                                                                          const string&          uniformName,
1056                                                                          GLfloat                        value,
1057                                                                          bool                           useProgramUniform)
1058 {
1059         const GLuint            progName        = program.getProgramName();
1060         const GLint                     location        = glGetUniformLocation(progName, uniformName.c_str());
1061         MessageBuilder          msg                     = log().message();
1062
1063         msg << "// Set program " << progName << "'s uniform '" << uniformName << "' to " << value;
1064         if (useProgramUniform)
1065         {
1066                 msg << " using glProgramUniform1f";
1067                 glProgramUniform1f(progName, location, value);
1068         }
1069         else
1070         {
1071                 msg << " using glUseProgram and glUniform1f";
1072                 glUseProgram(progName);
1073                 glUniform1f(location, value);
1074                 glUseProgram(0);
1075         }
1076         msg << TestLog::EndMessage;
1077 }
1078
1079 MovePtr<Pipeline> SeparateShaderTest::createPipeline(const ProgramParams& pp)
1080 {
1081         const bool              useUniform      = m_params.useUniform;
1082         const string    vtxName         = m_params.useSameName ? "scale" : "vtxScale";
1083         const deUint32  initVtxSeed     = m_params.switchVtx ? m_rnd.getUint32() : pp.vtxSeed;
1084
1085         const string    frgName         = m_params.useSameName ? "scale" : "frgScale";
1086         const deUint32  initFrgSeed     = m_params.switchFrg ? m_rnd.getUint32() : pp.frgSeed;
1087         const string    frgSource       = genFrgShaderSrc(initFrgSeed, m_varyings.frgInputs,
1088                                                                                                   frgName, useUniform, pp.frgScale);
1089
1090         const RenderContext&            renderCtx       = getRenderContext();
1091         MovePtr<ProgramPipeline>        pipeline        (new ProgramPipeline(renderCtx));
1092         MovePtr<ProgramWrapper>         fullProg;
1093         MovePtr<ProgramWrapper>         vtxProg;
1094         MovePtr<ProgramWrapper>         frgProg;
1095
1096         // We cannot allow a situation where we have a single program with a
1097         // single uniform, because then the vertex and fragment shader uniforms
1098         // would not be distinct in the final pipeline, and we are going to test
1099         // that altering one uniform will not affect the other.
1100         DE_ASSERT(!(m_params.initSingle && m_params.useSameName &&
1101                                 !m_params.switchVtx && !m_params.switchFrg));
1102
1103         if (m_params.initSingle)
1104         {
1105                 string vtxSource = genVtxShaderSrc(initVtxSeed,
1106                                                                                    varyingCompatVtxOutputs(m_varyings),
1107                                                                                    vtxName, useUniform, pp.vtxScale);
1108                 fullProg = createShaderProgram(&vtxSource, &frgSource, true);
1109                 pipeline->useProgramStages(GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT,
1110                                                                    fullProg->getProgramName());
1111                 log() << TestLog::Message
1112                           << "// Created pipeline " << pipeline->getPipeline()
1113                           << " with two-shader program " << fullProg->getProgramName()
1114                           << TestLog::EndMessage;
1115         }
1116         else
1117         {
1118                 string vtxSource = genVtxShaderSrc(initVtxSeed, m_varyings.vtxOutputs,
1119                                                                                    vtxName, useUniform, pp.vtxScale);
1120                 vtxProg = createSingleShaderProgram(glu::SHADERTYPE_VERTEX, vtxSource);
1121                 pipeline->useProgramStages(GL_VERTEX_SHADER_BIT, vtxProg->getProgramName());
1122
1123                 frgProg = createSingleShaderProgram(glu::SHADERTYPE_FRAGMENT, frgSource);
1124                 pipeline->useProgramStages(GL_FRAGMENT_SHADER_BIT, frgProg->getProgramName());
1125
1126                 log() << TestLog::Message
1127                           << "// Created pipeline " << pipeline->getPipeline()
1128                           << " with vertex program " << vtxProg->getProgramName()
1129                           << " and fragment program " << frgProg->getProgramName()
1130                           << TestLog::EndMessage;
1131         }
1132
1133         m_status.check(pipeline->isValid(),
1134                                    "Pipeline is invalid after initialization");
1135
1136         if (m_params.switchVtx)
1137         {
1138                 string newSource = genVtxShaderSrc(pp.vtxSeed, m_varyings.vtxOutputs,
1139                                                                                    vtxName, useUniform, pp.vtxScale);
1140                 vtxProg = createSingleShaderProgram(glu::SHADERTYPE_VERTEX, newSource);
1141                 pipeline->useProgramStages(GL_VERTEX_SHADER_BIT, vtxProg->getProgramName());
1142                 log() << TestLog::Message
1143                           << "// Switched pipeline " << pipeline->getPipeline()
1144                           << "'s vertex stage to single-shader program " << vtxProg->getProgramName()
1145                           << TestLog::EndMessage;
1146         }
1147         if (m_params.switchFrg)
1148         {
1149                 string newSource = genFrgShaderSrc(pp.frgSeed, m_varyings.frgInputs,
1150                                                                                    frgName, useUniform, pp.frgScale);
1151                 frgProg = createSingleShaderProgram(glu::SHADERTYPE_FRAGMENT, newSource);
1152                 pipeline->useProgramStages(GL_FRAGMENT_SHADER_BIT, frgProg->getProgramName());
1153                 log() << TestLog::Message
1154                           << "// Switched pipeline " << pipeline->getPipeline()
1155                           << "'s fragment stage to single-shader program " << frgProg->getProgramName()
1156                           << TestLog::EndMessage;
1157         }
1158
1159         if (m_params.switchVtx || m_params.switchFrg)
1160                 m_status.check(pipeline->isValid(),
1161                                            "Pipeline became invalid after changing a stage's program");
1162
1163         if (m_params.useUniform)
1164         {
1165                 ProgramWrapper& vtxStage = *(vtxProg ? vtxProg : fullProg);
1166                 ProgramWrapper& frgStage = *(frgProg ? frgProg : fullProg);
1167
1168                 setUniform(vtxStage, vtxName, pp.vtxScale, m_params.useProgramUniform);
1169                 setUniform(frgStage, frgName, pp.frgScale, m_params.useProgramUniform);
1170         }
1171         else
1172                 log().writeMessage("// Programs use constants instead of uniforms");
1173
1174         return MovePtr<Pipeline>(new Pipeline(pipeline, fullProg, vtxProg, frgProg));
1175 }
1176
1177 MovePtr<ProgramWrapper> SeparateShaderTest::createReferenceProgram(const ProgramParams& pp)
1178 {
1179         bool                                    useUniform      = m_params.useUniform;
1180         const string                    vtxSrc          = genVtxShaderSrc(pp.vtxSeed,
1181                                                                                                                   varyingCompatVtxOutputs(m_varyings),
1182                                                                                                                   "vtxScale", useUniform, pp.vtxScale);
1183         const string                    frgSrc          = genFrgShaderSrc(pp.frgSeed, m_varyings.frgInputs,
1184                                                                                                                   "frgScale", useUniform, pp.frgScale);
1185         MovePtr<ProgramWrapper> program         = createShaderProgram(&vtxSrc, &frgSrc, false);
1186         GLuint                                  progName        = program->getProgramName();
1187
1188         log() << TestLog::Message
1189                   << "// Created monolithic shader program " << progName
1190                   << TestLog::EndMessage;
1191
1192         if (useUniform)
1193         {
1194                 setUniform(*program, "vtxScale", pp.vtxScale, false);
1195                 setUniform(*program, "frgScale", pp.frgScale, false);
1196         }
1197
1198         return program;
1199 }
1200
1201 void SeparateShaderTest::drawSurface (Surface& dst, deUint32 seed)
1202 {
1203         const RenderContext&    renderCtx       = getRenderContext();
1204         Random                                  rnd                     (seed > 0 ? seed : m_rnd.getUint32());
1205         Rectangle                               viewport        = randomViewport(renderCtx, rnd,
1206                                                                                                                  VIEWPORT_SIZE, VIEWPORT_SIZE);
1207         glClearColor(0.125f, 0.25f, 0.5f, 1.f);
1208         setViewport(renderCtx, viewport);
1209         glClear(GL_COLOR_BUFFER_BIT);
1210         GLU_CHECK_CALL(glDrawArrays(GL_TRIANGLES, 0, 3));
1211         readRectangle(renderCtx, viewport, dst);
1212         log().writeMessage("// Drew a triangle");
1213 }
1214
1215 void SeparateShaderTest::testPipelineRendering (MovePtr<Pipeline>& pipeOut)
1216 {
1217         ProgramParams                           pp                      = genProgramParams(m_rnd);
1218         Pipeline&                                       pipeline        = *(pipeOut = createPipeline(pp));
1219         GLuint                                          pipeName        = pipeline.pipeline->getPipeline();
1220         UniquePtr<ProgramWrapper>       refProgram      (createReferenceProgram(pp));
1221         GLuint                                          refProgName     = refProgram->getProgramName();
1222         Surface                                         refSurface;
1223         Surface                                         pipelineSurface;
1224         GLuint                                          drawSeed        = m_rnd.getUint32();
1225
1226         glUseProgram(refProgName);
1227         log() << TestLog::Message << "// Use program " << refProgName << TestLog::EndMessage;
1228         drawSurface(refSurface, drawSeed);
1229         glUseProgram(0);
1230
1231         glBindProgramPipeline(pipeName);
1232         log() << TestLog::Message << "// Bind pipeline " << pipeName << TestLog::EndMessage;
1233         drawSurface(pipelineSurface, drawSeed);
1234         glBindProgramPipeline(0);
1235
1236         {
1237                 const bool result = tcu::fuzzyCompare(
1238                         m_testCtx.getLog(), "Program pipeline result",
1239                         "Result of comparing a program pipeline with a monolithic program",
1240                         refSurface, pipelineSurface, 0.05f, tcu::COMPARE_LOG_RESULT);
1241
1242                 m_status.check(result, "Pipeline rendering differs from equivalent monolithic program");
1243         }
1244 }
1245
1246 void SeparateShaderTest::testCurrentProgPriority (MovePtr<Pipeline>& pipeOut)
1247 {
1248         ProgramParams                           pipePp          = genProgramParams(m_rnd);
1249         ProgramParams                           programPp       = genProgramParams(m_rnd);
1250         Pipeline&                                       pipeline        = *(pipeOut = createPipeline(pipePp));
1251         GLuint                                          pipeName        = pipeline.pipeline->getPipeline();
1252         UniquePtr<ProgramWrapper>       program         (createReferenceProgram(programPp));
1253         Surface                                         pipelineSurface;
1254         Surface                                         refSurface;
1255         Surface                                         resultSurface;
1256         deUint32                                        drawSeed        = m_rnd.getUint32();
1257
1258         LOG_CALL(glBindProgramPipeline(pipeName));
1259         drawSurface(pipelineSurface, drawSeed);
1260         LOG_CALL(glBindProgramPipeline(0));
1261
1262         LOG_CALL(glUseProgram(program->getProgramName()));
1263         drawSurface(refSurface, drawSeed);
1264         LOG_CALL(glUseProgram(0));
1265
1266         LOG_CALL(glUseProgram(program->getProgramName()));
1267         LOG_CALL(glBindProgramPipeline(pipeName));
1268         drawSurface(resultSurface, drawSeed);
1269         LOG_CALL(glBindProgramPipeline(0));
1270         LOG_CALL(glUseProgram(0));
1271
1272         bool result = tcu::pixelThresholdCompare(
1273                 m_testCtx.getLog(), "Active program rendering result",
1274                 "Active program rendering result",
1275                 refSurface, resultSurface, tcu::RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT);
1276
1277         m_status.check(result, "glBindProgramPipeline() affects glUseProgram()");
1278         if (!result)
1279                 log() << TestLog::Image("Pipeline image", "Image produced by pipeline",
1280                                                                 pipelineSurface);
1281 }
1282
1283 void SeparateShaderTest::testActiveProgramUniform (MovePtr<Pipeline>& pipeOut)
1284 {
1285         ProgramParams                           refPp                   = genProgramParams(m_rnd);
1286         Surface                                         refSurface;
1287         Surface                                         resultSurface;
1288         deUint32                                        drawSeed                = m_rnd.getUint32();
1289
1290         DE_UNREF(pipeOut);
1291         {
1292                 UniquePtr<ProgramWrapper>       refProg         (createReferenceProgram(refPp));
1293                 GLuint                                          refProgName     = refProg->getProgramName();
1294
1295                 glUseProgram(refProgName);
1296                 log() << TestLog::Message << "// Use reference program " << refProgName
1297                           << TestLog::EndMessage;
1298                 drawSurface(refSurface, drawSeed);
1299                 glUseProgram(0);
1300         }
1301
1302         {
1303                 ProgramParams                           changePp        = genProgramParams(m_rnd);
1304                 changePp.vtxSeed                                                = refPp.vtxSeed;
1305                 changePp.frgSeed                                                = refPp.frgSeed;
1306                 UniquePtr<ProgramWrapper>       changeProg      (createReferenceProgram(changePp));
1307                 GLuint                                          changeName      = changeProg->getProgramName();
1308                 ProgramPipeline                         pipeline        (getRenderContext());
1309                 GLint                                           vtxLoc          = glGetUniformLocation(changeName, "vtxScale");
1310                 GLint                                           frgLoc          = glGetUniformLocation(changeName, "frgScale");
1311
1312                 LOG_CALL(glBindProgramPipeline(pipeline.getPipeline()));
1313
1314                 pipeline.activeShaderProgram(changeName);
1315                 log() << TestLog::Message << "// Set active shader program to " << changeName
1316                           << TestLog::EndMessage;
1317
1318                 glUniform1f(vtxLoc, refPp.vtxScale);
1319                 log() << TestLog::Message
1320                           << "// Set uniform 'vtxScale' to " << refPp.vtxScale << " using glUniform1f"
1321                           << TestLog::EndMessage;
1322                 glUniform1f(frgLoc, refPp.frgScale);
1323                 log() << TestLog::Message
1324                           << "// Set uniform 'frgScale' to " << refPp.frgScale << " using glUniform1f"
1325                           << TestLog::EndMessage;
1326
1327                 pipeline.activeShaderProgram(0);
1328                 LOG_CALL(glBindProgramPipeline(0));
1329
1330                 LOG_CALL(glUseProgram(changeName));
1331                 drawSurface(resultSurface, drawSeed);
1332                 LOG_CALL(glUseProgram(0));
1333         }
1334
1335         bool result = tcu::fuzzyCompare(
1336                 m_testCtx.getLog(), "Active program uniform result",
1337                 "Active program uniform result",
1338                 refSurface, resultSurface, 0.05f, tcu::COMPARE_LOG_RESULT);
1339
1340         m_status.check(result,
1341                                    "glUniform() did not correctly modify "
1342                                    "the active program of the bound pipeline");
1343 }
1344
1345 void SeparateShaderTest::testPipelineQueryPrograms (MovePtr<Pipeline>& pipeOut)
1346 {
1347         ProgramParams                           pipePp          = genProgramParams(m_rnd);
1348         Pipeline&                                       pipeline        = *(pipeOut = createPipeline(pipePp));
1349         GLuint                                          pipeName        = pipeline.pipeline->getPipeline();
1350         GLint                                           queryVtx        = 0;
1351         GLint                                           queryFrg        = 0;
1352
1353         LOG_CALL(GLU_CHECK_CALL(glGetProgramPipelineiv(pipeName, GL_VERTEX_SHADER, &queryVtx)));
1354         m_status.check(GLuint(queryVtx) == pipeline.getVertexProgram().getProgramName(),
1355                                    "Program pipeline query reported wrong vertex shader program");
1356
1357         LOG_CALL(GLU_CHECK_CALL(glGetProgramPipelineiv(pipeName, GL_FRAGMENT_SHADER, &queryFrg)));
1358         m_status.check(GLuint(queryFrg) == pipeline.getFragmentProgram().getProgramName(),
1359                                    "Program pipeline query reported wrong fragment shader program");
1360 }
1361
1362 void SeparateShaderTest::testPipelineQueryActive (MovePtr<Pipeline>& pipeOut)
1363 {
1364         ProgramParams                           pipePp          = genProgramParams(m_rnd);
1365         Pipeline&                                       pipeline        = *(pipeOut = createPipeline(pipePp));
1366         GLuint                                          pipeName        = pipeline.pipeline->getPipeline();
1367         GLuint                                          newActive       = pipeline.getVertexProgram().getProgramName();
1368         GLint                                           queryActive     = 0;
1369
1370         LOG_CALL(GLU_CHECK_CALL(glGetProgramPipelineiv(pipeName, GL_ACTIVE_PROGRAM, &queryActive)));
1371         m_status.check(queryActive == 0,
1372                                    "Program pipeline query reported non-zero initial active program");
1373
1374         pipeline.pipeline->activeShaderProgram(newActive);
1375         log() << TestLog::Message
1376                   << "Set pipeline " << pipeName << "'s active shader program to " << newActive
1377                   << TestLog::EndMessage;
1378
1379         LOG_CALL(GLU_CHECK_CALL(glGetProgramPipelineiv(pipeName, GL_ACTIVE_PROGRAM, &queryActive)));
1380         m_status.check(GLuint(queryActive) == newActive,
1381                                    "Program pipeline query reported incorrect active program");
1382
1383         pipeline.pipeline->activeShaderProgram(0);
1384 }
1385
1386 TestCase::IterateResult SeparateShaderTest::iterate (void)
1387 {
1388         MovePtr<Pipeline> pipeline;
1389
1390         DE_ASSERT(m_iterations > 0);
1391
1392         if (m_currentIteration == 0)
1393                 logParams(log(), m_params);
1394
1395         ++m_currentIteration;
1396
1397         try
1398         {
1399                 (this->*m_testFunc)(pipeline);
1400                 log().writeMessage("");
1401         }
1402         catch (const tcu::Exception&)
1403         {
1404                 if (pipeline)
1405                         logPipeline(log(), *pipeline);
1406                 throw;
1407         }
1408
1409         if (m_status.getResult() != QP_TEST_RESULT_PASS)
1410         {
1411                 if (pipeline)
1412                         logPipeline(log(), *pipeline);
1413         }
1414         else if (m_currentIteration < m_iterations)
1415                 return CONTINUE;
1416
1417         m_status.setTestContextResult(m_testCtx);
1418         return STOP;
1419 }
1420
1421 // Group construction utilities
1422
1423 enum ParamFlags
1424 {
1425         PARAMFLAGS_SWITCH_FRAGMENT      = 1 << 0,
1426         PARAMFLAGS_SWITCH_VERTEX        = 1 << 1,
1427         PARAMFLAGS_INIT_SINGLE          = 1 << 2,
1428         PARAMFLAGS_LAST                         = 1 << 3,
1429         PARAMFLAGS_MASK                         = PARAMFLAGS_LAST - 1
1430 };
1431
1432 bool areCaseParamFlagsValid (ParamFlags flags)
1433 {
1434         const ParamFlags switchAll = ParamFlags(PARAMFLAGS_SWITCH_VERTEX|PARAMFLAGS_SWITCH_FRAGMENT);
1435
1436         if ((flags & PARAMFLAGS_INIT_SINGLE) != 0)
1437                 return (flags & switchAll) == 0 ||
1438                            (flags & switchAll) == switchAll;
1439         else
1440                 return true;
1441 }
1442
1443 bool addRenderTest (TestCaseGroup& group, const string& namePrefix, const string& descPrefix,
1444                                         int numIterations, ParamFlags flags, TestParams params)
1445 {
1446         ostringstream   name;
1447         ostringstream   desc;
1448
1449         DE_ASSERT(areCaseParamFlagsValid(flags));
1450
1451         name << namePrefix;
1452         desc << descPrefix;
1453
1454         params.initSingle       = (flags & PARAMFLAGS_INIT_SINGLE) != 0;
1455         params.switchVtx        = (flags & PARAMFLAGS_SWITCH_VERTEX) != 0;
1456         params.switchFrg        = (flags & PARAMFLAGS_SWITCH_FRAGMENT) != 0;
1457
1458         name << (flags & PARAMFLAGS_INIT_SINGLE ? "single_program" : "separate_programs");
1459         desc << (flags & PARAMFLAGS_INIT_SINGLE
1460                          ? "Single program with two shaders"
1461                          : "Separate programs for each shader");
1462
1463         switch (flags & (PARAMFLAGS_SWITCH_FRAGMENT | PARAMFLAGS_SWITCH_VERTEX))
1464         {
1465                 case 0:
1466                         break;
1467                 case PARAMFLAGS_SWITCH_FRAGMENT:
1468                         name << "_add_fragment";
1469                         desc << ", then add a fragment program";
1470                         break;
1471                 case PARAMFLAGS_SWITCH_VERTEX:
1472                         name << "_add_vertex";
1473                         desc << ", then add a vertex program";
1474                         break;
1475                 case PARAMFLAGS_SWITCH_FRAGMENT | PARAMFLAGS_SWITCH_VERTEX:
1476                         name << "_add_both";
1477                         desc << ", then add both vertex and shader programs";
1478                         break;
1479         }
1480
1481         if (!paramsValid(params))
1482                 return false;
1483
1484         group.addChild(new SeparateShaderTest(group.getContext(), name.str(), desc.str(),
1485                                                                                   numIterations, params,
1486                                                                                   &SeparateShaderTest::testPipelineRendering));
1487
1488         return true;
1489 }
1490
1491 void describeInterpolation(const string& stage, Interpolation qual,
1492                                                    ostringstream& name, ostringstream& desc)
1493 {
1494         if (qual == glu::INTERPOLATION_LAST)
1495         {
1496                 desc << ", unqualified in " << stage << " shader";
1497                 return;
1498         }
1499         else
1500         {
1501                 const string qualName = glu::getInterpolationName(qual);
1502
1503                 name << "_" << stage << "_" << qualName;
1504                 desc << ", qualified '" << qualName << "' in " << stage << " shader";
1505         }
1506 }
1507
1508
1509 } // anonymous namespace
1510
1511 TestCaseGroup* createSeparateShaderTests (Context& ctx)
1512 {
1513         TestParams              defaultParams;
1514         int                             numIterations   = 4;
1515         TestCaseGroup*  group                   =
1516                 new TestCaseGroup(ctx, "separate_shader", "Separate shader tests");
1517
1518         defaultParams.useUniform                        = false;
1519         defaultParams.initSingle                        = false;
1520         defaultParams.switchVtx                         = false;
1521         defaultParams.switchFrg                         = false;
1522         defaultParams.useCreateHelper           = false;
1523         defaultParams.useProgramUniform         = false;
1524         defaultParams.useSameName                       = false;
1525         defaultParams.varyings.count            = 0;
1526         defaultParams.varyings.type                     = glu::TYPE_INVALID;
1527         defaultParams.varyings.binding          = BINDING_NAME;
1528         defaultParams.varyings.vtxInterp        = glu::INTERPOLATION_LAST;
1529         defaultParams.varyings.frgInterp        = glu::INTERPOLATION_LAST;
1530
1531         TestCaseGroup* stagesGroup =
1532                 new TestCaseGroup(ctx, "pipeline", "Pipeline configuration tests");
1533         group->addChild(stagesGroup);
1534
1535         for (deUint32 flags = 0; flags < PARAMFLAGS_LAST << 2; ++flags)
1536         {
1537                 TestParams              params                  = defaultParams;
1538                 ostringstream   name;
1539                 ostringstream   desc;
1540
1541                 if (!areCaseParamFlagsValid(ParamFlags(flags & PARAMFLAGS_MASK)))
1542                         continue;
1543
1544                 if (flags & (PARAMFLAGS_LAST << 1))
1545                 {
1546                         params.useSameName = true;
1547                         name << "same_";
1548                         desc << "Identically named ";
1549                 }
1550                 else
1551                 {
1552                         name << "different_";
1553                         desc << "Differently named ";
1554                 }
1555
1556                 if (flags & PARAMFLAGS_LAST)
1557                 {
1558                         params.useUniform = true;
1559                         name << "uniform_";
1560                         desc << "uniforms, ";
1561                 }
1562                 else
1563                 {
1564                         name << "constant_";
1565                         desc << "constants, ";
1566                 }
1567
1568                 addRenderTest(*stagesGroup, name.str(), desc.str(), numIterations,
1569                                           ParamFlags(flags & PARAMFLAGS_MASK), params);
1570         }
1571
1572         TestCaseGroup* programUniformGroup =
1573                 new TestCaseGroup(ctx, "program_uniform", "ProgramUniform tests");
1574         group->addChild(programUniformGroup);
1575
1576         for (deUint32 flags = 0; flags < PARAMFLAGS_LAST; ++flags)
1577         {
1578                 TestParams              params                  = defaultParams;
1579
1580                 if (!areCaseParamFlagsValid(ParamFlags(flags)))
1581                         continue;
1582
1583                 params.useUniform = true;
1584                 params.useProgramUniform = true;
1585
1586                 addRenderTest(*programUniformGroup, "", "", numIterations, ParamFlags(flags), params);
1587         }
1588
1589         TestCaseGroup* createShaderProgramGroup =
1590                 new TestCaseGroup(ctx, "create_shader_program", "CreateShaderProgram tests");
1591         group->addChild(createShaderProgramGroup);
1592
1593         for (deUint32 flags = 0; flags < PARAMFLAGS_LAST; ++flags)
1594         {
1595                 TestParams              params                  = defaultParams;
1596
1597                 if (!areCaseParamFlagsValid(ParamFlags(flags)))
1598                         continue;
1599
1600                 params.useCreateHelper = true;
1601
1602                 addRenderTest(*createShaderProgramGroup, "", "", numIterations,
1603                                           ParamFlags(flags), params);
1604         }
1605
1606         TestCaseGroup* interfaceGroup =
1607                 new TestCaseGroup(ctx, "interface", "Shader interface compatibility tests");
1608         group->addChild(interfaceGroup);
1609
1610         enum
1611         {
1612                 NUM_INTERPOLATIONS      = glu::INTERPOLATION_LAST + 1, // INTERPOLATION_LAST is valid
1613                 INTERFACEFLAGS_LAST = BINDING_LAST * NUM_INTERPOLATIONS * NUM_INTERPOLATIONS
1614         };
1615
1616         for (deUint32 flags = 0; flags < INTERFACEFLAGS_LAST; ++flags)
1617         {
1618                 deUint32                tmpFlags        = flags;
1619                 Interpolation   frgInterp       = Interpolation(tmpFlags % NUM_INTERPOLATIONS);
1620                 Interpolation   vtxInterp       = Interpolation((tmpFlags /= NUM_INTERPOLATIONS)
1621                                                                                                         % NUM_INTERPOLATIONS);
1622                 BindingKind             binding         = BindingKind((tmpFlags /= NUM_INTERPOLATIONS)
1623                                                                                                   % BINDING_LAST);
1624                 TestParams              params          = defaultParams;
1625                 ostringstream   name;
1626                 ostringstream   desc;
1627
1628                 params.varyings.count           = 1;
1629                 params.varyings.type            = glu::TYPE_FLOAT;
1630                 params.varyings.binding         = binding;
1631                 params.varyings.vtxInterp       = vtxInterp;
1632                 params.varyings.frgInterp       = frgInterp;
1633
1634                 switch (binding)
1635                 {
1636                         case BINDING_LOCATION:
1637                                 name << "same_location";
1638                                 desc << "Varyings have same location, ";
1639                                 break;
1640                         case BINDING_NAME:
1641                                 name << "same_name";
1642                                 desc << "Varyings have same name, ";
1643                                 break;
1644                         default:
1645                                 DE_ASSERT(!"Impossible");
1646                 }
1647
1648                 describeInterpolation("vertex", vtxInterp, name, desc);
1649                 describeInterpolation("fragment", frgInterp, name, desc);
1650
1651                 if (!paramsValid(params))
1652                         continue;
1653
1654                 interfaceGroup->addChild(
1655                         new SeparateShaderTest(ctx, name.str(), desc.str(), numIterations, params,
1656                                                                    &SeparateShaderTest::testPipelineRendering));
1657         }
1658
1659         deUint32                baseSeed        = ctx.getTestContext().getCommandLine().getBaseSeed();
1660         Random                  rnd                     (deStringHash("separate_shader.random") + baseSeed);
1661         set<string>             seen;
1662         TestCaseGroup*  randomGroup     = new TestCaseGroup(
1663                 ctx, "random", "Random pipeline configuration tests");
1664         group->addChild(randomGroup);
1665
1666         for (deUint32 i = 0; i < 128; ++i)
1667         {
1668                 TestParams              params;
1669                 string                  code;
1670                 deUint32                genIterations   = 4096;
1671
1672                 do
1673                 {
1674                         params  = genParams(rnd.getUint32());
1675                         code    = paramsCode(params);
1676                 } while (de::contains(seen, code) && --genIterations > 0);
1677
1678                 seen.insert(code);
1679
1680                 string name = de::toString(i); // Would be code but baseSeed can change
1681
1682                 randomGroup->addChild(new SeparateShaderTest(
1683                                                                   ctx, name, name, numIterations, params,
1684                                                                   &SeparateShaderTest::testPipelineRendering));
1685         }
1686
1687         TestCaseGroup* apiGroup =
1688                 new TestCaseGroup(ctx, "api", "Program pipeline API tests");
1689         group->addChild(apiGroup);
1690
1691         {
1692                 // More or less random parameters. These shouldn't have much effect, so just
1693                 // do a single sample.
1694                 TestParams params = defaultParams;
1695                 params.useUniform = true;
1696                 apiGroup->addChild(new SeparateShaderTest(
1697                                                                   ctx,
1698                                                                   "current_program_priority",
1699                                                                   "Test priority between current program and pipeline binding",
1700                                                                   1, params, &SeparateShaderTest::testCurrentProgPriority));
1701                 apiGroup->addChild(new SeparateShaderTest(
1702                                                                   ctx,
1703                                                                   "active_program_uniform",
1704                                                                   "Test that glUniform() affects a pipeline's active program",
1705                                                                   1, params, &SeparateShaderTest::testActiveProgramUniform));
1706
1707                 apiGroup->addChild(new SeparateShaderTest(
1708                                                                  ctx,
1709                                                                  "pipeline_programs",
1710                                                                  "Test queries for programs in program pipeline stages",
1711                                                                  1, params, &SeparateShaderTest::testPipelineQueryPrograms));
1712
1713                 apiGroup->addChild(new SeparateShaderTest(
1714                                                                  ctx,
1715                                                                  "pipeline_active",
1716                                                                  "Test query for active programs in a program pipeline",
1717                                                                  1, params, &SeparateShaderTest::testPipelineQueryActive));
1718         }
1719
1720         TestCaseGroup* interfaceMismatchGroup =
1721                 new TestCaseGroup(ctx, "validation", "Negative program pipeline interface matching");
1722         group->addChild(interfaceMismatchGroup);
1723
1724         {
1725                 gls::ShaderLibrary                                      shaderLibrary   (ctx.getTestContext(), ctx.getRenderContext(), ctx.getContextInfo());
1726                 const std::vector<tcu::TestNode*>       children                = shaderLibrary.loadShaderFile("shaders/separate_shader_validation.test");
1727
1728                 for (int i = 0; i < (int)children.size(); i++)
1729                         interfaceMismatchGroup->addChild(children[i]);
1730         }
1731
1732         return group;
1733 }
1734
1735 } // Functional
1736 } // gles31
1737 } // deqp