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 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 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 233 ShaderFloatPrecisionCase::~ShaderFloatPrecisionCase (void) 234 { 235 ShaderFloatPrecisionCase::deinit(); 236 } 237 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 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 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 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 int (*EvalFunc) (int a, int 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 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 486 ShaderIntPrecisionCase::~ShaderIntPrecisionCase (void) 487 { 488 ShaderIntPrecisionCase::deinit(); 489 } 490 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 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 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 = m_evalFunc(in0, in1) & 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 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 680 ShaderUintPrecisionCase::~ShaderUintPrecisionCase (void) 681 { 682 ShaderUintPrecisionCase::deinit(); 683 } 684 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 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 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 818 ShaderPrecisionTests::ShaderPrecisionTests (Context& context) 819 : TestCaseGroup(context, "precision", "Shader precision requirements validation tests") 820 { 821 } 822 823 ShaderPrecisionTests::~ShaderPrecisionTests (void) 824 { 825 } 826 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<int>, glu::PRECISION_HIGHP, 32, fullRange32I, fullRange32I }, 888 { "highp_sub", "in0 - in1", sub<int>, glu::PRECISION_HIGHP, 32, fullRange32I, fullRange32I }, 889 { "highp_mul", "in0 * in1", mul<int>, glu::PRECISION_HIGHP, 32, fullRange32I, fullRange32I }, 890 { "highp_div", "in0 / in1", div<int>, glu::PRECISION_HIGHP, 32, fullRange32I, IVec2(-10000, -1) }, 891 { "mediump_add", "in0 + in1", add<int>, glu::PRECISION_MEDIUMP, 16, fullRange16I, fullRange16I }, 892 { "mediump_sub", "in0 - in1", sub<int>, glu::PRECISION_MEDIUMP, 16, fullRange16I, fullRange16I }, 893 { "mediump_mul", "in0 * in1", mul<int>, glu::PRECISION_MEDIUMP, 16, fullRange16I, fullRange16I }, 894 { "mediump_div", "in0 / in1", div<int>, glu::PRECISION_MEDIUMP, 16, fullRange16I, IVec2(1, 1000) }, 895 { "lowp_add", "in0 + in1", add<int>, glu::PRECISION_LOWP, 8, fullRange8I, fullRange8I }, 896 { "lowp_sub", "in0 - in1", sub<int>, glu::PRECISION_LOWP, 8, fullRange8I, fullRange8I }, 897 { "lowp_mul", "in0 * in1", mul<int>, glu::PRECISION_LOWP, 8, fullRange8I, fullRange8I }, 898 { "lowp_div", "in0 / in1", div<int>, 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