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