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