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