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