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