1 /*------------------------------------------------------------------------- 2 * drawElements Quality Program EGL 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 Tests for resizing the native window of a surface. 22 *//*--------------------------------------------------------------------*/ 23 24 #include "teglResizeTests.hpp" 25 26 #include "teglSimpleConfigCase.hpp" 27 28 #include "tcuImageCompare.hpp" 29 #include "tcuSurface.hpp" 30 #include "tcuPlatform.hpp" 31 #include "tcuTestLog.hpp" 32 #include "tcuInterval.hpp" 33 #include "tcuTextureUtil.hpp" 34 #include "tcuResultCollector.hpp" 35 36 #include "egluNativeDisplay.hpp" 37 #include "egluNativeWindow.hpp" 38 #include "egluNativePixmap.hpp" 39 #include "egluUnique.hpp" 40 #include "egluUtil.hpp" 41 42 #include "eglwLibrary.hpp" 43 #include "eglwEnums.hpp" 44 45 #include "gluDefs.hpp" 46 #include "glwFunctions.hpp" 47 #include "glwEnums.hpp" 48 49 #include "tcuTestLog.hpp" 50 #include "tcuVector.hpp" 51 52 #include "deThread.h" 53 #include "deUniquePtr.hpp" 54 55 #include <sstream> 56 57 namespace deqp 58 { 59 namespace egl 60 { 61 62 using std::vector; 63 using std::string; 64 using std::ostringstream; 65 using de::MovePtr; 66 using tcu::CommandLine; 67 using tcu::ConstPixelBufferAccess; 68 using tcu::Interval; 69 using tcu::IVec2; 70 using tcu::Vec3; 71 using tcu::Vec4; 72 using tcu::UVec4; 73 using tcu::ResultCollector; 74 using tcu::Surface; 75 using tcu::TestLog; 76 using eglu::AttribMap; 77 using eglu::NativeDisplay; 78 using eglu::NativeWindow; 79 using eglu::ScopedCurrentContext; 80 using eglu::UniqueSurface; 81 using eglu::UniqueContext; 82 using eglu::NativeWindowFactory; 83 using eglu::WindowParams; 84 using namespace eglw; 85 86 typedef eglu::WindowParams::Visibility Visibility; 87 typedef TestCase::IterateResult IterateResult; 88 89 struct ResizeParams 90 { 91 string name; 92 string description; 93 IVec2 oldSize; 94 IVec2 newSize; 95 }; 96 97 class ResizeTest : public TestCase 98 { 99 public: 100 ResizeTest (EglTestContext& eglTestCtx, 101 const ResizeParams& params) 102 : TestCase (eglTestCtx, 103 params.name.c_str(), 104 params.description.c_str()) 105 , m_oldSize (params.oldSize) 106 , m_newSize (params.newSize) 107 , m_display (EGL_NO_DISPLAY) 108 , m_config (DE_NULL) 109 , m_log (m_testCtx.getLog()) 110 , m_status (m_log) {} 111 112 void init (void); 113 void deinit (void); 114 115 protected: 116 virtual EGLenum surfaceType (void) const { return EGL_WINDOW_BIT; } 117 void resize (IVec2 size); 118 119 const IVec2 m_oldSize; 120 const IVec2 m_newSize; 121 EGLDisplay m_display; 122 EGLConfig m_config; 123 MovePtr<NativeWindow> m_nativeWindow; 124 MovePtr<UniqueSurface> m_surface; 125 MovePtr<UniqueContext> m_context; 126 TestLog& m_log; 127 ResultCollector m_status; 128 glw::Functions m_gl; 129 }; 130 131 EGLConfig getEGLConfig (const Library& egl, const EGLDisplay eglDisplay, EGLenum surfaceType) 132 { 133 AttribMap attribMap; 134 135 attribMap[EGL_SURFACE_TYPE] = surfaceType; 136 attribMap[EGL_RENDERABLE_TYPE] = EGL_OPENGL_ES2_BIT; 137 138 return eglu::chooseSingleConfig(egl, eglDisplay, attribMap); 139 } 140 141 void ResizeTest::init (void) 142 { 143 TestCase::init(); 144 145 const Library& egl = m_eglTestCtx.getLibrary(); 146 const CommandLine& cmdLine = m_testCtx.getCommandLine(); 147 const EGLDisplay eglDisplay = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay()); 148 const EGLConfig eglConfig = getEGLConfig(egl, eglDisplay, surfaceType()); 149 const EGLint ctxAttribs[] = 150 { 151 EGL_CONTEXT_CLIENT_VERSION, 2, 152 EGL_NONE 153 }; 154 EGLContext eglContext = egl.createContext(eglDisplay, 155 eglConfig, 156 EGL_NO_CONTEXT, 157 ctxAttribs); 158 EGLU_CHECK_MSG(egl, "eglCreateContext()"); 159 MovePtr<UniqueContext> context (new UniqueContext(egl, eglDisplay, eglContext)); 160 const EGLint configId = eglu::getConfigAttribInt(egl, 161 eglDisplay, 162 eglConfig, 163 EGL_CONFIG_ID); 164 const Visibility visibility = eglu::parseWindowVisibility(cmdLine); 165 NativeDisplay& nativeDisplay = m_eglTestCtx.getNativeDisplay(); 166 const NativeWindowFactory& windowFactory = eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), 167 cmdLine); 168 169 const WindowParams windowParams (m_oldSize.x(), m_oldSize.y(), visibility); 170 MovePtr<NativeWindow> nativeWindow (windowFactory.createWindow(&nativeDisplay, 171 eglDisplay, 172 eglConfig, 173 DE_NULL, 174 windowParams)); 175 const EGLSurface eglSurface = eglu::createWindowSurface(nativeDisplay, 176 *nativeWindow, 177 eglDisplay, 178 eglConfig, 179 DE_NULL); 180 MovePtr<UniqueSurface> surface (new UniqueSurface(egl, eglDisplay, eglSurface)); 181 182 m_log << TestLog::Message 183 << "Chose EGLConfig with id " << configId << ".\n" 184 << "Created initial surface with size " << m_oldSize 185 << TestLog::EndMessage; 186 187 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2, 0)); 188 m_config = eglConfig; 189 m_surface = surface; 190 m_context = context; 191 m_display = eglDisplay; 192 m_nativeWindow = nativeWindow; 193 EGLU_CHECK_MSG(egl, "init"); 194 } 195 196 void ResizeTest::deinit (void) 197 { 198 if (m_display != EGL_NO_DISPLAY) 199 m_eglTestCtx.getLibrary().terminate(m_display); 200 201 m_config = DE_NULL; 202 m_display = EGL_NO_DISPLAY; 203 m_context.clear(); 204 m_surface.clear(); 205 m_nativeWindow.clear(); 206 } 207 208 void ResizeTest::resize (IVec2 size) 209 { 210 m_nativeWindow->setSurfaceSize(size); 211 m_testCtx.getPlatform().processEvents(); 212 m_log << TestLog::Message 213 << "Resized surface to size " << size 214 << TestLog::EndMessage; 215 } 216 217 class ChangeSurfaceSizeCase : public ResizeTest 218 { 219 public: 220 ChangeSurfaceSizeCase (EglTestContext& eglTestCtx, 221 const ResizeParams& params) 222 : ResizeTest(eglTestCtx, params) {} 223 224 IterateResult iterate (void); 225 }; 226 227 void drawRectangle (const glw::Functions& gl, IVec2 pos, IVec2 size, Vec3 color) 228 { 229 gl.clearColor(color.x(), color.y(), color.z(), 1.0); 230 gl.scissor(pos.x(), pos.y(), size.x(), size.y()); 231 gl.enable(GL_SCISSOR_TEST); 232 gl.clear(GL_COLOR_BUFFER_BIT); 233 gl.disable(GL_SCISSOR_TEST); 234 GLU_EXPECT_NO_ERROR(gl.getError(), 235 "Rectangle drawing with glScissor and glClear failed."); 236 } 237 238 void initSurface (const glw::Functions& gl, IVec2 oldSize) 239 { 240 const Vec3 frameColor (0.0f, 0.0f, 1.0f); 241 const Vec3 fillColor (1.0f, 0.0f, 0.0f); 242 const Vec3 markColor (0.0f, 1.0f, 0.0f); 243 244 drawRectangle(gl, IVec2(0, 0), oldSize, frameColor); 245 drawRectangle(gl, IVec2(2, 2), oldSize - IVec2(4, 4), fillColor); 246 247 drawRectangle(gl, IVec2(0, 0), IVec2(8, 4), markColor); 248 drawRectangle(gl, oldSize - IVec2(16, 16), IVec2(8, 4), markColor); 249 drawRectangle(gl, IVec2(0, oldSize.y() - 16), IVec2(8, 4), markColor); 250 drawRectangle(gl, IVec2(oldSize.x() - 16, 0), IVec2(8, 4), markColor); 251 } 252 253 bool compareRectangles (const ConstPixelBufferAccess& rectA, 254 const ConstPixelBufferAccess& rectB) 255 { 256 const int width = rectA.getWidth(); 257 const int height = rectA.getHeight(); 258 const int depth = rectA.getDepth(); 259 260 if (rectB.getWidth() != width || rectB.getHeight() != height || rectB.getDepth() != depth) 261 return false; 262 263 for (int z = 0; z < depth; ++z) 264 for (int y = 0; y < height; ++y) 265 for (int x = 0; x < width; ++x) 266 if (rectA.getPixel(x, y, z) != rectB.getPixel(x, y, z)) 267 return false; 268 269 return true; 270 } 271 272 // Check whether `oldSurface` and `newSurface` share a common corner. 273 bool compareCorners (const Surface& oldSurface, const Surface& newSurface) 274 { 275 const int oldWidth = oldSurface.getWidth(); 276 const int oldHeight = oldSurface.getHeight(); 277 const int newWidth = newSurface.getWidth(); 278 const int newHeight = newSurface.getHeight(); 279 const int minWidth = de::min(oldWidth, newWidth); 280 const int minHeight = de::min(oldHeight, newHeight); 281 282 for (int xCorner = 0; xCorner < 2; ++xCorner) 283 { 284 const int oldX = xCorner == 0 ? 0 : oldWidth - minWidth; 285 const int newX = xCorner == 0 ? 0 : newWidth - minWidth; 286 287 for (int yCorner = 0; yCorner < 2; ++yCorner) 288 { 289 const int oldY = yCorner == 0 ? 0 : oldHeight - minHeight; 290 const int newY = yCorner == 0 ? 0 : newHeight - minHeight; 291 ConstPixelBufferAccess oldAccess = 292 getSubregion(oldSurface.getAccess(), oldX, oldY, minWidth, minHeight); 293 ConstPixelBufferAccess newAccess = 294 getSubregion(newSurface.getAccess(), newX, newY, minWidth, minHeight); 295 296 if (compareRectangles(oldAccess, newAccess)) 297 return true; 298 } 299 } 300 301 return false; 302 } 303 304 Surface readSurface (const glw::Functions& gl, IVec2 size) 305 { 306 Surface ret (size.x(), size.y()); 307 gl.readPixels(0, 0, size.x(), size.y(), GL_RGBA, GL_UNSIGNED_BYTE, 308 ret.getAccess().getDataPtr()); 309 310 GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() failed"); 311 return ret; 312 } 313 314 template <typename T> 315 inline bool hasBits (T bitSet, T requiredBits) 316 { 317 return (bitSet & requiredBits) == requiredBits; 318 } 319 320 IVec2 getNativeSurfaceSize (const NativeWindow& nativeWindow, 321 IVec2 reqSize) 322 { 323 if (hasBits(nativeWindow.getCapabilities(), NativeWindow::CAPABILITY_GET_SURFACE_SIZE)) 324 return nativeWindow.getSurfaceSize(); 325 return reqSize; // assume we got the requested size 326 } 327 328 IVec2 checkSurfaceSize (const Library& egl, 329 EGLDisplay eglDisplay, 330 EGLSurface eglSurface, 331 const NativeWindow& nativeWindow, 332 IVec2 reqSize, 333 ResultCollector& status) 334 { 335 const IVec2 nativeSize = getNativeSurfaceSize(nativeWindow, reqSize); 336 IVec2 eglSize = eglu::getSurfaceSize(egl, eglDisplay, eglSurface); 337 ostringstream oss; 338 339 oss << "Size of EGL surface " << eglSize 340 << " differs from size of native window " << nativeSize; 341 status.check(eglSize == nativeSize, oss.str()); 342 343 return eglSize; 344 } 345 346 IterateResult ChangeSurfaceSizeCase::iterate (void) 347 { 348 const Library& egl = m_eglTestCtx.getLibrary(); 349 ScopedCurrentContext currentCtx (egl, m_display, **m_surface, **m_surface, **m_context); 350 IVec2 oldEglSize = checkSurfaceSize(egl, 351 m_display, 352 **m_surface, 353 *m_nativeWindow, 354 m_oldSize, 355 m_status); 356 357 initSurface(m_gl, oldEglSize); 358 359 this->resize(m_newSize); 360 361 egl.swapBuffers(m_display, **m_surface); 362 EGLU_CHECK_MSG(egl, "eglSwapBuffers()"); 363 checkSurfaceSize(egl, m_display, **m_surface, *m_nativeWindow, m_newSize, m_status); 364 365 m_status.setTestContextResult(m_testCtx); 366 return STOP; 367 } 368 369 class PreserveBackBufferCase : public ResizeTest 370 { 371 public: 372 PreserveBackBufferCase (EglTestContext& eglTestCtx, 373 const ResizeParams& params) 374 : ResizeTest(eglTestCtx, params) {} 375 376 IterateResult iterate (void); 377 EGLenum surfaceType (void) const; 378 }; 379 380 EGLenum PreserveBackBufferCase::surfaceType (void) const 381 { 382 return EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT; 383 } 384 385 IterateResult PreserveBackBufferCase::iterate (void) 386 { 387 const Library& egl = m_eglTestCtx.getLibrary(); 388 ScopedCurrentContext currentCtx (egl, m_display, **m_surface, **m_surface, **m_context); 389 390 EGLU_CHECK_CALL(egl, surfaceAttrib(m_display, **m_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)); 391 392 GLU_EXPECT_NO_ERROR(m_gl.getError(), "GL state erroneous upon initialization!"); 393 394 { 395 const IVec2 oldEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface); 396 initSurface(m_gl, oldEglSize); 397 398 m_gl.finish(); 399 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glFinish() failed"); 400 401 { 402 const Surface oldSurface = readSurface(m_gl, oldEglSize); 403 404 egl.swapBuffers(m_display, **m_surface); 405 this->resize(m_newSize); 406 egl.swapBuffers(m_display, **m_surface); 407 EGLU_CHECK_MSG(egl, "eglSwapBuffers()"); 408 409 { 410 const IVec2 newEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface); 411 const Surface newSurface = readSurface(m_gl, newEglSize); 412 413 m_log << TestLog::ImageSet("Corner comparison", 414 "Comparing old and new surfaces at all corners") 415 << TestLog::Image("Before resizing", "Before resizing", oldSurface) 416 << TestLog::Image("After resizing", "After resizing", newSurface) 417 << TestLog::EndImageSet; 418 419 m_status.checkResult(compareCorners(oldSurface, newSurface), 420 QP_TEST_RESULT_QUALITY_WARNING, 421 "Resizing the native window changed the contents of " 422 "the EGL surface"); 423 } 424 } 425 } 426 427 m_status.setTestContextResult(m_testCtx); 428 return STOP; 429 } 430 431 typedef tcu::Vector<Interval, 2> IvVec2; 432 433 class UpdateResolutionCase : public ResizeTest 434 { 435 public: 436 UpdateResolutionCase (EglTestContext& eglTestCtx, 437 const ResizeParams& params) 438 : ResizeTest(eglTestCtx, params) {} 439 440 IterateResult iterate (void); 441 442 private: 443 IvVec2 getNativePixelsPerInch (void); 444 }; 445 446 IvVec2 ivVec2 (const IVec2& vec) 447 { 448 return IvVec2(double(vec.x()), double(vec.y())); 449 } 450 451 Interval approximateInt (int i) 452 { 453 const Interval margin(-1.0, 1.0); // The resolution may be rounded 454 455 return (Interval(i) + margin) & Interval(0.0, TCU_INFINITY); 456 } 457 458 IvVec2 UpdateResolutionCase::getNativePixelsPerInch (void) 459 { 460 const Library& egl = m_eglTestCtx.getLibrary(); 461 const int inchPer10km = 254 * EGL_DISPLAY_SCALING; 462 const IVec2 bufSize = eglu::getSurfaceSize(egl, m_display, **m_surface); 463 const IVec2 winSize = m_nativeWindow->getScreenSize(); 464 const IVec2 bufPp10km = eglu::getSurfaceResolution(egl, m_display, **m_surface); 465 const IvVec2 bufPpiI = (IvVec2(approximateInt(bufPp10km.x()), 466 approximateInt(bufPp10km.y())) 467 / Interval(inchPer10km)); 468 const IvVec2 winPpiI = ivVec2(winSize) * bufPpiI / ivVec2(bufSize); 469 const IVec2 winPpi (int(winPpiI.x().midpoint()), int(winPpiI.y().midpoint())); 470 471 m_log << TestLog::Message 472 << "EGL surface size: " << bufSize << "\n" 473 << "EGL surface pixel density (pixels / 10 km): " << bufPp10km << "\n" 474 << "Native window size: " << winSize << "\n" 475 << "Native pixel density (ppi): " << winPpi << "\n" 476 << TestLog::EndMessage; 477 478 m_status.checkResult(bufPp10km.x() >= 1 && bufPp10km.y() >= 1, 479 QP_TEST_RESULT_QUALITY_WARNING, 480 "Surface pixel density is less than one pixel per 10 km. " 481 "Is the surface really visible from space?"); 482 483 return winPpiI; 484 } 485 486 IterateResult UpdateResolutionCase::iterate (void) 487 { 488 const Library& egl = m_eglTestCtx.getLibrary(); 489 ScopedCurrentContext currentCtx (egl, m_display, **m_surface, **m_surface, **m_context); 490 491 if (!hasBits(m_nativeWindow->getCapabilities(), 492 NativeWindow::CAPABILITY_GET_SCREEN_SIZE)) 493 TCU_THROW(NotSupportedError, "Unable to determine surface size in screen pixels"); 494 495 { 496 const IVec2 oldEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface); 497 initSurface(m_gl, oldEglSize); 498 } 499 { 500 const IvVec2 oldPpi = this->getNativePixelsPerInch(); 501 this->resize(m_newSize); 502 EGLU_CHECK_CALL(egl, swapBuffers(m_display, **m_surface)); 503 { 504 const IvVec2 newPpi = this->getNativePixelsPerInch(); 505 m_status.check(oldPpi.x().intersects(newPpi.x()) && 506 oldPpi.y().intersects(newPpi.y()), 507 "Window PPI differs after resizing"); 508 } 509 } 510 511 m_status.setTestContextResult(m_testCtx); 512 return STOP; 513 } 514 515 ResizeTests::ResizeTests (EglTestContext& eglTestCtx) 516 : TestCaseGroup(eglTestCtx, "resize", "Tests resizing the native surface") 517 { 518 } 519 520 template <class Case> 521 TestCaseGroup* createCaseGroup(EglTestContext& eglTestCtx, 522 const string& name, 523 const string& desc) 524 { 525 const ResizeParams params[] = 526 { 527 { "shrink", "Shrink in both dimensions", 528 IVec2(128, 128), IVec2(32, 32) }, 529 { "grow", "Grow in both dimensions", 530 IVec2(32, 32), IVec2(128, 128) }, 531 { "stretch_width", "Grow horizontally, shrink vertically", 532 IVec2(32, 128), IVec2(128, 32) }, 533 { "stretch_height", "Grow vertically, shrink horizontally", 534 IVec2(128, 32), IVec2(32, 128) }, 535 }; 536 TestCaseGroup* const group = 537 new TestCaseGroup(eglTestCtx, name.c_str(), desc.c_str()); 538 539 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(params); ++ndx) 540 group->addChild(new Case(eglTestCtx, params[ndx])); 541 542 return group; 543 } 544 545 void ResizeTests::init (void) 546 { 547 addChild(createCaseGroup<ChangeSurfaceSizeCase>(m_eglTestCtx, 548 "surface_size", 549 "EGL surface size update")); 550 addChild(createCaseGroup<PreserveBackBufferCase>(m_eglTestCtx, 551 "back_buffer", 552 "Back buffer contents")); 553 addChild(createCaseGroup<UpdateResolutionCase>(m_eglTestCtx, 554 "pixel_density", 555 "Pixel density")); 556 } 557 558 } // egl 559 } // deqp 560