Add test for EGL_KHR_swap_buffers_with_damage

The tests consist of three groups. One group with no dependency on other APIs,
one group using EGL_BUFFER_PRESERVED and one using EGL_BUFFER_AGE_EXT.

Change-Id: If7e5da80beeef7dffc6286df9632fc78577408cb
diff --git a/Android.mk b/Android.mk
index 5282fc4..9b9b5d2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -284,6 +284,7 @@
 	modules/egl/teglSimpleConfigCase.cpp \
 	modules/egl/teglSurfacelessContextTests.cpp \
 	modules/egl/teglSwapBuffersTests.cpp \
+	modules/egl/teglSwapBuffersWithDamageTests.cpp \
 	modules/egl/teglSyncTests.cpp \
 	modules/egl/teglTestCase.cpp \
 	modules/egl/teglTestPackage.cpp \
diff --git a/android/cts/master/src/egl-new-tests.txt b/android/cts/master/src/egl-new-tests.txt
index 6e1be0b..e3c1538 100644
--- a/android/cts/master/src/egl-new-tests.txt
+++ b/android/cts/master/src/egl-new-tests.txt
@@ -2,3 +2,4 @@
 dEQP-EGL.functional.buffer_age.*
 dEQP-EGL.functional.partial_update.*
 dEQP-EGL.functional.negative_partial_update.*
+dEQP-EGL.functional.swap_buffers_with_damage.*
diff --git a/modules/egl/CMakeLists.txt b/modules/egl/CMakeLists.txt
index 6777eb7..f038943 100644
--- a/modules/egl/CMakeLists.txt
+++ b/modules/egl/CMakeLists.txt
@@ -89,6 +89,8 @@
 	teglNativeCoordMappingTests.cpp
 	teglResizeTests.hpp
 	teglResizeTests.cpp
