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, ¤tBufferAge));
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, ¤tBuffer);
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, ¤tBuffer);
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, ¤tBuffer);
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, ¤tBuffer);
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, ¤tBuffer);
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