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> ¶ms = 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