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