+	teglSwapBuffersWithDamageTests.hpp
+	teglSwapBuffersWithDamageTests.cpp
 	)
 
 set(DEQP_EGL_LIBS
diff --git a/modules/egl/teglSwapBuffersWithDamageTests.cpp b/modules/egl/teglSwapBuffersWithDamageTests.cpp
new file mode 100644
index 0000000..5a6181e
--- /dev/null
+++ b/modules/egl/teglSwapBuffersWithDamageTests.cpp
@@ -0,0 +1,719 @@
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program EGL Module
+ * ---------------------------------------
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Test KHR_swap_buffer_with_damage
+ *//*--------------------------------------------------------------------*/
+
+#include "teglSwapBuffersWithDamageTests.hpp"
+
+#include "tcuImageCompare.hpp"
+#include "tcuSurface.hpp"
+#include "tcuTextureUtil.hpp"
+
+#include "egluNativeWindow.hpp"
+#include "egluUtil.hpp"
+#include "egluConfigFilter.hpp"
+
+#include "eglwLibrary.hpp"
+#include "eglwEnums.hpp"
+
+#include "gluDefs.hpp"
+#include "gluRenderContext.hpp"
+#include "gluShaderProgram.hpp"
+
+#include "glwDefs.hpp"
+#include "glwEnums.hpp"
+#include "glwFunctions.hpp"
+
+#include "deRandom.hpp"
+#include "deString.h"
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+using std::string;
+using std::vector;
+using glw::GLubyte;
+using tcu::IVec2;
+
+using namespace eglw;
+
+namespace deqp
+{
+namespace egl
+{
+namespace
+{
+
+typedef	tcu::Vector<GLubyte, 3> Color;
+
+enum DrawType
+{
+    DRAWTYPE_GLES2_CLEAR,
+    DRAWTYPE_GLES2_RENDER
+};
+
+struct ColoredRect
+{
+public:
+				ColoredRect (const IVec2& bottomLeft_, const IVec2& topRight_, const Color& color_);
+	IVec2		bottomLeft;
+	IVec2		topRight;
+	Color 		color;
+};
+
+ColoredRect::ColoredRect (const IVec2& bottomLeft_, const IVec2& topRight_, const Color& color_)
+	: bottomLeft	(bottomLeft_)
+	, topRight		(topRight_)
+	, color			(color_)
+{
+}
+
+struct DrawCommand
+{
+				DrawCommand (DrawType drawType_, const ColoredRect& rect_);
+    DrawType	drawType;
+	ColoredRect	rect;
+};
+
+DrawCommand::DrawCommand (DrawType drawType_, const ColoredRect& rect_)
+	: drawType	(drawType_)
+	, rect		(rect_)
+{
+}
+
+struct Frame
+{
+						Frame (int width_, int height_);
+	int 				width;
+	int					height;
+	vector<DrawCommand> draws;
+};
+
+Frame::Frame (int width_, int height_)
+	: width (width_)
+	, height(height_)
+{
+}
+
+typedef vector<Frame> FrameSequence;
+
+//helper function declaration
+EGLConfig		getEGLConfig					(const Library& egl, EGLDisplay eglDisplay, bool preserveBuffer);
+void			clearColorScreen				(const glw::Functions& gl, const tcu::Vec4& clearColor);
+float			windowToDeviceCoordinates		(int x, int length);
+
+class GLES2Renderer
+{
+public:
+							GLES2Renderer		(const glw::Functions& gl);
+							~GLES2Renderer		(void);
+	void					render				(int width, int height, const Frame& frame) const;
+
+private:
+							GLES2Renderer		(const GLES2Renderer&);
+	GLES2Renderer&			operator=			(const GLES2Renderer&);
+
+	const glw::Functions&	m_gl;
+	glu::ShaderProgram		m_glProgram;
+	glw::GLuint				m_coordLoc;
+	glw::GLuint				m_colorLoc;
+};
+
+// generate sources for vertex and fragment buffer
+glu::ProgramSources getSources (void)
+{
+	const char* const vertexShaderSource =
+		"attribute mediump vec2 a_pos;\n"
+		"attribute mediump vec4 a_color;\n"
+		"varying mediump vec4 v_color;\n"
+		"void main(void)\n"
+		"{\n"
+		"\tv_color = a_color;\n"
+		"\tgl_Position = vec4(a_pos, 0.0, 1.0);\n"
+		"}";
+
+	const char* const fragmentShaderSource =
+		"varying mediump vec4 v_color;\n"
+		"void main(void)\n"
+		"{\n"
+		"\tgl_FragColor = v_color;\n"
+		"}";
+
+	return glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource);
+}
+
+GLES2Renderer::GLES2Renderer (const glw::Functions& gl)
+	: m_gl        (gl)
+	, m_glProgram (gl, getSources())
+	, m_coordLoc  ((glw::GLuint)-1)
+	, m_colorLoc  ((glw::GLuint)-1)
+{
+	m_colorLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_color");
+	m_coordLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_pos");
+	GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to get attribute locations");
+}
+
+GLES2Renderer::~GLES2Renderer (void)
+{
+}
+
+void GLES2Renderer::render (int width, int height, const Frame& frame) const
+{
+	for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
+	{
+		const ColoredRect& coloredRect = frame.draws[drawNdx].rect;
+
+		if (frame.draws[drawNdx].drawType == DRAWTYPE_GLES2_RENDER)
+		{
+			const float x1 = windowToDeviceCoordinates(coloredRect.bottomLeft.x(), width);
+			const float y1 = windowToDeviceCoordinates(coloredRect.bottomLeft.y(), height);
+			const float x2 = windowToDeviceCoordinates(coloredRect.topRight.x(), width);
+			const float y2 = windowToDeviceCoordinates(coloredRect.topRight.y(), height);
+
+			const glw::GLfloat coords[] =
+			{
+				x1, y1,
+				x1, y2,
+				x2, y2,
+
+				x2, y2,
+				x2, y1,
+				x1, y1,
+			};
+
+			const glw::GLubyte colors[] =
+			{
+				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
+				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
+				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
+
+				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
+				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
+				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
+			};
+
+			m_gl.useProgram(m_glProgram.getProgram());
+			GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
+
+			m_gl.enableVertexAttribArray(m_coordLoc);
+			m_gl.enableVertexAttribArray(m_colorLoc);
+			GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to enable attributes");
+
+			m_gl.vertexAttribPointer(m_coordLoc, 2, GL_FLOAT, GL_FALSE, 0, coords);
+			m_gl.vertexAttribPointer(m_colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
+			GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to set attribute pointers");
+
+			m_gl.drawArrays(GL_TRIANGLES, 0, DE_LENGTH_OF_ARRAY(coords)/2);
+			GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays(), failed");
+
+			m_gl.disableVertexAttribArray(m_coordLoc);
+			m_gl.disableVertexAttribArray(m_colorLoc);
+			GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to disable attributes");
+
+			m_gl.useProgram(0);
+			GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
+		}
+		else if (frame.draws[drawNdx].drawType == DRAWTYPE_GLES2_CLEAR)
+		{
+			m_gl.enable(GL_SCISSOR_TEST);
+			m_gl.scissor(coloredRect.bottomLeft.x(), coloredRect.bottomLeft.y(),
+						 coloredRect.topRight.x()-coloredRect.bottomLeft.x(), coloredRect.topRight.y()-coloredRect.bottomLeft.y());
+			m_gl.clearColor(coloredRect.color.x()/255.0f, coloredRect.color.y()/255.0f, coloredRect.color.z()/255.0f, 1.0f);
+			m_gl.clear(GL_COLOR_BUFFER_BIT);
+			m_gl.disable(GL_SCISSOR_TEST);
+		}
+		else
+			DE_FATAL("Invalid drawtype");
+	}
+}
+
+class SwapBuffersWithDamageTest : public TestCase
+{
+public:
+								SwapBuffersWithDamageTest		(EglTestContext& eglTestCtx,
+																 const vector<DrawType>& frameDrawType,
+																 int iterationTimes,
+																 const char* name,
+																 const char* description);
+								~SwapBuffersWithDamageTest		(void);
+
+	virtual void				init							(void);
+	void						deinit							(void);
+	virtual IterateResult		iterate							(void);
+
+protected:
+	virtual EGLConfig			getConfig						(const Library& egl, EGLDisplay eglDisplay);
+	virtual void				checkExtension					(const Library& egl, EGLDisplay eglDisplay);
+	void						initEGLSurface					(EGLConfig config);
+	void						initEGLContext					(EGLConfig config);
+
+	eglu::NativeWindow*			m_window;
+	EGLConfig					m_eglConfig;
+	EGLContext					m_eglContext;
+	const int					m_seed;
+	const int					m_iterationTimes;
+	const vector<DrawType>	    m_frameDrawType;
+	EGLDisplay					m_eglDisplay;
+	EGLSurface					m_eglSurface;
+	glw::Functions				m_gl;
+	GLES2Renderer*				m_gles2Renderer;
+};
+
+SwapBuffersWithDamageTest::SwapBuffersWithDamageTest (EglTestContext& eglTestCtx, const vector<DrawType>& frameDrawType, int iterationTimes,  const char* name, const char* description)
+	: TestCase		  (eglTestCtx, name, description)
+	, m_window		  (DE_NULL)
+	, m_eglContext	  (EGL_NO_CONTEXT)
+	, m_seed		  (deStringHash(name))
+	, m_iterationTimes(iterationTimes)
+	, m_frameDrawType (frameDrawType)
+	, m_eglDisplay	  (EGL_NO_DISPLAY)
+	, m_eglSurface	  (EGL_NO_SURFACE)
+	, m_gles2Renderer (DE_NULL)
+{
+}
+
+SwapBuffersWithDamageTest::~SwapBuffersWithDamageTest (void)
+{
+	deinit();
+}
+
+EGLConfig SwapBuffersWithDamageTest::getConfig (const Library& egl, EGLDisplay eglDisplay)
+{
+	return getEGLConfig(egl, eglDisplay, false);
+}
+
+void SwapBuffersWithDamageTest::checkExtension (const Library& egl, EGLDisplay eglDisplay)
+{
+	if (!eglu::hasExtension(egl, eglDisplay, "EGL_KHR_swap_buffers_with_damage"))
+		TCU_THROW(NotSupportedError, "EGL_KHR_swap_buffers_with_damage is not supported");
+}
+
+void SwapBuffersWithDamageTest::init (void)
+{
+	const Library& egl = m_eglTestCtx.getLibrary();
+
+	m_eglDisplay = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
+	m_eglConfig  = getConfig(egl, m_eglDisplay);
+
+	checkExtension(egl, m_eglDisplay);
+
+	initEGLSurface(m_eglConfig);
+	initEGLContext(m_eglConfig);
+
+	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
+	m_gles2Renderer = new GLES2Renderer(m_gl);
+}
+
+void SwapBuffersWithDamageTest::deinit (void)
+{
+	const Library& egl = m_eglTestCtx.getLibrary();
+
+	delete m_gles2Renderer;
+	m_gles2Renderer = DE_NULL;
+
+	if (m_eglContext != EGL_NO_CONTEXT)
+	{
+		egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+		egl.destroyContext(m_eglDisplay, m_eglContext);
+		m_eglContext = EGL_NO_CONTEXT;
+	}
+
+	if (m_eglSurface != EGL_NO_SURFACE)
+	{
+		egl.destroySurface(m_eglDisplay, m_eglSurface);
+		m_eglSurface = EGL_NO_SURFACE;
+	}
+
+	if (m_eglDisplay != EGL_NO_DISPLAY)
+	{
+		egl.terminate(m_eglDisplay);
+		m_eglDisplay = EGL_NO_DISPLAY;
+	}
+
+	delete m_window;
+	m_window = DE_NULL;
+}
+
+void SwapBuffersWithDamageTest::initEGLSurface (EGLConfig config)
+{
+	const eglu::NativeWindowFactory& factory = eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
+	m_window = factory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_eglDisplay, config, DE_NULL,
+									eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
+	m_eglSurface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_eglDisplay, config, DE_NULL);
+}
+
+void SwapBuffersWithDamageTest::initEGLContext (EGLConfig config)
+{
+	const Library& 	egl 		 = m_eglTestCtx.getLibrary();
+	const EGLint 	attribList[] =
+	{
+		EGL_CONTEXT_CLIENT_VERSION, 2,
+		EGL_NONE
+	};
+
+	egl.bindAPI(EGL_OPENGL_ES_API);
+	m_eglContext = egl.createContext(m_eglDisplay, config, EGL_NO_CONTEXT, attribList);
+	EGLU_CHECK_MSG(egl, "eglCreateContext");
+	TCU_CHECK(m_eglSurface != EGL_NO_SURFACE);
+	egl.makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
+	EGLU_CHECK_MSG(egl, "eglMakeCurrent");
+}
+
+FrameSequence	generateFrameSequence	(const vector<DrawType>& frameDrawType, de::Random& rnd, int numFrames, int width, int height);
+vector<EGLint>	getDamageRegion			(const Frame& frame);
+
+TestCase::IterateResult SwapBuffersWithDamageTest::iterate (void)
+{
+	de::Random			rnd				(m_seed);
+	const Library&		egl				= m_eglTestCtx.getLibrary();
+	const int			width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
+	const int			height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
+	const float			clearRed		= rnd.getFloat();
+	const float			clearGreen		= rnd.getFloat();
+	const float			clearBlue		= rnd.getFloat();
+	const tcu::Vec4		clearColor		(clearRed, clearGreen, clearBlue, 1.0f);
+	const int			numFrames		= 24; // (width, height) = (480, 480) --> numFrame = 24, divisible
+	const FrameSequence frameSequence	= generateFrameSequence(m_frameDrawType, rnd, numFrames, width, height);
+
+	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
+	EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
+
+	for (int iterationNdx = 0; iterationNdx < m_iterationTimes; iterationNdx++)
+	{
+		for (int currentFrameNdx = 0; currentFrameNdx < numFrames; currentFrameNdx++)
+		{
+			vector<EGLint>	damageRegion = getDamageRegion(frameSequence[currentFrameNdx]);
+
+			clearColorScreen(m_gl, clearColor);
+			for (int ndx = 0; ndx <= currentFrameNdx; ndx++)
+				m_gles2Renderer->render(width, height, frameSequence[ndx]);
+
+			EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
+		}
+	}
+	return STOP;
+}
+
+class SwapBuffersWithDamageAndPreserveBufferTest : public SwapBuffersWithDamageTest
+{
+public:
+					SwapBuffersWithDamageAndPreserveBufferTest	(EglTestContext& eglTestCtx,
+																 const vector<DrawType>& frameDrawType,
+																 int itertionTimes,
+																 const char* name,
+																 const char* description);
+	IterateResult	iterate										(void);
+
+protected:
+	EGLConfig		getConfig									(const Library& egl, EGLDisplay eglDisplay);
+};
+
+SwapBuffersWithDamageAndPreserveBufferTest::SwapBuffersWithDamageAndPreserveBufferTest (EglTestContext& eglTestCtx,
+																						const vector<DrawType>& frameDrawType,
+																						int iterationTimes,
+																						const char* name,
+																						const char* description)
+	: SwapBuffersWithDamageTest (eglTestCtx, frameDrawType, iterationTimes, name, description)
+{
+}
+
+EGLConfig SwapBuffersWithDamageAndPreserveBufferTest::getConfig (const Library& egl, EGLDisplay eglDisplay)
+{
+	return getEGLConfig(egl, eglDisplay, true);
+}
+
+TestCase::IterateResult SwapBuffersWithDamageAndPreserveBufferTest::iterate (void)
+{
+
+	de::Random			rnd				(m_seed);
+	const Library&		egl				= m_eglTestCtx.getLibrary();
+	const int			width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
+	const int			height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
+	const float			clearRed		= rnd.getFloat();
+	const float			clearGreen		= rnd.getFloat();
+	const float			clearBlue		= rnd.getFloat();
+	const tcu::Vec4		clearColor		(clearRed, clearGreen, clearBlue, 1.0f);
+	const int			numFrames		= 24; // (width, height) = (480, 480) --> numFrame = 24, divisible
+	const FrameSequence frameSequence	= generateFrameSequence(m_frameDrawType, rnd, numFrames, width, height);
+
+	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
+	EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED));
+
+	for (int iterationNdx = 0; iterationNdx < m_iterationTimes; iterationNdx++)
+	{
+		clearColorScreen(m_gl, clearColor);
+		EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, DE_NULL, 0));
+
+		for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
+		{
+			const Frame&	currentFrame = frameSequence[frameNdx];
+			vector<EGLint>	damageRegion = getDamageRegion(currentFrame);
+
+			m_gles2Renderer->render(width, height, currentFrame);
+			EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
+		}
+	}
+
+	return STOP;
+}
+
+class SwapBuffersWithDamageAndBufferAgeTest : public SwapBuffersWithDamageTest
+{
+public:
+					SwapBuffersWithDamageAndBufferAgeTest	(EglTestContext& eglTestCtx,
+															 const vector<DrawType>& frameDrawType,
+															 int iterationTimes,
+															 const char* name,
+															 const char* description);
+	IterateResult	iterate									(void);
+
+protected:
+	void			checkExtension							(const Library& egl, EGLDisplay eglDisplay);
+};
+
+SwapBuffersWithDamageAndBufferAgeTest::SwapBuffersWithDamageAndBufferAgeTest (EglTestContext& eglTestCtx,
+																			  const vector<DrawType>& frameDrawType,
+																			  int iterationTimes,
+																			  const char* name,
+																			  const char* description)
+	: SwapBuffersWithDamageTest (eglTestCtx, frameDrawType, iterationTimes, name, description)
+{
+}
+
+
+void SwapBuffersWithDamageAndBufferAgeTest::checkExtension (const Library& egl, EGLDisplay eglDisplay)
+{
+	if (!eglu::hasExtension(egl, eglDisplay, "EGL_KHR_swap_buffers_with_damage"))
+		TCU_THROW(NotSupportedError, "EGL_KHR_swap_buffers_with_damage is not supported");
+
+	if (!eglu::hasExtension(egl, eglDisplay, "EGL_EXT_buffer_age"))
+		TCU_THROW(NotSupportedError, "EGL_EXT_buffer_age not supported");
+}
+
+TestCase::IterateResult SwapBuffersWithDamageAndBufferAgeTest::iterate (void)
+{
+
+	de::Random			rnd				(m_seed);
+	const Library&		egl				= m_eglTestCtx.getLibrary();
+	const int			width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
+	const int			height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
+	const float			clearRed		= rnd.getFloat();
+	const float			clearGreen		= rnd.getFloat();
+	const float			clearBlue		= rnd.getFloat();
+	const tcu::Vec4		clearColor		(clearRed, clearGreen, clearBlue, 1.0f);
+	const int			numFrames		= 24; // (width, height) = (480, 480) --> numFrame = 24, divisible
+	const FrameSequence frameSequence	= generateFrameSequence(m_frameDrawType, rnd, numFrames, width, height);
+
+	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
+	EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
+
+	for (int iterationNdx = 0; iterationNdx < m_iterationTimes; iterationNdx++)
+	{
+		for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
+		{
+			vector<EGLint>	damageRegion;
+			int				bufferAge		= -1;
+			int				startFrameNdx	= -1;
+			int				endFrameNdx		= frameNdx;
+
+			EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_EXT, &bufferAge));
+
+			if (bufferAge < 0) // invalid buffer age
+			{
+				std::ostringstream stream;
+				stream << "Fail, the age is invalid. Age: " << bufferAge << ", frameNdx: " << frameNdx;
+				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, stream.str().c_str());
+				return STOP;
+			}
+
+			if (bufferAge == 0 || bufferAge > frameNdx)
+			{
+				clearColorScreen(m_gl, clearColor);
+				startFrameNdx = 0;
+			}
+			else
+				startFrameNdx = frameNdx-bufferAge+1;
+
+			for (int ndx = startFrameNdx; ndx <= endFrameNdx; ndx++)
+			{
+				const vector<EGLint> partialDamageRegion = getDamageRegion(frameSequence[ndx]);
+
+				damageRegion.insert(damageRegion.end(), partialDamageRegion.begin(), partialDamageRegion.end());
+				m_gles2Renderer->render(width, height, frameSequence[ndx]);
+			}
+
+			EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
+		}
+	}
+	return STOP;
+}
+
+// generate a frame sequence with certain frame for visual verification
+FrameSequence generateFrameSequence (const vector<DrawType>& frameDrawType, de::Random& rnd, int numFrames, int width, int height)
+{
+	const int			frameDiff		= height / numFrames;
+	const GLubyte		r				= rnd.getUint8();
+	const GLubyte		g				= rnd.getUint8();
+	const GLubyte		b				= rnd.getUint8();
+	const Color			color			(r, g, b);
+	FrameSequence		frameSequence;
+
+	for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
+	{
+		Frame frame (width, height);
+
+		for (int rectNdx = 0; rectNdx < (int)frameDrawType.size(); rectNdx++)
+		{
+			const int			rectHeight		= frameDiff / (int)frameDrawType.size();
+			const ColoredRect	rect			(IVec2(0, frameNdx*frameDiff+rectNdx*rectHeight), IVec2(width, frameNdx*frameDiff+(rectNdx+1)*rectHeight), color);
+			const DrawCommand	drawCommand		(frameDrawType[rectNdx], rect);
+
+			frame.draws.push_back(drawCommand);
+		}
+		frameSequence.push_back(frame);
+	}
+	return frameSequence;
+}
+
+vector<EGLint> getDamageRegion (const Frame& frame)
+{
+	vector<EGLint> damageRegion;
+	for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
+	{
+		const ColoredRect& rect = frame.draws[drawNdx].rect;
+		damageRegion.push_back(rect.bottomLeft.x());
+		damageRegion.push_back(rect.bottomLeft.y());
+		damageRegion.push_back(rect.topRight.x() - rect.bottomLeft.x());
+		damageRegion.push_back(rect.topRight.y() - rect.bottomLeft.y());
+	}
+
+	DE_ASSERT(damageRegion.size() % 4 == 0);
+	return damageRegion;
+}
+
+string generateTestName (const vector<DrawType>& frameDrawType)
+{
+	std::ostringstream stream;
+
+	for (size_t ndx = 0; ndx < frameDrawType.size(); ndx++)
+	{
+		if (frameDrawType[ndx] == DRAWTYPE_GLES2_RENDER)
+			stream << "render";
+		else if (frameDrawType[ndx] == DRAWTYPE_GLES2_CLEAR)
+			stream << "clear";
+		else
+			DE_ASSERT(false);
+
+		if (ndx < frameDrawType.size()-1)
+			stream << "_";
+	}
+
+	return stream.str();
+}
+
+bool isWindow (const eglu::CandidateConfig& c)
+{
+	return (c.surfaceType() & EGL_WINDOW_BIT) == EGL_WINDOW_BIT;
+}
+
+bool isES2Renderable (const eglu::CandidateConfig& c)
+{
+	return (c.get(EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT;
+}
+
+bool hasPreserveSwap (const eglu::CandidateConfig& c)
+{
+	return (c.surfaceType() & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) == EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
+}
+
+EGLConfig getEGLConfig (const Library& egl, EGLDisplay eglDisplay, bool preserveBuffer)
+{
+	eglu::FilterList filters;
+
+	filters << isWindow << isES2Renderable;
+	if (preserveBuffer)
+		filters << hasPreserveSwap;
+
+	return eglu::chooseSingleConfig(egl, eglDisplay, filters);
+}
+
+void clearColorScreen (const glw::Functions& gl, const tcu::Vec4& clearColor)
+{
+	gl.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
+	gl.clear(GL_COLOR_BUFFER_BIT);
+}
+
+float windowToDeviceCoordinates (int x, int length)
+{
+	return (2.0f * float(x) / float(length)) - 1.0f;
+}
+
+} // anonymous
+
+SwapBuffersWithDamageTests::SwapBuffersWithDamageTests (EglTestContext& eglTestCtx)
+	: TestCaseGroup(eglTestCtx, "swap_buffers_with_damage", "Swap buffers with damages tests")
+{
+}
+
+void SwapBuffersWithDamageTests::init (void)
+{
+	const DrawType clearRender[2] =
+	{
+		DRAWTYPE_GLES2_CLEAR,
+		DRAWTYPE_GLES2_RENDER
+	};
+
+	const DrawType renderClear[2] =
+	{
+		DRAWTYPE_GLES2_RENDER,
+		DRAWTYPE_GLES2_CLEAR
+	};
+
+	vector< vector<DrawType> > frameDrawTypes;
+	frameDrawTypes.push_back(vector<DrawType> (1, DRAWTYPE_GLES2_CLEAR));
+	frameDrawTypes.push_back(vector<DrawType> (1, DRAWTYPE_GLES2_RENDER));
+	frameDrawTypes.push_back(vector<DrawType> (2, DRAWTYPE_GLES2_CLEAR));
+	frameDrawTypes.push_back(vector<DrawType> (2, DRAWTYPE_GLES2_RENDER));
+	frameDrawTypes.push_back(vector<DrawType> (DE_ARRAY_BEGIN(clearRender), DE_ARRAY_END(clearRender)));
+	frameDrawTypes.push_back(vector<DrawType> (DE_ARRAY_BEGIN(renderClear), DE_ARRAY_END(renderClear)));
+
+	for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
+	{
+		string name = generateTestName(frameDrawTypes[drawTypeNdx]);
+		addChild(new SwapBuffersWithDamageTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4, name.c_str(), ""));
+	}
+
+	for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
+	{
+		string name = "preserve_buffer_" + generateTestName(frameDrawTypes[drawTypeNdx]);
+		addChild(new SwapBuffersWithDamageAndPreserveBufferTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4, name.c_str(), ""));
+	}
+
+	for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
+	{
+		string name = "buffer_age_" + generateTestName(frameDrawTypes[drawTypeNdx]);
+		addChild(new SwapBuffersWithDamageAndBufferAgeTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4, name.c_str(), ""));
+	}
+}
+
+} // egl
+} // deqp
diff --git a/modules/egl/teglSwapBuffersWithDamageTests.hpp b/modules/egl/teglSwapBuffersWithDamageTests.hpp
new file mode 100644
index 0000000..384a381
--- /dev/null
+++ b/modules/egl/teglSwapBuffersWithDamageTests.hpp
@@ -0,0 +1,48 @@
+#ifndef _TEGLSWAPBUFFERSWITHDAMAGETESTS_HPP
+#define _TEGLSWAPBUFFERSWITHDAMAGETESTS_HPP
+/*-------------------------------------------------------------------------
+ * drawElements Quality Program EGL Module
+ * ---------------------------------------
+ *
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief Test KHR_swap_buffers_with_damage
+ *//*--------------------------------------------------------------------*/
+
+#include "tcuDefs.hpp"
+#include "teglTestCase.hpp"
+
+namespace deqp
+{
+namespace egl
+{
+
+class SwapBuffersWithDamageTests : public TestCaseGroup
+{
+public:
+								SwapBuffersWithDamageTests		(EglTestContext& eglTestCtx);
+	void						init							(void);
+
+private:
+								SwapBuffersWithDamageTests		(const SwapBuffersWithDamageTests&);
+	SwapBuffersWithDamageTests&	operator=						(const SwapBuffersWithDamageTests&);
+};
+
+} // egl
+} // deqp
+
+#endif // _TEGLSWAPBUFFERSWITHDAMAGETESTS_HPP
diff --git a/modules/egl/teglTestPackage.cpp b/modules/egl/teglTestPackage.cpp
index 291d51e..7361506 100644
--- a/modules/egl/teglTestPackage.cpp
+++ b/modules/egl/teglTestPackage.cpp
@@ -59,6 +59,7 @@
 #include "teglBufferAgeTests.hpp"
 #include "teglPartialUpdateTests.hpp"
 #include "teglNegativePartialUpdateTests.hpp"
+#include "teglSwapBuffersWithDamageTests.hpp"
 
 namespace deqp
 {
@@ -130,6 +131,7 @@
 		addChild(new BufferAgeTests				(m_eglTestCtx));
 		addChild(new PartialUpdateTests			(m_eglTestCtx));
 		addChild(new NegativePartialUpdateTests	(m_eglTestCtx));
+		addChild(new SwapBuffersWithDamageTests	(m_eglTestCtx));
 	}
 };