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 char* desc, 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 char * desc,const std::string & vertexShader1,const std::string & vertexShader2,const std::string & fragmentShader)122 InvarianceTest::InvarianceTest(tcu::TestContext& ctx, const char* name, const char* desc, const std::string& vertexShader1, const std::string& vertexShader2, const std::string& fragmentShader)
123 : vkt::TestCase(ctx, name, desc)
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", "Invariance tests"));
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 tcu::TestCaseGroup* const group = new tcu::TestCaseGroup(testCtx, precisionName, "Invariance tests using the given precision.");
420
421 const deUint32 VAR_GROUP_SIZE = 2u;
422 tcu::TestCaseGroup* varGroup[VAR_GROUP_SIZE];
423 varGroup[0] = new tcu::TestCaseGroup(testCtx, "gl_position", "Invariance tests using gl_Position variable");
424 varGroup[1] = new tcu::TestCaseGroup(testCtx, "user_defined", "Invariance tests using user defined variable");
425 FormatArgumentList args[VAR_GROUP_SIZE];
426 for (deUint32 groupNdx = 0u; groupNdx < VAR_GROUP_SIZE; ++groupNdx)
427 {
428 group->addChild(varGroup[groupNdx]);
429 args[groupNdx] = FormatArgumentList()
430 << FormatArgument("VERSION", "#version 450\n")
431 << FormatArgument("IN", "layout(location = 0) in")
432 << FormatArgument("OUT", "layout(location = 0) out")
433 << FormatArgument("IN_PREC", precisionName)
434 << FormatArgument("INVARIANT_DECLARATION", invariantDeclaration[groupNdx])
435 << FormatArgument("INVARIANT_ASSIGN_0", invariantAssignment0[groupNdx])
436 << FormatArgument("INVARIANT_ASSIGN_1", invariantAssignment1[groupNdx])
437 << FormatArgument("FRAG_DECLARATION", fragDeclaration[groupNdx])
438 << FormatArgument("HIGH_VALUE", de::toString(precisions[precNdx].highValue))
439 << FormatArgument("HIGH_VALUE_INV", de::toString(precisions[precNdx].invHighValue))
440 << FormatArgument("MEDIUM_VALUE", de::toString(precisions[precNdx].mediumValue))
441 << FormatArgument("LOW_VALUE", de::toString(precisions[precNdx].lowValue))
442 << FormatArgument("LOW_VALUE_INV", de::toString(precisions[precNdx].invlowValue))
443 << FormatArgument("LOOP_ITERS", de::toString(precisions[precNdx].loopIterations))
444 << FormatArgument("LOOP_ITERS_PARTIAL", de::toString(precisions[precNdx].loopPartialIterations))
445 << FormatArgument("LOOP_NORM_FRACT_EXP", de::toString(precisions[precNdx].loopNormalizationExponent))
446 << FormatArgument("LOOP_NORM_LITERAL", precisions[precNdx].loopNormalizationConstantLiteral)
447 << FormatArgument("LOOP_MULTIPLIER", precisions[precNdx].loopMultiplier)
448 << FormatArgument("SUM_LOOP_NORM_LITERAL", precisions[precNdx].sumLoopNormalizationConstantLiteral);
449 }
450
451 // subexpression cases
452 for (deUint32 groupNdx = 0u; groupNdx < VAR_GROUP_SIZE; ++groupNdx)
453 {
454 // 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
455 // due to the high exponent. In the second shader, the high exponent may be removed during compilation.
456
457 varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "common_subexpression_0", "Shader shares a subexpression with an unrelated variable.",
458 formatGLSL("${VERSION}"
459 "${IN} ${IN_PREC} vec4 a_input;\n"
460 "${OUT} mediump vec4 v_unrelated;\n"
461 "${INVARIANT_DECLARATION}\n"
462 "void main ()\n"
463 "{\n"
464 " 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"
465 " ${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"
466 " ${INVARIANT_ASSIGN_1}\n"
467 "}\n", args[groupNdx]),
468 formatGLSL("${VERSION}"
469 "${IN} ${IN_PREC} vec4 a_input;\n"
470 "${OUT} mediump vec4 v_unrelated;\n"
471 "${INVARIANT_DECLARATION}\n"
472 "void main ()\n"
473 "{\n"
474 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
475 " ${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"
476 " ${INVARIANT_ASSIGN_1}\n"
477 "}\n", args[groupNdx]),
478 formatGLSL(basicFragmentShader, args[groupNdx])));
479
480 // In the first shader, the unrelated variable "d" has mathematically the same expression as "e", but the different
481 // order of calculation might cause different results.
482
483 varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "common_subexpression_1", "Shader shares a subexpression with an unrelated variable.",
484 formatGLSL("${VERSION}"
485 "${IN} ${IN_PREC} vec4 a_input;\n"
486 "${OUT} mediump vec4 v_unrelated;\n"
487 "${INVARIANT_DECLARATION}\n"
488 "void main ()\n"
489 "{\n"
490 " ${IN_PREC} vec4 a = ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy - ${HIGH_VALUE} * a_input.zzxx;\n"
491 " ${IN_PREC} vec4 b = ${HIGH_VALUE} * a_input.zzxx;\n"
492 " ${IN_PREC} vec4 c = b - ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy;\n"
493 " ${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"
494 " ${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"
495 " v_unrelated = a + b + c + d + e;\n"
496 " ${INVARIANT_ASSIGN_0} = a_input + fract(c) + e;\n"
497 " ${INVARIANT_ASSIGN_1}\n"
498 "}\n", args[groupNdx]),
499 formatGLSL("${VERSION}"
500 "${IN} ${IN_PREC} vec4 a_input;\n"
501 "${OUT} mediump vec4 v_unrelated;\n"
502 "${INVARIANT_DECLARATION}\n"
503 "void main ()\n"
504 "{\n"
505 " ${IN_PREC} vec4 b = ${HIGH_VALUE} * a_input.zzxx;\n"
506 " ${IN_PREC} vec4 c = b - ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy;\n"
507 " ${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"
508 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
509 " ${INVARIANT_ASSIGN_0} = a_input + fract(c) + e;\n"
510 " ${INVARIANT_ASSIGN_1}\n"
511 "}\n", args[groupNdx]),
512 formatGLSL(basicFragmentShader, args[groupNdx])));
513
514 // Intermediate values used by an unrelated output variable
515
516 varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "common_subexpression_2", "Shader shares a subexpression with an unrelated variable.",
517 formatGLSL("${VERSION}"
518 "${IN} ${IN_PREC} vec4 a_input;\n"
519 "${OUT} mediump vec4 v_unrelated;\n"
520 "${INVARIANT_DECLARATION}\n"
521 "void main ()\n"
522 "{\n"
523 " ${IN_PREC} vec4 a = ${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy);\n"
524 " ${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"
525 " ${IN_PREC} vec4 c = a * a;\n"
526 " ${IN_PREC} vec4 d = c / ${MEDIUM_VALUE} / ${MEDIUM_VALUE};\n"
527 " v_unrelated = a + b + c + d;\n"
528 " ${INVARIANT_ASSIGN_0} = a_input + d;\n"
529 " ${INVARIANT_ASSIGN_1}\n"
530 "}\n", args[groupNdx]),
531 formatGLSL("${VERSION}"
532 "${IN} ${IN_PREC} vec4 a_input;\n"
533 "${OUT} mediump vec4 v_unrelated;\n"
534 "${INVARIANT_DECLARATION}\n"
535 "void main ()\n"
536 "{\n"
537 " ${IN_PREC} vec4 a = ${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy);\n"
538 " ${IN_PREC} vec4 c = a * a;\n"
539 " ${IN_PREC} vec4 d = c / ${MEDIUM_VALUE} / ${MEDIUM_VALUE};\n"
540 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
541 " ${INVARIANT_ASSIGN_0} = a_input + d;\n"
542 " ${INVARIANT_ASSIGN_1}\n"
543 "}\n", args[groupNdx]),
544 formatGLSL(basicFragmentShader, args[groupNdx])));
545
546 // Invariant value can be calculated using unrelated value
547
548 varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "common_subexpression_3", "Shader shares a subexpression with an unrelated variable.",
549 formatGLSL("${VERSION}"
550 "${IN} ${IN_PREC} vec4 a_input;\n"
551 "${OUT} mediump vec4 v_unrelated;\n"
552 "${INVARIANT_DECLARATION}\n"
553 "void main ()\n"
554 "{\n"
555 " ${IN_PREC} float x = a_input.x * 0.2;\n"
556 " ${IN_PREC} vec4 a = a_input.xxyx * 0.7;\n"
557 " ${IN_PREC} vec4 b = a_input.yxyz * 0.7;\n"
558 " ${IN_PREC} vec4 c = a_input.zxyx * 0.5;\n"
559 " ${IN_PREC} vec4 f = x*a + x*b + x*c;\n"
560 " v_unrelated = f;\n"
561 " ${IN_PREC} vec4 g = x * (a + b + c);\n"
562 " ${INVARIANT_ASSIGN_0} = a_input + g;\n"
563 " ${INVARIANT_ASSIGN_1}\n"
564 "}\n", args[groupNdx]),
565 formatGLSL("${VERSION}"
566 "${IN} ${IN_PREC} vec4 a_input;\n"
567 "${OUT} mediump vec4 v_unrelated;\n"
568 "${INVARIANT_DECLARATION}\n"
569 "void main ()\n"
570 "{\n"
571 " ${IN_PREC} float x = a_input.x * 0.2;\n"
572 " ${IN_PREC} vec4 a = a_input.xxyx * 0.7;\n"
573 " ${IN_PREC} vec4 b = a_input.yxyz * 0.7;\n"
574 " ${IN_PREC} vec4 c = a_input.zxyx * 0.5;\n"
575 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
576 " ${IN_PREC} vec4 g = x * (a + b + c);\n"
577 " ${INVARIANT_ASSIGN_0} = a_input + g;\n"
578 " ${INVARIANT_ASSIGN_1}\n"
579 "}\n", args[groupNdx]),
580 formatGLSL(basicFragmentShader, args[groupNdx])));
581 }
582
583 // shared subexpression of different precision
584 for (deUint32 groupNdx = 0u; groupNdx < VAR_GROUP_SIZE; ++groupNdx)
585 {
586 for (int precisionOther = glu::PRECISION_LOWP; precisionOther != glu::PRECISION_LAST; ++precisionOther)
587 {
588 const char* const unrelatedPrec = glu::getPrecisionName((glu::Precision)precisionOther);
589 const glu::Precision minPrecision = (precisionOther < (int)precision) ? ((glu::Precision)precisionOther) : (precision);
590 const char* const multiplierStr = (minPrecision == glu::PRECISION_LOWP) ? ("0.8, 0.4, -0.2, 0.3") : ("1.0e1, 5.0e2, 2.0e2, 1.0");
591 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)");
592 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)");
593
594 varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, ("subexpression_precision_" + std::string(unrelatedPrec)).c_str(), "Shader shares subexpression of different precision with an unrelated variable.",
595 formatGLSL("${VERSION}"
596 "${IN} ${IN_PREC} vec4 a_input;\n"
597 "${OUT} ${UNRELATED_PREC} vec4 v_unrelated;\n"
598 "${INVARIANT_DECLARATION}\n"
599 "void main ()\n"
600 "{\n"
601 " ${UNRELATED_PREC} vec4 unrelated0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
602 " ${UNRELATED_PREC} vec4 unrelated1 = vec4(${MULTIPLIER}) * unrelated0.xywz + unrelated0;\n"
603 " ${UNRELATED_PREC} vec4 unrelated2 = refract(unrelated1, unrelated0, distance(unrelated0, unrelated1));\n"
604 " v_unrelated = a_input + 0.02 * ${NORMALIZE_UNRELATED};\n"
605 " ${IN_PREC} vec4 used0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
606 " ${IN_PREC} vec4 used1 = vec4(${MULTIPLIER}) * used0.xywz + used0;\n"
607 " ${IN_PREC} vec4 used2 = refract(used1, used0, distance(used0, used1));\n"
608 " ${INVARIANT_ASSIGN_0} = a_input + 0.02 * ${NORMALIZE_USED};\n"
609 " ${INVARIANT_ASSIGN_1}\n"
610 "}\n", FormatArgumentList(args[groupNdx])
611 << FormatArgument("UNRELATED_PREC", unrelatedPrec)
612 << FormatArgument("MULTIPLIER", multiplierStr)
613 << FormatArgument("NORMALIZE_USED", normalizationStrUsed)
614 << FormatArgument("NORMALIZE_UNRELATED", normalizationStrUnrelated)),
615 formatGLSL("${VERSION}"
616 "${IN} ${IN_PREC} vec4 a_input;\n"
617 "${OUT} ${UNRELATED_PREC} vec4 v_unrelated;\n"
618 "${INVARIANT_DECLARATION}\n"
619 "void main ()\n"
620 "{\n"
621 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
622 " ${IN_PREC} vec4 used0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
623 " ${IN_PREC} vec4 used1 = vec4(${MULTIPLIER}) * used0.xywz + used0;\n"
624 " ${IN_PREC} vec4 used2 = refract(used1, used0, distance(used0, used1));\n"
625 " ${INVARIANT_ASSIGN_0} = a_input + 0.02 * ${NORMALIZE_USED};\n"
626 " ${INVARIANT_ASSIGN_1}\n"
627 "}\n", FormatArgumentList(args[groupNdx])
628 << FormatArgument("UNRELATED_PREC", unrelatedPrec)
629 << FormatArgument("MULTIPLIER", multiplierStr)
630 << FormatArgument("NORMALIZE_USED", normalizationStrUsed)
631 << FormatArgument("NORMALIZE_UNRELATED", normalizationStrUnrelated)),
632 formatGLSL("${VERSION}"
633 "precision mediump float;\n"
634 "${IN} ${UNRELATED_PREC} vec4 v_unrelated;\n"
635 "${FRAG_DECLARATION}\n"
636 "layout(binding = 0) uniform ColorUniform\n"
637 "{\n"
638 " vec4 u_color;\n"
639 "} ucolor;\n"
640 "${OUT} vec4 fragColor;\n"
641 "void main ()\n"
642 "{\n"
643 " float blue = dot(v_unrelated, vec4(1.0, 1.0, 1.0, 1.0));\n"
644 " fragColor = vec4(ucolor.u_color.r, ucolor.u_color.g, blue, ucolor.u_color.a);\n"
645 "}\n", FormatArgumentList(args[groupNdx])
646 << FormatArgument("UNRELATED_PREC", unrelatedPrec)
647 << FormatArgument("MULTIPLIER", multiplierStr)
648 << FormatArgument("NORMALIZE_USED", normalizationStrUsed)
649 << FormatArgument("NORMALIZE_UNRELATED", normalizationStrUnrelated))));
650 }
651 }
652
653 // loops
654 for (deUint32 groupNdx = 0u; groupNdx < VAR_GROUP_SIZE; ++groupNdx)
655 {
656 varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "loop_0", "Invariant value set using a loop",
657 formatGLSL("${VERSION}"
658 "${IN} ${IN_PREC} vec4 a_input;\n"
659 "${OUT} highp vec4 v_unrelated;\n"
660 "${INVARIANT_DECLARATION}\n"
661 "void main ()\n"
662 "{\n"
663 " ${IN_PREC} vec4 value = a_input;\n"
664 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
665 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
666 " {\n"
667 " value *= ${LOOP_MULTIPLIER};\n"
668 " v_unrelated += value;\n"
669 " }\n"
670 " ${INVARIANT_ASSIGN_0} = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
671 " ${INVARIANT_ASSIGN_1}\n"
672 "}\n", args[groupNdx]),
673 formatGLSL("${VERSION}"
674 "${IN} ${IN_PREC} vec4 a_input;\n"
675 "${OUT} highp vec4 v_unrelated;\n"
676 "${INVARIANT_DECLARATION}\n"
677 "void main ()\n"
678 "{\n"
679 " ${IN_PREC} vec4 value = a_input;\n"
680 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
681 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
682 " {\n"
683 " value *= ${LOOP_MULTIPLIER};\n"
684 " }\n"
685 " ${INVARIANT_ASSIGN_0} = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
686 " ${INVARIANT_ASSIGN_1}\n"
687 "}\n", args[groupNdx]),
688 formatGLSL("${VERSION}"
689 "precision mediump float;\n"
690 "layout(location=0) in highp vec4 v_unrelated;\n"
691 "${FRAG_DECLARATION}\n"
692 "layout(binding = 0) uniform ColorUniform\n"
693 "{\n"
694 " vec4 u_color;\n"
695 "} ucolor;\n"
696 "layout(location = 0) out vec4 fragColor;\n"
697 "void main ()\n"
698 "{\n"
699 " float blue = dot(v_unrelated, vec4(1.0, 1.0, 1.0, 1.0));\n"
700 " fragColor = vec4(ucolor.u_color.r, ucolor.u_color.g, blue, ucolor.u_color.a);\n"
701 "}\n", args[groupNdx])));
702
703 varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "loop_1", "Invariant value set using a loop",
704 formatGLSL("${VERSION}"
705 "${IN} ${IN_PREC} vec4 a_input;\n"
706 "${OUT} mediump vec4 v_unrelated;\n"
707 "${INVARIANT_DECLARATION}\n"
708 "void main ()\n"
709 "{\n"
710 " ${IN_PREC} vec4 value = a_input;\n"
711 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
712 " {\n"
713 " value *= ${LOOP_MULTIPLIER};\n"
714 " if (i == ${LOOP_ITERS_PARTIAL})\n"
715 " v_unrelated = value;\n"
716 " }\n"
717 " ${INVARIANT_ASSIGN_0} = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
718 " ${INVARIANT_ASSIGN_1}\n"
719 "}\n", args[groupNdx]),
720 formatGLSL("${VERSION}"
721 "${IN} ${IN_PREC} vec4 a_input;\n"
722 "${OUT} mediump vec4 v_unrelated;\n"
723 "${INVARIANT_DECLARATION}\n"
724 "void main ()\n"
725 "{\n"
726 " ${IN_PREC} vec4 value = a_input;\n"
727 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
728 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
729 " {\n"
730 " value *= ${LOOP_MULTIPLIER};\n"
731 " }\n"
732 " ${INVARIANT_ASSIGN_0} = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
733 " ${INVARIANT_ASSIGN_1}\n"
734 "}\n", args[groupNdx]),
735 formatGLSL(basicFragmentShader, args[groupNdx])));
736
737 varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "loop_2", "Invariant value set using a loop",
738 formatGLSL("${VERSION}"
739 "${IN} ${IN_PREC} vec4 a_input;\n"
740 "${OUT} mediump vec4 v_unrelated;\n"
741 "${INVARIANT_DECLARATION}\n"
742 "void main ()\n"
743 "{\n"
744 " ${IN_PREC} vec4 value = a_input;\n"
745 " v_unrelated = vec4(0.0, 0.0, -1.0, 1.0);\n"
746 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
747 " {\n"
748 " value *= ${LOOP_MULTIPLIER};\n"
749 " if (i == ${LOOP_ITERS_PARTIAL})\n"
750 " ${INVARIANT_ASSIGN_0} = a_input + 0.05 * vec4(fract(value.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
751 " else\n"
752 " v_unrelated = value + a_input;\n"
753 " ${INVARIANT_ASSIGN_1}\n"
754 " }\n"
755 "}\n", args[groupNdx]),
756 formatGLSL("${VERSION}"
757 "${IN} ${IN_PREC} vec4 a_input;\n"
758 "${OUT} mediump vec4 v_unrelated;\n"
759 "${INVARIANT_DECLARATION}\n"
760 "void main ()\n"
761 "{\n"
762 " ${IN_PREC} vec4 value = a_input;\n"
763 " v_unrelated = vec4(0.0, 0.0, -1.0, 1.0);\n"
764 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
765 " {\n"
766 " value *= ${LOOP_MULTIPLIER};\n"
767 " if (i == ${LOOP_ITERS_PARTIAL})\n"
768 " ${INVARIANT_ASSIGN_0} = a_input + 0.05 * vec4(fract(value.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
769 " else\n"
770 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
771 " ${INVARIANT_ASSIGN_1}\n"
772 " }\n"
773 "}\n", args[groupNdx]),
774 formatGLSL(basicFragmentShader, args[groupNdx])));
775
776 varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "loop_3", "Invariant value set using a loop",
777 formatGLSL("${VERSION}"
778 "${IN} ${IN_PREC} vec4 a_input;\n"
779 "${OUT} mediump vec4 v_unrelated;\n"
780 "${INVARIANT_DECLARATION}\n"
781 "void main ()\n"
782 "{\n"
783 " ${IN_PREC} vec4 value = a_input;\n"
784 " ${INVARIANT_ASSIGN_0} = vec4(0.0, 0.0, 0.0, 0.0);\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 " ${INVARIANT_ASSIGN_0} += vec4(value.xyz / ${SUM_LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
790 " v_unrelated = ${INVARIANT_ASSIGN_0}.xyzx * a_input;\n"
791 " }\n"
792 " ${INVARIANT_ASSIGN_1}\n"
793 "}\n", args[groupNdx]),
794 formatGLSL("${VERSION}"
795 "${IN} ${IN_PREC} vec4 a_input;\n"
796 "${OUT} mediump vec4 v_unrelated;\n"
797 "${INVARIANT_DECLARATION}\n"
798 "void main ()\n"
799 "{\n"
800 " ${IN_PREC} vec4 value = a_input;\n"
801 " ${INVARIANT_ASSIGN_0} = vec4(0.0, 0.0, 0.0, 0.0);\n"
802 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
803 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
804 " {\n"
805 " value *= ${LOOP_MULTIPLIER};\n"
806 " ${INVARIANT_ASSIGN_0} += vec4(value.xyz / ${SUM_LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
807 " }\n"
808 " ${INVARIANT_ASSIGN_1}\n"
809 "}\n", args[groupNdx]),
810 formatGLSL(basicFragmentShader, args[groupNdx])));
811
812 varGroup[groupNdx]->addChild(new InvarianceTest(testCtx, "loop_4", "Invariant value set using a loop",
813 formatGLSL("${VERSION}"
814 "${IN} ${IN_PREC} vec4 a_input;\n"
815 "${OUT} mediump vec4 v_unrelated;\n"
816 "${INVARIANT_DECLARATION}\n"
817 "void main ()\n"
818 "{\n"
819 " ${IN_PREC} vec4 position = vec4(0.0, 0.0, 0.0, 0.0);\n"
820 " ${IN_PREC} vec4 value1 = a_input;\n"
821 " ${IN_PREC} vec4 value2 = a_input;\n"
822 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
823 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
824 " {\n"
825 " value1 *= ${LOOP_MULTIPLIER};\n"
826 " v_unrelated = v_unrelated*1.3 + a_input.xyzx * value1.xyxw;\n"
827 " }\n"
828 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
829 " {\n"
830 " value2 *= ${LOOP_MULTIPLIER};\n"
831 " position = position*1.3 + a_input.xyzx * value2.xyxw;\n"
832 " }\n"
833 " ${INVARIANT_ASSIGN_0} = a_input + 0.05 * vec4(fract(position.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
834 " ${INVARIANT_ASSIGN_1}\n"
835 "}\n", args[groupNdx]),
836 formatGLSL("${VERSION}"
837 "${IN} ${IN_PREC} vec4 a_input;\n"
838 "${OUT} mediump vec4 v_unrelated;\n"
839 "${INVARIANT_DECLARATION}\n"
840 "void main ()\n"
841 "{\n"
842 " ${IN_PREC} vec4 position = vec4(0.0, 0.0, 0.0, 0.0);\n"
843 " ${IN_PREC} vec4 value2 = a_input;\n"
844 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
845 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
846 " {\n"
847 " value2 *= ${LOOP_MULTIPLIER};\n"
848 " position = position*1.3 + a_input.xyzx * value2.xyxw;\n"
849 " }\n"
850 " ${INVARIANT_ASSIGN_0} = a_input + 0.05 * vec4(fract(position.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
851 " ${INVARIANT_ASSIGN_1}\n"
852 "}\n", args[groupNdx]),
853 formatGLSL(basicFragmentShader, args[groupNdx])));
854 }
855 invarianceGroup->addChild(group);
856 }
857 return invarianceGroup.release();
858 }
859
860 } // namespace sr
861
862 } // namespace vkt
863