1/*------------------------------------------------------------------------- 2 * drawElements Quality Program EGL Module 3 * --------------------------------------- 4 * 5 * Copyright 2015 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 Test KHR_partial_update 22 *//*--------------------------------------------------------------------*/ 23 24#include "teglPartialUpdateTests.hpp" 25 26#include "tcuImageCompare.hpp" 27#include "tcuTestLog.hpp" 28#include "tcuSurface.hpp" 29#include "tcuTextureUtil.hpp" 30 31#include "egluNativeWindow.hpp" 32#include "egluUtil.hpp" 33#include "egluConfigFilter.hpp" 34 35#include "eglwLibrary.hpp" 36#include "eglwEnums.hpp" 37 38#include "gluDefs.hpp" 39#include "gluRenderContext.hpp" 40#include "gluShaderProgram.hpp" 41 42#include "glwDefs.hpp" 43#include "glwEnums.hpp" 44#include "glwFunctions.hpp" 45 46#include "deRandom.hpp" 47#include "deString.h" 48 49#include <string> 50#include <vector> 51#include <sstream> 52 53using std::string; 54using std::vector; 55using glw::GLubyte; 56using tcu::IVec2; 57 58using namespace eglw; 59 60namespace deqp 61{ 62namespace egl 63{ 64namespace 65{ 66 67typedef tcu::Vector<GLubyte, 3> Color; 68 69class GLES2Renderer; 70 71class ReferenceRenderer; 72 73class PartialUpdateTest : public TestCase 74{ 75public: 76 enum DrawType 77 { 78 DRAWTYPE_GLES2_CLEAR, 79 DRAWTYPE_GLES2_RENDER 80 }; 81 82 PartialUpdateTest (EglTestContext& eglTestCtx, 83 const vector<DrawType>& oddFrameDrawType, 84 const vector<DrawType>& evenFrameDrawType, 85 const char* name, 86 const char* description); 87 ~PartialUpdateTest (void); 88 89 void init (void); 90 void deinit (void); 91 IterateResult iterate (void); 92 93private: 94 eglu::NativeWindow* m_window; 95 EGLConfig m_eglConfig; 96 EGLContext m_eglContext; 97 98protected: 99 void initEGLSurface (EGLConfig config); 100 void initEGLContext (EGLConfig config); 101 102 const int m_seed; 103 const vector<DrawType> m_oddFrameDrawType; 104 const vector<DrawType> m_evenFrameDrawType; 105 106 bool m_supportBufferAge; 107 EGLDisplay m_eglDisplay; 108 EGLSurface m_eglSurface; 109 glw::Functions m_gl; 110 111 GLES2Renderer* m_gles2Renderer; 112 ReferenceRenderer* m_refRenderer; 113}; 114 115struct ColoredRect 116{ 117public: 118 ColoredRect (const IVec2& bottomLeft_, const IVec2& topRight_, const Color& color_); 119 IVec2 bottomLeft; 120 IVec2 topRight; 121 Color color; 122}; 123 124ColoredRect::ColoredRect (const IVec2& bottomLeft_, const IVec2& topRight_, const Color& color_) 125 : bottomLeft (bottomLeft_) 126 , topRight (topRight_) 127 , color (color_) 128{ 129} 130 131struct DrawCommand 132{ 133 DrawCommand (const PartialUpdateTest::DrawType drawType_, const ColoredRect& rect_); 134 PartialUpdateTest::DrawType drawType; 135 ColoredRect rect; 136}; 137 138DrawCommand::DrawCommand (const PartialUpdateTest::DrawType drawType_, const ColoredRect& rect_) 139 : drawType (drawType_) 140 , rect (rect_) 141{ 142} 143 144struct Frame 145{ 146 Frame (int width_, int height_); 147 int width; 148 int height; 149 vector<DrawCommand> draws; 150}; 151 152Frame::Frame (int width_, int height_) 153 : width (width_) 154 , height(height_) 155{ 156} 157 158// (x1,y1) lie in the lower-left quadrant while (x2,y2) lie in the upper-right. 159// the coords are multiplied by 4 to amplify the minimial difference between coords to 4 (if not zero) 160// to avoid the situation where two edges are too close to each other which makes the rounding error 161// intoleratable by compareToReference() 162void generateRandomFrame (Frame& dst, const vector<PartialUpdateTest::DrawType>& drawTypes, de::Random& rnd) 163{ 164 for (size_t ndx = 0; ndx < drawTypes.size(); ndx++) 165 { 166 const int x1 = rnd.getInt(0, (dst.width-1)/8) * 4; 167 const int y1 = rnd.getInt(0, (dst.height-1)/8) * 4; 168 const int x2 = rnd.getInt((dst.width-1)/8, (dst.width-1)/4) * 4; 169 const int y2 = rnd.getInt((dst.height-1)/8, (dst.height-1)/4) * 4; 170 const GLubyte r = rnd.getUint8(); 171 const GLubyte g = rnd.getUint8(); 172 const GLubyte b = rnd.getUint8(); 173 const ColoredRect coloredRect (IVec2(x1, y1), IVec2(x2, y2), Color(r, g, b)); 174 const DrawCommand drawCommand (drawTypes[ndx], coloredRect); 175 176 dst.draws.push_back(drawCommand); 177 } 178} 179 180typedef vector<Frame> FrameSequence; 181 182//helper function declaration 183EGLConfig getEGLConfig (const Library& egl, EGLDisplay eglDisplay); 184void clearColorScreen (const glw::Functions& gl, const tcu::Vec4& clearColor); 185void clearColorReference (tcu::Surface* ref, const tcu::Vec4& clearColor); 186void readPixels (const glw::Functions& gl, tcu::Surface* screen); 187float windowToDeviceCoordinates (int x, int length); 188bool compareToReference (tcu::TestLog& log, const tcu::Surface& reference, const tcu::Surface& buffer, int frameNdx, int bufferNum); 189vector<int> getFramesOnBuffer (const vector<int>& bufferAges, int frameNdx); 190 191class GLES2Renderer 192{ 193public: 194 GLES2Renderer (const glw::Functions& gl); 195 ~GLES2Renderer (void); 196 void render (int width, int height, const Frame& frame) const; 197 198private: 199 GLES2Renderer (const GLES2Renderer&); 200 GLES2Renderer& operator= (const GLES2Renderer&); 201 202 const glw::Functions& m_gl; 203 glu::ShaderProgram m_glProgram; 204 glw::GLuint m_coordLoc; 205 glw::GLuint m_colorLoc; 206}; 207 208// generate sources for vertex and fragment buffer 209glu::ProgramSources getSources (void) 210{ 211 const char* const vertexShaderSource = 212 "attribute mediump vec2 a_pos;\n" 213 "attribute mediump vec4 a_color;\n" 214 "varying mediump vec4 v_color;\n" 215 "void main(void)\n" 216 "{\n" 217 "\tv_color = a_color;\n" 218 "\tgl_Position = vec4(a_pos, 0.0, 1.0);\n" 219 "}"; 220 221 const char* const fragmentShaderSource = 222 "varying mediump vec4 v_color;\n" 223 "void main(void)\n" 224 "{\n" 225 "\tgl_FragColor = v_color;\n" 226 "}"; 227 228 return glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource); 229} 230 231GLES2Renderer::GLES2Renderer (const glw::Functions& gl) 232 : m_gl (gl) 233 , m_glProgram (gl, getSources()) 234 , m_coordLoc ((glw::GLuint)-1) 235 , m_colorLoc ((glw::GLuint)-1) 236{ 237 m_colorLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_color"); 238 m_coordLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_pos"); 239 GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to get attribute locations"); 240} 241 242GLES2Renderer::~GLES2Renderer (void) 243{ 244} 245 246void GLES2Renderer::render (int width, int height, const Frame& frame) const 247{ 248 for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++) 249 { 250 const ColoredRect& coloredRect = frame.draws[drawNdx].rect; 251 252 if (frame.draws[drawNdx].drawType == PartialUpdateTest::DRAWTYPE_GLES2_RENDER) 253 { 254 const float x1 = windowToDeviceCoordinates(coloredRect.bottomLeft.x(), width); 255 const float y1 = windowToDeviceCoordinates(coloredRect.bottomLeft.y(), height); 256 const float x2 = windowToDeviceCoordinates(coloredRect.topRight.x(), width); 257 const float y2 = windowToDeviceCoordinates(coloredRect.topRight.y(), height); 258 259 const glw::GLfloat coords[] = 260 { 261 x1, y1, 262 x1, y2, 263 x2, y2, 264 265 x2, y2, 266 x2, y1, 267 x1, y1, 268 }; 269 270 const glw::GLubyte colors[] = 271 { 272 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255, 273 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255, 274 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255, 275 276 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255, 277 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255, 278 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255, 279 }; 280 281 m_gl.useProgram(m_glProgram.getProgram()); 282 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed"); 283 284 m_gl.enableVertexAttribArray(m_coordLoc); 285 m_gl.enableVertexAttribArray(m_colorLoc); 286 GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to enable attributes"); 287 288 m_gl.vertexAttribPointer(m_coordLoc, 2, GL_FLOAT, GL_FALSE, 0, coords); 289 m_gl.vertexAttribPointer(m_colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors); 290 GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to set attribute pointers"); 291 292 m_gl.drawArrays(GL_TRIANGLES, 0, DE_LENGTH_OF_ARRAY(coords)/2); 293 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays(), failed"); 294 295 m_gl.disableVertexAttribArray(m_coordLoc); 296 m_gl.disableVertexAttribArray(m_colorLoc); 297 GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to disable attributes"); 298 299 m_gl.useProgram(0); 300 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed"); 301 } 302 else if (frame.draws[drawNdx].drawType == PartialUpdateTest::DRAWTYPE_GLES2_CLEAR) 303 { 304 m_gl.enable(GL_SCISSOR_TEST); 305 m_gl.scissor(coloredRect.bottomLeft.x(), coloredRect.bottomLeft.y(), 306 coloredRect.topRight.x()-coloredRect.bottomLeft.x(), coloredRect.topRight.y()-coloredRect.bottomLeft.y()); 307 m_gl.clearColor(coloredRect.color.x()/255.0f, coloredRect.color.y()/255.0f, coloredRect.color.z()/255.0f, 1.0f); 308 m_gl.clear(GL_COLOR_BUFFER_BIT); 309 m_gl.disable(GL_SCISSOR_TEST); 310 } 311 else 312 DE_FATAL("Invalid drawtype"); 313 } 314} 315 316class ReferenceRenderer 317{ 318public: 319 ReferenceRenderer (void); 320 void render (tcu::Surface* target, const Frame& frame) const; 321private: 322 ReferenceRenderer (const ReferenceRenderer&); 323 ReferenceRenderer& operator= (const ReferenceRenderer&); 324}; 325 326ReferenceRenderer::ReferenceRenderer(void) 327{ 328} 329 330void ReferenceRenderer::render (tcu::Surface* target, const Frame& frame) const 331{ 332 for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++) 333 { 334 const ColoredRect& coloredRect = frame.draws[drawNdx].rect; 335 if (frame.draws[drawNdx].drawType == PartialUpdateTest::DRAWTYPE_GLES2_RENDER || frame.draws[drawNdx].drawType == PartialUpdateTest::DRAWTYPE_GLES2_CLEAR) 336 { 337 // tcu does not support degenerate subregions. Since they correspond to no-op rendering, just skip them. 338 if (coloredRect.bottomLeft.x() == coloredRect.topRight.x() || coloredRect.bottomLeft.y() == coloredRect.topRight.y()) 339 continue; 340 341 const tcu::UVec4 color(coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255); 342 tcu::clear(tcu::getSubregion(target->getAccess(), coloredRect.bottomLeft.x(), coloredRect.bottomLeft.y(), 343 coloredRect.topRight.x()-coloredRect.bottomLeft.x(), coloredRect.topRight.y()-coloredRect.bottomLeft.y()), color); 344 } 345 else 346 DE_FATAL("Invalid drawtype"); 347 } 348} 349 350PartialUpdateTest::PartialUpdateTest (EglTestContext& eglTestCtx, 351 const vector<DrawType>& oddFrameDrawType, 352 const vector<DrawType>& evenFrameDrawType, 353 const char* name, const char* description) 354 : TestCase (eglTestCtx, name, description) 355 , m_window (DE_NULL) 356 , m_eglContext (EGL_NO_CONTEXT) 357 , m_seed (deStringHash(name)) 358 , m_oddFrameDrawType (oddFrameDrawType) 359 , m_evenFrameDrawType (evenFrameDrawType) 360 , m_supportBufferAge (false) 361 , m_eglDisplay (EGL_NO_DISPLAY) 362 , m_eglSurface (EGL_NO_SURFACE) 363 , m_gles2Renderer (DE_NULL) 364 , m_refRenderer (DE_NULL) 365{ 366} 367 368PartialUpdateTest::~PartialUpdateTest (void) 369{ 370 deinit(); 371} 372 373void PartialUpdateTest::init (void) 374{ 375 const Library& egl = m_eglTestCtx.getLibrary(); 376 377 m_eglDisplay = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay()); 378 379 if (!eglu::hasExtension(egl, m_eglDisplay, "EGL_KHR_partial_update")) 380 { 381 egl.terminate(m_eglDisplay); 382 m_eglDisplay = EGL_NO_DISPLAY; 383 TCU_THROW(NotSupportedError, "EGL_KHR_partial_update is not supported"); 384 } 385 386 m_eglConfig = getEGLConfig(m_eglTestCtx.getLibrary(), m_eglDisplay); 387 388 //create surface and context and make them current 389 initEGLSurface(m_eglConfig); 390 initEGLContext(m_eglConfig); 391 392 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0)); 393 394 m_supportBufferAge = eglu::hasExtension(egl, m_eglDisplay, "EGL_EXT_buffer_age"); 395 396 m_gles2Renderer = new GLES2Renderer(m_gl); 397 m_refRenderer = new ReferenceRenderer(); 398} 399 400void PartialUpdateTest::deinit (void) 401{ 402 const Library& egl = m_eglTestCtx.getLibrary(); 403 404 delete m_refRenderer; 405 m_refRenderer = DE_NULL; 406 407 delete m_gles2Renderer; 408 m_gles2Renderer = DE_NULL; 409 410 if (m_eglContext != EGL_NO_CONTEXT) 411 { 412 egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 413 egl.destroyContext(m_eglDisplay, m_eglContext); 414 m_eglContext = EGL_NO_CONTEXT; 415 } 416 417 if (m_eglSurface != EGL_NO_SURFACE) 418 { 419 egl.destroySurface(m_eglDisplay, m_eglSurface); 420 m_eglSurface = EGL_NO_SURFACE; 421 } 422 423 if (m_eglDisplay != EGL_NO_DISPLAY) 424 { 425 egl.terminate(m_eglDisplay); 426 m_eglDisplay = EGL_NO_DISPLAY; 427 } 428 429 delete m_window; 430 m_window = DE_NULL; 431} 432 433void PartialUpdateTest::initEGLSurface (EGLConfig config) 434{ 435 const eglu::NativeWindowFactory& factory = eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine()); 436 m_window = factory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_eglDisplay, config, DE_NULL, 437 eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine()))); 438 m_eglSurface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_eglDisplay, config, DE_NULL); 439} 440 441void PartialUpdateTest::initEGLContext (EGLConfig config) 442{ 443 const Library& egl = m_eglTestCtx.getLibrary(); 444 const EGLint attribList[] = 445 { 446 EGL_CONTEXT_CLIENT_VERSION, 2, 447 EGL_NONE 448 }; 449 450 egl.bindAPI(EGL_OPENGL_ES_API); 451 m_eglContext = egl.createContext(m_eglDisplay, config, EGL_NO_CONTEXT, attribList); 452 EGLU_CHECK_MSG(egl, "eglCreateContext"); 453 TCU_CHECK(m_eglSurface != EGL_NO_SURFACE); 454 egl.makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext); 455 EGLU_CHECK_MSG(egl, "eglMakeCurrent"); 456} 457 458// return indices of frames that have been written to the given buffer 459vector<int> getFramesOnBuffer (const vector<int>& bufferAges, int frameNdx) 460{ 461 DE_ASSERT(frameNdx < (int)bufferAges.size()); 462 vector<int> frameOnBuffer; 463 int age = bufferAges[frameNdx]; 464 while (age != 0) 465 { 466 frameNdx = frameNdx - age; 467 DE_ASSERT(frameNdx >= 0); 468 frameOnBuffer.push_back(frameNdx); 469 age = bufferAges[frameNdx]; 470 } 471 472 reverse(frameOnBuffer.begin(), frameOnBuffer.end()); 473 return frameOnBuffer; 474} 475 476vector<EGLint> getDamageRegion (const Frame& frame, int marginLeft, int marginBottom, int marginRight, int marginTop) 477{ 478 vector<EGLint> damageRegion; 479 for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++) 480 { 481 const ColoredRect& rect = frame.draws[drawNdx].rect; 482 damageRegion.push_back(rect.bottomLeft.x() - marginLeft); 483 damageRegion.push_back(rect.bottomLeft.y() - marginBottom); 484 damageRegion.push_back(rect.topRight.x() - rect.bottomLeft.x() + marginLeft + marginRight); 485 damageRegion.push_back(rect.topRight.y() - rect.bottomLeft.y() + marginBottom + marginTop); 486 } 487 488 DE_ASSERT(damageRegion.size() % 4 == 0); 489 return damageRegion; 490} 491 492TestCase::IterateResult PartialUpdateTest::iterate (void) 493{ 494 de::Random rnd (m_seed); 495 const Library& egl = m_eglTestCtx.getLibrary(); 496 tcu::TestLog& log = m_testCtx.getLog(); 497 const int width = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH); 498 const int height = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT); 499 const float clearRed = rnd.getFloat(); 500 const float clearGreen = rnd.getFloat(); 501 const float clearBlue = rnd.getFloat(); 502 const tcu::Vec4 clearColor (clearRed, clearGreen, clearBlue, 1.0f); 503 const int numFrames = 20; 504 FrameSequence frameSequence; 505 vector<int> bufferAges; 506 bool hasPositiveAge = false; 507 508 EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED)); 509 510 for (int frameNdx = 0; frameNdx < numFrames; frameNdx++) 511 { 512 tcu::Surface currentBuffer (width, height); 513 tcu::Surface refBuffer (width, height); 514 Frame newFrame (width, height); 515 EGLint currentBufferAge = -1; 516 517 if (frameNdx % 2 == 0) 518 generateRandomFrame(newFrame, m_evenFrameDrawType, rnd); 519 else 520 generateRandomFrame(newFrame, m_oddFrameDrawType, rnd); 521 522 frameSequence.push_back(newFrame); 523 524 EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, ¤tBufferAge)); 525 526 if (currentBufferAge > frameNdx || currentBufferAge < 0) // invalid buffer age 527 { 528 std::ostringstream stream; 529 stream << "Fail, the age is invalid. Age: " << currentBufferAge << ", frameNdx: " << frameNdx; 530 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, stream.str().c_str()); 531 return STOP; 532 } 533 534 bufferAges.push_back(currentBufferAge); 535 DE_ASSERT((int)bufferAges.size() == frameNdx+1); 536 537 if (currentBufferAge > 0) 538 { 539 vector<EGLint> damageRegion; 540 541 hasPositiveAge = true; 542 543 if (m_supportBufferAge) 544 { 545 damageRegion = getDamageRegion(newFrame, 10, 10, 10, 10); 546 } 547 else 548 { 549 damageRegion = getDamageRegion(newFrame, 0, 0, 0, 0); 550 } 551 552 // Set empty damage region to avoid invalidating the framebuffer. The damage area is invalidated 553 // if the buffer age extension is not supported. 554 if (damageRegion.size() == 0) 555 damageRegion = vector<EGLint>(4, 0); 556 557 EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4)); 558 } 559 else 560 { 561 EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, NULL, 0)); 562 clearColorScreen(m_gl, clearColor); 563 } 564 565 // during first half, just keep rendering without reading pixel back to mimic ordinary use case 566 if (frameNdx < numFrames/2) 567 m_gles2Renderer->render(width, height, newFrame); 568 else // do verification in the second half 569 { 570 const vector<int> framesOnBuffer = getFramesOnBuffer(bufferAges, frameNdx); 571 572 clearColorReference(&refBuffer, clearColor); 573 574 for (vector<int>::const_iterator it = framesOnBuffer.begin(); it != framesOnBuffer.end(); it++) 575 m_refRenderer->render(&refBuffer, frameSequence[*it]); 576 577 m_gles2Renderer->render(width, height, newFrame); 578 m_refRenderer->render(&refBuffer, newFrame); 579 580 readPixels(m_gl, ¤tBuffer); 581 582 if (!compareToReference(log, refBuffer, currentBuffer, frameNdx, frameNdx)) 583 { 584 string errorMessage("Fail, render result is wrong. Buffer age is "); 585 errorMessage += (m_supportBufferAge ? "supported" : "not supported"); 586 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, errorMessage.c_str()); 587 return STOP; 588 } 589 } 590 EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface)); 591 } 592 593 if (!hasPositiveAge) // fraud behavior, pretend to support partial_update 594 { 595 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail, claim to support partial_update but buffer age is always 0"); 596 return STOP; 597 } 598 599 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 600 return STOP; 601} 602 603string generateDrawTypeName (const vector<PartialUpdateTest::DrawType>& drawTypes) 604{ 605 std::ostringstream stream; 606 if (drawTypes.size() == 0) 607 return string("_none"); 608 609 for (size_t ndx = 0; ndx < drawTypes.size(); ndx++) 610 { 611 if (drawTypes[ndx] == PartialUpdateTest::DRAWTYPE_GLES2_RENDER) 612 stream << "_render"; 613 else if (drawTypes[ndx] == PartialUpdateTest::DRAWTYPE_GLES2_CLEAR) 614 stream << "_clear"; 615 else 616 DE_ASSERT(false); 617 } 618 return stream.str(); 619} 620 621string generateTestName (const vector<PartialUpdateTest::DrawType>& oddFrameDrawType, const vector<PartialUpdateTest::DrawType>& evenFrameDrawType) 622{ 623 return "odd" + generateDrawTypeName(oddFrameDrawType) + "_even" + generateDrawTypeName(evenFrameDrawType); 624} 625 626bool isWindow (const eglu::CandidateConfig& c) 627{ 628 return (c.surfaceType() & EGL_WINDOW_BIT) == EGL_WINDOW_BIT; 629} 630 631bool isES2Renderable (const eglu::CandidateConfig& c) 632{ 633 return (c.get(EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT; 634} 635 636EGLConfig getEGLConfig (const Library& egl, EGLDisplay eglDisplay) 637{ 638 eglu::FilterList filters; 639 filters << isWindow << isES2Renderable; 640 return eglu::chooseSingleConfig(egl, eglDisplay, filters); 641} 642 643void clearColorScreen (const glw::Functions& gl, const tcu::Vec4& clearColor) 644{ 645 gl.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w()); 646 gl.clear(GL_COLOR_BUFFER_BIT); 647} 648 649void clearColorReference (tcu::Surface* ref, const tcu::Vec4& clearColor) 650{ 651 tcu::clear(ref->getAccess(), clearColor); 652} 653 654void readPixels (const glw::Functions& gl, tcu::Surface* screen) 655{ 656 gl.readPixels(0, 0, screen->getWidth(), screen->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, screen->getAccess().getDataPtr()); 657} 658 659float windowToDeviceCoordinates (int x, int length) 660{ 661 return (2.0f * float(x) / float(length)) - 1.0f; 662} 663 664bool compareToReference (tcu::TestLog& log, const tcu::Surface& reference, const tcu::Surface& buffer, int frameNdx, int bufferNum) 665{ 666 std::ostringstream stream; 667 stream << "FrameNdx = " << frameNdx << ", compare current buffer (numbered: " << bufferNum << ") to reference"; 668 return tcu::intThresholdPositionDeviationCompare(log, "partial update test", stream.str().c_str(), reference.getAccess(), buffer.getAccess(), 669 tcu::UVec4(8, 8, 8, 0), tcu::IVec3(2,2,0), true, tcu::COMPARE_LOG_RESULT); 670} 671 672class RenderOutsideDamageRegion : public PartialUpdateTest 673{ 674public: 675 RenderOutsideDamageRegion (EglTestContext& eglTestCtx); 676 TestCase::IterateResult iterate (void); 677}; 678 679RenderOutsideDamageRegion::RenderOutsideDamageRegion (EglTestContext& eglTestCtx) 680 : PartialUpdateTest (eglTestCtx, vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), "render_outside_damage_region", "") 681{ 682} 683 684TestCase::IterateResult RenderOutsideDamageRegion::iterate (void) 685{ 686 de::Random rnd (m_seed); 687 const Library& egl = m_eglTestCtx.getLibrary(); 688 tcu::TestLog& log = m_testCtx.getLog(); 689 const int width = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH); 690 const int height = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT); 691 const float clearRed = rnd.getFloat(); 692 const float clearGreen = rnd.getFloat(); 693 const float clearBlue = rnd.getFloat(); 694 const tcu::Vec4 clearColor (clearRed, clearGreen, clearBlue, 1.0f); 695 tcu::Surface currentBuffer (width, height); 696 tcu::Surface refBuffer (width, height); 697 Frame frame (width, height); 698 699 EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED)); 700 701 generateRandomFrame(frame, m_evenFrameDrawType, rnd); 702 703 { 704 // render outside the region 705 EGLint bufferAge = -1; 706 vector<EGLint> damageRegion = getDamageRegion(frame, 0, 0, 0, 0); 707 708 EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &bufferAge)); 709 EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4)); 710 clearColorScreen(m_gl, clearColor); 711 m_gles2Renderer->render(width, height, frame); 712 713 // next line will make the bug on Nexus 6 disappear 714 // readPixels(m_gl, ¤tBuffer); 715 } 716 717 EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface)); 718 719 // render a new frame 720 clearColorScreen(m_gl, clearColor); 721 m_gles2Renderer->render(width, height, frame); 722 clearColorReference(&refBuffer, clearColor); 723 m_refRenderer->render(&refBuffer, frame); 724 readPixels(m_gl, ¤tBuffer); 725 726 if (!compareToReference(log, refBuffer, currentBuffer, 0, 0)) 727 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail, fail to recover after rendering outside damageRegion"); 728 else 729 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 730 731 return STOP; 732} 733 734class RenderBeforeSetDamageRegion : public PartialUpdateTest 735{ 736public: 737 RenderBeforeSetDamageRegion (EglTestContext& eglTestCtx); 738 TestCase::IterateResult iterate (void); 739}; 740 741RenderBeforeSetDamageRegion::RenderBeforeSetDamageRegion (EglTestContext& eglTestCtx) 742 : PartialUpdateTest (eglTestCtx, vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), "render_before_set_damage_region", "") 743{ 744} 745 746TestCase::IterateResult RenderBeforeSetDamageRegion::iterate (void) 747{ 748 de::Random rnd (m_seed); 749 const Library& egl = m_eglTestCtx.getLibrary(); 750 tcu::TestLog& log = m_testCtx.getLog(); 751 const int width = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH); 752 const int height = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT); 753 const float clearRed = rnd.getFloat(); 754 const float clearGreen = rnd.getFloat(); 755 const float clearBlue = rnd.getFloat(); 756 const tcu::Vec4 clearColor (clearRed, clearGreen, clearBlue, 1.0f); 757 tcu::Surface currentBuffer (width, height); 758 tcu::Surface refBuffer (width, height); 759 Frame frame (width, height); 760 761 EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED)); 762 763 generateRandomFrame(frame, m_evenFrameDrawType, rnd); 764 765 { 766 // render before setDamageRegion 767 EGLint bufferAge = -1; 768 vector<EGLint> damageRegion = getDamageRegion(frame, 0, 0, 0, 0); 769 770 m_gles2Renderer->render(width, height, frame); 771 EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &bufferAge)); 772 EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4)); 773 774 // next line will make the bug on Nexus 6 disappear 775 // readPixels(m_gl, ¤tBuffer); 776 } 777 778 EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface)); 779 780 // render a new frame 781 clearColorScreen(m_gl, clearColor); 782 m_gles2Renderer->render(width, height, frame); 783 clearColorReference(&refBuffer, clearColor); 784 m_refRenderer->render(&refBuffer, frame); 785 readPixels(m_gl, ¤tBuffer); 786 787 if (!compareToReference(log, refBuffer, currentBuffer, 0, 0)) 788 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail"); 789 else 790 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass"); 791 792 return STOP; 793} 794 795} // anonymous 796 797PartialUpdateTests::PartialUpdateTests (EglTestContext& eglTestCtx) 798 : TestCaseGroup(eglTestCtx, "partial_update", "Partial update tests") 799{ 800} 801 802void PartialUpdateTests::init (void) 803{ 804 const PartialUpdateTest::DrawType clearRender[2] = 805 { 806 PartialUpdateTest::DRAWTYPE_GLES2_CLEAR, 807 PartialUpdateTest::DRAWTYPE_GLES2_RENDER 808 }; 809 810 const PartialUpdateTest::DrawType renderClear[2] = 811 { 812 PartialUpdateTest::DRAWTYPE_GLES2_RENDER, 813 PartialUpdateTest::DRAWTYPE_GLES2_CLEAR 814 }; 815 816 vector< vector<PartialUpdateTest::DrawType> > frameDrawTypes; 817 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> ()); 818 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (1, PartialUpdateTest::DRAWTYPE_GLES2_CLEAR)); 819 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (1, PartialUpdateTest::DRAWTYPE_GLES2_RENDER)); 820 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (2, PartialUpdateTest::DRAWTYPE_GLES2_CLEAR)); 821 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (2, PartialUpdateTest::DRAWTYPE_GLES2_RENDER)); 822 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (DE_ARRAY_BEGIN(clearRender), DE_ARRAY_END(clearRender))); 823 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (DE_ARRAY_BEGIN(renderClear), DE_ARRAY_END(renderClear))); 824 825 for (size_t evenNdx = 0; evenNdx < frameDrawTypes.size(); evenNdx++) 826 { 827 const vector<PartialUpdateTest::DrawType>& evenFrameDrawType = frameDrawTypes[evenNdx]; 828 829 for (size_t oddNdx = evenNdx; oddNdx < frameDrawTypes.size(); oddNdx++) 830 { 831 const vector<PartialUpdateTest::DrawType>& oddFrameDrawType = frameDrawTypes[oddNdx]; 832 const std::string name = generateTestName(oddFrameDrawType, evenFrameDrawType); 833 if (oddFrameDrawType.size() == 0 && evenFrameDrawType.size() == 0) 834 continue; 835 836 addChild(new PartialUpdateTest(m_eglTestCtx, oddFrameDrawType, evenFrameDrawType, name.c_str(), "")); 837 } 838 } 839 addChild(new RenderOutsideDamageRegion(m_eglTestCtx)); 840 addChild(new RenderBeforeSetDamageRegion(m_eglTestCtx)); 841} 842 843} // egl 844} // deqp 845