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 = refBits >> maskBits; 335 const deUint64 accurateResBits = resBits >> maskBits; 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 inline int extendTo32Bit (int value, int bits) 563 { 564 return (value & ((1<<(bits-1))-1)) | (((value & (1<<(bits-1))) << (32-bits)) >> (32-bits)); 565 } 566 567 ShaderIntPrecisionCase::IterateResult ShaderIntPrecisionCase::iterate (void) 568 { 569 // Constant data. 570 const float position[] = 571 { 572 -1.0f, -1.0f, 0.0f, 1.0f, 573 -1.0f, 1.0f, 0.0f, 1.0f, 574 1.0f, -1.0f, 0.0f, 1.0f, 575 1.0f, 1.0f, 0.0f, 1.0f 576 }; 577 const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 }; 578 579 const int numVertices = 4; 580 int in0Arr[4] = { 0 }; 581 int in1Arr[4] = { 0 }; 582 583 TestLog& log = m_testCtx.getLog(); 584 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 585 deUint32 mask = m_bits == 32 ? 0xffffffffu : ((1u<<m_bits)-1); 586 vector<int> pixels (FRAMEBUFFER_WIDTH*FRAMEBUFFER_HEIGHT*4); 587 vector<glu::VertexArrayBinding> vertexArrays; 588 589 deUint32 prog = m_program->getProgram(); 590 591 // \todo [2012-05-03 pyry] A bit hacky. getInt() should work fine with ranges like this. 592 bool isMaxRangeA = m_rangeA.x() == (int)0x80000000 && m_rangeA.y() == (int)0x7fffffff; 593 bool isMaxRangeB = m_rangeB.x() == (int)0x80000000 && m_rangeB.y() == (int)0x7fffffff; 594 595 gl.useProgram(prog); 596 gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); 597 598 vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0])); 599 vertexArrays.push_back(glu::va::Int32("a_in0", 1, numVertices, 0, &in0Arr[0])); 600 vertexArrays.push_back(glu::va::Int32("a_in1", 1, numVertices, 0, &in1Arr[0])); 601 602 GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup"); 603 604 // Compute values and reference. 605 for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++) 606 { 607 int in0 = extendTo32Bit(((isMaxRangeA ? (int)m_rnd.getUint32() : m_rnd.getInt(m_rangeA.x(), m_rangeA.y())) & mask), m_bits); 608 int in1 = extendTo32Bit(((isMaxRangeB ? (int)m_rnd.getUint32() : m_rnd.getInt(m_rangeB.x(), m_rangeB.y())) & mask), m_bits); 609 int refMasked = m_evalFunc(in0, in1) & mask; 610 int refOut = extendTo32Bit(refMasked, m_bits); 611 612 log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": " 613 << "in0 = " << in0 << ", in1 = " << in1 << ", ref out = " << refOut << " / " << tcu::toHex(refMasked) 614 << TestLog::EndMessage; 615 616 std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0); 617 std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1); 618 619 glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0], 620 glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0])); 621 gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_INT, &pixels[0]); 622 GLU_EXPECT_NO_ERROR(gl.getError(), "After render"); 623 624 // Compare pixels. 625 for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++) 626 { 627 for (int x = 0; x < FRAMEBUFFER_WIDTH; x++) 628 { 629 int cmpOut = pixels[(y*FRAMEBUFFER_WIDTH + x)*4]; 630 int cmpMasked = cmpOut & mask; 631 632 if (cmpMasked != refMasked) 633 { 634 log << TestLog::Message << "Comparison failed (at " << x << ", " << y << "): " 635 << "got " << cmpOut << " / " << tcu::toHex(cmpOut) 636 << TestLog::EndMessage; 637 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); 638 return STOP; 639 } 640 } 641 } 642 } 643 644 gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer()); 645 GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration"); 646 647 m_iterNdx += 1; 648 return (m_iterNdx < m_numIters) ? CONTINUE : STOP; 649 } 650 651 class ShaderUintPrecisionCase : public TestCase 652 { 653 public: 654 typedef deUint32 (*EvalFunc) (deUint32 a, deUint32 b); 655 656 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); 657 ~ShaderUintPrecisionCase (void); 658 659 void init (void); 660 void deinit (void); 661 IterateResult iterate (void); 662 663 private: 664 ShaderUintPrecisionCase (const ShaderUintPrecisionCase& other); 665 ShaderUintPrecisionCase& operator= (const ShaderUintPrecisionCase& other); 666 667 // Case parameters. 668 std::string m_op; 669 EvalFunc m_evalFunc; 670 glu::Precision m_precision; 671 int m_bits; 672 tcu::UVec2 m_rangeA; 673 tcu::UVec2 m_rangeB; 674 bool m_isVertexCase; 675 676 int m_numTestsPerIter; 677 int m_numIters; 678 de::Random m_rnd; 679 680 // Iteration state. 681 glu::ShaderProgram* m_program; 682 deUint32 m_framebuffer; 683 deUint32 m_renderbuffer; 684 int m_iterNdx; 685 }; 686 687 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) 688 : TestCase (context, name, desc) 689 , m_op (op) 690 , m_evalFunc (evalFunc) 691 , m_precision (precision) 692 , m_bits (bits) 693 , m_rangeA (rangeA) 694 , m_rangeB (rangeB) 695 , m_isVertexCase (isVertexCase) 696 , m_numTestsPerIter (32) 697 , m_numIters (4) 698 , m_rnd (deStringHash(name)) 699 , m_program (DE_NULL) 700 , m_framebuffer (0) 701 , m_renderbuffer (0) 702 , m_iterNdx (0) 703 { 704 } 705 706 ShaderUintPrecisionCase::~ShaderUintPrecisionCase (void) 707 { 708 ShaderUintPrecisionCase::deinit(); 709 } 710 711 void ShaderUintPrecisionCase::init (void) 712 { 713 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 714 TestLog& log = m_testCtx.getLog(); 715 716 DE_ASSERT(!m_program && !m_framebuffer && !m_renderbuffer); 717 718 // Create program. 719 m_program = createIntUintPrecisionEvalProgram(m_context.getRenderContext(), glu::TYPE_UINT, m_precision, m_op.c_str(), m_isVertexCase); 720 log << *m_program; 721 722 TCU_CHECK(m_program->isOk()); 723 724 // Create framebuffer. 725 gl.genFramebuffers(1, &m_framebuffer); 726 gl.genRenderbuffers(1, &m_renderbuffer); 727 728 gl.bindRenderbuffer(GL_RENDERBUFFER, m_renderbuffer); 729 gl.renderbufferStorage(GL_RENDERBUFFER, GL_R32UI, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT); 730 731 gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); 732 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_renderbuffer); 733 734 GLU_EXPECT_NO_ERROR(gl.getError(), "Post framebuffer setup"); 735 TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); 736 737 gl.bindFramebuffer(GL_FRAMEBUFFER, m_context.getRenderContext().getDefaultFramebuffer()); 738 739 // Initialize test result to pass. 740 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 741 m_iterNdx = 0; 742 743 log << TestLog::Message << "Number of accurate bits assumed = " << m_bits << TestLog::EndMessage; 744 } 745 746 void ShaderUintPrecisionCase::deinit (void) 747 { 748 delete m_program; 749 750 if (m_framebuffer) 751 m_context.getRenderContext().getFunctions().deleteFramebuffers(1, &m_framebuffer); 752 753 if (m_renderbuffer) 754 m_context.getRenderContext().getFunctions().deleteRenderbuffers(1, &m_renderbuffer); 755 756 m_program = DE_NULL; 757 m_framebuffer = 0; 758 m_renderbuffer = 0; 759 } 760 761 ShaderUintPrecisionCase::IterateResult ShaderUintPrecisionCase::iterate (void) 762 { 763 // Constant data. 764 const float position[] = 765 { 766 -1.0f, -1.0f, 0.0f, 1.0f, 767 -1.0f, 1.0f, 0.0f, 1.0f, 768 1.0f, -1.0f, 0.0f, 1.0f, 769 1.0f, 1.0f, 0.0f, 1.0f 770 }; 771 const deUint16 indices[] = { 0, 1, 2, 2, 1, 3 }; 772 773 const int numVertices = 4; 774 deUint32 in0Arr[4] = { 0 }; 775 deUint32 in1Arr[4] = { 0 }; 776 777 TestLog& log = m_testCtx.getLog(); 778 const glw::Functions& gl = m_context.getRenderContext().getFunctions(); 779 deUint32 mask = m_bits == 32 ? 0xffffffffu : ((1u<<m_bits)-1); 780 vector<deUint32> pixels (FRAMEBUFFER_WIDTH*FRAMEBUFFER_HEIGHT*4); 781 vector<glu::VertexArrayBinding> vertexArrays; 782 783 deUint32 prog = m_program->getProgram(); 784 785 // \todo [2012-05-03 pyry] A bit hacky. 786 bool isMaxRangeA = m_rangeA.x() == 0 && m_rangeA.y() == 0xffffffff; 787 bool isMaxRangeB = m_rangeB.x() == 0 && m_rangeB.y() == 0xffffffff; 788 789 gl.useProgram(prog); 790 gl.bindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); 791 792 vertexArrays.push_back(glu::va::Float("a_position", 4, numVertices, 0, &position[0])); 793 vertexArrays.push_back(glu::va::Uint32("a_in0", 1, numVertices, 0, &in0Arr[0])); 794 vertexArrays.push_back(glu::va::Uint32("a_in1", 1, numVertices, 0, &in1Arr[0])); 795 796 GLU_EXPECT_NO_ERROR(gl.getError(), "After program setup"); 797 798 // Compute values and reference. 799 for (int testNdx = 0; testNdx < m_numTestsPerIter; testNdx++) 800 { 801 deUint32 in0 = (isMaxRangeA ? m_rnd.getUint32() : (m_rangeA.x() + m_rnd.getUint32()%(m_rangeA.y()-m_rangeA.x()+1))) & mask; 802 deUint32 in1 = (isMaxRangeB ? m_rnd.getUint32() : (m_rangeB.x() + m_rnd.getUint32()%(m_rangeB.y()-m_rangeB.x()+1))) & mask; 803 deUint32 refOut = m_evalFunc(in0, in1) & mask; 804 805 log << TestLog::Message << "iter " << m_iterNdx << ", test " << testNdx << ": " 806 << "in0 = " << tcu::toHex(in0) << ", in1 = " << tcu::toHex(in1) << ", ref out = " << tcu::toHex(refOut) 807 << TestLog::EndMessage; 808 809 std::fill(&in0Arr[0], &in0Arr[0] + DE_LENGTH_OF_ARRAY(in0Arr), in0); 810 std::fill(&in1Arr[0], &in1Arr[0] + DE_LENGTH_OF_ARRAY(in1Arr), in1); 811 812 glu::draw(m_context.getRenderContext(), prog, (int)vertexArrays.size(), &vertexArrays[0], 813 glu::pr::Triangles(DE_LENGTH_OF_ARRAY(indices), &indices[0])); 814 gl.readPixels(0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, GL_RGBA_INTEGER, GL_UNSIGNED_INT, &pixels[0]); 815 GLU_EXPECT_NO_ERROR(gl.getError(), "After render"); 816 817 // Compare pixels. 818 for (int y = 0; y < FRAMEBUFFER_HEIGHT; y++) 819 { 820 for (int x = 0; x < FRAMEBUFFER_WIDTH; x++) 821 { 822 deUint32 cmpOut = pixels[(y*FRAMEBUFFER_WIDTH + x)*4]; 823 deUint32 cmpMasked = cmpOut & mask; 824 825 if (cmpMasked != refOut) 826 { 827 log << TestLog::Message << "Comparison failed (at " << x << ", " << y << "): " 828 << "got " << tcu::toHex(cmpOut) 829 << TestLog::EndMessage; 830 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); 831 return STOP; 832 } 833 } 834 } 835 } 836 837 gl.bindFramebuffer(GL_FRAMEBUFFER, 0); 838 GLU_EXPECT_NO_ERROR(gl.getError(), "After iteration"); 839 840 m_iterNdx += 1; 841 return (m_iterNdx < m_numIters) ? CONTINUE : STOP; 842 } 843 844 ShaderPrecisionTests::ShaderPrecisionTests (Context& context) 845 : TestCaseGroup(context, "precision", "Shader precision requirements validation tests") 846 { 847 } 848 849 ShaderPrecisionTests::~ShaderPrecisionTests (void) 850 { 851 } 852 853 void ShaderPrecisionTests::init (void) 854 { 855 using tcu::add; 856 using tcu::sub; 857 using tcu::mul; 858 using tcu::div; 859 using tcu::Vec2; 860 using tcu::IVec2; 861 using tcu::UVec2; 862 863 // Exp = Emax-2, Mantissa = 0 864 float minF32 = tcu::Float32((1u<<31) | (0xfdu<<23) | 0x0u).asFloat(); 865 float maxF32 = tcu::Float32((0u<<31) | (0xfdu<<23) | 0x0u).asFloat(); 866 float minF16 = tcu::Float16((deUint16)((1u<<15) | (0x1du<<10) | 0x0u)).asFloat(); 867 float maxF16 = tcu::Float16((deUint16)((0u<<15) | (0x1du<<10) | 0x0u)).asFloat(); 868 tcu::Vec2 fullRange32F (minF32, maxF32); 869 tcu::Vec2 fullRange16F (minF16, maxF16); 870 tcu::IVec2 fullRange32I (0x80000000, 0x7fffffff); 871 tcu::IVec2 fullRange16I (-(1<<15), (1<<15)-1); 872 tcu::IVec2 fullRange8I (-(1<<7), (1<<7)-1); 873 tcu::UVec2 fullRange32U (0u, 0xffffffffu); 874 tcu::UVec2 fullRange16U (0u, 0xffffu); 875 tcu::UVec2 fullRange8U (0u, 0xffu); 876 877 // \note Right now it is not programmatically verified that the results shouldn't end up being inf/nan but 878 // actual values used are ok. 879 880 static const struct 881 { 882 const char* name; 883 const char* op; 884 ShaderFloatPrecisionCase::EvalFunc evalFunc; 885 glu::Precision precision; 886 tcu::Vec2 rangeA; 887 tcu::Vec2 rangeB; 888 } floatCases[] = 889 { 890 // Name Op Eval Precision RangeA RangeB 891 { "highp_add", "in0 + in1", add<double>, glu::PRECISION_HIGHP, fullRange32F, fullRange32F }, 892 { "highp_sub", "in0 - in1", sub<double>, glu::PRECISION_HIGHP, fullRange32F, fullRange32F }, 893 { "highp_mul", "in0 * in1", mul<double>, glu::PRECISION_HIGHP, Vec2(-1e5f, 1e5f), Vec2(-1e5f, 1e5f) }, 894 { "highp_div", "in0 / in1", div<double>, glu::PRECISION_HIGHP, Vec2(-1e5f, 1e5f), Vec2(-1e5f, 1e5f) }, 895 { "mediump_add", "in0 + in1", add<double>, glu::PRECISION_MEDIUMP, fullRange16F, fullRange16F }, 896 { "mediump_sub", "in0 - in1", sub<double>, glu::PRECISION_MEDIUMP, fullRange16F, fullRange16F }, 897 { "mediump_mul", "in0 * in1", mul<double>, glu::PRECISION_MEDIUMP, Vec2(-1e2f, 1e2f), Vec2(-1e2f, 1e2f) }, 898 { "mediump_div", "in0 / in1", div<double>, glu::PRECISION_MEDIUMP, Vec2(-1e2f, 1e2f), Vec2(-1e2f, 1e2f) } 899 }; 900 901 static const struct 902 { 903 const char* name; 904 const char* op; 905 ShaderIntPrecisionCase::EvalFunc evalFunc; 906 glu::Precision precision; 907 int bits; 908 tcu::IVec2 rangeA; 909 tcu::IVec2 rangeB; 910 } intCases[] = 911 { 912 // Name Op Eval Precision Bits RangeA RangeB 913 { "highp_add", "in0 + in1", add<int>, glu::PRECISION_HIGHP, 32, fullRange32I, fullRange32I }, 914 { "highp_sub", "in0 - in1", sub<int>, glu::PRECISION_HIGHP, 32, fullRange32I, fullRange32I }, 915 { "highp_mul", "in0 * in1", mul<int>, glu::PRECISION_HIGHP, 32, fullRange32I, fullRange32I }, 916 { "highp_div", "in0 / in1", div<int>, glu::PRECISION_HIGHP, 32, fullRange32I, IVec2(-10000, -1) }, 917 { "mediump_add", "in0 + in1", add<int>, glu::PRECISION_MEDIUMP, 16, fullRange16I, fullRange16I }, 918 { "mediump_sub", "in0 - in1", sub<int>, glu::PRECISION_MEDIUMP, 16, fullRange16I, fullRange16I }, 919 { "mediump_mul", "in0 * in1", mul<int>, glu::PRECISION_MEDIUMP, 16, fullRange16I, fullRange16I }, 920 { "mediump_div", "in0 / in1", div<int>, glu::PRECISION_MEDIUMP, 16, fullRange16I, IVec2(1, 1000) }, 921 { "lowp_add", "in0 + in1", add<int>, glu::PRECISION_LOWP, 8, fullRange8I, fullRange8I }, 922 { "lowp_sub", "in0 - in1", sub<int>, glu::PRECISION_LOWP, 8, fullRange8I, fullRange8I }, 923 { "lowp_mul", "in0 * in1", mul<int>, glu::PRECISION_LOWP, 8, fullRange8I, fullRange8I }, 924 { "lowp_div", "in0 / in1", div<int>, glu::PRECISION_LOWP, 8, fullRange8I, IVec2(-50, -1) } 925 }; 926 927 static const struct 928 { 929 const char* name; 930 const char* op; 931 ShaderUintPrecisionCase::EvalFunc evalFunc; 932 glu::Precision precision; 933 int bits; 934 tcu::UVec2 rangeA; 935 tcu::UVec2 rangeB; 936 } uintCases[] = 937 { 938 // Name Op Eval Precision Bits RangeA RangeB 939 { "highp_add", "in0 + in1", add<deUint32>, glu::PRECISION_HIGHP, 32, fullRange32U, fullRange32U }, 940 { "highp_sub", "in0 - in1", sub<deUint32>, glu::PRECISION_HIGHP, 32, fullRange32U, fullRange32U }, 941 { "highp_mul", "in0 * in1", mul<deUint32>, glu::PRECISION_HIGHP, 32, fullRange32U, fullRange32U }, 942 { "highp_div", "in0 / in1", div<deUint32>, glu::PRECISION_HIGHP, 32, fullRange32U, UVec2(1u, 10000u) }, 943 { "mediump_add", "in0 + in1", add<deUint32>, glu::PRECISION_MEDIUMP, 16, fullRange16U, fullRange16U }, 944 { "mediump_sub", "in0 - in1", sub<deUint32>, glu::PRECISION_MEDIUMP, 16, fullRange16U, fullRange16U }, 945 { "mediump_mul", "in0 * in1", mul<deUint32>, glu::PRECISION_MEDIUMP, 16, fullRange16U, fullRange16U }, 946 { "mediump_div", "in0 / in1", div<deUint32>, glu::PRECISION_MEDIUMP, 16, fullRange16U, UVec2(1, 1000u) }, 947 { "lowp_add", "in0 + in1", add<deUint32>, glu::PRECISION_LOWP, 8, fullRange8U, fullRange8U }, 948 { "lowp_sub", "in0 - in1", sub<deUint32>, glu::PRECISION_LOWP, 8, fullRange8U, fullRange8U }, 949 { "lowp_mul", "in0 * in1", mul<deUint32>, glu::PRECISION_LOWP, 8, fullRange8U, fullRange8U }, 950 { "lowp_div", "in0 / in1", div<deUint32>, glu::PRECISION_LOWP, 8, fullRange8U, UVec2(1, 50u) } 951 }; 952 953 tcu::TestCaseGroup* floatGroup = new tcu::TestCaseGroup(m_testCtx, "float", "Floating-point precision tests"); 954 addChild(floatGroup); 955 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(floatCases); ndx++) 956 { 957 floatGroup->addChild(new ShaderFloatPrecisionCase(m_context, 958 (string(floatCases[ndx].name) + "_vertex").c_str(), "", 959 floatCases[ndx].op, 960 floatCases[ndx].evalFunc, 961 floatCases[ndx].precision, 962 floatCases[ndx].rangeA, 963 floatCases[ndx].rangeB, 964 true)); 965 floatGroup->addChild(new ShaderFloatPrecisionCase(m_context, 966 (string(floatCases[ndx].name) + "_fragment").c_str(), "", 967 floatCases[ndx].op, 968 floatCases[ndx].evalFunc, 969 floatCases[ndx].precision, 970 floatCases[ndx].rangeA, 971 floatCases[ndx].rangeB, 972 false)); 973 } 974 975 tcu::TestCaseGroup* intGroup = new tcu::TestCaseGroup(m_testCtx, "int", "Integer precision tests"); 976 addChild(intGroup); 977 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(intCases); ndx++) 978 { 979 intGroup->addChild(new ShaderIntPrecisionCase(m_context, 980 (string(intCases[ndx].name) + "_vertex").c_str(), "", 981 intCases[ndx].op, 982 intCases[ndx].evalFunc, 983 intCases[ndx].precision, 984 intCases[ndx].bits, 985 intCases[ndx].rangeA, 986 intCases[ndx].rangeB, 987 true)); 988 intGroup->addChild(new ShaderIntPrecisionCase(m_context, 989 (string(intCases[ndx].name) + "_fragment").c_str(), "", 990 intCases[ndx].op, 991 intCases[ndx].evalFunc, 992 intCases[ndx].precision, 993 intCases[ndx].bits, 994 intCases[ndx].rangeA, 995 intCases[ndx].rangeB, 996 false)); 997 } 998 999 tcu::TestCaseGroup* uintGroup = new tcu::TestCaseGroup(m_testCtx, "uint", "Unsigned integer precision tests"); 1000 addChild(uintGroup); 1001 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(uintCases); ndx++) 1002 { 1003 uintGroup->addChild(new ShaderUintPrecisionCase(m_context, 1004 (string(uintCases[ndx].name) + "_vertex").c_str(), "", 1005 uintCases[ndx].op, 1006 uintCases[ndx].evalFunc, 1007 uintCases[ndx].precision, 1008 uintCases[ndx].bits, 1009 uintCases[ndx].rangeA, 1010 uintCases[ndx].rangeB, 1011 true)); 1012 uintGroup->addChild(new ShaderUintPrecisionCase(m_context, 1013 (string(uintCases[ndx].name) + "_fragment").c_str(), "", 1014 uintCases[ndx].op, 1015 uintCases[ndx].evalFunc, 1016 uintCases[ndx].precision, 1017 uintCases[ndx].bits, 1018 uintCases[ndx].rangeA, 1019 uintCases[ndx].rangeB, 1020 false)); 1021 } 1022 } 1023 1024 } // Functional 1025 } // gles3 1026 } // deqp 1027