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