Home | History | Annotate | Download | only in egl
      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 
    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 
    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 
    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()
    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
    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 
    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 
    242 GLES2Renderer::~GLES2Renderer (void)
    243 {
    244 }
    245 
    246 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 
    326 ReferenceRenderer::ReferenceRenderer(void)
    327 {
    328 }
    329 
    330 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 
    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 
    368 PartialUpdateTest::~PartialUpdateTest (void)
    369 {
    370 	deinit();
    371 }
    372 
    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 
    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 
    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 
    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
    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 
    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 
    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, &currentBufferAge));
    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, &currentBuffer);
    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 
    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 
    615 string generateTestName (const vector<PartialUpdateTest::DrawType>& oddFrameDrawType, const vector<PartialUpdateTest::DrawType>& evenFrameDrawType)
    616 {
    617 	return "odd" + generateDrawTypeName(oddFrameDrawType) + "_even" + generateDrawTypeName(evenFrameDrawType);
    618 }
    619 
    620 bool isWindow (const eglu::CandidateConfig& c)
    621 {
    622 	return (c.surfaceType() & EGL_WINDOW_BIT) == EGL_WINDOW_BIT;
    623 }
    624 
    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 
    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 
    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 
    643 void clearColorReference (tcu::Surface* ref, const tcu::Vec4& clearColor)
    644 {
    645 	tcu::clear(ref->getAccess(), clearColor);
    646 }
    647 
    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 
    653 float windowToDeviceCoordinates (int x, int length)
    654 {
    655 	return (2.0f * float(x) / float(length)) - 1.0f;
    656 }
    657 
    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 
    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 
    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, &currentBuffer);
    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, &currentBuffer);
    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 
    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 
    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, &currentBuffer);
    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, &currentBuffer);
    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 
    791 PartialUpdateTests::PartialUpdateTests (EglTestContext& eglTestCtx)
    792 	: TestCaseGroup(eglTestCtx, "partial_update", "Partial update tests")
    793 {
    794 }
    795 
    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