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