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