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:
ResizeTest(EglTestContext & eglTestCtx,const ResizeParams & params)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:
surfaceType(void) const109 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
getEGLConfig(const EGLDisplay eglDisplay,EGLenum surfaceType)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
init(void)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
deinit(void)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
resize(IVec2 size)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:
ChangeSurfaceSizeCase(EglTestContext & eglTestCtx,const ResizeParams & params)205 ChangeSurfaceSizeCase (EglTestContext& eglTestCtx,
206 const ResizeParams& params)
207 : ResizeTest(eglTestCtx, params) {}
208
209 IterateResult iterate (void);
210 };
211
drawRectangle(const glw::Functions & gl,IVec2 pos,IVec2 size,Vec3 color)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
initSurface(const glw::Functions & gl,IVec2 oldSize)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
compareRectangles(const ConstPixelBufferAccess & rectA,const ConstPixelBufferAccess & rectB)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.
compareCorners(const Surface & oldSurface,const Surface & newSurface)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
readSurface(const glw::Functions & gl,IVec2 size)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>
hasBits(T bitSet,T requiredBits)300 inline bool hasBits (T bitSet, T requiredBits)
301 {
302 return (bitSet & requiredBits) == requiredBits;
303 }
304
getNativeSurfaceSize(const NativeWindow & nativeWindow,IVec2 reqSize)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
checkSurfaceSize(EGLDisplay eglDisplay,EGLSurface eglSurface,const NativeWindow & nativeWindow,IVec2 reqSize,ResultCollector & status)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
iterate(void)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:
PreserveBackBufferCase(EglTestContext & eglTestCtx,const ResizeParams & params)356 PreserveBackBufferCase (EglTestContext& eglTestCtx,
357 const ResizeParams& params)
358 : ResizeTest(eglTestCtx, params) {}
359
360 IterateResult iterate (void);
361 EGLenum surfaceType (void) const;
362 };
363
surfaceType(void) const364 EGLenum PreserveBackBufferCase::surfaceType (void) const
365 {
366 return EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
367 }
368
iterate(void)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:
UpdateResolutionCase(EglTestContext & eglTestCtx,const ResizeParams & params)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
ivVec2(const IVec2 & vec)428 IvVec2 ivVec2 (const IVec2& vec)
429 {
430 return IvVec2(double(vec.x()), double(vec.y()));
431 }
432
approximateInt(int i)433 Interval approximateInt (int i)
434 {
435 return (Interval(i) + Interval(-1.0, 1.0)) & Interval(0.0, TCU_INFINITY);
436 }
437
getNativePixelsPerInch(void)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
iterate(void)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
ResizeTests(EglTestContext & eglTestCtx)494 ResizeTests::ResizeTests (EglTestContext& eglTestCtx)
495 : TestCaseGroup(eglTestCtx, "resize", "Tests resizing the native surface")
496 {
497 }
498
499 template <class Case>
createCaseGroup(EglTestContext & eglTestCtx,const string & name,const string & desc)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
init(void)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