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 
52 using std::string;
53 using std::vector;
54 using glw::GLubyte;
55 using tcu::IVec2;
56 
57 using namespace eglw;
58 
59 namespace deqp
60 {
61 namespace egl
62 {
63 namespace
64 {
65 
66 typedef	tcu::Vector<GLubyte, 3> Color;
67 
68 enum DrawType
69 {
70     DRAWTYPE_GLES2_CLEAR,
71     DRAWTYPE_GLES2_RENDER
72 };
73 
74 enum ResizeType
75 {
76 	RESIZETYPE_NONE = 0,
77 	RESIZETYPE_BEFORE_SWAP,
78 	RESIZETYPE_AFTER_SWAP,
79 
80 	RESIZETYPE_LAST
81 };
82 
83 struct ColoredRect
84 {
85 public:
86 				ColoredRect (const IVec2& bottomLeft_, const IVec2& topRight_, const Color& color_);
87 	IVec2		bottomLeft;
88 	IVec2		topRight;
89 	Color 		color;
90 };
91 
ColoredRect(const IVec2 & bottomLeft_,const IVec2 & topRight_,const Color & color_)92 ColoredRect::ColoredRect (const IVec2& bottomLeft_, const IVec2& topRight_, const Color& color_)
93 	: bottomLeft	(bottomLeft_)
94 	, topRight		(topRight_)
95 	, color			(color_)
96 {
97 }
98 
99 struct DrawCommand
100 {
101 				DrawCommand (DrawType drawType_, const ColoredRect& rect_);
102     DrawType	drawType;
103 	ColoredRect	rect;
104 };
105 
DrawCommand(DrawType drawType_,const ColoredRect & rect_)106 DrawCommand::DrawCommand (DrawType drawType_, const ColoredRect& rect_)
107 	: drawType	(drawType_)
108 	, rect		(rect_)
109 {
110 }
111 
112 struct Frame
113 {
114 						Frame (int width_, int height_);
115 	int 				width;
116 	int					height;
117 	vector<DrawCommand> draws;
118 };
119 
Frame(int width_,int height_)120 Frame::Frame (int width_, int height_)
121 	: width (width_)
122 	, height(height_)
123 {
124 }
125 
126 typedef vector<Frame> FrameSequence;
127 
128 //helper function declaration
129 EGLConfig		getEGLConfig					(const Library& egl, EGLDisplay eglDisplay, bool preserveBuffer);
130 void			clearColorScreen				(const glw::Functions& gl, const tcu::Vec4& clearColor);
131 float			windowToDeviceCoordinates		(int x, int length);
132 
133 class GLES2Renderer
134 {
135 public:
136 							GLES2Renderer		(const glw::Functions& gl);
137 							~GLES2Renderer		(void);
138 	void					render				(int width, int height, const Frame& frame) const;
139 
140 private:
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
getSources(void)151 glu::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 
GLES2Renderer(const glw::Functions & gl)173 GLES2Renderer::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 
~GLES2Renderer(void)184 GLES2Renderer::~GLES2Renderer (void)
185 {
186 }
187 
render(int width,int height,const Frame & frame) const188 void 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 
258 class SwapBuffersWithDamageTest : public TestCase
259 {
260 public:
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 
274 protected:
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 
SwapBuffersWithDamageTest(EglTestContext & eglTestCtx,const vector<DrawType> & frameDrawType,int iterationTimes,ResizeType resizeType,const char * name,const char * description)293 SwapBuffersWithDamageTest::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 
~SwapBuffersWithDamageTest(void)307 SwapBuffersWithDamageTest::~SwapBuffersWithDamageTest (void)
308 {
309 	deinit();
310 }
311 
getConfig(const Library & egl,EGLDisplay eglDisplay)312 EGLConfig SwapBuffersWithDamageTest::getConfig (const Library& egl, EGLDisplay eglDisplay)
313 {
314 	return getEGLConfig(egl, eglDisplay, false);
315 }
316 
checkExtension(const Library & egl,EGLDisplay eglDisplay)317 void 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 
init(void)323 void 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 
deinit(void)339 void 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 
initEGLSurface(EGLConfig config)369 void 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 
initEGLContext(EGLConfig config)377 void 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 
394 FrameSequence	generateFrameSequence	(const vector<DrawType>& frameDrawType, de::Random& rnd, int numFrames, int width, int height);
395 vector<EGLint>	getDamageRegion			(const Frame& frame);
396 
iterate(void)397 TestCase::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 
445 class SwapBuffersWithDamageAndPreserveBufferTest : public SwapBuffersWithDamageTest
446 {
447 public:
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 
457 protected:
458 	EGLConfig		getConfig									(const Library& egl, EGLDisplay eglDisplay);
459 };
460 
SwapBuffersWithDamageAndPreserveBufferTest(EglTestContext & eglTestCtx,const vector<DrawType> & frameDrawType,int iterationTimes,ResizeType resizeType,const char * name,const char * description)461 SwapBuffersWithDamageAndPreserveBufferTest::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 
getConfig(const Library & egl,EGLDisplay eglDisplay)471 EGLConfig SwapBuffersWithDamageAndPreserveBufferTest::getConfig (const Library& egl, EGLDisplay eglDisplay)
472 {
473 	return getEGLConfig(egl, eglDisplay, true);
474 }
475 
iterate(void)476 TestCase::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 
528 class SwapBuffersWithDamageAndBufferAgeTest : public SwapBuffersWithDamageTest
529 {
530 public:
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 
540 protected:
541 	void			checkExtension							(const Library& egl, EGLDisplay eglDisplay);
542 };
543 
SwapBuffersWithDamageAndBufferAgeTest(EglTestContext & eglTestCtx,const vector<DrawType> & frameDrawType,int iterationTimes,ResizeType resizeType,const char * name,const char * description)544 SwapBuffersWithDamageAndBufferAgeTest::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 
checkExtension(const Library & egl,EGLDisplay eglDisplay)555 void 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 
iterate(void)564 TestCase::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
generateFrameSequence(const vector<DrawType> & frameDrawType,de::Random & rnd,int numFrames,int width,int height)642 FrameSequence 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 
getDamageRegion(const Frame & frame)668 vector<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 
generateTestName(const vector<DrawType> & frameDrawType)684 string 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 
generateResizeGroupName(ResizeType resizeType)704 string 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 
isWindow(const eglu::CandidateConfig & c)723 bool isWindow (const eglu::CandidateConfig& c)
724 {
725 	return (c.surfaceType() & EGL_WINDOW_BIT) == EGL_WINDOW_BIT;
726 }
727 
isES2Renderable(const eglu::CandidateConfig & c)728 bool isES2Renderable (const eglu::CandidateConfig& c)
729 {
730 	return (c.get(EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT) == EGL_OPENGL_ES2_BIT;
731 }
732 
hasPreserveSwap(const eglu::CandidateConfig & c)733 bool hasPreserveSwap (const eglu::CandidateConfig& c)
734 {
735 	return (c.surfaceType() & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) == EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
736 }
737 
getEGLConfig(const Library & egl,EGLDisplay eglDisplay,bool preserveBuffer)738 EGLConfig 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 
clearColorScreen(const glw::Functions & gl,const tcu::Vec4 & clearColor)749 void 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 
windowToDeviceCoordinates(int x,int length)755 float windowToDeviceCoordinates (int x, int length)
756 {
757 	return (2.0f * float(x) / float(length)) - 1.0f;
758 }
759 
760 } // anonymous
761 
SwapBuffersWithDamageTests(EglTestContext & eglTestCtx)762 SwapBuffersWithDamageTests::SwapBuffersWithDamageTests (EglTestContext& eglTestCtx)
763 	: TestCaseGroup(eglTestCtx, "swap_buffers_with_damage", "Swap buffers with damages tests")
764 {
765 }
766 
init(void)767 void 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