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