xref: /aosp_15_r20/external/deqp/modules/gles3/functional/es3fShaderPrecisionTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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 Shader precision tests.
22  *
23  * \note Floating-point case uses R32UI render target and uses
24  *         floatBitsToUint() in shader to write out floating-point value bits.
25  *         This is done since ES3 core doesn't support FP render targets.
26  *//*--------------------------------------------------------------------*/
27 
28 #include "es3fShaderPrecisionTests.hpp"
29 #include "tcuVector.hpp"
30 #include "tcuTestLog.hpp"
31 #include "tcuVectorUtil.hpp"
32 #include "tcuFloat.hpp"
33 #include "tcuFormatUtil.hpp"
34 #include "gluRenderContext.hpp"
35 #include "gluShaderProgram.hpp"
36 #include "gluShaderUtil.hpp"
37 #include "gluDrawUtil.hpp"
38 #include "deRandom.hpp"
39 #include "deString.h"
40 
41 #include "glwEnums.hpp"
42 #include "glwFunctions.hpp"
43 
44 #include <algorithm>
45 
46 namespace deqp
47 {
48 namespace gles3
49 {
50 namespace Functional
51 {
52 
53 using std::ostringstream;
54 using std::string;
55 using std::vector;
56 using tcu::TestLog;
57 
58 enum
59 {
60     FRAMEBUFFER_WIDTH  = 32,
61     FRAMEBUFFER_HEIGHT = 32
62 };
63 
createFloatPrecisionEvalProgram(const glu::RenderContext & context,glu::Precision precision,const char * evalOp,bool isVertexCase)64 static glu::ShaderProgram *createFloatPrecisionEvalProgram(const glu::RenderContext &context, glu::Precision precision,
65                                                            const char *evalOp, bool isVertexCase)
66 {
67     glu::DataType type      = glu::TYPE_FLOAT;
68     glu::DataType outType   = glu::TYPE_UINT;
69     const char *typeName    = glu::getDataTypeName(type);
70     const char *outTypeName = glu::getDataTypeName(outType);
71     const char *precName    = glu::getPrecisionName(precision);
72     ostringstream vtx;
73     ostringstream frag;
74     ostringstream &op = isVertexCase ? vtx : frag;
75 
76     vtx << "#version 300 es\n"
77         << "in highp vec4 a_position;\n"
78         << "in " << precName << " " << typeName << " a_in0;\n"
79         << "in " << precName << " " << typeName << " a_in1;\n";
80     frag << "#version 300 es\n"
81          << "layout(location = 0) out highp " << outTypeName << " o_out;\n";
82 
83     if (isVertexCase)
84     {
85         vtx << "flat out " << precName << " " << typeName << " v_out;\n";
86         frag << "flat in " << precName << " " << typeName << " v_out;\n";
87     }
88     else
89     {
90         vtx << "flat out " << precName << " " << typeName << " v_in0;\n"
91             << "flat out " << precName << " " << typeName << " v_in1;\n";
92         frag << "flat in " << precName << " " << typeName << " v_in0;\n"
93              << "flat in " << precName << " " << typeName << " v_in1;\n";
94     }
95 
96     vtx << "\nvoid main (void)\n{\n"
97         << "    gl_Position = a_position;\n";
98     frag << "\nvoid main (void)\n{\n";
99 
100     op << "\t" << precName << " " << typeName << " in0 = " << (isVertexCase ? "a_" : "v_") << "in0;\n"
101        << "\t" << precName << " " << typeName << " in1 = " << (isVertexCase ? "a_" : "v_") << "in1;\n";
102 
103     if (!isVertexCase)
104         op << "\t" << precName << " " << typeName << " res;\n";
105 
106     op << "\t" << (isVertexCase ? "v_out" : "res") << " = " << evalOp << ";\n";
107 
108     if (isVertexCase)
109     {
110         frag << "    o_out = floatBitsToUint(v_out);\n";
111     }
112     else
113     {
114         vtx << "    v_in0 = a_in0;\n"
115             << "    v_in1 = a_in1;\n";
116         frag << "    o_out = floatBitsToUint(res);\n";
117     }
118 
119     vtx << "}\n";
120     frag << "}\n";
121 
122     return new glu::ShaderProgram(context, glu::makeVtxFragSources(vtx.str(), frag.str()));
123 }
124 
createIntUintPrecisionEvalProgram(const glu::RenderContext & context,glu::DataType type,glu::Precision precision,const char * evalOp,bool isVertexCase)125 static glu::ShaderProgram *createIntUintPrecisionEvalProgram(const glu::RenderContext &context, glu::DataType type,
126                                                              glu::Precision precision, const char *evalOp,
127                                                              bool isVertexCase)
128 {
129     const char *typeName = glu::getDataTypeName(type);
130     const char *precName = glu::getPrecisionName(precision);
131     ostringstream vtx;
132     ostringstream frag;
133     ostringstream &op = isVertexCase ? vtx : frag;
134 
135     vtx << "#version 300 es\n"
136         << "in highp vec4 a_position;\n"
137         << "in " << precName << " " << typeName << " a_in0;\n"
138         << "in " << precName << " " << typeName << " a_in1;\n";
139     frag << "#version 300 es\n"
140          << "layout(location = 0) out " << precName << " " << typeName << " o_out;\n";
141 
142     if (isVertexCase)
143     {
144         vtx << "flat out " << precName << " " << typeName << " v_out;\n";
145         frag << "flat in " << precName << " " << typeName << " v_out;\n";
146     }
147     else
148     {
149         vtx << "flat out " << precName << " " << typeName << " v_in0;\n"
150             << "flat out " << precName << " " << typeName << " v_in1;\n";
151         frag << "flat in " << precName << " " << typeName << " v_in0;\n"
152              << "flat in " << precName << " " << typeName << " v_in1;\n";
153     }
154 
155     vtx << "\nvoid main (void)\n{\n"
156         << "    gl_Position = a_position;\n";
157     frag << "\nvoid main (void)\n{\n";
158 
159     op << "\t" << precName << " " << typeName << " in0 = " << (isVertexCase ? "a_" : "v_") << "in0;\n"
160        << "\t" << precName << " " << typeName << " in1 = " << (isVertexCase ? "a_" : "v_") << "in1;\n";
161 
162     op << "\t" << (isVertexCase ? "v_" : "o_") << "out = " << evalOp << ";\n";
163 
164     if (isVertexCase)
165     {
166         frag << "    o_out = v_out;\n";
167     }
168     else
169     {
170         vtx << "    v_in0 = a_in0;\n"
171             << "    v_in1 = a_in1;\n";
172     }
173 
174     vtx << "}\n";
175     frag << "}\n";
176 
177     return new glu::ShaderProgram(context, glu::makeVtxFragSources(vtx.str(), frag.str()));
178 }
179 
180 class ShaderFloatPrecisionCase : public TestCase
181 {
182 public:
183     typedef double (*EvalFunc)(double in0, double in1);
184 
185     ShaderFloatPrecisionCase(Context &context, const char *name, const char *desc, const char *op, EvalFunc evalFunc,
186                              glu::Precision precision, const tcu::Vec2 &rangeA, const tcu::Vec2 &rangeB,
187                              bool isVertexCase);
188     ~ShaderFloatPrecisionCase(void);
189 
190     void init(void);
191     void deinit(void);
192     IterateResult iterate(void);
193 
194 protected:
195     bool compare(float in0, float in1, double reference, float result);
196 
197 private:
198     ShaderFloatPrecisionCase(const ShaderFloatPrecisionCase &other);
199     ShaderFloatPrecisionCase &operator=(const ShaderFloatPrecisionCase &other);
200 
201     // Case parameters.
202     std::string m_op;
203     EvalFunc m_evalFunc;
204     glu::Precision m_precision;
205     tcu::Vec2 m_rangeA;
206     tcu::Vec2 m_rangeB;
207     bool m_isVertexCase;
208 
209     int m_numTestsPerIter;
210     int m_numIters;
211     de::Random m_rnd;
212 
213     // Iteration state.
214     glu::ShaderProgram *m_program;
215     uint32_t m_framebuffer;
216     uint32_t m_renderbuffer;
217     int m_iterNdx;
218 };
219 
ShaderFloatPrecisionCase(Context & context,const char * name,const char * desc,const char * op,EvalFunc evalFunc,glu::Precision precision,const tcu::Vec2 & rangeA,const tcu::Vec2 & rangeB,bool isVertexCase)220 ShaderFloatPrecisionCase::ShaderFloatPrecisionCase(Context &context, const char *name, const char *desc, const char *op,
221                                                    EvalFunc evalFunc, glu::Precision precision, const tcu::Vec2 &rangeA,
222                                                    const tcu::Vec2 &rangeB, bool isVertexCase)
223     : TestCase(context, name, desc)
224     , m_op(op)
225     , m_evalFunc(evalFunc)
226     , m_precision(precision)
227     , m_rangeA(rangeA)
228     , m_rangeB(rangeB)
229     , m_isVertexCase(isVertexCase)
230     , m_numTestsPerIter(32)
231     , m_numIters(4)
232     , m_rnd(deStringHash(name))
233     , m_program(DE_NULL)
234     , m_framebuffer(0)
235     , m_renderbuffer(0)
236     , m_iterNdx(0)
237 {
238 }
239 
~ShaderFloatPrecisionCase(void)240 ShaderFloatPrecisionCase::~ShaderFloatPrecisionCase(void)
241 {
242     ShaderFloatPrecisionCase::deinit();
243 }
244 
init(void)245 void ShaderFloatPrecisionCase::init(void)
246 {
247     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
248     TestLog &log             = m_testCtx.getLog();
249 
250     DE_ASSERT(!m_program && !m_framebuffer && !m_renderbuffer);
251 
252     // Create program.
253     m_program =
254         createFloatPrecisionEvalProgram(m_context.getRenderContext(), m_precision, m_op.c_str(), m_isVertexCase);
255     log << *m_program;
256 
257     TCU_CHECK(m_program->isOk());
258 
259     // Create framebuffer.
260     gl.genFramebuffers(1, &m_framebuffer);
261     gl.genRenderbuffers(1, &m_renderbuffer);
262 
263     gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
264     gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
265 
266     gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
267     gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer);
268 
269     GLU_EXPECT_NO_ERROR(gl.getError(), "Post framebuffer setup");
270     TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
271 
272     gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
273 
274     // Initialize test result to pass.
275     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
276     m_iterNdx = 0;
277 }
278 
deinit(void)279 void ShaderFloatPrecisionCase::deinit(void)
280 {
281     delete m_program;
282 
283     if (m_framebuffer)
284         m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_framebuffer);
285 
286     if (m_renderbuffer)
287         m_context.getRenderContext().getFunctions().deleteRenderbuffers(1, &m_renderbuffer);
288 
289     m_program      = DE_NULL;
290     m_framebuffer  = 0;
291     m_renderbuffer = 0;
292 }
293 
compare(float in0,float in1,double reference,float result)294 bool ShaderFloatPrecisionCase::compare(float in0, float in1, double reference, float result)
295 {
296     // Comparison is done using 64-bit reference value to accurately evaluate rounding mode error.
297     // If 32-bit reference value is used, 2 bits of rounding error must be allowed.
298 
299     // For mediump and lowp types the comparison currently allows 3 bits of rounding error:
300     // two bits from conversions and one from actual operation.
301 
302     // \todo [2013-09-30 pyry] Make this more strict: determine if rounding can actually happen.
303 
304     const int mantissaBits = m_precision == glu::PRECISION_HIGHP ? 23 : 10;
305     const int numPrecBits  = 52 - mantissaBits;
306 
307     const int in0Exp      = tcu::Float32(in0).exponent();
308     const int in1Exp      = tcu::Float32(in1).exponent();
309     const int resExp      = tcu::Float32(result).exponent();
310     const int numLostBits = de::max(de::max(in0Exp - resExp, in1Exp - resExp), 0); // Lost due to mantissa shift.
311 
312     const int roundingUlpError = m_precision == glu::PRECISION_HIGHP ? 1 : 3;
313     const int maskBits         = numLostBits + numPrecBits;
314 
315     m_testCtx.getLog() << TestLog::Message << "Assuming " << mantissaBits << " mantissa bits, " << numLostBits
316                        << " bits lost in operation, and " << roundingUlpError << " ULP rounding error."
317                        << TestLog::EndMessage;
318 
319     {
320         const uint64_t refBits         = tcu::Float64(reference).bits();
321         const uint64_t resBits         = tcu::Float64(result).bits();
322         const uint64_t accurateRefBits = maskBits < 64 ? refBits >> (uint64_t)maskBits : 0u;
323         const uint64_t accurateResBits = maskBits < 64 ? resBits >> (uint64_t)maskBits : 0u;
324         const uint64_t ulpDiff         = (uint64_t)de::abs((int64_t)accurateRefBits - (int64_t)accurateResBits);
325 
326         if (ulpDiff > (uint64_t)roundingUlpError)
327         {
328             m_testCtx.getLog() << TestLog::Message
329                                << "ERROR: comparison failed! ULP diff (ignoring lost/undefined bits) = " << ulpDiff
330                                << TestLog::EndMessage;
331             return false;
332         }
333         else
334             return true;
335     }
336 }
337 
iterate(void)338 ShaderFloatPrecisionCase::IterateResult ShaderFloatPrecisionCase::iterate(void)
339 {
340     // Constant data.
341     const float position[]   = {-1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
342                                 1.0f,  -1.0f, 0.0f, 1.0f, 1.0f,  1.0f, 0.0f, 1.0f};
343     const uint16_t indices[] = {0, 1, 2, 2, 1, 3};
344 
345     const int numVertices = 4;
346     float in0Arr[4]       = {0.0f};
347     float in1Arr[4]       = {0.0f};
348 
349     TestLog &log             = m_testCtx.getLog();
350     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
351     vector<glu::VertexArrayBinding> vertexArrays;
352 
353     // Image read from GL.
354     std::vector<float> pixels(FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT * 4);
355 
356     // \todo [2012-05-03 pyry] Could be cached.
357     uint32_t prog = m_program->getProgram();
358 
359     gl.useProgram(prog);
360     gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
361 
362     vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0]));
363     vertexArrays.push_back(glu::va::Float("a_in0", 1, numVertices, 0, &in0Arr[0]));
364     vertexArrays.push_back(glu::va::Float("a_in1", 1, numVertices, 0, &in1Arr[0]));
365 
366     GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
367 
368     // Compute values and reference.
369     for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++)
370     {
371         const float in0   = m_rnd.getFloat(m_rangeA.x(), m_rangeA.y());
372         const float in1   = m_rnd.getFloat(m_rangeB.x(), m_rangeB.y());
373         const double refD = m_evalFunc((double)in0, (double)in1);
374         const float refF  = tcu::Float64(refD).asFloat(); // Uses RTE rounding mode.
375 
376         log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": "
377             << "in0 = " << in0 << " / " << tcu::toHex(tcu::Float32(in0).bits()) << ", in1 = " << in1 << " / "
378             << tcu::toHex(tcu::Float32(in1).bits()) << TestLog::EndMessage << TestLog::Message
379             << "  reference = " << refF << " / " << tcu::toHex(tcu::Float32(refF).bits()) << TestLog::EndMessage;
380 
381         std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0);
382         std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1);
383 
384         glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0],
385                   glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
386         gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
387         GLU_EXPECT_NO_ERROR(gl.getError(), "After render");
388 
389         log << TestLog::Message << "  result = " << pixels[0] << " / " << tcu::toHex(tcu::Float32(pixels[0]).bits())
390             << TestLog::EndMessage;
391 
392         // Verify results
393         {
394             const bool firstPixelOk = compare(in0, in1, refD, pixels[0]);
395 
396             if (firstPixelOk)
397             {
398                 // Check that rest of pixels match to first one.
399                 const uint32_t firstPixelBits = tcu::Float32(pixels[0]).bits();
400                 bool allPixelsOk              = true;
401 
402                 for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++)
403                 {
404                     for (int x = 0; x < FRAMEBUFFER_WIDTH; x++)
405                     {
406                         const uint32_t pixelBits = tcu::Float32(pixels[(y * FRAMEBUFFER_WIDTH + x) * 4]).bits();
407 
408                         if (pixelBits != firstPixelBits)
409                         {
410                             log << TestLog::Message << "ERROR: Inconsistent results, got " << tcu::toHex(pixelBits)
411                                 << " at (" << x << ", " << y << ")" << TestLog::EndMessage;
412                             allPixelsOk = false;
413                         }
414                     }
415 
416                     if (!allPixelsOk)
417                         break;
418                 }
419 
420                 if (!allPixelsOk)
421                     m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Inconsistent values in framebuffer");
422             }
423             else
424                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Result comparison failed");
425         }
426 
427         if (m_testCtx.getTestResult() != QP_TEST_RESULT_PASS)
428             break;
429     }
430 
431     gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
432     GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration");
433 
434     m_iterNdx += 1;
435     return (m_iterNdx < m_numIters && m_testCtx.getTestResult() == QP_TEST_RESULT_PASS) ? CONTINUE : STOP;
436 }
437 
438 class ShaderIntPrecisionCase : public TestCase
439 {
440 public:
441     typedef int64_t (*EvalFunc)(int64_t a, int64_t b);
442 
443     ShaderIntPrecisionCase(Context &context, const char *name, const char *desc, const char *op, EvalFunc evalFunc,
444                            glu::Precision precision, int bits, const tcu::IVec2 &rangeA, const tcu::IVec2 &rangeB,
445                            bool isVertexCase);
446     ~ShaderIntPrecisionCase(void);
447 
448     void init(void);
449     void deinit(void);
450     IterateResult iterate(void);
451 
452 private:
453     ShaderIntPrecisionCase(const ShaderIntPrecisionCase &other);
454     ShaderIntPrecisionCase &operator=(const ShaderIntPrecisionCase &other);
455 
456     // Case parameters.
457     std::string m_op;
458     EvalFunc m_evalFunc;
459     glu::Precision m_precision;
460     int m_bits;
461     tcu::IVec2 m_rangeA;
462     tcu::IVec2 m_rangeB;
463     bool m_isVertexCase;
464 
465     int m_numTestsPerIter;
466     int m_numIters;
467     de::Random m_rnd;
468 
469     // Iteration state.
470     glu::ShaderProgram *m_program;
471     uint32_t m_framebuffer;
472     uint32_t m_renderbuffer;
473     int m_iterNdx;
474 };
475 
ShaderIntPrecisionCase(Context & context,const char * name,const char * desc,const char * op,EvalFunc evalFunc,glu::Precision precision,int bits,const tcu::IVec2 & rangeA,const tcu::IVec2 & rangeB,bool isVertexCase)476 ShaderIntPrecisionCase::ShaderIntPrecisionCase(Context &context, const char *name, const char *desc, const char *op,
477                                                EvalFunc evalFunc, glu::Precision precision, int bits,
478                                                const tcu::IVec2 &rangeA, const tcu::IVec2 &rangeB, bool isVertexCase)
479     : TestCase(context, name, desc)
480     , m_op(op)
481     , m_evalFunc(evalFunc)
482     , m_precision(precision)
483     , m_bits(bits)
484     , m_rangeA(rangeA)
485     , m_rangeB(rangeB)
486     , m_isVertexCase(isVertexCase)
487     , m_numTestsPerIter(32)
488     , m_numIters(4)
489     , m_rnd(deStringHash(name))
490     , m_program(DE_NULL)
491     , m_framebuffer(0)
492     , m_renderbuffer(0)
493     , m_iterNdx(0)
494 {
495 }
496 
~ShaderIntPrecisionCase(void)497 ShaderIntPrecisionCase::~ShaderIntPrecisionCase(void)
498 {
499     ShaderIntPrecisionCase::deinit();
500 }
501 
init(void)502 void ShaderIntPrecisionCase::init(void)
503 {
504     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
505     TestLog &log             = m_testCtx.getLog();
506 
507     DE_ASSERT(!m_program && !m_framebuffer && !m_renderbuffer);
508 
509     // Create program.
510     m_program = createIntUintPrecisionEvalProgram(m_context.getRenderContext(), glu::TYPE_INT, m_precision,
511                                                   m_op.c_str(), m_isVertexCase);
512     log << *m_program;
513 
514     TCU_CHECK(m_program->isOk());
515 
516     // Create framebuffer.
517     gl.genFramebuffers(1, &m_framebuffer);
518     gl.genRenderbuffers(1, &m_renderbuffer);
519 
520     gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
521     gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32I, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
522 
523     gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
524     gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer);
525 
526     GLU_EXPECT_NO_ERROR(gl.getError(), "Post framebuffer setup");
527     TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
528 
529     gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
530 
531     // Initialize test result to pass.
532     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
533     m_iterNdx = 0;
534 
535     log << TestLog::Message << "Number of accurate bits assumed = " << m_bits << TestLog::EndMessage;
536 }
537 
deinit(void)538 void ShaderIntPrecisionCase::deinit(void)
539 {
540     delete m_program;
541 
542     if (m_framebuffer)
543         m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_framebuffer);
544 
545     if (m_renderbuffer)
546         m_context.getRenderContext().getFunctions().deleteRenderbuffers(1, &m_renderbuffer);
547 
548     m_program      = DE_NULL;
549     m_framebuffer  = 0;
550     m_renderbuffer = 0;
551 }
552 
iterate(void)553 ShaderIntPrecisionCase::IterateResult ShaderIntPrecisionCase::iterate(void)
554 {
555     // Constant data.
556     const float position[]   = {-1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
557                                 1.0f,  -1.0f, 0.0f, 1.0f, 1.0f,  1.0f, 0.0f, 1.0f};
558     const uint16_t indices[] = {0, 1, 2, 2, 1, 3};
559 
560     const int numVertices = 4;
561     int in0Arr[4]         = {0};
562     int in1Arr[4]         = {0};
563 
564     TestLog &log             = m_testCtx.getLog();
565     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
566     uint32_t mask            = m_bits == 32 ? 0xffffffffu : ((1u << m_bits) - 1);
567     vector<int> pixels(FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT * 4);
568     vector<glu::VertexArrayBinding> vertexArrays;
569 
570     uint32_t prog = m_program->getProgram();
571 
572     // \todo [2012-05-03 pyry] A bit hacky. getInt() should work fine with ranges like this.
573     bool isMaxRangeA = m_rangeA.x() == (int)0x80000000 && m_rangeA.y() == (int)0x7fffffff;
574     bool isMaxRangeB = m_rangeB.x() == (int)0x80000000 && m_rangeB.y() == (int)0x7fffffff;
575 
576     gl.useProgram(prog);
577     gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
578 
579     vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0]));
580     vertexArrays.push_back(glu::va::Int32("a_in0", 1, numVertices, 0, &in0Arr[0]));
581     vertexArrays.push_back(glu::va::Int32("a_in1", 1, numVertices, 0, &in1Arr[0]));
582 
583     GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
584 
585     // Compute values and reference.
586     for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++)
587     {
588         int in0 = deSignExtendTo32(
589             ((isMaxRangeA ? (int)m_rnd.getUint32() : m_rnd.getInt(m_rangeA.x(), m_rangeA.y())) & mask), m_bits);
590         int in1 = deSignExtendTo32(
591             ((isMaxRangeB ? (int)m_rnd.getUint32() : m_rnd.getInt(m_rangeB.x(), m_rangeB.y())) & mask), m_bits);
592         int refMasked = static_cast<int>(m_evalFunc(in0, in1) & static_cast<uint64_t>(mask));
593         int refOut    = deSignExtendTo32(refMasked, m_bits);
594 
595         log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": "
596             << "in0 = " << in0 << ", in1 = " << in1 << ", ref out = " << refOut << " / " << tcu::toHex(refMasked)
597             << TestLog::EndMessage;
598 
599         std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0);
600         std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1);
601 
602         glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0],
603                   glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
604         gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_INT, &pixels[0]);
605         GLU_EXPECT_NO_ERROR(gl.getError(), "After render");
606 
607         // Compare pixels.
608         for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++)
609         {
610             for (int x = 0; x < FRAMEBUFFER_WIDTH; x++)
611             {
612                 int cmpOut    = pixels[(y * FRAMEBUFFER_WIDTH + x) * 4];
613                 int cmpMasked = cmpOut & mask;
614 
615                 if (cmpMasked != refMasked)
616                 {
617                     log << TestLog::Message << "Comparison failed (at " << x << ", " << y << "): "
618                         << "got " << cmpOut << " / " << tcu::toHex(cmpOut) << TestLog::EndMessage;
619                     m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
620                     return STOP;
621                 }
622             }
623         }
624     }
625 
626     gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
627     GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration");
628 
629     m_iterNdx += 1;
630     return (m_iterNdx < m_numIters) ? CONTINUE : STOP;
631 }
632 
633 class ShaderUintPrecisionCase : public TestCase
634 {
635 public:
636     typedef uint32_t (*EvalFunc)(uint32_t a, uint32_t b);
637 
638     ShaderUintPrecisionCase(Context &context, const char *name, const char *desc, const char *op, EvalFunc evalFunc,
639                             glu::Precision precision, int bits, const tcu::UVec2 &rangeA, const tcu::UVec2 &rangeB,
640                             bool isVertexCase);
641     ~ShaderUintPrecisionCase(void);
642 
643     void init(void);
644     void deinit(void);
645     IterateResult iterate(void);
646 
647 private:
648     ShaderUintPrecisionCase(const ShaderUintPrecisionCase &other);
649     ShaderUintPrecisionCase &operator=(const ShaderUintPrecisionCase &other);
650 
651     // Case parameters.
652     std::string m_op;
653     EvalFunc m_evalFunc;
654     glu::Precision m_precision;
655     int m_bits;
656     tcu::UVec2 m_rangeA;
657     tcu::UVec2 m_rangeB;
658     bool m_isVertexCase;
659 
660     int m_numTestsPerIter;
661     int m_numIters;
662     de::Random m_rnd;
663 
664     // Iteration state.
665     glu::ShaderProgram *m_program;
666     uint32_t m_framebuffer;
667     uint32_t m_renderbuffer;
668     int m_iterNdx;
669 };
670 
ShaderUintPrecisionCase(Context & context,const char * name,const char * desc,const char * op,EvalFunc evalFunc,glu::Precision precision,int bits,const tcu::UVec2 & rangeA,const tcu::UVec2 & rangeB,bool isVertexCase)671 ShaderUintPrecisionCase::ShaderUintPrecisionCase(Context &context, const char *name, const char *desc, const char *op,
672                                                  EvalFunc evalFunc, glu::Precision precision, int bits,
673                                                  const tcu::UVec2 &rangeA, const tcu::UVec2 &rangeB, bool isVertexCase)
674     : TestCase(context, name, desc)
675     , m_op(op)
676     , m_evalFunc(evalFunc)
677     , m_precision(precision)
678     , m_bits(bits)
679     , m_rangeA(rangeA)
680     , m_rangeB(rangeB)
681     , m_isVertexCase(isVertexCase)
682     , m_numTestsPerIter(32)
683     , m_numIters(4)
684     , m_rnd(deStringHash(name))
685     , m_program(DE_NULL)
686     , m_framebuffer(0)
687     , m_renderbuffer(0)
688     , m_iterNdx(0)
689 {
690 }
691 
~ShaderUintPrecisionCase(void)692 ShaderUintPrecisionCase::~ShaderUintPrecisionCase(void)
693 {
694     ShaderUintPrecisionCase::deinit();
695 }
696 
init(void)697 void ShaderUintPrecisionCase::init(void)
698 {
699     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
700     TestLog &log             = m_testCtx.getLog();
701 
702     DE_ASSERT(!m_program && !m_framebuffer && !m_renderbuffer);
703 
704     // Create program.
705     m_program = createIntUintPrecisionEvalProgram(m_context.getRenderContext(), glu::TYPE_UINT, m_precision,
706                                                   m_op.c_str(), m_isVertexCase);
707     log << *m_program;
708 
709     TCU_CHECK(m_program->isOk());
710 
711     // Create framebuffer.
712     gl.genFramebuffers(1, &m_framebuffer);
713     gl.genRenderbuffers(1, &m_renderbuffer);
714 
715     gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer);
716     gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT);
717 
718     gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
719     gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer);
720 
721     GLU_EXPECT_NO_ERROR(gl.getError(), "Post framebuffer setup");
722     TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
723 
724     gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer());
725 
726     // Initialize test result to pass.
727     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
728     m_iterNdx = 0;
729 
730     log << TestLog::Message << "Number of accurate bits assumed = " << m_bits << TestLog::EndMessage;
731 }
732 
deinit(void)733 void ShaderUintPrecisionCase::deinit(void)
734 {
735     delete m_program;
736 
737     if (m_framebuffer)
738         m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_framebuffer);
739 
740     if (m_renderbuffer)
741         m_context.getRenderContext().getFunctions().deleteRenderbuffers(1, &m_renderbuffer);
742 
743     m_program      = DE_NULL;
744     m_framebuffer  = 0;
745     m_renderbuffer = 0;
746 }
747 
iterate(void)748 ShaderUintPrecisionCase::IterateResult ShaderUintPrecisionCase::iterate(void)
749 {
750     // Constant data.
751     const float position[]   = {-1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
752                                 1.0f,  -1.0f, 0.0f, 1.0f, 1.0f,  1.0f, 0.0f, 1.0f};
753     const uint16_t indices[] = {0, 1, 2, 2, 1, 3};
754 
755     const int numVertices = 4;
756     uint32_t in0Arr[4]    = {0};
757     uint32_t in1Arr[4]    = {0};
758 
759     TestLog &log             = m_testCtx.getLog();
760     const glw::Functions &gl = m_context.getRenderContext().getFunctions();
761     uint32_t mask            = m_bits == 32 ? 0xffffffffu : ((1u << m_bits) - 1);
762     vector<uint32_t> pixels(FRAMEBUFFER_WIDTH * FRAMEBUFFER_HEIGHT * 4);
763     vector<glu::VertexArrayBinding> vertexArrays;
764 
765     uint32_t prog = m_program->getProgram();
766 
767     // \todo [2012-05-03 pyry] A bit hacky.
768     bool isMaxRangeA = m_rangeA.x() == 0 && m_rangeA.y() == 0xffffffff;
769     bool isMaxRangeB = m_rangeB.x() == 0 && m_rangeB.y() == 0xffffffff;
770 
771     gl.useProgram(prog);
772     gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
773 
774     vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0]));
775     vertexArrays.push_back(glu::va::Uint32("a_in0", 1, numVertices, 0, &in0Arr[0]));
776     vertexArrays.push_back(glu::va::Uint32("a_in1", 1, numVertices, 0, &in1Arr[0]));
777 
778     GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup");
779 
780     // Compute values and reference.
781     for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++)
782     {
783         uint32_t in0 =
784             (isMaxRangeA ? m_rnd.getUint32() : (m_rangeA.x() + m_rnd.getUint32() % (m_rangeA.y() - m_rangeA.x() + 1))) &
785             mask;
786         uint32_t in1 =
787             (isMaxRangeB ? m_rnd.getUint32() : (m_rangeB.x() + m_rnd.getUint32() % (m_rangeB.y() - m_rangeB.x() + 1))) &
788             mask;
789         uint32_t refOut = m_evalFunc(in0, in1) & mask;
790 
791         log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": "
792             << "in0 = " << tcu::toHex(in0) << ", in1 = " << tcu::toHex(in1) << ", ref out = " << tcu::toHex(refOut)
793             << TestLog::EndMessage;
794 
795         std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0);
796         std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1);
797 
798         glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0],
799                   glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0]));
800         gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]);
801         GLU_EXPECT_NO_ERROR(gl.getError(), "After render");
802 
803         // Compare pixels.
804         for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++)
805         {
806             for (int x = 0; x < FRAMEBUFFER_WIDTH; x++)
807             {
808                 uint32_t cmpOut    = pixels[(y * FRAMEBUFFER_WIDTH + x) * 4];
809                 uint32_t cmpMasked = cmpOut & mask;
810 
811                 if (cmpMasked != refOut)
812                 {
813                     log << TestLog::Message << "Comparison failed (at " << x << ", " << y << "): "
814                         << "got " << tcu::toHex(cmpOut) << TestLog::EndMessage;
815                     m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
816                     return STOP;
817                 }
818             }
819         }
820     }
821 
822     gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
823     GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration");
824 
825     m_iterNdx += 1;
826     return (m_iterNdx < m_numIters) ? CONTINUE : STOP;
827 }
828 
ShaderPrecisionTests(Context & context)829 ShaderPrecisionTests::ShaderPrecisionTests(Context &context)
830     : TestCaseGroup(context, "precision", "Shader precision requirements validation tests")
831 {
832 }
833 
~ShaderPrecisionTests(void)834 ShaderPrecisionTests::~ShaderPrecisionTests(void)
835 {
836 }
837 
init(void)838 void ShaderPrecisionTests::init(void)
839 {
840     using tcu::add;
841     using tcu::div;
842     using tcu::IVec2;
843     using tcu::mul;
844     using tcu::sub;
845     using tcu::UVec2;
846     using tcu::Vec2;
847 
848     // Exp = Emax-2, Mantissa = 0
849     float minF32 = tcu::Float32((1u << 31) | (0xfdu << 23) | 0x0u).asFloat();
850     float maxF32 = tcu::Float32((0u << 31) | (0xfdu << 23) | 0x0u).asFloat();
851     float minF16 = tcu::Float16((uint16_t)((1u << 15) | (0x1du << 10) | 0x0u)).asFloat();
852     float maxF16 = tcu::Float16((uint16_t)((0u << 15) | (0x1du << 10) | 0x0u)).asFloat();
853     tcu::Vec2 fullRange32F(minF32, maxF32);
854     tcu::Vec2 fullRange16F(minF16, maxF16);
855     tcu::IVec2 fullRange32I(0x80000000, 0x7fffffff);
856     tcu::IVec2 fullRange16I(-(1 << 15), (1 << 15) - 1);
857     tcu::IVec2 fullRange8I(-(1 << 7), (1 << 7) - 1);
858     tcu::UVec2 fullRange32U(0u, 0xffffffffu);
859     tcu::UVec2 fullRange16U(0u, 0xffffu);
860     tcu::UVec2 fullRange8U(0u, 0xffu);
861 
862     // \note Right now it is not programmatically verified that the results shouldn't end up being inf/nan but
863     //       actual values used are ok.
864 
865     static const struct
866     {
867         const char *name;
868         const char *op;
869         ShaderFloatPrecisionCase::EvalFunc evalFunc;
870         glu::Precision precision;
871         tcu::Vec2 rangeA;
872         tcu::Vec2 rangeB;
873     } floatCases[] = {
874         // Name                Op                Eval            Precision                RangeA                RangeB
875         {"highp_add", "in0 + in1", add<double>, glu::PRECISION_HIGHP, fullRange32F, fullRange32F},
876         {"highp_sub", "in0 - in1", sub<double>, glu::PRECISION_HIGHP, fullRange32F, fullRange32F},
877         {"highp_mul", "in0 * in1", mul<double>, glu::PRECISION_HIGHP, Vec2(-1e5f, 1e5f), Vec2(-1e5f, 1e5f)},
878         {"highp_div", "in0 / in1", div<double>, glu::PRECISION_HIGHP, Vec2(-1e5f, 1e5f), Vec2(-1e5f, 1e5f)},
879         {"mediump_add", "in0 + in1", add<double>, glu::PRECISION_MEDIUMP, fullRange16F, fullRange16F},
880         {"mediump_sub", "in0 - in1", sub<double>, glu::PRECISION_MEDIUMP, fullRange16F, fullRange16F},
881         {"mediump_mul", "in0 * in1", mul<double>, glu::PRECISION_MEDIUMP, Vec2(-1e2f, 1e2f), Vec2(-1e2f, 1e2f)},
882         {"mediump_div", "in0 / in1", div<double>, glu::PRECISION_MEDIUMP, Vec2(-1e2f, 1e2f), Vec2(-1e2f, 1e2f)}};
883 
884     static const struct
885     {
886         const char *name;
887         const char *op;
888         ShaderIntPrecisionCase::EvalFunc evalFunc;
889         glu::Precision precision;
890         int bits;
891         tcu::IVec2 rangeA;
892         tcu::IVec2 rangeB;
893     } intCases[] = {
894         // Name                Op                Eval                Precision                Bits    RangeA            RangeB
895         {"highp_add", "in0 + in1", add<int64_t>, glu::PRECISION_HIGHP, 32, fullRange32I, fullRange32I},
896         {"highp_sub", "in0 - in1", sub<int64_t>, glu::PRECISION_HIGHP, 32, fullRange32I, fullRange32I},
897         {"highp_mul", "in0 * in1", mul<int64_t>, glu::PRECISION_HIGHP, 32, fullRange32I, fullRange32I},
898         {"highp_div", "in0 / in1", div<int64_t>, glu::PRECISION_HIGHP, 32, fullRange32I, IVec2(-10000, -1)},
899         {"mediump_add", "in0 + in1", add<int64_t>, glu::PRECISION_MEDIUMP, 16, fullRange16I, fullRange16I},
900         {"mediump_sub", "in0 - in1", sub<int64_t>, glu::PRECISION_MEDIUMP, 16, fullRange16I, fullRange16I},
901         {"mediump_mul", "in0 * in1", mul<int64_t>, glu::PRECISION_MEDIUMP, 16, fullRange16I, fullRange16I},
902         {"mediump_div", "in0 / in1", div<int64_t>, glu::PRECISION_MEDIUMP, 16, fullRange16I, IVec2(1, 1000)},
903         {"lowp_add", "in0 + in1", add<int64_t>, glu::PRECISION_LOWP, 8, fullRange8I, fullRange8I},
904         {"lowp_sub", "in0 - in1", sub<int64_t>, glu::PRECISION_LOWP, 8, fullRange8I, fullRange8I},
905         {"lowp_mul", "in0 * in1", mul<int64_t>, glu::PRECISION_LOWP, 8, fullRange8I, fullRange8I},
906         {"lowp_div", "in0 / in1", div<int64_t>, glu::PRECISION_LOWP, 8, fullRange8I, IVec2(-50, -1)}};
907 
908     static const struct
909     {
910         const char *name;
911         const char *op;
912         ShaderUintPrecisionCase::EvalFunc evalFunc;
913         glu::Precision precision;
914         int bits;
915         tcu::UVec2 rangeA;
916         tcu::UVec2 rangeB;
917     } uintCases[] = {
918         // Name                Op                Eval                Precision                Bits    RangeA            RangeB
919         {"highp_add", "in0 + in1", add<uint32_t>, glu::PRECISION_HIGHP, 32, fullRange32U, fullRange32U},
920         {"highp_sub", "in0 - in1", sub<uint32_t>, glu::PRECISION_HIGHP, 32, fullRange32U, fullRange32U},
921         {"highp_mul", "in0 * in1", mul<uint32_t>, glu::PRECISION_HIGHP, 32, fullRange32U, fullRange32U},
922         {"highp_div", "in0 / in1", div<uint32_t>, glu::PRECISION_HIGHP, 32, fullRange32U, UVec2(1u, 10000u)},
923         {"mediump_add", "in0 + in1", add<uint32_t>, glu::PRECISION_MEDIUMP, 16, fullRange16U, fullRange16U},
924         {"mediump_sub", "in0 - in1", sub<uint32_t>, glu::PRECISION_MEDIUMP, 16, fullRange16U, fullRange16U},
925         {"mediump_mul", "in0 * in1", mul<uint32_t>, glu::PRECISION_MEDIUMP, 16, fullRange16U, fullRange16U},
926         {"mediump_div", "in0 / in1", div<uint32_t>, glu::PRECISION_MEDIUMP, 16, fullRange16U, UVec2(1, 1000u)},
927         {"lowp_add", "in0 + in1", add<uint32_t>, glu::PRECISION_LOWP, 8, fullRange8U, fullRange8U},
928         {"lowp_sub", "in0 - in1", sub<uint32_t>, glu::PRECISION_LOWP, 8, fullRange8U, fullRange8U},
929         {"lowp_mul", "in0 * in1", mul<uint32_t>, glu::PRECISION_LOWP, 8, fullRange8U, fullRange8U},
930         {"lowp_div", "in0 / in1", div<uint32_t>, glu::PRECISION_LOWP, 8, fullRange8U, UVec2(1, 50u)}};
931 
932     tcu::TestCaseGroup *floatGroup = new tcu::TestCaseGroup(m_testCtx, "float", "Floating-point precision tests");
933     addChild(floatGroup);
934     for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(floatCases); ndx++)
935     {
936         floatGroup->addChild(new ShaderFloatPrecisionCase(
937             m_context, (string(floatCases[ndx].name) + "_vertex").c_str(), "", floatCases[ndx].op,
938             floatCases[ndx].evalFunc, floatCases[ndx].precision, floatCases[ndx].rangeA, floatCases[ndx].rangeB, true));
939         floatGroup->addChild(
940             new ShaderFloatPrecisionCase(m_context, (string(floatCases[ndx].name) + "_fragment").c_str(), "",
941                                          floatCases[ndx].op, floatCases[ndx].evalFunc, floatCases[ndx].precision,
942                                          floatCases[ndx].rangeA, floatCases[ndx].rangeB, false));
943     }
944 
945     tcu::TestCaseGroup *intGroup = new tcu::TestCaseGroup(m_testCtx, "int", "Integer precision tests");
946     addChild(intGroup);
947     for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(intCases); ndx++)
948     {
949         intGroup->addChild(new ShaderIntPrecisionCase(
950             m_context, (string(intCases[ndx].name) + "_vertex").c_str(), "", intCases[ndx].op, intCases[ndx].evalFunc,
951             intCases[ndx].precision, intCases[ndx].bits, intCases[ndx].rangeA, intCases[ndx].rangeB, true));
952         intGroup->addChild(new ShaderIntPrecisionCase(
953             m_context, (string(intCases[ndx].name) + "_fragment").c_str(), "", intCases[ndx].op, intCases[ndx].evalFunc,
954             intCases[ndx].precision, intCases[ndx].bits, intCases[ndx].rangeA, intCases[ndx].rangeB, false));
955     }
956 
957     tcu::TestCaseGroup *uintGroup = new tcu::TestCaseGroup(m_testCtx, "uint", "Unsigned integer precision tests");
958     addChild(uintGroup);
959     for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(uintCases); ndx++)
960     {
961         uintGroup->addChild(new ShaderUintPrecisionCase(m_context, (string(uintCases[ndx].name) + "_vertex").c_str(),
962                                                         "", uintCases[ndx].op, uintCases[ndx].evalFunc,
963                                                         uintCases[ndx].precision, uintCases[ndx].bits,
964                                                         uintCases[ndx].rangeA, uintCases[ndx].rangeB, true));
965         uintGroup->addChild(new ShaderUintPrecisionCase(m_context, (string(uintCases[ndx].name) + "_fragment").c_str(),
966                                                         "", uintCases[ndx].op, uintCases[ndx].evalFunc,
967                                                         uintCases[ndx].precision, uintCases[ndx].bits,
968                                                         uintCases[ndx].rangeA, uintCases[ndx].rangeB, false));
969     }
970 }
971 
972 } // namespace Functional
973 } // namespace gles3
974 } // namespace deqp
975