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
57namespace deqp
58{
59namespace gles31
60{
61namespace Functional
62{
63namespace
64{
65
66using std::map;
67using std::set;
68using std::ostringstream;
69using std::string;
70using std::vector;
71using de::MovePtr;
72using de::Random;
73using de::UniquePtr;
74using tcu::MessageBuilder;
75using tcu::RenderTarget;
76using tcu::StringTemplate;
77using tcu::Surface;
78using tcu::TestLog;
79using tcu::ResultCollector;
80using glu::CallLogWrapper;
81using glu::DataType;
82using glu::VariableDeclaration;
83using glu::Precision;
84using glu::Program;
85using glu::ProgramPipeline;
86using glu::ProgramSources;
87using glu::RenderContext;
88using glu::ShaderProgram;
89using glu::ShaderType;
90using glu::Storage;
91using glu::VarType;
92using glu::VertexSource;
93using glu::FragmentSource;
94using glu::ProgramSeparable;
95
96using namespace glw;
97
98#define LOG_CALL(CALL) do	\
99{							\
100	enableLogging(true);	\
101	CALL;					\
102	enableLogging(false);	\
103} while (deGetFalse())
104
105enum
106{
107	VIEWPORT_SIZE = 128
108};
109
110enum VaryingInterpolation
111{
112	VARYINGINTERPOLATION_SMOOTH		= 0,
113	VARYINGINTERPOLATION_FLAT,
114	VARYINGINTERPOLATION_CENTROID,
115	VARYINGINTERPOLATION_DEFAULT,
116	VARYINGINTERPOLATION_RANDOM,
117
118	VARYINGINTERPOLATION_LAST
119};
120
121DataType 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
145VaryingInterpolation 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
157glu::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)
173VaryingInterpolation 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
188enum BindingKind
189{
190	BINDING_NAME,
191	BINDING_LOCATION,
192	BINDING_LAST
193};
194
195BindingKind randomBinding (Random& rnd)
196{
197	return rnd.getBool() ? BINDING_LOCATION : BINDING_NAME;
198}
199
200void 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
270struct 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
286struct 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
295VaryingInterpolation 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
306bool 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
325VaryingInterface 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
369vector<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
384void 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
392void 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
408void 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
443string 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
508string 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
598class ProgramWrapper
599{
600public:
601	virtual			~ProgramWrapper			(void) {}
602
603	virtual GLuint	getProgramName			(void) = 0;
604	virtual void	writeToLog				(TestLog& log) = 0;
605};
606
607class ShaderProgramWrapper : public ProgramWrapper
608{
609public:
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
619private:
620	ShaderProgram	m_shaderProgram;
621};
622
623class RawProgramWrapper : public ProgramWrapper
624{
625public:
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
639private:
640	Program			m_program;
641	ShaderType		m_shaderType;
642	const string	m_source;
643};
644
645void 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
658struct 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
671ProgramParams 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
683struct 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
695deUint32 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
714string 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
745bool 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)
790bool 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
805void 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
870TestParams 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
914struct 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
941void 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
963struct 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
976void setViewport (const RenderContext& renderCtx, const Rectangle& rect)
977{
978	renderCtx.getFunctions().viewport(rect.x, rect.y, rect.width, rect.height);
979}
980
981void 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
987Rectangle 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
1001class SeparateShaderTest : public TestCase, private CallLogWrapper
1002{
1003public:
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
1022private:
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
1060const RenderContext& SeparateShaderTest::getRenderContext (void)
1061{
1062	return m_context.getRenderContext();
1063}
1064
1065TestLog& SeparateShaderTest::log (void)
1066{
1067	return m_testCtx.getLog();
1068}
1069
1070SeparateShaderTest::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
1090MovePtr<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
1114MovePtr<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
1166void 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
1191MovePtr<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
1289MovePtr<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
1313void 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
1327void 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
1358void 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
1395void 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
1457void 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
1474void 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
1498TestCase::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
1535enum 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
1544bool 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
1555bool 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
1603void 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
1625TestCaseGroup* createSeparateShaderTests (Context& ctx)
1626{
1627	TestParams		defaultParams;
1628	int				numIterations	= 4;
1629	TestCaseGroup*	group			=
1630		new TestCaseGroup(ctx, "separate_shader", "Separate shader tests");
1631
1632	defaultParams.useUniform			= false;
1633	defaultParams.initSingle			= false;
1634	defaultParams.switchVtx				= false;
1635	defaultParams.switchFrg				= false;
1636	defaultParams.useCreateHelper		= false;
1637	defaultParams.useProgramUniform		= false;
1638	defaultParams.useSameName			= false;
1639	defaultParams.varyings.count		= 0;
1640	defaultParams.varyings.type			= glu::TYPE_INVALID;
1641	defaultParams.varyings.binding		= BINDING_NAME;
1642	defaultParams.varyings.vtxInterp	= VARYINGINTERPOLATION_LAST;
1643	defaultParams.varyings.frgInterp	= VARYINGINTERPOLATION_LAST;
1644
1645	TestCaseGroup* stagesGroup =
1646		new TestCaseGroup(ctx, "pipeline", "Pipeline configuration tests");
1647	group->addChild(stagesGroup);
1648
1649	for (deUint32 flags = 0; flags < PARAMFLAGS_LAST << 2; ++flags)
1650	{
1651		TestParams		params			= defaultParams;
1652		ostringstream	name;
1653		ostringstream	desc;
1654
1655		if (!areCaseParamFlagsValid(ParamFlags(flags & PARAMFLAGS_MASK)))
1656			continue;
1657
1658		if (flags & (PARAMFLAGS_LAST << 1))
1659		{
1660			params.useSameName = true;
1661			name << "same_";
1662			desc << "Identically named ";
1663		}
1664		else
1665		{
1666			name << "different_";
1667			desc << "Differently named ";
1668		}
1669
1670		if (flags & PARAMFLAGS_LAST)
1671		{
1672			params.useUniform = true;
1673			name << "uniform_";
1674			desc << "uniforms, ";
1675		}
1676		else
1677		{
1678			name << "constant_";
1679			desc << "constants, ";
1680		}
1681
1682		addRenderTest(*stagesGroup, name.str(), desc.str(), numIterations,
1683					  ParamFlags(flags & PARAMFLAGS_MASK), params);
1684	}
1685
1686	TestCaseGroup* programUniformGroup =
1687		new TestCaseGroup(ctx, "program_uniform", "ProgramUniform tests");
1688	group->addChild(programUniformGroup);
1689
1690	for (deUint32 flags = 0; flags < PARAMFLAGS_LAST; ++flags)
1691	{
1692		TestParams		params			= defaultParams;
1693
1694		if (!areCaseParamFlagsValid(ParamFlags(flags)))
1695			continue;
1696
1697		params.useUniform = true;
1698		params.useProgramUniform = true;
1699
1700		addRenderTest(*programUniformGroup, "", "", numIterations, ParamFlags(flags), params);
1701	}
1702
1703	TestCaseGroup* createShaderProgramGroup =
1704		new TestCaseGroup(ctx, "create_shader_program", "CreateShaderProgram tests");
1705	group->addChild(createShaderProgramGroup);
1706
1707	for (deUint32 flags = 0; flags < PARAMFLAGS_LAST; ++flags)
1708	{
1709		TestParams		params			= defaultParams;
1710
1711		if (!areCaseParamFlagsValid(ParamFlags(flags)))
1712			continue;
1713
1714		params.useCreateHelper = true;
1715
1716		addRenderTest(*createShaderProgramGroup, "", "", numIterations,
1717					  ParamFlags(flags), params);
1718	}
1719
1720	TestCaseGroup* interfaceGroup =
1721		new TestCaseGroup(ctx, "interface", "Shader interface compatibility tests");
1722	group->addChild(interfaceGroup);
1723
1724	enum
1725	{
1726		NUM_INTERPOLATIONS	= VARYINGINTERPOLATION_RANDOM, // VARYINGINTERPOLATION_RANDOM is one after last fully specified interpolation
1727		INTERFACEFLAGS_LAST = BINDING_LAST * NUM_INTERPOLATIONS * NUM_INTERPOLATIONS
1728	};
1729
1730	for (deUint32 flags = 0; flags < INTERFACEFLAGS_LAST; ++flags)
1731	{
1732		deUint32				tmpFlags	= flags;
1733		VaryingInterpolation	frgInterp	= VaryingInterpolation(tmpFlags % NUM_INTERPOLATIONS);
1734		VaryingInterpolation	vtxInterp	= VaryingInterpolation((tmpFlags /= NUM_INTERPOLATIONS)
1735																   % NUM_INTERPOLATIONS);
1736		BindingKind				binding		= BindingKind((tmpFlags /= NUM_INTERPOLATIONS)
1737														  % BINDING_LAST);
1738		TestParams				params		= defaultParams;
1739		ostringstream			name;
1740		ostringstream			desc;
1741
1742		params.varyings.count		= 1;
1743		params.varyings.type		= glu::TYPE_FLOAT;
1744		params.varyings.binding		= binding;
1745		params.varyings.vtxInterp	= vtxInterp;
1746		params.varyings.frgInterp	= frgInterp;
1747
1748		switch (binding)
1749		{
1750			case BINDING_LOCATION:
1751				name << "same_location";
1752				desc << "Varyings have same location, ";
1753				break;
1754			case BINDING_NAME:
1755				name << "same_name";
1756				desc << "Varyings have same name, ";
1757				break;
1758			default:
1759				DE_FATAL("Impossible");
1760		}
1761
1762		describeInterpolation("vertex", vtxInterp, name, desc);
1763		describeInterpolation("fragment", frgInterp, name, desc);
1764
1765		if (!paramsValid(params))
1766			continue;
1767
1768		interfaceGroup->addChild(
1769			new SeparateShaderTest(ctx, name.str(), desc.str(), numIterations, params,
1770								   &SeparateShaderTest::testPipelineRendering));
1771	}
1772
1773	deUint32		baseSeed	= ctx.getTestContext().getCommandLine().getBaseSeed();
1774	Random			rnd			(deStringHash("separate_shader.random") + baseSeed);
1775	set<string>		seen;
1776	TestCaseGroup*	randomGroup	= new TestCaseGroup(
1777		ctx, "random", "Random pipeline configuration tests");
1778	group->addChild(randomGroup);
1779
1780	for (deUint32 i = 0; i < 128; ++i)
1781	{
1782		TestParams		params;
1783		string			code;
1784		deUint32		genIterations	= 4096;
1785
1786		do
1787		{
1788			params	= genParams(rnd.getUint32());
1789			code	= paramsCode(params);
1790		} while (de::contains(seen, code) && --genIterations > 0);
1791
1792		seen.insert(code);
1793
1794		string name = de::toString(i); // Would be code but baseSeed can change
1795
1796		randomGroup->addChild(new SeparateShaderTest(
1797								  ctx, name, name, numIterations, params,
1798								  &SeparateShaderTest::testPipelineRendering));
1799	}
1800
1801	TestCaseGroup* apiGroup =
1802		new TestCaseGroup(ctx, "api", "Program pipeline API tests");
1803	group->addChild(apiGroup);
1804
1805	{
1806		// More or less random parameters. These shouldn't have much effect, so just
1807		// do a single sample.
1808		TestParams params = defaultParams;
1809		params.useUniform = true;
1810		apiGroup->addChild(new SeparateShaderTest(
1811								  ctx,
1812								  "current_program_priority",
1813								  "Test priority between current program and pipeline binding",
1814								  1, params, &SeparateShaderTest::testCurrentProgPriority));
1815		apiGroup->addChild(new SeparateShaderTest(
1816								  ctx,
1817								  "active_program_uniform",
1818								  "Test that glUniform() affects a pipeline's active program",
1819								  1, params, &SeparateShaderTest::testActiveProgramUniform));
1820
1821		apiGroup->addChild(new SeparateShaderTest(
1822								 ctx,
1823								 "pipeline_programs",
1824								 "Test queries for programs in program pipeline stages",
1825								 1, params, &SeparateShaderTest::testPipelineQueryPrograms));
1826
1827		apiGroup->addChild(new SeparateShaderTest(
1828								 ctx,
1829								 "pipeline_active",
1830								 "Test query for active programs in a program pipeline",
1831								 1, params, &SeparateShaderTest::testPipelineQueryActive));
1832	}
1833
1834	TestCaseGroup* interfaceMismatchGroup =
1835		new TestCaseGroup(ctx, "validation", "Negative program pipeline interface matching");
1836	group->addChild(interfaceMismatchGroup);
1837
1838	{
1839		gls::ShaderLibrary					shaderLibrary	(ctx.getTestContext(), ctx.getRenderContext(), ctx.getContextInfo());
1840		const std::vector<tcu::TestNode*>	children		= shaderLibrary.loadShaderFile("shaders/separate_shader_validation.test");
1841
1842		for (int i = 0; i < (int)children.size(); i++)
1843			interfaceMismatchGroup->addChild(children[i]);
1844	}
1845
1846	return group;
1847}
1848
1849} // Functional
1850} // gles31
1851} // deqp
1852