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_swap_buffer_with_damage
22 *//*--------------------------------------------------------------------*/
23
24#include "teglSwapBuffersWithDamageTests.hpp"
25
26#include "tcuImageCompare.hpp"
27#include "tcuSurface.hpp"
28#include "tcuTextureUtil.hpp"
29
30#include "egluNativeWindow.hpp"
31#include "egluUtil.hpp"
32#include "egluConfigFilter.hpp"
33
34#include "eglwLibrary.hpp"
35#include "eglwEnums.hpp"
36
37#include "gluDefs.hpp"
38#include "gluRenderContext.hpp"
39#include "gluShaderProgram.hpp"
40
41#include "glwDefs.hpp"
42#include "glwEnums.hpp"
43#include "glwFunctions.hpp"
44
45#include "deRandom.hpp"
46#include "deString.h"
47
48#include <string>
49#include <vector>
50#include <sstream>
51
52using std::string;
53using std::vector;
54using glw::GLubyte;
55using tcu::IVec2;
56
57using namespace eglw;
58
59namespace deqp
60{
61namespace egl
62{
63namespace
64{
65
66typedef	tcu::Vector<GLubyte, 3> Color;
67
68enum DrawType
69{
70    DRAWTYPE_GLES2_CLEAR,
71    DRAWTYPE_GLES2_RENDER
72};
73
74enum ResizeType
75{
76	RESIZETYPE_NONE = 0,
77	RESIZETYPE_BEFORE_SWAP,
78	RESIZETYPE_AFTER_SWAP,
79
80	RESIZETYPE_LAST
81};
82
83struct ColoredRect
84{
85public:
86				ColoredRect (const IVec2& bottomLeft_, const IVec2& topRight_, const Color& color_);
87	IVec2		bottomLeft;
88	IVec2		topRight;
89	Color		color;
90};
91
92ColoredRect::ColoredRect (const IVec2& bottomLeft_, const IVec2& topRight_, const Color& color_)
93	: bottomLeft	(bottomLeft_)
94	, topRight		(topRight_)
95	, color			(color_)
96{
97}
98
99struct DrawCommand
100{
101				DrawCommand (DrawType drawType_, const ColoredRect& rect_);
102    DrawType	drawType;
103	ColoredRect	rect;
104};
105
106DrawCommand::DrawCommand (DrawType drawType_, const ColoredRect& rect_)
107	: drawType	(drawType_)
108	, rect		(rect_)
109{
110}
111
112struct Frame
113{
114						Frame (int width_, int height_);
115	int					width;
116	int					height;
117	vector<DrawCommand> draws;
118};
119
120Frame::Frame (int width_, int height_)
121	: width (width_)
122	, height(height_)
123{
124}
125
126typedef vector<Frame> FrameSequence;
127
128//helper function declaration
129EGLConfig		getEGLConfig					(const Library& egl, EGLDisplay eglDisplay, bool preserveBuffer);
130void			clearColorScreen				(const glw::Functions& gl, const tcu::Vec4& clearColor);
131float			windowToDeviceCoordinates		(int x, int length);
132
133class GLES2Renderer
134{
135public:
136							GLES2Renderer		(const glw::Functions& gl);
137							~GLES2Renderer		(void);
138	void					render				(int width, int height, const Frame& frame) const;
139
140private:
141							GLES2Renderer		(const GLES2Renderer&);
142	GLES2Renderer&			operator=			(const GLES2Renderer&);
143
144	const glw::Functions&	m_gl;
145	glu::ShaderProgram		m_glProgram;
146	glw::GLuint				m_coordLoc;
147	glw::GLuint				m_colorLoc;
148};
149
150// generate sources for vertex and fragment buffer
151glu::ProgramSources getSources (void)
152{
153	const char* const vertexShaderSource =
154		"attribute mediump vec2 a_pos;\n"
155		"attribute mediump vec4 a_color;\n"
156		"varying mediump vec4 v_color;\n"
157		"void main(void)\n"
158		"{\n"
159		"\tv_color = a_color;\n"
160		"\tgl_Position = vec4(a_pos, 0.0, 1.0);\n"
161		"}";
162
163	const char* const fragmentShaderSource =
164		"varying mediump vec4 v_color;\n"
165		"void main(void)\n"
166		"{\n"
167		"\tgl_FragColor = v_color;\n"
168		"}";
169
170	return glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource);
171}
172
173GLES2Renderer::GLES2Renderer (const glw::Functions& gl)
174	: m_gl        (gl)
175	, m_glProgram (gl, getSources())
176	, m_coordLoc  ((glw::GLuint)-1)
177	, m_colorLoc  ((glw::GLuint)-1)
178{
179	m_colorLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_color");
180	m_coordLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_pos");
181	GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to get attribute locations");
182}
183
184GLES2Renderer::~GLES2Renderer (void)
185{
186}
187
188void GLES2Renderer::render (int width, int height, const Frame& frame) const
189{
190	for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
191	{
192		const ColoredRect& coloredRect = frame.draws[drawNdx].rect;
193
194		if (frame.draws[drawNdx].drawType == DRAWTYPE_GLES2_RENDER)
195		{
196			const float x1 = windowToDeviceCoordinates(coloredRect.bottomLeft.x(), width);
197			const float y1 = windowToDeviceCoordinates(coloredRect.bottomLeft.y(), height);
198			const float x2 = windowToDeviceCoordinates(coloredRect.topRight.x(), width);
199			const float y2 = windowToDeviceCoordinates(coloredRect.topRight.y(), height);
200
201			const glw::GLfloat coords[] =
202			{
203				x1, y1,
204				x1, y2,
205				x2, y2,
206
207				x2, y2,
208				x2, y1,
209				x1, y1,
210			};
211
212			const glw::GLubyte colors[] =
213			{
214				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
215				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
216				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
217
218				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
219				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
220				coloredRect.color.x(), coloredRect.color.y(), coloredRect.color.z(), 255,
221			};
222
223			m_gl.useProgram(m_glProgram.getProgram());
224			GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
225
226			m_gl.enableVertexAttribArray(m_coordLoc);
227			m_gl.enableVertexAttribArray(m_colorLoc);
228			GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to enable attributes");
229
230			m_gl.vertexAttribPointer(m_coordLoc, 2, GL_FLOAT, GL_FALSE, 0, coords);
231			m_gl.vertexAttribPointer(m_colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
232			GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to set attribute pointers");
233
234			m_gl.drawArrays(GL_TRIANGLES, 0, DE_LENGTH_OF_ARRAY(coords)/2);
235			GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays(), failed");
236
237			m_gl.disableVertexAttribArray(m_coordLoc);
238			m_gl.disableVertexAttribArray(m_colorLoc);
239			GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to disable attributes");
240
241			m_gl.useProgram(0);
242			GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
243		}
244		else if (frame.draws[drawNdx].drawType == DRAWTYPE_GLES2_CLEAR)
245		{
246			m_gl.enable(GL_SCISSOR_TEST);
247			m_gl.scissor(coloredRect.bottomLeft.x(), coloredRect.bottomLeft.y(),
248						 coloredRect.topRight.x()-coloredRect.bottomLeft.x(), coloredRect.topRight.y()-coloredRect.bottomLeft.y());
249			m_gl.clearColor(coloredRect.color.x()/255.0f, coloredRect.color.y()/255.0f, coloredRect.color.z()/255.0f, 1.0f);
250			m_gl.clear(GL_COLOR_BUFFER_BIT);
251			m_gl.disable(GL_SCISSOR_TEST);
252		}
253		else
254			DE_FATAL("Invalid drawtype");
255	}
256}
257
258class SwapBuffersWithDamageTest : public TestCase
259{
260public:
261								SwapBuffersWithDamageTest		(EglTestContext&			eglTestCtx,
262																 const vector<DrawType>&	frameDrawType,
263																 int						iterationTimes,
264																 ResizeType					resizeType,
265																 const char*				name,
266																 const char*				description);
267
268								~SwapBuffersWithDamageTest		(void);
269
270	virtual void				init							(void);
271	void						deinit							(void);
272	virtual IterateResult		iterate							(void);
273
274protected:
275	virtual EGLConfig			getConfig						(const Library& egl, EGLDisplay eglDisplay);
276	virtual void				checkExtension					(const Library& egl, EGLDisplay eglDisplay);
277	void						initEGLSurface					(EGLConfig config);
278	void						initEGLContext					(EGLConfig config);
279
280	eglu::NativeWindow*			m_window;
281	EGLConfig					m_eglConfig;
282	EGLContext					m_eglContext;
283	const int					m_seed;
284	const int					m_iterationTimes;
285	const vector<DrawType>		m_frameDrawType;
286	const ResizeType			m_resizeType;
287	EGLDisplay					m_eglDisplay;
288	EGLSurface					m_eglSurface;
289	glw::Functions				m_gl;
290	GLES2Renderer*				m_gles2Renderer;
291};
292
293SwapBuffersWithDamageTest::SwapBuffersWithDamageTest (EglTestContext& eglTestCtx, const vector<DrawType>& frameDrawType, int iterationTimes, ResizeType resizeType, const char* name, const char* description)
294	: TestCase			(eglTestCtx, name, description)
295	, m_window			(DE_NULL)
296	, m_eglContext		(EGL_NO_CONTEXT)
297	, m_seed			(deStringHash(name))
298	, m_iterationTimes	(iterationTimes)
299	, m_frameDrawType	(frameDrawType)
300	, m_resizeType		(resizeType)
301	, m_eglDisplay		(EGL_NO_DISPLAY)
302	, m_eglSurface		(EGL_NO_SURFACE)
303	, m_gles2Renderer	 (DE_NULL)
304{
305}
306
307SwapBuffersWithDamageTest::~SwapBuffersWithDamageTest (void)
308{
309	deinit();
310}
311
312EGLConfig SwapBuffersWithDamageTest::getConfig (const Library& egl, EGLDisplay eglDisplay)
313{
314	return getEGLConfig(egl, eglDisplay, false);
315}
316
317void SwapBuffersWithDamageTest::checkExtension (const Library& egl, EGLDisplay eglDisplay)
318{
319	if (!eglu::hasExtension(egl, eglDisplay, "EGL_KHR_swap_buffers_with_damage"))
320		TCU_THROW(NotSupportedError, "EGL_KHR_swap_buffers_with_damage is not supported");
321}
322
323void SwapBuffersWithDamageTest::init (void)
324{
325	const Library& egl = m_eglTestCtx.getLibrary();
326
327	m_eglDisplay = eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
328	m_eglConfig  = getConfig(egl, m_eglDisplay);
329
330	checkExtension(egl, m_eglDisplay);
331
332	initEGLSurface(m_eglConfig);
333	initEGLContext(m_eglConfig);
334
335	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
336	m_gles2Renderer = new GLES2Renderer(m_gl);
337}
338
339void SwapBuffersWithDamageTest::deinit (void)
340{
341	const Library& egl = m_eglTestCtx.getLibrary();
342
343	delete m_gles2Renderer;
344	m_gles2Renderer = DE_NULL;
345
346	if (m_eglContext != EGL_NO_CONTEXT)
347	{
348		egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
349		egl.destroyContext(m_eglDisplay, m_eglContext);
350		m_eglContext = EGL_NO_CONTEXT;
351	}
352
353	if (m_eglSurface != EGL_NO_SURFACE)
354	{
355		egl.destroySurface(m_eglDisplay, m_eglSurface);
356		m_eglSurface = EGL_NO_SURFACE;
357	}
358
359	if (m_eglDisplay != EGL_NO_DISPLAY)
360	{
361		egl.terminate(m_eglDisplay);
362		m_eglDisplay = EGL_NO_DISPLAY;
363	}
364
365	delete m_window;
366	m_window = DE_NULL;
367}
368
369void SwapBuffersWithDamageTest::initEGLSurface (EGLConfig config)
370{
371	const eglu::NativeWindowFactory& factory = eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
372	m_window = factory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_eglDisplay, config, DE_NULL,
373									eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
374	m_eglSurface = eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_eglDisplay, config, DE_NULL);
375}
376
377void SwapBuffersWithDamageTest::initEGLContext (EGLConfig config)
378{
379	const Library&	egl			 = m_eglTestCtx.getLibrary();
380	const EGLint	attribList[] =
381	{
382		EGL_CONTEXT_CLIENT_VERSION, 2,
383		EGL_NONE
384	};
385
386	egl.bindAPI(EGL_OPENGL_ES_API);
387	m_eglContext = egl.createContext(m_eglDisplay, config, EGL_NO_CONTEXT, attribList);
388	EGLU_CHECK_MSG(egl, "eglCreateContext");
389	TCU_CHECK(m_eglSurface != EGL_NO_SURFACE);
390	egl.makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
391	EGLU_CHECK_MSG(egl, "eglMakeCurrent");
392}
393
394FrameSequence	generateFrameSequence	(const vector<DrawType>& frameDrawType, de::Random& rnd, int numFrames, int width, int height);
395vector<EGLint>	getDamageRegion			(const Frame& frame);
396
397TestCase::IterateResult SwapBuffersWithDamageTest::iterate (void)
398{
399	de::Random			rnd				(m_seed);
400	const Library&		egl				= m_eglTestCtx.getLibrary();
401	const int			width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
402	const int			height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
403	const float			clearRed		= rnd.getFloat();
404	const float			clearGreen		= rnd.getFloat();
405	const float			clearBlue		= rnd.getFloat();
406	const tcu::Vec4		clearColor		(clearRed, clearGreen, clearBlue, 1.0f);
407	const int			numFrames		= 24; // (width, height) = (480, 480) --> numFrame = 24, divisible
408	const FrameSequence frameSequence	= generateFrameSequence(m_frameDrawType, rnd, numFrames, width, height);
409
410	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
411	EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
412
413	for (int iterationNdx = 0; iterationNdx < m_iterationTimes; iterationNdx++)
414	{
415		for (int currentFrameNdx = 0; currentFrameNdx < numFrames; currentFrameNdx++)
416		{
417			vector<EGLint>	damageRegion = getDamageRegion(frameSequence[currentFrameNdx]);
418
419			clearColorScreen(m_gl, clearColor);
420			for (int ndx = 0; ndx <= currentFrameNdx; ndx++)
421				m_gles2Renderer->render(width, height, frameSequence[ndx]);
422
423			if (m_resizeType == RESIZETYPE_BEFORE_SWAP)
424			{
425				if (iterationNdx % 2 == 0)
426					m_window->setSurfaceSize(IVec2(width*2, height/2));
427				else
428					m_window->setSurfaceSize(IVec2(height/2, width*2));
429			}
430
431			EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
432
433			if (m_resizeType == RESIZETYPE_AFTER_SWAP)
434			{
435				if (iterationNdx % 2 == 0)
436					m_window->setSurfaceSize(IVec2(width*2, height/2));
437				else
438					m_window->setSurfaceSize(IVec2(height/2, width*2));
439			}
440		}
441	}
442	return STOP;
443}
444
445class SwapBuffersWithDamageAndPreserveBufferTest : public SwapBuffersWithDamageTest
446{
447public:
448					SwapBuffersWithDamageAndPreserveBufferTest	(EglTestContext&			eglTestCtx,
449																 const vector<DrawType>&	frameDrawType,
450																 int						iterationTimes,
451																 ResizeType					resizeType,
452																 const char*				name,
453																 const char*				description);
454
455	IterateResult	iterate										(void);
456
457protected:
458	EGLConfig		getConfig									(const Library& egl, EGLDisplay eglDisplay);
459};
460
461SwapBuffersWithDamageAndPreserveBufferTest::SwapBuffersWithDamageAndPreserveBufferTest (EglTestContext&			eglTestCtx,
462																						const vector<DrawType>&	frameDrawType,
463																						int						iterationTimes,
464																						ResizeType				resizeType,
465																						const char*				name,
466																						const char*				description)
467	: SwapBuffersWithDamageTest (eglTestCtx, frameDrawType, iterationTimes, resizeType, name, description)
468{
469}
470
471EGLConfig SwapBuffersWithDamageAndPreserveBufferTest::getConfig (const Library& egl, EGLDisplay eglDisplay)
472{
473	return getEGLConfig(egl, eglDisplay, true);
474}
475
476TestCase::IterateResult SwapBuffersWithDamageAndPreserveBufferTest::iterate (void)
477{
478
479	de::Random			rnd				(m_seed);
480	const Library&		egl				= m_eglTestCtx.getLibrary();
481	const int			width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
482	const int			height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
483	const float			clearRed		= rnd.getFloat();
484	const float			clearGreen		= rnd.getFloat();
485	const float			clearBlue		= rnd.getFloat();
486	const tcu::Vec4		clearColor		(clearRed, clearGreen, clearBlue, 1.0f);
487	const int			numFrames		= 24; // (width, height) = (480, 480) --> numFrame = 24, divisible
488	const FrameSequence frameSequence	= generateFrameSequence(m_frameDrawType, rnd, numFrames, width, height);
489
490	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
491	EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED));
492
493	for (int iterationNdx = 0; iterationNdx < m_iterationTimes; iterationNdx++)
494	{
495		clearColorScreen(m_gl, clearColor);
496		EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, DE_NULL, 0));
497
498		for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
499		{
500			const Frame&	currentFrame = frameSequence[frameNdx];
501			vector<EGLint>	damageRegion = getDamageRegion(currentFrame);
502
503			m_gles2Renderer->render(width, height, currentFrame);
504
505			if (m_resizeType == RESIZETYPE_BEFORE_SWAP)
506			{
507				if (iterationNdx % 2 == 0)
508					m_window->setSurfaceSize(IVec2(width*2, height/2));
509				else
510					m_window->setSurfaceSize(IVec2(height/2, width*2));
511			}
512
513			EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
514
515			if (m_resizeType == RESIZETYPE_AFTER_SWAP)
516			{
517				if (iterationNdx % 2 == 0)
518					m_window->setSurfaceSize(IVec2(width*2, height/2));
519				else
520					m_window->setSurfaceSize(IVec2(height/2, width*2));
521			}
522		}
523	}
524
525	return STOP;
526}
527
528class SwapBuffersWithDamageAndBufferAgeTest : public SwapBuffersWithDamageTest
529{
530public:
531					SwapBuffersWithDamageAndBufferAgeTest	(EglTestContext&			eglTestCtx,
532															 const vector<DrawType>&	frameDrawType,
533															 int						iterationTimes,
534															 ResizeType					resizeType,
535															 const char*				name,
536															 const char*				description);
537
538	IterateResult	iterate									(void);
539
540protected:
541	void			checkExtension							(const Library& egl, EGLDisplay eglDisplay);
542};
543
544SwapBuffersWithDamageAndBufferAgeTest::SwapBuffersWithDamageAndBufferAgeTest (EglTestContext&			eglTestCtx,
545																			  const vector<DrawType>&	frameDrawType,
546																			  int						iterationTimes,
547																			  ResizeType				resizeType,
548																			  const char*				name,
549																			  const char*				description)
550	: SwapBuffersWithDamageTest (eglTestCtx, frameDrawType, iterationTimes, resizeType, name, description)
551{
552}
553
554
555void SwapBuffersWithDamageAndBufferAgeTest::checkExtension (const Library& egl, EGLDisplay eglDisplay)
556{
557	if (!eglu::hasExtension(egl, eglDisplay, "EGL_KHR_swap_buffers_with_damage"))
558		TCU_THROW(NotSupportedError, "EGL_KHR_swap_buffers_with_damage is not supported");
559
560	if (!eglu::hasExtension(egl, eglDisplay, "EGL_EXT_buffer_age"))
561		TCU_THROW(NotSupportedError, "EGL_EXT_buffer_age not supported");
562}
563
564TestCase::IterateResult SwapBuffersWithDamageAndBufferAgeTest::iterate (void)
565{
566
567	de::Random			rnd				(m_seed);
568	const Library&		egl				= m_eglTestCtx.getLibrary();
569	const int			width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
570	const int			height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
571	const float			clearRed		= rnd.getFloat();
572	const float			clearGreen		= rnd.getFloat();
573	const float			clearBlue		= rnd.getFloat();
574	const tcu::Vec4		clearColor		(clearRed, clearGreen, clearBlue, 1.0f);
575	const int			numFrames		= 24; // (width, height) = (480, 480) --> numFrame = 24, divisible
576	const FrameSequence frameSequence	= generateFrameSequence(m_frameDrawType, rnd, numFrames, width, height);
577
578	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
579	EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED));
580
581	for (int iterationNdx = 0; iterationNdx < m_iterationTimes; iterationNdx++)
582	{
583		clearColorScreen(m_gl, clearColor);
584		EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, DE_NULL, 0));
585
586		for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
587		{
588			vector<EGLint>	damageRegion;
589			int				bufferAge		= -1;
590			int				startFrameNdx	= -1;
591			int				endFrameNdx		= frameNdx;
592
593			EGLU_CHECK_CALL(egl, querySurface(m_eglDisplay, m_eglSurface, EGL_BUFFER_AGE_EXT, &bufferAge));
594
595			if (bufferAge < 0) // invalid buffer age
596			{
597				std::ostringstream stream;
598				stream << "Fail, the age is invalid. Age: " << bufferAge << ", frameNdx: " << frameNdx;
599				m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, stream.str().c_str());
600				return STOP;
601			}
602
603			if (bufferAge == 0 || bufferAge > frameNdx)
604			{
605				clearColorScreen(m_gl, clearColor);
606				startFrameNdx = 0;
607			}
608			else
609				startFrameNdx = frameNdx-bufferAge+1;
610
611			for (int ndx = startFrameNdx; ndx <= endFrameNdx; ndx++)
612			{
613				const vector<EGLint> partialDamageRegion = getDamageRegion(frameSequence[ndx]);
614
615				damageRegion.insert(damageRegion.end(), partialDamageRegion.begin(), partialDamageRegion.end());
616				m_gles2Renderer->render(width, height, frameSequence[ndx]);
617			}
618
619			if (m_resizeType == RESIZETYPE_BEFORE_SWAP)
620			{
621				if (iterationNdx % 2 == 0)
622					m_window->setSurfaceSize(IVec2(width*2, height/2));
623				else
624					m_window->setSurfaceSize(IVec2(height/2, width*2));
625			}
626
627			EGLU_CHECK_CALL(egl, swapBuffersWithDamageKHR(m_eglDisplay, m_eglSurface, &damageRegion[0], (EGLint)damageRegion.size()/4));
628
629			if (m_resizeType == RESIZETYPE_AFTER_SWAP)
630			{
631				if (iterationNdx % 2 == 0)
632					m_window->setSurfaceSize(IVec2(width*2, height/2));
633				else
634					m_window->setSurfaceSize(IVec2(height/2, width*2));
635			}
636		}
637	}
638	return STOP;
639}
640
641// generate a frame sequence with certain frame for visual verification
642FrameSequence generateFrameSequence (const vector<DrawType>& frameDrawType, de::Random& rnd, int numFrames, int width, int height)
643{
644	const int			frameDiff		= height / numFrames;
645	const GLubyte		r				= rnd.getUint8();
646	const GLubyte		g				= rnd.getUint8();
647	const GLubyte		b				= rnd.getUint8();
648	const Color			color			(r, g, b);
649	FrameSequence		frameSequence;
650
651	for (int frameNdx = 0; frameNdx < numFrames; frameNdx++)
652	{
653		Frame frame (width, height);
654
655		for (int rectNdx = 0; rectNdx < (int)frameDrawType.size(); rectNdx++)
656		{
657			const int			rectHeight		= frameDiff / (int)frameDrawType.size();
658			const ColoredRect	rect			(IVec2(0, frameNdx*frameDiff+rectNdx*rectHeight), IVec2(width, frameNdx*frameDiff+(rectNdx+1)*rectHeight), color);
659			const DrawCommand	drawCommand		(frameDrawType[rectNdx], rect);
660
661			frame.draws.push_back(drawCommand);
662		}
663		frameSequence.push_back(frame);
664	}
665	return frameSequence;
666}
667
668vector<EGLint> getDamageRegion (const Frame& frame)
669{
670	vector<EGLint> damageRegion;
671	for (size_t drawNdx = 0; drawNdx < frame.draws.size(); drawNdx++)
672	{
673		const ColoredRect& rect = frame.draws[drawNdx].rect;
674		damageRegion.push_back(rect.bottomLeft.x());
675		damageRegion.push_back(rect.bottomLeft.y());
676		damageRegion.push_back(rect.topRight.x() - rect.bottomLeft.x());
677		damageRegion.push_back(rect.topRight.y() - rect.bottomLeft.y());
678	}
679
680	DE_ASSERT(damageRegion.size() % 4 == 0);
681	return damageRegion;
682}
683
684string generateTestName (const vector<DrawType>& frameDrawType)
685{
686	std::ostringstream stream;
687
688	for (size_t ndx = 0; ndx < frameDrawType.size(); ndx++)
689	{
690		if (frameDrawType[ndx] == DRAWTYPE_GLES2_RENDER)
691			stream << "render";
692		else if (frameDrawType[ndx] == DRAWTYPE_GLES2_CLEAR)
693			stream << "clear";
694		else
695			DE_ASSERT(false);
696
697		if (ndx < frameDrawType.size()-1)
698			stream << "_";
699	}
700
701	return stream.str();
702}
703
704string generateResizeGroupName (ResizeType resizeType)
705{
706	switch (resizeType)
707	{
708		case RESIZETYPE_NONE:
709			return "no_resize";
710
711		case RESIZETYPE_AFTER_SWAP:
712			return "resize_after_swap";
713
714		case RESIZETYPE_BEFORE_SWAP:
715			return "resize_before_swap";
716
717		default:
718			DE_FATAL("Unknown resize type");
719			return "";
720	}
721}
722
723bool isWindow (const eglu::CandidateConfig& c)
724{
725	return (c.surfaceType() & EGL_WINDOW_BIT) == EGL_WINDOW_BIT;
726}
727
728bool isES2Renderable (const eglu::CandidateConfig& c)
729{
730	return (c.get(EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT;
731}
732
733bool hasPreserveSwap (const eglu::CandidateConfig& c)
734{
735	return (c.surfaceType() & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) == EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
736}
737
738EGLConfig getEGLConfig (const Library& egl, EGLDisplay eglDisplay, bool preserveBuffer)
739{
740	eglu::FilterList filters;
741
742	filters << isWindow << isES2Renderable;
743	if (preserveBuffer)
744		filters << hasPreserveSwap;
745
746	return eglu::chooseSingleConfig(egl, eglDisplay, filters);
747}
748
749void clearColorScreen (const glw::Functions& gl, const tcu::Vec4& clearColor)
750{
751	gl.clearColor(clearColor.x(), clearColor.y(), clearColor.z(), clearColor.w());
752	gl.clear(GL_COLOR_BUFFER_BIT);
753}
754
755float windowToDeviceCoordinates (int x, int length)
756{
757	return (2.0f * float(x) / float(length)) - 1.0f;
758}
759
760} // anonymous
761
762SwapBuffersWithDamageTests::SwapBuffersWithDamageTests (EglTestContext& eglTestCtx)
763	: TestCaseGroup(eglTestCtx, "swap_buffers_with_damage", "Swap buffers with damages tests")
764{
765}
766
767void SwapBuffersWithDamageTests::init (void)
768{
769	const DrawType clearRender[2] =
770	{
771		DRAWTYPE_GLES2_CLEAR,
772		DRAWTYPE_GLES2_RENDER
773	};
774
775	const DrawType renderClear[2] =
776	{
777		DRAWTYPE_GLES2_RENDER,
778		DRAWTYPE_GLES2_CLEAR
779	};
780
781	const ResizeType resizeTypes[] =
782	{
783		RESIZETYPE_NONE,
784		RESIZETYPE_BEFORE_SWAP,
785		RESIZETYPE_AFTER_SWAP
786	};
787
788	vector< vector<DrawType> > frameDrawTypes;
789	frameDrawTypes.push_back(vector<DrawType> (1, DRAWTYPE_GLES2_CLEAR));
790	frameDrawTypes.push_back(vector<DrawType> (1, DRAWTYPE_GLES2_RENDER));
791	frameDrawTypes.push_back(vector<DrawType> (2, DRAWTYPE_GLES2_CLEAR));
792	frameDrawTypes.push_back(vector<DrawType> (2, DRAWTYPE_GLES2_RENDER));
793	frameDrawTypes.push_back(vector<DrawType> (DE_ARRAY_BEGIN(clearRender), DE_ARRAY_END(clearRender)));
794	frameDrawTypes.push_back(vector<DrawType> (DE_ARRAY_BEGIN(renderClear), DE_ARRAY_END(renderClear)));
795
796	for (size_t resizeTypeNdx = 0; resizeTypeNdx < DE_LENGTH_OF_ARRAY(resizeTypes); resizeTypeNdx++)
797	{
798		const ResizeType		resizeType	= resizeTypes[resizeTypeNdx];
799		TestCaseGroup* const	resizeGroup	= new TestCaseGroup(m_eglTestCtx, generateResizeGroupName(resizeType).c_str(), "");
800
801		for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
802		{
803			string name = generateTestName(frameDrawTypes[drawTypeNdx]);
804			resizeGroup->addChild(new SwapBuffersWithDamageTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4, resizeType, name.c_str(), ""));
805		}
806
807		for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
808		{
809			string name = "preserve_buffer_" + generateTestName(frameDrawTypes[drawTypeNdx]);
810			resizeGroup->addChild(new SwapBuffersWithDamageAndPreserveBufferTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4, resizeType, name.c_str(), ""));
811		}
812
813		for (size_t drawTypeNdx = 0; drawTypeNdx < frameDrawTypes.size(); drawTypeNdx++)
814		{
815			string name = "buffer_age_" + generateTestName(frameDrawTypes[drawTypeNdx]);
816			resizeGroup->addChild(new SwapBuffersWithDamageAndBufferAgeTest(m_eglTestCtx, frameDrawTypes[drawTypeNdx], 4, resizeType, name.c_str(),  ""));
817		}
818
819		addChild(resizeGroup);
820	}
821}
822
823} // egl
824} // deqp
825