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