1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program OpenGL ES 3.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 "es3fShaderInvarianceTests.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 
40 namespace deqp
41 {
42 namespace gles3
43 {
44 namespace Functional
45 {
46 namespace
47 {
48 
49 class FormatArgumentList;
50 
genRandomVector(de::Random & rnd)51 static 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 
63 class FormatArgument
64 {
65 public:
66 						FormatArgument (const char* name, const std::string& value);
67 
68 private:
69 	friend class FormatArgumentList;
70 
71 	const char* const	m_name;
72 	const std::string	m_value;
73 };
74 
FormatArgument(const char * name,const std::string & value)75 FormatArgument::FormatArgument (const char* name, const std::string& value)
76 	: m_name	(name)
77 	, m_value	(value)
78 {
79 }
80 
81 class FormatArgumentList
82 {
83 public:
84 												FormatArgumentList	(void);
85 
86 	FormatArgumentList&							operator<<			(const FormatArgument&);
87 	const std::map<std::string, std::string>&	getArguments		(void) const;
88 
89 private:
90 	std::map<std::string, std::string>			m_formatArguments;
91 };
92 
FormatArgumentList(void)93 FormatArgumentList::FormatArgumentList (void)
94 {
95 }
96 
operator <<(const FormatArgument & arg)97 FormatArgumentList&	FormatArgumentList::operator<< (const FormatArgument& arg)
98 {
99 	m_formatArguments[arg.m_name] = arg.m_value;
100 	return *this;
101 }
102 
getArguments(void) const103 const std::map<std::string, std::string>& FormatArgumentList::getArguments (void) const
104 {
105 	return m_formatArguments;
106 }
107 
formatGLSL(const char * templateString,const FormatArgumentList & args)108 static 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  *//*--------------------------------------------------------------------*/
123 class InvarianceTest : public TestCase
124 {
125 public:
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 
141 private:
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 
InvarianceTest(Context & ctx,const char * name,const char * desc)153 InvarianceTest::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 
~InvarianceTest(void)163 InvarianceTest::~InvarianceTest (void)
164 {
165 	deinit();
166 }
167 
init(void)168 void 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 
deinit(void)241 void 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 
iterate(void)256 InvarianceTest::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 
checkImage(const tcu::Surface & surface) const356 bool 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 
401 class BasicInvarianceTest : public InvarianceTest
402 {
403 public:
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 
408 private:
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 
415 const char* const BasicInvarianceTest::s_basicFragmentShader =	"#version 300 es\n"
416 																"layout(location = 0) out mediump vec4 fragColor;\n"
417 																"uniform mediump vec4 u_color;\n"
418 																"in mediump vec4 v_unrelated;\n"
419 																"void main ()\n"
420 																"{\n"
421 																"	mediump float blue = dot(v_unrelated, vec4(1.0, 1.0, 1.0, 1.0));\n"
422 																"	fragColor = vec4(u_color.r, u_color.g, blue, u_color.a);\n"
423 																"}\n";
424 
BasicInvarianceTest(Context & ctx,const char * name,const char * desc,const std::string & vertexShader1,const std::string & vertexShader2)425 BasicInvarianceTest::BasicInvarianceTest (Context& ctx, const char* name, const char* desc, const std::string& vertexShader1, const std::string& vertexShader2)
426 	: InvarianceTest	(ctx, name, desc)
427 	, m_vertexShader1	(vertexShader1)
428 	, m_vertexShader2	(vertexShader2)
429 	, m_fragmentShader	(s_basicFragmentShader)
430 {
431 }
432 
BasicInvarianceTest(Context & ctx,const char * name,const char * desc,const std::string & vertexShader1,const std::string & vertexShader2,const std::string & fragmentShader)433 BasicInvarianceTest::BasicInvarianceTest (Context& ctx, const char* name, const char* desc, const std::string& vertexShader1, const std::string& vertexShader2, const std::string& fragmentShader)
434 	: InvarianceTest	(ctx, name, desc)
435 	, m_vertexShader1	(vertexShader1)
436 	, m_vertexShader2	(vertexShader2)
437 	, m_fragmentShader	(fragmentShader)
438 {
439 }
440 
genShaders(void) const441 BasicInvarianceTest::ShaderPair BasicInvarianceTest::genShaders (void) const
442 {
443 	ShaderPair retVal;
444 
445 	retVal.vertexShaderSource0 = m_vertexShader1;
446 	retVal.vertexShaderSource1 = m_vertexShader2;
447 	retVal.fragmentShaderSource0 = m_fragmentShader;
448 	retVal.fragmentShaderSource1 = m_fragmentShader;
449 
450 	return retVal;
451 }
452 
453 } // anonymous
454 
ShaderInvarianceTests(Context & context)455 ShaderInvarianceTests::ShaderInvarianceTests (Context& context)
456 	: TestCaseGroup(context, "invariance", "Invariance tests")
457 {
458 }
459 
~ShaderInvarianceTests(void)460 ShaderInvarianceTests::~ShaderInvarianceTests (void)
461 {
462 }
463 
init(void)464 void ShaderInvarianceTests::init (void)
465 {
466 	static const struct PrecisionCase
467 	{
468 		glu::Precision	prec;
469 		const char*		name;
470 
471 		// set literals in the glsl to be in the representable range
472 		const char*		highValue;		// !< highValue < maxValue
473 		const char*		invHighValue;
474 		const char*		mediumValue;	// !< mediumValue^2 < maxValue
475 		const char*		lowValue;		// !< lowValue^4 < maxValue
476 		const char*		invlowValue;
477 		int				loopIterations;
478 		int				loopPartialIterations;
479 		int				loopNormalizationExponent;
480 		const char*		loopNormalizationConstantLiteral;
481 		const char*		loopMultiplier;
482 		const char*		sumLoopNormalizationConstantLiteral;
483 	} precisions[] =
484 	{
485 		{ 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"	},
486 		{ 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"	},
487 		{ glu::PRECISION_LOWP,		"lowp",		"0.9",		"1.1",		"1.1",		"1.15",		"0.87",		6,	2,	0,	"2.0",		"1.1",	"1.0"	},
488 	};
489 
490 	for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); ++precNdx)
491 	{
492 		const char* const			precisionName	= precisions[precNdx].name;
493 		const glu::Precision		precision		= precisions[precNdx].prec;
494 		tcu::TestCaseGroup* const	group			= new tcu::TestCaseGroup(m_testCtx, precisionName, "Invariance tests using the given precision.");
495 
496 		const FormatArgumentList	args			= FormatArgumentList()
497 														<< FormatArgument("VERSION",				"#version 300 es\n")
498 														<< FormatArgument("IN",						"in")
499 														<< FormatArgument("OUT",					"out")
500 														<< FormatArgument("IN_PREC",				precisionName)
501 														<< FormatArgument("HIGH_VALUE",				de::toString(precisions[precNdx].highValue))
502 														<< FormatArgument("HIGH_VALUE_INV",			de::toString(precisions[precNdx].invHighValue))
503 														<< FormatArgument("MEDIUM_VALUE",			de::toString(precisions[precNdx].mediumValue))
504 														<< FormatArgument("LOW_VALUE",				de::toString(precisions[precNdx].lowValue))
505 														<< FormatArgument("LOW_VALUE_INV",			de::toString(precisions[precNdx].invlowValue))
506 														<< FormatArgument("LOOP_ITERS",				de::toString(precisions[precNdx].loopIterations))
507 														<< FormatArgument("LOOP_ITERS_PARTIAL",		de::toString(precisions[precNdx].loopPartialIterations))
508 														<< FormatArgument("LOOP_NORM_FRACT_EXP",	de::toString(precisions[precNdx].loopNormalizationExponent))
509 														<< FormatArgument("LOOP_NORM_LITERAL",		precisions[precNdx].loopNormalizationConstantLiteral)
510 														<< FormatArgument("LOOP_MULTIPLIER",		precisions[precNdx].loopMultiplier)
511 														<< FormatArgument("SUM_LOOP_NORM_LITERAL",	precisions[precNdx].sumLoopNormalizationConstantLiteral);
512 
513 		addChild(group);
514 
515 		// subexpression cases
516 		{
517 			// 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
518 			// due to the high exponent. In the second shader, the high exponent may be removed during compilation.
519 
520 			group->addChild(new BasicInvarianceTest(m_context, "common_subexpression_0", "Shader shares a subexpression with an unrelated variable.",
521 				formatGLSL(	"${VERSION}"
522 							"${IN} ${IN_PREC} vec4 a_input;\n"
523 							"${OUT} mediump vec4 v_unrelated;\n"
524 							"invariant gl_Position;\n"
525 							"void main ()\n"
526 							"{\n"
527 							"	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"
528 							"	gl_Position = a_input + (${HIGH_VALUE}*a_input.x*a_input.xxxx + ${HIGH_VALUE}*a_input.y*a_input.yyyy) * ${HIGH_VALUE_INV};\n"
529 							"}\n", args),
530 				formatGLSL(	"${VERSION}"
531 							"${IN} ${IN_PREC} vec4 a_input;\n"
532 							"${OUT} mediump vec4 v_unrelated;\n"
533 							"invariant gl_Position;\n"
534 							"void main ()\n"
535 							"{\n"
536 							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
537 							"	gl_Position = a_input + (${HIGH_VALUE}*a_input.x*a_input.xxxx + ${HIGH_VALUE}*a_input.y*a_input.yyyy) * ${HIGH_VALUE_INV};\n"
538 							"}\n", args)));
539 
540 			// In the first shader, the unrelated variable "d" has mathematically the same expression as "e", but the different
541 			// order of calculation might cause different results.
542 
543 			group->addChild(new BasicInvarianceTest(m_context, "common_subexpression_1", "Shader shares a subexpression with an unrelated variable.",
544 				formatGLSL(	"${VERSION}"
545 							"${IN} ${IN_PREC} vec4 a_input;\n"
546 							"${OUT} mediump vec4 v_unrelated;\n"
547 							"invariant gl_Position;\n"
548 							"void main ()\n"
549 							"{\n"
550 							"	${IN_PREC} vec4 a = ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy - ${HIGH_VALUE} * a_input.zzxx;\n"
551 							"	${IN_PREC} vec4 b = ${HIGH_VALUE} * a_input.zzxx;\n"
552 							"	${IN_PREC} vec4 c = b - ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy;\n"
553 							"	${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"
554 							"	${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"
555 							"	v_unrelated = a + b + c + d + e;\n"
556 							"	gl_Position = a_input + fract(c) + e;\n"
557 							"}\n", args),
558 				formatGLSL(	"${VERSION}"
559 							"${IN} ${IN_PREC} vec4 a_input;\n"
560 							"${OUT} mediump vec4 v_unrelated;\n"
561 							"invariant gl_Position;\n"
562 							"void main ()\n"
563 							"{\n"
564 							"	${IN_PREC} vec4 b = ${HIGH_VALUE} * a_input.zzxx;\n"
565 							"	${IN_PREC} vec4 c = b - ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy;\n"
566 							"	${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"
567 							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
568 							"	gl_Position = a_input + fract(c) + e;\n"
569 							"}\n", args)));
570 
571 			// Intermediate values used by an unrelated output variable
572 
573 			group->addChild(new BasicInvarianceTest(m_context, "common_subexpression_2", "Shader shares a subexpression with an unrelated variable.",
574 				formatGLSL(	"${VERSION}"
575 							"${IN} ${IN_PREC} vec4 a_input;\n"
576 							"${OUT} mediump vec4 v_unrelated;\n"
577 							"invariant gl_Position;\n"
578 							"void main ()\n"
579 							"{\n"
580 							"	${IN_PREC} vec4 a = ${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy);\n"
581 							"	${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"
582 							"	${IN_PREC} vec4 c = a * a;\n"
583 							"	${IN_PREC} vec4 d = c / ${MEDIUM_VALUE} / ${MEDIUM_VALUE};\n"
584 							"	v_unrelated = a + b + c + d;\n"
585 							"	gl_Position = a_input + d;\n"
586 							"}\n", args),
587 				formatGLSL(	"${VERSION}"
588 							"${IN} ${IN_PREC} vec4 a_input;\n"
589 							"${OUT} mediump vec4 v_unrelated;\n"
590 							"invariant gl_Position;\n"
591 							"void main ()\n"
592 							"{\n"
593 							"	${IN_PREC} vec4 a = ${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy);\n"
594 							"	${IN_PREC} vec4 c = a * a;\n"
595 							"	${IN_PREC} vec4 d = c / ${MEDIUM_VALUE} / ${MEDIUM_VALUE};\n"
596 							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
597 							"	gl_Position = a_input + d;\n"
598 							"}\n", args)));
599 
600 			// Invariant value can be calculated using unrelated value
601 
602 			group->addChild(new BasicInvarianceTest(m_context, "common_subexpression_3", "Shader shares a subexpression with an unrelated variable.",
603 				formatGLSL(	"${VERSION}"
604 							"${IN} ${IN_PREC} vec4 a_input;\n"
605 							"${OUT} mediump vec4 v_unrelated;\n"
606 							"invariant gl_Position;\n"
607 							"void main ()\n"
608 							"{\n"
609 							"	${IN_PREC} float x = a_input.x * 0.2;\n"
610 							"	${IN_PREC} vec4 a = a_input.xxyx * 0.7;\n"
611 							"	${IN_PREC} vec4 b = a_input.yxyz * 0.7;\n"
612 							"	${IN_PREC} vec4 c = a_input.zxyx * 0.5;\n"
613 							"	${IN_PREC} vec4 f = x*a + x*b + x*c;\n"
614 							"	v_unrelated = f;\n"
615 							"	${IN_PREC} vec4 g = x * (a + b + c);\n"
616 							"	gl_Position = a_input + g;\n"
617 							"}\n", args),
618 				formatGLSL(	"${VERSION}"
619 							"${IN} ${IN_PREC} vec4 a_input;\n"
620 							"${OUT} mediump vec4 v_unrelated;\n"
621 							"invariant gl_Position;\n"
622 							"void main ()\n"
623 							"{\n"
624 							"	${IN_PREC} float x = a_input.x * 0.2;\n"
625 							"	${IN_PREC} vec4 a = a_input.xxyx * 0.7;\n"
626 							"	${IN_PREC} vec4 b = a_input.yxyz * 0.7;\n"
627 							"	${IN_PREC} vec4 c = a_input.zxyx * 0.5;\n"
628 							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
629 							"	${IN_PREC} vec4 g = x * (a + b + c);\n"
630 							"	gl_Position = a_input + g;\n"
631 							"}\n", args)));
632 		}
633 
634 		// shared subexpression of different precision
635 		{
636 			for (int precisionOther = glu::PRECISION_LOWP; precisionOther != glu::PRECISION_LAST; ++precisionOther)
637 			{
638 				const char* const		unrelatedPrec				= glu::getPrecisionName((glu::Precision)precisionOther);
639 				const glu::Precision	minPrecision				= (precisionOther < (int)precision) ? ((glu::Precision)precisionOther) : (precision);
640 				const char* const		multiplierStr				= (minPrecision == glu::PRECISION_LOWP) ? ("0.8, 0.4, -0.2, 0.3") : ("1.0e1, 5.0e2, 2.0e2, 1.0");
641 				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)");
642 				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)");
643 
644 				group->addChild(new BasicInvarianceTest(m_context, ("subexpression_precision_" + std::string(unrelatedPrec)).c_str(), "Shader shares subexpression of different precision with an unrelated variable.",
645 					formatGLSL(	"${VERSION}"
646 								"${IN} ${IN_PREC} vec4 a_input;\n"
647 								"${OUT} ${UNRELATED_PREC} vec4 v_unrelated;\n"
648 								"invariant gl_Position;\n"
649 								"void main ()\n"
650 								"{\n"
651 								"	${UNRELATED_PREC} vec4 unrelated0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
652 								"	${UNRELATED_PREC} vec4 unrelated1 = vec4(${MULTIPLIER}) * unrelated0.xywz + unrelated0;\n"
653 								"	${UNRELATED_PREC} vec4 unrelated2 = refract(unrelated1, unrelated0, distance(unrelated0, unrelated1));\n"
654 								"	v_unrelated = a_input + 0.02 * ${NORMALIZE_UNRELATED};\n"
655 								"	${IN_PREC} vec4 used0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
656 								"	${IN_PREC} vec4 used1 = vec4(${MULTIPLIER}) * used0.xywz + used0;\n"
657 								"	${IN_PREC} vec4 used2 = refract(used1, used0, distance(used0, used1));\n"
658 								"	gl_Position = a_input + 0.02 * ${NORMALIZE_USED};\n"
659 								"}\n", FormatArgumentList(args)
660 											<< FormatArgument("UNRELATED_PREC",			unrelatedPrec)
661 											<< FormatArgument("MULTIPLIER",				multiplierStr)
662 											<< FormatArgument("NORMALIZE_USED",			normalizationStrUsed)
663 											<< FormatArgument("NORMALIZE_UNRELATED",	normalizationStrUnrelated)),
664 					formatGLSL(	"${VERSION}"
665 								"${IN} ${IN_PREC} vec4 a_input;\n"
666 								"${OUT} ${UNRELATED_PREC} vec4 v_unrelated;\n"
667 								"invariant gl_Position;\n"
668 								"void main ()\n"
669 								"{\n"
670 								"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
671 								"	${IN_PREC} vec4 used0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
672 								"	${IN_PREC} vec4 used1 = vec4(${MULTIPLIER}) * used0.xywz + used0;\n"
673 								"	${IN_PREC} vec4 used2 = refract(used1, used0, distance(used0, used1));\n"
674 								"	gl_Position = a_input + 0.02 * ${NORMALIZE_USED};\n"
675 								"}\n", FormatArgumentList(args)
676 											<< FormatArgument("UNRELATED_PREC",			unrelatedPrec)
677 											<< FormatArgument("MULTIPLIER",				multiplierStr)
678 											<< FormatArgument("NORMALIZE_USED",			normalizationStrUsed)
679 											<< FormatArgument("NORMALIZE_UNRELATED",	normalizationStrUnrelated))));
680 			}
681 		}
682 
683 		// loops
684 		{
685 			group->addChild(new BasicInvarianceTest(m_context, "loop_0", "Invariant value set using a loop",
686 				formatGLSL(	"${VERSION}"
687 							"${IN} ${IN_PREC} vec4 a_input;\n"
688 							"${OUT} highp vec4 v_unrelated;\n"
689 							"invariant gl_Position;\n"
690 							"void main ()\n"
691 							"{\n"
692 							"	${IN_PREC} vec4 value = a_input;\n"
693 							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
694 							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
695 							"	{\n"
696 							"		value *= ${LOOP_MULTIPLIER};\n"
697 							"		v_unrelated += value;\n"
698 							"	}\n"
699 							"	gl_Position = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
700 							"}\n", args),
701 				formatGLSL(	"${VERSION}"
702 							"${IN} ${IN_PREC} vec4 a_input;\n"
703 							"${OUT} highp vec4 v_unrelated;\n"
704 							"invariant gl_Position;\n"
705 							"void main ()\n"
706 							"{\n"
707 							"	${IN_PREC} vec4 value = a_input;\n"
708 							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
709 							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
710 							"	{\n"
711 							"		value *= ${LOOP_MULTIPLIER};\n"
712 							"	}\n"
713 							"	gl_Position = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
714 							"}\n", args)));
715 
716 			group->addChild(new BasicInvarianceTest(m_context, "loop_1", "Invariant value set using a loop",
717 				formatGLSL(	"${VERSION}"
718 							"${IN} ${IN_PREC} vec4 a_input;\n"
719 							"${OUT} mediump vec4 v_unrelated;\n"
720 							"invariant gl_Position;\n"
721 							"void main ()\n"
722 							"{\n"
723 							"	${IN_PREC} vec4 value = a_input;\n"
724 							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
725 							"	{\n"
726 							"		value *= ${LOOP_MULTIPLIER};\n"
727 							"		if (i == ${LOOP_ITERS_PARTIAL})\n"
728 							"			v_unrelated = value;\n"
729 							"	}\n"
730 							"	gl_Position = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
731 							"}\n", args),
732 				formatGLSL(	"${VERSION}"
733 							"${IN} ${IN_PREC} vec4 a_input;\n"
734 							"${OUT} mediump vec4 v_unrelated;\n"
735 							"invariant gl_Position;\n"
736 							"void main ()\n"
737 							"{\n"
738 							"	${IN_PREC} vec4 value = a_input;\n"
739 							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
740 							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
741 							"	{\n"
742 							"		value *= ${LOOP_MULTIPLIER};\n"
743 							"	}\n"
744 							"	gl_Position = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
745 							"}\n", args)));
746 
747 			group->addChild(new BasicInvarianceTest(m_context, "loop_2", "Invariant value set using a loop",
748 				formatGLSL(	"${VERSION}"
749 							"${IN} ${IN_PREC} vec4 a_input;\n"
750 							"${OUT} mediump vec4 v_unrelated;\n"
751 							"invariant gl_Position;\n"
752 							"void main ()\n"
753 							"{\n"
754 							"	${IN_PREC} vec4 value = a_input;\n"
755 							"	v_unrelated = vec4(0.0, 0.0, -1.0, 1.0);\n"
756 							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
757 							"	{\n"
758 							"		value *= ${LOOP_MULTIPLIER};\n"
759 							"		if (i == ${LOOP_ITERS_PARTIAL})\n"
760 							"			gl_Position = a_input + 0.05 * vec4(fract(value.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
761 							"		else\n"
762 							"			v_unrelated = value + a_input;\n"
763 							"	}\n"
764 							"}\n", args),
765 				formatGLSL(	"${VERSION}"
766 							"${IN} ${IN_PREC} vec4 a_input;\n"
767 							"${OUT} mediump vec4 v_unrelated;\n"
768 							"invariant gl_Position;\n"
769 							"void main ()\n"
770 							"{\n"
771 							"	${IN_PREC} vec4 value = a_input;\n"
772 							"	v_unrelated = vec4(0.0, 0.0, -1.0, 1.0);\n"
773 							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
774 							"	{\n"
775 							"		value *= ${LOOP_MULTIPLIER};\n"
776 							"		if (i == ${LOOP_ITERS_PARTIAL})\n"
777 							"			gl_Position = a_input + 0.05 * vec4(fract(value.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
778 							"		else\n"
779 							"			v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
780 							"	}\n"
781 							"}\n", args)));
782 
783 			group->addChild(new BasicInvarianceTest(m_context, "loop_3", "Invariant value set using a loop",
784 				formatGLSL(	"${VERSION}"
785 							"${IN} ${IN_PREC} vec4 a_input;\n"
786 							"${OUT} mediump vec4 v_unrelated;\n"
787 							"invariant gl_Position;\n"
788 							"void main ()\n"
789 							"{\n"
790 							"	${IN_PREC} vec4 value = a_input;\n"
791 							"	gl_Position = vec4(0.0, 0.0, 0.0, 0.0);\n"
792 							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
793 							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
794 							"	{\n"
795 							"		value *= ${LOOP_MULTIPLIER};\n"
796 							"		gl_Position += vec4(value.xyz / ${SUM_LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
797 							"		v_unrelated = gl_Position.xyzx * a_input;\n"
798 							"	}\n"
799 							"}\n", args),
800 				formatGLSL(	"${VERSION}"
801 							"${IN} ${IN_PREC} vec4 a_input;\n"
802 							"${OUT} mediump vec4 v_unrelated;\n"
803 							"invariant gl_Position;\n"
804 							"void main ()\n"
805 							"{\n"
806 							"	${IN_PREC} vec4 value = a_input;\n"
807 							"	gl_Position = vec4(0.0, 0.0, 0.0, 0.0);\n"
808 							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
809 							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
810 							"	{\n"
811 							"		value *= ${LOOP_MULTIPLIER};\n"
812 							"		gl_Position += vec4(value.xyz / ${SUM_LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
813 							"	}\n"
814 							"}\n", args)));
815 
816 			group->addChild(new BasicInvarianceTest(m_context, "loop_4", "Invariant value set using a loop",
817 				formatGLSL(	"${VERSION}"
818 							"${IN} ${IN_PREC} vec4 a_input;\n"
819 							"${OUT} mediump vec4 v_unrelated;\n"
820 							"invariant gl_Position;\n"
821 							"void main ()\n"
822 							"{\n"
823 							"	${IN_PREC} vec4 position = vec4(0.0, 0.0, 0.0, 0.0);\n"
824 							"	${IN_PREC} vec4 value1 = a_input;\n"
825 							"	${IN_PREC} vec4 value2 = a_input;\n"
826 							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
827 							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
828 							"	{\n"
829 							"		value1 *= ${LOOP_MULTIPLIER};\n"
830 							"		v_unrelated = v_unrelated*1.3 + a_input.xyzx * value1.xyxw;\n"
831 							"	}\n"
832 							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
833 							"	{\n"
834 							"		value2 *= ${LOOP_MULTIPLIER};\n"
835 							"		position = position*1.3 + a_input.xyzx * value2.xyxw;\n"
836 							"	}\n"
837 							"	gl_Position = a_input + 0.05 * vec4(fract(position.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
838 							"}\n", args),
839 				formatGLSL(	"${VERSION}"
840 							"${IN} ${IN_PREC} vec4 a_input;\n"
841 							"${OUT} mediump vec4 v_unrelated;\n"
842 							"invariant gl_Position;\n"
843 							"void main ()\n"
844 							"{\n"
845 							"	${IN_PREC} vec4 position = vec4(0.0, 0.0, 0.0, 0.0);\n"
846 							"	${IN_PREC} vec4 value2 = a_input;\n"
847 							"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
848 							"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
849 							"	{\n"
850 							"		value2 *= ${LOOP_MULTIPLIER};\n"
851 							"		position = position*1.3 + a_input.xyzx * value2.xyxw;\n"
852 							"	}\n"
853 							"	gl_Position = a_input + 0.05 * vec4(fract(position.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
854 							"}\n", args)));
855 		}
856 	}
857 }
858 
859 } // Functional
860 } // gles3
861 } // deqp
862