1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program OpenGL ES 2.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 Multisampling tests. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "es2fMultisampleTests.hpp" 25 #include "gluPixelTransfer.hpp" 26 #include "gluShaderProgram.hpp" 27 #include "tcuSurface.hpp" 28 #include "tcuImageCompare.hpp" 29 #include "tcuRenderTarget.hpp" 30 #include "tcuTestLog.hpp" 31 #include "tcuTextureUtil.hpp" 32 #include "tcuCommandLine.hpp" 33 #include "deStringUtil.hpp" 34 #include "deRandom.hpp" 35 #include "deMath.h" 36 #include "deString.h" 37 38 #include "glw.h" 39 40 #include <string> 41 #include <vector> 42 43 namespace deqp 44 { 45 namespace gles2 46 { 47 namespace Functional 48 { 49 50 using tcu::Vec2; 51 using tcu::Vec3; 52 using tcu::Vec4; 53 using tcu::IVec2; 54 using tcu::IVec4; 55 using tcu::TestLog; 56 using std::vector; 57 58 static const float SQRT_HALF = 0.707107f; 59 60 namespace 61 { 62 63 struct QuadCorners 64 { 65 Vec2 p0; 66 Vec2 p1; 67 Vec2 p2; 68 Vec2 p3; 69 70 QuadCorners(const Vec2& p0_, const Vec2& p1_, const Vec2& p2_, const Vec2& p3_) : p0(p0_), p1(p1_), p2(p2_), p3(p3_) {} 71 }; 72 73 } // anonymous 74 75 static inline int getIterationCount (const tcu::TestContext& ctx, int defaultCount) 76 { 77 int cmdLineValue = ctx.getCommandLine().getTestIterationCount(); 78 return cmdLineValue > 0 ? cmdLineValue : defaultCount; 79 } 80 81 static inline int getGLInteger (GLenum name) 82 { 83 int result; 84 GLU_CHECK_CALL(glGetIntegerv(name, &result)); 85 return result; 86 } 87 88 template<typename T> 89 static inline T min4 (T a, T b, T c, T d) 90 { 91 return de::min(de::min(de::min(a, b), c), d); 92 } 93 94 template<typename T> 95 static inline T max4 (T a, T b, T c, T d) 96 { 97 return de::max(de::max(de::max(a, b), c), d); 98 } 99 100 static inline bool isInsideQuad (const IVec2& point, const IVec2& p0, const IVec2& p1, const IVec2& p2, const IVec2& p3) 101 { 102 int dot0 = (point.x()-p0.x()) * (p1.y()-p0.y()) + (point.y()-p0.y()) * (p0.x()-p1.x()); 103 int dot1 = (point.x()-p1.x()) * (p2.y()-p1.y()) + (point.y()-p1.y()) * (p1.x()-p2.x()); 104 int dot2 = (point.x()-p2.x()) * (p3.y()-p2.y()) + (point.y()-p2.y()) * (p2.x()-p3.x()); 105 int dot3 = (point.x()-p3.x()) * (p0.y()-p3.y()) + (point.y()-p3.y()) * (p3.x()-p0.x()); 106 107 return (dot0 > 0) == (dot1 > 0) && (dot1 > 0) == (dot2 > 0) && (dot2 > 0) == (dot3 > 0); 108 } 109 110 /*--------------------------------------------------------------------*//*! 111 * \brief Check if a region in an image is unicolored. 112 * 113 * Checks if the pixels in img inside the convex quadilateral defined by 114 * p0, p1, p2 and p3 are all (approximately) of the same color. 115 *//*--------------------------------------------------------------------*/ 116 static bool isPixelRegionUnicolored (const tcu::Surface& img, const IVec2& p0, const IVec2& p1, const IVec2& p2, const IVec2& p3) 117 { 118 int xMin = de::clamp(min4(p0.x(), p1.x(), p2.x(), p3.x()), 0, img.getWidth()-1); 119 int yMin = de::clamp(min4(p0.y(), p1.y(), p2.y(), p3.y()), 0, img.getHeight()-1); 120 int xMax = de::clamp(max4(p0.x(), p1.x(), p2.x(), p3.x()), 0, img.getWidth()-1); 121 int yMax = de::clamp(max4(p0.y(), p1.y(), p2.y(), p3.y()), 0, img.getHeight()-1); 122 bool insideEncountered = false; //!< Whether we have already seen at least one pixel inside the region. 123 tcu::RGBA insideColor; //!< Color of the first pixel inside the region. 124 125 for (int y = yMin; y <= yMax; y++) 126 for (int x = xMin; x <= xMax; x++) 127 { 128 if (isInsideQuad(IVec2(x, y), p0, p1, p2, p3)) 129 { 130 tcu::RGBA pixColor = img.getPixel(x, y); 131 132 if (insideEncountered) 133 { 134 if (!tcu::compareThreshold(pixColor, insideColor, tcu::RGBA(3, 3, 3, 3))) // Pixel color differs from already-detected color inside same region - region not unicolored. 135 return false; 136 } 137 else 138 { 139 insideEncountered = true; 140 insideColor = pixColor; 141 } 142 } 143 } 144 145 return true; 146 } 147 148 static bool drawUnicolorTestErrors (tcu::Surface& img, const tcu::PixelBufferAccess& errorImg, const IVec2& p0, const IVec2& p1, const IVec2& p2, const IVec2& p3) 149 { 150 int xMin = de::clamp(min4(p0.x(), p1.x(), p2.x(), p3.x()), 0, img.getWidth()-1); 151 int yMin = de::clamp(min4(p0.y(), p1.y(), p2.y(), p3.y()), 0, img.getHeight()-1); 152 int xMax = de::clamp(max4(p0.x(), p1.x(), p2.x(), p3.x()), 0, img.getWidth()-1); 153 int yMax = de::clamp(max4(p0.y(), p1.y(), p2.y(), p3.y()), 0, img.getHeight()-1); 154 tcu::RGBA refColor = img.getPixel((xMin + xMax) / 2, (yMin + yMax) / 2); 155 156 for (int y = yMin; y <= yMax; y++) 157 for (int x = xMin; x <= xMax; x++) 158 { 159 if (isInsideQuad(IVec2(x, y), p0, p1, p2, p3)) 160 { 161 if (!tcu::compareThreshold(img.getPixel(x, y), refColor, tcu::RGBA(3, 3, 3, 3))) 162 { 163 img.setPixel(x, y, tcu::RGBA::red()); 164 errorImg.setPixel(Vec4(1.0f, 0.0f, 0.0f, 1.0f), x, y); 165 } 166 } 167 } 168 169 return true; 170 } 171 172 /*--------------------------------------------------------------------*//*! 173 * \brief Abstract base class handling common stuff for multisample cases. 174 *//*--------------------------------------------------------------------*/ 175 class MultisampleCase : public TestCase 176 { 177 public: 178 MultisampleCase (Context& context, const char* name, const char* desc); 179 virtual ~MultisampleCase (void); 180 181 virtual void init (void); 182 virtual void deinit (void); 183 184 protected: 185 virtual int getDesiredViewportSize (void) const = 0; 186 187 void renderTriangle (const Vec3& p0, const Vec3& p1, const Vec3& p2, const Vec4& c0, const Vec4& c1, const Vec4& c2) const; 188 void renderTriangle (const Vec3& p0, const Vec3& p1, const Vec3& p2, const Vec4& color) const; 189 void renderTriangle (const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec4& c0, const Vec4& c1, const Vec4& c2) const; 190 void renderTriangle (const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec4& color) const; 191 void renderQuad (const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec2& p3, const Vec4& c0, const Vec4& c1, const Vec4& c2, const Vec4& c3) const; 192 void renderQuad (const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec2& p3, const Vec4& color) const; 193 void renderLine (const Vec2& p0, const Vec2& p1, const Vec4& color) const; 194 195 void randomizeViewport (void); 196 void readImage (tcu::Surface& dst) const; 197 198 int m_numSamples; 199 200 int m_viewportSize; 201 202 private: 203 MultisampleCase (const MultisampleCase& other); 204 MultisampleCase& operator= (const MultisampleCase& other); 205 206 glu::ShaderProgram* m_program; 207 int m_attrPositionLoc; 208 int m_attrColorLoc; 209 210 int m_viewportX; 211 int m_viewportY; 212 de::Random m_rnd; 213 }; 214 215 MultisampleCase::MultisampleCase (Context& context, const char* name, const char* desc) 216 : TestCase (context, name, desc) 217 , m_numSamples (0) 218 , m_viewportSize (0) 219 , m_program (DE_NULL) 220 , m_attrPositionLoc (-1) 221 , m_attrColorLoc (-1) 222 , m_viewportX (0) 223 , m_viewportY (0) 224 , m_rnd (deStringHash(name)) 225 { 226 } 227 228 void MultisampleCase::renderTriangle (const Vec3& p0, const Vec3& p1, const Vec3& p2, const Vec4& c0, const Vec4& c1, const Vec4& c2) const 229 { 230 float vertexPositions[] = 231 { 232 p0.x(), p0.y(), p0.z(), 1.0f, 233 p1.x(), p1.y(), p1.z(), 1.0f, 234 p2.x(), p2.y(), p2.z(), 1.0f 235 }; 236 float vertexColors[] = 237 { 238 c0.x(), c0.y(), c0.z(), c0.w(), 239 c1.x(), c1.y(), c1.z(), c1.w(), 240 c2.x(), c2.y(), c2.z(), c2.w(), 241 }; 242 243 GLU_CHECK_CALL(glEnableVertexAttribArray(m_attrPositionLoc)); 244 GLU_CHECK_CALL(glVertexAttribPointer(m_attrPositionLoc, 4, GL_FLOAT, false, 0, &vertexPositions[0])); 245 246 GLU_CHECK_CALL(glEnableVertexAttribArray(m_attrColorLoc)); 247 GLU_CHECK_CALL(glVertexAttribPointer(m_attrColorLoc, 4, GL_FLOAT, false, 0, &vertexColors[0])); 248 249 GLU_CHECK_CALL(glUseProgram(m_program->getProgram())); 250 GLU_CHECK_CALL(glDrawArrays(GL_TRIANGLES, 0, 3)); 251 } 252 253 void MultisampleCase::renderTriangle (const Vec3& p0, const Vec3& p1, const Vec3& p2, const Vec4& color) const 254 { 255 renderTriangle(p0, p1, p2, color, color, color); 256 } 257 258 void MultisampleCase::renderTriangle (const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec4& c0, const Vec4& c1, const Vec4& c2) const 259 { 260 renderTriangle(Vec3(p0.x(), p0.y(), 0.0f), 261 Vec3(p1.x(), p1.y(), 0.0f), 262 Vec3(p2.x(), p2.y(), 0.0f), 263 c0, c1, c2); 264 } 265 266 void MultisampleCase::renderTriangle (const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec4& color) const 267 { 268 renderTriangle(p0, p1, p2, color, color, color); 269 } 270 271 void MultisampleCase::renderQuad (const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec2& p3, const Vec4& c0, const Vec4& c1, const Vec4& c2, const Vec4& c3) const 272 { 273 renderTriangle(p0, p1, p2, c0, c1, c2); 274 renderTriangle(p2, p1, p3, c2, c1, c3); 275 } 276 277 void MultisampleCase::renderQuad (const Vec2& p0, const Vec2& p1, const Vec2& p2, const Vec2& p3, const Vec4& color) const 278 { 279 renderQuad(p0, p1, p2, p3, color, color, color, color); 280 } 281 282 void MultisampleCase::renderLine (const Vec2& p0, const Vec2& p1, const Vec4& color) const 283 { 284 float vertexPositions[] = 285 { 286 p0.x(), p0.y(), 0.0f, 1.0f, 287 p1.x(), p1.y(), 0.0f, 1.0f 288 }; 289 float vertexColors[] = 290 { 291 color.x(), color.y(), color.z(), color.w(), 292 color.x(), color.y(), color.z(), color.w() 293 }; 294 295 GLU_CHECK_CALL(glEnableVertexAttribArray(m_attrPositionLoc)); 296 GLU_CHECK_CALL(glVertexAttribPointer(m_attrPositionLoc, 4, GL_FLOAT, false, 0, &vertexPositions[0])); 297 298 GLU_CHECK_CALL(glEnableVertexAttribArray(m_attrColorLoc)); 299 GLU_CHECK_CALL(glVertexAttribPointer(m_attrColorLoc, 4, GL_FLOAT, false, 0, &vertexColors[0])); 300 301 GLU_CHECK_CALL(glUseProgram(m_program->getProgram())); 302 GLU_CHECK_CALL(glDrawArrays(GL_LINES, 0, 2)); 303 } 304 305 void MultisampleCase::randomizeViewport (void) 306 { 307 m_viewportX = m_rnd.getInt(0, m_context.getRenderTarget().getWidth() - m_viewportSize); 308 m_viewportY = m_rnd.getInt(0, m_context.getRenderTarget().getHeight() - m_viewportSize); 309 310 GLU_CHECK_CALL(glViewport(m_viewportX, m_viewportY, m_viewportSize, m_viewportSize)); 311 } 312 313 void MultisampleCase::readImage (tcu::Surface& dst) const 314 { 315 glu::readPixels(m_context.getRenderContext(), m_viewportX, m_viewportY, dst.getAccess()); 316 } 317 318 void MultisampleCase::init (void) 319 { 320 static const char* vertShaderSource = 321 "attribute highp vec4 a_position;\n" 322 "attribute mediump vec4 a_color;\n" 323 "varying mediump vec4 v_color;\n" 324 "void main()\n" 325 "{\n" 326 " gl_Position = a_position;\n" 327 " v_color = a_color;\n" 328 "}\n"; 329 330 static const char* fragShaderSource = 331 "varying mediump vec4 v_color;\n" 332 "void main()\n" 333 "{\n" 334 " gl_FragColor = v_color;\n" 335 "}\n"; 336 337 // Check multisample support. 338 339 if (m_context.getRenderTarget().getNumSamples() <= 1) 340 throw tcu::NotSupportedError("No multisample buffers"); 341 342 // Prepare program. 343 344 DE_ASSERT(!m_program); 345 346 m_program = new glu::ShaderProgram(m_context.getRenderContext(), glu::makeVtxFragSources(vertShaderSource, fragShaderSource)); 347 if (!m_program->isOk()) 348 throw tcu::TestError("Failed to compile program", DE_NULL, __FILE__, __LINE__); 349 350 GLU_CHECK_CALL(m_attrPositionLoc = glGetAttribLocation(m_program->getProgram(), "a_position")); 351 GLU_CHECK_CALL(m_attrColorLoc = glGetAttribLocation(m_program->getProgram(), "a_color")); 352 353 if (m_attrPositionLoc < 0 || m_attrColorLoc < 0) 354 { 355 delete m_program; 356 throw tcu::TestError("Invalid attribute locations", DE_NULL, __FILE__, __LINE__); 357 } 358 359 // Get suitable viewport size. 360 361 m_viewportSize = de::min<int>(getDesiredViewportSize(), de::min(m_context.getRenderTarget().getWidth(), m_context.getRenderTarget().getHeight())); 362 randomizeViewport(); 363 364 // Query and log number of samples per pixel. 365 366 m_numSamples = getGLInteger(GL_SAMPLES); 367 m_testCtx.getLog() << TestLog::Message << "GL_SAMPLES = " << m_numSamples << TestLog::EndMessage; 368 } 369 370 MultisampleCase::~MultisampleCase (void) 371 { 372 delete m_program; 373 } 374 375 void MultisampleCase::deinit (void) 376 { 377 delete m_program; 378 379 m_program = DE_NULL; 380 } 381 382 /*--------------------------------------------------------------------*//*! 383 * \brief Base class for cases testing the value of GL_SAMPLES. 384 * 385 * Draws a test pattern (defined by renderPattern() of an inheriting class) 386 * and counts the number of distinct colors in the resulting image. That 387 * number should be at least the value of GL_SAMPLES plus one. This is 388 * repeated with increased values of m_currentIteration until this correct 389 * number of colors is detected or m_currentIteration reaches 390 * m_maxNumIterations. 391 *//*--------------------------------------------------------------------*/ 392 class NumSamplesCase : public MultisampleCase 393 { 394 public: 395 NumSamplesCase (Context& context, const char* name, const char* description); 396 ~NumSamplesCase (void) {} 397 398 IterateResult iterate (void); 399 400 protected: 401 int getDesiredViewportSize (void) const { return 256; } 402 virtual void renderPattern (void) const = 0; 403 404 int m_currentIteration; 405 406 private: 407 enum { DEFAULT_MAX_NUM_ITERATIONS = 16 }; 408 409 const int m_maxNumIterations; 410 vector<tcu::RGBA> m_detectedColors; 411 }; 412 413 NumSamplesCase::NumSamplesCase (Context& context, const char* name, const char* description) 414 : MultisampleCase (context, name, description) 415 , m_currentIteration (0) 416 , m_maxNumIterations (getIterationCount(m_testCtx, DEFAULT_MAX_NUM_ITERATIONS)) 417 { 418 } 419 420 NumSamplesCase::IterateResult NumSamplesCase::iterate (void) 421 { 422 TestLog& log = m_testCtx.getLog(); 423 tcu::Surface renderedImg (m_viewportSize, m_viewportSize); 424 425 randomizeViewport(); 426 427 GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); 428 GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT)); 429 430 renderPattern(); 431 432 // Read and log rendered image. 433 434 readImage(renderedImg); 435 436 log << TestLog::Image("RenderedImage", "Rendered image", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG); 437 438 // Detect new, previously unseen colors from image. 439 440 int requiredNumDistinctColors = m_numSamples + 1; 441 442 for (int y = 0; y < renderedImg.getHeight() && (int)m_detectedColors.size() < requiredNumDistinctColors; y++) 443 for (int x = 0; x < renderedImg.getWidth() && (int)m_detectedColors.size() < requiredNumDistinctColors; x++) 444 { 445 tcu::RGBA color = renderedImg.getPixel(x, y); 446 447 int i; 448 for (i = 0; i < (int)m_detectedColors.size(); i++) 449 { 450 if (tcu::compareThreshold(color, m_detectedColors[i], tcu::RGBA(3, 3, 3, 3))) 451 break; 452 } 453 454 if (i == (int)m_detectedColors.size()) 455 m_detectedColors.push_back(color); // Color not previously detected. 456 } 457 458 // Log results. 459 460 log << TestLog::Message 461 << "Number of distinct colors detected so far: " 462 << ((int)m_detectedColors.size() >= requiredNumDistinctColors ? "at least " : "") 463 << de::toString(m_detectedColors.size()) 464 << TestLog::EndMessage; 465 466 if ((int)m_detectedColors.size() < requiredNumDistinctColors) 467 { 468 // Haven't detected enough different colors yet. 469 470 m_currentIteration++; 471 472 if (m_currentIteration >= m_maxNumIterations) 473 { 474 log << TestLog::Message << "Failure: Number of distinct colors detected is lower than GL_SAMPLES+1" << TestLog::EndMessage; 475 m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed"); 476 return STOP; 477 } 478 else 479 { 480 log << TestLog::Message << "The number of distinct colors detected is lower than GL_SAMPLES+1 - trying again with a slightly altered pattern" << TestLog::EndMessage; 481 return CONTINUE; 482 } 483 } 484 else 485 { 486 log << TestLog::Message << "Success: The number of distinct colors detected is at least GL_SAMPLES+1" << TestLog::EndMessage; 487 m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed"); 488 return STOP; 489 } 490 } 491 492 class PolygonNumSamplesCase : public NumSamplesCase 493 { 494 public: 495 PolygonNumSamplesCase (Context& context, const char* name, const char* description); 496 ~PolygonNumSamplesCase (void) {} 497 498 protected: 499 void renderPattern (void) const; 500 }; 501 502 PolygonNumSamplesCase::PolygonNumSamplesCase (Context& context, const char* name, const char* description) 503 : NumSamplesCase(context, name, description) 504 { 505 } 506 507 void PolygonNumSamplesCase::renderPattern (void) const 508 { 509 // The test pattern consists of several triangles with edges at different angles. 510 511 const int numTriangles = 25; 512 for (int i = 0; i < numTriangles; i++) 513 { 514 float angle0 = 2.0f*DE_PI * (float)i / (float)numTriangles + 0.001f*(float)m_currentIteration; 515 float angle1 = 2.0f*DE_PI * ((float)i + 0.5f) / (float)numTriangles + 0.001f*(float)m_currentIteration; 516 517 renderTriangle(Vec2(0.0f, 0.0f), 518 Vec2(deFloatCos(angle0)*0.95f, deFloatSin(angle0)*0.95f), 519 Vec2(deFloatCos(angle1)*0.95f, deFloatSin(angle1)*0.95f), 520 Vec4(1.0f)); 521 } 522 } 523 524 class LineNumSamplesCase : public NumSamplesCase 525 { 526 public: 527 LineNumSamplesCase (Context& context, const char* name, const char* description); 528 ~LineNumSamplesCase (void) {} 529 530 protected: 531 void renderPattern (void) const; 532 }; 533 534 LineNumSamplesCase::LineNumSamplesCase (Context& context, const char* name, const char* description) 535 : NumSamplesCase (context, name, description) 536 { 537 } 538 539 void LineNumSamplesCase::renderPattern (void) const 540 { 541 // The test pattern consists of several lines at different angles. 542 543 // We scale the number of lines based on the viewport size. This is because a gl line's thickness is 544 // constant in pixel units, i.e. they get relatively thicker as viewport size decreases. Thus we must 545 // decrease the number of lines in order to decrease the extent of overlap among the lines in the 546 // center of the pattern. 547 const int numLines = (int)(100.0f * deFloatSqrt((float)m_viewportSize / 256.0f)); 548 549 for (int i = 0; i < numLines; i++) 550 { 551 float angle = 2.0f*DE_PI * (float)i / (float)numLines + 0.001f*(float)m_currentIteration; 552 renderLine(Vec2(0.0f, 0.0f), Vec2(deFloatCos(angle)*0.95f, deFloatSin(angle)*0.95f), Vec4(1.0f)); 553 } 554 } 555 556 /*--------------------------------------------------------------------*//*! 557 * \brief Case testing behaviour of common edges when multisampling. 558 * 559 * Draws a number of test patterns, each with a number of quads, each made 560 * of two triangles, rotated at different angles. The inner edge inside the 561 * quad (i.e. the common edge of the two triangles) still should not be 562 * visible, despite multisampling - i.e. the two triangles forming the quad 563 * should never get any common coverage bits in any pixel. 564 *//*--------------------------------------------------------------------*/ 565 class CommonEdgeCase : public MultisampleCase 566 { 567 public: 568 enum CaseType 569 { 570 CASETYPE_SMALL_QUADS = 0, //!< Draw several small quads per iteration. 571 CASETYPE_BIGGER_THAN_VIEWPORT_QUAD, //!< Draw one bigger-than-viewport quad per iteration. 572 CASETYPE_FIT_VIEWPORT_QUAD, //!< Draw one exactly viewport-sized, axis aligned quad per iteration. 573 574 CASETYPE_LAST 575 }; 576 577 CommonEdgeCase (Context& context, const char* name, const char* description, CaseType caseType); 578 ~CommonEdgeCase (void) {} 579 580 void init (void); 581 582 IterateResult iterate (void); 583 584 protected: 585 int getDesiredViewportSize (void) const { return m_caseType == CASETYPE_SMALL_QUADS ? 128 : 32; } 586 587 private: 588 enum 589 { 590 DEFAULT_SMALL_QUADS_ITERATIONS = 16, 591 DEFAULT_BIGGER_THAN_VIEWPORT_QUAD_ITERATIONS = 8*8 592 // \note With CASETYPE_FIT_VIEWPORT_QUAD, we don't do rotations other than multiples of 90 deg -> constant number of iterations. 593 }; 594 595 const CaseType m_caseType; 596 597 const int m_numIterations; 598 int m_currentIteration; 599 }; 600 601 CommonEdgeCase::CommonEdgeCase (Context& context, const char* name, const char* description, CaseType caseType) 602 : MultisampleCase (context, name, description) 603 , m_caseType (caseType) 604 , m_numIterations (caseType == CASETYPE_SMALL_QUADS ? getIterationCount(m_testCtx, DEFAULT_SMALL_QUADS_ITERATIONS) 605 : caseType == CASETYPE_BIGGER_THAN_VIEWPORT_QUAD ? getIterationCount(m_testCtx, DEFAULT_BIGGER_THAN_VIEWPORT_QUAD_ITERATIONS) 606 : 8) 607 , m_currentIteration (0) 608 { 609 } 610 611 void CommonEdgeCase::init (void) 612 { 613 MultisampleCase::init(); 614 615 if (m_caseType == CASETYPE_SMALL_QUADS) 616 { 617 // Check for a big enough viewport. With too small viewports the test case can't analyze the resulting image well enough. 618 619 const int minViewportSize = 32; 620 621 DE_ASSERT(minViewportSize <= getDesiredViewportSize()); 622 623 if (m_viewportSize < minViewportSize) 624 throw tcu::InternalError("Render target width or height too low (is " + de::toString(m_viewportSize) + ", should be at least " + de::toString(minViewportSize) + ")"); 625 } 626 627 GLU_CHECK_CALL(glEnable(GL_BLEND)); 628 GLU_CHECK_CALL(glBlendEquation(GL_FUNC_ADD)); 629 GLU_CHECK_CALL(glBlendFunc(GL_ONE, GL_ONE)); 630 631 m_testCtx.getLog() << TestLog::Message << "Additive blending enabled in order to detect (erroneously) overlapping samples" << TestLog::EndMessage; 632 } 633 634 CommonEdgeCase::IterateResult CommonEdgeCase::iterate (void) 635 { 636 TestLog& log = m_testCtx.getLog(); 637 tcu::Surface renderedImg (m_viewportSize, m_viewportSize); 638 tcu::Surface errorImg (m_viewportSize, m_viewportSize); 639 640 randomizeViewport(); 641 642 GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); 643 GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT)); 644 645 // Draw test pattern. Test patterns consist of quads formed with two triangles. 646 // After drawing the pattern, we check that the interior pixels of each quad are 647 // all the same color - this is meant to verify that there are no artifacts on the inner edge. 648 649 vector<QuadCorners> unicoloredRegions; 650 651 if (m_caseType == CASETYPE_SMALL_QUADS) 652 { 653 // Draw several quads, rotated at different angles. 654 655 const float quadDiagLen = 2.0f / 3.0f * 0.9f; // \note Fit 3 quads in both x and y directions. 656 float angleCos; 657 float angleSin; 658 659 // \note First and second iteration get exact 0 (and 90, 180, 270) and 45 (and 135, 225, 315) angle quads, as they are kind of a special case. 660 661 if (m_currentIteration == 0) 662 { 663 angleCos = 1.0f; 664 angleSin = 0.0f; 665 } 666 else if (m_currentIteration == 1) 667 { 668 angleCos = SQRT_HALF; 669 angleSin = SQRT_HALF; 670 } 671 else 672 { 673 float angle = 0.5f * DE_PI * (float)(m_currentIteration-1) / (float)(m_numIterations-1); 674 angleCos = deFloatCos(angle); 675 angleSin = deFloatSin(angle); 676 } 677 678 Vec2 corners[4] = 679 { 680 0.5f * quadDiagLen * Vec2( angleCos, angleSin), 681 0.5f * quadDiagLen * Vec2(-angleSin, angleCos), 682 0.5f * quadDiagLen * Vec2(-angleCos, -angleSin), 683 0.5f * quadDiagLen * Vec2( angleSin, -angleCos) 684 }; 685 686 unicoloredRegions.reserve(8); 687 688 // Draw 8 quads. 689 // First four are rotated at angles angle+0, angle+90, angle+180 and angle+270. 690 // Last four are rotated the same angles as the first four, but the ordering of the last triangle's vertices is reversed. 691 692 for (int quadNdx = 0; quadNdx < 8; quadNdx++) 693 { 694 Vec2 center = (2.0f-quadDiagLen) * Vec2((float)(quadNdx%3), (float)(quadNdx/3)) / 2.0f - 0.5f*(2.0f-quadDiagLen); 695 696 renderTriangle(corners[(0+quadNdx) % 4] + center, 697 corners[(1+quadNdx) % 4] + center, 698 corners[(2+quadNdx) % 4] + center, 699 Vec4(0.5f, 0.5f, 0.5f, 1.0f)); 700 701 if (quadNdx >= 4) 702 { 703 renderTriangle(corners[(3+quadNdx) % 4] + center, 704 corners[(2+quadNdx) % 4] + center, 705 corners[(0+quadNdx) % 4] + center, 706 Vec4(0.5f, 0.5f, 0.5f, 1.0f)); 707 } 708 else 709 { 710 renderTriangle(corners[(0+quadNdx) % 4] + center, 711 corners[(2+quadNdx) % 4] + center, 712 corners[(3+quadNdx) % 4] + center, 713 Vec4(0.5f, 0.5f, 0.5f, 1.0f)); 714 } 715 716 // The size of the "interior" of a quad is assumed to be approximately unicolorRegionScale*<actual size of quad>. 717 // By "interior" we here mean the region of non-boundary pixels of the rendered quad for which we can safely assume 718 // that it has all coverage bits set to 1, for every pixel. 719 float unicolorRegionScale = 1.0f - 6.0f*2.0f / (float)m_viewportSize / quadDiagLen; 720 unicoloredRegions.push_back(QuadCorners((center + corners[0]*unicolorRegionScale), 721 (center + corners[1]*unicolorRegionScale), 722 (center + corners[2]*unicolorRegionScale), 723 (center + corners[3]*unicolorRegionScale))); 724 } 725 } 726 else if (m_caseType == CASETYPE_BIGGER_THAN_VIEWPORT_QUAD) 727 { 728 // Draw a bigger-than-viewport quad, rotated at an angle depending on m_currentIteration. 729 730 int quadBaseAngleNdx = m_currentIteration / 8; 731 int quadSubAngleNdx = m_currentIteration % 8; 732 float angleCos; 733 float angleSin; 734 735 if (quadBaseAngleNdx == 0) 736 { 737 angleCos = 1.0f; 738 angleSin = 0.0f; 739 } 740 else if (quadBaseAngleNdx == 1) 741 { 742 angleCos = SQRT_HALF; 743 angleSin = SQRT_HALF; 744 } 745 else 746 { 747 float angle = 0.5f * DE_PI * (float)(m_currentIteration-1) / (float)(m_numIterations-1); 748 angleCos = deFloatCos(angle); 749 angleSin = deFloatSin(angle); 750 } 751 752 float quadDiagLen = 2.5f / de::max(angleCos, angleSin); 753 754 Vec2 corners[4] = 755 { 756 0.5f * quadDiagLen * Vec2( angleCos, angleSin), 757 0.5f * quadDiagLen * Vec2(-angleSin, angleCos), 758 0.5f * quadDiagLen * Vec2(-angleCos, -angleSin), 759 0.5f * quadDiagLen * Vec2( angleSin, -angleCos) 760 }; 761 762 renderTriangle(corners[(0+quadSubAngleNdx) % 4], 763 corners[(1+quadSubAngleNdx) % 4], 764 corners[(2+quadSubAngleNdx) % 4], 765 Vec4(0.5f, 0.5f, 0.5f, 1.0f)); 766 767 if (quadSubAngleNdx >= 4) 768 { 769 renderTriangle(corners[(3+quadSubAngleNdx) % 4], 770 corners[(2+quadSubAngleNdx) % 4], 771 corners[(0+quadSubAngleNdx) % 4], 772 Vec4(0.5f, 0.5f, 0.5f, 1.0f)); 773 } 774 else 775 { 776 renderTriangle(corners[(0+quadSubAngleNdx) % 4], 777 corners[(2+quadSubAngleNdx) % 4], 778 corners[(3+quadSubAngleNdx) % 4], 779 Vec4(0.5f, 0.5f, 0.5f, 1.0f)); 780 } 781 782 float unicolorRegionScale = 1.0f - 6.0f*2.0f / (float)m_viewportSize / quadDiagLen; 783 unicoloredRegions.push_back(QuadCorners((corners[0]*unicolorRegionScale), 784 (corners[1]*unicolorRegionScale), 785 (corners[2]*unicolorRegionScale), 786 (corners[3]*unicolorRegionScale))); 787 } 788 else if (m_caseType == CASETYPE_FIT_VIEWPORT_QUAD) 789 { 790 // Draw an exactly viewport-sized quad, rotated by multiples of 90 degrees angle depending on m_currentIteration. 791 792 int quadSubAngleNdx = m_currentIteration % 8; 793 794 Vec2 corners[4] = 795 { 796 Vec2( 1.0f, 1.0f), 797 Vec2(-1.0f, 1.0f), 798 Vec2(-1.0f, -1.0f), 799 Vec2( 1.0f, -1.0f) 800 }; 801 802 renderTriangle(corners[(0+quadSubAngleNdx) % 4], 803 corners[(1+quadSubAngleNdx) % 4], 804 corners[(2+quadSubAngleNdx) % 4], 805 Vec4(0.5f, 0.5f, 0.5f, 1.0f)); 806 807 if (quadSubAngleNdx >= 4) 808 { 809 renderTriangle(corners[(3+quadSubAngleNdx) % 4], 810 corners[(2+quadSubAngleNdx) % 4], 811 corners[(0+quadSubAngleNdx) % 4], 812 Vec4(0.5f, 0.5f, 0.5f, 1.0f)); 813 } 814 else 815 { 816 renderTriangle(corners[(0+quadSubAngleNdx) % 4], 817 corners[(2+quadSubAngleNdx) % 4], 818 corners[(3+quadSubAngleNdx) % 4], 819 Vec4(0.5f, 0.5f, 0.5f, 1.0f)); 820 } 821 822 unicoloredRegions.push_back(QuadCorners(corners[0], corners[1], corners[2], corners[3])); 823 } 824 else 825 DE_ASSERT(false); 826 827 // Read pixels and check unicolored regions. 828 829 readImage(renderedImg); 830 831 tcu::clear(errorImg.getAccess(), Vec4(0.0f, 1.0f, 0.0f, 1.0f)); 832 833 log << TestLog::Image("RenderedImage", "Rendered image", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG); 834 835 bool errorsDetected = false; 836 for (int i = 0; i < (int)unicoloredRegions.size(); i++) 837 { 838 const QuadCorners& region = unicoloredRegions[i]; 839 IVec2 p0Win = ((region.p0+1.0f) * 0.5f * (float)(m_viewportSize-1) + 0.5f).asInt(); 840 IVec2 p1Win = ((region.p1+1.0f) * 0.5f * (float)(m_viewportSize-1) + 0.5f).asInt(); 841 IVec2 p2Win = ((region.p2+1.0f) * 0.5f * (float)(m_viewportSize-1) + 0.5f).asInt(); 842 IVec2 p3Win = ((region.p3+1.0f) * 0.5f * (float)(m_viewportSize-1) + 0.5f).asInt(); 843 bool errorsInCurrentRegion = !isPixelRegionUnicolored(renderedImg, p0Win, p1Win, p2Win, p3Win); 844 845 if (errorsInCurrentRegion) 846 drawUnicolorTestErrors(renderedImg, errorImg.getAccess(), p0Win, p1Win, p2Win, p3Win); 847 848 errorsDetected = errorsDetected || errorsInCurrentRegion; 849 } 850 851 m_currentIteration++; 852 853 if (errorsDetected) 854 { 855 log << TestLog::Message << "Failure: Not all quad interiors seem unicolored - common-edge artifacts?" << TestLog::EndMessage; 856 log << TestLog::Message << "Erroneous pixels are drawn red in the following image" << TestLog::EndMessage; 857 log << TestLog::Image("RenderedImageWithErrors", "Rendered image with errors marked", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG); 858 log << TestLog::Image("ErrorsOnly", "Image with error pixels only", errorImg, QP_IMAGE_COMPRESSION_MODE_PNG); 859 m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed"); 860 return STOP; 861 } 862 else if (m_currentIteration < m_numIterations) 863 { 864 log << TestLog::Message << "Quads seem OK - moving on to next pattern" << TestLog::EndMessage; 865 return CONTINUE; 866 } 867 else 868 { 869 log << TestLog::Message << "Success: All quad interiors seem unicolored (no common-edge artifacts)" << TestLog::EndMessage; 870 m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed"); 871 return STOP; 872 } 873 } 874 875 /*--------------------------------------------------------------------*//*! 876 * \brief Test that depth values are per-sample. 877 * 878 * Draws intersecting, differently-colored polygons and checks that there 879 * are at least GL_SAMPLES+1 distinct colors present, due to some of the 880 * samples at the intersection line belonging to one and some to another 881 * polygon. 882 *//*--------------------------------------------------------------------*/ 883 class SampleDepthCase : public NumSamplesCase 884 { 885 public: 886 SampleDepthCase (Context& context, const char* name, const char* description); 887 ~SampleDepthCase (void) {} 888 889 void init (void); 890 891 protected: 892 void renderPattern (void) const; 893 }; 894 895 SampleDepthCase::SampleDepthCase (Context& context, const char* name, const char* description) 896 : NumSamplesCase (context, name, description) 897 { 898 } 899 900 void SampleDepthCase::init (void) 901 { 902 TestLog& log = m_testCtx.getLog(); 903 904 if (m_context.getRenderTarget().getDepthBits() == 0) 905 TCU_THROW(NotSupportedError, "Test requires depth buffer"); 906 907 MultisampleCase::init(); 908 909 GLU_CHECK_CALL(glEnable(GL_DEPTH_TEST)); 910 GLU_CHECK_CALL(glDepthFunc(GL_LESS)); 911 912 log << TestLog::Message << "Depth test enabled, depth func is GL_LESS" << TestLog::EndMessage; 913 log << TestLog::Message << "Drawing several bigger-than-viewport black or white polygons intersecting each other" << TestLog::EndMessage; 914 } 915 916 void SampleDepthCase::renderPattern (void) const 917 { 918 GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); 919 GLU_CHECK_CALL(glClearDepthf(1.0f)); 920 GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); 921 922 { 923 const int numPolygons = 50; 924 925 for (int i = 0; i < numPolygons; i++) 926 { 927 Vec4 color = i % 2 == 0 ? Vec4(1.0f, 1.0f, 1.0f, 1.0f) : Vec4(0.0f, 0.0f, 0.0f, 1.0f); 928 float angle = 2.0f * DE_PI * (float)i / (float)numPolygons + 0.001f*(float)m_currentIteration; 929 Vec3 pt0 (3.0f*deFloatCos(angle + 2.0f*DE_PI*0.0f/3.0f), 3.0f*deFloatSin(angle + 2.0f*DE_PI*0.0f/3.0f), 1.0f); 930 Vec3 pt1 (3.0f*deFloatCos(angle + 2.0f*DE_PI*1.0f/3.0f), 3.0f*deFloatSin(angle + 2.0f*DE_PI*1.0f/3.0f), 0.0f); 931 Vec3 pt2 (3.0f*deFloatCos(angle + 2.0f*DE_PI*2.0f/3.0f), 3.0f*deFloatSin(angle + 2.0f*DE_PI*2.0f/3.0f), 0.0f); 932 933 renderTriangle(pt0, pt1, pt2, color); 934 } 935 } 936 } 937 938 /*--------------------------------------------------------------------*//*! 939 * \brief Test that stencil buffer values are per-sample. 940 * 941 * Draws a unicolored pattern and marks drawn samples in stencil buffer; 942 * then clears and draws a viewport-size quad with that color and with 943 * proper stencil test such that the resulting image should be exactly the 944 * same as after the pattern was first drawn. 945 *//*--------------------------------------------------------------------*/ 946 class SampleStencilCase : public MultisampleCase 947 { 948 public: 949 SampleStencilCase (Context& context, const char* name, const char* description); 950 ~SampleStencilCase (void) {} 951 952 void init (void); 953 IterateResult iterate (void); 954 955 protected: 956 int getDesiredViewportSize (void) const { return 256; } 957 }; 958 959 SampleStencilCase::SampleStencilCase (Context& context, const char* name, const char* description) 960 : MultisampleCase (context, name, description) 961 { 962 } 963 964 void SampleStencilCase::init (void) 965 { 966 if (m_context.getRenderTarget().getStencilBits() == 0) 967 TCU_THROW(NotSupportedError, "Test requires stencil buffer"); 968 969 MultisampleCase::init(); 970 } 971 972 SampleStencilCase::IterateResult SampleStencilCase::iterate (void) 973 { 974 TestLog& log = m_testCtx.getLog(); 975 tcu::Surface renderedImgFirst (m_viewportSize, m_viewportSize); 976 tcu::Surface renderedImgSecond (m_viewportSize, m_viewportSize); 977 978 randomizeViewport(); 979 980 GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); 981 GLU_CHECK_CALL(glClearStencil(0)); 982 GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)); 983 GLU_CHECK_CALL(glEnable(GL_STENCIL_TEST)); 984 GLU_CHECK_CALL(glStencilFunc(GL_ALWAYS, 1, 1)); 985 GLU_CHECK_CALL(glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)); 986 987 log << TestLog::Message << "Drawing a pattern with glStencilFunc(GL_ALWAYS, 1, 1) and glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)" << TestLog::EndMessage; 988 989 { 990 const int numTriangles = 25; 991 for (int i = 0; i < numTriangles; i++) 992 { 993 float angle0 = 2.0f*DE_PI * (float)i / (float)numTriangles; 994 float angle1 = 2.0f*DE_PI * ((float)i + 0.5f) / (float)numTriangles; 995 996 renderTriangle(Vec2(0.0f, 0.0f), 997 Vec2(deFloatCos(angle0)*0.95f, deFloatSin(angle0)*0.95f), 998 Vec2(deFloatCos(angle1)*0.95f, deFloatSin(angle1)*0.95f), 999 Vec4(1.0f)); 1000 } 1001 } 1002 1003 readImage(renderedImgFirst); 1004 log << TestLog::Image("RenderedImgFirst", "First image rendered", renderedImgFirst, QP_IMAGE_COMPRESSION_MODE_PNG); 1005 1006 log << TestLog::Message << "Clearing color buffer to black" << TestLog::EndMessage; 1007 1008 GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT)); 1009 GLU_CHECK_CALL(glStencilFunc(GL_EQUAL, 1, 1)); 1010 GLU_CHECK_CALL(glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)); 1011 1012 { 1013 log << TestLog::Message << "Checking that color buffer was actually cleared to black" << TestLog::EndMessage; 1014 1015 tcu::Surface clearedImg(m_viewportSize, m_viewportSize); 1016 readImage(clearedImg); 1017 1018 for (int y = 0; y < clearedImg.getHeight(); y++) 1019 for (int x = 0; x < clearedImg.getWidth(); x++) 1020 { 1021 const tcu::RGBA& clr = clearedImg.getPixel(x, y); 1022 if (clr != tcu::RGBA::black()) 1023 { 1024 log << TestLog::Message << "Failure: first non-black pixel, color " << clr << ", detected at coordinates (" << x << ", " << y << ")" << TestLog::EndMessage; 1025 log << TestLog::Image("ClearedImg", "Image after clearing, erroneously non-black", clearedImg); 1026 m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed"); 1027 return STOP; 1028 } 1029 } 1030 } 1031 1032 log << TestLog::Message << "Drawing a viewport-sized quad with glStencilFunc(GL_EQUAL, 1, 1) and glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP) - should result in same image as the first" << TestLog::EndMessage; 1033 1034 renderQuad(Vec2(-1.0f, -1.0f), 1035 Vec2( 1.0f, -1.0f), 1036 Vec2(-1.0f, 1.0f), 1037 Vec2( 1.0f, 1.0f), 1038 Vec4(1.0f)); 1039 1040 readImage(renderedImgSecond); 1041 log << TestLog::Image("RenderedImgSecond", "Second image rendered", renderedImgSecond, QP_IMAGE_COMPRESSION_MODE_PNG); 1042 1043 bool passed = tcu::pixelThresholdCompare(log, 1044 "ImageCompare", 1045 "Image comparison", 1046 renderedImgFirst, 1047 renderedImgSecond, 1048 tcu::RGBA(0), 1049 tcu::COMPARE_LOG_ON_ERROR); 1050 1051 if (passed) 1052 log << TestLog::Message << "Success: The two images rendered are identical" << TestLog::EndMessage; 1053 1054 m_context.getTestContext().setTestResult(passed ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, 1055 passed ? "Passed" : "Failed"); 1056 1057 return STOP; 1058 } 1059 1060 /*--------------------------------------------------------------------*//*! 1061 * \brief Tests coverage mask generation proportionality property. 1062 * 1063 * Tests that the number of coverage bits in a coverage mask created by 1064 * GL_SAMPLE_ALPHA_TO_COVERAGE or GL_SAMPLE_COVERAGE is, on average, 1065 * proportional to the alpha or coverage value, respectively. Draws 1066 * multiple frames, each time increasing the alpha or coverage value used, 1067 * and checks that the average color is changing appropriately. 1068 *//*--------------------------------------------------------------------*/ 1069 class MaskProportionalityCase : public MultisampleCase 1070 { 1071 public: 1072 enum CaseType 1073 { 1074 CASETYPE_ALPHA_TO_COVERAGE = 0, 1075 CASETYPE_SAMPLE_COVERAGE, 1076 CASETYPE_SAMPLE_COVERAGE_INVERTED, 1077 1078 CASETYPE_LAST 1079 }; 1080 1081 MaskProportionalityCase (Context& context, const char* name, const char* description, CaseType type); 1082 ~MaskProportionalityCase (void) {} 1083 1084 void init (void); 1085 1086 IterateResult iterate (void); 1087 1088 protected: 1089 int getDesiredViewportSize (void) const { return 32; } 1090 1091 private: 1092 const CaseType m_type; 1093 1094 int m_numIterations; 1095 int m_currentIteration; 1096 1097 deInt32 m_previousIterationColorSum; 1098 }; 1099 1100 MaskProportionalityCase::MaskProportionalityCase (Context& context, const char* name, const char* description, CaseType type) 1101 : MultisampleCase (context, name, description) 1102 , m_type (type) 1103 , m_currentIteration (0) 1104 , m_previousIterationColorSum (-1) 1105 { 1106 } 1107 1108 void MaskProportionalityCase::init (void) 1109 { 1110 TestLog& log = m_testCtx.getLog(); 1111 1112 MultisampleCase::init(); 1113 1114 if (m_type == CASETYPE_ALPHA_TO_COVERAGE) 1115 { 1116 GLU_CHECK_CALL(glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE)); 1117 log << TestLog::Message << "GL_SAMPLE_ALPHA_TO_COVERAGE is enabled" << TestLog::EndMessage; 1118 } 1119 else 1120 { 1121 DE_ASSERT(m_type == CASETYPE_SAMPLE_COVERAGE || m_type == CASETYPE_SAMPLE_COVERAGE_INVERTED); 1122 1123 GLU_CHECK_CALL(glEnable(GL_SAMPLE_COVERAGE)); 1124 log << TestLog::Message << "GL_SAMPLE_COVERAGE is enabled" << TestLog::EndMessage; 1125 } 1126 1127 m_numIterations = de::max(2, getIterationCount(m_testCtx, m_numSamples * 5)); 1128 1129 randomizeViewport(); // \note Using the same viewport for every iteration since coverage mask may depend on window-relative pixel coordinate. 1130 } 1131 1132 MaskProportionalityCase::IterateResult MaskProportionalityCase::iterate (void) 1133 { 1134 TestLog& log = m_testCtx.getLog(); 1135 tcu::Surface renderedImg (m_viewportSize, m_viewportSize); 1136 deInt32 numPixels = (deInt32)renderedImg.getWidth()*(deInt32)renderedImg.getHeight(); 1137 1138 log << TestLog::Message << "Clearing color to black" << TestLog::EndMessage; 1139 GLU_CHECK_CALL(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); 1140 GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); 1141 GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT)); 1142 1143 if (m_type == CASETYPE_ALPHA_TO_COVERAGE) 1144 { 1145 GLU_CHECK_CALL(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE)); 1146 log << TestLog::Message << "Using color mask TRUE, TRUE, TRUE, FALSE" << TestLog::EndMessage; 1147 } 1148 1149 // Draw quad. 1150 1151 { 1152 const Vec2 pt0 (-1.0f, -1.0f); 1153 const Vec2 pt1 ( 1.0f, -1.0f); 1154 const Vec2 pt2 (-1.0f, 1.0f); 1155 const Vec2 pt3 ( 1.0f, 1.0f); 1156 Vec4 quadColor (1.0f, 0.0f, 0.0f, 1.0f); 1157 float alphaOrCoverageValue = (float)m_currentIteration / (float)(m_numIterations-1); 1158 1159 if (m_type == CASETYPE_ALPHA_TO_COVERAGE) 1160 { 1161 log << TestLog::Message << "Drawing a red quad using alpha value " + de::floatToString(alphaOrCoverageValue, 2) << TestLog::EndMessage; 1162 quadColor.w() = alphaOrCoverageValue; 1163 } 1164 else 1165 { 1166 DE_ASSERT(m_type == CASETYPE_SAMPLE_COVERAGE || m_type == CASETYPE_SAMPLE_COVERAGE_INVERTED); 1167 1168 bool isInverted = m_type == CASETYPE_SAMPLE_COVERAGE_INVERTED; 1169 float coverageValue = isInverted ? 1.0f - alphaOrCoverageValue : alphaOrCoverageValue; 1170 log << TestLog::Message << "Drawing a red quad using sample coverage value " + de::floatToString(coverageValue, 2) << (isInverted ? " (inverted)" : "") << TestLog::EndMessage; 1171 GLU_CHECK_CALL(glSampleCoverage(coverageValue, isInverted ? GL_TRUE : GL_FALSE)); 1172 } 1173 1174 renderQuad(pt0, pt1, pt2, pt3, quadColor); 1175 } 1176 1177 // Read ang log image. 1178 1179 readImage(renderedImg); 1180 1181 log << TestLog::Image("RenderedImage", "Rendered image", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG); 1182 1183 // Compute average red component in rendered image. 1184 1185 deInt32 sumRed = 0; 1186 1187 for (int y = 0; y < renderedImg.getHeight(); y++) 1188 for (int x = 0; x < renderedImg.getWidth(); x++) 1189 sumRed += renderedImg.getPixel(x, y).getRed(); 1190 1191 log << TestLog::Message << "Average red color component: " << de::floatToString((float)sumRed / 255.0f / (float)numPixels, 2) << TestLog::EndMessage; 1192 1193 // Check if average color has decreased from previous frame's color. 1194 1195 if (sumRed < m_previousIterationColorSum) 1196 { 1197 log << TestLog::Message << "Failure: Current average red color component is lower than previous" << TestLog::EndMessage; 1198 m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed"); 1199 return STOP; 1200 } 1201 1202 // Check if coverage mask is not all-zeros if alpha or coverage value is 0 (or 1, if inverted). 1203 1204 if (m_currentIteration == 0 && sumRed != 0) 1205 { 1206 log << TestLog::Message << "Failure: Image should be completely black" << TestLog::EndMessage; 1207 m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed"); 1208 return STOP; 1209 } 1210 1211 if (m_currentIteration == m_numIterations-1 && sumRed != 0xff*numPixels) 1212 { 1213 log << TestLog::Message << "Failure: Image should be completely red" << TestLog::EndMessage; 1214 1215 m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed"); 1216 return STOP; 1217 } 1218 1219 m_previousIterationColorSum = sumRed; 1220 1221 m_currentIteration++; 1222 1223 if (m_currentIteration >= m_numIterations) 1224 { 1225 log << TestLog::Message 1226 << "Success: Number of coverage mask bits set appears to be, on average, proportional to " 1227 << (m_type == CASETYPE_ALPHA_TO_COVERAGE ? "alpha" : m_type == CASETYPE_SAMPLE_COVERAGE ? "sample coverage value" : "inverted sample coverage value") 1228 << TestLog::EndMessage; 1229 1230 m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed"); 1231 return STOP; 1232 } 1233 else 1234 return CONTINUE; 1235 } 1236 1237 /*--------------------------------------------------------------------*//*! 1238 * \brief Tests coverage mask generation constancy property. 1239 * 1240 * Tests that the coverage mask created by GL_SAMPLE_ALPHA_TO_COVERAGE or 1241 * GL_SAMPLE_COVERAGE is constant at given pixel coordinates, with a given 1242 * alpha component or coverage value, respectively. Draws two quads, with 1243 * the second one fully overlapping the first one such that at any given 1244 * pixel, both quads have the same alpha or coverage value. This way, if 1245 * the constancy property is fulfilled, only the second quad should be 1246 * visible. 1247 *//*--------------------------------------------------------------------*/ 1248 class MaskConstancyCase : public MultisampleCase 1249 { 1250 public: 1251 enum CaseType 1252 { 1253 CASETYPE_ALPHA_TO_COVERAGE = 0, //!< Use only alpha-to-coverage. 1254 CASETYPE_SAMPLE_COVERAGE, //!< Use only sample coverage. 1255 CASETYPE_SAMPLE_COVERAGE_INVERTED, //!< Use only inverted sample coverage. 1256 CASETYPE_BOTH, //!< Use both alpha-to-coverage and sample coverage. 1257 CASETYPE_BOTH_INVERTED, //!< Use both alpha-to-coverage and inverted sample coverage. 1258 1259 CASETYPE_LAST 1260 }; 1261 1262 MaskConstancyCase (Context& context, const char* name, const char* description, CaseType type); 1263 ~MaskConstancyCase (void) {} 1264 1265 IterateResult iterate (void); 1266 1267 protected: 1268 int getDesiredViewportSize (void) const { return 256; } 1269 1270 private: 1271 const bool m_isAlphaToCoverageCase; 1272 const bool m_isSampleCoverageCase; 1273 const bool m_isInvertedSampleCoverageCase; 1274 }; 1275 1276 MaskConstancyCase::MaskConstancyCase (Context& context, const char* name, const char* description, CaseType type) 1277 : MultisampleCase (context, name, description) 1278 , m_isAlphaToCoverageCase (type == CASETYPE_ALPHA_TO_COVERAGE || type == CASETYPE_BOTH || type == CASETYPE_BOTH_INVERTED) 1279 , m_isSampleCoverageCase (type == CASETYPE_SAMPLE_COVERAGE || type == CASETYPE_SAMPLE_COVERAGE_INVERTED || type == CASETYPE_BOTH || type == CASETYPE_BOTH_INVERTED) 1280 , m_isInvertedSampleCoverageCase (type == CASETYPE_SAMPLE_COVERAGE_INVERTED || type == CASETYPE_BOTH_INVERTED) 1281 { 1282 } 1283 1284 MaskConstancyCase::IterateResult MaskConstancyCase::iterate (void) 1285 { 1286 TestLog& log = m_testCtx.getLog(); 1287 tcu::Surface renderedImg (m_viewportSize, m_viewportSize); 1288 1289 randomizeViewport(); 1290 1291 log << TestLog::Message << "Clearing color to black" << TestLog::EndMessage; 1292 GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 1.0f)); 1293 GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT)); 1294 1295 if (m_isAlphaToCoverageCase) 1296 { 1297 GLU_CHECK_CALL(glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE)); 1298 GLU_CHECK_CALL(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE)); 1299 log << TestLog::Message << "GL_SAMPLE_ALPHA_TO_COVERAGE is enabled" << TestLog::EndMessage; 1300 log << TestLog::Message << "Color mask is TRUE, TRUE, TRUE, FALSE" << TestLog::EndMessage; 1301 } 1302 1303 if (m_isSampleCoverageCase) 1304 { 1305 GLU_CHECK_CALL(glEnable(GL_SAMPLE_COVERAGE)); 1306 log << TestLog::Message << "GL_SAMPLE_COVERAGE is enabled" << TestLog::EndMessage; 1307 } 1308 1309 log << TestLog::Message 1310 << "Drawing several green quads, each fully overlapped by a red quad with the same " 1311 << (m_isAlphaToCoverageCase ? "alpha" : "") 1312 << (m_isAlphaToCoverageCase && m_isSampleCoverageCase ? " and " : "") 1313 << (m_isInvertedSampleCoverageCase ? "inverted " : "") 1314 << (m_isSampleCoverageCase ? "sample coverage" : "") 1315 << " values" 1316 << TestLog::EndMessage; 1317 1318 const int numQuadRowsCols = m_numSamples*4; 1319 1320 for (int row = 0; row < numQuadRowsCols; row++) 1321 { 1322 for (int col = 0; col < numQuadRowsCols; col++) 1323 { 1324 float x0 = (float)(col+0) / (float)numQuadRowsCols * 2.0f - 1.0f; 1325 float x1 = (float)(col+1) / (float)numQuadRowsCols * 2.0f - 1.0f; 1326 float y0 = (float)(row+0) / (float)numQuadRowsCols * 2.0f - 1.0f; 1327 float y1 = (float)(row+1) / (float)numQuadRowsCols * 2.0f - 1.0f; 1328 const Vec4 baseGreen (0.0f, 1.0f, 0.0f, 0.0f); 1329 const Vec4 baseRed (1.0f, 0.0f, 0.0f, 0.0f); 1330 Vec4 alpha0 (0.0f, 0.0f, 0.0f, m_isAlphaToCoverageCase ? (float)col / (float)(numQuadRowsCols-1) : 1.0f); 1331 Vec4 alpha1 (0.0f, 0.0f, 0.0f, m_isAlphaToCoverageCase ? (float)row / (float)(numQuadRowsCols-1) : 1.0f); 1332 1333 if (m_isSampleCoverageCase) 1334 { 1335 float value = (float)(row*numQuadRowsCols + col) / (float)(numQuadRowsCols*numQuadRowsCols-1); 1336 GLU_CHECK_CALL(glSampleCoverage(m_isInvertedSampleCoverageCase ? 1.0f - value : value, m_isInvertedSampleCoverageCase ? GL_TRUE : GL_FALSE)); 1337 } 1338 1339 renderQuad(Vec2(x0, y0), Vec2(x1, y0), Vec2(x0, y1), Vec2(x1, y1), baseGreen + alpha0, baseGreen + alpha1, baseGreen + alpha0, baseGreen + alpha1); 1340 renderQuad(Vec2(x0, y0), Vec2(x1, y0), Vec2(x0, y1), Vec2(x1, y1), baseRed + alpha0, baseRed + alpha1, baseRed + alpha0, baseRed + alpha1); 1341 } 1342 } 1343 1344 readImage(renderedImg); 1345 1346 log << TestLog::Image("RenderedImage", "Rendered image", renderedImg, QP_IMAGE_COMPRESSION_MODE_PNG); 1347 1348 for (int y = 0; y < renderedImg.getHeight(); y++) 1349 for (int x = 0; x < renderedImg.getWidth(); x++) 1350 { 1351 if (renderedImg.getPixel(x, y).getGreen() > 0) 1352 { 1353 log << TestLog::Message << "Failure: Non-zero green color component detected - should have been completely overwritten by red quad" << TestLog::EndMessage; 1354 m_context.getTestContext().setTestResult(QP_TEST_RESULT_FAIL, "Failed"); 1355 return STOP; 1356 } 1357 } 1358 1359 log << TestLog::Message 1360 << "Success: Coverage mask appears to be constant at a given pixel coordinate with a given " 1361 << (m_isAlphaToCoverageCase ? "alpha" : "") 1362 << (m_isAlphaToCoverageCase && m_isSampleCoverageCase ? " and " : "") 1363 << (m_isSampleCoverageCase ? "coverage value" : "") 1364 << TestLog::EndMessage; 1365 1366 m_context.getTestContext().setTestResult(QP_TEST_RESULT_PASS, "Passed"); 1367 1368 return STOP; 1369 } 1370 1371 /*--------------------------------------------------------------------*//*! 1372 * \brief Tests coverage mask inversion validity. 1373 * 1374 * Tests that the coverage masks obtained by glSampleCoverage(..., GL_TRUE) 1375 * and glSampleCoverage(..., GL_FALSE) are indeed each others' inverses. 1376 * This is done by drawing a pattern, with varying coverage values, 1377 * overlapped by a pattern that has inverted masks and is otherwise 1378 * identical. The resulting image is compared to one obtained by drawing 1379 * the same pattern but with all-ones coverage masks. 1380 *//*--------------------------------------------------------------------*/ 1381 class CoverageMaskInvertCase : public MultisampleCase 1382 { 1383 public: 1384 CoverageMaskInvertCase (Context& context, const char* name, const char* description); 1385 ~CoverageMaskInvertCase (void) {} 1386 1387 IterateResult iterate (void); 1388 1389 protected: 1390 int getDesiredViewportSize (void) const { return 256; } 1391 1392 private: 1393 void drawPattern (bool invertSampleCoverage) const; 1394 }; 1395 1396 CoverageMaskInvertCase::CoverageMaskInvertCase (Context& context, const char* name, const char* description) 1397 : MultisampleCase (context, name, description) 1398 { 1399 } 1400 1401 void CoverageMaskInvertCase::drawPattern (bool invertSampleCoverage) const 1402 { 1403 const int numTriangles = 25; 1404 for (int i = 0; i < numTriangles; i++) 1405 { 1406 GLU_CHECK_CALL(glSampleCoverage((float)i / (float)(numTriangles-1), invertSampleCoverage ? GL_TRUE : GL_FALSE)); 1407 1408 float angle0 = 2.0f*DE_PI * (float)i / (float)numTriangles; 1409 float angle1 = 2.0f*DE_PI * ((float)i + 0.5f) / (float)numTriangles; 1410 1411 renderTriangle(Vec2(0.0f, 0.0f), 1412 Vec2(deFloatCos(angle0)*0.95f, deFloatSin(angle0)*0.95f), 1413 Vec2(deFloatCos(angle1)*0.95f, deFloatSin(angle1)*0.95f), 1414 Vec4(0.4f + (float)i/(float)numTriangles*0.6f, 1415 0.5f + (float)i/(float)numTriangles*0.3f, 1416 0.6f - (float)i/(float)numTriangles*0.5f, 1417 0.7f - (float)i/(float)numTriangles*0.7f)); 1418 } 1419 } 1420 1421 CoverageMaskInvertCase::IterateResult CoverageMaskInvertCase::iterate (void) 1422 { 1423 TestLog& log = m_testCtx.getLog(); 1424 tcu::Surface renderedImgNoSampleCoverage (m_viewportSize, m_viewportSize); 1425 tcu::Surface renderedImgSampleCoverage (m_viewportSize, m_viewportSize); 1426 1427 randomizeViewport(); 1428 1429 GLU_CHECK_CALL(glEnable(GL_BLEND)); 1430 GLU_CHECK_CALL(glBlendEquation(GL_FUNC_ADD)); 1431 GLU_CHECK_CALL(glBlendFunc(GL_ONE, GL_ONE)); 1432 log << TestLog::Message << "Additive blending enabled in order to detect (erroneously) overlapping samples" << TestLog::EndMessage; 1433 1434 log << TestLog::Message << "Clearing color to all-zeros" << TestLog::EndMessage; 1435 GLU_CHECK_CALL(glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); 1436 GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT)); 1437 log << TestLog::Message << "Drawing the pattern with GL_SAMPLE_COVERAGE disabled" << TestLog::EndMessage; 1438 drawPattern(false); 1439 readImage(renderedImgNoSampleCoverage); 1440 1441 log << TestLog::Image("RenderedImageNoSampleCoverage", "Rendered image with GL_SAMPLE_COVERAGE disabled", renderedImgNoSampleCoverage, QP_IMAGE_COMPRESSION_MODE_PNG); 1442 1443 log << TestLog::Message << "Clearing color to all-zeros" << TestLog::EndMessage; 1444 GLU_CHECK_CALL(glClear(GL_COLOR_BUFFER_BIT)); 1445 GLU_CHECK_CALL(glEnable(GL_SAMPLE_COVERAGE)); 1446 log << TestLog::Message << "Drawing the pattern with GL_SAMPLE_COVERAGE enabled, using non-inverted masks" << TestLog::EndMessage; 1447 drawPattern(false); 1448 log << TestLog::Message << "Drawing the pattern with GL_SAMPLE_COVERAGE enabled, using same sample coverage values but inverted masks" << TestLog::EndMessage; 1449 drawPattern(true); 1450 readImage(renderedImgSampleCoverage); 1451 1452 log << TestLog::Image("RenderedImageSampleCoverage", "Rendered image with GL_SAMPLE_COVERAGE enabled", renderedImgSampleCoverage, QP_IMAGE_COMPRESSION_MODE_PNG); 1453 1454 bool passed = tcu::pixelThresholdCompare(log, 1455 "CoverageVsNoCoverage", 1456 "Comparison of same pattern with GL_SAMPLE_COVERAGE disabled and enabled", 1457 renderedImgNoSampleCoverage, 1458 renderedImgSampleCoverage, 1459 tcu::RGBA(0), 1460 tcu::COMPARE_LOG_ON_ERROR); 1461 1462 if (passed) 1463 log << TestLog::Message << "Success: The two images rendered are identical" << TestLog::EndMessage; 1464 1465 m_context.getTestContext().setTestResult(passed ? QP_TEST_RESULT_PASS : QP_TEST_RESULT_FAIL, 1466 passed ? "Passed" : "Failed"); 1467 1468 return STOP; 1469 } 1470 1471 MultisampleTests::MultisampleTests (Context& context) 1472 : TestCaseGroup(context, "multisample", "Multisampling tests") 1473 { 1474 } 1475 1476 MultisampleTests::~MultisampleTests (void) 1477 { 1478 } 1479 1480 void MultisampleTests::init (void) 1481 { 1482 addChild(new PolygonNumSamplesCase (m_context, "num_samples_polygon", "Test sanity of the value of GL_SAMPLES, with polygons")); 1483 addChild(new LineNumSamplesCase (m_context, "num_samples_line", "Test sanity of the value of GL_SAMPLES, with lines")); 1484 addChild(new CommonEdgeCase (m_context, "common_edge_small_quads", "Test polygons' common edges with small quads", CommonEdgeCase::CASETYPE_SMALL_QUADS)); 1485 addChild(new CommonEdgeCase (m_context, "common_edge_big_quad", "Test polygons' common edges with bigger-than-viewport quads", CommonEdgeCase::CASETYPE_BIGGER_THAN_VIEWPORT_QUAD)); 1486 addChild(new CommonEdgeCase (m_context, "common_edge_viewport_quad", "Test polygons' common edges with exactly viewport-sized quads", CommonEdgeCase::CASETYPE_FIT_VIEWPORT_QUAD)); 1487 addChild(new SampleDepthCase (m_context, "depth", "Test that depth values are per-sample")); 1488 addChild(new SampleStencilCase (m_context, "stencil", "Test that stencil values are per-sample")); 1489 addChild(new CoverageMaskInvertCase (m_context, "sample_coverage_invert", "Test that non-inverted and inverted sample coverage masks are each other's negations")); 1490 1491 addChild(new MaskProportionalityCase(m_context, "proportionality_alpha_to_coverage", "Test the proportionality property of GL_SAMPLE_ALPHA_TO_COVERAGE", MaskProportionalityCase::CASETYPE_ALPHA_TO_COVERAGE)); 1492 addChild(new MaskProportionalityCase(m_context, "proportionality_sample_coverage", "Test the proportionality property of GL_SAMPLE_COVERAGE", MaskProportionalityCase::CASETYPE_SAMPLE_COVERAGE)); 1493 addChild(new MaskProportionalityCase(m_context, "proportionality_sample_coverage_inverted", "Test the proportionality property of inverted-mask GL_SAMPLE_COVERAGE", MaskProportionalityCase::CASETYPE_SAMPLE_COVERAGE_INVERTED)); 1494 1495 addChild(new MaskConstancyCase(m_context, "constancy_alpha_to_coverage", "Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE", MaskConstancyCase::CASETYPE_ALPHA_TO_COVERAGE)); 1496 addChild(new MaskConstancyCase(m_context, "constancy_sample_coverage", "Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_COVERAGE", MaskConstancyCase::CASETYPE_SAMPLE_COVERAGE)); 1497 addChild(new MaskConstancyCase(m_context, "constancy_sample_coverage_inverted", "Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using inverted-mask GL_SAMPLE_COVERAGE", MaskConstancyCase::CASETYPE_SAMPLE_COVERAGE_INVERTED)); 1498 addChild(new MaskConstancyCase(m_context, "constancy_both", "Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE and GL_SAMPLE_COVERAGE", MaskConstancyCase::CASETYPE_BOTH)); 1499 addChild(new MaskConstancyCase(m_context, "constancy_both_inverted", "Test that coverage mask is constant at given coordinates with a given alpha or coverage value, using GL_SAMPLE_ALPHA_TO_COVERAGE and inverted-mask GL_SAMPLE_COVERAGE", MaskConstancyCase::CASETYPE_BOTH_INVERTED)); 1500 } 1501 1502 } // Functional 1503 } // gles2 1504 } // deqp 1505