Home | History | Annotate | Download | only in functional
      1 /*-------------------------------------------------------------------------
      2  * drawElements Quality Program OpenGL ES 3.1 Module
      3  * -------------------------------------------------
      4  *
      5  * Copyright 2014 The Android Open Source Project
      6  *
      7  * Licensed under the Apache License, Version 2.0 (the "License");
      8  * you may not use this file except in compliance with the License.
      9  * You may obtain a copy of the License at
     10  *
     11  *      http://www.apache.org/licenses/LICENSE-2.0
     12  *
     13  * Unless required by applicable law or agreed to in writing, software
     14  * distributed under the License is distributed on an "AS IS" BASIS,
     15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  * See the License for the specific language governing permissions and
     17  * limitations under the License.
     18  *
     19  *//*!
     20  * \file
     21  * \brief Tests for separate shader objects
     22  *//*--------------------------------------------------------------------*/
     23 
     24 #include "es31fSeparateShaderTests.hpp"
     25 
     26 #include "deInt32.h"
     27 #include "deString.h"
     28 #include "deStringUtil.hpp"
     29 #include "deUniquePtr.hpp"
     30 #include "deRandom.hpp"
     31 #include "deSTLUtil.hpp"
     32 #include "tcuCommandLine.hpp"
     33 #include "tcuImageCompare.hpp"
     34 #include "tcuRenderTarget.hpp"
     35 #include "tcuRGBA.hpp"
     36 #include "tcuSurface.hpp"
     37 #include "tcuStringTemplate.hpp"
     38 #include "gluCallLogWrapper.hpp"
     39 #include "gluPixelTransfer.hpp"
     40 #include "gluRenderContext.hpp"
     41 #include "gluShaderProgram.hpp"
     42 #include "gluVarType.hpp"
     43 #include "glsShaderLibrary.hpp"
     44 #include "glwFunctions.hpp"
     45 #include "glwDefs.hpp"
     46 #include "glwEnums.hpp"
     47 
     48 #include <cstdarg>
     49 #include <algorithm>
     50 #include <map>
     51 #include <sstream>
     52 #include <string>
     53 #include <set>
     54 #include <vector>
     55 
     56 namespace deqp
     57 {
     58 namespace gles31
     59 {
     60 namespace Functional
     61 {
     62 namespace
     63 {
     64 
     65 using std::map;
     66 using std::set;
     67 using std::ostringstream;
     68 using std::string;
     69 using std::vector;
     70 using de::MovePtr;
     71 using de::Random;
     72 using de::UniquePtr;
     73 using tcu::MessageBuilder;
     74 using tcu::RenderTarget;
     75 using tcu::StringTemplate;
     76 using tcu::Surface;
     77 using tcu::TestLog;
     78 using tcu::ResultCollector;
     79 using glu::CallLogWrapper;
     80 using glu::DataType;
     81 using glu::VariableDeclaration;
     82 using glu::Interpolation;
     83 using glu::Precision;
     84 using glu::Program;
     85 using glu::ProgramPipeline;
     86 using glu::ProgramSources;
     87 using glu::RenderContext;
     88 using glu::ShaderProgram;
     89 using glu::ShaderType;
     90 using glu::Storage;
     91 using glu::VarType;
     92 using glu::VertexSource;
     93 using glu::FragmentSource;
     94 using glu::ProgramSeparable;
     95 
     96 using namespace glw;
     97 
     98 #define LOG_CALL(CALL) do	\
     99 {							\
    100 	enableLogging(true);	\
    101 	CALL;					\
    102 	enableLogging(false);	\
    103 } while (deGetFalse())
    104 
    105 enum
    106 {
    107 	VIEWPORT_SIZE = 128
    108 };
    109 
    110 DataType randomType (Random& rnd)
    111 {
    112 	using namespace glu;
    113 
    114 	if (rnd.getInt(0, 7) == 0)
    115 	{
    116 		const int numCols = rnd.getInt(2, 4), numRows = rnd.getInt(2, 4);
    117 
    118 		return getDataTypeMatrix(numCols, numRows);
    119 	}
    120 	else
    121 	{
    122 		static const DataType	s_types[]	= { TYPE_FLOAT,	TYPE_INT,	TYPE_UINT	};
    123 		static const float		s_weights[] = { 3.0,		1.0,		1.0			};
    124 		const int				size		= rnd.getInt(1, 4);
    125 		const DataType			scalarType	= rnd.chooseWeighted<DataType>(
    126 			DE_ARRAY_BEGIN(s_types), DE_ARRAY_END(s_types), DE_ARRAY_BEGIN(s_weights));
    127 		return getDataTypeVector(scalarType, size);
    128 	}
    129 
    130 	DE_ASSERT(!"Impossible");
    131 	return TYPE_INVALID;
    132 }
    133 
    134 Interpolation randomInterpolation (Random& rnd)
    135 {
    136 	return Interpolation(rnd.getInt(0, glu::INTERPOLATION_LAST - 1));
    137 }
    138 
    139 enum BindingKind
    140 {
    141 	BINDING_NAME,
    142 	BINDING_LOCATION,
    143 	BINDING_LAST
    144 };
    145 
    146 BindingKind randomBinding (Random& rnd)
    147 {
    148 	return rnd.getBool() ? BINDING_LOCATION : BINDING_NAME;
    149 }
    150 
    151 void printInputColor (ostringstream& oss, const VariableDeclaration& input)
    152 {
    153 	using namespace glu;
    154 
    155 	const DataType	basicType	= input.varType.getBasicType();
    156 	string			exp			= input.name;
    157 
    158 	switch (getDataTypeScalarType(basicType))
    159 	{
    160 		case TYPE_FLOAT:
    161 			break;
    162 
    163 		case TYPE_INT:
    164 		case TYPE_UINT:
    165 		{
    166 			DataType floatType = getDataTypeFloatScalars(basicType);
    167 			exp = string() + "(" + getDataTypeName(floatType) + "(" + exp + ") / 255.0" + ")";
    168 			break;
    169 		}
    170 
    171 		default:
    172 			DE_ASSERT(!"Impossible");
    173 	}
    174 
    175 	if (isDataTypeScalarOrVector(basicType))
    176 	{
    177 		switch (getDataTypeScalarSize(basicType))
    178 		{
    179 			case 1:
    180 				oss << "hsv(vec3(" << exp << ", 1.0, 1.0))";
    181 				break;
    182 			case 2:
    183 				oss << "hsv(vec3(" << exp << ", 1.0))";
    184 				break;
    185 			case 3:
    186 				oss << "vec4(" << exp << ", 1.0)";
    187 				break;
    188 			case 4:
    189 				oss << exp;
    190 				break;
    191 			default:
    192 				DE_ASSERT(!"Impossible");
    193 		}
    194 	}
    195 	else if (isDataTypeMatrix(basicType))
    196 	{
    197 		int	rows	= getDataTypeMatrixNumRows(basicType);
    198 		int	columns	= getDataTypeMatrixNumColumns(basicType);
    199 
    200 		if (rows == columns)
    201 			oss << "hsv(vec3(determinant(" << exp << ")))";
    202 		else
    203 		{
    204 			if (rows != 3 && columns >= 3)
    205 			{
    206 				exp = "transpose(" + exp + ")";
    207 				std::swap(rows, columns);
    208 			}
    209 			exp = exp + "[0]";
    210 			if (rows > 3)
    211 				exp = exp + ".xyz";
    212 			oss << "hsv(" << exp << ")";
    213 		}
    214 	}
    215 	else
    216 		DE_ASSERT(!"Impossible");
    217 }
    218 
    219 // Representation for the varyings between vertex and fragment shaders
    220 
    221 struct VaryingParams
    222 {
    223 	VaryingParams 			(void)
    224 		: count				(0)
    225 		, type				(glu::TYPE_LAST)
    226 		, binding			(BINDING_LAST)
    227 		, vtxInterp			(glu::INTERPOLATION_LAST)
    228 		, frgInterp			(glu::INTERPOLATION_LAST) {}
    229 
    230 	int						count;
    231 	DataType				type;
    232 	BindingKind				binding;
    233 	Interpolation			vtxInterp;
    234 	Interpolation			frgInterp;
    235 };
    236 
    237 struct VaryingInterface
    238 {
    239 	vector<VariableDeclaration>	vtxOutputs;
    240 	vector<VariableDeclaration>	frgInputs;
    241 };
    242 
    243 // Generate corresponding input and output variable declarations that may vary
    244 // in compatible ways.
    245 
    246 Interpolation chooseInterpolation (Interpolation param, DataType type, Random& rnd)
    247 {
    248 	if (glu::getDataTypeScalarType(type) != glu::TYPE_FLOAT)
    249 		return glu::INTERPOLATION_FLAT;
    250 
    251 	if (param == glu::INTERPOLATION_LAST)
    252 		return randomInterpolation(rnd);
    253 
    254 	return param;
    255 }
    256 
    257 VaryingInterface genVaryingInterface (const VaryingParams&		params,
    258 									  Random&					rnd)
    259 {
    260 	using namespace	glu;
    261 
    262 	VaryingInterface	ret;
    263 	int					offset = 0;
    264 
    265 	for (int varNdx = 0; varNdx < params.count; ++varNdx)
    266 	{
    267 		const BindingKind	binding			= ((params.binding == BINDING_LAST)
    268 											   ? randomBinding(rnd) : params.binding);
    269 		const DataType		type			= ((params.type == TYPE_LAST)
    270 											   ? randomType(rnd) : params.type);
    271 		const Interpolation	vtxInterp		= chooseInterpolation(params.vtxInterp, type, rnd);
    272 		const Interpolation	frgInterp		= chooseInterpolation(params.frgInterp, type, rnd);
    273 		const int			loc				= ((binding == BINDING_LOCATION) ? offset : -1);
    274 		const string		ndxStr			= de::toString(varNdx);
    275 		const string		vtxName			= ((binding == BINDING_NAME)
    276 											   ? "var" + ndxStr : "vtxVar" + ndxStr);
    277 		const string		frgName			= ((binding == BINDING_NAME)
    278 											   ? "var" + ndxStr : "frgVar" + ndxStr);
    279 		const VarType		varType			(type, PRECISION_HIGHP);
    280 
    281 		offset += getDataTypeNumLocations(type);
    282 
    283 		// Over 16 locations aren't necessarily supported, so halt here.
    284 		if (offset > 16)
    285 			break;
    286 
    287 		ret.vtxOutputs.push_back(
    288 			VariableDeclaration(varType, vtxName, STORAGE_OUT, vtxInterp, loc));
    289 		ret.frgInputs.push_back(
    290 			VariableDeclaration(varType, frgName, STORAGE_IN, frgInterp, loc));
    291 	}
    292 
    293 	return ret;
    294 }
    295 
    296 // Create vertex output variable declarations that are maximally compatible
    297 // with the fragment input variables.
    298 
    299 vector<VariableDeclaration> varyingCompatVtxOutputs (const VaryingInterface& varyings)
    300 {
    301 	vector<VariableDeclaration> outputs = varyings.vtxOutputs;
    302 
    303 	for (size_t i = 0; i < outputs.size(); ++i)
    304 	{
    305 		outputs[i].interpolation = varyings.frgInputs[i].interpolation;
    306 		outputs[i].name = varyings.frgInputs[i].name;
    307 	}
    308 
    309 	return outputs;
    310 }
    311 
    312 // Shader source generation
    313 
    314 void printFloat (ostringstream& oss, double d)
    315 {
    316 	oss.setf(oss.fixed | oss.internal);
    317 	oss.precision(4);
    318 	oss.width(7);
    319 	oss << d;
    320 }
    321 
    322 void printFloatDeclaration (ostringstream&	oss,
    323 							const string&	varName,
    324 							bool			uniform,
    325 							GLfloat			value		= 0.0)
    326 {
    327 	using namespace glu;
    328 
    329 	const VarType	varType	(TYPE_FLOAT, PRECISION_HIGHP);
    330 
    331 	if (uniform)
    332 		oss << VariableDeclaration(varType, varName, STORAGE_UNIFORM) << ";\n";
    333 	else
    334 		oss << VariableDeclaration(varType, varName, STORAGE_CONST)
    335 			<< " = " << de::floatToString(value, 6) << ";\n";
    336 }
    337 
    338 void printRandomInitializer (ostringstream& oss, DataType type, Random& rnd)
    339 {
    340 	using namespace glu;
    341 	const int		size	= getDataTypeScalarSize(type);
    342 
    343 	if (size > 0)
    344 		oss << getDataTypeName(type) << "(";
    345 
    346 	for (int i = 0; i < size; ++i)
    347 	{
    348 		oss << (i == 0 ? "" : ", ");
    349 		switch (getDataTypeScalarType(type))
    350 		{
    351 			case TYPE_FLOAT:
    352 				printFloat(oss, rnd.getInt(0, 16) / 16.0);
    353 				break;
    354 
    355 			case TYPE_INT:
    356 			case TYPE_UINT:
    357 				oss << rnd.getInt(0, 255);
    358 				break;
    359 
    360 			case TYPE_BOOL:
    361 				oss << (rnd.getBool() ? "true" : "false");
    362 				break;
    363 
    364 			default:
    365 				DE_ASSERT(!"Impossible");
    366 		}
    367 	}
    368 
    369 	if (size > 0)
    370 		oss << ")";
    371 }
    372 
    373 string genVtxShaderSrc (deUint32							seed,
    374 						const vector<VariableDeclaration>&	outputs,
    375 						const string&						varName,
    376 						bool								uniform,
    377 						float								value = 0.0)
    378 {
    379 	ostringstream		oss;
    380 	Random				rnd								(seed);
    381 	enum { 				NUM_COMPONENTS 					= 2 };
    382 	static const int	s_quadrants[][NUM_COMPONENTS]	= { {1, 1}, {-1, 1}, {1, -1} };
    383 
    384 	oss << "#version 310 es\n";
    385 
    386 	oss << "const vec2 triangle[3] = vec2[3](\n";
    387 
    388 	for (int vertexNdx = 0; vertexNdx < DE_LENGTH_OF_ARRAY(s_quadrants); ++vertexNdx)
    389 	{
    390 		oss << "\tvec2(";
    391 
    392 		for (int componentNdx = 0; componentNdx < NUM_COMPONENTS; ++componentNdx)
    393 		{
    394 			printFloat(oss, s_quadrants[vertexNdx][componentNdx] * rnd.getInt(4,16) / 16.0);
    395 			oss << (componentNdx < 1 ? ", " : "");
    396 		}
    397 
    398 		oss << ")" << (vertexNdx < 2 ? "," : "") << "\n";
    399 	}
    400 	oss << ");\n";
    401 
    402 
    403 	for (vector<VariableDeclaration>::const_iterator it = outputs.begin();
    404 		 it != outputs.end(); ++it)
    405 	{
    406 		const DataType	type		= it->varType.getBasicType();
    407 		const string	typeName	= glu::getDataTypeName(type);
    408 
    409 		oss << "const " << typeName << " " << it->name << "Inits[3] = "
    410 			<< typeName << "[3](\n";
    411 		for (int i = 0; i < 3; ++i)
    412 		{
    413 			oss << (i == 0 ? "\t" : ",\n\t");
    414 			printRandomInitializer(oss, type, rnd);
    415 		}
    416 		oss << ");\n"
    417 			<< *it << ";\n";
    418 	}
    419 
    420 	printFloatDeclaration(oss, varName, uniform, value);
    421 
    422 	oss << "void main (void)\n"
    423 		<< "{\n"
    424 		<< "\tgl_Position = vec4(" << varName << " * triangle[gl_VertexID], 0.0, 1.0);\n";
    425 
    426 	for (vector<VariableDeclaration>::const_iterator it = outputs.begin();
    427 		 it != outputs.end(); ++it)
    428 		oss << "\t" << it->name << " = " << it->name << "Inits[gl_VertexID];\n";
    429 
    430 	oss << "}\n";
    431 
    432 	return oss.str();
    433 }
    434 
    435 string genFrgShaderSrc (deUint32							seed,
    436 						const vector<VariableDeclaration>&	inputs,
    437 						const string&						varName,
    438 						bool								uniform,
    439 						float								value = 0.0)
    440 {
    441 	Random				rnd		(seed);
    442 	ostringstream		oss;
    443 
    444 	oss.precision(4);
    445 	oss.width(7);
    446 	oss << "#version 310 es\n";
    447 
    448 	oss << "precision highp float;\n";;
    449 
    450 	oss << "out vec4 fragColor;\n";;
    451 
    452 	printFloatDeclaration(oss, varName, uniform, value);
    453 
    454 	for (vector<VariableDeclaration>::const_iterator it = inputs.begin();
    455 		 it != inputs.end(); ++it)
    456 		oss << *it << ";\n";
    457 
    458 	// glsl % isn't defined for negative numbers
    459 	oss << "int imod (int n, int d)" << "\n"
    460 		<< "{" << "\n"
    461 		<< "\t" << "return (n < 0 ? d - 1 - (-1 - n) % d : n % d);" << "\n"
    462 		<< "}" << "\n";
    463 
    464 	oss << "vec4 hsv (vec3 hsv)"
    465 		<< "{" << "\n"
    466 		<< "\tfloat h = hsv.x * 3.0;\n"
    467 		<< "\tfloat r = max(0.0, 1.0 - h) + max(0.0, h - 2.0);\n"
    468 		<< "\tfloat g = max(0.0, 1.0 - abs(h - 1.0));\n"
    469 		<< "\tfloat b = max(0.0, 1.0 - abs(h - 2.0));\n"
    470 		<< "\tvec3 hs = mix(vec3(1.0), vec3(r, g, b), hsv.y);\n"
    471 		<< "\treturn vec4(hsv.z * hs, 1.0);\n"
    472 		<< "}\n";
    473 
    474 	oss << "void main (void)\n"
    475 		<< "{\n";
    476 
    477 	oss << "\t" << "fragColor = vec4(vec3(" << varName << "), 1.0);" << "\n";
    478 
    479 	if (inputs.size() > 0)
    480 	{
    481 		oss << "\t" << "switch (imod(int(0.5 * (gl_FragCoord.x - gl_FragCoord.y)), "
    482 			<< inputs.size() << "))" << "\n"
    483 			<< "\t" << "{" << "\n";
    484 
    485 		for (size_t i = 0; i < inputs.size(); ++i)
    486 		{
    487 			oss << "\t\t" << "case " << i << ":" << "\n"
    488 				<< "\t\t\t" << "fragColor *= ";
    489 
    490 			printInputColor(oss, inputs[i]);
    491 
    492 			oss << ";" << "\n"
    493 				<< "\t\t\t" << "break;" << "\n";
    494 		}
    495 
    496 		oss << "\t\t" << "case " << inputs.size() << ":\n"
    497 			<< "\t\t\t" << "fragColor = vec4(1.0, 0.0, 1.0, 1.0);" << "\n";
    498 		oss << "\t\t\t" << "break;" << "\n";
    499 
    500 		oss << "\t\t" << "case -1:\n"
    501 			<< "\t\t\t" << "fragColor = vec4(1.0, 1.0, 0.0, 1.0);" << "\n";
    502 		oss << "\t\t\t" << "break;" << "\n";
    503 
    504 		oss << "\t\t" << "default:" << "\n"
    505 			<< "\t\t\t" << "fragColor = vec4(1.0, 1.0, 0.0, 1.0);" << "\n";
    506 
    507 		oss << "\t" << "}\n";
    508 
    509 	}
    510 
    511 	oss << "}\n";
    512 
    513 	return oss.str();
    514 }
    515 
    516 // ProgramWrapper
    517 
    518 class ProgramWrapper
    519 {
    520 public:
    521 	virtual 		~ProgramWrapper			(void) {}
    522 
    523 	virtual GLuint	getProgramName			(void) = 0;
    524 	virtual void	writeToLog				(TestLog& log) = 0;
    525 };
    526 
    527 class ShaderProgramWrapper : public ProgramWrapper
    528 {
    529 public:
    530 					ShaderProgramWrapper	(const RenderContext&	renderCtx,
    531 											 const ProgramSources&	sources)
    532 						: m_shaderProgram	(renderCtx, sources) {}
    533 					~ShaderProgramWrapper	(void) {}
    534 
    535 	GLuint			getProgramName			(void) { return m_shaderProgram.getProgram(); }
    536 	ShaderProgram&	getShaderProgram		(void) { return m_shaderProgram; }
    537 	void			writeToLog				(TestLog& log) { log << m_shaderProgram; }
    538 
    539 private:
    540 	ShaderProgram	m_shaderProgram;
    541 };
    542 
    543 class RawProgramWrapper : public ProgramWrapper
    544 {
    545 public:
    546 					RawProgramWrapper		(const RenderContext&	renderCtx,
    547 											 GLuint					programName,
    548 											 ShaderType				shaderType,
    549 											 const string&			source)
    550 						: m_program			(renderCtx, programName)
    551 						, m_shaderType		(shaderType)
    552 						, m_source			(source) {}
    553 					~RawProgramWrapper		(void) {}
    554 
    555 	GLuint			getProgramName			(void) { return m_program.getProgram(); }
    556 	Program&		getProgram				(void) { return m_program; }
    557 	void			writeToLog				(TestLog& log);
    558 
    559 private:
    560 	Program			m_program;
    561 	ShaderType		m_shaderType;
    562 	const string	m_source;
    563 };
    564 
    565 void RawProgramWrapper::writeToLog (TestLog& log)
    566 {
    567 	const string	info	= m_program.getInfoLog();
    568 	qpShaderType	qpType	= glu::getLogShaderType(m_shaderType);
    569 
    570 	log << TestLog::ShaderProgram(true, info)
    571 		<< TestLog::Shader(qpType, m_source,
    572 						   true, "[Shader created by glCreateShaderProgramv()]")
    573 		<< TestLog::EndShaderProgram;
    574 }
    575 
    576 // ProgramParams
    577 
    578 struct ProgramParams
    579 {
    580 	ProgramParams (deUint32 vtxSeed_, GLfloat vtxScale_, deUint32 frgSeed_, GLfloat frgScale_)
    581 		: vtxSeed	(vtxSeed_)
    582 		, vtxScale	(vtxScale_)
    583 		, frgSeed	(frgSeed_)
    584 		, frgScale	(frgScale_) {}
    585 	deUint32	vtxSeed;
    586 	GLfloat		vtxScale;
    587 	deUint32	frgSeed;
    588 	GLfloat		frgScale;
    589 };
    590 
    591 ProgramParams genProgramParams (Random& rnd)
    592 {
    593 	const deUint32	vtxSeed		= rnd.getUint32();
    594 	const GLfloat	vtxScale	= rnd.getInt(8, 16) / 16.0f;
    595 	const deUint32	frgSeed		= rnd.getUint32();
    596 	const GLfloat	frgScale	= rnd.getInt(0, 16) / 16.0f;
    597 
    598 	return ProgramParams(vtxSeed, vtxScale, frgSeed, frgScale);
    599 }
    600 
    601 // TestParams
    602 
    603 struct TestParams
    604 {
    605 	bool					initSingle;
    606 	bool					switchVtx;
    607 	bool					switchFrg;
    608 	bool					useUniform;
    609 	bool					useSameName;
    610 	bool					useCreateHelper;
    611 	bool					useProgramUniform;
    612 	VaryingParams			varyings;
    613 };
    614 
    615 deUint32 paramsSeed (const TestParams& params)
    616 {
    617 	deUint32 paramCode 	= (params.initSingle	 		<< 0 |
    618 						   params.switchVtx 			<< 1 |
    619 						   params.switchFrg				<< 2 |
    620 						   params.useUniform			<< 3 |
    621 						   params.useSameName			<< 4 |
    622 						   params.useCreateHelper		<< 5 |
    623 						   params.useProgramUniform		<< 6);
    624 
    625 	paramCode = deUint32Hash(paramCode) + params.varyings.count;
    626 	paramCode = deUint32Hash(paramCode) + params.varyings.type;
    627 	paramCode = deUint32Hash(paramCode) + params.varyings.binding;
    628 	paramCode = deUint32Hash(paramCode) + params.varyings.vtxInterp;
    629 	paramCode = deUint32Hash(paramCode) + params.varyings.frgInterp;
    630 
    631 	return deUint32Hash(paramCode);
    632 }
    633 
    634 string paramsCode (const TestParams& params)
    635 {
    636 	using namespace glu;
    637 
    638 	ostringstream oss;
    639 
    640 	oss << (params.initSingle ? "1" : "2")
    641 		<< (params.switchVtx ? "v" : "")
    642 		<< (params.switchFrg ? "f" : "")
    643 		<< (params.useProgramUniform ? "p" : "")
    644 		<< (params.useUniform ? "u" : "")
    645 		<< (params.useSameName ? "s" : "")
    646 		<< (params.useCreateHelper ? "c" : "")
    647 		 << de::toString(params.varyings.count)
    648 		 << (params.varyings.binding == BINDING_NAME ? "n" :
    649 			 params.varyings.binding == BINDING_LOCATION ? "l" :
    650 			 params.varyings.binding == BINDING_LAST ? "r" :
    651 			"")
    652 		 << (params.varyings.vtxInterp == INTERPOLATION_SMOOTH ? "m" :
    653 			 params.varyings.vtxInterp == INTERPOLATION_CENTROID ? "e" :
    654 			 params.varyings.vtxInterp == INTERPOLATION_FLAT ? "a" :
    655 			 params.varyings.vtxInterp == INTERPOLATION_LAST ? "r" :
    656 			"o")
    657 		 << (params.varyings.frgInterp == INTERPOLATION_SMOOTH ? "m" :
    658 			 params.varyings.frgInterp == INTERPOLATION_CENTROID ? "e" :
    659 			 params.varyings.frgInterp == INTERPOLATION_FLAT ? "a" :
    660 			 params.varyings.frgInterp == INTERPOLATION_LAST ? "r" :
    661 			"o");
    662 	return oss.str();
    663 }
    664 
    665 bool paramsValid (const TestParams& params)
    666 {
    667 	using namespace glu;
    668 
    669 	// Final pipeline has a single program?
    670 	if (params.initSingle)
    671 	{
    672 		// Cannot have conflicting names for uniforms or constants
    673 		if (params.useSameName)
    674 			return false;
    675 
    676 		// CreateShaderProgram would never get called
    677 		if (!params.switchVtx && !params.switchFrg && params.useCreateHelper)
    678 			return false;
    679 
    680 		// Must switch either all or nothing
    681 		if (params.switchVtx != params.switchFrg)
    682 			return false;
    683 	}
    684 
    685 	// ProgramUniform would never get called
    686 	if (params.useProgramUniform && !params.useUniform)
    687 		return false;
    688 
    689 	// Interpolation is meaningless if we don't use an in/out variable.
    690 	if (params.varyings.count == 0 &&
    691 		!(params.varyings.vtxInterp == INTERPOLATION_LAST &&
    692 		  params.varyings.frgInterp == INTERPOLATION_LAST))
    693 		return false;
    694 
    695 	// 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 
    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 
    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 {
    809 								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 
    818 	ProgramWrapper&				getVertexProgram	(void) const
    819 	{
    820 		return vtxProg ? *vtxProg : *fullProg;
    821 	}
    822 
    823 	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 
    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 {
    858 			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 
    869 void setViewport (const RenderContext& renderCtx, const Rectangle& rect)
    870 {
    871 	renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height);
    872 }
    873 
    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 
    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 
    953 const RenderContext& SeparateShaderTest::getRenderContext (void)
    954 {
    955 	return m_context.getRenderContext();
    956 }
    957 
    958 TestLog& SeparateShaderTest::log (void)
    959 {
    960 	return m_testCtx.getLog();
    961 }
    962 
    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 
    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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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 
   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