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
379 if (!eglu::hasExtension(egl, m_eglDisplay, "EGL_KHR_partial_update"))
380 {
381 egl.terminate(m_eglDisplay);
382 m_eglDisplay = EGL_NO_DISPLAY;
383 TCU_THROW(NotSupportedError, "EGL_KHR_partial_update is not supported");
384 }
385
386 m_eglConfig = getEGLConfig(m_eglTestCtx.getLibrary(), m_eglDisplay);
387
388 //create surface and context and make them current
389 initEGLSurface(m_eglConfig);
390 initEGLContext(m_eglConfig);
391
392 m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
393
394 m_supportBufferAge = eglu::hasExtension(egl, m_eglDisplay, "EGL_EXT_buffer_age");
395
396 m_gles2Renderer = new GLES2Renderer(m_gl);
397 m_refRenderer = new ReferenceRenderer();
398 }
399
deinit(void)400 void PartialUpdateTest::deinit (void)
401 {
402 const Library& egl = m_eglTestCtx.getLibrary();
403
404 delete m_refRenderer;
405 m_refRenderer = DE_NULL;
406
407 delete m_gles2Renderer;
408 m_gles2Renderer = DE_NULL;
409
410 if (m_eglContext != EGL_NO_CONTEXT)
411 {
412 egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
413 egl.destroyContext(m_eglDisplay, m_eglContext);
414 m_eglContext = EGL_NO_CONTEXT;
415 }
416
417 if (m_eglSurface != EGL_NO_SURFACE)
418 {
419 egl.destroySurface(m_eglDisplay, m_eglSurface);
420 m_eglSurface = EGL_NO_SURFACE;
421 }
422
423 if (m_eglDisplay != EGL_NO_DISPLAY)
424 {
425 egl.terminate(m_eglDisplay);
426 m_eglDisplay = EGL_NO_DISPLAY;
427 }
428
429 delete m_window;
430 m_window = DE_NULL;
431 }
432
initEGLSurface(EGLConfig config)433 void PartialUpdateTest::initEGLSurface (EGLConfig config)
434 {
435 const eglu::NativeWindowFactory& factory = eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
436 m_window = factory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_eglDisplay, config, DE_NULL,
437 eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
438 m_eglSurface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_eglDisplay, config, DE_NULL);
439 }
440
initEGLContext(EGLConfig config)441 void PartialUpdateTest::initEGLContext (EGLConfig config)
442 {
443 const Library& egl = m_eglTestCtx.getLibrary();
444 const EGLint attribList[] =
445 {
446 EGL_CONTEXT_CLIENT_VERSION, 2,
447 EGL_NONE
448 };
449
450 egl.bindAPI(EGL_OPENGL_ES_API);
451 m_eglContext = egl.createContext(m_eglDisplay, config, EGL_NO_CONTEXT, attribList);
452 EGLU_CHECK_MSG(egl, "eglCreateContext");
453 TCU_CHECK(m_eglSurface != EGL_NO_SURFACE);
454 egl.makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
455 EGLU_CHECK_MSG(egl, "eglMakeCurrent");
456 }
457
458 // return indices of frames that have been written to the given buffer
getFramesOnBuffer(const vector<int> & bufferAges,int frameNdx)459 vector<int> getFramesOnBuffer (const vector<int>& bufferAges, int frameNdx)
460 {
461 DE_ASSERT(frameNdx < (int)bufferAges.size());
462 vector<int> frameOnBuffer;
463 int age = bufferAges[frameNdx];
464 while (age != 0)
465 {
466 frameNdx = frameNdx - age;
467 DE_ASSERT(frameNdx >= 0);
468 frameOnBuffer.push_back(frameNdx);
469 age = bufferAges[frameNdx];
470 }
471
472 reverse(frameOnBuffer.begin(), frameOnBuffer.end());
473 return frameOnBuffer;
474 }
475
getDamageRegion(const Frame & frame,int marginLeft,int marginBottom,int marginRight,int marginTop)476 vector<EGLint> getDamageRegion (const Frame& frame, int marginLeft, int marginBottom, int marginRight, int marginTop)
477 {
478 vector<EGLint> damageRegion;
479 for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
480 {
481 const ColoredRect& rect = frame.draws[drawNdx].rect;
482 damageRegion.push_back(rect.bottomLeft.x() - marginLeft);
483 damageRegion.push_back(rect.bottomLeft.y() - marginBottom);
484 damageRegion.push_back(rect.topRight.x() - rect.bottomLeft.x() + marginLeft + marginRight);
485 damageRegion.push_back(rect.topRight.y() - rect.bottomLeft.y() + marginBottom + marginTop);
486 }
487
488 DE_ASSERT(damageRegion.size() % 4 == 0);
489 return damageRegion;
490 }
491
iterate(void)492 TestCase::IterateResult PartialUpdateTest::iterate (void)
493 {
494 de::Random rnd (m_seed);
495 const Library& egl = m_eglTestCtx.getLibrary();
496 tcu::TestLog& log = m_testCtx.getLog();
497 const int width = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
498 const int height = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
499 const float clearRed = rnd.getFloat();
500 const float clearGreen = rnd.getFloat();
501 const float clearBlue = rnd.getFloat();
502 const tcu::Vec4 clearColor (clearRed, clearGreen, clearBlue, 1.0f);
503 const int numFrames = 20;
504 FrameSequence frameSequence;
505 vector<int> bufferAges;
506 bool hasPositiveAge = false;
507
508 EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
509
510 for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
511 {
512 tcu::Surface currentBuffer (width, height);
513 tcu::Surface refBuffer (width, height);
514 Frame newFrame (width, height);
515 EGLint currentBufferAge = -1;
516
517 if (frameNdx % 2 == 0)
518 generateRandomFrame(newFrame, m_evenFrameDrawType, rnd);
519 else
520 generateRandomFrame(newFrame, m_oddFrameDrawType, rnd);
521
522 frameSequence.push_back(newFrame);
523
524 EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, ¤tBufferAge));
525
526 if (currentBufferAge > frameNdx || currentBufferAge < 0) // invalid buffer age
527 {
528 std::ostringstream stream;
529 stream << "Fail, the age is invalid. Age: " << currentBufferAge << ", frameNdx: " << frameNdx;
530 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, stream.str().c_str());
531 return STOP;
532 }
533
534 bufferAges.push_back(currentBufferAge);
535 DE_ASSERT((int)bufferAges.size() == frameNdx+1);
536
537 if (currentBufferAge > 0)
538 {
539 vector<EGLint> damageRegion;
540
541 hasPositiveAge = true;
542
543 if (m_supportBufferAge)
544 {
545 damageRegion = getDamageRegion(newFrame, 10, 10, 10, 10);
546 }
547 else
548 {
549 damageRegion = getDamageRegion(newFrame, 0, 0, 0, 0);
550 }
551
552 // Set empty damage region to avoid invalidating the framebuffer. The damage area is invalidated
553 // if the buffer age extension is not supported.
554 if (damageRegion.size() == 0)
555 damageRegion = vector<EGLint>(4, 0);
556
557 EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
558 }
559 else
560 {
561 EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, NULL, 0));
562 clearColorScreen(m_gl, clearColor);
563 }
564
565 // during first half, just keep rendering without reading pixel back to mimic ordinary use case
566 if (frameNdx < numFrames/2)
567 m_gles2Renderer->render(width, height, newFrame);
568 else // do verification in the second half
569 {
570 const vector<int> framesOnBuffer = getFramesOnBuffer(bufferAges, frameNdx);
571
572 clearColorReference(&refBuffer, clearColor);
573
574 for (vector<int>::const_iterator it = framesOnBuffer.begin(); it != framesOnBuffer.end(); it++)
575 m_refRenderer->render(&refBuffer, frameSequence[*it]);
576
577 m_gles2Renderer->render(width, height, newFrame);
578 m_refRenderer->render(&refBuffer, newFrame);
579
580 readPixels(m_gl, ¤tBuffer);
581
582 if (!compareToReference(log, refBuffer, currentBuffer, frameNdx, frameNdx))
583 {
584 string errorMessage("Fail, render result is wrong. Buffer age is ");
585 errorMessage += (m_supportBufferAge ? "supported" : "not supported");
586 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, errorMessage.c_str());
587 return STOP;
588 }
589 }
590 EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
591 }
592
593 if (!hasPositiveAge) // fraud behavior, pretend to support partial_update
594 {
595 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail, claim to support partial_update but buffer age is always 0");
596 return STOP;
597 }
598
599 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
600 return STOP;
601 }
602
generateDrawTypeName(const vector<PartialUpdateTest::DrawType> & drawTypes)603 string generateDrawTypeName (const vector<PartialUpdateTest::DrawType>& drawTypes)
604 {
605 std::ostringstream stream;
606 if (drawTypes.size() == 0)
607 return string("_none");
608
609 for (size_t ndx = 0; ndx < drawTypes.size(); ndx++)
610 {
611 if (drawTypes[ndx] == PartialUpdateTest::DRAWTYPE_GLES2_RENDER)
612 stream << "_render";
613 else if (drawTypes[ndx] == PartialUpdateTest::DRAWTYPE_GLES2_CLEAR)
614 stream << "_clear";
615 else
616 DE_ASSERT(false);
617 }
618 return stream.str();
619 }
620
generateTestName(const vector<PartialUpdateTest::DrawType> & oddFrameDrawType,const vector<PartialUpdateTest::DrawType> & evenFrameDrawType)621 string generateTestName (const vector<PartialUpdateTest::DrawType>& oddFrameDrawType, const vector<PartialUpdateTest::DrawType>& evenFrameDrawType)
622 {
623 return "odd" + generateDrawTypeName(oddFrameDrawType) + "_even" + generateDrawTypeName(evenFrameDrawType);
624 }
625
isWindow(const eglu::CandidateConfig & c)626 bool isWindow (const eglu::CandidateConfig& c)
627 {
628 return (c.surfaceType() & EGL_WINDOW_BIT) == EGL_WINDOW_BIT;
629 }
630
isES2Renderable(const eglu::CandidateConfig & c)631 bool isES2Renderable (const eglu::CandidateConfig& c)
632 {
633 return (c.get(EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT;
634 }
635
getEGLConfig(const Library & egl,EGLDisplay eglDisplay)636 EGLConfig getEGLConfig (const Library& egl, EGLDisplay eglDisplay)
637 {
638 eglu::FilterList filters;
639 filters << isWindow << isES2Renderable;
640 return eglu::chooseSingleConfig(egl, eglDisplay, filters);
641 }
642
clearColorScreen(const glw::Functions & gl,const tcu::Vec4 & clearColor)643 void clearColorScreen (const glw::Functions& gl, const tcu::Vec4& clearColor)
644 {
645 gl.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
646 gl.clear(GL_COLOR_BUFFER_BIT);
647 }
648
clearColorReference(tcu::Surface * ref,const tcu::Vec4 & clearColor)649 void clearColorReference (tcu::Surface* ref, const tcu::Vec4& clearColor)
650 {
651 tcu::clear(ref->getAccess(), clearColor);
652 }
653
readPixels(const glw::Functions & gl,tcu::Surface * screen)654 void readPixels (const glw::Functions& gl, tcu::Surface* screen)
655 {
656 gl.readPixels(0, 0, screen->getWidth(), screen->getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, screen->getAccess().getDataPtr());
657 }
658
windowToDeviceCoordinates(int x,int length)659 float windowToDeviceCoordinates (int x, int length)
660 {
661 return (2.0f * float(x) / float(length)) - 1.0f;
662 }
663
compareToReference(tcu::TestLog & log,const tcu::Surface & reference,const tcu::Surface & buffer,int frameNdx,int bufferNum)664 bool compareToReference (tcu::TestLog& log, const tcu::Surface& reference, const tcu::Surface& buffer, int frameNdx, int bufferNum)
665 {
666 std::ostringstream stream;
667 stream << "FrameNdx = " << frameNdx << ", compare current buffer (numbered: " << bufferNum << ") to reference";
668 return tcu::intThresholdPositionDeviationCompare(log, "partial update test", stream.str().c_str(), reference.getAccess(), buffer.getAccess(),
669 tcu::UVec4(8, 8, 8, 0), tcu::IVec3(2,2,0), true, tcu::COMPARE_LOG_RESULT);
670 }
671
672 class RenderOutsideDamageRegion : public PartialUpdateTest
673 {
674 public:
675 RenderOutsideDamageRegion (EglTestContext& eglTestCtx);
676 TestCase::IterateResult iterate (void);
677 };
678
RenderOutsideDamageRegion(EglTestContext & eglTestCtx)679 RenderOutsideDamageRegion::RenderOutsideDamageRegion (EglTestContext& eglTestCtx)
680 : PartialUpdateTest (eglTestCtx, vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), "render_outside_damage_region", "")
681 {
682 }
683
iterate(void)684 TestCase::IterateResult RenderOutsideDamageRegion::iterate (void)
685 {
686 de::Random rnd (m_seed);
687 const Library& egl = m_eglTestCtx.getLibrary();
688 tcu::TestLog& log = m_testCtx.getLog();
689 const int width = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
690 const int height = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
691 const float clearRed = rnd.getFloat();
692 const float clearGreen = rnd.getFloat();
693 const float clearBlue = rnd.getFloat();
694 const tcu::Vec4 clearColor (clearRed, clearGreen, clearBlue, 1.0f);
695 tcu::Surface currentBuffer (width, height);
696 tcu::Surface refBuffer (width, height);
697 Frame frame (width, height);
698
699 EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
700
701 generateRandomFrame(frame, m_evenFrameDrawType, rnd);
702
703 {
704 // render outside the region
705 EGLint bufferAge = -1;
706 vector<EGLint> damageRegion = getDamageRegion(frame, 0, 0, 0, 0);
707
708 EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &bufferAge));
709 EGLU_CHECK_CALL(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), vector<DrawType>(1, DRAWTYPE_GLES2_RENDER), "render_before_set_damage_region", "")
743 {
744 }
745
iterate(void)746 TestCase::IterateResult RenderBeforeSetDamageRegion::iterate (void)
747 {
748 de::Random rnd (m_seed);
749 const Library& egl = m_eglTestCtx.getLibrary();
750 tcu::TestLog& log = m_testCtx.getLog();
751 const int width = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
752 const int height = eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
753 const float clearRed = rnd.getFloat();
754 const float clearGreen = rnd.getFloat();
755 const float clearBlue = rnd.getFloat();
756 const tcu::Vec4 clearColor (clearRed, clearGreen, clearBlue, 1.0f);
757 tcu::Surface currentBuffer (width, height);
758 tcu::Surface refBuffer (width, height);
759 Frame frame (width, height);
760
761 EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
762
763 generateRandomFrame(frame, m_evenFrameDrawType, rnd);
764
765 {
766 // render before setDamageRegion
767 EGLint bufferAge = -1;
768 vector<EGLint> damageRegion = getDamageRegion(frame, 0, 0, 0, 0);
769
770 m_gles2Renderer->render(width, height, frame);
771 EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_KHR, &bufferAge));
772 EGLU_CHECK_CALL(egl, setDamageRegionKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
773
774 // next line will make the bug on Nexus 6 disappear
775 // readPixels(m_gl, ¤tBuffer);
776 }
777
778 EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
779
780 // render a new frame
781 clearColorScreen(m_gl, clearColor);
782 m_gles2Renderer->render(width, height, frame);
783 clearColorReference(&refBuffer, clearColor);
784 m_refRenderer->render(&refBuffer, frame);
785 readPixels(m_gl, ¤tBuffer);
786
787 if (!compareToReference(log, refBuffer, currentBuffer, 0, 0))
788 m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
789 else
790 m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
791
792 return STOP;
793 }
794
795 } // anonymous
796
PartialUpdateTests(EglTestContext & eglTestCtx)797 PartialUpdateTests::PartialUpdateTests (EglTestContext& eglTestCtx)
798 : TestCaseGroup(eglTestCtx, "partial_update", "Partial update tests")
799 {
800 }
801
init(void)802 void PartialUpdateTests::init (void)
803 {
804 const PartialUpdateTest::DrawType clearRender[2] =
805 {
806 PartialUpdateTest::DRAWTYPE_GLES2_CLEAR,
807 PartialUpdateTest::DRAWTYPE_GLES2_RENDER
808 };
809
810 const PartialUpdateTest::DrawType renderClear[2] =
811 {
812 PartialUpdateTest::DRAWTYPE_GLES2_RENDER,
813 PartialUpdateTest::DRAWTYPE_GLES2_CLEAR
814 };
815
816 vector< vector<PartialUpdateTest::DrawType> > frameDrawTypes;
817 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> ());
818 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (1, PartialUpdateTest::DRAWTYPE_GLES2_CLEAR));
819 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (1, PartialUpdateTest::DRAWTYPE_GLES2_RENDER));
820 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (2, PartialUpdateTest::DRAWTYPE_GLES2_CLEAR));
821 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (2, PartialUpdateTest::DRAWTYPE_GLES2_RENDER));
822 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (DE_ARRAY_BEGIN(clearRender), DE_ARRAY_END(clearRender)));
823 frameDrawTypes.push_back(vector<PartialUpdateTest::DrawType> (DE_ARRAY_BEGIN(renderClear), DE_ARRAY_END(renderClear)));
824
825 for (size_t evenNdx = 0; evenNdx < frameDrawTypes.size(); evenNdx++)
826 {
827 const vector<PartialUpdateTest::DrawType>& evenFrameDrawType = frameDrawTypes[evenNdx];
828
829 for (size_t oddNdx = evenNdx; oddNdx < frameDrawTypes.size(); oddNdx++)
830 {
831 const vector<PartialUpdateTest::DrawType>& oddFrameDrawType = frameDrawTypes[oddNdx];
832 const std::string name = generateTestName(oddFrameDrawType, evenFrameDrawType);
833 if (oddFrameDrawType.size() == 0 && evenFrameDrawType.size() == 0)
834 continue;
835
836 addChild(new PartialUpdateTest(m_eglTestCtx, oddFrameDrawType, evenFrameDrawType, name.c_str(), ""));
837 }
838 }
839 addChild(new RenderOutsideDamageRegion(m_eglTestCtx));
840 addChild(new RenderBeforeSetDamageRegion(m_eglTestCtx));
841 }
842
843 } // egl
844 } // deqp
845