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