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