xref: /aosp_15_r20/external/deqp/modules/egl/teglResizeTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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 &params)
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 &params) : 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 &params) : 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 &params) : 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