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