1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 3.1 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 gl_HelperInvocation tests. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "es31fShaderHelperInvocationTests.hpp" 25 26 #include "gluObjectWrapper.hpp" 27 #include "gluShaderProgram.hpp" 28 #include "gluDrawUtil.hpp" 29 #include "gluPixelTransfer.hpp" 30 31 #include "glwFunctions.hpp" 32 #include "glwEnums.hpp" 33 34 #include "tcuTestLog.hpp" 35 #include "tcuVector.hpp" 36 #include "tcuSurface.hpp" 37 38 #include "deUniquePtr.hpp" 39 #include "deStringUtil.hpp" 40 #include "deRandom.hpp" 41 #include "deString.h" 42 43 namespace deqp 44 { 45 namespace gles31 46 { 47 namespace Functional 48 { 49 namespace 50 { 51 52 using glu::ShaderProgram; 53 using tcu::TestLog; 54 using tcu::Vec2; 55 using tcu::IVec2; 56 using de::MovePtr; 57 using std::string; 58 using std::vector; 59 60 enum PrimitiveType 61 { 62 PRIMITIVETYPE_TRIANGLE = 0, 63 PRIMITIVETYPE_LINE, 64 PRIMITIVETYPE_WIDE_LINE, 65 PRIMITIVETYPE_POINT, 66 PRIMITIVETYPE_WIDE_POINT, 67 68 PRIMITIVETYPE_LAST 69 }; 70 71 static int getNumVerticesPerPrimitive (PrimitiveType primType) 72 { 73 switch (primType) 74 { 75 case PRIMITIVETYPE_TRIANGLE: return 3; 76 case PRIMITIVETYPE_LINE: return 2; 77 case PRIMITIVETYPE_WIDE_LINE: return 2; 78 case PRIMITIVETYPE_POINT: return 1; 79 case PRIMITIVETYPE_WIDE_POINT: return 1; 80 default: 81 DE_ASSERT(false); 82 return 0; 83 } 84 } 85 86 static glu::PrimitiveType getGluPrimitiveType (PrimitiveType primType) 87 { 88 switch (primType) 89 { 90 case PRIMITIVETYPE_TRIANGLE: return glu::PRIMITIVETYPE_TRIANGLES; 91 case PRIMITIVETYPE_LINE: return glu::PRIMITIVETYPE_LINES; 92 case PRIMITIVETYPE_WIDE_LINE: return glu::PRIMITIVETYPE_LINES; 93 case PRIMITIVETYPE_POINT: return glu::PRIMITIVETYPE_POINTS; 94 case PRIMITIVETYPE_WIDE_POINT: return glu::PRIMITIVETYPE_POINTS; 95 default: 96 DE_ASSERT(false); 97 return glu::PRIMITIVETYPE_LAST; 98 } 99 } 100 101 static void genVertices (PrimitiveType primType, int numPrimitives, de::Random* rnd, vector<Vec2>* dst) 102 { 103 const bool isTri = primType == PRIMITIVETYPE_TRIANGLE; 104 const float minCoord = isTri ? -1.5f : -1.0f; 105 const float maxCoord = isTri ? +1.5f : +1.0f; 106 const int numVerticesPerPrimitive = getNumVerticesPerPrimitive(primType); 107 const int numVert = numVerticesPerPrimitive*numPrimitives; 108 109 dst->resize(numVert); 110 111 for (size_t ndx = 0; ndx < dst->size(); ndx++) 112 { 113 (*dst)[ndx][0] = rnd->getFloat(minCoord, maxCoord); 114 (*dst)[ndx][1] = rnd->getFloat(minCoord, maxCoord); 115 } 116 117 // Don't produce completely or almost completely discardable primitives. 118 // \note: This doesn't guarantee that resulting primitives are visible or 119 // produce any fragments. This just removes trivially discardable 120 // primitives. 121 for (int primitiveNdx = 0; primitiveNdx < numPrimitives; ++primitiveNdx) 122 for (int component = 0; component < 2; ++component) 123 { 124 bool negativeClip = true; 125 bool positiveClip = true; 126 127 for (int vertexNdx = 0; vertexNdx < numVerticesPerPrimitive; ++vertexNdx) 128 { 129 const float p = (*dst)[primitiveNdx * numVerticesPerPrimitive + vertexNdx][component]; 130 // \note 0.9 instead of 1.0 to avoid just barely visible primitives 131 if (p > -0.9f) 132 negativeClip = false; 133 if (p < +0.9f) 134 positiveClip = false; 135 } 136 137 // if discardable, just mirror first vertex along center 138 if (negativeClip || positiveClip) 139 { 140 (*dst)[primitiveNdx * numVerticesPerPrimitive + 0][0] *= -1.0f; 141 (*dst)[primitiveNdx * numVerticesPerPrimitive + 0][1] *= -1.0f; 142 } 143 } 144 } 145 146 static int getInteger (const glw::Functions& gl, deUint32 pname) 147 { 148 int v = 0; 149 gl.getIntegerv(pname, &v); 150 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv()"); 151 return v; 152 } 153 154 static Vec2 getRange (const glw::Functions& gl, deUint32 pname) 155 { 156 Vec2 v(0.0f); 157 gl.getFloatv(pname, v.getPtr()); 158 GLU_EXPECT_NO_ERROR(gl.getError(), "glGetFloatv()"); 159 return v; 160 } 161 162 static void drawRandomPrimitives (const glu::RenderContext& renderCtx, deUint32 program, PrimitiveType primType, int numPrimitives, de::Random* rnd) 163 { 164 const glw::Functions& gl = renderCtx.getFunctions(); 165 const float minPointSize = 16.0f; 166 const float maxPointSize = 32.0f; 167 const float minLineWidth = 16.0f; 168 const float maxLineWidth = 32.0f; 169 vector<Vec2> vertices; 170 vector<glu::VertexArrayBinding> vertexArrays; 171 172 genVertices(primType, numPrimitives, rnd, &vertices); 173 174 vertexArrays.push_back(glu::va::Float("a_position", 2, (int)vertices.size(), 0, (const float*)&vertices[0])); 175 176 gl.useProgram(program); 177 178 // Special state for certain primitives 179 if (primType == PRIMITIVETYPE_POINT || primType == PRIMITIVETYPE_WIDE_POINT) 180 { 181 const Vec2 range = getRange(gl, GL_ALIASED_POINT_SIZE_RANGE); 182 const bool isWidePoint = primType == PRIMITIVETYPE_WIDE_POINT; 183 const float pointSize = isWidePoint ? de::min(rnd->getFloat(minPointSize, maxPointSize), range.y()) : 1.0f; 184 const int pointSizeLoc = gl.getUniformLocation(program, "u_pointSize"); 185 186 gl.uniform1f(pointSizeLoc, pointSize); 187 } 188 else if (primType == PRIMITIVETYPE_WIDE_LINE) 189 { 190 const Vec2 range = getRange(gl, GL_ALIASED_LINE_WIDTH_RANGE); 191 const float lineWidth = de::min(rnd->getFloat(minLineWidth, maxLineWidth), range.y()); 192 193 gl.lineWidth(lineWidth); 194 } 195 196 glu::draw(renderCtx, program, (int)vertexArrays.size(), &vertexArrays[0], 197 glu::PrimitiveList(getGluPrimitiveType(primType), (int)vertices.size())); 198 } 199 200 class FboHelper 201 { 202 public: 203 FboHelper (const glu::RenderContext& renderCtx, int width, int height, deUint32 format, int numSamples); 204 ~FboHelper (void); 205 206 void bindForRendering (void); 207 void readPixels (int x, int y, const tcu::PixelBufferAccess& dst); 208 209 private: 210 const glu::RenderContext& m_renderCtx; 211 const int m_numSamples; 212 const IVec2 m_size; 213 214 glu::Renderbuffer m_colorbuffer; 215 glu::Framebuffer m_framebuffer; 216 glu::Renderbuffer m_resolveColorbuffer; 217 glu::Framebuffer m_resolveFramebuffer; 218 }; 219 220 FboHelper::FboHelper (const glu::RenderContext& renderCtx, int width, int height, deUint32 format, int numSamples) 221 : m_renderCtx (renderCtx) 222 , m_numSamples (numSamples) 223 , m_size (width, height) 224 , m_colorbuffer (renderCtx) 225 , m_framebuffer (renderCtx) 226 , m_resolveColorbuffer (renderCtx) 227 , m_resolveFramebuffer (renderCtx) 228 { 229 const glw::Functions& gl = m_renderCtx.getFunctions(); 230 const int maxSamples = getInteger(gl, GL_MAX_SAMPLES); 231 232 gl.bindRenderbuffer(GL_RENDERBUFFER, *m_colorbuffer); 233 gl.renderbufferStorageMultisample(GL_RENDERBUFFER, m_numSamples, format, width, height); 234 gl.bindFramebuffer(GL_FRAMEBUFFER, *m_framebuffer); 235 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *m_colorbuffer); 236 237 if (m_numSamples > maxSamples && gl.checkFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) 238 throw tcu::NotSupportedError("Sample count exceeds GL_MAX_SAMPLES"); 239 240 TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); 241 242 if (m_numSamples != 0) 243 { 244 gl.bindRenderbuffer(GL_RENDERBUFFER, *m_resolveColorbuffer); 245 gl.renderbufferStorage(GL_RENDERBUFFER, format, width, height); 246 gl.bindFramebuffer(GL_FRAMEBUFFER, *m_resolveFramebuffer); 247 gl.framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *m_resolveColorbuffer); 248 TCU_CHECK(gl.checkFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); 249 } 250 251 GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to create framebuffer"); 252 } 253 254 FboHelper::~FboHelper (void) 255 { 256 } 257 258 void FboHelper::bindForRendering (void) 259 { 260 const glw::Functions& gl = m_renderCtx.getFunctions(); 261 gl.bindFramebuffer(GL_FRAMEBUFFER, *m_framebuffer); 262 GLU_EXPECT_NO_ERROR(gl.getError(), "glBindFramebuffer()"); 263 gl.viewport(0, 0, m_size.x(), m_size.y()); 264 GLU_EXPECT_NO_ERROR(gl.getError(), "viewport()"); 265 } 266 267 void FboHelper::readPixels (int x, int y, const tcu::PixelBufferAccess& dst) 268 { 269 const glw::Functions& gl = m_renderCtx.getFunctions(); 270 const int width = dst.getWidth(); 271 const int height = dst.getHeight(); 272 273 if (m_numSamples != 0) 274 { 275 gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, *m_resolveFramebuffer); 276 gl.blitFramebuffer(x, y, width, height, x, y, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); 277 gl.bindFramebuffer(GL_READ_FRAMEBUFFER, *m_resolveFramebuffer); 278 } 279 280 glu::readPixels(m_renderCtx, x, y, dst); 281 } 282 283 enum 284 { 285 FRAMEBUFFER_WIDTH = 256, 286 FRAMEBUFFER_HEIGHT = 256, 287 FRAMEBUFFER_FORMAT = GL_RGBA8, 288 NUM_SAMPLES_MAX = -1 289 }; 290 291 //! Verifies that gl_HelperInvocation is false in all rendered pixels. 292 class HelperInvocationValueCase : public TestCase 293 { 294 public: 295 HelperInvocationValueCase (Context& context, const char* name, const char* description, PrimitiveType primType, int numSamples); 296 ~HelperInvocationValueCase (void); 297 298 void init (void); 299 void deinit (void); 300 IterateResult iterate (void); 301 302 private: 303 const PrimitiveType m_primitiveType; 304 const int m_numSamples; 305 306 const int m_numIters; 307 const int m_numPrimitivesPerIter; 308 309 MovePtr<ShaderProgram> m_program; 310 MovePtr<FboHelper> m_fbo; 311 int m_iterNdx; 312 }; 313 314 HelperInvocationValueCase::HelperInvocationValueCase (Context& context, const char* name, const char* description, PrimitiveType primType, int numSamples) 315 : TestCase (context, name, description) 316 , m_primitiveType (primType) 317 , m_numSamples (numSamples) 318 , m_numIters (5) 319 , m_numPrimitivesPerIter (10) 320 , m_iterNdx (0) 321 { 322 } 323 324 HelperInvocationValueCase::~HelperInvocationValueCase (void) 325 { 326 deinit(); 327 } 328 329 void HelperInvocationValueCase::init (void) 330 { 331 const glu::RenderContext& renderCtx = m_context.getRenderContext(); 332 const glw::Functions& gl = renderCtx.getFunctions(); 333 const int maxSamples = getInteger(gl, GL_MAX_SAMPLES); 334 const int actualSamples = m_numSamples == NUM_SAMPLES_MAX ? maxSamples : m_numSamples; 335 336 m_program = MovePtr<ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), 337 glu::ProgramSources() 338 << glu::VertexSource( 339 "#version 310 es\n" 340 "in highp vec2 a_position;\n" 341 "uniform highp float u_pointSize;\n" 342 "void main (void)\n" 343 "{\n" 344 " gl_Position = vec4(a_position, 0.0, 1.0);\n" 345 " gl_PointSize = u_pointSize;\n" 346 "}\n") 347 << glu::FragmentSource( 348 "#version 310 es\n" 349 "out mediump vec4 o_color;\n" 350 "void main (void)\n" 351 "{\n" 352 " if (gl_HelperInvocation)\n" 353 " o_color = vec4(1.0, 0.0, 0.0, 1.0);\n" 354 " else\n" 355 " o_color = vec4(0.0, 1.0, 0.0, 1.0);\n" 356 "}\n"))); 357 358 m_testCtx.getLog() << *m_program; 359 360 if (!m_program->isOk()) 361 { 362 m_program.clear(); 363 TCU_FAIL("Compile failed"); 364 } 365 366 m_testCtx.getLog() << TestLog::Message << "Using GL_RGBA8 framebuffer with " 367 << actualSamples << " samples" << TestLog::EndMessage; 368 369 m_fbo = MovePtr<FboHelper>(new FboHelper(renderCtx, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, 370 FRAMEBUFFER_FORMAT, actualSamples)); 371 372 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 373 } 374 375 void HelperInvocationValueCase::deinit (void) 376 { 377 m_program.clear(); 378 m_fbo.clear(); 379 } 380 381 static bool verifyHelperInvocationValue (TestLog& log, const tcu::Surface& result, bool isMultiSample) 382 { 383 const tcu::RGBA bgRef (0, 0, 0, 255); 384 const tcu::RGBA fgRef (0, 255, 0, 255); 385 const tcu::RGBA threshold (1, isMultiSample ? 254 : 1, 1, 1); 386 int numInvalidPixels = 0; 387 bool renderedSomething = false; 388 389 for (int y = 0; y < result.getHeight(); ++y) 390 { 391 for (int x = 0; x < result.getWidth(); ++x) 392 { 393 const tcu::RGBA resPix = result.getPixel(x, y); 394 const bool isBg = tcu::compareThreshold(resPix, bgRef, threshold); 395 const bool isFg = tcu::compareThreshold(resPix, fgRef, threshold); 396 397 if (!isBg && !isFg) 398 numInvalidPixels += 1; 399 400 if (isFg) 401 renderedSomething = true; 402 } 403 } 404 405 if (numInvalidPixels > 0) 406 { 407 log << TestLog::Image("Result", "Result image", result); 408 log << TestLog::Message << "ERROR: Found " << numInvalidPixels << " invalid result pixels!" << TestLog::EndMessage; 409 return false; 410 } 411 else if (!renderedSomething) 412 { 413 log << TestLog::Image("Result", "Result image", result); 414 log << TestLog::Message << "ERROR: Result image was empty!" << TestLog::EndMessage; 415 return false; 416 } 417 else 418 { 419 log << TestLog::Message << "All result pixels are valid" << TestLog::EndMessage; 420 return true; 421 } 422 } 423 424 HelperInvocationValueCase::IterateResult HelperInvocationValueCase::iterate (void) 425 { 426 const glu::RenderContext& renderCtx = m_context.getRenderContext(); 427 const glw::Functions& gl = renderCtx.getFunctions(); 428 const string sectionName = string("Iteration ") + de::toString(m_iterNdx+1) + " / " + de::toString(m_numIters); 429 const tcu::ScopedLogSection section (m_testCtx.getLog(), (string("Iter") + de::toString(m_iterNdx)), sectionName); 430 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(m_iterNdx)); 431 tcu::Surface result (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT); 432 433 m_fbo->bindForRendering(); 434 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 435 gl.clear(GL_COLOR_BUFFER_BIT); 436 437 drawRandomPrimitives(renderCtx, m_program->getProgram(), m_primitiveType, m_numPrimitivesPerIter, &rnd); 438 439 m_fbo->readPixels(0, 0, result.getAccess()); 440 441 if (!verifyHelperInvocationValue(m_testCtx.getLog(), result, m_numSamples != 0)) 442 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid pixels found"); 443 444 m_iterNdx += 1; 445 return (m_iterNdx < m_numIters) ? CONTINUE : STOP; 446 } 447 448 //! Checks derivates when value depends on gl_HelperInvocation. 449 class HelperInvocationDerivateCase : public TestCase 450 { 451 public: 452 HelperInvocationDerivateCase (Context& context, const char* name, const char* description, PrimitiveType primType, int numSamples, const char* derivateFunc, bool checkAbsoluteValue); 453 ~HelperInvocationDerivateCase (void); 454 455 void init (void); 456 void deinit (void); 457 IterateResult iterate (void); 458 459 private: 460 const PrimitiveType m_primitiveType; 461 const int m_numSamples; 462 const std::string m_derivateFunc; 463 const bool m_checkAbsoluteValue; 464 465 const int m_numIters; 466 467 MovePtr<ShaderProgram> m_program; 468 MovePtr<FboHelper> m_fbo; 469 int m_iterNdx; 470 }; 471 472 HelperInvocationDerivateCase::HelperInvocationDerivateCase (Context& context, const char* name, const char* description, PrimitiveType primType, int numSamples, const char* derivateFunc, bool checkAbsoluteValue) 473 : TestCase (context, name, description) 474 , m_primitiveType (primType) 475 , m_numSamples (numSamples) 476 , m_derivateFunc (derivateFunc) 477 , m_checkAbsoluteValue (checkAbsoluteValue) 478 , m_numIters (16) 479 , m_iterNdx (0) 480 { 481 } 482 483 HelperInvocationDerivateCase::~HelperInvocationDerivateCase (void) 484 { 485 deinit(); 486 } 487 488 void HelperInvocationDerivateCase::init (void) 489 { 490 const glu::RenderContext& renderCtx = m_context.getRenderContext(); 491 const glw::Functions& gl = renderCtx.getFunctions(); 492 const int maxSamples = getInteger(gl, GL_MAX_SAMPLES); 493 const int actualSamples = m_numSamples == NUM_SAMPLES_MAX ? maxSamples : m_numSamples; 494 const std::string funcSource = (m_checkAbsoluteValue) ? ("abs(" + m_derivateFunc + "(value))") : (m_derivateFunc + "(value)"); 495 496 m_program = MovePtr<ShaderProgram>(new ShaderProgram(m_context.getRenderContext(), 497 glu::ProgramSources() 498 << glu::VertexSource( 499 "#version 310 es\n" 500 "in highp vec2 a_position;\n" 501 "uniform highp float u_pointSize;\n" 502 "void main (void)\n" 503 "{\n" 504 " gl_Position = vec4(a_position, 0.0, 1.0);\n" 505 " gl_PointSize = u_pointSize;\n" 506 "}\n") 507 << glu::FragmentSource(string( 508 "#version 310 es\n" 509 "out mediump vec4 o_color;\n" 510 "void main (void)\n" 511 "{\n" 512 " highp float value = gl_HelperInvocation ? 1.0 : 0.0;\n" 513 " highp float derivate = ") + funcSource + ";\n" 514 " if (gl_HelperInvocation)\n" 515 " o_color = vec4(1.0, 0.0, derivate, 1.0);\n" 516 " else\n" 517 " o_color = vec4(0.0, 1.0, derivate, 1.0);\n" 518 "}\n"))); 519 520 m_testCtx.getLog() << *m_program; 521 522 if (!m_program->isOk()) 523 { 524 m_program.clear(); 525 TCU_FAIL("Compile failed"); 526 } 527 528 m_testCtx.getLog() << TestLog::Message << "Using GL_RGBA8 framebuffer with " 529 << actualSamples << " samples" << TestLog::EndMessage; 530 531 m_fbo = MovePtr<FboHelper>(new FboHelper(renderCtx, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, 532 FRAMEBUFFER_FORMAT, actualSamples)); 533 534 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 535 } 536 537 void HelperInvocationDerivateCase::deinit (void) 538 { 539 m_program.clear(); 540 m_fbo.clear(); 541 } 542 543 static bool hasNeighborWithColor (const tcu::Surface& surface, int x, int y, tcu::RGBA color, tcu::RGBA threshold) 544 { 545 const int w = surface.getWidth(); 546 const int h = surface.getHeight(); 547 548 for (int dx = -1; dx < 2; dx++) 549 for (int dy = -1; dy < 2; dy++) 550 { 551 const IVec2 pos = IVec2(x + dx, y + dy); 552 553 if (dx == 0 && dy == 0) 554 continue; 555 556 if (de::inBounds(pos.x(), 0, w) && de::inBounds(pos.y(), 0, h)) 557 { 558 const tcu::RGBA neighborColor = surface.getPixel(pos.x(), pos.y()); 559 560 if (tcu::compareThreshold(color, neighborColor, threshold)) 561 return true; 562 } 563 else 564 return true; // Can't know for certain 565 } 566 567 return false; 568 } 569 570 static bool verifyHelperInvocationDerivate (TestLog& log, const tcu::Surface& result, bool isMultiSample) 571 { 572 const tcu::RGBA bgRef (0, 0, 0, 255); 573 const tcu::RGBA fgRef (0, 255, 0, 255); 574 const tcu::RGBA isBgThreshold (1, isMultiSample ? 254 : 1, 0, 1); 575 const tcu::RGBA isFgThreshold (1, isMultiSample ? 254 : 1, 255, 1); 576 int numInvalidPixels = 0; 577 int numNonZeroDeriv = 0; 578 bool renderedSomething = false; 579 580 for (int y = 0; y < result.getHeight(); ++y) 581 { 582 for (int x = 0; x < result.getWidth(); ++x) 583 { 584 const tcu::RGBA resPix = result.getPixel(x, y); 585 const bool isBg = tcu::compareThreshold(resPix, bgRef, isBgThreshold); 586 const bool isFg = tcu::compareThreshold(resPix, fgRef, isFgThreshold); 587 const bool nonZeroDeriv = resPix.getBlue() > 0; 588 const bool neighborBg = nonZeroDeriv ? hasNeighborWithColor(result, x, y, bgRef, isBgThreshold) : false; 589 590 if (nonZeroDeriv) 591 numNonZeroDeriv += 1; 592 593 if ((!isBg && !isFg) || // Neither of valid colors (ignoring blue channel that has derivate) 594 (nonZeroDeriv && !neighborBg && !isFg)) // Has non-zero derivate, but sample not at primitive edge or inside primitive 595 numInvalidPixels += 1; 596 597 if (isFg) 598 renderedSomething = true; 599 } 600 } 601 602 log << TestLog::Message << "Found " << numNonZeroDeriv << " pixels with non-zero derivate (neighbor sample has gl_HelperInvocation = true)" << TestLog::EndMessage; 603 604 if (numInvalidPixels > 0) 605 { 606 log << TestLog::Image("Result", "Result image", result); 607 log << TestLog::Message << "ERROR: Found " << numInvalidPixels << " invalid result pixels!" << TestLog::EndMessage; 608 return false; 609 } 610 else if (!renderedSomething) 611 { 612 log << TestLog::Image("Result", "Result image", result); 613 log << TestLog::Message << "ERROR: Result image was empty!" << TestLog::EndMessage; 614 return false; 615 } 616 else 617 { 618 log << TestLog::Message << "All result pixels are valid" << TestLog::EndMessage; 619 return true; 620 } 621 } 622 623 HelperInvocationDerivateCase::IterateResult HelperInvocationDerivateCase::iterate (void) 624 { 625 const glu::RenderContext& renderCtx = m_context.getRenderContext(); 626 const glw::Functions& gl = renderCtx.getFunctions(); 627 const string sectionName = string("Iteration ") + de::toString(m_iterNdx+1) + " / " + de::toString(m_numIters); 628 const tcu::ScopedLogSection section (m_testCtx.getLog(), (string("Iter") + de::toString(m_iterNdx)), sectionName); 629 de::Random rnd (deStringHash(getName()) ^ deInt32Hash(m_iterNdx)); 630 tcu::Surface result (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT); 631 632 m_fbo->bindForRendering(); 633 gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f); 634 gl.clear(GL_COLOR_BUFFER_BIT); 635 636 drawRandomPrimitives(renderCtx, m_program->getProgram(), m_primitiveType, 1, &rnd); 637 638 m_fbo->readPixels(0, 0, result.getAccess()); 639 640 if (!verifyHelperInvocationDerivate(m_testCtx.getLog(), result, m_numSamples != 0)) 641 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Invalid pixels found"); 642 643 m_iterNdx += 1; 644 return (m_iterNdx < m_numIters) ? CONTINUE : STOP; 645 } 646 647 } // anonymous 648 649 ShaderHelperInvocationTests::ShaderHelperInvocationTests (Context& context) 650 : TestCaseGroup(context, "helper_invocation", "gl_HelperInvocation tests") 651 { 652 } 653 654 ShaderHelperInvocationTests::~ShaderHelperInvocationTests (void) 655 { 656 } 657 658 void ShaderHelperInvocationTests::init (void) 659 { 660 static const struct 661 { 662 const char* caseName; 663 PrimitiveType primType; 664 } s_primTypes[] = 665 { 666 { "triangles", PRIMITIVETYPE_TRIANGLE }, 667 { "lines", PRIMITIVETYPE_LINE }, 668 { "wide_lines", PRIMITIVETYPE_WIDE_LINE }, 669 { "points", PRIMITIVETYPE_POINT }, 670 { "wide_points", PRIMITIVETYPE_WIDE_POINT } 671 }; 672 673 static const struct 674 { 675 const char* suffix; 676 int numSamples; 677 } s_sampleCounts[] = 678 { 679 { "", 0 }, 680 { "_4_samples", 4 }, 681 { "_8_samples", 8 }, 682 { "_max_samples", NUM_SAMPLES_MAX } 683 }; 684 685 // value 686 { 687 tcu::TestCaseGroup* const valueGroup = new tcu::TestCaseGroup(m_testCtx, "value", "gl_HelperInvocation value in rendered pixels"); 688 addChild(valueGroup); 689 690 for (int sampleCountNdx = 0; sampleCountNdx < DE_LENGTH_OF_ARRAY(s_sampleCounts); sampleCountNdx++) 691 { 692 for (int primTypeNdx = 0; primTypeNdx < DE_LENGTH_OF_ARRAY(s_primTypes); primTypeNdx++) 693 { 694 const string name = string(s_primTypes[primTypeNdx].caseName) + s_sampleCounts[sampleCountNdx].suffix; 695 const PrimitiveType primType = s_primTypes[primTypeNdx].primType; 696 const int numSamples = s_sampleCounts[sampleCountNdx].numSamples; 697 698 valueGroup->addChild(new HelperInvocationValueCase(m_context, name.c_str(), "", primType, numSamples)); 699 } 700 } 701 } 702 703 // derivate 704 { 705 tcu::TestCaseGroup* const derivateGroup = new tcu::TestCaseGroup(m_testCtx, "derivate", "Derivate of gl_HelperInvocation-dependent value"); 706 addChild(derivateGroup); 707 708 for (int sampleCountNdx = 0; sampleCountNdx < DE_LENGTH_OF_ARRAY(s_sampleCounts); sampleCountNdx++) 709 { 710 for (int primTypeNdx = 0; primTypeNdx < DE_LENGTH_OF_ARRAY(s_primTypes); primTypeNdx++) 711 { 712 const string name = string(s_primTypes[primTypeNdx].caseName) + s_sampleCounts[sampleCountNdx].suffix; 713 const PrimitiveType primType = s_primTypes[primTypeNdx].primType; 714 const int numSamples = s_sampleCounts[sampleCountNdx].numSamples; 715 716 derivateGroup->addChild(new HelperInvocationDerivateCase(m_context, (name + "_dfdx").c_str(), "", primType, numSamples, "dFdx", true)); 717 derivateGroup->addChild(new HelperInvocationDerivateCase(m_context, (name + "_dfdy").c_str(), "", primType, numSamples, "dFdy", true)); 718 derivateGroup->addChild(new HelperInvocationDerivateCase(m_context, (name + "_fwidth").c_str(), "", primType, numSamples, "fwidth", false)); 719 } 720 } 721 } 722 } 723 724 } // Functional 725 } // gles31 726 } // deqp 727