xref: /aosp_15_r20/external/deqp/modules/egl/teglPartialUpdateTests.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_partial_update
22  *//*--------------------------------------------------------------------*/
23 
24 #include "teglPartialUpdateTests.hpp"
25 
26 #include "tcuImageCompare.hpp"
27 #include "tcuTestLog.hpp"
28 #include "tcuSurface.hpp"
29 #include "tcuTextureUtil.hpp"
30 
31 #include "egluNativeWindow.hpp"
32 #include "egluUtil.hpp"
33 #include "egluConfigFilter.hpp"
34 
35 #include "eglwLibrary.hpp"
36 #include "eglwEnums.hpp"
37 
38 #include "gluDefs.hpp"
39 #include "gluRenderContext.hpp"
40 #include "gluShaderProgram.hpp"
41 
42 #include "glwDefs.hpp"
43 #include "glwEnums.hpp"
44 #include "glwFunctions.hpp"
45 
46 #include "deRandom.hpp"
47 #include "deString.h"
48 
49 #include <string>
50 #include <vector>
51 #include <sstream>
52 
53 using glw::GLubyte;
54 using std::string;
55 using std::vector;
56 using tcu::IVec2;
57 
58 using namespace eglw;
59 
60 namespace deqp
61 {
62 namespace egl
63 {
64 namespace
65 {
66 
67 typedef tcu::Vector<GLubyte, 3> Color;
68 
69 class GLES2Renderer;
70 
71 class ReferenceRenderer;
72 
73 class PartialUpdateTest : public TestCase
74 {
75 public:
76     enum DrawType
77     {
78         DRAWTYPE_GLES2_CLEAR,
79         DRAWTYPE_GLES2_RENDER
80     };
81 
82     PartialUpdateTest(EglTestContext &eglTestCtx, const vector<DrawType> &oddFrameDrawType,
83                       const vector<DrawType> &evenFrameDrawType, const char *name, const char *description);
84     ~PartialUpdateTest(void);
85 
86     void init(void);
87     void deinit(void);
88     IterateResult iterate(void);
89 
90 private:
91     eglu::NativeWindow *m_window;
92     EGLConfig m_eglConfig;
93     EGLContext m_eglContext;
94 
95 protected:
96     void initEGLSurface(EGLConfig config);
97     void initEGLContext(EGLConfig config);
98 
99     const int m_seed;
100     const vector<DrawType> m_oddFrameDrawType;
101     const vector<DrawType> m_evenFrameDrawType;
102 
103     bool m_supportBufferAge;
104     EGLDisplay m_eglDisplay;
105     EGLSurface m_eglSurface;
106     glw::Functions m_gl;
107 
108     GLES2Renderer *m_gles2Renderer;
109     ReferenceRenderer *m_refRenderer;
110 };
111 
112 struct ColoredRect
113 {
114 public:
115     ColoredRect(const IVec2 &bottomLeft_, const IVec2 &topRight_, const Color &color_);
116     IVec2 bottomLeft;
117     IVec2 topRight;
118     Color color;
119 };
120 
ColoredRect(const IVec2 & bottomLeft_,const IVec2 & topRight_,const Color & color_)121 ColoredRect::ColoredRect(const IVec2 &bottomLeft_, const IVec2 &topRight_, const Color &color_)
122     : bottomLeft(bottomLeft_)
123     , topRight(topRight_)
124     , color(color_)
125 {
126 }
127 
128 struct DrawCommand
129 {
130     DrawCommand(const PartialUpdateTest::DrawType drawType_, const ColoredRect &rect_);
131     PartialUpdateTest::DrawType drawType;
132     ColoredRect rect;
133 };
134 
DrawCommand(const PartialUpdateTest::DrawType drawType_,const ColoredRect & rect_)135 DrawCommand::DrawCommand(const PartialUpdateTest::DrawType drawType_, const ColoredRect &rect_)
136     : drawType(drawType_)
137     , rect(rect_)
138 {
139 }
140 
141 struct Frame
142 {
143     Frame(int width_, int height_);
144     int width;
145     int height;
146     vector<DrawCommand> draws;
147 };
148 
Frame(int width_,int height_)149 Frame::Frame(int width_, int height_) : width(width_), height(height_)
150 {
151 }
152 
153 // (x1,y1) lie in the lower-left quadrant while (x2,y2) lie in the upper-right.
154 // the coords are multiplied by 4 to amplify the minimial difference between coords to 4 (if not zero)
155 // to avoid the situation where two edges are too close to each other which makes the rounding error
156 // intoleratable by compareToReference()
generateRandomFrame(Frame & dst,const vector<PartialUpdateTest::DrawType> & drawTypes,de::Random & rnd)157 void generateRandomFrame(Frame &dst, const vector<PartialUpdateTest::DrawType> &drawTypes, de::Random &rnd)
158 {
159     for (size_t ndx = 0; ndx < drawTypes.size(); ndx++)
160     {
161         const int x1    = rnd.getInt(0, (dst.width - 1) / 8) * 4;
162         const int y1    = rnd.getInt(0, (dst.height - 1) / 8) * 4;
163         const int x2    = rnd.getInt((dst.width - 1) / 8, (dst.width - 1) / 4) * 4;
164         const int y2    = rnd.getInt((dst.height - 1) / 8, (dst.height - 1) / 4) * 4;
165         const GLubyte r = rnd.getUint8();
166         const GLubyte g = rnd.getUint8();
167         const GLubyte b = rnd.getUint8();
168         const ColoredRect coloredRect(IVec2(x1, y1), IVec2(x2, y2), Color(r, g, b));
169         const DrawCommand drawCommand(drawTypes[ndx], coloredRect);
170 
171         dst.draws.push_back(drawCommand);
172     }
173 }
174 
175 typedef vector<Frame> FrameSequence;
176 
177 //helper function declaration
178 EGLConfig getEGLConfig(const Library &egl, EGLDisplay eglDisplay);
179 void clearColorScreen(const glw::Functions &gl, const tcu::Vec4 &clearColor);
180 void clearColorReference(tcu::Surface *ref, const tcu::Vec4 &clearColor);
181 void readPixels(const glw::Functions &gl, tcu::Surface *screen);
182 float windowToDeviceCoordinates(int x, int length);
183 bool compareToReference(tcu::TestLog &log, const tcu::Surface &reference, const tcu::Surface &buffer, int frameNdx,
184                         int bufferNum);
185 vector<int> getFramesOnBuffer(const vector<int> &bufferAges, int frameNdx);
186 
187 class GLES2Renderer
188 {
189 public:
190     GLES2Renderer(const glw::Functions &gl);
191     ~GLES2Renderer(void);
192     void render(int width, int height, const Frame &frame) const;
193 
194 private:
195     GLES2Renderer(const GLES2Renderer &);
196     GLES2Renderer &operator=(const GLES2Renderer &);
197 
198     const glw::Functions &m_gl;
199     glu::ShaderProgram m_glProgram;
200     glw::GLuint m_coordLoc;
201     glw::GLuint m_colorLoc;
202 };
203 
204 // generate sources for vertex and fragment buffer
getSources(void)205 glu::ProgramSources getSources(void)
206 {
207     const char *const vertexShaderSource = "attribute mediump vec2 a_pos;\n"
208                                            "attribute mediump vec4 a_color;\n"
209                                            "varying mediump vec4 v_color;\n"
210                                            "void main(void)\n"
211                                            "{\n"
212                                            "\tv_color = a_color;\n"
213                                            "\tgl_Position = vec4(a_pos, 0.0, 1.0);\n"
214                                            "}";
215 
216     const char *const fragmentShaderSource = "varying mediump vec4 v_color;\n"
217                                              "void main(void)\n"
218                                              "{\n"
219                                              "\tgl_FragColor = v_color;\n"
220                                              "}";
221 
222     return glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource);
223 }
224 
GLES2Renderer(const glw::Functions & gl)225 GLES2Renderer::GLES2Renderer(const glw::Functions &gl)
226     : m_gl(gl)
227     , m_glProgram(gl, getSources())
228     , m_coordLoc((glw::GLuint)-1)
229     , m_colorLoc((glw::GLuint)-1)
230 {
231     m_colorLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_color");
232     m_coordLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_pos");
233     GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to get attribute locations");
234 }
235 
~GLES2Renderer(void)236 GLES2Renderer::~GLES2Renderer(void)
237 {
238 }
239 
render(int width,int height,const Frame & frame) const240 void GLES2Renderer::render(int width, int height, const Frame &frame) const
241 {
242     for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
243     {
244         const ColoredRect &coloredRect = frame.draws[drawNdx].rect;
245 
246         if (frame.draws[drawNdx].drawType == PartialUpdateTest::DRAWTYPE_GLES2_RENDER)
247         {
248             const float x1 = windowToDeviceCoordinates(coloredRect.bottomLeft.x(), width);
249             const float y1 = windowToDeviceCoordinates(coloredRect.bottomLeft.y(), height);
250             const float x2 = windowToDeviceCoordinates(coloredRect.topRight.x(), width);
251             const float y2 = windowToDeviceCoordinates(coloredRect.topRight.y(), height);
252 
253             const glw::GLfloat coords[] = {
254                 x1, y1, x1, y2, x2, y2,
255 
256                 x2, y2, x2, y1, x1, y1,
257             };
258 
259             const glw::GLubyte colors[] = {
260                 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
261                 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
262                 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
263 
264                 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
265                 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
266                 coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
267             };
268 
269             m_gl.useProgram(m_glProgram.getProgram());
270             GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
271 
272             m_gl.enableVertexAttribArray(m_coordLoc);
273             m_gl.enableVertexAttribArray(m_colorLoc);
274             GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to enable attributes");
275 
276             m_gl.vertexAttribPointer(m_coordLoc, 2, GL_FLOAT, GL_FALSE, 0, coords);
277             m_gl.vertexAttribPointer(m_colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
278             GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to set attribute pointers");
279 
280             m_gl.drawArrays(GL_TRIANGLES, 0, DE_LENGTH_OF_ARRAY(coords) / 2);
281             GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays(), failed");
282 
283             m_gl.disableVertexAttribArray(m_coordLoc);
284             m_gl.disableVertexAttribArray(m_colorLoc);
285             GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to disable attributes");
286 
287             m_gl.useProgram(0);
288             GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
289         }
290         else if (frame.draws[drawNdx].drawType == PartialUpdateTest::DRAWTYPE_GLES2_CLEAR)
291         {
292             m_gl.enable(GL_SCISSOR_TEST);
293             m_gl.scissor(coloredRect.bottomLeft.x(), coloredRect.bottomLeft.y(),
294                          coloredRect.topRight.x() - coloredRect.bottomLeft.x(),
295                          coloredRect.topRight.y() - coloredRect.bottomLeft.y());
296             m_gl.clearColor(coloredRect.color.x() / 255.0f, coloredRect.color.y() / 255.0f,
297                             coloredRect.color.z() / 255.0f, 1.0f);
298             m_gl.clear(GL_COLOR_BUFFER_BIT);
299             m_gl.disable(GL_SCISSOR_TEST);
300         }
301         else
302             DE_FATAL("Invalid drawtype");
303     }
304 }
305 
306 class ReferenceRenderer
307 {
308 public:
309     ReferenceRenderer(void);
310     void render(tcu::Surface *target, const Frame &frame) const;
311 
312 private:
313     ReferenceRenderer(const ReferenceRenderer &);
314     ReferenceRenderer &operator=(const ReferenceRenderer &);
315 };
316 
ReferenceRenderer(void)317 ReferenceRenderer::ReferenceRenderer(void)
318 {
319 }
320 
render(tcu::Surface * target,const Frame & frame) const321 void ReferenceRenderer::render(tcu::Surface *target, const Frame &frame) const
322 {
323     for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
324     {
325         const ColoredRect &coloredRect = frame.draws[drawNdx].rect;
326         if (frame.draws[drawNdx].drawType == PartialUpdateTest::DRAWTYPE_GLES2_RENDER ||
327             frame.draws[drawNdx].drawType == PartialUpdateTest::DRAWTYPE_GLES2_CLEAR)
328         {
329             // tcu does not support degenerate subregions. Since they correspond to no-op rendering, just skip them.
330             if (coloredRect.bottomLeft.x() == coloredRect.topRight.x() ||
331                 coloredRect.bottomLeft.y() == coloredRect.topRight.y())
332                 continue;
333 
334             const tcu::UVec4 color(coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255);
335             tcu::clear(tcu::getSubregion(target->getAccess(), coloredRect.bottomLeft.x(), coloredRect.bottomLeft.y(),
336                                          coloredRect.topRight.x() - coloredRect.bottomLeft.x(),
337                                          coloredRect.topRight.y() - coloredRect.bottomLeft.y()),
338                        color);
339         }
340         else
341             DE_FATAL("Invalid drawtype");
342     }
343 }
344 
PartialUpdateTest(EglTestContext & eglTestCtx,const vector<DrawType> & oddFrameDrawType,const vector<DrawType> & evenFrameDrawType,const char * name,const char * description)345 PartialUpdateTest::PartialUpdateTest(EglTestContext &eglTestCtx, const vector<DrawType> &oddFrameDrawType,
346                                      const vector<DrawType> &evenFrameDrawType, const char *name,
347                                      const char *description)
348     : TestCase(eglTestCtx, name, description)
349     , m_window(DE_NULL)
350     , m_eglContext(EGL_NO_CONTEXT)
351     , m_seed(deStringHash(name))
352     , m_oddFrameDrawType(oddFrameDrawType)
353     , m_evenFrameDrawType(evenFrameDrawType)
354     , m_supportBufferAge(false)
355     , m_eglDisplay(EGL_NO_DISPLAY)
356     , m_eglSurface(EGL_NO_SURFACE)
357     , m_gles2Renderer(DE_NULL)
358     , m_refRenderer(DE_NULL)
359 {
360 }
361 
~PartialUpdateTest(void)362 PartialUpdateTest::~PartialUpdateTest(void)
363 {
364     deinit();
365 }
366 
init(void)367 void PartialUpdateTest::init(void)
368 {
369     const Library &egl = m_eglTestCtx.getLibrary();
370 
371     m_eglDisplay = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
372 
373     if (!eglu::hasExtension(egl, m_eglDisplay, "EGL_KHR_partial_update"))
374     {
375         egl.terminate(m_eglDisplay);
376         m_eglDisplay = EGL_NO_DISPLAY;
377         TCU_THROW(NotSupportedError, "EGL_KHR_partial_update is not supported");
378     }
379 
380     m_eglConfig = getEGLConfig(m_eglTestCtx.getLibrary(), m_eglDisplay);
381 
382     //create surface and context and make them current
383     initEGLSurface(m_eglConfig);
384     initEGLContext(m_eglConfig);
385 
386     m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2, 0));
387 
388     m_supportBufferAge = eglu::hasExtension(egl, m_eglDisplay, "EGL_EXT_buffer_age");
389 
390     m_gles2Renderer = new GLES2Renderer(m_gl);
391     m_refRenderer   = new ReferenceRenderer();
392 }
393 
deinit(void)394 void PartialUpdateTest::deinit(void)
395 {
396     const Library &egl = m_eglTestCtx.getLibrary();
397 
398     delete m_refRenderer;
399     m_refRenderer = DE_NULL;
400 
401     delete m_gles2Renderer;
402     m_gles2Renderer = DE_NULL;
403 
404     if (m_eglContext != EGL_NO_CONTEXT)
405     {
406         egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
407         egl.destroyContext(m_eglDisplay, m_eglContext);
408         m_eglContext = EGL_NO_CONTEXT;
409     }
410 
411     if (m_eglSurface != EGL_NO_SURFACE)
412     {
413         egl.destroySurface(m_eglDisplay, m_eglSurface);
414         m_eglSurface = EGL_NO_SURFACE;
415     }
416 
417     if (m_eglDisplay != EGL_NO_DISPLAY)
418     {
419         egl.terminate(m_eglDisplay);
420         m_eglDisplay = EGL_NO_DISPLAY;
421     }
422 
423     delete m_window;
424     m_window = DE_NULL;
425 }
426 
initEGLSurface(EGLConfig config)427 void PartialUpdateTest::initEGLSurface(EGLConfig config)
428 {
429     const eglu::NativeWindowFactory &factory =
430         eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
431     m_window =
432         factory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_eglDisplay, config, DE_NULL,
433                              eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
434     m_eglSurface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_eglDisplay, config, DE_NULL);
435 }
436 
initEGLContext(EGLConfig config)437 void PartialUpdateTest::initEGLContext(EGLConfig config)
438 {
439     const Library &egl        = m_eglTestCtx.getLibrary();
440     const EGLint attribList[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
441 
442     egl.bindAPI(EGL_OPENGL_ES_API);
443     m_eglContext = egl.createContext(m_eglDisplay, config, EGL_NO_CONTEXT, attribList);
444     EGLU_CHECK_MSG(egl, "eglCreateContext");
445     TCU_CHECK(m_eglSurface != EGL_NO_SURFACE);
446     egl.makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
447     EGLU_CHECK_MSG(egl, "eglMakeCurrent");
448 }
449 
450 // return indices of frames that have been written to the given buffer
getFramesOnBuffer(const vector<int> & bufferAges,int frameNdx)451 vector<int> getFramesOnBuffer(const vector<int> &bufferAges, int frameNdx)
452 {
453     DE_ASSERT(frameNdx < (int)bufferAges.size());
454     vector<int> frameOnBuffer;
455     int age = bufferAges[frameNdx];
456     while (age != 0)
457     {
458         frameNdx = frameNdx - age;
459         DE_ASSERT(frameNdx >= 0);
460         frameOnBuffer.push_back(frameNdx);
461         age = bufferAges[frameNdx];
462     }
463 
464     reverse(frameOnBuffer.begin(), frameOnBuffer.end());
465     return frameOnBuffer;
466 }
467 
getDamageRegion(const Frame & frame,int marginLeft,int marginBottom,int marginRight,int marginTop)468 vector<EGLint> getDamageRegion(const Frame &frame, int marginLeft, int marginBottom, int marginRight, int marginTop)
469 {
470     vector<EGLint> damageRegion;
471     for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
472     {
473         const ColoredRect &rect = frame.draws[drawNdx].rect;
474         damageRegion.push_back(rect.bottomLeft.x() - marginLeft);
475         damageRegion.push_back(rect.bottomLeft.y() - marginBottom);
476         damageRegion.push_back(rect.topRight.x() - rect.bottomLeft.x() + marginLeft + marginRight);
477         damageRegion.push_back(rect.topRight.y() - rect.bottomLeft.y() + marginBottom + marginTop);
478     }
479 
480     DE_ASSERT(damageRegion.size() % 4 == 0);
481     return damageRegion;
482 }
483 
iterate(void)484 TestCase::IterateResult PartialUpdateTest::iterate(void)
485 {
486     de::Random rnd(m_seed);
487     const Library &egl     = m_eglTestCtx.getLibrary();
488     tcu::TestLog &log      = m_testCtx.getLog();
489     const int width        = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
490     const int height       = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
491     const float clearRed   = rnd.getFloat();
492     const float clearGreen = rnd.getFloat();
493     const float clearBlue  = rnd.getFloat();
494     const tcu::Vec4 clearColor(clearRed, clearGreen, clearBlue, 1.0f);
495     const int numFrames = 20;
496     FrameSequence frameSequence;
497     vector<int> bufferAges;
498     bool hasPositiveAge = false;
499 
500     EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
501 
502     for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
503     {
504         tcu::Surface currentBuffer(width, height);
505         tcu::Surface refBuffer(width, height);
506         Frame newFrame(width, height);
507         EGLint currentBufferAge = -1;
508 
509         if (frameNdx % 2 == 0)
510             generateRandomFrame(newFrame, m_evenFrameDrawType, rnd);
511         else
512             generateRandomFrame(newFrame, m_oddFrameDrawType, rnd);
513 
514         frameSequence.push_back(newFrame);
515 
516         EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &currentBufferAge));
517 
518         if (currentBufferAge > frameNdx || currentBufferAge < 0) // invalid buffer age
519         {
520             std::ostringstream stream;
521             stream << "Fail, the age is invalid. Age: " << currentBufferAge << ", frameNdx: " << frameNdx;
522             m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, stream.str().c_str());
523             return STOP;
524         }
525 
526         bufferAges.push_back(currentBufferAge);
527         DE_ASSERT((int)bufferAges.size() == frameNdx + 1);
528 
529         if (currentBufferAge > 0)
530         {
531             vector<EGLint> damageRegion;
532 
533             hasPositiveAge = true;
534 
535             if (m_supportBufferAge)
536             {
537                 damageRegion = getDamageRegion(newFrame, 10, 10, 10, 10);
538             }
539             else
540             {
541                 damageRegion = getDamageRegion(newFrame, 0, 0, 0, 0);
542             }
543 
544             // Set empty damage region to avoid invalidating the framebuffer. The damage area is invalidated
545             // if the buffer age extension is not supported.
546             if (damageRegion.size() == 0)
547                 damageRegion = vector<EGLint>(4, 0);
548 
549             EGLU_CHECK_CALL(
550                 egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size() / 4));
551         }
552         else
553         {
554             EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, NULL, 0));
555             clearColorScreen(m_gl, clearColor);
556         }
557 
558         // during first half, just keep rendering without reading pixel back to mimic ordinary use case
559         if (frameNdx < numFrames / 2)
560             m_gles2Renderer->render(width, height, newFrame);
561         else // do verification in the second half
562         {
563             const vector<int> framesOnBuffer = getFramesOnBuffer(bufferAges, frameNdx);
564 
565             clearColorReference(&refBuffer, clearColor);
566 
567             for (vector<int>::const_iterator it = framesOnBuffer.begin(); it != framesOnBuffer.end(); it++)
568                 m_refRenderer->render(&refBuffer, frameSequence[*it]);
569 
570             m_gles2Renderer->render(width, height, newFrame);
571             m_refRenderer->render(&refBuffer, newFrame);
572 
573             readPixels(m_gl, &currentBuffer);
574 
575             if (!compareToReference(log, refBuffer, currentBuffer, frameNdx, frameNdx))
576             {
577                 string errorMessage("Fail, render result is wrong. Buffer age is ");
578                 errorMessage += (m_supportBufferAge ? "supported" : "not supported");
579                 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, errorMessage.c_str());
580                 return STOP;
581             }
582         }
583         EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
584     }
585 
586     if (!hasPositiveAge) // fraud behavior, pretend to support partial_update
587     {
588         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL,
589                                 "Fail, claim to support partial_update but buffer age is always 0");
590         return STOP;
591     }
592 
593     m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
594     return STOP;
595 }
596 
generateDrawTypeName(const vector<PartialUpdateTest::DrawType> & drawTypes)597 string generateDrawTypeName(const vector<PartialUpdateTest::DrawType> &drawTypes)
598 {
599     std::ostringstream stream;
600     if (drawTypes.size() == 0)
601         return string("_none");
602 
603     for (size_t ndx = 0; ndx < drawTypes.size(); ndx++)
604     {
605         if (drawTypes[ndx] == PartialUpdateTest::DRAWTYPE_GLES2_RENDER)
606             stream << "_render";
607         else if (drawTypes[ndx] == PartialUpdateTest::DRAWTYPE_GLES2_CLEAR)
608             stream << "_clear";
609         else
610             DE_ASSERT(false);
611     }
612     return stream.str();
613 }
614 
generateTestName(const vector<PartialUpdateTest::DrawType> & oddFrameDrawType,const vector<PartialUpdateTest::DrawType> & evenFrameDrawType)615 string generateTestName(const vector<PartialUpdateTest::DrawType> &oddFrameDrawType,
616                         const vector<PartialUpdateTest::DrawType> &evenFrameDrawType)
617 {
618     return "odd" + generateDrawTypeName(oddFrameDrawType) + "_even" + generateDrawTypeName(evenFrameDrawType);
619 }
620 
isWindow(const eglu::CandidateConfig & c)621 bool isWindow(const eglu::CandidateConfig &c)
622 {
623     return (c.surfaceType() & EGL_WINDOW_BIT) == EGL_WINDOW_BIT;
624 }
625 
isES2Renderable(const eglu::CandidateConfig & c)626 bool isES2Renderable(const eglu::CandidateConfig &c)
627 {
628     return (c.get(EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT;
629 }
630 
getEGLConfig(const Library & egl,EGLDisplay eglDisplay)631 EGLConfig getEGLConfig(const Library &egl, EGLDisplay eglDisplay)
632 {
633     eglu::FilterList filters;
634     filters << isWindow << isES2Renderable;
635     return eglu::chooseSingleConfig(egl, eglDisplay, filters);
636 }
637 
clearColorScreen(const glw::Functions & gl,const tcu::Vec4 & clearColor)638 void clearColorScreen(const glw::Functions &gl, const tcu::Vec4 &clearColor)
639 {
640     gl.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
641     gl.clear(GL_COLOR_BUFFER_BIT);
642 }
643 
clearColorReference(tcu::Surface * ref,const tcu::Vec4 & clearColor)644 void clearColorReference(tcu::Surface *ref, const tcu::Vec4 &clearColor)
645 {
646     tcu::clear(ref->getAccess(), clearColor);
647 }
648 
readPixels(const glw::Functions & gl,tcu::Surface * screen)649 void readPixels(const glw::Functions &gl, tcu::Surface *screen)
650 {
651     gl.readPixels(0, 0, screen->getWidth(), screen->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
652                   screen->getAccess().getDataPtr());
653 }
654 
windowToDeviceCoordinates(int x,int length)655 float windowToDeviceCoordinates(int x, int length)
656 {
657     return (2.0f * float(x) / float(length)) - 1.0f;
658 }
659 
compareToReference(tcu::TestLog & log,const tcu::Surface & reference,const tcu::Surface & buffer,int frameNdx,int bufferNum)660 bool compareToReference(tcu::TestLog &log, const tcu::Surface &reference, const tcu::Surface &buffer, int frameNdx,
661                         int bufferNum)
662 {
663     std::ostringstream stream;
664     stream << "FrameNdx = " << frameNdx << ", compare current buffer (numbered: " << bufferNum << ") to reference";
665     return tcu::intThresholdPositionDeviationCompare(log, "partial update test", stream.str().c_str(),
666                                                      reference.getAccess(), buffer.getAccess(), tcu::UVec4(8, 8, 8, 0),
667                                                      tcu::IVec3(2, 2, 0), true, tcu::COMPARE_LOG_RESULT);
668 }
669 
670 class RenderOutsideDamageRegion : public PartialUpdateTest
671 {
672 public:
673     RenderOutsideDamageRegion(EglTestContext &eglTestCtx);
674     TestCase::IterateResult iterate(void);
675 };
676 
RenderOutsideDamageRegion(EglTestContext & eglTestCtx)677 RenderOutsideDamageRegion::RenderOutsideDamageRegion(EglTestContext &eglTestCtx)
678     : PartialUpdateTest(eglTestCtx, vector<DrawType>(1, DRAWTYPE_GLES2_RENDER),
679                         vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), "render_outside_damage_region", "")
680 {
681 }
682 
iterate(void)683 TestCase::IterateResult RenderOutsideDamageRegion::iterate(void)
684 {
685     de::Random rnd(m_seed);
686     const Library &egl     = m_eglTestCtx.getLibrary();
687     tcu::TestLog &log      = m_testCtx.getLog();
688     const int width        = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
689     const int height       = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
690     const float clearRed   = rnd.getFloat();
691     const float clearGreen = rnd.getFloat();
692     const float clearBlue  = rnd.getFloat();
693     const tcu::Vec4 clearColor(clearRed, clearGreen, clearBlue, 1.0f);
694     tcu::Surface currentBuffer(width, height);
695     tcu::Surface refBuffer(width, height);
696     Frame frame(width, height);
697 
698     EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
699 
700     generateRandomFrame(frame, m_evenFrameDrawType, rnd);
701 
702     {
703         // render outside the region
704         EGLint bufferAge            = -1;
705         vector<EGLint> damageRegion = getDamageRegion(frame, 0, 0, 0, 0);
706 
707         EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &bufferAge));
708         EGLU_CHECK_CALL(
709             egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size() / 4));
710         clearColorScreen(m_gl, clearColor);
711         m_gles2Renderer->render(width, height, frame);
712 
713         // next line will make the bug on Nexus 6 disappear
714         // readPixels(m_gl, &currentBuffer);
715     }
716 
717     EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
718 
719     // render a new frame
720     clearColorScreen(m_gl, clearColor);
721     m_gles2Renderer->render(width, height, frame);
722     clearColorReference(&refBuffer, clearColor);
723     m_refRenderer->render(&refBuffer, frame);
724     readPixels(m_gl, &currentBuffer);
725 
726     if (!compareToReference(log, refBuffer, currentBuffer, 0, 0))
727         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail, fail to recover after rendering outside damageRegion");
728     else
729         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
730 
731     return STOP;
732 }
733 
734 class RenderBeforeSetDamageRegion : public PartialUpdateTest
735 {
736 public:
737     RenderBeforeSetDamageRegion(EglTestContext &eglTestCtx);
738     TestCase::IterateResult iterate(void);
739 };
740 
RenderBeforeSetDamageRegion(EglTestContext & eglTestCtx)741 RenderBeforeSetDamageRegion::RenderBeforeSetDamageRegion(EglTestContext &eglTestCtx)
742     : PartialUpdateTest(eglTestCtx, vector<DrawType>(1, DRAWTYPE_GLES2_RENDER),
743                         vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), "render_before_set_damage_region", "")
744 {
745 }
746 
iterate(void)747 TestCase::IterateResult RenderBeforeSetDamageRegion::iterate(void)
748 {
749     de::Random rnd(m_seed);
750     const Library &egl     = m_eglTestCtx.getLibrary();
751     tcu::TestLog &log      = m_testCtx.getLog();
752     const int width        = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
753     const int height       = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
754     const float clearRed   = rnd.getFloat();
755     const float clearGreen = rnd.getFloat();
756     const float clearBlue  = rnd.getFloat();
757     const tcu::Vec4 clearColor(clearRed, clearGreen, clearBlue, 1.0f);
758     tcu::Surface currentBuffer(width, height);
759     tcu::Surface refBuffer(width, height);
760     Frame frame(width, height);
761 
762     EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
763 
764     generateRandomFrame(frame, m_evenFrameDrawType, rnd);
765 
766     {
767         // render before setDamageRegion
768         EGLint bufferAge            = -1;
769         vector<EGLint> damageRegion = getDamageRegion(frame, 0, 0, 0, 0);
770 
771         m_gles2Renderer->render(width, height, frame);
772         EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &bufferAge));
773         EGLU_CHECK_CALL(
774             egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size() / 4));
775 
776         // next line will make the bug on Nexus 6 disappear
777         // readPixels(m_gl, &currentBuffer);
778     }
779 
780     EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
781 
782     // render a new frame
783     clearColorScreen(m_gl, clearColor);
784     m_gles2Renderer->render(width, height, frame);
785     clearColorReference(&refBuffer, clearColor);
786     m_refRenderer->render(&refBuffer, frame);
787     readPixels(m_gl, &currentBuffer);
788 
789     if (!compareToReference(log, refBuffer, currentBuffer, 0, 0))
790         m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
791     else
792         m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
793 
794     return STOP;
795 }
796 
797 } // namespace
798 
PartialUpdateTests(EglTestContext & eglTestCtx)799 PartialUpdateTests::PartialUpdateTests(EglTestContext &eglTestCtx)
800     : TestCaseGroup(eglTestCtx, "partial_update", "Partial update tests")
801 {
802 }
803 
init(void)804 void PartialUpdateTests::init(void)
805 {
806     const PartialUpdateTest::DrawType clearRender[2] = {PartialUpdateTest::DRAWTYPE_GLES2_CLEAR,
807                                                         PartialUpdateTest::DRAWTYPE_GLES2_RENDER};
808 
809     const PartialUpdateTest::DrawType renderClear[2] = {PartialUpdateTest::DRAWTYPE_GLES2_RENDER,
810                                                         PartialUpdateTest::DRAWTYPE_GLES2_CLEAR};
811 
812     vector<vector<PartialUpdateTest::DrawType>> frameDrawTypes;
813     frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType>());
814     frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType>(1, PartialUpdateTest::DRAWTYPE_GLES2_CLEAR));
815     frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType>(1, PartialUpdateTest::DRAWTYPE_GLES2_RENDER));
816     frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType>(2, PartialUpdateTest::DRAWTYPE_GLES2_CLEAR));
817     frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType>(2, PartialUpdateTest::DRAWTYPE_GLES2_RENDER));
818     frameDrawTypes.push_back(
819         vector<PartialUpdateTest::DrawType>(DE_ARRAY_BEGIN(clearRender), DE_ARRAY_END(clearRender)));
820     frameDrawTypes.push_back(
821         vector<PartialUpdateTest::DrawType>(DE_ARRAY_BEGIN(renderClear), DE_ARRAY_END(renderClear)));
822 
823     for (size_t evenNdx = 0; evenNdx < frameDrawTypes.size(); evenNdx++)
824     {
825         const vector<PartialUpdateTest::DrawType> &evenFrameDrawType = frameDrawTypes[evenNdx];
826 
827         for (size_t oddNdx = evenNdx; oddNdx < frameDrawTypes.size(); oddNdx++)
828         {
829             const vector<PartialUpdateTest::DrawType> &oddFrameDrawType = frameDrawTypes[oddNdx];
830             const std::string name = generateTestName(oddFrameDrawType, evenFrameDrawType);
831             if (oddFrameDrawType.size() == 0 && evenFrameDrawType.size() == 0)
832                 continue;
833 
834             addChild(new PartialUpdateTest(m_eglTestCtx, oddFrameDrawType, evenFrameDrawType, name.c_str(), ""));
835         }
836     }
837     addChild(new RenderOutsideDamageRegion(m_eglTestCtx));
838     addChild(new RenderBeforeSetDamageRegion(m_eglTestCtx));
839 }
840 
841 } // namespace egl
842 } // namespace deqp
843