1/*-------------------------------------------------------------------------
2 * drawElements Quality Program EGL Module
3 * ---------------------------------------
4 *
5 * Copyright 2014 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 EGL_SWAP_BEHAVIOR_PRESERVED_BIT.
22 *//*--------------------------------------------------------------------*/
23
24#include "teglPreservingSwapTests.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
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
47#include "deString.h"
48
49#include <vector>
50#include <string>
51
52using std::vector;
53using std::string;
54
55using namespace eglw;
56
57namespace deqp
58{
59namespace egl
60{
61
62namespace
63{
64class GLES2Program;
65class ReferenceProgram;
66
67class PreservingSwapTest : public TestCase
68{
69public:
70	enum DrawType
71	{
72		DRAWTYPE_NONE = 0,
73		DRAWTYPE_GLES2_CLEAR,
74		DRAWTYPE_GLES2_RENDER
75	};
76
77					PreservingSwapTest	(EglTestContext& eglTestCtx, bool preserveColorbuffer, bool readPixelsBeforeSwap, DrawType preSwapDrawType, DrawType postSwapDrawType, const char* name, const char* description);
78					~PreservingSwapTest	(void);
79
80	void			init				(void);
81	void			deinit				(void);
82	IterateResult	iterate				(void);
83
84private:
85	const int					m_seed;
86	const bool					m_preserveColorbuffer;
87	const bool					m_readPixelsBeforeSwap;
88	const DrawType				m_preSwapDrawType;
89	const DrawType				m_postSwapDrawType;
90
91	EGLDisplay					m_eglDisplay;
92	eglu::NativeWindow*			m_window;
93	EGLSurface					m_eglSurface;
94	EGLConfig					m_eglConfig;
95	EGLContext					m_eglContext;
96	glw::Functions				m_gl;
97
98	GLES2Program*				m_gles2Program;
99	ReferenceProgram*			m_refProgram;
100
101	void initEGLSurface	(EGLConfig config);
102	void initEGLContext (EGLConfig config);
103};
104
105class GLES2Program
106{
107public:
108					GLES2Program	(const glw::Functions& gl);
109					~GLES2Program	(void);
110
111	void			render			(int width, int height, float x1, float y1, float x2, float y2, PreservingSwapTest::DrawType drawType);
112
113private:
114	const glw::Functions&	m_gl;
115	glu::ShaderProgram		m_glProgram;
116	glw::GLuint				m_coordLoc;
117	glw::GLuint				m_colorLoc;
118
119	GLES2Program&			operator=		(const GLES2Program&);
120							GLES2Program	(const GLES2Program&);
121};
122
123static glu::ProgramSources getSources (void)
124{
125	const char* const vertexShaderSource =
126		"attribute mediump vec4 a_pos;\n"
127		"attribute mediump vec4 a_color;\n"
128		"varying mediump vec4 v_color;\n"
129		"void main(void)\n"
130		"{\n"
131		"\tv_color = a_color;\n"
132		"\tgl_Position = a_pos;\n"
133		"}";
134
135	const char* const fragmentShaderSource =
136		"varying mediump vec4 v_color;\n"
137		"void main(void)\n"
138		"{\n"
139		"\tgl_FragColor = v_color;\n"
140		"}";
141
142	return glu::makeVtxFragSources(vertexShaderSource, fragmentShaderSource);
143}
144
145GLES2Program::GLES2Program (const glw::Functions& gl)
146	: m_gl			(gl)
147	, m_glProgram	(gl, getSources())
148	, m_coordLoc	((glw::GLuint)-1)
149	, m_colorLoc	((glw::GLuint)-1)
150{
151	m_colorLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_color");
152	m_coordLoc = m_gl.getAttribLocation(m_glProgram.getProgram(), "a_pos");
153	GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to get attribute locations");
154}
155
156GLES2Program::~GLES2Program (void)
157{
158}
159
160void GLES2Program::render (int width, int height, float x1, float y1, float x2, float y2, PreservingSwapTest::DrawType drawType)
161{
162	if (drawType == PreservingSwapTest::DRAWTYPE_GLES2_RENDER)
163	{
164		const glw::GLfloat coords[] =
165		{
166			x1, y1, 0.0f, 1.0f,
167			x1, y2, 0.0f, 1.0f,
168			x2, y2, 0.0f, 1.0f,
169
170			x2, y2, 0.0f, 1.0f,
171			x2, y1, 0.0f, 1.0f,
172			x1, y1, 0.0f, 1.0f
173		};
174
175		const glw::GLubyte colors[] =
176		{
177			127,	127,	127,	255,
178			127,	127,	127,	255,
179			127,	127,	127,	255,
180
181			127,	127,	127,	255,
182			127,	127,	127,	255,
183			127,	127,	127,	255
184		};
185
186		m_gl.useProgram(m_glProgram.getProgram());
187		GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
188
189		m_gl.enableVertexAttribArray(m_coordLoc);
190		m_gl.enableVertexAttribArray(m_colorLoc);
191		GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to enable attributes");
192
193		m_gl.vertexAttribPointer(m_coordLoc, 4, GL_FLOAT, GL_FALSE, 0, coords);
194		m_gl.vertexAttribPointer(m_colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
195		GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to set attribute pointers");
196
197		m_gl.drawArrays(GL_TRIANGLES, 0, 6);
198		GLU_EXPECT_NO_ERROR(m_gl.getError(), "glDrawArrays() failed");
199
200		m_gl.disableVertexAttribArray(m_coordLoc);
201		m_gl.disableVertexAttribArray(m_colorLoc);
202		GLU_EXPECT_NO_ERROR(m_gl.getError(), "Failed to disable attributes");
203
204		m_gl.useProgram(0);
205		GLU_EXPECT_NO_ERROR(m_gl.getError(), "glUseProgram() failed");
206	}
207	else if (drawType == PreservingSwapTest::DRAWTYPE_GLES2_CLEAR)
208	{
209		const int ox	= width/2;
210		const int oy	= height/2;
211
212		const int px	= width;
213		const int py	= height;
214
215		const int x1i	= (int)((px/2.0f) * x1 + ox);
216		const int y1i	= (int)((py/2.0f) * y1 + oy);
217
218		const int x2i	= (int)((px/2.0f) * x2 + ox);
219		const int y2i	= (int)((py/2.0f) * y2 + oy);
220
221		m_gl.enable(GL_SCISSOR_TEST);
222		m_gl.scissor(x1i, y1i, x2i-x1i, y2i-y1i);
223		m_gl.clearColor(0.5f, 0.5f, 0.5f, 1.0f);
224		m_gl.clear(GL_COLOR_BUFFER_BIT);
225		m_gl.disable(GL_SCISSOR_TEST);
226	}
227	else
228		DE_ASSERT(false);
229}
230
231class ReferenceProgram
232{
233public:
234			ReferenceProgram	(void);
235			~ReferenceProgram	(void);
236
237	void	render				(tcu::Surface* target, float x1, float y1, float x2, float y2, PreservingSwapTest::DrawType drawType);
238
239private:
240						ReferenceProgram	(const ReferenceProgram&);
241	ReferenceProgram&	operator=			(const ReferenceProgram&);
242};
243
244ReferenceProgram::ReferenceProgram (void)
245{
246}
247
248ReferenceProgram::~ReferenceProgram (void)
249{
250}
251
252void ReferenceProgram::render (tcu::Surface* target, float x1, float y1, float x2, float y2, PreservingSwapTest::DrawType drawType)
253{
254	if (drawType == PreservingSwapTest::DRAWTYPE_GLES2_RENDER || drawType == PreservingSwapTest::DRAWTYPE_GLES2_CLEAR)
255	{
256		const int ox	= target->getWidth()/2;
257		const int oy	= target->getHeight()/2;
258
259		const int px	= target->getWidth();
260		const int py	= target->getHeight();
261
262		const int x1i	= (int)((px/2.0) * x1 + ox);
263		const int y1i	= (int)((py/2.0) * y1 + oy);
264
265		const int x2i	= (int)((px/2.0) * x2 + ox);
266		const int y2i	= (int)((py/2.0) * y2 + oy);
267
268		const tcu::RGBA	color(127, 127, 127, 255);
269
270		for (int y = y1i; y <= y2i; y++)
271		{
272			for (int x = x1i; x <= x2i; x++)
273				target->setPixel(x, y, color);
274		}
275	}
276	else
277		DE_ASSERT(false);
278}
279
280PreservingSwapTest::PreservingSwapTest (EglTestContext& eglTestCtx, bool preserveColorbuffer, bool readPixelsBeforeSwap, DrawType preSwapDrawType, DrawType postSwapDrawType, const char* name, const char* description)
281	: TestCase					(eglTestCtx, name, description)
282	, m_seed					(deStringHash(name))
283	, m_preserveColorbuffer		(preserveColorbuffer)
284	, m_readPixelsBeforeSwap	(readPixelsBeforeSwap)
285	, m_preSwapDrawType			(preSwapDrawType)
286	, m_postSwapDrawType		(postSwapDrawType)
287	, m_eglDisplay				(EGL_NO_DISPLAY)
288	, m_window					(DE_NULL)
289	, m_eglSurface				(EGL_NO_SURFACE)
290	, m_eglContext				(EGL_NO_CONTEXT)
291	, m_gles2Program			(DE_NULL)
292	, m_refProgram				(DE_NULL)
293{
294}
295
296PreservingSwapTest::~PreservingSwapTest (void)
297{
298	deinit();
299}
300
301EGLConfig getEGLConfig (const Library& egl, EGLDisplay eglDisplay, bool preserveColorbuffer)
302{
303	const EGLint attribList[] =
304	{
305		EGL_SURFACE_TYPE,		EGL_WINDOW_BIT | (preserveColorbuffer ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0),
306		EGL_RENDERABLE_TYPE,	EGL_OPENGL_ES2_BIT,
307		EGL_NONE
308	};
309
310	return eglu::chooseSingleConfig(egl, eglDisplay, &attribList[0]);
311}
312
313void clearColorScreen (const glw::Functions& gl, float red, float green, float blue, float alpha)
314{
315	gl.clearColor(red, green, blue, alpha);
316	gl.clear(GL_COLOR_BUFFER_BIT);
317}
318
319void clearColorReference (tcu::Surface* ref, float red, float green, float blue, float alpha)
320{
321	tcu::clear(ref->getAccess(), tcu::Vec4(red, green, blue, alpha));
322}
323
324void readPixels (const glw::Functions& gl, tcu::Surface* screen)
325{
326	gl.readPixels(0, 0, screen->getWidth(), screen->getHeight(),  GL_RGBA, GL_UNSIGNED_BYTE, screen->getAccess().getDataPtr());
327}
328
329void PreservingSwapTest::initEGLSurface (EGLConfig config)
330{
331	const eglu::NativeWindowFactory&	factory	= eglu::selectNativeWindowFactory(m_eglTestCtx.getNativeDisplayFactory(), m_testCtx.getCommandLine());
332
333	m_window		= factory.createWindow(&m_eglTestCtx.getNativeDisplay(), m_eglDisplay, config, DE_NULL, eglu::WindowParams(480, 480, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
334	m_eglSurface	= eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *m_window, m_eglDisplay, config, DE_NULL);
335}
336
337void PreservingSwapTest::initEGLContext (EGLConfig config)
338{
339	const Library&	egl				= m_eglTestCtx.getLibrary();
340	const EGLint	attribList[]	=
341	{
342		EGL_CONTEXT_CLIENT_VERSION, 2,
343		EGL_NONE
344	};
345
346	egl.bindAPI(EGL_OPENGL_ES_API);
347	m_eglContext = egl.createContext(m_eglDisplay, config, EGL_NO_CONTEXT, attribList);
348	EGLU_CHECK_MSG(egl, "eglCreateContext");
349
350	DE_ASSERT(m_eglSurface != EGL_NO_SURFACE);
351	egl.makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
352	EGLU_CHECK_MSG(egl, "eglMakeCurrent");
353}
354
355void PreservingSwapTest::init (void)
356{
357	m_eglDisplay	= eglu::getAndInitDisplay(m_eglTestCtx.getNativeDisplay());
358	m_eglConfig		= getEGLConfig(m_eglTestCtx.getLibrary(), m_eglDisplay, m_preserveColorbuffer);
359
360	if (m_eglConfig == DE_NULL)
361		TCU_THROW(NotSupportedError, "No supported config found");
362
363	initEGLSurface(m_eglConfig);
364	initEGLContext(m_eglConfig);
365
366	m_eglTestCtx.initGLFunctions(&m_gl, glu::ApiType::es(2,0));
367
368	m_gles2Program	= new GLES2Program(m_gl);
369	m_refProgram	= new ReferenceProgram();
370}
371
372void PreservingSwapTest::deinit (void)
373{
374	const Library& egl = m_eglTestCtx.getLibrary();
375
376	delete m_refProgram;
377	m_refProgram = DE_NULL;
378
379	delete m_gles2Program;
380	m_gles2Program = DE_NULL;
381
382	if (m_eglContext != EGL_NO_CONTEXT)
383	{
384		egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
385		egl.destroyContext(m_eglDisplay, m_eglContext);
386		m_eglContext = EGL_NO_CONTEXT;
387	}
388
389	if (m_eglSurface != EGL_NO_SURFACE)
390	{
391		egl.destroySurface(m_eglDisplay, m_eglSurface);
392		m_eglSurface = EGL_NO_SURFACE;
393	}
394
395	if (m_eglDisplay != EGL_NO_DISPLAY)
396	{
397		egl.terminate(m_eglDisplay);
398		m_eglDisplay = EGL_NO_DISPLAY;
399	}
400
401	delete m_window;
402	m_window = DE_NULL;
403}
404
405bool compareToReference (tcu::TestLog& log, const char* name, const char* description, const tcu::Surface& reference, const tcu::Surface& screen, int x, int y, int width, int height)
406{
407	return tcu::fuzzyCompare(log, name, description,
408							 getSubregion(reference.getAccess(), x, y, width, height),
409							 getSubregion(screen.getAccess(), x, y, width, height),
410							 0.05f, tcu::COMPARE_LOG_RESULT);
411}
412
413bool comparePreAndPostSwapFramebuffers (tcu::TestLog& log, const tcu::Surface& preSwap, const tcu::Surface& postSwap)
414{
415	return tcu::pixelThresholdCompare(log, "Pre- / Post framebuffer compare", "Compare pre- and post-swap framebuffers", preSwap, postSwap, tcu::RGBA(0, 0, 0, 0), tcu::COMPARE_LOG_RESULT);
416}
417
418TestCase::IterateResult PreservingSwapTest::iterate (void)
419{
420	const Library&	egl				= m_eglTestCtx.getLibrary();
421	tcu::TestLog&	log				= m_testCtx.getLog();
422	de::Random		rnd(m_seed);
423
424	const int		width			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_WIDTH);
425	const int		height			= eglu::querySurfaceInt(egl, m_eglDisplay, m_eglSurface, EGL_HEIGHT);
426
427	const float		clearRed		= rnd.getFloat();
428	const float		clearGreen		= rnd.getFloat();
429	const float		clearBlue		= rnd.getFloat();
430	const float		clearAlpha		= 1.0f;
431
432	const float		preSwapX1		= -0.9f * rnd.getFloat();
433	const float		preSwapY1		= -0.9f * rnd.getFloat();
434	const float		preSwapX2		= 0.9f * rnd.getFloat();
435	const float		preSwapY2		= 0.9f * rnd.getFloat();
436
437	const float		postSwapX1		= -0.9f * rnd.getFloat();
438	const float		postSwapY1		= -0.9f * rnd.getFloat();
439	const float		postSwapX2		= 0.9f * rnd.getFloat();
440	const float		postSwapY2		= 0.9f * rnd.getFloat();
441
442	tcu::Surface	postSwapFramebufferReference(width, height);
443	tcu::Surface	preSwapFramebufferReference(width, height);
444
445	tcu::Surface	postSwapFramebuffer(width, height);
446	tcu::Surface	preSwapFramebuffer(width, height);
447
448	if (m_preserveColorbuffer)
449		EGLU_CHECK_CALL(egl, surfaceAttrib(m_eglDisplay, m_eglSurface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED));
450
451	EGLU_CHECK_CALL(egl, makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext));
452
453	clearColorScreen(m_gl, clearRed, clearGreen, clearBlue, clearAlpha);
454
455	if (m_readPixelsBeforeSwap)
456		clearColorReference(&preSwapFramebufferReference, clearRed, clearGreen, clearBlue, clearAlpha);
457
458	clearColorReference(&postSwapFramebufferReference, clearRed, clearGreen, clearBlue, clearAlpha);
459
460	if (m_preSwapDrawType != DRAWTYPE_NONE)
461	{
462		m_gles2Program->render(width, height, preSwapX1, preSwapY1, preSwapX2, preSwapY2, m_preSwapDrawType);
463		m_refProgram->render(&postSwapFramebufferReference, preSwapX1, preSwapY1, preSwapX2, preSwapY2, m_preSwapDrawType);
464	}
465
466	if (m_readPixelsBeforeSwap)
467	{
468		if (m_preSwapDrawType != DRAWTYPE_NONE)
469			m_refProgram->render(&preSwapFramebufferReference, preSwapX1, preSwapY1, preSwapX2, preSwapY2, m_preSwapDrawType);
470
471		readPixels(m_gl, &preSwapFramebuffer);
472	}
473
474	EGLU_CHECK_CALL(egl, swapBuffers(m_eglDisplay, m_eglSurface));
475
476	if (m_postSwapDrawType != DRAWTYPE_NONE)
477	{
478		m_refProgram->render(&postSwapFramebufferReference, postSwapX1, postSwapY1, postSwapX2, postSwapY2, m_postSwapDrawType);
479		m_gles2Program->render(width, height, postSwapX1, postSwapY1, postSwapX2, postSwapY2, m_postSwapDrawType);
480	}
481
482	readPixels(m_gl, &postSwapFramebuffer);
483
484	bool isOk = true;
485
486	if (m_preserveColorbuffer)
487	{
488		if (m_readPixelsBeforeSwap)
489			isOk = isOk && compareToReference(log, "Compare pre-swap framebuffer to reference", "Compare pre-swap framebuffer to reference", preSwapFramebufferReference, preSwapFramebuffer, 0, 0, width, height);
490
491		isOk = isOk && compareToReference(log, "Compare post-swap framebuffer to reference", "Compare post-swap framebuffer to reference", postSwapFramebufferReference, postSwapFramebuffer, 0, 0, width, height);
492
493		if (m_readPixelsBeforeSwap && m_postSwapDrawType == PreservingSwapTest::DRAWTYPE_NONE)
494			isOk = isOk && comparePreAndPostSwapFramebuffers(log, preSwapFramebuffer, postSwapFramebuffer);
495	}
496	else
497	{
498		const int ox	= width/2;
499		const int oy	= height/2;
500
501		const int px	= width;
502		const int py	= height;
503
504		const int x1i	= (int)((px/2.0f) * postSwapX1 + ox);
505		const int y1i	= (int)((py/2.0f) * postSwapY1 + oy);
506
507		const int x2i	= (int)((px/2.0f) * postSwapX2 + ox);
508		const int y2i	= (int)((py/2.0f) * postSwapY2 + oy);
509
510		if (m_readPixelsBeforeSwap)
511			isOk = isOk && compareToReference(log, "Compare pre-swap framebuffer to reference", "Compare pre-swap framebuffer to reference", preSwapFramebufferReference, preSwapFramebuffer, 0, 0, width, height);
512
513		DE_ASSERT(m_postSwapDrawType != DRAWTYPE_NONE);
514		isOk = isOk && compareToReference(log, "Compare valid are of post-swap framebuffer to reference", "Compare valid area of post-swap framebuffer to reference", postSwapFramebufferReference, postSwapFramebuffer, x1i, y1i, x2i - x1i, y2i - y1i);
515	}
516
517	if (isOk)
518		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
519	else
520		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
521
522	return STOP;
523}
524
525string generateTestName (PreservingSwapTest::DrawType preSwapDrawType, PreservingSwapTest::DrawType postSwapDrawType)
526{
527	std::ostringstream stream;
528
529	if (preSwapDrawType == PreservingSwapTest::DRAWTYPE_NONE && postSwapDrawType == PreservingSwapTest::DRAWTYPE_NONE)
530		stream << "no_draw";
531	else
532	{
533		switch (preSwapDrawType)
534		{
535			case PreservingSwapTest::DRAWTYPE_NONE:
536				// Do nothing
537				break;
538
539			case PreservingSwapTest::DRAWTYPE_GLES2_RENDER:
540				stream << "pre_render";
541				break;
542
543			case PreservingSwapTest::DRAWTYPE_GLES2_CLEAR:
544				stream << "pre_clear";
545				break;
546
547			default:
548				DE_ASSERT(false);
549		}
550
551		if (preSwapDrawType != PreservingSwapTest::DRAWTYPE_NONE && postSwapDrawType != PreservingSwapTest::DRAWTYPE_NONE)
552			stream << "_";
553
554		switch (postSwapDrawType)
555		{
556			case PreservingSwapTest::DRAWTYPE_NONE:
557				// Do nothing
558				break;
559
560			case PreservingSwapTest::DRAWTYPE_GLES2_RENDER:
561				stream << "post_render";
562				break;
563
564			case PreservingSwapTest::DRAWTYPE_GLES2_CLEAR:
565				stream << "post_clear";
566				break;
567
568			default:
569				DE_ASSERT(false);
570		}
571	}
572
573	return stream.str();
574}
575
576} // anonymous
577
578PreservingSwapTests::PreservingSwapTests (EglTestContext& eglTestCtx)
579	: TestCaseGroup(eglTestCtx, "preserve_swap", "Color buffer preserving swap tests")
580{
581}
582
583void PreservingSwapTests::init (void)
584{
585	const PreservingSwapTest::DrawType preSwapDrawTypes[] =
586	{
587		PreservingSwapTest::DRAWTYPE_NONE,
588		PreservingSwapTest::DRAWTYPE_GLES2_CLEAR,
589		PreservingSwapTest::DRAWTYPE_GLES2_RENDER
590	};
591
592	const PreservingSwapTest::DrawType postSwapDrawTypes[] =
593	{
594		PreservingSwapTest::DRAWTYPE_NONE,
595		PreservingSwapTest::DRAWTYPE_GLES2_CLEAR,
596		PreservingSwapTest::DRAWTYPE_GLES2_RENDER
597	};
598
599	for (int preserveNdx = 0; preserveNdx < 2; preserveNdx++)
600	{
601		const bool				preserve		= (preserveNdx == 0);
602		TestCaseGroup* const	preserveGroup	= new TestCaseGroup(m_eglTestCtx, (preserve ? "preserve" : "no_preserve"), "");
603
604		for (int readPixelsNdx = 0; readPixelsNdx < 2; readPixelsNdx++)
605		{
606			const bool				readPixelsBeforeSwap		= (readPixelsNdx == 1);
607			TestCaseGroup* const	readPixelsBeforeSwapGroup	= new TestCaseGroup(m_eglTestCtx, (readPixelsBeforeSwap ? "read_before_swap" : "no_read_before_swap"), "");
608
609			for (int preSwapDrawTypeNdx = 0; preSwapDrawTypeNdx < DE_LENGTH_OF_ARRAY(preSwapDrawTypes); preSwapDrawTypeNdx++)
610			{
611				const PreservingSwapTest::DrawType preSwapDrawType = preSwapDrawTypes[preSwapDrawTypeNdx];
612
613				for (int postSwapDrawTypeNdx = 0; postSwapDrawTypeNdx < DE_LENGTH_OF_ARRAY(postSwapDrawTypes); postSwapDrawTypeNdx++)
614				{
615					const PreservingSwapTest::DrawType postSwapDrawType = postSwapDrawTypes[postSwapDrawTypeNdx];
616
617					// If not preserving and rendering after swap, then there is nothing to verify
618					if (!preserve && postSwapDrawType == PreservingSwapTest::DRAWTYPE_NONE)
619						continue;
620
621					const std::string name = generateTestName(preSwapDrawType, postSwapDrawType);
622
623					readPixelsBeforeSwapGroup->addChild(new PreservingSwapTest(m_eglTestCtx, preserve, readPixelsBeforeSwap, preSwapDrawType, postSwapDrawType, name.c_str(), ""));
624				}
625			}
626
627			preserveGroup->addChild(readPixelsBeforeSwapGroup);
628		}
629
630		addChild(preserveGroup);
631	}
632}
633
634} // egl
635} // deqp
636