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