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