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