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 ¤tFrame = 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