xref: /aosp_15_r20/external/deqp/modules/egl/teglSwapBuffersWithDamageTests.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program EGL Module
3  * ---------------------------------------
4  *
5  * Copyright 2015 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 Test KHR_swap_buffer_with_damage
22  *//*--------------------------------------------------------------------*/
23 
24 #include "teglSwapBuffersWithDamageTests.hpp"
25 
26 #include "tcuImageCompare.hpp"
27 #include "tcuSurface.hpp"
28 #include "tcuTextureUtil.hpp"
29 
30 #include "egluNativeWindow.hpp"
31 #include "egluUtil.hpp"
32 #include "egluConfigFilter.hpp"
33 
34 #include "eglwLibrary.hpp"
35 #include "eglwEnums.hpp"
36 
37 #include "gluDefs.hpp"
38 #include "gluRenderContext.hpp"
39 #include "gluShaderProgram.hpp"
40 
41 #include "glwDefs.hpp"
42 #include "glwEnums.hpp"
43 #include "glwFunctions.hpp"
44 
45 #include "deRandom.hpp"
46 #include "deString.h"
47 
48 #include <string>
49 #include <vector>
50 #include <sstream>
51 
52 using glw::GLubyte;
53 using std::string;
54 using std::vector;
55 using tcu::IVec2;
56 
57 using namespace eglw;
58 
59 namespace deqp
60 {
61 namespace egl
62 {
63 namespace
64 {
65 
66 typedef tcu::Vector<GLubyte, 3> Color;
67 
68 enum DrawType
69 {
70     DRAWTYPE_GLES2_CLEAR,
71     DRAWTYPE_GLES2_RENDER
72 };
73 
74 enum ResizeType
75 {
76     RESIZETYPE_NONE = 0,
77     RESIZETYPE_BEFORE_SWAP,
78     RESIZETYPE_AFTER_SWAP,
79 
80     RESIZETYPE_LAST
81 };
82 
83 struct ColoredRect
84 {
85 public:
86     ColoredRect(const IVec2 &bottomLeft_, const IVec2 &topRight_, const Color &color_);
87     IVec2 bottomLeft;
88     IVec2 topRight;
89     Color color;
90 };
91 
ColoredRect(const IVec2 & bottomLeft_,const IVec2 & topRight_,const Color & color_)92 ColoredRect::ColoredRect(const IVec2 &bottomLeft_, const IVec2 &topRight_, const Color &color_)
93     : bottomLeft(bottomLeft_)
94     , topRight(topRight_)
95     , color(color_)
96 {
97 }
98 
99 struct DrawCommand
100 {
101     DrawCommand(DrawType drawType_, const ColoredRect &rect_);
102     DrawType drawType;
103     ColoredRect rect;
104 };
105 
DrawCommand(DrawType drawType_,const ColoredRect & rect_)106 DrawCommand::DrawCommand(DrawType drawType_, const ColoredRect &rect_) : drawType(drawType_), rect(rect_)
107 {
108 }
109 
110 struct Frame
111 {
112     Frame(int width_, int height_);
113     int width;
114     int height;
115     vector<DrawCommand> draws;
116 };
117 
Frame(int width_,int height_)118 Frame::Frame(int width_, int height_) : width(width_), height(height_)
119 {
120 }
121 
122 typedef vector<Frame> FrameSequence;
123 
124 //helper function declaration
125 EGLConfig getEGLConfig(const Library &egl, EGLDisplay eglDisplay, bool preserveBuffer);
126 void clearColorScreen(const glw::Functions &gl, const tcu::Vec4 &clearColor);
127 float windowToDeviceCoordinates(int x, int length);
128 
129 class GLES2Renderer
130 {
131 public:
132     GLES2Renderer(const glw::Functions &gl);
133     ~GLES2Renderer(void);
134     void render(int width, int height, const Frame &frame) const;
135 
136 private:
137     GLES2Renderer(const GLES2Renderer &);
138     GLES2Renderer &operator=(const GLES2Renderer &);
139 
140     const glw::Functions &m_gl;
141     glu::ShaderProgram m_glProgram;
142     glw::GLuint m_coordLoc;
143     glw::GLuint m_colorLoc;
144 };
145 
146 // generate sources for vertex and fragment buffer
getSources(void)147 glu::ProgramSources getSources(void)
148 {
149     const char *const vertexShaderSource = "attribute mediump vec2 a_pos;\n"
150                                            "attribute mediump vec4 a_color;\n"
151                                            "varying mediump vec4 v_color;\n"
152                                            "void main(void)\n"
153                                            "{\n"
154                                            "\tv_color = a_color;\n"
155                                            "\tgl_Position = vec4(a_pos, 0.0, 1.0);\n"
156                                            "}";
157 
158     const char *const fragmentShaderSource = "varying mediump vec4 v_color;\n"
159                                              "void main(void)\n"
160                                              "{\n"
161                                              "\tgl_FragColor = v_color;\n"
162                                              "}";
163 
164     return glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource);
165 }
166 
GLES2Renderer(const glw::Functions & gl)167 GLES2Renderer::GLES2Renderer(const glw::Functions &gl)
168     : m_gl(gl)
169     , m_glProgram(gl, getSources())
170     , m_coordLoc((glw::GLuint)-1)
171     , m_colorLoc((glw::GLuint)-1)
172 {
173     m_colorLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_color");
174     m_coordLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_pos");
175     GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to get attribute locations");
176 }
177 
~GLES2Renderer(void)178 GLES2Renderer::~GLES2Renderer(void)
179 {
180 }
181 
render(int width,int height,const Frame & frame) const182 void GLES2Renderer::render(int width, int height, const Frame &frame) const
183 {
184     for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
185     {
186         const ColoredRect &coloredRect = frame.draws[drawNdx].rect;
187 
188         if (frame.draws[drawNdx].drawType == DRAWTYPE_GLES2_RENDER)
189         {
190             const float x1 = windowToDeviceCoordinates(coloredRect.bottomLeft.x(), width);
191             const float y1 = windowToDeviceCoordinates(coloredRect.bottomLeft.y(), height);
192             const float x2 = windowToDeviceCoordinates(coloredRect.topRight.x(), width);
193             const float y2 = windowToDeviceCoordinates(coloredRect.topRight.y(), height);
194 
195             const glw::GLfloat coords[] = {
196                 x1, y1, x1, y2, x2, y2,
197 
198                 x2, y2, x2, y1, x1, y1,
199             };
200 
201             const glw::GLubyte colors[] = {
202                 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
203                 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
204                 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
205 
206                 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
207                 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
208                 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
209             };
210 
211             m_gl.useProgram(m_glProgram.getProgram());
212             GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
213 
214             m_gl.enableVertexAttribArray(m_coordLoc);
215             m_gl.enableVertexAttribArray(m_colorLoc);
216             GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to enable attributes");
217 
218             m_gl.vertexAttribPointer(m_coordLoc, 2, GL_FLOAT, GL_FALSE, 0, coords);
219             m_gl.vertexAttribPointer(m_colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
220             GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to set attribute pointers");
221 
222             m_gl.drawArrays(GL_TRIANGLES, 0, DE_LENGTH_OF_ARRAY(coords) / 2);
223             GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays(), failed");
224 
225             m_gl.disableVertexAttribArray(m_coordLoc);
226             m_gl.disableVertexAttribArray(m_colorLoc);
227             GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to disable attributes");
228 
229             m_gl.useProgram(0);
230             GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
231         }
232         else if (frame.draws[drawNdx].drawType == DRAWTYPE_GLES2_CLEAR)
233         {
234             m_gl.enable(GL_SCISSOR_TEST);
235             m_gl.scissor(coloredRect.bottomLeft.x(), coloredRect.bottomLeft.y(),
236                          coloredRect.topRight.x() - coloredRect.bottomLeft.x(),
237                          coloredRect.topRight.y() - coloredRect.bottomLeft.y());
238             m_gl.clearColor(coloredRect.color.x() / 255.0f, coloredRect.color.y() / 255.0f,
239                             coloredRect.color.z() / 255.0f, 1.0f);
240             m_gl.clear(GL_COLOR_BUFFER_BIT);
241             m_gl.disable(GL_SCISSOR_TEST);
242         }
243         else
244             DE_FATAL("Invalid drawtype");
245     }
246 }
247 
248 class SwapBuffersWithDamageTest : public TestCase
249 {
250 public:
251     SwapBuffersWithDamageTest(EglTestContext &eglTestCtx, const vector<DrawType> &frameDrawType, int iterationTimes,
252                               ResizeType resizeType, const char *name, const char *description);
253 
254     ~SwapBuffersWithDamageTest(void);
255 
256     virtual void init(void);
257     void deinit(void);
258     virtual IterateResult iterate(void);
259 
260 protected:
261     virtual EGLConfig getConfig(const Library &egl, EGLDisplay eglDisplay);
262     virtual void checkExtension(const Library &egl, EGLDisplay eglDisplay);
263     void initEGLSurface(EGLConfig config);
264     void initEGLContext(EGLConfig config);
265 
266     eglu::NativeWindow *m_window;
267     EGLConfig m_eglConfig;
268     EGLContext m_eglContext;
269     const int m_seed;
270     const int m_iterationTimes;
271     const vector<DrawType> m_frameDrawType;
272     const ResizeType m_resizeType;
273     EGLDisplay m_eglDisplay;
274     EGLSurface m_eglSurface;
275     glw::Functions m_gl;
276     GLES2Renderer *m_gles2Renderer;
277 };
278 
SwapBuffersWithDamageTest(EglTestContext & eglTestCtx,const vector<DrawType> & frameDrawType,int iterationTimes,ResizeType resizeType,const char * name,const char * description)279 SwapBuffersWithDamageTest::SwapBuffersWithDamageTest(EglTestContext &eglTestCtx, const vector<DrawType> &frameDrawType,
280                                                      int iterationTimes, ResizeType resizeType, const char *name,
281                                                      const char *description)
282     : TestCase(eglTestCtx, name, description)
283     , m_window(DE_NULL)
284     , m_eglContext(EGL_NO_CONTEXT)
285     , m_seed(deStringHash(name))
286     , m_iterationTimes(iterationTimes)
287     , m_frameDrawType(frameDrawType)
288     , m_resizeType(resizeType)
289     , m_eglDisplay(EGL_NO_DISPLAY)
290     , m_eglSurface(EGL_NO_SURFACE)
291     , m_gles2Renderer(DE_NULL)
292 {
293 }
294 
~SwapBuffersWithDamageTest(void)295 SwapBuffersWithDamageTest::~SwapBuffersWithDamageTest(void)
296 {
297     deinit();
298 }
299 
getConfig(const Library & egl,EGLDisplay eglDisplay)300 EGLConfig SwapBuffersWithDamageTest::getConfig(const Library &egl, EGLDisplay eglDisplay)
301 {
302     return getEGLConfig(egl, eglDisplay, false);
303 }
304 
checkExtension(const Library & egl,EGLDisplay eglDisplay)305 void SwapBuffersWithDamageTest::checkExtension(const Library &egl, EGLDisplay eglDisplay)
306 {
307     if (!eglu::hasExtension(egl, eglDisplay, "EGL_KHR_swap_buffers_with_damage"))
308         TCU_THROW(NotSupportedError, "EGL_KHR_swap_buffers_with_damage is not supported");
309 }
310 
init(void)311 void SwapBuffersWithDamageTest::init(void)
312 {
313     const Library &egl = m_eglTestCtx.getLibrary();
314 
315     m_eglDisplay = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
316     m_eglConfig  = getConfig(egl, m_eglDisplay);
317 
318     checkExtension(egl, m_eglDisplay);
319 
320     initEGLSurface(m_eglConfig);
321     initEGLContext(m_eglConfig);
322 
323     m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2, 0));
324     m_gles2Renderer = new GLES2Renderer(m_gl);
325 }
326 
deinit(void)327 void SwapBuffersWithDamageTest::deinit(void)
328 {
329     const Library &egl = m_eglTestCtx.getLibrary();
330 
331     delete m_gles2Renderer;
332     m_gles2Renderer = DE_NULL;
333 
334     if (m_eglContext != EGL_NO_CONTEXT)
335     {
336         egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
337         egl.destroyContext(m_eglDisplay, m_eglContext);
338         m_eglContext = EGL_NO_CONTEXT;
339     }
340 
341     if (m_eglSurface != EGL_NO_SURFACE)
342     {
343         egl.destroySurface(m_eglDisplay, m_eglSurface);
344         m_eglSurface = EGL_NO_SURFACE;
345     }
346 
347     if (m_eglDisplay != EGL_NO_DISPLAY)
348     {
349         egl.terminate(m_eglDisplay);
350         m_eglDisplay = EGL_NO_DISPLAY;
351     }
352 
353     delete m_window;
354     m_window = DE_NULL;
355 }
356 
initEGLSurface(EGLConfig config)357 void SwapBuffersWithDamageTest::initEGLSurface(EGLConfig config)
358 {
359     const eglu::NativeWindowFactory &factory =
360         eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
361     m_window =
362         factory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_eglDisplay, config, DE_NULL,
363                              eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
364     m_eglSurface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_eglDisplay, config, DE_NULL);
365 }
366 
initEGLContext(EGLConfig config)367 void SwapBuffersWithDamageTest::initEGLContext(EGLConfig config)
368 {
369     const Library &egl        = m_eglTestCtx.getLibrary();
370     const EGLint attribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
371 
372     egl.bindAPI(EGL_OPENGL_ES_API);
373     m_eglContext = egl.createContext(m_eglDisplay, config, EGL_NO_CONTEXT, attribList);
374     EGLU_CHECK_MSG(egl, "eglCreateContext");
375     TCU_CHECK(m_eglSurface != EGL_NO_SURFACE);
376     egl.makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
377     EGLU_CHECK_MSG(egl, "eglMakeCurrent");
378 }
379 
380 FrameSequence generateFrameSequence(const vector<DrawType> &frameDrawType, de::Random &rnd, int numFrames, int width,
381                                     int height);
382 vector<EGLint> getDamageRegion(const Frame &frame);
383 
iterate(void)384 TestCase::IterateResult SwapBuffersWithDamageTest::iterate(void)
385 {
386     de::Random rnd(m_seed);
387     const Library &egl     = m_eglTestCtx.getLibrary();
388     const int width        = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
389     const int height       = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
390     const float clearRed   = rnd.getFloat();
391     const float clearGreen = rnd.getFloat();
392     const float clearBlue  = rnd.getFloat();
393     const tcu::Vec4 clearColor(clearRed, clearGreen, clearBlue, 1.0f);
394     const int numFrames               = 24; // (width, height) = (480, 480) --> numFrame = 24, divisible
395     const FrameSequence frameSequence = generateFrameSequence(m_frameDrawType, rnd, numFrames, width, height);
396 
397     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
398     EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
399 
400     for (int iterationNdx = 0; iterationNdx < m_iterationTimes; iterationNdx++)
401     {
402         for (int currentFrameNdx = 0; currentFrameNdx < numFrames; currentFrameNdx++)
403         {
404             vector<EGLint> damageRegion = getDamageRegion(frameSequence[currentFrameNdx]);
405 
406             clearColorScreen(m_gl, clearColor);
407             for (int ndx = 0; ndx <= currentFrameNdx; ndx++)
408                 m_gles2Renderer->render(width, height, frameSequence[ndx]);
409 
410             if (m_resizeType == RESIZETYPE_BEFORE_SWAP)
411             {
412                 if (currentFrameNdx % 2 == 0)
413                     m_window->setSurfaceSize(IVec2(width * 2, height / 2));
414                 else
415                     m_window->setSurfaceSize(IVec2(height / 2, width * 2));
416             }
417 
418             EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0],
419                                                           (EGLint)damageRegion.size() / 4));
420 
421             if (m_resizeType == RESIZETYPE_AFTER_SWAP)
422             {
423                 if (currentFrameNdx % 2 == 0)
424                     m_window->setSurfaceSize(IVec2(width * 2, height / 2));
425                 else
426                     m_window->setSurfaceSize(IVec2(height / 2, width * 2));
427             }
428         }
429     }
430     return STOP;
431 }
432 
433 class SwapBuffersWithDamageAndPreserveBufferTest : public SwapBuffersWithDamageTest
434 {
435 public:
436     SwapBuffersWithDamageAndPreserveBufferTest(EglTestContext &eglTestCtx, const vector<DrawType> &frameDrawType,
437                                                int iterationTimes, ResizeType resizeType, const char *name,
438                                                const char *description);
439 
440     IterateResult iterate(void);
441 
442 protected:
443     EGLConfig getConfig(const Library &egl, EGLDisplay eglDisplay);
444 };
445 
SwapBuffersWithDamageAndPreserveBufferTest(EglTestContext & eglTestCtx,const vector<DrawType> & frameDrawType,int iterationTimes,ResizeType resizeType,const char * name,const char * description)446 SwapBuffersWithDamageAndPreserveBufferTest::SwapBuffersWithDamageAndPreserveBufferTest(
447     EglTestContext &eglTestCtx, const vector<DrawType> &frameDrawType, int iterationTimes, ResizeType resizeType,
448     const char *name, const char *description)
449     : SwapBuffersWithDamageTest(eglTestCtx, frameDrawType, iterationTimes, resizeType, name, description)
450 {
451 }
452 
getConfig(const Library & egl,EGLDisplay eglDisplay)453 EGLConfig SwapBuffersWithDamageAndPreserveBufferTest::getConfig(const Library &egl, EGLDisplay eglDisplay)
454 {
455     return getEGLConfig(egl, eglDisplay, true);
456 }
457 
iterate(void)458 TestCase::IterateResult SwapBuffersWithDamageAndPreserveBufferTest::iterate(void)
459 {
460 
461     de::Random rnd(m_seed);
462     const Library &egl     = m_eglTestCtx.getLibrary();
463     const int width        = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
464     const int height       = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
465     const float clearRed   = rnd.getFloat();
466     const float clearGreen = rnd.getFloat();
467     const float clearBlue  = rnd.getFloat();
468     const tcu::Vec4 clearColor(clearRed, clearGreen, clearBlue, 1.0f);
469     const int numFrames               = 24; // (width, height) = (480, 480) --> numFrame = 24, divisible
470     const FrameSequence frameSequence = generateFrameSequence(m_frameDrawType, rnd, numFrames, width, height);
471 
472     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
473     EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED));
474 
475     for (int iterationNdx = 0; iterationNdx < m_iterationTimes; iterationNdx++)
476     {
477         clearColorScreen(m_gl, clearColor);
478         EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, DE_NULL, 0));
479 
480         for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
481         {
482             const Frame &currentFrame   = frameSequence[frameNdx];
483             vector<EGLint> damageRegion = getDamageRegion(currentFrame);
484 
485             m_gles2Renderer->render(width, height, currentFrame);
486 
487             if (m_resizeType == RESIZETYPE_BEFORE_SWAP)
488             {
489                 if (frameNdx % 2 == 0)
490                     m_window->setSurfaceSize(IVec2(width * 2, height / 2));
491                 else
492                     m_window->setSurfaceSize(IVec2(height / 2, width * 2));
493             }
494 
495             EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0],
496                                                           (EGLint)damageRegion.size() / 4));
497 
498             if (m_resizeType == RESIZETYPE_AFTER_SWAP)
499             {
500                 if (frameNdx % 2 == 0)
501                     m_window->setSurfaceSize(IVec2(width * 2, height / 2));
502                 else
503                     m_window->setSurfaceSize(IVec2(height / 2, width * 2));
504             }
505         }
506     }
507 
508     return STOP;
509 }
510 
511 class SwapBuffersWithDamageAndBufferAgeTest : public SwapBuffersWithDamageTest
512 {
513 public:
514     SwapBuffersWithDamageAndBufferAgeTest(EglTestContext &eglTestCtx, const vector<DrawType> &frameDrawType,
515                                           int iterationTimes, ResizeType resizeType, const char *name,
516                                           const char *description);
517 
518     IterateResult iterate(void);
519 
520 protected:
521     void checkExtension(const Library &egl, EGLDisplay eglDisplay);
522 };
523 
SwapBuffersWithDamageAndBufferAgeTest(EglTestContext & eglTestCtx,const vector<DrawType> & frameDrawType,int iterationTimes,ResizeType resizeType,const char * name,const char * description)524 SwapBuffersWithDamageAndBufferAgeTest::SwapBuffersWithDamageAndBufferAgeTest(EglTestContext &eglTestCtx,
525                                                                              const vector<DrawType> &frameDrawType,
526                                                                              int iterationTimes, ResizeType resizeType,
527                                                                              const char *name, const char *description)
528     : SwapBuffersWithDamageTest(eglTestCtx, frameDrawType, iterationTimes, resizeType, name, description)
529 {
530 }
531 
checkExtension(const Library & egl,EGLDisplay eglDisplay)532 void SwapBuffersWithDamageAndBufferAgeTest::checkExtension(const Library &egl, EGLDisplay eglDisplay)
533 {
534     if (!eglu::hasExtension(egl, eglDisplay, "EGL_KHR_swap_buffers_with_damage"))
535         TCU_THROW(NotSupportedError, "EGL_KHR_swap_buffers_with_damage is not supported");
536 
537     if (!eglu::hasExtension(egl, eglDisplay, "EGL_EXT_buffer_age"))
538         TCU_THROW(NotSupportedError, "EGL_EXT_buffer_age not supported");
539 }
540 
iterate(void)541 TestCase::IterateResult SwapBuffersWithDamageAndBufferAgeTest::iterate(void)
542 {
543 
544     de::Random rnd(m_seed);
545     const Library &egl     = m_eglTestCtx.getLibrary();
546     const int width        = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
547     const int height       = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
548     const float clearRed   = rnd.getFloat();
549     const float clearGreen = rnd.getFloat();
550     const float clearBlue  = rnd.getFloat();
551     const tcu::Vec4 clearColor(clearRed, clearGreen, clearBlue, 1.0f);
552     const int numFrames               = 24; // (width, height) = (480, 480) --> numFrame = 24, divisible
553     const FrameSequence frameSequence = generateFrameSequence(m_frameDrawType, rnd, numFrames, width, height);
554 
555     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
556     EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
557 
558     for (int iterationNdx = 0; iterationNdx < m_iterationTimes; iterationNdx++)
559     {
560         clearColorScreen(m_gl, clearColor);
561         EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, DE_NULL, 0));
562 
563         for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
564         {
565             vector<EGLint> damageRegion;
566             int bufferAge     = -1;
567             int startFrameNdx = -1;
568             int endFrameNdx   = frameNdx;
569 
570             EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_EXT, &bufferAge));
571 
572             if (bufferAge < 0) // invalid buffer age
573             {
574                 std::ostringstream stream;
575                 stream << "Fail, the age is invalid. Age: " << bufferAge << ", frameNdx: " << frameNdx;
576                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, stream.str().c_str());
577                 return STOP;
578             }
579 
580             if (bufferAge == 0 || bufferAge > frameNdx)
581             {
582                 clearColorScreen(m_gl, clearColor);
583                 startFrameNdx = 0;
584             }
585             else
586                 startFrameNdx = frameNdx - bufferAge + 1;
587 
588             for (int ndx = startFrameNdx; ndx <= endFrameNdx; ndx++)
589             {
590                 const vector<EGLint> partialDamageRegion = getDamageRegion(frameSequence[ndx]);
591 
592                 damageRegion.insert(damageRegion.end(), partialDamageRegion.begin(), partialDamageRegion.end());
593                 m_gles2Renderer->render(width, height, frameSequence[ndx]);
594             }
595 
596             if (m_resizeType == RESIZETYPE_BEFORE_SWAP)
597             {
598                 if (frameNdx % 2 == 0)
599                     m_window->setSurfaceSize(IVec2(width * 2, height / 2));
600                 else
601                     m_window->setSurfaceSize(IVec2(height / 2, width * 2));
602             }
603 
604             EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0],
605                                                           (EGLint)damageRegion.size() / 4));
606 
607             if (m_resizeType == RESIZETYPE_AFTER_SWAP)
608             {
609                 if (frameNdx % 2 == 0)
610                     m_window->setSurfaceSize(IVec2(width * 2, height / 2));
611                 else
612                     m_window->setSurfaceSize(IVec2(height / 2, width * 2));
613             }
614         }
615     }
616     return STOP;
617 }
618 
619 // generate a frame sequence with certain frame for visual verification
generateFrameSequence(const vector<DrawType> & frameDrawType,de::Random & rnd,int numFrames,int width,int height)620 FrameSequence generateFrameSequence(const vector<DrawType> &frameDrawType, de::Random &rnd, int numFrames, int width,
621                                     int height)
622 {
623     const int frameDiff = height / numFrames;
624     const GLubyte r     = rnd.getUint8();
625     const GLubyte g     = rnd.getUint8();
626     const GLubyte b     = rnd.getUint8();
627     const Color color(r, g, b);
628     FrameSequence frameSequence;
629 
630     for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
631     {
632         Frame frame(width, height);
633 
634         for (int rectNdx = 0; rectNdx < (int)frameDrawType.size(); rectNdx++)
635         {
636             const int rectHeight = frameDiff / (int)frameDrawType.size();
637             const ColoredRect rect(IVec2(0, frameNdx * frameDiff + rectNdx * rectHeight),
638                                    IVec2(width, frameNdx * frameDiff + (rectNdx + 1) * rectHeight), color);
639             const DrawCommand drawCommand(frameDrawType[rectNdx], rect);
640 
641             frame.draws.push_back(drawCommand);
642         }
643         frameSequence.push_back(frame);
644     }
645     return frameSequence;
646 }
647 
getDamageRegion(const Frame & frame)648 vector<EGLint> getDamageRegion(const Frame &frame)
649 {
650     vector<EGLint> damageRegion;
651     for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
652     {
653         const ColoredRect &rect = frame.draws[drawNdx].rect;
654         damageRegion.push_back(rect.bottomLeft.x());
655         damageRegion.push_back(rect.bottomLeft.y());
656         damageRegion.push_back(rect.topRight.x() - rect.bottomLeft.x());
657         damageRegion.push_back(rect.topRight.y() - rect.bottomLeft.y());
658     }
659 
660     DE_ASSERT(damageRegion.size() % 4 == 0);
661     return damageRegion;
662 }
663 
generateTestName(const vector<DrawType> & frameDrawType)664 string generateTestName(const vector<DrawType> &frameDrawType)
665 {
666     std::ostringstream stream;
667 
668     for (size_t ndx = 0; ndx < frameDrawType.size(); ndx++)
669     {
670         if (frameDrawType[ndx] == DRAWTYPE_GLES2_RENDER)
671             stream << "render";
672         else if (frameDrawType[ndx] == DRAWTYPE_GLES2_CLEAR)
673             stream << "clear";
674         else
675             DE_ASSERT(false);
676 
677         if (ndx < frameDrawType.size() - 1)
678             stream << "_";
679     }
680 
681     return stream.str();
682 }
683 
generateResizeGroupName(ResizeType resizeType)684 string generateResizeGroupName(ResizeType resizeType)
685 {
686     switch (resizeType)
687     {
688     case RESIZETYPE_NONE:
689         return "no_resize";
690 
691     case RESIZETYPE_AFTER_SWAP:
692         return "resize_after_swap";
693 
694     case RESIZETYPE_BEFORE_SWAP:
695         return "resize_before_swap";
696 
697     default:
698         DE_FATAL("Unknown resize type");
699         return "";
700     }
701 }
702 
isWindow(const eglu::CandidateConfig & c)703 bool isWindow(const eglu::CandidateConfig &c)
704 {
705     return (c.surfaceType() & EGL_WINDOW_BIT) == EGL_WINDOW_BIT;
706 }
707 
isES2Renderable(const eglu::CandidateConfig & c)708 bool isES2Renderable(const eglu::CandidateConfig &c)
709 {
710     return (c.get(EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT;
711 }
712 
hasPreserveSwap(const eglu::CandidateConfig & c)713 bool hasPreserveSwap(const eglu::CandidateConfig &c)
714 {
715     return (c.surfaceType() & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) == EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
716 }
717 
getEGLConfig(const Library & egl,EGLDisplay eglDisplay,bool preserveBuffer)718 EGLConfig getEGLConfig(const Library &egl, EGLDisplay eglDisplay, bool preserveBuffer)
719 {
720     eglu::FilterList filters;
721 
722     filters << isWindow << isES2Renderable;
723     if (preserveBuffer)
724         filters << hasPreserveSwap;
725 
726     return eglu::chooseSingleConfig(egl, eglDisplay, filters);
727 }
728 
clearColorScreen(const glw::Functions & gl,const tcu::Vec4 & clearColor)729 void clearColorScreen(const glw::Functions &gl, const tcu::Vec4 &clearColor)
730 {
731     gl.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
732     gl.clear(GL_COLOR_BUFFER_BIT);
733 }
734 
windowToDeviceCoordinates(int x,int length)735 float windowToDeviceCoordinates(int x, int length)
736 {
737     return (2.0f * float(x) / float(length)) - 1.0f;
738 }
739 
740 } // namespace
741 
SwapBuffersWithDamageTests(EglTestContext & eglTestCtx)742 SwapBuffersWithDamageTests::SwapBuffersWithDamageTests(EglTestContext &eglTestCtx)
743     : TestCaseGroup(eglTestCtx, "swap_buffers_with_damage", "Swap buffers with damages tests")
744 {
745 }
746 
init(void)747 void SwapBuffersWithDamageTests::init(void)
748 {
749     const DrawType clearRender[2] = {DRAWTYPE_GLES2_CLEAR, DRAWTYPE_GLES2_RENDER};
750 
751     const DrawType renderClear[2] = {DRAWTYPE_GLES2_RENDER, DRAWTYPE_GLES2_CLEAR};
752 
753     const ResizeType resizeTypes[] = {RESIZETYPE_NONE, RESIZETYPE_BEFORE_SWAP, RESIZETYPE_AFTER_SWAP};
754 
755     vector<vector<DrawType>> frameDrawTypes;
756     frameDrawTypes.push_back(vector<DrawType>(1, DRAWTYPE_GLES2_CLEAR));
757     frameDrawTypes.push_back(vector<DrawType>(1, DRAWTYPE_GLES2_RENDER));
758     frameDrawTypes.push_back(vector<DrawType>(2, DRAWTYPE_GLES2_CLEAR));
759     frameDrawTypes.push_back(vector<DrawType>(2, DRAWTYPE_GLES2_RENDER));
760     frameDrawTypes.push_back(vector<DrawType>(DE_ARRAY_BEGIN(clearRender), DE_ARRAY_END(clearRender)));
761     frameDrawTypes.push_back(vector<DrawType>(DE_ARRAY_BEGIN(renderClear), DE_ARRAY_END(renderClear)));
762 
763     for (size_t resizeTypeNdx = 0; resizeTypeNdx < DE_LENGTH_OF_ARRAY(resizeTypes); resizeTypeNdx++)
764     {
765         const ResizeType resizeType = resizeTypes[resizeTypeNdx];
766         TestCaseGroup *const resizeGroup =
767             new TestCaseGroup(m_eglTestCtx, generateResizeGroupName(resizeType).c_str(), "");
768 
769         for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
770         {
771             string name = generateTestName(frameDrawTypes[drawTypeNdx]);
772             resizeGroup->addChild(new SwapBuffersWithDamageTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4,
773                                                                 resizeType, name.c_str(), ""));
774         }
775 
776         for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
777         {
778             string name = "preserve_buffer_" + generateTestName(frameDrawTypes[drawTypeNdx]);
779             resizeGroup->addChild(new SwapBuffersWithDamageAndPreserveBufferTest(
780                 m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4, resizeType, name.c_str(), ""));
781         }
782 
783         for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
784         {
785             string name = "buffer_age_" + generateTestName(frameDrawTypes[drawTypeNdx]);
786             resizeGroup->addChild(new SwapBuffersWithDamageAndBufferAgeTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx],
787                                                                             4, resizeType, name.c_str(), ""));
788         }
789 
790         addChild(resizeGroup);
791     }
792 }
793 
794 } // namespace egl
795 } // namespace deqp
796