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:
ResizeTest(EglTestContext & eglTestCtx,const ResizeParams & params)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:
surfaceType(void) const116 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
getEGLConfig(const Library & egl,const EGLDisplay eglDisplay,EGLenum surfaceType)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
init(void)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
deinit(void)196 void ResizeTest::deinit (void)
197 {
198 m_config = DE_NULL;
199 m_context.clear();
200 m_surface.clear();
201 m_nativeWindow.clear();
202
203 if (m_display != EGL_NO_DISPLAY)
204 {
205 m_eglTestCtx.getLibrary().terminate(m_display);
206 m_display = EGL_NO_DISPLAY;
207 }
208 }
209
resize(IVec2 size)210 void ResizeTest::resize (IVec2 size)
211 {
212 m_nativeWindow->setSurfaceSize(size);
213 m_testCtx.getPlatform().processEvents();
214 m_log << TestLog::Message
215 << "Resized surface to size " << size
216 << TestLog::EndMessage;
217 }
218
219 class ChangeSurfaceSizeCase : public ResizeTest
220 {
221 public:
ChangeSurfaceSizeCase(EglTestContext & eglTestCtx,const ResizeParams & params)222 ChangeSurfaceSizeCase (EglTestContext& eglTestCtx,
223 const ResizeParams& params)
224 : ResizeTest(eglTestCtx, params) {}
225
226 IterateResult iterate (void);
227 };
228
drawRectangle(const glw::Functions & gl,IVec2 pos,IVec2 size,Vec3 color)229 void drawRectangle (const glw::Functions& gl, IVec2 pos, IVec2 size, Vec3 color)
230 {
231 gl.clearColor(color.x(), color.y(), color.z(), 1.0);
232 gl.scissor(pos.x(), pos.y(), size.x(), size.y());
233 gl.enable(GL_SCISSOR_TEST);
234 gl.clear(GL_COLOR_BUFFER_BIT);
235 gl.disable(GL_SCISSOR_TEST);
236 GLU_EXPECT_NO_ERROR(gl.getError(),
237 "Rectangle drawing with glScissor and glClear failed.");
238 }
239
initSurface(const glw::Functions & gl,IVec2 oldSize)240 void initSurface (const glw::Functions& gl, IVec2 oldSize)
241 {
242 const Vec3 frameColor (0.0f, 0.0f, 1.0f);
243 const Vec3 fillColor (1.0f, 0.0f, 0.0f);
244 const Vec3 markColor (0.0f, 1.0f, 0.0f);
245
246 drawRectangle(gl, IVec2(0, 0), oldSize, frameColor);
247 drawRectangle(gl, IVec2(2, 2), oldSize - IVec2(4, 4), fillColor);
248
249 drawRectangle(gl, IVec2(0, 0), IVec2(8, 4), markColor);
250 drawRectangle(gl, oldSize - IVec2(16, 16), IVec2(8, 4), markColor);
251 drawRectangle(gl, IVec2(0, oldSize.y() - 16), IVec2(8, 4), markColor);
252 drawRectangle(gl, IVec2(oldSize.x() - 16, 0), IVec2(8, 4), markColor);
253 }
254
compareRectangles(const ConstPixelBufferAccess & rectA,const ConstPixelBufferAccess & rectB)255 bool compareRectangles (const ConstPixelBufferAccess& rectA,
256 const ConstPixelBufferAccess& rectB)
257 {
258 const int width = rectA.getWidth();
259 const int height = rectA.getHeight();
260 const int depth = rectA.getDepth();
261
262 if (rectB.getWidth() != width || rectB.getHeight() != height || rectB.getDepth() != depth)
263 return false;
264
265 for (int z = 0; z < depth; ++z)
266 for (int y = 0; y < height; ++y)
267 for (int x = 0; x < width; ++x)
268 if (rectA.getPixel(x, y, z) != rectB.getPixel(x, y, z))
269 return false;
270
271 return true;
272 }
273
274 // Check whether `oldSurface` and `newSurface` share a common corner.
compareCorners(const Surface & oldSurface,const Surface & newSurface)275 bool compareCorners (const Surface& oldSurface, const Surface& newSurface)
276 {
277 const int oldWidth = oldSurface.getWidth();
278 const int oldHeight = oldSurface.getHeight();
279 const int newWidth = newSurface.getWidth();
280 const int newHeight = newSurface.getHeight();
281 const int minWidth = de::min(oldWidth, newWidth);
282 const int minHeight = de::min(oldHeight, newHeight);
283
284 for (int xCorner = 0; xCorner < 2; ++xCorner)
285 {
286 const int oldX = xCorner == 0 ? 0 : oldWidth - minWidth;
287 const int newX = xCorner == 0 ? 0 : newWidth - minWidth;
288
289 for (int yCorner = 0; yCorner < 2; ++yCorner)
290 {
291 const int oldY = yCorner == 0 ? 0 : oldHeight - minHeight;
292 const int newY = yCorner == 0 ? 0 : newHeight - minHeight;
293 ConstPixelBufferAccess oldAccess =
294 getSubregion(oldSurface.getAccess(), oldX, oldY, minWidth, minHeight);
295 ConstPixelBufferAccess newAccess =
296 getSubregion(newSurface.getAccess(), newX, newY, minWidth, minHeight);
297
298 if (compareRectangles(oldAccess, newAccess))
299 return true;
300 }
301 }
302
303 return false;
304 }
305
readSurface(const glw::Functions & gl,IVec2 size)306 Surface readSurface (const glw::Functions& gl, IVec2 size)
307 {
308 Surface ret (size.x(), size.y());
309 gl.readPixels(0, 0, size.x(), size.y(), GL_RGBA, GL_UNSIGNED_BYTE,
310 ret.getAccess().getDataPtr());
311
312 GLU_EXPECT_NO_ERROR(gl.getError(), "glReadPixels() failed");
313 return ret;
314 }
315
316 template <typename T>
hasBits(T bitSet,T requiredBits)317 inline bool hasBits (T bitSet, T requiredBits)
318 {
319 return (bitSet & requiredBits) == requiredBits;
320 }
321
getNativeSurfaceSize(const NativeWindow & nativeWindow,IVec2 reqSize)322 IVec2 getNativeSurfaceSize (const NativeWindow& nativeWindow,
323 IVec2 reqSize)
324 {
325 if (hasBits(nativeWindow.getCapabilities(), NativeWindow::CAPABILITY_GET_SURFACE_SIZE))
326 return nativeWindow.getSurfaceSize();
327 return reqSize; // assume we got the requested size
328 }
329
checkSurfaceSize(const Library & egl,EGLDisplay eglDisplay,EGLSurface eglSurface,const NativeWindow & nativeWindow,IVec2 reqSize,ResultCollector & status)330 IVec2 checkSurfaceSize (const Library& egl,
331 EGLDisplay eglDisplay,
332 EGLSurface eglSurface,
333 const NativeWindow& nativeWindow,
334 IVec2 reqSize,
335 ResultCollector& status)
336 {
337 const IVec2 nativeSize = getNativeSurfaceSize(nativeWindow, reqSize);
338 IVec2 eglSize = eglu::getSurfaceSize(egl, eglDisplay, eglSurface);
339 ostringstream oss;
340
341 oss << "Size of EGL surface " << eglSize
342 << " differs from size of native window " << nativeSize;
343 status.check(eglSize == nativeSize, oss.str());
344
345 return eglSize;
346 }
347
iterate(void)348 IterateResult ChangeSurfaceSizeCase::iterate (void)
349 {
350 const Library& egl = m_eglTestCtx.getLibrary();
351 ScopedCurrentContext currentCtx (egl, m_display, **m_surface, **m_surface, **m_context);
352 egl.swapBuffers(m_display, **m_surface);
353 IVec2 oldEglSize = checkSurfaceSize(egl,
354 m_display,
355 **m_surface,
356 *m_nativeWindow,
357 m_oldSize,
358 m_status);
359
360 initSurface(m_gl, oldEglSize);
361
362 this->resize(m_newSize);
363
364 egl.swapBuffers(m_display, **m_surface);
365 EGLU_CHECK_MSG(egl, "eglSwapBuffers()");
366 checkSurfaceSize(egl, m_display, **m_surface, *m_nativeWindow, m_newSize, m_status);
367
368 m_status.setTestContextResult(m_testCtx);
369 return STOP;
370 }
371
372 class PreserveBackBufferCase : public ResizeTest
373 {
374 public:
PreserveBackBufferCase(EglTestContext & eglTestCtx,const ResizeParams & params)375 PreserveBackBufferCase (EglTestContext& eglTestCtx,
376 const ResizeParams& params)
377 : ResizeTest(eglTestCtx, params) {}
378
379 IterateResult iterate (void);
380 EGLenum surfaceType (void) const;
381 };
382
surfaceType(void) const383 EGLenum PreserveBackBufferCase::surfaceType (void) const
384 {
385 return EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
386 }
387
iterate(void)388 IterateResult PreserveBackBufferCase::iterate (void)
389 {
390 const Library& egl = m_eglTestCtx.getLibrary();
391 ScopedCurrentContext currentCtx (egl, m_display, **m_surface, **m_surface, **m_context);
392
393 EGLU_CHECK_CALL(egl, surfaceAttrib(m_display, **m_surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED));
394
395 GLU_EXPECT_NO_ERROR(m_gl.getError(), "GL state erroneous upon initialization!");
396
397 {
398 const IVec2 oldEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface);
399 initSurface(m_gl, oldEglSize);
400
401 m_gl.finish();
402 GLU_EXPECT_NO_ERROR(m_gl.getError(), "glFinish() failed");
403
404 {
405 const Surface oldSurface = readSurface(m_gl, oldEglSize);
406
407 egl.swapBuffers(m_display, **m_surface);
408 this->resize(m_newSize);
409 egl.swapBuffers(m_display, **m_surface);
410 EGLU_CHECK_MSG(egl, "eglSwapBuffers()");
411
412 {
413 const IVec2 newEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface);
414 const Surface newSurface = readSurface(m_gl, newEglSize);
415
416 m_log << TestLog::ImageSet("Corner comparison",
417 "Comparing old and new surfaces at all corners")
418 << TestLog::Image("Before resizing", "Before resizing", oldSurface)
419 << TestLog::Image("After resizing", "After resizing", newSurface)
420 << TestLog::EndImageSet;
421
422 m_status.checkResult(compareCorners(oldSurface, newSurface),
423 QP_TEST_RESULT_QUALITY_WARNING,
424 "Resizing the native window changed the contents of "
425 "the EGL surface");
426 }
427 }
428 }
429
430 m_status.setTestContextResult(m_testCtx);
431 return STOP;
432 }
433
434 typedef tcu::Vector<Interval, 2> IvVec2;
435
436 class UpdateResolutionCase : public ResizeTest
437 {
438 public:
UpdateResolutionCase(EglTestContext & eglTestCtx,const ResizeParams & params)439 UpdateResolutionCase (EglTestContext& eglTestCtx,
440 const ResizeParams& params)
441 : ResizeTest(eglTestCtx, params) {}
442
443 IterateResult iterate (void);
444
445 private:
446 IvVec2 getNativePixelsPerInch (void);
447 };
448
ivVec2(const IVec2 & vec)449 IvVec2 ivVec2 (const IVec2& vec)
450 {
451 return IvVec2(double(vec.x()), double(vec.y()));
452 }
453
approximateInt(int i)454 Interval approximateInt (int i)
455 {
456 const Interval margin(-1.0, 1.0); // The resolution may be rounded
457
458 return (Interval(i) + margin) & Interval(0.0, TCU_INFINITY);
459 }
460
getNativePixelsPerInch(void)461 IvVec2 UpdateResolutionCase::getNativePixelsPerInch (void)
462 {
463 const Library& egl = m_eglTestCtx.getLibrary();
464 const int inchPer10km = 254 * EGL_DISPLAY_SCALING;
465 const IVec2 bufSize = eglu::getSurfaceSize(egl, m_display, **m_surface);
466 const IVec2 winSize = m_nativeWindow->getScreenSize();
467 const IVec2 bufPp10km = eglu::getSurfaceResolution(egl, m_display, **m_surface);
468 const IvVec2 bufPpiI = (IvVec2(approximateInt(bufPp10km.x()),
469 approximateInt(bufPp10km.y()))
470 / Interval(inchPer10km));
471 const IvVec2 winPpiI = ivVec2(winSize) * bufPpiI / ivVec2(bufSize);
472 const IVec2 winPpi (int(winPpiI.x().midpoint()), int(winPpiI.y().midpoint()));
473
474 m_log << TestLog::Message
475 << "EGL surface size: " << bufSize << "\n"
476 << "EGL surface pixel density (pixels / 10 km): " << bufPp10km << "\n"
477 << "Native window size: " << winSize << "\n"
478 << "Native pixel density (ppi): " << winPpi << "\n"
479 << TestLog::EndMessage;
480
481 m_status.checkResult(bufPp10km.x() >= 1 && bufPp10km.y() >= 1,
482 QP_TEST_RESULT_QUALITY_WARNING,
483 "Surface pixel density is less than one pixel per 10 km. "
484 "Is the surface really visible from space?");
485
486 return winPpiI;
487 }
488
iterate(void)489 IterateResult UpdateResolutionCase::iterate (void)
490 {
491 const Library& egl = m_eglTestCtx.getLibrary();
492 ScopedCurrentContext currentCtx (egl, m_display, **m_surface, **m_surface, **m_context);
493
494 if (!hasBits(m_nativeWindow->getCapabilities(),
495 NativeWindow::CAPABILITY_GET_SCREEN_SIZE))
496 TCU_THROW(NotSupportedError, "Unable to determine surface size in screen pixels");
497
498 {
499 const IVec2 oldEglSize = eglu::getSurfaceSize(egl, m_display, **m_surface);
500 initSurface(m_gl, oldEglSize);
501 }
502 {
503 const IvVec2 oldPpi = this->getNativePixelsPerInch();
504 this->resize(m_newSize);
505 EGLU_CHECK_CALL(egl, swapBuffers(m_display, **m_surface));
506 {
507 const IvVec2 newPpi = this->getNativePixelsPerInch();
508 m_status.check(oldPpi.x().intersects(newPpi.x()) &&
509 oldPpi.y().intersects(newPpi.y()),
510 "Window PPI differs after resizing");
511 }
512 }
513
514 m_status.setTestContextResult(m_testCtx);
515 return STOP;
516 }
517
ResizeTests(EglTestContext & eglTestCtx)518 ResizeTests::ResizeTests (EglTestContext& eglTestCtx)
519 : TestCaseGroup(eglTestCtx, "resize", "Tests resizing the native surface")
520 {
521 }
522
523 template <class Case>
createCaseGroup(EglTestContext & eglTestCtx,const string & name,const string & desc)524 TestCaseGroup* createCaseGroup(EglTestContext& eglTestCtx,
525 const string& name,
526 const string& desc)
527 {
528 const ResizeParams params[] =
529 {
530 { "shrink", "Shrink in both dimensions",
531 IVec2(128, 128), IVec2(32, 32) },
532 { "grow", "Grow in both dimensions",
533 IVec2(32, 32), IVec2(128, 128) },
534 { "stretch_width", "Grow horizontally, shrink vertically",
535 IVec2(32, 128), IVec2(128, 32) },
536 { "stretch_height", "Grow vertically, shrink horizontally",
537 IVec2(128, 32), IVec2(32, 128) },
538 };
539 TestCaseGroup* const group =
540 new TestCaseGroup(eglTestCtx, name.c_str(), desc.c_str());
541
542 for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(params); ++ndx)
543 group->addChild(new Case(eglTestCtx, params[ndx]));
544
545 return group;
546 }
547
init(void)548 void ResizeTests::init (void)
549 {
550 addChild(createCaseGroup<ChangeSurfaceSizeCase>(m_eglTestCtx,
551 "surface_size",
552 "EGL surface size update"));
553 addChild(createCaseGroup<PreserveBackBufferCase>(m_eglTestCtx,
554 "back_buffer",
555 "Back buffer contents"));
556 addChild(createCaseGroup<UpdateResolutionCase>(m_eglTestCtx,
557 "pixel_density",
558 "Pixel density"));
559 }
560
561 } // egl
562 } // deqp
563