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