1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 2.0 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 Invariance tests.
22 *//*--------------------------------------------------------------------*/
23
24#include "es2fShaderInvarianceTests.hpp"
25#include "deStringUtil.hpp"
26#include "deRandom.hpp"
27#include "gluContextInfo.hpp"
28#include "gluRenderContext.hpp"
29#include "gluShaderProgram.hpp"
30#include "gluPixelTransfer.hpp"
31#include "glwFunctions.hpp"
32#include "glwEnums.hpp"
33#include "tcuRenderTarget.hpp"
34#include "tcuTestLog.hpp"
35#include "tcuSurface.hpp"
36#include "tcuTextureUtil.hpp"
37#include "tcuStringTemplate.hpp"
38
39
40namespace deqp
41{
42namespace gles2
43{
44namespace Functional
45{
46namespace
47{
48
49class FormatArgumentList;
50
51static tcu::Vec4 genRandomVector (de::Random& rnd)
52{
53	tcu::Vec4 retVal;
54
55	retVal.x() = rnd.getFloat(-1.0f, 1.0f);
56	retVal.y() = rnd.getFloat(-1.0f, 1.0f);
57	retVal.z() = rnd.getFloat(-1.0f, 1.0f);
58	retVal.w() = rnd.getFloat( 0.2f, 1.0f);
59
60	return retVal;
61}
62
63class FormatArgument
64{
65public:
66						FormatArgument (const char* name, const std::string& value);
67
68private:
69	friend class FormatArgumentList;
70
71	const char* const	m_name;
72	const std::string	m_value;
73};
74
75FormatArgument::FormatArgument (const char* name, const std::string& value)
76	: m_name	(name)
77	, m_value	(value)
78{
79}
80
81class FormatArgumentList
82{
83public:
84												FormatArgumentList	(void);
85
86	FormatArgumentList&							operator<<			(const FormatArgument&);
87	const std::map<std::string, std::string>&	getArguments		(void) const;
88
89private:
90	std::map<std::string, std::string>			m_formatArguments;
91};
92
93FormatArgumentList::FormatArgumentList (void)
94{
95}
96
97FormatArgumentList&	FormatArgumentList::operator<< (const FormatArgument& arg)
98{
99	m_formatArguments[arg.m_name] = arg.m_value;
100	return *this;
101}
102
103const std::map<std::string, std::string>& FormatArgumentList::getArguments (void) const
104{
105	return m_formatArguments;
106}
107
108static std::string formatGLSL (const char* templateString, const FormatArgumentList& args)
109{
110	const std::map<std::string, std::string>& params = args.getArguments();
111
112	return tcu::StringTemplate(std::string(templateString)).specialize(params);
113}
114
115/*--------------------------------------------------------------------*//*!
116 * \brief Vertex shader invariance test
117 *
118 * Test vertex shader invariance by drawing a test pattern two times, each
119 * time with a different shader. Shaders have set identical values to
120 * invariant gl_Position using identical expressions. No fragments from the
121 * first pass using should remain visible.
122 *//*--------------------------------------------------------------------*/
123class InvarianceTest : public TestCase
124{
125public:
126	struct ShaderPair
127	{
128		std::string vertexShaderSource0;
129		std::string fragmentShaderSource0;
130		std::string vertexShaderSource1;
131		std::string fragmentShaderSource1;
132	};
133
134							InvarianceTest		(Context& ctx, const char* name, const char* desc);
135							~InvarianceTest		(void);
136
137	void					init				(void);
138	void					deinit				(void);
139	IterateResult			iterate				(void);
140
141private:
142	virtual ShaderPair		genShaders			(void) const = DE_NULL;
143	bool					checkImage			(const tcu::Surface&) const;
144
145	glu::ShaderProgram*		m_shader0;
146	glu::ShaderProgram*		m_shader1;
147	glw::GLuint				m_arrayBuf;
148	int						m_verticesInPattern;
149
150	const int				m_renderSize;
151};
152
153InvarianceTest::InvarianceTest (Context& ctx, const char* name, const char* desc)
154	: TestCase				(ctx, name, desc)
155	, m_shader0				(DE_NULL)
156	, m_shader1				(DE_NULL)
157	, m_arrayBuf			(0)
158	, m_verticesInPattern	(0)
159	, m_renderSize			(256)
160{
161}
162
163InvarianceTest::~InvarianceTest (void)
164{
165	deinit();
166}
167
168void InvarianceTest::init (void)
169{
170	// Invariance tests require drawing to the screen and reading back results.
171	// Tests results are not reliable if the resolution is too small
172	{
173		if (m_context.getRenderTarget().getWidth()  < m_renderSize ||
174			m_context.getRenderTarget().getHeight() < m_renderSize)
175			throw tcu::NotSupportedError(std::string("Render target size must be at least ") + de::toString(m_renderSize) + "x" + de::toString(m_renderSize));
176	}
177
178	// Gen shaders
179	{
180		ShaderPair vertexShaders = genShaders();
181
182		m_shader0 = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(vertexShaders.vertexShaderSource0) << glu::FragmentSource(vertexShaders.fragmentShaderSource0));
183		if (!m_shader0->isOk())
184		{
185			m_testCtx.getLog() << *m_shader0;
186			throw tcu::TestError("Test shader compile failed.");
187		}
188
189		m_shader1 = new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources() << glu::VertexSource(vertexShaders.vertexShaderSource1) << glu::FragmentSource(vertexShaders.fragmentShaderSource1));
190		if (!m_shader1->isOk())
191		{
192			m_testCtx.getLog() << *m_shader1;
193			throw tcu::TestError("Test shader compile failed.");
194		}
195
196		// log
197		m_testCtx.getLog()
198			<< tcu::TestLog::Message << "Shader 1:" << tcu::TestLog::EndMessage
199			<< *m_shader0
200			<< tcu::TestLog::Message << "Shader 2:" << tcu::TestLog::EndMessage
201			<< *m_shader1;
202	}
203
204	// Gen test pattern
205	{
206		const int				numTriangles	= 72;
207		de::Random				rnd				(123);
208		std::vector<tcu::Vec4>	triangles		(numTriangles * 3 * 2);
209		const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
210
211		// Narrow triangle pattern
212		for (int triNdx = 0; triNdx < numTriangles; ++triNdx)
213		{
214			const tcu::Vec4 vertex1 = genRandomVector(rnd);
215			const tcu::Vec4 vertex2 = genRandomVector(rnd);
216			const tcu::Vec4 vertex3 = vertex2 + genRandomVector(rnd) * 0.01f; // generate narrow triangles
217
218			triangles[triNdx*3 + 0] = vertex1;
219			triangles[triNdx*3 + 1] = vertex2;
220			triangles[triNdx*3 + 2] = vertex3;
221		}
222
223		// Normal triangle pattern
224		for (int triNdx = 0; triNdx < numTriangles; ++triNdx)
225		{
226			triangles[(numTriangles + triNdx)*3 + 0] = genRandomVector(rnd);
227			triangles[(numTriangles + triNdx)*3 + 1] = genRandomVector(rnd);
228			triangles[(numTriangles + triNdx)*3 + 2] = genRandomVector(rnd);
229		}
230
231		// upload
232		gl.genBuffers(1, &m_arrayBuf);
233		gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuf);
234		gl.bufferData(GL_ARRAY_BUFFER, (int)(triangles.size() * sizeof(tcu::Vec4)), &triangles[0], GL_STATIC_DRAW);
235		GLU_EXPECT_NO_ERROR(gl.getError(), "buffer gen");
236
237		m_verticesInPattern = numTriangles * 3;
238	}
239}
240
241void InvarianceTest::deinit (void)
242{
243	delete m_shader0;
244	delete m_shader1;
245
246	m_shader0 = DE_NULL;
247	m_shader1 = DE_NULL;
248
249	if (m_arrayBuf)
250	{
251		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_arrayBuf);
252		m_arrayBuf = 0;
253	}
254}
255
256InvarianceTest::IterateResult InvarianceTest::iterate (void)
257{
258	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
259	const bool				depthBufferExists	= m_context.getRenderTarget().getDepthBits() != 0;
260	tcu::Surface			resultSurface		(m_renderSize, m_renderSize);
261	bool					error				= false;
262
263	// Prepare draw
264	gl.clearColor		(0.0f, 0.0f, 0.0f, 1.0f);
265	gl.clear			(GL_COLOR_BUFFER_BIT);
266	gl.viewport			(0, 0, m_renderSize, m_renderSize);
267	gl.bindBuffer		(GL_ARRAY_BUFFER, m_arrayBuf);
268	GLU_EXPECT_NO_ERROR	(gl.getError(), "setup draw");
269
270	m_testCtx.getLog() << tcu::TestLog::Message << "Testing position invariance." << tcu::TestLog::EndMessage;
271
272	// Draw position check passes
273	for (int passNdx = 0; passNdx < 2; ++passNdx)
274	{
275		const glu::ShaderProgram&	shader		= (passNdx == 0) ? (*m_shader0) : (*m_shader1);
276		const glw::GLint			positionLoc = gl.getAttribLocation(shader.getProgram(), "a_input");
277		const glw::GLint			colorLoc	= gl.getUniformLocation(shader.getProgram(), "u_color");
278		const tcu::Vec4				red			= tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f);
279		const tcu::Vec4				green		= tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f);
280		const tcu::Vec4				color		= (passNdx == 0) ? (red) : (green);
281		const char* const			colorStr	= (passNdx == 0) ? ("red - purple") : ("green");
282
283		m_testCtx.getLog() << tcu::TestLog::Message << "Drawing position test pattern using shader " << (passNdx+1) << ". Primitive color: " << colorStr << "." << tcu::TestLog::EndMessage;
284
285		gl.useProgram				(shader.getProgram());
286		gl.uniform4fv				(colorLoc, 1, color.getPtr());
287		gl.enableVertexAttribArray	(positionLoc);
288		gl.vertexAttribPointer		(positionLoc, 4, GL_FLOAT, GL_FALSE, sizeof(tcu::Vec4), DE_NULL);
289		gl.drawArrays				(GL_TRIANGLES, 0, m_verticesInPattern);
290		gl.disableVertexAttribArray	(positionLoc);
291		GLU_EXPECT_NO_ERROR			(gl.getError(), "draw pass");
292	}
293
294	// Read result
295	glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess());
296
297	// Check there are no red pixels
298	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying output. Expecting only green or background colored pixels." << tcu::TestLog::EndMessage;
299	error |= !checkImage(resultSurface);
300
301	if (!depthBufferExists)
302	{
303		m_testCtx.getLog() << tcu::TestLog::Message << "Depth buffer not available, skipping z-test." << tcu::TestLog::EndMessage;
304	}
305	else
306	{
307		// Test with Z-test
308		gl.clearDepthf		(1.0f);
309		gl.clear			(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
310		gl.enable			(GL_DEPTH_TEST);
311
312		m_testCtx.getLog() << tcu::TestLog::Message << "Testing position invariance with z-test. Enabling GL_DEPTH_TEST." << tcu::TestLog::EndMessage;
313
314		// Draw position check passes
315		for (int passNdx = 0; passNdx < 2; ++passNdx)
316		{
317			const glu::ShaderProgram&	shader			= (passNdx == 0) ? (*m_shader0) : (*m_shader1);
318			const glw::GLint			positionLoc		= gl.getAttribLocation(shader.getProgram(), "a_input");
319			const glw::GLint			colorLoc		= gl.getUniformLocation(shader.getProgram(), "u_color");
320			const tcu::Vec4				red				= tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f);
321			const tcu::Vec4				green			= tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f);
322			const tcu::Vec4				color			= (passNdx == 0) ? (red) : (green);
323			const glw::GLenum			depthFunc		= (passNdx == 0) ? (GL_ALWAYS) : (GL_EQUAL);
324			const char* const			depthFuncStr	= (passNdx == 0) ? ("GL_ALWAYS") : ("GL_EQUAL");
325			const char* const			colorStr		= (passNdx == 0) ? ("red - purple") : ("green");
326
327			m_testCtx.getLog() << tcu::TestLog::Message << "Drawing Z-test pattern using shader " << (passNdx+1) << ". Primitive color: " << colorStr << ". DepthFunc: " << depthFuncStr << tcu::TestLog::EndMessage;
328
329			gl.useProgram				(shader.getProgram());
330			gl.uniform4fv				(colorLoc, 1, color.getPtr());
331			gl.depthFunc				(depthFunc);
332			gl.enableVertexAttribArray	(positionLoc);
333			gl.vertexAttribPointer		(positionLoc, 4, GL_FLOAT, GL_FALSE, sizeof(tcu::Vec4), DE_NULL);
334			gl.drawArrays				(GL_TRIANGLES, m_verticesInPattern, m_verticesInPattern); // !< buffer contains 2 m_verticesInPattern-sized patterns
335			gl.disableVertexAttribArray	(positionLoc);
336			GLU_EXPECT_NO_ERROR			(gl.getError(), "draw pass");
337		}
338
339		// Read result
340		glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess());
341
342		// Check there are no red pixels
343		m_testCtx.getLog() << tcu::TestLog::Message << "Verifying output. Expecting only green or background colored pixels." << tcu::TestLog::EndMessage;
344		error |= !checkImage(resultSurface);
345	}
346
347	// Report result
348	if (error)
349		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Detected variance between two invariant values");
350	else
351		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
352
353	return STOP;
354}
355
356bool InvarianceTest::checkImage (const tcu::Surface& surface) const
357{
358	const tcu::IVec4	okColor		= tcu::IVec4(0, 255, 0, 255);
359	const tcu::RGBA		errColor	= tcu::RGBA(255, 0, 0, 255);
360	bool				error		= false;
361	tcu::Surface		errorMask	(m_renderSize, m_renderSize);
362
363	tcu::clear(errorMask.getAccess(), okColor);
364
365	for (int y = 0; y < m_renderSize; ++y)
366	for (int x = 0; x < m_renderSize; ++x)
367	{
368		const tcu::RGBA col = surface.getPixel(x, y);
369
370		if (col.getRed() != 0)
371		{
372			errorMask.setPixel(x, y, errColor);
373			error = true;
374		}
375	}
376
377	// report error
378	if (error)
379	{
380		m_testCtx.getLog() << tcu::TestLog::Message << "Invalid pixels found (fragments from first render pass found). Variance detected." << tcu::TestLog::EndMessage;
381		m_testCtx.getLog()
382			<< tcu::TestLog::ImageSet("Results", "Result verification")
383			<< tcu::TestLog::Image("Result",		"Result",		surface)
384			<< tcu::TestLog::Image("Error mask",	"Error mask",	errorMask)
385			<< tcu::TestLog::EndImageSet;
386
387		return false;
388	}
389	else
390	{
391		m_testCtx.getLog() << tcu::TestLog::Message << "No variance found." << tcu::TestLog::EndMessage;
392		m_testCtx.getLog()
393			<< tcu::TestLog::ImageSet("Results", "Result verification")
394			<< tcu::TestLog::Image("Result", "Result", surface)
395			<< tcu::TestLog::EndImageSet;
396
397		return true;
398	}
399}
400
401class BasicInvarianceTest : public InvarianceTest
402{
403public:
404								BasicInvarianceTest		(Context& ctx, const char* name, const char* desc, const std::string& vertexShader1, const std::string& vertexShader2);
405								BasicInvarianceTest		(Context& ctx, const char* name, const char* desc, const std::string& vertexShader1, const std::string& vertexShader2, const std::string& fragmentShader);
406	ShaderPair					genShaders				(void) const;
407
408private:
409	const std::string			m_vertexShader1;
410	const std::string			m_vertexShader2;
411	const std::string			m_fragmentShader;
412	static const char* const	s_basicFragmentShader;
413};
414
415const char* const BasicInvarianceTest::s_basicFragmentShader =	"uniform mediump vec4 u_color;\n"
416																"varying mediump vec4 v_unrelated;\n"
417																"void main ()\n"
418																"{\n"
419																"	mediump float blue = dot(v_unrelated, vec4(1.0, 1.0, 1.0, 1.0));\n"
420																"	gl_FragColor = vec4(u_color.r, u_color.g, blue, u_color.a);\n"
421																"}\n";
422
423BasicInvarianceTest::BasicInvarianceTest (Context& ctx, const char* name, const char* desc, const std::string& vertexShader1, const std::string& vertexShader2)
424	: InvarianceTest	(ctx, name, desc)
425	, m_vertexShader1	(vertexShader1)
426	, m_vertexShader2	(vertexShader2)
427	, m_fragmentShader	(s_basicFragmentShader)
428{
429}
430
431BasicInvarianceTest::BasicInvarianceTest (Context& ctx, const char* name, const char* desc, const std::string& vertexShader1, const std::string& vertexShader2, const std::string& fragmentShader)
432	: InvarianceTest	(ctx, name, desc)
433	, m_vertexShader1	(vertexShader1)
434	, m_vertexShader2	(vertexShader2)
435	, m_fragmentShader	(fragmentShader)
436{
437}
438
439BasicInvarianceTest::ShaderPair BasicInvarianceTest::genShaders (void) const
440{
441	ShaderPair retVal;
442
443	retVal.vertexShaderSource0 = m_vertexShader1;
444	retVal.vertexShaderSource1 = m_vertexShader2;
445	retVal.fragmentShaderSource0 = m_fragmentShader;
446	retVal.fragmentShaderSource1 = m_fragmentShader;
447
448	return retVal;
449}
450
451} // anonymous
452
453ShaderInvarianceTests::ShaderInvarianceTests (Context& context)
454	: TestCaseGroup(context, "invariance", "Invariance tests")
455{
456}
457
458ShaderInvarianceTests::~ShaderInvarianceTests (void)
459{
460}
461
462void ShaderInvarianceTests::init (void)
463{
464	static const struct PrecisionCase
465	{
466		glu::Precision	prec;
467		const char*		name;
468
469		// set literals in the glsl to be in the representable range
470		const char*		highValue;		// !< highValue < maxValue
471		const char*		invHighValue;
472		const char*		mediumValue;	// !< mediumValue^2 < maxValue
473		const char*		lowValue;		// !< lowValue^4 < maxValue
474		const char*		invlowValue;
475		int				loopIterations;
476		int				loopPartialIterations;
477		int				loopNormalizationExponent;
478		const char*		loopNormalizationConstantLiteral;
479		const char*		loopMultiplier;
480		const char*		sumLoopNormalizationConstantLiteral;
481	} precisions[] =
482	{
483		{ glu::PRECISION_HIGHP,		"highp",	"1.0e20",	"1.0e-20",	"1.0e14",	"1.0e9",	"1.0e-9",	14,	11,	2,	"1.0e4",	"1.9",	"1.0e3"	},
484		{ glu::PRECISION_MEDIUMP,	"mediump",	"1.0e4",	"1.0e-4",	"1.0e2",	"1.0e1",	"1.0e-1",	13,	11,	2,	"1.0e4",	"1.9",	"1.0e3"	},
485		{ glu::PRECISION_LOWP,		"lowp",		"0.9",		"1.1",		"1.1",		"1.15",		"0.87",		6,	2,	0,	"2.0",		"1.1",	"1.0"	},
486	};
487
488	for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); ++precNdx)
489	{
490		const char* const			precisionName	= precisions[precNdx].name;
491		const glu::Precision		precision		= precisions[precNdx].prec;
492		tcu::TestCaseGroup* const	group			= new tcu::TestCaseGroup(m_testCtx, precisionName, "Invariance tests using the given precision.");
493
494		const FormatArgumentList	args			= FormatArgumentList()
495														<< FormatArgument("VERSION",				"")
496														<< FormatArgument("IN",						"attribute")
497														<< FormatArgument("OUT",					"varying")
498														<< FormatArgument("IN_PREC",				precisionName)
499														<< FormatArgument("HIGH_VALUE",				de::toString(precisions[precNdx].highValue))
500														<< FormatArgument("HIGH_VALUE_INV",			de::toString(precisions[precNdx].invHighValue))
501														<< FormatArgument("MEDIUM_VALUE",			de::toString(precisions[precNdx].mediumValue))
502														<< FormatArgument("LOW_VALUE",				de::toString(precisions[precNdx].lowValue))
503														<< FormatArgument("LOW_VALUE_INV",			de::toString(precisions[precNdx].invlowValue))
504														<< FormatArgument("LOOP_ITERS",				de::toString(precisions[precNdx].loopIterations))
505														<< FormatArgument("LOOP_ITERS_PARTIAL",		de::toString(precisions[precNdx].loopPartialIterations))
506														<< FormatArgument("LOOP_NORM_FRACT_EXP",	de::toString(precisions[precNdx].loopNormalizationExponent))
507														<< FormatArgument("LOOP_NORM_LITERAL",		precisions[precNdx].loopNormalizationConstantLiteral)
508														<< FormatArgument("LOOP_MULTIPLIER",		precisions[precNdx].loopMultiplier)
509														<< FormatArgument("SUM_LOOP_NORM_LITERAL",	precisions[precNdx].sumLoopNormalizationConstantLiteral);
510
511		addChild(group);
512
513		// subexpression cases
514		{
515			// First shader shares "${HIGH_VALUE}*a_input.x*a_input.xxxx + ${HIGH_VALUE}*a_input.y*a_input.yyyy" with unrelated output variable. Reordering might result in accuracy loss
516			// due to the high exponent. In the second shader, the high exponent may be removed during compilation.
517
518			group->addChild(new BasicInvarianceTest(m_context, "common_subexpression_0", "Shader shares a subexpression with an unrelated variable.",
519				formatGLSL(	"${VERSION}"
520							"${IN} ${IN_PREC} vec4 a_input;\n"
521							"${OUT} mediump vec4 v_unrelated;\n"
522							"invariant gl_Position;\n"
523							"void main ()\n"
524							"{\n"
525							"	v_unrelated = a_input.xzxz + (${HIGH_VALUE}*a_input.x*a_input.xxxx + ${HIGH_VALUE}*a_input.y*a_input.yyyy) * (1.08 * a_input.zyzy * a_input.xzxz) * ${HIGH_VALUE_INV} * (a_input.z * a_input.zzxz - a_input.z * a_input.zzxz) + (${HIGH_VALUE}*a_input.x*a_input.xxxx + ${HIGH_VALUE}*a_input.y*a_input.yyyy) / ${HIGH_VALUE};\n"
526							"	gl_Position = a_input + (${HIGH_VALUE}*a_input.x*a_input.xxxx + ${HIGH_VALUE}*a_input.y*a_input.yyyy) * ${HIGH_VALUE_INV};\n"
527							"}\n", args),
528				formatGLSL(	"${VERSION}"
529							"${IN} ${IN_PREC} vec4 a_input;\n"
530							"${OUT} mediump vec4 v_unrelated;\n"
531							"invariant gl_Position;\n"
532							"void main ()\n"
533							"{\n"
534							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
535							"	gl_Position = a_input + (${HIGH_VALUE}*a_input.x*a_input.xxxx + ${HIGH_VALUE}*a_input.y*a_input.yyyy) * ${HIGH_VALUE_INV};\n"
536							"}\n", args)));
537
538			// In the first shader, the unrelated variable "d" has mathematically the same expression as "e", but the different
539			// order of calculation might cause different results.
540
541			group->addChild(new BasicInvarianceTest(m_context, "common_subexpression_1", "Shader shares a subexpression with an unrelated variable.",
542				formatGLSL(	"${VERSION}"
543							"${IN} ${IN_PREC} vec4 a_input;\n"
544							"${OUT} mediump vec4 v_unrelated;\n"
545							"invariant gl_Position;\n"
546							"void main ()\n"
547							"{\n"
548							"	${IN_PREC} vec4 a = ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy - ${HIGH_VALUE} * a_input.zzxx;\n"
549							"	${IN_PREC} vec4 b = ${HIGH_VALUE} * a_input.zzxx;\n"
550							"	${IN_PREC} vec4 c = b - ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy;\n"
551							"	${IN_PREC} vec4 d = (${LOW_VALUE} * a_input.yzxx) * (${LOW_VALUE} * a_input.yzzw) * (1.1*${LOW_VALUE_INV} * a_input.yzxx) * (${LOW_VALUE_INV} * a_input.xzzy);\n"
552							"	${IN_PREC} vec4 e = ((${LOW_VALUE} * a_input.yzxx) * (1.1*${LOW_VALUE_INV} * a_input.yzxx)) * ((${LOW_VALUE_INV} * a_input.xzzy) * (${LOW_VALUE} * a_input.yzzw));\n"
553							"	v_unrelated = a + b + c + d + e;\n"
554							"	gl_Position = a_input + fract(c) + e;\n"
555							"}\n", args),
556				formatGLSL(	"${VERSION}"
557							"${IN} ${IN_PREC} vec4 a_input;\n"
558							"${OUT} mediump vec4 v_unrelated;\n"
559							"invariant gl_Position;\n"
560							"void main ()\n"
561							"{\n"
562							"	${IN_PREC} vec4 b = ${HIGH_VALUE} * a_input.zzxx;\n"
563							"	${IN_PREC} vec4 c = b - ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy;\n"
564							"	${IN_PREC} vec4 e = ((${LOW_VALUE} * a_input.yzxx) * (1.1*${LOW_VALUE_INV} * a_input.yzxx)) * ((${LOW_VALUE_INV} * a_input.xzzy) * (${LOW_VALUE} * a_input.yzzw));\n"
565							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
566							"	gl_Position = a_input + fract(c) + e;\n"
567							"}\n", args)));
568
569			// Intermediate values used by an unrelated output variable
570
571			group->addChild(new BasicInvarianceTest(m_context, "common_subexpression_2", "Shader shares a subexpression with an unrelated variable.",
572				formatGLSL(	"${VERSION}"
573							"${IN} ${IN_PREC} vec4 a_input;\n"
574							"${OUT} mediump vec4 v_unrelated;\n"
575							"invariant gl_Position;\n"
576							"void main ()\n"
577							"{\n"
578							"	${IN_PREC} vec4 a = ${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy);\n"
579							"	${IN_PREC} vec4 b = (${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy)) * (${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy)) / ${MEDIUM_VALUE} / ${MEDIUM_VALUE};\n"
580							"	${IN_PREC} vec4 c = a * a;\n"
581							"	${IN_PREC} vec4 d = c / ${MEDIUM_VALUE} / ${MEDIUM_VALUE};\n"
582							"	v_unrelated = a + b + c + d;\n"
583							"	gl_Position = a_input + d;\n"
584							"}\n", args),
585				formatGLSL(	"${VERSION}"
586							"${IN} ${IN_PREC} vec4 a_input;\n"
587							"${OUT} mediump vec4 v_unrelated;\n"
588							"invariant gl_Position;\n"
589							"void main ()\n"
590							"{\n"
591							"	${IN_PREC} vec4 a = ${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy);\n"
592							"	${IN_PREC} vec4 c = a * a;\n"
593							"	${IN_PREC} vec4 d = c / ${MEDIUM_VALUE} / ${MEDIUM_VALUE};\n"
594							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
595							"	gl_Position = a_input + d;\n"
596							"}\n", args)));
597
598			// Invariant value can be calculated using unrelated value
599
600			group->addChild(new BasicInvarianceTest(m_context, "common_subexpression_3", "Shader shares a subexpression with an unrelated variable.",
601				formatGLSL(	"${VERSION}"
602							"${IN} ${IN_PREC} vec4 a_input;\n"
603							"${OUT} mediump vec4 v_unrelated;\n"
604							"invariant gl_Position;\n"
605							"void main ()\n"
606							"{\n"
607							"	${IN_PREC} float x = a_input.x * 0.2;\n"
608							"	${IN_PREC} vec4 a = a_input.xxyx * 0.7;\n"
609							"	${IN_PREC} vec4 b = a_input.yxyz * 0.7;\n"
610							"	${IN_PREC} vec4 c = a_input.zxyx * 0.5;\n"
611							"	${IN_PREC} vec4 f = x*a + x*b + x*c;\n"
612							"	v_unrelated = f;\n"
613							"	${IN_PREC} vec4 g = x * (a + b + c);\n"
614							"	gl_Position = a_input + g;\n"
615							"}\n", args),
616				formatGLSL(	"${VERSION}"
617							"${IN} ${IN_PREC} vec4 a_input;\n"
618							"${OUT} mediump vec4 v_unrelated;\n"
619							"invariant gl_Position;\n"
620							"void main ()\n"
621							"{\n"
622							"	${IN_PREC} float x = a_input.x * 0.2;\n"
623							"	${IN_PREC} vec4 a = a_input.xxyx * 0.7;\n"
624							"	${IN_PREC} vec4 b = a_input.yxyz * 0.7;\n"
625							"	${IN_PREC} vec4 c = a_input.zxyx * 0.5;\n"
626							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
627							"	${IN_PREC} vec4 g = x * (a + b + c);\n"
628							"	gl_Position = a_input + g;\n"
629							"}\n", args)));
630		}
631
632		// shared subexpression of different precision
633		{
634			for (int precisionOther = glu::PRECISION_LOWP; precisionOther != glu::PRECISION_LAST; ++precisionOther)
635			{
636				const char* const		unrelatedPrec				= glu::getPrecisionName((glu::Precision)precisionOther);
637				const glu::Precision	minPrecision				= (precisionOther < (int)precision) ? ((glu::Precision)precisionOther) : (precision);
638				const char* const		multiplierStr				= (minPrecision == glu::PRECISION_LOWP) ? ("0.8, 0.4, -0.2, 0.3") : ("1.0e1, 5.0e2, 2.0e2, 1.0");
639				const char* const		normalizationStrUsed		= (minPrecision == glu::PRECISION_LOWP) ? ("vec4(fract(used2).xyz, 0.0)") : ("vec4(fract(used2 / 1.0e2).xyz - fract(used2 / 1.0e3).xyz, 0.0)");
640				const char* const		normalizationStrUnrelated	= (minPrecision == glu::PRECISION_LOWP) ? ("vec4(fract(unrelated2).xyz, 0.0)") : ("vec4(fract(unrelated2 / 1.0e2).xyz - fract(unrelated2 / 1.0e3).xyz, 0.0)");
641
642				group->addChild(new BasicInvarianceTest(m_context, ("subexpression_precision_" + std::string(unrelatedPrec)).c_str(), "Shader shares subexpression of different precision with an unrelated variable.",
643					formatGLSL(	"${VERSION}"
644								"${IN} ${IN_PREC} vec4 a_input;\n"
645								"${OUT} ${UNRELATED_PREC} vec4 v_unrelated;\n"
646								"invariant gl_Position;\n"
647								"void main ()\n"
648								"{\n"
649								"	${UNRELATED_PREC} vec4 unrelated0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
650								"	${UNRELATED_PREC} vec4 unrelated1 = vec4(${MULTIPLIER}) * unrelated0.xywz + unrelated0;\n"
651								"	${UNRELATED_PREC} vec4 unrelated2 = refract(unrelated1, unrelated0, distance(unrelated0, unrelated1));\n"
652								"	v_unrelated = a_input + 0.02 * ${NORMALIZE_UNRELATED};\n"
653								"	${IN_PREC} vec4 used0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
654								"	${IN_PREC} vec4 used1 = vec4(${MULTIPLIER}) * used0.xywz + used0;\n"
655								"	${IN_PREC} vec4 used2 = refract(used1, used0, distance(used0, used1));\n"
656								"	gl_Position = a_input + 0.02 * ${NORMALIZE_USED};\n"
657								"}\n", FormatArgumentList(args)
658											<< FormatArgument("UNRELATED_PREC",			unrelatedPrec)
659											<< FormatArgument("MULTIPLIER",				multiplierStr)
660											<< FormatArgument("NORMALIZE_USED",			normalizationStrUsed)
661											<< FormatArgument("NORMALIZE_UNRELATED",	normalizationStrUnrelated)),
662					formatGLSL(	"${VERSION}"
663								"${IN} ${IN_PREC} vec4 a_input;\n"
664								"${OUT} ${UNRELATED_PREC} vec4 v_unrelated;\n"
665								"invariant gl_Position;\n"
666								"void main ()\n"
667								"{\n"
668								"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
669								"	${IN_PREC} vec4 used0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
670								"	${IN_PREC} vec4 used1 = vec4(${MULTIPLIER}) * used0.xywz + used0;\n"
671								"	${IN_PREC} vec4 used2 = refract(used1, used0, distance(used0, used1));\n"
672								"	gl_Position = a_input + 0.02 * ${NORMALIZE_USED};\n"
673								"}\n", FormatArgumentList(args)
674											<< FormatArgument("UNRELATED_PREC",			unrelatedPrec)
675											<< FormatArgument("MULTIPLIER",				multiplierStr)
676											<< FormatArgument("NORMALIZE_USED",			normalizationStrUsed)
677											<< FormatArgument("NORMALIZE_UNRELATED",	normalizationStrUnrelated))));
678			}
679		}
680
681		// loops
682		{
683			group->addChild(new BasicInvarianceTest(m_context, "loop_0", "Invariant value set using a loop",
684				formatGLSL(	"${VERSION}"
685							"${IN} ${IN_PREC} vec4 a_input;\n"
686							"${OUT} highp vec4 v_unrelated;\n"
687							"invariant gl_Position;\n"
688							"void main ()\n"
689							"{\n"
690							"	${IN_PREC} vec4 value = a_input;\n"
691							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
692							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
693							"	{\n"
694							"		value *= ${LOOP_MULTIPLIER};\n"
695							"		v_unrelated += value;\n"
696							"	}\n"
697							"	gl_Position = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
698							"}\n", args),
699				formatGLSL(	"${VERSION}"
700							"${IN} ${IN_PREC} vec4 a_input;\n"
701							"${OUT} highp vec4 v_unrelated;\n"
702							"invariant gl_Position;\n"
703							"void main ()\n"
704							"{\n"
705							"	${IN_PREC} vec4 value = a_input;\n"
706							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
707							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
708							"	{\n"
709							"		value *= ${LOOP_MULTIPLIER};\n"
710							"	}\n"
711							"	gl_Position = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
712							"}\n", args)));
713
714			group->addChild(new BasicInvarianceTest(m_context, "loop_1", "Invariant value set using a loop",
715				formatGLSL(	"${VERSION}"
716							"${IN} ${IN_PREC} vec4 a_input;\n"
717							"${OUT} mediump vec4 v_unrelated;\n"
718							"invariant gl_Position;\n"
719							"void main ()\n"
720							"{\n"
721							"	${IN_PREC} vec4 value = a_input;\n"
722							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
723							"	{\n"
724							"		value *= ${LOOP_MULTIPLIER};\n"
725							"		if (i == ${LOOP_ITERS_PARTIAL})\n"
726							"			v_unrelated = value;\n"
727							"	}\n"
728							"	gl_Position = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
729							"}\n", args),
730				formatGLSL(	"${VERSION}"
731							"${IN} ${IN_PREC} vec4 a_input;\n"
732							"${OUT} mediump vec4 v_unrelated;\n"
733							"invariant gl_Position;\n"
734							"void main ()\n"
735							"{\n"
736							"	${IN_PREC} vec4 value = a_input;\n"
737							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
738							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
739							"	{\n"
740							"		value *= ${LOOP_MULTIPLIER};\n"
741							"	}\n"
742							"	gl_Position = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
743							"}\n", args)));
744
745			group->addChild(new BasicInvarianceTest(m_context, "loop_2", "Invariant value set using a loop",
746				formatGLSL(	"${VERSION}"
747							"${IN} ${IN_PREC} vec4 a_input;\n"
748							"${OUT} mediump vec4 v_unrelated;\n"
749							"invariant gl_Position;\n"
750							"void main ()\n"
751							"{\n"
752							"	${IN_PREC} vec4 value = a_input;\n"
753							"	v_unrelated = vec4(0.0, 0.0, -1.0, 1.0);\n"
754							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
755							"	{\n"
756							"		value *= ${LOOP_MULTIPLIER};\n"
757							"		if (i == ${LOOP_ITERS_PARTIAL})\n"
758							"			gl_Position = a_input + 0.05 * vec4(fract(value.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
759							"		else\n"
760							"			v_unrelated = value + a_input;\n"
761							"	}\n"
762							"}\n", args),
763				formatGLSL(	"${VERSION}"
764							"${IN} ${IN_PREC} vec4 a_input;\n"
765							"${OUT} mediump vec4 v_unrelated;\n"
766							"invariant gl_Position;\n"
767							"void main ()\n"
768							"{\n"
769							"	${IN_PREC} vec4 value = a_input;\n"
770							"	v_unrelated = vec4(0.0, 0.0, -1.0, 1.0);\n"
771							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
772							"	{\n"
773							"		value *= ${LOOP_MULTIPLIER};\n"
774							"		if (i == ${LOOP_ITERS_PARTIAL})\n"
775							"			gl_Position = a_input + 0.05 * vec4(fract(value.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
776							"		else\n"
777							"			v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
778							"	}\n"
779							"}\n", args)));
780
781			group->addChild(new BasicInvarianceTest(m_context, "loop_3", "Invariant value set using a loop",
782				formatGLSL(	"${VERSION}"
783							"${IN} ${IN_PREC} vec4 a_input;\n"
784							"${OUT} mediump vec4 v_unrelated;\n"
785							"invariant gl_Position;\n"
786							"void main ()\n"
787							"{\n"
788							"	${IN_PREC} vec4 value = a_input;\n"
789							"	gl_Position = vec4(0.0, 0.0, 0.0, 0.0);\n"
790							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
791							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
792							"	{\n"
793							"		value *= ${LOOP_MULTIPLIER};\n"
794							"		gl_Position += vec4(value.xyz / ${SUM_LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
795							"		v_unrelated = gl_Position.xyzx * a_input;\n"
796							"	}\n"
797							"}\n", args),
798				formatGLSL(	"${VERSION}"
799							"${IN} ${IN_PREC} vec4 a_input;\n"
800							"${OUT} mediump vec4 v_unrelated;\n"
801							"invariant gl_Position;\n"
802							"void main ()\n"
803							"{\n"
804							"	${IN_PREC} vec4 value = a_input;\n"
805							"	gl_Position = vec4(0.0, 0.0, 0.0, 0.0);\n"
806							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
807							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
808							"	{\n"
809							"		value *= ${LOOP_MULTIPLIER};\n"
810							"		gl_Position += vec4(value.xyz / ${SUM_LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
811							"	}\n"
812							"}\n", args)));
813
814			group->addChild(new BasicInvarianceTest(m_context, "loop_4", "Invariant value set using a loop",
815				formatGLSL(	"${VERSION}"
816							"${IN} ${IN_PREC} vec4 a_input;\n"
817							"${OUT} mediump vec4 v_unrelated;\n"
818							"invariant gl_Position;\n"
819							"void main ()\n"
820							"{\n"
821							"	${IN_PREC} vec4 position = vec4(0.0, 0.0, 0.0, 0.0);\n"
822							"	${IN_PREC} vec4 value1 = a_input;\n"
823							"	${IN_PREC} vec4 value2 = a_input;\n"
824							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
825							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
826							"	{\n"
827							"		value1 *= ${LOOP_MULTIPLIER};\n"
828							"		v_unrelated = v_unrelated*1.3 + a_input.xyzx * value1.xyxw;\n"
829							"	}\n"
830							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
831							"	{\n"
832							"		value2 *= ${LOOP_MULTIPLIER};\n"
833							"		position = position*1.3 + a_input.xyzx * value2.xyxw;\n"
834							"	}\n"
835							"	gl_Position = a_input + 0.05 * vec4(fract(position.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
836							"}\n", args),
837				formatGLSL(	"${VERSION}"
838							"${IN} ${IN_PREC} vec4 a_input;\n"
839							"${OUT} mediump vec4 v_unrelated;\n"
840							"invariant gl_Position;\n"
841							"void main ()\n"
842							"{\n"
843							"	${IN_PREC} vec4 position = vec4(0.0, 0.0, 0.0, 0.0);\n"
844							"	${IN_PREC} vec4 value2 = a_input;\n"
845							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
846							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
847							"	{\n"
848							"		value2 *= ${LOOP_MULTIPLIER};\n"
849							"		position = position*1.3 + a_input.xyzx * value2.xyxw;\n"
850							"	}\n"
851							"	gl_Position = a_input + 0.05 * vec4(fract(position.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
852							"}\n", args)));
853		}
854	}
855}
856
857} // Functional
858} // gles2
859} // deqp
860