• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2019 The Khronos Group Inc.
6  * Copyright (c) 2018 Google Inc.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief Invariant decoration tests.
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktShaderRenderInvarianceTests.hpp"
26 #include "vktShaderRender.hpp"
27 #include "tcuImageCompare.hpp"
28 #include "tcuStringTemplate.hpp"
29 #include "tcuTextureUtil.hpp"
30 #include "tcuTestLog.hpp"
31 #include "vktDrawUtil.hpp"
32 #include "deMath.h"
33 #include "deRandom.hpp"
34 
35 using namespace vk;
36 
37 namespace vkt
38 {
39 using namespace drawutil;
40 
41 namespace sr
42 {
43 
44 namespace
45 {
46 
47 class FormatArgument
48 {
49 public:
50 						FormatArgument (const char* name, const std::string& value);
51 
52 private:
53 	friend class FormatArgumentList;
54 
55 	const char* const	m_name;
56 	const std::string	m_value;
57 };
58 
FormatArgument(const char * name,const std::string & value)59 FormatArgument::FormatArgument (const char* name, const std::string& value)
60 	: m_name	(name)
61 	, m_value	(value)
62 {
63 }
64 
65 class FormatArgumentList
66 {
67 public:
68 												FormatArgumentList	(void);
69 
70 	FormatArgumentList&							operator<<			(const FormatArgument&);
71 	const std::map<std::string, std::string>&	getArguments		(void) const;
72 
73 private:
74 	std::map<std::string, std::string>			m_formatArguments;
75 };
76 
FormatArgumentList(void)77 FormatArgumentList::FormatArgumentList (void)
78 {
79 }
80 
operator <<(const FormatArgument & arg)81 FormatArgumentList&	FormatArgumentList::operator<< (const FormatArgument& arg)
82 {
83 	m_formatArguments[arg.m_name] = arg.m_value;
84 	return *this;
85 }
86 
getArguments(void) const87 const std::map<std::string, std::string>& FormatArgumentList::getArguments (void) const
88 {
89 	return m_formatArguments;
90 }
91 
formatGLSL(const char * templateString,const FormatArgumentList & args)92 static std::string formatGLSL(const char* templateString, const FormatArgumentList& args)
93 {
94 	const std::map<std::string, std::string>& params = args.getArguments();
95 
96 	return tcu::StringTemplate(std::string(templateString)).specialize(params);
97 }
98 
99 class InvarianceTest : public vkt::TestCase
100 {
101 public:
102 								InvarianceTest(tcu::TestContext& ctx, const char* name, const std::string& vertexShader1, const std::string& vertexShader2, const std::string& fragmentShader = "");
103 
104 	void						initPrograms	(SourceCollections& sourceCollections) const override;
105 	vkt::TestInstance*			createInstance	(vkt::Context& context) const override;
106 
107 private:
108 	const std::string			m_vertexShader1;
109 	const std::string			m_vertexShader2;
110 	const std::string			m_fragmentShader;
111 };
112 
113 class InvarianceTestInstance : public vkt::TestInstance
114 {
115 public:
116 						InvarianceTestInstance(vkt::Context &context);
117 	tcu::TestStatus		iterate(void) override;
118 	bool				checkImage(const tcu::ConstPixelBufferAccess& image) const;
119 	const int			m_renderSize = 256;
120 };
121 
InvarianceTest(tcu::TestContext & ctx,const char * name,const std::string & vertexShader1,const std::string & vertexShader2,const std::string & fragmentShader)122  InvarianceTest::InvarianceTest(tcu::TestContext& ctx, const char* name, const std::string& vertexShader1, const std::string& vertexShader2, const std::string& fragmentShader)
123 	: vkt::TestCase(ctx, name)
124 	, m_vertexShader1(vertexShader1)
125 	, m_vertexShader2(vertexShader2)
126 	, m_fragmentShader(fragmentShader)
127 
128 {
129 }
130 
initPrograms(SourceCollections & sourceCollections) const131 void InvarianceTest::initPrograms(SourceCollections& sourceCollections) const
132 {
133 	sourceCollections.glslSources.add("vertex1") << glu::VertexSource(m_vertexShader1);
134 	sourceCollections.glslSources.add("vertex2") << glu::VertexSource(m_vertexShader2);
135 	sourceCollections.glslSources.add("fragment") << glu::FragmentSource(m_fragmentShader);
136 }
137 
createInstance(Context & context) const138 vkt::TestInstance* InvarianceTest::createInstance(Context& context) const
139 {
140 	return new InvarianceTestInstance(context);
141 }
142 
InvarianceTestInstance(vkt::Context & context)143 InvarianceTestInstance::InvarianceTestInstance(vkt::Context &context)
144 	: vkt::TestInstance(context)
145 {
146 }
147 
genRandomVector(de::Random & rnd)148 static tcu::Vec4 genRandomVector(de::Random& rnd)
149 {
150 	tcu::Vec4 retVal;
151 
152 	retVal.x() = rnd.getFloat(-1.0f, 1.0f);
153 	retVal.y() = rnd.getFloat(-1.0f, 1.0f);
154 	retVal.z() = rnd.getFloat(-1.0f, 1.0f);
155 	retVal.w() = rnd.getFloat(0.2f, 1.0f);
156 
157 	return retVal;
158 }
159 
160 struct ColorUniform
161 {
162 	tcu::Vec4 color;
163 };
164 
iterate(void)165 tcu::TestStatus InvarianceTestInstance::iterate(void)
166 {
167 	const VkDevice			device			= m_context.getDevice();
168 	const DeviceInterface&	vk				= m_context.getDeviceInterface();
169 	Allocator&				allocator		= m_context.getDefaultAllocator();
170 	tcu::TestLog&			log				= m_context.getTestContext().getLog();
171 
172 	const int				numTriangles	= 72;
173 	de::Random				rnd				(123);
174 	std::vector<tcu::Vec4>	vertices		(numTriangles * 3 * 2);
175 
176 	{
177 		// Narrow triangle pattern
178 		for (int triNdx = 0; triNdx < numTriangles; ++triNdx)
179 		{
180 			const tcu::Vec4 vertex1 = genRandomVector(rnd);
181 			const tcu::Vec4 vertex2 = genRandomVector(rnd);
182 			const tcu::Vec4 vertex3 = vertex2 + genRandomVector(rnd) * 0.01f; // generate narrow triangles
183 
184 			vertices[triNdx * 3 + 0] = vertex1;
185 			vertices[triNdx * 3 + 1] = vertex2;
186 			vertices[triNdx * 3 + 2] = vertex3;
187 		}
188 
189 		// Normal triangle pattern
190 		for (int triNdx = 0; triNdx < numTriangles; ++triNdx)
191 		{
192 			vertices[(numTriangles + triNdx) * 3 + 0] = genRandomVector(rnd);
193 			vertices[(numTriangles + triNdx) * 3 + 1] = genRandomVector(rnd);
194 			vertices[(numTriangles + triNdx) * 3 + 2] = genRandomVector(rnd);
195 		}
196 	}
197 
198 	Move<VkDescriptorSetLayout>		descriptorSetLayout;
199 	Move<VkDescriptorPool>			descriptorPool;
200 	Move<VkBuffer>					uniformBuffer[2];
201 	de::MovePtr<Allocation>			uniformBufferAllocation[2];
202 	Move<VkDescriptorSet>			descriptorSet[2];
203 	const tcu::Vec4					red		= tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f);
204 	const tcu::Vec4					green	= tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f);
205 
206 	// Descriptors
207 	{
208 		DescriptorSetLayoutBuilder	layoutBuilder;
209 		layoutBuilder.addSingleBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_FRAGMENT_BIT);
210 		descriptorSetLayout = layoutBuilder.build(vk, device);
211 		descriptorPool = DescriptorPoolBuilder()
212 				.addType(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2u)
213 				.build(vk, device, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, 2u);
214 
215 		const VkDescriptorSetAllocateInfo descriptorSetAllocInfo =
216 		{
217 			VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
218 			DE_NULL,
219 			*descriptorPool,
220 			1u,
221 			&descriptorSetLayout.get()
222 		};
223 
224 		const VkBufferCreateInfo uniformBufferCreateInfo =
225 		{
226 			VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,					// VkStructureType		sType
227 			DE_NULL,												// const void*			pNext
228 			(VkBufferCreateFlags)0,									// VkBufferCreateFlags	flags
229 			sizeof(ColorUniform),									// VkDeviceSize			size
230 			VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,						// VkBufferUsageFlags	usage
231 			VK_SHARING_MODE_EXCLUSIVE,								// VkSharingMode		sharingMode
232 			0u,														// deUint32				queueFamilyIndexCount
233 			DE_NULL													// pQueueFamilyIndices
234 		};
235 
236 		for (deUint32 passNdx = 0; passNdx < 2; ++passNdx)
237 		{
238 			uniformBuffer[passNdx]				= createBuffer(vk, device, &uniformBufferCreateInfo, DE_NULL);
239 			uniformBufferAllocation[passNdx]	= allocator.allocate(getBufferMemoryRequirements(vk, device, *uniformBuffer[passNdx]), MemoryRequirement::HostVisible);
240 			VK_CHECK(vk.bindBufferMemory(device, *uniformBuffer[passNdx], uniformBufferAllocation[passNdx]->getMemory(), uniformBufferAllocation[passNdx]->getOffset()));
241 
242 			{
243 				ColorUniform* bufferData	= (ColorUniform*)(uniformBufferAllocation[passNdx]->getHostPtr());
244 				bufferData->color			= (passNdx == 0) ? (red) : (green);
245 				flushAlloc(vk, device, *uniformBufferAllocation[passNdx]);
246 			}
247 			descriptorSet[passNdx] = allocateDescriptorSet(vk, device, &descriptorSetAllocInfo);
248 
249 			const VkDescriptorBufferInfo bufferInfo =
250 			{
251 				*uniformBuffer[passNdx],
252 				0u,
253 				VK_WHOLE_SIZE
254 			};
255 
256 			DescriptorSetUpdateBuilder()
257 				.writeSingle(*descriptorSet[passNdx], DescriptorSetUpdateBuilder::Location::binding(0u), VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, &bufferInfo)
258 				.update(vk, device);
259 		}
260 	}
261 
262 	// pick first available depth buffer format
263 	const std::vector<VkFormat>	depthFormats	{ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D24_UNORM_S8_UINT, VK_FORMAT_X8_D24_UNORM_PACK32, VK_FORMAT_D24_UNORM_S8_UINT };
264 	VkFormat					depthFormat		= VK_FORMAT_UNDEFINED;
265 	const InstanceInterface&	vki				= m_context.getInstanceInterface();
266 	const VkPhysicalDevice		vkPhysDevice	= m_context.getPhysicalDevice();
267 	for (const auto& df : depthFormats)
268 	{
269 		const VkFormatProperties	properties = getPhysicalDeviceFormatProperties(vki, vkPhysDevice, df);
270 		if ((properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) == VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
271 		{
272 			depthFormat = df;
273 			break;
274 		}
275 	}
276 	if(depthFormat == VK_FORMAT_UNDEFINED)
277 		return tcu::TestStatus::fail("There must be at least one depth depth format handled (Vulkan spec 37.3, table 65)");
278 
279 	FrameBufferState				frameBufferState(m_renderSize, m_renderSize);
280 	frameBufferState.depthFormat	= depthFormat;
281 	PipelineState					pipelineState(m_context.getDeviceProperties().limits.subPixelPrecisionBits);
282 	DrawCallData					drawCallData(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, vertices);
283 	VulkanDrawContext				vulkanDrawContext(m_context, frameBufferState);
284 
285 	const std::vector<std::string>				vertexShaderNames	= { "vertex1", "vertex2" };
286 
287 	log << tcu::TestLog::Message << "Testing position invariance." << tcu::TestLog::EndMessage;
288 
289 	for (deUint32 passNdx = 0; passNdx < 2; ++passNdx)
290 	{
291 		std::vector<VulkanShader>			shaders;
292 		shaders.push_back(VulkanShader(VK_SHADER_STAGE_VERTEX_BIT, m_context.getBinaryCollection().get(vertexShaderNames[passNdx])));
293 		shaders.push_back(VulkanShader(VK_SHADER_STAGE_FRAGMENT_BIT, m_context.getBinaryCollection().get("fragment")));
294 		VulkanProgram						vulkanProgram(shaders);
295 		vulkanProgram.descriptorSetLayout	= *descriptorSetLayout;
296 		vulkanProgram.descriptorSet			= *descriptorSet[passNdx];
297 
298 		const char* const			colorStr = (passNdx == 0) ? ("red - purple") : ("green");
299 		log << tcu::TestLog::Message << "Drawing position test pattern using shader " << (passNdx + 1) << ". Primitive color: " << colorStr << "." << tcu::TestLog::EndMessage;
300 
301 		vulkanDrawContext.registerDrawObject(pipelineState, vulkanProgram, drawCallData);
302 	}
303 	vulkanDrawContext.draw();
304 
305 	tcu::ConstPixelBufferAccess	resultImage(
306 		tcu::TextureFormat(vulkanDrawContext.getColorPixels().getFormat()),
307 		vulkanDrawContext.getColorPixels().getWidth(),
308 		vulkanDrawContext.getColorPixels().getHeight(),
309 		1,
310 		vulkanDrawContext.getColorPixels().getDataPtr());
311 
312 	log << tcu::TestLog::Message << "Verifying output. Expecting only green or background colored pixels." << tcu::TestLog::EndMessage;
313 	if( !checkImage(resultImage) )
314 		return tcu::TestStatus::fail("Detected variance between two invariant values");
315 
316 	return tcu::TestStatus::pass("Passed");
317 }
318 
checkImage(const tcu::ConstPixelBufferAccess & image) const319 bool InvarianceTestInstance::checkImage(const tcu::ConstPixelBufferAccess& image) const
320 {
321 	const tcu::IVec4	okColor		(0, 255, 0, 255);
322 	const tcu::RGBA		errColor	(255, 0, 0, 255);
323 	bool				error		= false;
324 	tcu::Surface		errorMask	(image.getWidth(), image.getHeight());
325 
326 	tcu::clear(errorMask.getAccess(), okColor);
327 
328 	for (int y = 0; y < m_renderSize; ++y)
329 		for (int x = 0; x < m_renderSize; ++x)
330 		{
331 			const tcu::IVec4 col = image.getPixelInt(x, y);
332 
333 			if (col.x() != 0)
334 			{
335 				errorMask.setPixel(x, y, errColor);
336 				error = true;
337 			}
338 		}
339 
340 	// report error
341 	if (error)
342 	{
343 		m_context.getTestContext().getLog() << tcu::TestLog::Message << "Invalid pixels found (fragments from first render pass found). Variance detected." << tcu::TestLog::EndMessage;
344 		m_context.getTestContext().getLog()
345 			<< tcu::TestLog::ImageSet("Results", "Result verification")
346 			<< tcu::TestLog::Image("Result", "Result", image)
347 			<< tcu::TestLog::Image("Error mask", "Error mask", errorMask)
348 			<< tcu::TestLog::EndImageSet;
349 
350 		return false;
351 	}
352 	else
353 	{
354 		m_context.getTestContext().getLog() << tcu::TestLog::Message << "No variance found." << tcu::TestLog::EndMessage;
355 		m_context.getTestContext().getLog()
356 			<< tcu::TestLog::ImageSet("Results", "Result verification")
357 			<< tcu::TestLog::Image("Result", "Result", image)
358 			<< tcu::TestLog::EndImageSet;
359 
360 		return true;
361 	}
362 }
363 
364 } // namespace
365 
createShaderInvarianceTests(tcu::TestContext & testCtx)366 tcu::TestCaseGroup* createShaderInvarianceTests (tcu::TestContext& testCtx)
367 {
368 	de::MovePtr<tcu::TestCaseGroup> invarianceGroup(new tcu::TestCaseGroup(testCtx, "invariance"));
369 
370 	static const struct PrecisionCase
371 	{
372 		glu::Precision	prec;
373 		const char*		name;
374 
375 		// set literals in the glsl to be in the representable range
376 		const char*		highValue;		// !< highValue < maxValue
377 		const char*		invHighValue;
378 		const char*		mediumValue;	// !< mediumValue^2 < maxValue
379 		const char*		lowValue;		// !< lowValue^4 < maxValue
380 		const char*		invlowValue;
381 		int				loopIterations;
382 		int				loopPartialIterations;
383 		int				loopNormalizationExponent;
384 		const char*		loopNormalizationConstantLiteral;
385 		const char*		loopMultiplier;
386 		const char*		sumLoopNormalizationConstantLiteral;
387 	} precisions[] =
388 	{
389 		{ 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"	},
390 		{ 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"	},
391 		{ glu::PRECISION_LOWP,		"lowp",		"0.9",		"1.1",		"1.1",		"1.15",		"0.87",		6,	2,	0,	"2.0",		"1.1",	"1.0"	},
392 	};
393 
394 	// gl_Position must always be invariant for comparisons on gl_Position to be valid.
395 	static const std::string invariantDeclaration[]	= { "invariant gl_Position;", "invariant gl_Position;\nlayout(location = 1) invariant highp out vec4 v_value;" };
396 	static const std::string invariantAssignment0[]	= { "gl_Position", "v_value" };
397 	static const std::string invariantAssignment1[]	= { "", "gl_Position = v_value;" };
398 	static const std::string fragDeclaration[]		= { "", "layout(location = 1) highp in vec4 v_value;" };
399 
400 	static const char* basicFragmentShader = "${VERSION}"
401 		"precision mediump float;\n"
402 		"${IN} vec4 v_unrelated;\n"
403 		"${FRAG_DECLARATION}\n"
404 		"layout(binding = 0) uniform ColorUniform\n"
405 		"{\n"
406 		"	vec4 u_color;\n"
407 		"} ucolor;\n"
408 		"layout(location = 0) out vec4 fragColor;\n"
409 		"void main ()\n"
410 		"{\n"
411 		"	float blue = dot(v_unrelated, vec4(1.0, 1.0, 1.0, 1.0));\n"
412 		"	fragColor = vec4(ucolor.u_color.r, ucolor.u_color.g, blue, ucolor.u_color.a);\n"
413 		"}\n";
414 
415 	for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); ++precNdx)
416 	{
417 		const char* const			precisionName = precisions[precNdx].name;
418 		const glu::Precision		precision = precisions[precNdx].prec;
419 		// Invariance tests using the given precision.
420 		tcu::TestCaseGroup* const	group = new tcu::TestCaseGroup(testCtx, precisionName);
421 
422 		const deUint32 VAR_GROUP_SIZE = 2u;
423 		tcu::TestCaseGroup* varGroup[VAR_GROUP_SIZE];
424 		varGroup[0] = new tcu::TestCaseGroup(testCtx, "gl_position", "Invariance tests using gl_Position variable");
425 		varGroup[1] = new tcu::TestCaseGroup(testCtx, "user_defined", "Invariance tests using user defined variable");
426 		FormatArgumentList	args[VAR_GROUP_SIZE];
427 		for (deUint32 groupNdx = 0u; groupNdx < VAR_GROUP_SIZE; ++groupNdx)
428 		{
429 			group->addChild(varGroup[groupNdx]);
430 			args[groupNdx] = FormatArgumentList()
431 				<< FormatArgument("VERSION",				"#version 450\n")
432 				<< FormatArgument("IN",						"layout(location = 0) in")
433 				<< FormatArgument("OUT",					"layout(location = 0) out")
434 				<< FormatArgument("IN_PREC",				precisionName)
435 				<< FormatArgument("INVARIANT_DECLARATION",	invariantDeclaration[groupNdx])
436 				<< FormatArgument("INVARIANT_ASSIGN_0",		invariantAssignment0[groupNdx])
437 				<< FormatArgument("INVARIANT_ASSIGN_1",		invariantAssignment1[groupNdx])
438 				<< FormatArgument("FRAG_DECLARATION",		fragDeclaration[groupNdx])
439 				<< FormatArgument("HIGH_VALUE",				de::toString(precisions[precNdx].highValue))
440 				<< FormatArgument("HIGH_VALUE_INV",			de::toString(precisions[precNdx].invHighValue))
441 				<< FormatArgument("MEDIUM_VALUE",			de::toString(precisions[precNdx].mediumValue))
442 				<< FormatArgument("LOW_VALUE",				de::toString(precisions[precNdx].lowValue))
443 				<< FormatArgument("LOW_VALUE_INV",			de::toString(precisions[precNdx].invlowValue))
444 				<< FormatArgument("LOOP_ITERS",				de::toString(precisions[precNdx].loopIterations))
445 				<< FormatArgument("LOOP_ITERS_PARTIAL",		de::toString(precisions[precNdx].loopPartialIterations))
446 				<< FormatArgument("LOOP_NORM_FRACT_EXP",	de::toString(precisions[precNdx].loopNormalizationExponent))
447 				<< FormatArgument("LOOP_NORM_LITERAL",		precisions[precNdx].loopNormalizationConstantLiteral)
448 				<< FormatArgument("LOOP_MULTIPLIER",		precisions[precNdx].loopMultiplier)
449 				<< FormatArgument("SUM_LOOP_NORM_LITERAL",	precisions[precNdx].sumLoopNormalizationConstantLiteral);
450 		}
451 
452 		// subexpression cases
453 		for (deUint32 groupNdx = 0u; groupNdx < VAR_GROUP_SIZE; ++groupNdx)
454 		{
455 			// 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
456 			// due to the high exponent. In the second shader, the high exponent may be removed during compilation.
457 
458 			// Shader shares a subexpression with an unrelated variable.
459 			varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "common_subexpression_0",
460 				formatGLSL("${VERSION}"
461 					"${IN} ${IN_PREC} vec4 a_input;\n"
462 					"${OUT} mediump vec4 v_unrelated;\n"
463 					"${INVARIANT_DECLARATION}\n"
464 					"void main ()\n"
465 					"{\n"
466 					"	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"
467 					"	${INVARIANT_ASSIGN_0} = a_input + (${HIGH_VALUE}*a_input.x*a_input.xxxx + ${HIGH_VALUE}*a_input.y*a_input.yyyy) * ${HIGH_VALUE_INV};\n"
468 					"	${INVARIANT_ASSIGN_1}\n"
469 					"}\n", args[groupNdx]),
470 				formatGLSL("${VERSION}"
471 					"${IN} ${IN_PREC} vec4 a_input;\n"
472 					"${OUT} mediump vec4 v_unrelated;\n"
473 					"${INVARIANT_DECLARATION}\n"
474 					"void main ()\n"
475 					"{\n"
476 					"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
477 					"	${INVARIANT_ASSIGN_0} = a_input + (${HIGH_VALUE}*a_input.x*a_input.xxxx + ${HIGH_VALUE}*a_input.y*a_input.yyyy) * ${HIGH_VALUE_INV};\n"
478 					"	${INVARIANT_ASSIGN_1}\n"
479 					"}\n", args[groupNdx]),
480 				formatGLSL(basicFragmentShader, args[groupNdx])));
481 
482 			// In the first shader, the unrelated variable "d" has mathematically the same expression as "e", but the different
483 			// order of calculation might cause different results.
484 
485 			// Shader shares a subexpression with an unrelated variable.
486 			varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "common_subexpression_1",
487 				formatGLSL("${VERSION}"
488 					"${IN} ${IN_PREC} vec4 a_input;\n"
489 					"${OUT} mediump vec4 v_unrelated;\n"
490 					"${INVARIANT_DECLARATION}\n"
491 					"void main ()\n"
492 					"{\n"
493 					"	${IN_PREC} vec4 a = ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy - ${HIGH_VALUE} * a_input.zzxx;\n"
494 					"	${IN_PREC} vec4 b = ${HIGH_VALUE} * a_input.zzxx;\n"
495 					"	${IN_PREC} vec4 c = b - ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy;\n"
496 					"	${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"
497 					"	${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"
498 					"	v_unrelated = a + b + c + d + e;\n"
499 					"	${INVARIANT_ASSIGN_0} = a_input + fract(c) + e;\n"
500 					"	${INVARIANT_ASSIGN_1}\n"
501 					"}\n", args[groupNdx]),
502 				formatGLSL("${VERSION}"
503 					"${IN} ${IN_PREC} vec4 a_input;\n"
504 					"${OUT} mediump vec4 v_unrelated;\n"
505 					"${INVARIANT_DECLARATION}\n"
506 					"void main ()\n"
507 					"{\n"
508 					"	${IN_PREC} vec4 b = ${HIGH_VALUE} * a_input.zzxx;\n"
509 					"	${IN_PREC} vec4 c = b - ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy;\n"
510 					"	${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"
511 					"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
512 					"	${INVARIANT_ASSIGN_0} = a_input + fract(c) + e;\n"
513 					"	${INVARIANT_ASSIGN_1}\n"
514 					"}\n", args[groupNdx]),
515 				formatGLSL(basicFragmentShader, args[groupNdx])));
516 
517 			// Intermediate values used by an unrelated output variable
518 
519 			// Shader shares a subexpression with an unrelated variable.
520 			varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "common_subexpression_2",
521 				formatGLSL("${VERSION}"
522 					"${IN} ${IN_PREC} vec4 a_input;\n"
523 					"${OUT} mediump vec4 v_unrelated;\n"
524 					"${INVARIANT_DECLARATION}\n"
525 					"void main ()\n"
526 					"{\n"
527 					"	${IN_PREC} vec4 a = ${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy);\n"
528 					"	${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"
529 					"	${IN_PREC} vec4 c = a * a;\n"
530 					"	${IN_PREC} vec4 d = c / ${MEDIUM_VALUE} / ${MEDIUM_VALUE};\n"
531 					"	v_unrelated = a + b + c + d;\n"
532 					"	${INVARIANT_ASSIGN_0} = a_input + d;\n"
533 					"	${INVARIANT_ASSIGN_1}\n"
534 					"}\n", args[groupNdx]),
535 				formatGLSL("${VERSION}"
536 					"${IN} ${IN_PREC} vec4 a_input;\n"
537 					"${OUT} mediump vec4 v_unrelated;\n"
538 					"${INVARIANT_DECLARATION}\n"
539 					"void main ()\n"
540 					"{\n"
541 					"	${IN_PREC} vec4 a = ${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy);\n"
542 					"	${IN_PREC} vec4 c = a * a;\n"
543 					"	${IN_PREC} vec4 d = c / ${MEDIUM_VALUE} / ${MEDIUM_VALUE};\n"
544 					"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
545 					"	${INVARIANT_ASSIGN_0} = a_input + d;\n"
546 					"	${INVARIANT_ASSIGN_1}\n"
547 					"}\n", args[groupNdx]),
548 				formatGLSL(basicFragmentShader, args[groupNdx])));
549 
550 			// Invariant value can be calculated using unrelated value
551 
552 			// Shader shares a subexpression with an unrelated variable.
553 			varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "common_subexpression_3",
554 				formatGLSL("${VERSION}"
555 					"${IN} ${IN_PREC} vec4 a_input;\n"
556 					"${OUT} mediump vec4 v_unrelated;\n"
557 					"${INVARIANT_DECLARATION}\n"
558 					"void main ()\n"
559 					"{\n"
560 					"	${IN_PREC} float x = a_input.x * 0.2;\n"
561 					"	${IN_PREC} vec4 a = a_input.xxyx * 0.7;\n"
562 					"	${IN_PREC} vec4 b = a_input.yxyz * 0.7;\n"
563 					"	${IN_PREC} vec4 c = a_input.zxyx * 0.5;\n"
564 					"	${IN_PREC} vec4 f = x*a + x*b + x*c;\n"
565 					"	v_unrelated = f;\n"
566 					"	${IN_PREC} vec4 g = x * (a + b + c);\n"
567 					"	${INVARIANT_ASSIGN_0} = a_input + g;\n"
568 					"	${INVARIANT_ASSIGN_1}\n"
569 					"}\n", args[groupNdx]),
570 				formatGLSL("${VERSION}"
571 					"${IN} ${IN_PREC} vec4 a_input;\n"
572 					"${OUT} mediump vec4 v_unrelated;\n"
573 					"${INVARIANT_DECLARATION}\n"
574 					"void main ()\n"
575 					"{\n"
576 					"	${IN_PREC} float x = a_input.x * 0.2;\n"
577 					"	${IN_PREC} vec4 a = a_input.xxyx * 0.7;\n"
578 					"	${IN_PREC} vec4 b = a_input.yxyz * 0.7;\n"
579 					"	${IN_PREC} vec4 c = a_input.zxyx * 0.5;\n"
580 					"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
581 					"	${IN_PREC} vec4 g = x * (a + b + c);\n"
582 					"	${INVARIANT_ASSIGN_0} = a_input + g;\n"
583 					"	${INVARIANT_ASSIGN_1}\n"
584 					"}\n", args[groupNdx]),
585 				formatGLSL(basicFragmentShader, args[groupNdx])));
586 		}
587 
588 		// shared subexpression of different precision
589 		for (deUint32 groupNdx = 0u; groupNdx < VAR_GROUP_SIZE; ++groupNdx)
590 		{
591 			for (int precisionOther = glu::PRECISION_LOWP; precisionOther != glu::PRECISION_LAST; ++precisionOther)
592 			{
593 				const char* const		unrelatedPrec = glu::getPrecisionName((glu::Precision)precisionOther);
594 				const glu::Precision	minPrecision = (precisionOther < (int)precision) ? ((glu::Precision)precisionOther) : (precision);
595 				const char* const		multiplierStr = (minPrecision == glu::PRECISION_LOWP) ? ("0.8, 0.4, -0.2, 0.3") : ("1.0e1, 5.0e2, 2.0e2, 1.0");
596 				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)");
597 				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)");
598 
599 				// Shader shares subexpression of different precision with an unrelated variable.
600 				varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, ("subexpression_precision_" + std::string(unrelatedPrec)).c_str(),
601 					formatGLSL("${VERSION}"
602 						"${IN} ${IN_PREC} vec4 a_input;\n"
603 						"${OUT} ${UNRELATED_PREC} vec4 v_unrelated;\n"
604 						"${INVARIANT_DECLARATION}\n"
605 						"void main ()\n"
606 						"{\n"
607 						"	${UNRELATED_PREC} vec4 unrelated0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
608 						"	${UNRELATED_PREC} vec4 unrelated1 = vec4(${MULTIPLIER}) * unrelated0.xywz + unrelated0;\n"
609 						"	${UNRELATED_PREC} vec4 unrelated2 = refract(unrelated1, unrelated0, distance(unrelated0, unrelated1));\n"
610 						"	v_unrelated = a_input + 0.02 * ${NORMALIZE_UNRELATED};\n"
611 						"	${IN_PREC} vec4 used0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
612 						"	${IN_PREC} vec4 used1 = vec4(${MULTIPLIER}) * used0.xywz + used0;\n"
613 						"	${IN_PREC} vec4 used2 = refract(used1, used0, distance(used0, used1));\n"
614 						"	${INVARIANT_ASSIGN_0} = a_input + 0.02 * ${NORMALIZE_USED};\n"
615 						"	${INVARIANT_ASSIGN_1}\n"
616 						"}\n", FormatArgumentList(args[groupNdx])
617 						<< FormatArgument("UNRELATED_PREC", unrelatedPrec)
618 						<< FormatArgument("MULTIPLIER", multiplierStr)
619 						<< FormatArgument("NORMALIZE_USED", normalizationStrUsed)
620 						<< FormatArgument("NORMALIZE_UNRELATED", normalizationStrUnrelated)),
621 					formatGLSL("${VERSION}"
622 						"${IN} ${IN_PREC} vec4 a_input;\n"
623 						"${OUT} ${UNRELATED_PREC} vec4 v_unrelated;\n"
624 						"${INVARIANT_DECLARATION}\n"
625 						"void main ()\n"
626 						"{\n"
627 						"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
628 						"	${IN_PREC} vec4 used0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
629 						"	${IN_PREC} vec4 used1 = vec4(${MULTIPLIER}) * used0.xywz + used0;\n"
630 						"	${IN_PREC} vec4 used2 = refract(used1, used0, distance(used0, used1));\n"
631 						"	${INVARIANT_ASSIGN_0} = a_input + 0.02 * ${NORMALIZE_USED};\n"
632 						"	${INVARIANT_ASSIGN_1}\n"
633 						"}\n", FormatArgumentList(args[groupNdx])
634 						<< FormatArgument("UNRELATED_PREC", unrelatedPrec)
635 						<< FormatArgument("MULTIPLIER", multiplierStr)
636 						<< FormatArgument("NORMALIZE_USED", normalizationStrUsed)
637 						<< FormatArgument("NORMALIZE_UNRELATED", normalizationStrUnrelated)),
638 					formatGLSL("${VERSION}"
639 						"precision mediump float;\n"
640 						"${IN} ${UNRELATED_PREC} vec4 v_unrelated;\n"
641 						"${FRAG_DECLARATION}\n"
642 						"layout(binding = 0) uniform ColorUniform\n"
643 						"{\n"
644 						"	vec4 u_color;\n"
645 						"} ucolor;\n"
646 						"${OUT} vec4 fragColor;\n"
647 						"void main ()\n"
648 						"{\n"
649 						"	float blue = dot(v_unrelated, vec4(1.0, 1.0, 1.0, 1.0));\n"
650 						"	fragColor = vec4(ucolor.u_color.r, ucolor.u_color.g, blue, ucolor.u_color.a);\n"
651 						"}\n", FormatArgumentList(args[groupNdx])
652 						<< FormatArgument("UNRELATED_PREC", unrelatedPrec)
653 						<< FormatArgument("MULTIPLIER", multiplierStr)
654 						<< FormatArgument("NORMALIZE_USED", normalizationStrUsed)
655 						<< FormatArgument("NORMALIZE_UNRELATED", normalizationStrUnrelated))));
656 			}
657 		}
658 
659 		// loops
660 		for (deUint32 groupNdx = 0u; groupNdx < VAR_GROUP_SIZE; ++groupNdx)
661 		{
662 			// Invariant value set using a loop
663 			varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "loop_0",
664 				formatGLSL("${VERSION}"
665 					"${IN} ${IN_PREC} vec4 a_input;\n"
666 					"${OUT} highp vec4 v_unrelated;\n"
667 					"${INVARIANT_DECLARATION}\n"
668 					"void main ()\n"
669 					"{\n"
670 					"	${IN_PREC} vec4 value = a_input;\n"
671 					"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
672 					"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
673 					"	{\n"
674 					"		value *= ${LOOP_MULTIPLIER};\n"
675 					"		v_unrelated += value;\n"
676 					"	}\n"
677 					"	${INVARIANT_ASSIGN_0} = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
678 					"	${INVARIANT_ASSIGN_1}\n"
679 					"}\n", args[groupNdx]),
680 				formatGLSL("${VERSION}"
681 					"${IN} ${IN_PREC} vec4 a_input;\n"
682 					"${OUT} highp vec4 v_unrelated;\n"
683 					"${INVARIANT_DECLARATION}\n"
684 					"void main ()\n"
685 					"{\n"
686 					"	${IN_PREC} vec4 value = a_input;\n"
687 					"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
688 					"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
689 					"	{\n"
690 					"		value *= ${LOOP_MULTIPLIER};\n"
691 					"	}\n"
692 					"	${INVARIANT_ASSIGN_0} = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
693 					"	${INVARIANT_ASSIGN_1}\n"
694 					"}\n", args[groupNdx]),
695 				formatGLSL("${VERSION}"
696 					"precision mediump float;\n"
697 					"layout(location=0) in highp vec4 v_unrelated;\n"
698 					"${FRAG_DECLARATION}\n"
699 					"layout(binding = 0) uniform ColorUniform\n"
700 					"{\n"
701 					"	vec4 u_color;\n"
702 					"} ucolor;\n"
703 					"layout(location = 0) out vec4 fragColor;\n"
704 					"void main ()\n"
705 					"{\n"
706 					"	float blue = dot(v_unrelated, vec4(1.0, 1.0, 1.0, 1.0));\n"
707 					"	fragColor = vec4(ucolor.u_color.r, ucolor.u_color.g, blue, ucolor.u_color.a);\n"
708 					"}\n", args[groupNdx])));
709 
710 			// Invariant value set using a loop
711 			varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "loop_1",
712 				formatGLSL("${VERSION}"
713 					"${IN} ${IN_PREC} vec4 a_input;\n"
714 					"${OUT} mediump vec4 v_unrelated;\n"
715 					"${INVARIANT_DECLARATION}\n"
716 					"void main ()\n"
717 					"{\n"
718 					"	${IN_PREC} vec4 value = a_input;\n"
719 					"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
720 					"	{\n"
721 					"		value *= ${LOOP_MULTIPLIER};\n"
722 					"		if (i == ${LOOP_ITERS_PARTIAL})\n"
723 					"			v_unrelated = value;\n"
724 					"	}\n"
725 					"	${INVARIANT_ASSIGN_0} = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
726 					"	${INVARIANT_ASSIGN_1}\n"
727 					"}\n", args[groupNdx]),
728 				formatGLSL("${VERSION}"
729 					"${IN} ${IN_PREC} vec4 a_input;\n"
730 					"${OUT} mediump vec4 v_unrelated;\n"
731 					"${INVARIANT_DECLARATION}\n"
732 					"void main ()\n"
733 					"{\n"
734 					"	${IN_PREC} vec4 value = a_input;\n"
735 					"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
736 					"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
737 					"	{\n"
738 					"		value *= ${LOOP_MULTIPLIER};\n"
739 					"	}\n"
740 					"	${INVARIANT_ASSIGN_0} = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
741 					"	${INVARIANT_ASSIGN_1}\n"
742 					"}\n", args[groupNdx]),
743 				formatGLSL(basicFragmentShader, args[groupNdx])));
744 
745 			// Invariant value set using a loop
746 			varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "loop_2",
747 				formatGLSL("${VERSION}"
748 					"${IN} ${IN_PREC} vec4 a_input;\n"
749 					"${OUT} mediump vec4 v_unrelated;\n"
750 					"${INVARIANT_DECLARATION}\n"
751 					"void main ()\n"
752 					"{\n"
753 					"	${IN_PREC} vec4 value = a_input;\n"
754 					"	v_unrelated = vec4(0.0, 0.0, -1.0, 1.0);\n"
755 					"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
756 					"	{\n"
757 					"		value *= ${LOOP_MULTIPLIER};\n"
758 					"		if (i == ${LOOP_ITERS_PARTIAL})\n"
759 					"			${INVARIANT_ASSIGN_0} = a_input + 0.05 * vec4(fract(value.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
760 					"		else\n"
761 					"			v_unrelated = value + a_input;\n"
762 					"	${INVARIANT_ASSIGN_1}\n"
763 					"	}\n"
764 					"}\n", args[groupNdx]),
765 				formatGLSL("${VERSION}"
766 					"${IN} ${IN_PREC} vec4 a_input;\n"
767 					"${OUT} mediump vec4 v_unrelated;\n"
768 					"${INVARIANT_DECLARATION}\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 					"			${INVARIANT_ASSIGN_0} = 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 					"	${INVARIANT_ASSIGN_1}\n"
781 					"	}\n"
782 					"}\n", args[groupNdx]),
783 				formatGLSL(basicFragmentShader, args[groupNdx])));
784 
785 			// Invariant value set using a loop
786 			varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "loop_3",
787 				formatGLSL("${VERSION}"
788 					"${IN} ${IN_PREC} vec4 a_input;\n"
789 					"${OUT} mediump vec4 v_unrelated;\n"
790 					"${INVARIANT_DECLARATION}\n"
791 					"void main ()\n"
792 					"{\n"
793 					"	${IN_PREC} vec4 value = a_input;\n"
794 					"	${INVARIANT_ASSIGN_0} = vec4(0.0, 0.0, 0.0, 0.0);\n"
795 					"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
796 					"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
797 					"	{\n"
798 					"		value *= ${LOOP_MULTIPLIER};\n"
799 					"		${INVARIANT_ASSIGN_0} += vec4(value.xyz / ${SUM_LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
800 					"		v_unrelated = ${INVARIANT_ASSIGN_0}.xyzx * a_input;\n"
801 					"	}\n"
802 					"	${INVARIANT_ASSIGN_1}\n"
803 					"}\n", args[groupNdx]),
804 				formatGLSL("${VERSION}"
805 					"${IN} ${IN_PREC} vec4 a_input;\n"
806 					"${OUT} mediump vec4 v_unrelated;\n"
807 					"${INVARIANT_DECLARATION}\n"
808 					"void main ()\n"
809 					"{\n"
810 					"	${IN_PREC} vec4 value = a_input;\n"
811 					"	${INVARIANT_ASSIGN_0} = vec4(0.0, 0.0, 0.0, 0.0);\n"
812 					"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
813 					"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
814 					"	{\n"
815 					"		value *= ${LOOP_MULTIPLIER};\n"
816 					"		${INVARIANT_ASSIGN_0} += vec4(value.xyz / ${SUM_LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
817 					"	}\n"
818 					"	${INVARIANT_ASSIGN_1}\n"
819 					"}\n", args[groupNdx]),
820 				formatGLSL(basicFragmentShader, args[groupNdx])));
821 
822 			// Invariant value set using a loop
823 			varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "loop_4",
824 				formatGLSL("${VERSION}"
825 					"${IN} ${IN_PREC} vec4 a_input;\n"
826 					"${OUT} mediump vec4 v_unrelated;\n"
827 					"${INVARIANT_DECLARATION}\n"
828 					"void main ()\n"
829 					"{\n"
830 					"	${IN_PREC} vec4 position = vec4(0.0, 0.0, 0.0, 0.0);\n"
831 					"	${IN_PREC} vec4 value1 = a_input;\n"
832 					"	${IN_PREC} vec4 value2 = a_input;\n"
833 					"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
834 					"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
835 					"	{\n"
836 					"		value1 *= ${LOOP_MULTIPLIER};\n"
837 					"		v_unrelated = v_unrelated*1.3 + a_input.xyzx * value1.xyxw;\n"
838 					"	}\n"
839 					"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
840 					"	{\n"
841 					"		value2 *= ${LOOP_MULTIPLIER};\n"
842 					"		position = position*1.3 + a_input.xyzx * value2.xyxw;\n"
843 					"	}\n"
844 					"	${INVARIANT_ASSIGN_0} = a_input + 0.05 * vec4(fract(position.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
845 					"	${INVARIANT_ASSIGN_1}\n"
846 					"}\n", args[groupNdx]),
847 				formatGLSL("${VERSION}"
848 					"${IN} ${IN_PREC} vec4 a_input;\n"
849 					"${OUT} mediump vec4 v_unrelated;\n"
850 					"${INVARIANT_DECLARATION}\n"
851 					"void main ()\n"
852 					"{\n"
853 					"	${IN_PREC} vec4 position = vec4(0.0, 0.0, 0.0, 0.0);\n"
854 					"	${IN_PREC} vec4 value2 = a_input;\n"
855 					"	v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
856 					"	for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
857 					"	{\n"
858 					"		value2 *= ${LOOP_MULTIPLIER};\n"
859 					"		position = position*1.3 + a_input.xyzx * value2.xyxw;\n"
860 					"	}\n"
861 					"	${INVARIANT_ASSIGN_0} = a_input + 0.05 * vec4(fract(position.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
862 					"	${INVARIANT_ASSIGN_1}\n"
863 					"}\n", args[groupNdx]),
864 				formatGLSL(basicFragmentShader, args[groupNdx])));
865 		}
866 		invarianceGroup->addChild(group);
867 	}
868 	return invarianceGroup.release();
869 }
870 
871 } // namespace sr
872 
873 } // namespace vkt
874