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