1 /*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.0 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2014 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 *//*!
20 * \file
21 * \brief Invariance tests.
22 *//*--------------------------------------------------------------------*/
23
24 #include "es3fShaderInvarianceTests.hpp"
25 #include "deStringUtil.hpp"
26 #include "deRandom.hpp"
27 #include "gluContextInfo.hpp"
28 #include "gluRenderContext.hpp"
29 #include "gluShaderProgram.hpp"
30 #include "gluPixelTransfer.hpp"
31 #include "glwFunctions.hpp"
32 #include "glwEnums.hpp"
33 #include "tcuRenderTarget.hpp"
34 #include "tcuTestLog.hpp"
35 #include "tcuSurface.hpp"
36 #include "tcuTextureUtil.hpp"
37 #include "tcuStringTemplate.hpp"
38
39 namespace deqp
40 {
41 namespace gles3
42 {
43 namespace Functional
44 {
45 namespace
46 {
47
48 class FormatArgumentList;
49
genRandomVector(de::Random & rnd)50 static tcu::Vec4 genRandomVector(de::Random &rnd)
51 {
52 tcu::Vec4 retVal;
53
54 retVal.x() = rnd.getFloat(-1.0f, 1.0f);
55 retVal.y() = rnd.getFloat(-1.0f, 1.0f);
56 retVal.z() = rnd.getFloat(-1.0f, 1.0f);
57 retVal.w() = rnd.getFloat(0.2f, 1.0f);
58
59 return retVal;
60 }
61
62 class FormatArgument
63 {
64 public:
65 FormatArgument(const char *name, const std::string &value);
66
67 private:
68 friend class FormatArgumentList;
69
70 const char *const m_name;
71 const std::string m_value;
72 };
73
FormatArgument(const char * name,const std::string & value)74 FormatArgument::FormatArgument(const char *name, const std::string &value) : m_name(name), m_value(value)
75 {
76 }
77
78 class FormatArgumentList
79 {
80 public:
81 FormatArgumentList(void);
82
83 FormatArgumentList &operator<<(const FormatArgument &);
84 const std::map<std::string, std::string> &getArguments(void) const;
85
86 private:
87 std::map<std::string, std::string> m_formatArguments;
88 };
89
FormatArgumentList(void)90 FormatArgumentList::FormatArgumentList(void)
91 {
92 }
93
operator <<(const FormatArgument & arg)94 FormatArgumentList &FormatArgumentList::operator<<(const FormatArgument &arg)
95 {
96 m_formatArguments[arg.m_name] = arg.m_value;
97 return *this;
98 }
99
getArguments(void) const100 const std::map<std::string, std::string> &FormatArgumentList::getArguments(void) const
101 {
102 return m_formatArguments;
103 }
104
formatGLSL(const char * templateString,const FormatArgumentList & args)105 static std::string formatGLSL(const char *templateString, const FormatArgumentList &args)
106 {
107 const std::map<std::string, std::string> ¶ms = args.getArguments();
108
109 return tcu::StringTemplate(std::string(templateString)).specialize(params);
110 }
111
112 /*--------------------------------------------------------------------*//*!
113 * \brief Vertex shader invariance test
114 *
115 * Test vertex shader invariance by drawing a test pattern two times, each
116 * time with a different shader. Shaders have set identical values to
117 * invariant gl_Position using identical expressions. No fragments from the
118 * first pass using should remain visible.
119 *//*--------------------------------------------------------------------*/
120 class InvarianceTest : public TestCase
121 {
122 public:
123 struct ShaderPair
124 {
125 std::string vertexShaderSource0;
126 std::string fragmentShaderSource0;
127 std::string vertexShaderSource1;
128 std::string fragmentShaderSource1;
129 };
130
131 InvarianceTest(Context &ctx, const char *name, const char *desc);
132 ~InvarianceTest(void);
133
134 void init(void);
135 void deinit(void);
136 IterateResult iterate(void);
137
138 private:
139 virtual ShaderPair genShaders(void) const = DE_NULL;
140 bool checkImage(const tcu::Surface &) const;
141
142 glu::ShaderProgram *m_shader0;
143 glu::ShaderProgram *m_shader1;
144 glw::GLuint m_arrayBuf;
145 int m_verticesInPattern;
146
147 const int m_renderSize;
148 };
149
InvarianceTest(Context & ctx,const char * name,const char * desc)150 InvarianceTest::InvarianceTest(Context &ctx, const char *name, const char *desc)
151 : TestCase(ctx, name, desc)
152 , m_shader0(DE_NULL)
153 , m_shader1(DE_NULL)
154 , m_arrayBuf(0)
155 , m_verticesInPattern(0)
156 , m_renderSize(256)
157 {
158 }
159
~InvarianceTest(void)160 InvarianceTest::~InvarianceTest(void)
161 {
162 deinit();
163 }
164
init(void)165 void InvarianceTest::init(void)
166 {
167 // Invariance tests require drawing to the screen and reading back results.
168 // Tests results are not reliable if the resolution is too small
169 {
170 if (m_context.getRenderTarget().getWidth() < m_renderSize ||
171 m_context.getRenderTarget().getHeight() < m_renderSize)
172 throw tcu::NotSupportedError(std::string("Render target size must be at least ") +
173 de::toString(m_renderSize) + "x" + de::toString(m_renderSize));
174 }
175
176 // Gen shaders
177 {
178 ShaderPair vertexShaders = genShaders();
179
180 m_shader0 =
181 new glu::ShaderProgram(m_context.getRenderContext(),
182 glu::ProgramSources() << glu::VertexSource(vertexShaders.vertexShaderSource0)
183 << glu::FragmentSource(vertexShaders.fragmentShaderSource0));
184 if (!m_shader0->isOk())
185 {
186 m_testCtx.getLog() << *m_shader0;
187 throw tcu::TestError("Test shader compile failed.");
188 }
189
190 m_shader1 =
191 new glu::ShaderProgram(m_context.getRenderContext(),
192 glu::ProgramSources() << glu::VertexSource(vertexShaders.vertexShaderSource1)
193 << glu::FragmentSource(vertexShaders.fragmentShaderSource1));
194 if (!m_shader1->isOk())
195 {
196 m_testCtx.getLog() << *m_shader1;
197 throw tcu::TestError("Test shader compile failed.");
198 }
199
200 // log
201 m_testCtx.getLog() << tcu::TestLog::Message << "Shader 1:" << tcu::TestLog::EndMessage << *m_shader0
202 << tcu::TestLog::Message << "Shader 2:" << tcu::TestLog::EndMessage << *m_shader1;
203 }
204
205 // Gen test pattern
206 {
207 const int numTriangles = 72;
208 de::Random rnd(123);
209 std::vector<tcu::Vec4> triangles(numTriangles * 3 * 2);
210 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
211
212 // Narrow triangle pattern
213 for (int triNdx = 0; triNdx < numTriangles; ++triNdx)
214 {
215 const tcu::Vec4 vertex1 = genRandomVector(rnd);
216 const tcu::Vec4 vertex2 = genRandomVector(rnd);
217 const tcu::Vec4 vertex3 = vertex2 + genRandomVector(rnd) * 0.01f; // generate narrow triangles
218
219 triangles[triNdx * 3 + 0] = vertex1;
220 triangles[triNdx * 3 + 1] = vertex2;
221 triangles[triNdx * 3 + 2] = vertex3;
222 }
223
224 // Normal triangle pattern
225 for (int triNdx = 0; triNdx < numTriangles; ++triNdx)
226 {
227 triangles[(numTriangles + triNdx) * 3 + 0] = genRandomVector(rnd);
228 triangles[(numTriangles + triNdx) * 3 + 1] = genRandomVector(rnd);
229 triangles[(numTriangles + triNdx) * 3 + 2] = genRandomVector(rnd);
230 }
231
232 // upload
233 gl.genBuffers(1, &m_arrayBuf);
234 gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuf);
235 gl.bufferData(GL_ARRAY_BUFFER, (int)(triangles.size() * sizeof(tcu::Vec4)), &triangles[0], GL_STATIC_DRAW);
236 GLU_EXPECT_NO_ERROR(gl.getError(), "buffer gen");
237
238 m_verticesInPattern = numTriangles * 3;
239 }
240 }
241
deinit(void)242 void InvarianceTest::deinit(void)
243 {
244 delete m_shader0;
245 delete m_shader1;
246
247 m_shader0 = DE_NULL;
248 m_shader1 = DE_NULL;
249
250 if (m_arrayBuf)
251 {
252 m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_arrayBuf);
253 m_arrayBuf = 0;
254 }
255 }
256
iterate(void)257 InvarianceTest::IterateResult InvarianceTest::iterate(void)
258 {
259 const glw::Functions &gl = m_context.getRenderContext().getFunctions();
260 const bool depthBufferExists = m_context.getRenderTarget().getDepthBits() != 0;
261 tcu::Surface resultSurface(m_renderSize, m_renderSize);
262 bool error = false;
263
264 // Prepare draw
265 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
266 gl.clear(GL_COLOR_BUFFER_BIT);
267 gl.viewport(0, 0, m_renderSize, m_renderSize);
268 gl.bindBuffer(GL_ARRAY_BUFFER, m_arrayBuf);
269 GLU_EXPECT_NO_ERROR(gl.getError(), "setup draw");
270
271 m_testCtx.getLog() << tcu::TestLog::Message << "Testing position invariance." << tcu::TestLog::EndMessage;
272
273 // Draw position check passes
274 for (int passNdx = 0; passNdx < 2; ++passNdx)
275 {
276 const glu::ShaderProgram &shader = (passNdx == 0) ? (*m_shader0) : (*m_shader1);
277 const glw::GLint positionLoc = gl.getAttribLocation(shader.getProgram(), "a_input");
278 const glw::GLint colorLoc = gl.getUniformLocation(shader.getProgram(), "u_color");
279 const tcu::Vec4 red = tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f);
280 const tcu::Vec4 green = tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f);
281 const tcu::Vec4 color = (passNdx == 0) ? (red) : (green);
282 const char *const colorStr = (passNdx == 0) ? ("red - purple") : ("green");
283
284 m_testCtx.getLog() << tcu::TestLog::Message << "Drawing position test pattern using shader " << (passNdx + 1)
285 << ". Primitive color: " << colorStr << "." << tcu::TestLog::EndMessage;
286
287 gl.useProgram(shader.getProgram());
288 gl.uniform4fv(colorLoc, 1, color.getPtr());
289 gl.enableVertexAttribArray(positionLoc);
290 gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, sizeof(tcu::Vec4), DE_NULL);
291 gl.drawArrays(GL_TRIANGLES, 0, m_verticesInPattern);
292 gl.disableVertexAttribArray(positionLoc);
293 GLU_EXPECT_NO_ERROR(gl.getError(), "draw pass");
294 }
295
296 // Read result
297 glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess());
298
299 // Check there are no red pixels
300 m_testCtx.getLog() << tcu::TestLog::Message
301 << "Verifying output. Expecting only green or background colored pixels."
302 << tcu::TestLog::EndMessage;
303 error |= !checkImage(resultSurface);
304
305 if (!depthBufferExists)
306 {
307 m_testCtx.getLog() << tcu::TestLog::Message << "Depth buffer not available, skipping z-test."
308 << tcu::TestLog::EndMessage;
309 }
310 else
311 {
312 // Test with Z-test
313 gl.clearDepthf(1.0f);
314 gl.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
315 gl.enable(GL_DEPTH_TEST);
316
317 m_testCtx.getLog() << tcu::TestLog::Message
318 << "Testing position invariance with z-test. Enabling GL_DEPTH_TEST."
319 << tcu::TestLog::EndMessage;
320
321 // Draw position check passes
322 for (int passNdx = 0; passNdx < 2; ++passNdx)
323 {
324 const glu::ShaderProgram &shader = (passNdx == 0) ? (*m_shader0) : (*m_shader1);
325 const glw::GLint positionLoc = gl.getAttribLocation(shader.getProgram(), "a_input");
326 const glw::GLint colorLoc = gl.getUniformLocation(shader.getProgram(), "u_color");
327 const tcu::Vec4 red = tcu::Vec4(1.0f, 0.0f, 0.0f, 1.0f);
328 const tcu::Vec4 green = tcu::Vec4(0.0f, 1.0f, 0.0f, 1.0f);
329 const tcu::Vec4 color = (passNdx == 0) ? (red) : (green);
330 const glw::GLenum depthFunc = (passNdx == 0) ? (GL_ALWAYS) : (GL_EQUAL);
331 const char *const depthFuncStr = (passNdx == 0) ? ("GL_ALWAYS") : ("GL_EQUAL");
332 const char *const colorStr = (passNdx == 0) ? ("red - purple") : ("green");
333
334 m_testCtx.getLog() << tcu::TestLog::Message << "Drawing Z-test pattern using shader " << (passNdx + 1)
335 << ". Primitive color: " << colorStr << ". DepthFunc: " << depthFuncStr
336 << tcu::TestLog::EndMessage;
337
338 gl.useProgram(shader.getProgram());
339 gl.uniform4fv(colorLoc, 1, color.getPtr());
340 gl.depthFunc(depthFunc);
341 gl.enableVertexAttribArray(positionLoc);
342 gl.vertexAttribPointer(positionLoc, 4, GL_FLOAT, GL_FALSE, sizeof(tcu::Vec4), DE_NULL);
343 gl.drawArrays(GL_TRIANGLES, m_verticesInPattern,
344 m_verticesInPattern); // !< buffer contains 2 m_verticesInPattern-sized patterns
345 gl.disableVertexAttribArray(positionLoc);
346 GLU_EXPECT_NO_ERROR(gl.getError(), "draw pass");
347 }
348
349 // Read result
350 glu::readPixels(m_context.getRenderContext(), 0, 0, resultSurface.getAccess());
351
352 // Check there are no red pixels
353 m_testCtx.getLog() << tcu::TestLog::Message
354 << "Verifying output. Expecting only green or background colored pixels."
355 << tcu::TestLog::EndMessage;
356 error |= !checkImage(resultSurface);
357 }
358
359 // Report result
360 if (error)
361 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Detected variance between two invariant values");
362 else
363 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
364
365 return STOP;
366 }
367
checkImage(const tcu::Surface & surface) const368 bool InvarianceTest::checkImage(const tcu::Surface &surface) const
369 {
370 const tcu::IVec4 okColor = tcu::IVec4(0, 255, 0, 255);
371 const tcu::RGBA errColor = tcu::RGBA(255, 0, 0, 255);
372 bool error = false;
373 tcu::Surface errorMask(m_renderSize, m_renderSize);
374
375 tcu::clear(errorMask.getAccess(), okColor);
376
377 for (int y = 0; y < m_renderSize; ++y)
378 for (int x = 0; x < m_renderSize; ++x)
379 {
380 const tcu::RGBA col = surface.getPixel(x, y);
381
382 if (col.getRed() != 0)
383 {
384 errorMask.setPixel(x, y, errColor);
385 error = true;
386 }
387 }
388
389 // report error
390 if (error)
391 {
392 m_testCtx.getLog() << tcu::TestLog::Message
393 << "Invalid pixels found (fragments from first render pass found). Variance detected."
394 << tcu::TestLog::EndMessage;
395 m_testCtx.getLog() << tcu::TestLog::ImageSet("Results", "Result verification")
396 << tcu::TestLog::Image("Result", "Result", surface)
397 << tcu::TestLog::Image("Error mask", "Error mask", errorMask) << tcu::TestLog::EndImageSet;
398
399 return false;
400 }
401 else
402 {
403 m_testCtx.getLog() << tcu::TestLog::Message << "No variance found." << tcu::TestLog::EndMessage;
404 m_testCtx.getLog() << tcu::TestLog::ImageSet("Results", "Result verification")
405 << tcu::TestLog::Image("Result", "Result", surface) << tcu::TestLog::EndImageSet;
406
407 return true;
408 }
409 }
410
411 class BasicInvarianceTest : public InvarianceTest
412 {
413 public:
414 BasicInvarianceTest(Context &ctx, const char *name, const char *desc, const std::string &vertexShader1,
415 const std::string &vertexShader2);
416 ShaderPair genShaders(void) const;
417
418 private:
419 const std::string m_vertexShader1;
420 const std::string m_vertexShader2;
421 const std::string m_fragmentShader;
422 static const char *const s_basicFragmentShader;
423 };
424
425 const char *const BasicInvarianceTest::s_basicFragmentShader =
426 "#version 300 es\n"
427 "layout(location = 0) out mediump vec4 fragColor;\n"
428 "uniform mediump vec4 u_color;\n"
429 "in mediump vec4 v_unrelated;\n"
430 "void main ()\n"
431 "{\n"
432 " mediump float blue = dot(v_unrelated, vec4(1.0, 1.0, 1.0, 1.0));\n"
433 " fragColor = vec4(u_color.r, u_color.g, blue, u_color.a);\n"
434 "}\n";
435
BasicInvarianceTest(Context & ctx,const char * name,const char * desc,const std::string & vertexShader1,const std::string & vertexShader2)436 BasicInvarianceTest::BasicInvarianceTest(Context &ctx, const char *name, const char *desc,
437 const std::string &vertexShader1, const std::string &vertexShader2)
438 : InvarianceTest(ctx, name, desc)
439 , m_vertexShader1(vertexShader1)
440 , m_vertexShader2(vertexShader2)
441 , m_fragmentShader(s_basicFragmentShader)
442 {
443 }
444
genShaders(void) const445 BasicInvarianceTest::ShaderPair BasicInvarianceTest::genShaders(void) const
446 {
447 ShaderPair retVal;
448
449 retVal.vertexShaderSource0 = m_vertexShader1;
450 retVal.vertexShaderSource1 = m_vertexShader2;
451 retVal.fragmentShaderSource0 = m_fragmentShader;
452 retVal.fragmentShaderSource1 = m_fragmentShader;
453
454 return retVal;
455 }
456
457 } // namespace
458
ShaderInvarianceTests(Context & context)459 ShaderInvarianceTests::ShaderInvarianceTests(Context &context)
460 : TestCaseGroup(context, "invariance", "Invariance tests")
461 {
462 }
463
~ShaderInvarianceTests(void)464 ShaderInvarianceTests::~ShaderInvarianceTests(void)
465 {
466 }
467
init(void)468 void ShaderInvarianceTests::init(void)
469 {
470 static const struct PrecisionCase
471 {
472 glu::Precision prec;
473 const char *name;
474
475 // set literals in the glsl to be in the representable range
476 const char *highValue; // !< highValue < maxValue
477 const char *invHighValue;
478 const char *mediumValue; // !< mediumValue^2 < maxValue
479 const char *lowValue; // !< lowValue^4 < maxValue
480 const char *invlowValue;
481 int loopIterations;
482 int loopPartialIterations;
483 int loopNormalizationExponent;
484 const char *loopNormalizationConstantLiteral;
485 const char *loopMultiplier;
486 const char *sumLoopNormalizationConstantLiteral;
487 } precisions[] = {
488 {glu::PRECISION_HIGHP, "highp", "1.0e20", "1.0e-20", "1.0e14", "1.0e9", "1.0e-9", 14, 11, 2, "1.0e4", "1.9",
489 "1.0e3"},
490 {glu::PRECISION_MEDIUMP, "mediump", "1.0e4", "1.0e-4", "1.0e2", "1.0e1", "1.0e-1", 13, 11, 2, "1.0e4", "1.9",
491 "1.0e3"},
492 {glu::PRECISION_LOWP, "lowp", "0.9", "1.1", "1.1", "1.15", "0.87", 6, 2, 0, "2.0", "1.1", "1.0"},
493 };
494
495 for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); ++precNdx)
496 {
497 const char *const precisionName = precisions[precNdx].name;
498 const glu::Precision precision = precisions[precNdx].prec;
499 tcu::TestCaseGroup *const group =
500 new tcu::TestCaseGroup(m_testCtx, precisionName, "Invariance tests using the given precision.");
501
502 const FormatArgumentList args =
503 FormatArgumentList() << FormatArgument("VERSION", "#version 300 es\n") << FormatArgument("IN", "in")
504 << FormatArgument("OUT", "out") << FormatArgument("IN_PREC", precisionName)
505 << FormatArgument("HIGH_VALUE", de::toString(precisions[precNdx].highValue))
506 << FormatArgument("HIGH_VALUE_INV", de::toString(precisions[precNdx].invHighValue))
507 << FormatArgument("MEDIUM_VALUE", de::toString(precisions[precNdx].mediumValue))
508 << FormatArgument("LOW_VALUE", de::toString(precisions[precNdx].lowValue))
509 << FormatArgument("LOW_VALUE_INV", de::toString(precisions[precNdx].invlowValue))
510 << FormatArgument("LOOP_ITERS", de::toString(precisions[precNdx].loopIterations))
511 << FormatArgument("LOOP_ITERS_PARTIAL",
512 de::toString(precisions[precNdx].loopPartialIterations))
513 << FormatArgument("LOOP_NORM_FRACT_EXP",
514 de::toString(precisions[precNdx].loopNormalizationExponent))
515 << FormatArgument("LOOP_NORM_LITERAL",
516 precisions[precNdx].loopNormalizationConstantLiteral)
517 << FormatArgument("LOOP_MULTIPLIER", precisions[precNdx].loopMultiplier)
518 << FormatArgument("SUM_LOOP_NORM_LITERAL",
519 precisions[precNdx].sumLoopNormalizationConstantLiteral);
520
521 addChild(group);
522
523 // subexpression cases
524 {
525 // 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
526 // due to the high exponent. In the second shader, the high exponent may be removed during compilation.
527
528 group->addChild(new BasicInvarianceTest(
529 m_context, "common_subexpression_0", "Shader shares a subexpression with an unrelated variable.",
530 formatGLSL(
531 "${VERSION}"
532 "${IN} ${IN_PREC} vec4 a_input;\n"
533 "${OUT} mediump vec4 v_unrelated;\n"
534 "invariant gl_Position;\n"
535 "void main ()\n"
536 "{\n"
537 " v_unrelated = a_input.xzxz + (${HIGH_VALUE}*a_input.x*a_input.xxxx + "
538 "${HIGH_VALUE}*a_input.y*a_input.yyyy) * (1.08 * a_input.zyzy * a_input.xzxz) * ${HIGH_VALUE_INV} "
539 "* (a_input.z * a_input.zzxz - a_input.z * a_input.zzxz) + (${HIGH_VALUE}*a_input.x*a_input.xxxx + "
540 "${HIGH_VALUE}*a_input.y*a_input.yyyy) / ${HIGH_VALUE};\n"
541 " gl_Position = a_input + (${HIGH_VALUE}*a_input.x*a_input.xxxx + "
542 "${HIGH_VALUE}*a_input.y*a_input.yyyy) * ${HIGH_VALUE_INV};\n"
543 "}\n",
544 args),
545 formatGLSL("${VERSION}"
546 "${IN} ${IN_PREC} vec4 a_input;\n"
547 "${OUT} mediump vec4 v_unrelated;\n"
548 "invariant gl_Position;\n"
549 "void main ()\n"
550 "{\n"
551 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
552 " gl_Position = a_input + (${HIGH_VALUE}*a_input.x*a_input.xxxx + "
553 "${HIGH_VALUE}*a_input.y*a_input.yyyy) * ${HIGH_VALUE_INV};\n"
554 "}\n",
555 args)));
556
557 // In the first shader, the unrelated variable "d" has mathematically the same expression as "e", but the different
558 // order of calculation might cause different results.
559
560 group->addChild(new BasicInvarianceTest(
561 m_context, "common_subexpression_1", "Shader shares a subexpression with an unrelated variable.",
562 formatGLSL("${VERSION}"
563 "${IN} ${IN_PREC} vec4 a_input;\n"
564 "${OUT} mediump vec4 v_unrelated;\n"
565 "invariant gl_Position;\n"
566 "void main ()\n"
567 "{\n"
568 " ${IN_PREC} vec4 a = ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy - ${HIGH_VALUE} * "
569 "a_input.zzxx;\n"
570 " ${IN_PREC} vec4 b = ${HIGH_VALUE} * a_input.zzxx;\n"
571 " ${IN_PREC} vec4 c = b - ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy;\n"
572 " ${IN_PREC} vec4 d = (${LOW_VALUE} * a_input.yzxx) * (${LOW_VALUE} * a_input.yzzw) * "
573 "(1.1*${LOW_VALUE_INV} * a_input.yzxx) * (${LOW_VALUE_INV} * a_input.xzzy);\n"
574 " ${IN_PREC} vec4 e = ((${LOW_VALUE} * a_input.yzxx) * (1.1*${LOW_VALUE_INV} * "
575 "a_input.yzxx)) * ((${LOW_VALUE_INV} * a_input.xzzy) * (${LOW_VALUE} * a_input.yzzw));\n"
576 " v_unrelated = a + b + c + d + e;\n"
577 " gl_Position = a_input + fract(c) + e;\n"
578 "}\n",
579 args),
580 formatGLSL("${VERSION}"
581 "${IN} ${IN_PREC} vec4 a_input;\n"
582 "${OUT} mediump vec4 v_unrelated;\n"
583 "invariant gl_Position;\n"
584 "void main ()\n"
585 "{\n"
586 " ${IN_PREC} vec4 b = ${HIGH_VALUE} * a_input.zzxx;\n"
587 " ${IN_PREC} vec4 c = b - ${HIGH_VALUE} * a_input.zzxx + a_input.xzxy;\n"
588 " ${IN_PREC} vec4 e = ((${LOW_VALUE} * a_input.yzxx) * (1.1*${LOW_VALUE_INV} * "
589 "a_input.yzxx)) * ((${LOW_VALUE_INV} * a_input.xzzy) * (${LOW_VALUE} * a_input.yzzw));\n"
590 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
591 " gl_Position = a_input + fract(c) + e;\n"
592 "}\n",
593 args)));
594
595 // Intermediate values used by an unrelated output variable
596
597 group->addChild(new BasicInvarianceTest(
598 m_context, "common_subexpression_2", "Shader shares a subexpression with an unrelated variable.",
599 formatGLSL("${VERSION}"
600 "${IN} ${IN_PREC} vec4 a_input;\n"
601 "${OUT} mediump vec4 v_unrelated;\n"
602 "invariant gl_Position;\n"
603 "void main ()\n"
604 "{\n"
605 " ${IN_PREC} vec4 a = ${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy);\n"
606 " ${IN_PREC} vec4 b = (${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy)) * "
607 "(${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy)) / ${MEDIUM_VALUE} / ${MEDIUM_VALUE};\n"
608 " ${IN_PREC} vec4 c = a * a;\n"
609 " ${IN_PREC} vec4 d = c / ${MEDIUM_VALUE} / ${MEDIUM_VALUE};\n"
610 " v_unrelated = a + b + c + d;\n"
611 " gl_Position = a_input + d;\n"
612 "}\n",
613 args),
614 formatGLSL("${VERSION}"
615 "${IN} ${IN_PREC} vec4 a_input;\n"
616 "${OUT} mediump vec4 v_unrelated;\n"
617 "invariant gl_Position;\n"
618 "void main ()\n"
619 "{\n"
620 " ${IN_PREC} vec4 a = ${MEDIUM_VALUE} * (a_input.xxxx + a_input.yyyy);\n"
621 " ${IN_PREC} vec4 c = a * a;\n"
622 " ${IN_PREC} vec4 d = c / ${MEDIUM_VALUE} / ${MEDIUM_VALUE};\n"
623 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
624 " gl_Position = a_input + d;\n"
625 "}\n",
626 args)));
627
628 // Invariant value can be calculated using unrelated value
629
630 group->addChild(new BasicInvarianceTest(m_context, "common_subexpression_3",
631 "Shader shares a subexpression with an unrelated variable.",
632 formatGLSL("${VERSION}"
633 "${IN} ${IN_PREC} vec4 a_input;\n"
634 "${OUT} mediump vec4 v_unrelated;\n"
635 "invariant gl_Position;\n"
636 "void main ()\n"
637 "{\n"
638 " ${IN_PREC} float x = a_input.x * 0.2;\n"
639 " ${IN_PREC} vec4 a = a_input.xxyx * 0.7;\n"
640 " ${IN_PREC} vec4 b = a_input.yxyz * 0.7;\n"
641 " ${IN_PREC} vec4 c = a_input.zxyx * 0.5;\n"
642 " ${IN_PREC} vec4 f = x*a + x*b + x*c;\n"
643 " v_unrelated = f;\n"
644 " ${IN_PREC} vec4 g = x * (a + b + c);\n"
645 " gl_Position = a_input + g;\n"
646 "}\n",
647 args),
648 formatGLSL("${VERSION}"
649 "${IN} ${IN_PREC} vec4 a_input;\n"
650 "${OUT} mediump vec4 v_unrelated;\n"
651 "invariant gl_Position;\n"
652 "void main ()\n"
653 "{\n"
654 " ${IN_PREC} float x = a_input.x * 0.2;\n"
655 " ${IN_PREC} vec4 a = a_input.xxyx * 0.7;\n"
656 " ${IN_PREC} vec4 b = a_input.yxyz * 0.7;\n"
657 " ${IN_PREC} vec4 c = a_input.zxyx * 0.5;\n"
658 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
659 " ${IN_PREC} vec4 g = x * (a + b + c);\n"
660 " gl_Position = a_input + g;\n"
661 "}\n",
662 args)));
663 }
664
665 // shared subexpression of different precision
666 {
667 for (int precisionOther = glu::PRECISION_LOWP; precisionOther != glu::PRECISION_LAST; ++precisionOther)
668 {
669 const char *const unrelatedPrec = glu::getPrecisionName((glu::Precision)precisionOther);
670 const glu::Precision minPrecision =
671 (precisionOther < (int)precision) ? ((glu::Precision)precisionOther) : (precision);
672 const char *const multiplierStr =
673 (minPrecision == glu::PRECISION_LOWP) ? ("0.8, 0.4, -0.2, 0.3") : ("1.0e1, 5.0e2, 2.0e2, 1.0");
674 const char *const normalizationStrUsed =
675 (minPrecision == glu::PRECISION_LOWP) ?
676 ("vec4(fract(used2).xyz, 0.0)") :
677 ("vec4(fract(used2 / 1.0e2).xyz - fract(used2 / 1.0e3).xyz, 0.0)");
678 const char *const normalizationStrUnrelated =
679 (minPrecision == glu::PRECISION_LOWP) ?
680 ("vec4(fract(unrelated2).xyz, 0.0)") :
681 ("vec4(fract(unrelated2 / 1.0e2).xyz - fract(unrelated2 / 1.0e3).xyz, 0.0)");
682
683 group->addChild(new BasicInvarianceTest(
684 m_context, ("subexpression_precision_" + std::string(unrelatedPrec)).c_str(),
685 "Shader shares subexpression of different precision with an unrelated variable.",
686 formatGLSL(
687 "${VERSION}"
688 "${IN} ${IN_PREC} vec4 a_input;\n"
689 "${OUT} ${UNRELATED_PREC} vec4 v_unrelated;\n"
690 "invariant gl_Position;\n"
691 "void main ()\n"
692 "{\n"
693 " ${UNRELATED_PREC} vec4 unrelated0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
694 " ${UNRELATED_PREC} vec4 unrelated1 = vec4(${MULTIPLIER}) * unrelated0.xywz + unrelated0;\n"
695 " ${UNRELATED_PREC} vec4 unrelated2 = refract(unrelated1, unrelated0, distance(unrelated0, "
696 "unrelated1));\n"
697 " v_unrelated = a_input + 0.02 * ${NORMALIZE_UNRELATED};\n"
698 " ${IN_PREC} vec4 used0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
699 " ${IN_PREC} vec4 used1 = vec4(${MULTIPLIER}) * used0.xywz + used0;\n"
700 " ${IN_PREC} vec4 used2 = refract(used1, used0, distance(used0, used1));\n"
701 " gl_Position = a_input + 0.02 * ${NORMALIZE_USED};\n"
702 "}\n",
703 FormatArgumentList(args) << FormatArgument("UNRELATED_PREC", unrelatedPrec)
704 << FormatArgument("MULTIPLIER", multiplierStr)
705 << FormatArgument("NORMALIZE_USED", normalizationStrUsed)
706 << FormatArgument("NORMALIZE_UNRELATED", normalizationStrUnrelated)),
707 formatGLSL("${VERSION}"
708 "${IN} ${IN_PREC} vec4 a_input;\n"
709 "${OUT} ${UNRELATED_PREC} vec4 v_unrelated;\n"
710 "invariant gl_Position;\n"
711 "void main ()\n"
712 "{\n"
713 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
714 " ${IN_PREC} vec4 used0 = a_input + vec4(0.1, 0.2, 0.3, 0.4);\n"
715 " ${IN_PREC} vec4 used1 = vec4(${MULTIPLIER}) * used0.xywz + used0;\n"
716 " ${IN_PREC} vec4 used2 = refract(used1, used0, distance(used0, used1));\n"
717 " gl_Position = a_input + 0.02 * ${NORMALIZE_USED};\n"
718 "}\n",
719 FormatArgumentList(args)
720 << FormatArgument("UNRELATED_PREC", unrelatedPrec)
721 << FormatArgument("MULTIPLIER", multiplierStr)
722 << FormatArgument("NORMALIZE_USED", normalizationStrUsed)
723 << FormatArgument("NORMALIZE_UNRELATED", normalizationStrUnrelated))));
724 }
725 }
726
727 // loops
728 {
729 group->addChild(new BasicInvarianceTest(
730 m_context, "loop_0", "Invariant value set using a loop",
731 formatGLSL("${VERSION}"
732 "${IN} ${IN_PREC} vec4 a_input;\n"
733 "${OUT} highp vec4 v_unrelated;\n"
734 "invariant gl_Position;\n"
735 "void main ()\n"
736 "{\n"
737 " ${IN_PREC} vec4 value = a_input;\n"
738 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
739 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
740 " {\n"
741 " value *= ${LOOP_MULTIPLIER};\n"
742 " v_unrelated += value;\n"
743 " }\n"
744 " gl_Position = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
745 "}\n",
746 args),
747 formatGLSL("${VERSION}"
748 "${IN} ${IN_PREC} vec4 a_input;\n"
749 "${OUT} highp vec4 v_unrelated;\n"
750 "invariant gl_Position;\n"
751 "void main ()\n"
752 "{\n"
753 " ${IN_PREC} vec4 value = a_input;\n"
754 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
755 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
756 " {\n"
757 " value *= ${LOOP_MULTIPLIER};\n"
758 " }\n"
759 " gl_Position = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
760 "}\n",
761 args)));
762
763 group->addChild(new BasicInvarianceTest(
764 m_context, "loop_1", "Invariant value set using a loop",
765 formatGLSL("${VERSION}"
766 "${IN} ${IN_PREC} vec4 a_input;\n"
767 "${OUT} mediump vec4 v_unrelated;\n"
768 "invariant gl_Position;\n"
769 "void main ()\n"
770 "{\n"
771 " ${IN_PREC} vec4 value = a_input;\n"
772 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
773 " {\n"
774 " value *= ${LOOP_MULTIPLIER};\n"
775 " if (i == ${LOOP_ITERS_PARTIAL})\n"
776 " v_unrelated = value;\n"
777 " }\n"
778 " gl_Position = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
779 "}\n",
780 args),
781 formatGLSL("${VERSION}"
782 "${IN} ${IN_PREC} vec4 a_input;\n"
783 "${OUT} mediump vec4 v_unrelated;\n"
784 "invariant gl_Position;\n"
785 "void main ()\n"
786 "{\n"
787 " ${IN_PREC} vec4 value = a_input;\n"
788 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
789 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
790 " {\n"
791 " value *= ${LOOP_MULTIPLIER};\n"
792 " }\n"
793 " gl_Position = vec4(value.xyz / ${LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
794 "}\n",
795 args)));
796
797 group->addChild(
798 new BasicInvarianceTest(m_context, "loop_2", "Invariant value set using a loop",
799 formatGLSL("${VERSION}"
800 "${IN} ${IN_PREC} vec4 a_input;\n"
801 "${OUT} mediump vec4 v_unrelated;\n"
802 "invariant gl_Position;\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 " gl_Position = 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 " }\n"
816 "}\n",
817 args),
818 formatGLSL("${VERSION}"
819 "${IN} ${IN_PREC} vec4 a_input;\n"
820 "${OUT} mediump vec4 v_unrelated;\n"
821 "invariant gl_Position;\n"
822 "void main ()\n"
823 "{\n"
824 " ${IN_PREC} vec4 value = a_input;\n"
825 " v_unrelated = vec4(0.0, 0.0, -1.0, 1.0);\n"
826 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
827 " {\n"
828 " value *= ${LOOP_MULTIPLIER};\n"
829 " if (i == ${LOOP_ITERS_PARTIAL})\n"
830 " gl_Position = a_input + 0.05 * vec4(fract(value.xyz / "
831 "1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
832 " else\n"
833 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
834 " }\n"
835 "}\n",
836 args)));
837
838 group->addChild(new BasicInvarianceTest(
839 m_context, "loop_3", "Invariant value set using a loop",
840 formatGLSL(
841 "${VERSION}"
842 "${IN} ${IN_PREC} vec4 a_input;\n"
843 "${OUT} mediump vec4 v_unrelated;\n"
844 "invariant gl_Position;\n"
845 "void main ()\n"
846 "{\n"
847 " ${IN_PREC} vec4 value = a_input;\n"
848 " gl_Position = vec4(0.0, 0.0, 0.0, 0.0);\n"
849 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
850 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
851 " {\n"
852 " value *= ${LOOP_MULTIPLIER};\n"
853 " gl_Position += vec4(value.xyz / ${SUM_LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
854 " v_unrelated = gl_Position.xyzx * a_input;\n"
855 " }\n"
856 "}\n",
857 args),
858 formatGLSL(
859 "${VERSION}"
860 "${IN} ${IN_PREC} vec4 a_input;\n"
861 "${OUT} mediump vec4 v_unrelated;\n"
862 "invariant gl_Position;\n"
863 "void main ()\n"
864 "{\n"
865 " ${IN_PREC} vec4 value = a_input;\n"
866 " gl_Position = vec4(0.0, 0.0, 0.0, 0.0);\n"
867 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
868 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
869 " {\n"
870 " value *= ${LOOP_MULTIPLIER};\n"
871 " gl_Position += vec4(value.xyz / ${SUM_LOOP_NORM_LITERAL} + a_input.xyz * 0.1, 1.0);\n"
872 " }\n"
873 "}\n",
874 args)));
875
876 group->addChild(new BasicInvarianceTest(
877 m_context, "loop_4", "Invariant value set using a loop",
878 formatGLSL(
879 "${VERSION}"
880 "${IN} ${IN_PREC} vec4 a_input;\n"
881 "${OUT} mediump vec4 v_unrelated;\n"
882 "invariant gl_Position;\n"
883 "void main ()\n"
884 "{\n"
885 " ${IN_PREC} vec4 position = vec4(0.0, 0.0, 0.0, 0.0);\n"
886 " ${IN_PREC} vec4 value1 = a_input;\n"
887 " ${IN_PREC} vec4 value2 = a_input;\n"
888 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
889 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
890 " {\n"
891 " value1 *= ${LOOP_MULTIPLIER};\n"
892 " v_unrelated = v_unrelated*1.3 + a_input.xyzx * value1.xyxw;\n"
893 " }\n"
894 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
895 " {\n"
896 " value2 *= ${LOOP_MULTIPLIER};\n"
897 " position = position*1.3 + a_input.xyzx * value2.xyxw;\n"
898 " }\n"
899 " gl_Position = a_input + 0.05 * vec4(fract(position.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
900 "}\n",
901 args),
902 formatGLSL(
903 "${VERSION}"
904 "${IN} ${IN_PREC} vec4 a_input;\n"
905 "${OUT} mediump vec4 v_unrelated;\n"
906 "invariant gl_Position;\n"
907 "void main ()\n"
908 "{\n"
909 " ${IN_PREC} vec4 position = vec4(0.0, 0.0, 0.0, 0.0);\n"
910 " ${IN_PREC} vec4 value2 = a_input;\n"
911 " v_unrelated = vec4(0.0, 0.0, 0.0, 0.0);\n"
912 " for (mediump int i = 0; i < ${LOOP_ITERS}; ++i)\n"
913 " {\n"
914 " value2 *= ${LOOP_MULTIPLIER};\n"
915 " position = position*1.3 + a_input.xyzx * value2.xyxw;\n"
916 " }\n"
917 " gl_Position = a_input + 0.05 * vec4(fract(position.xyz / 1.0e${LOOP_NORM_FRACT_EXP}), 1.0);\n"
918 "}\n",
919 args)));
920 }
921 }
922 }
923
924 } // namespace Functional
925 } // namespace gles3
926 } // namespace deqp
927