C++程序  |  690行  |  19.77 KB

/*-------------------------------------------------------------------------
 * drawElements Quality Program EGL Module
 * ---------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief eglMakeCurrent performance tests.
 *//*--------------------------------------------------------------------*/

#include "teglMakeCurrentPerfTests.hpp"

#include "egluNativeWindow.hpp"
#include "egluNativePixmap.hpp"
#include "egluUtil.hpp"

#include "tcuTestLog.hpp"

#include "deRandom.hpp"
#include "deStringUtil.hpp"

#include "deClock.h"
#include "deString.h"

#include <algorithm>
#include <cmath>
#include <limits>
#include <sstream>
#include <string>
#include <vector>

using std::ostringstream;
using std::string;
using std::vector;

using tcu::TestLog;

namespace deqp
{
namespace egl
{

class MakeCurrentPerfCase : public TestCase
{
public:
	enum SurfaceType
	{
		SURFACETYPE_PBUFFER	= (1<<0),
		SURFACETYPE_WINDOW	= (1<<1),
		SURFACETYPE_PIXMAP	= (1<<2)
	};

	struct Spec
	{
		SurfaceType	surfaceTypes;
		int			contextCount;
		int			surfaceCount;

		bool		release;

		int			iterationCount;
		int			sampleCount;

		string		toName			(void) const;
		string		toDescription	(void) const;
	};
					MakeCurrentPerfCase		(EglTestContext& eglTestCtx, const Spec& spec, const char* name, const char* description);
					~MakeCurrentPerfCase	(void);

	void			init					(void);
	void			deinit					(void);
	IterateResult	iterate					(void);

private:
	Spec						m_spec;
	de::Random					m_rnd;

	EGLConfig					m_config;
	vector<EGLContext>			m_contexts;
	vector<EGLSurface>			m_surfaces;

	vector<eglu::NativeWindow*>	m_windows;
	vector<eglu::NativePixmap*>	m_pixmaps;

	vector<deUint64>			m_samples;

	void					chooseConfig	(void);
	void					createSurfaces	(void);
	void					createContexts	(void);

	void					destroySurfaces	(void);
	void					destroyContexts	(void);

	void					createPBuffer	(void);
	void					createWindow	(void);
	void					createPixmap	(void);

	void					logTestInfo		(void);
	void					logResults		(void);
	// Disabled
							MakeCurrentPerfCase	(const MakeCurrentPerfCase&);
	MakeCurrentPerfCase&	operator=			(const MakeCurrentPerfCase&);
};

string MakeCurrentPerfCase::Spec::toName (void) const
{
	ostringstream name;

	name << "context";

	if (contextCount > 1)
		name << "s_" << contextCount;

	if ((surfaceTypes & SURFACETYPE_WINDOW) != 0)
		name << "_window" << (surfaceCount > 1 ? "s" : "");

	if ((surfaceTypes & SURFACETYPE_PIXMAP) != 0)
		name << "_pixmap" << (surfaceCount > 1 ? "s" : "");

	if ((surfaceTypes & SURFACETYPE_PBUFFER) != 0)
		name << "_pbuffer" << (surfaceCount > 1 ? "s" : "");

	if (surfaceCount > 1)
		name << "_" << surfaceCount;

	if (release)
		name << "_release";

	return name.str();
}

string MakeCurrentPerfCase::Spec::toDescription (void) const
{
	// \todo [mika] Generate descrpition
	return toName();
}

MakeCurrentPerfCase::MakeCurrentPerfCase (EglTestContext& eglTestCtx, const Spec& spec, const char* name, const char* description)
	: TestCase		(eglTestCtx, tcu::NODETYPE_PERFORMANCE, name, description)
	, m_spec		(spec)
	, m_rnd			(deStringHash(name))
	, m_config		(DE_NULL)
{
}

MakeCurrentPerfCase::~MakeCurrentPerfCase (void)
{
	deinit();
}

void MakeCurrentPerfCase::init (void)
{
	chooseConfig();
	createContexts();
	createSurfaces();
}

void MakeCurrentPerfCase::deinit (void)
{
	destroyContexts();
	destroySurfaces();
}

void MakeCurrentPerfCase::chooseConfig (void)
{
	const EGLint	surfaceBits	= ((m_spec.surfaceTypes & SURFACETYPE_WINDOW) != 0 ? EGL_WINDOW_BIT : 0)
									| ((m_spec.surfaceTypes & SURFACETYPE_PIXMAP) != 0 ? EGL_PIXMAP_BIT : 0)
									| ((m_spec.surfaceTypes & SURFACETYPE_PBUFFER) != 0 ? EGL_PBUFFER_BIT : 0);

	const EGLint	attribList[] = {
		EGL_SURFACE_TYPE,		surfaceBits,
		EGL_RENDERABLE_TYPE,	EGL_OPENGL_ES2_BIT,
		EGL_NONE
	};

	EGLint		configCount = 0;
	EGLDisplay	display	= m_eglTestCtx.getDisplay().getEGLDisplay();

	TCU_CHECK_EGL_CALL(eglChooseConfig(display, attribList, &m_config, 1, &configCount));

	if (configCount <= 0)
		throw tcu::NotSupportedError("No compatible configs found");
}

void MakeCurrentPerfCase::createSurfaces (void)
{
	vector<SurfaceType> types;

	if ((m_spec.surfaceTypes & SURFACETYPE_WINDOW) != 0)
		types.push_back(SURFACETYPE_WINDOW);

	if ((m_spec.surfaceTypes & SURFACETYPE_PIXMAP) != 0)
		types.push_back(SURFACETYPE_PIXMAP);

	if ((m_spec.surfaceTypes & SURFACETYPE_PBUFFER) != 0)
		types.push_back(SURFACETYPE_PBUFFER);

	DE_ASSERT((int)types.size() <= m_spec.surfaceCount);

	// Create surfaces
	for (int surfaceNdx = 0; surfaceNdx < m_spec.surfaceCount; surfaceNdx++)
	{
		SurfaceType type = types[surfaceNdx % types.size()];

		switch (type)
		{
			case SURFACETYPE_PBUFFER:
				createPBuffer();
				break;

			case SURFACETYPE_WINDOW:
				createWindow();
				break;

			case SURFACETYPE_PIXMAP:
				createPixmap();
				break;

			default:
				DE_ASSERT(false);
		};
	}
}

void MakeCurrentPerfCase::createPBuffer (void)
{
	const EGLint	width	= 256;
	const EGLint	height	= 256;

	const EGLint attribList[] = {
		EGL_WIDTH,	width,
		EGL_HEIGHT, height,
		EGL_NONE
	};

	EGLDisplay	display	= m_eglTestCtx.getDisplay().getEGLDisplay();
	EGLSurface	surface = eglCreatePbufferSurface(display, m_config, attribList);

	TCU_CHECK_EGL_MSG("eglCreatePbufferSurface()");

	m_surfaces.push_back(surface);
}

void MakeCurrentPerfCase::createWindow (void)
{
	const EGLint	width	= 256;
	const EGLint	height	= 256;

	eglu::NativeWindow* window	= DE_NULL;
	EGLSurface			surface	= EGL_NO_SURFACE;

	try
	{
		window	= m_eglTestCtx.createNativeWindow(m_eglTestCtx.getDisplay().getEGLDisplay(), m_config, DE_NULL, width, height, eglu::parseWindowVisibility(m_eglTestCtx.getTestContext().getCommandLine()));
		surface	= eglu::createWindowSurface(m_eglTestCtx.getNativeDisplay(), *window, m_eglTestCtx.getDisplay().getEGLDisplay(), m_config, DE_NULL);
	}
	catch (const std::exception&)
	{
		if (surface != EGL_NO_SURFACE)
			TCU_CHECK_EGL_CALL(eglDestroySurface(m_eglTestCtx.getDisplay().getEGLDisplay(), surface));

		delete window;
		throw;
	}

	m_windows.push_back(window);
	m_surfaces.push_back(surface);
}

void MakeCurrentPerfCase::createPixmap (void)
{
	const EGLint	width	= 256;
	const EGLint	height	= 256;

	eglu::NativePixmap*	pixmap	= DE_NULL;
	EGLSurface			surface	= EGL_NO_SURFACE;

	try
	{
		pixmap	= m_eglTestCtx.createNativePixmap(m_eglTestCtx.getDisplay().getEGLDisplay(), m_config, DE_NULL, width, height);
		surface	= eglu::createPixmapSurface(m_eglTestCtx.getNativeDisplay(), *pixmap, m_eglTestCtx.getDisplay().getEGLDisplay(), m_config, DE_NULL);
	}
	catch (const std::exception&)
	{
		if (surface != EGL_NO_SURFACE)
			TCU_CHECK_EGL_CALL(eglDestroySurface(m_eglTestCtx.getDisplay().getEGLDisplay(), surface));

		delete pixmap;
		throw;
	}

	m_pixmaps.push_back(pixmap);
	m_surfaces.push_back(surface);
}

void MakeCurrentPerfCase::destroySurfaces (void)
{
	if (m_surfaces.size() > 0)
	{
		EGLDisplay display = m_eglTestCtx.getDisplay().getEGLDisplay();

		// Destroy surfaces
		for (vector<EGLSurface>::iterator iter = m_surfaces.begin(); iter != m_surfaces.end(); ++iter)
		{
			if (*iter != EGL_NO_SURFACE)
				TCU_CHECK_EGL_CALL(eglDestroySurface(display, *iter));
			*iter = EGL_NO_SURFACE;
		}

		m_surfaces.clear();

		// Destroy pixmaps
		for (vector<eglu::NativePixmap*>::iterator iter = m_pixmaps.begin(); iter != m_pixmaps.end(); ++iter)
		{
			delete *iter;
			*iter = NULL;
		}

		m_pixmaps.clear();

		// Destroy windows
		for (vector<eglu::NativeWindow*>::iterator iter = m_windows.begin(); iter != m_windows.end(); ++iter)
		{
			delete *iter;
			*iter = NULL;
		}

		m_windows.clear();

		// Clear all surface handles
		m_surfaces.clear();
	}
}

void MakeCurrentPerfCase::createContexts (void)
{
	EGLDisplay display = m_eglTestCtx.getDisplay().getEGLDisplay();

	for (int contextNdx = 0; contextNdx < m_spec.contextCount; contextNdx++)
	{
		const EGLint attribList[] = {
			EGL_CONTEXT_CLIENT_VERSION, 2,
			EGL_NONE
		};

		TCU_CHECK_EGL_CALL(eglBindAPI(EGL_OPENGL_ES_API));
		EGLContext context = eglCreateContext(display, m_config, EGL_NO_CONTEXT, attribList);
		TCU_CHECK_EGL_MSG("eglCreateContext()");

		m_contexts.push_back(context);
	}
}

void MakeCurrentPerfCase::destroyContexts (void)
{
	if (m_contexts.size() > 0)
	{
		EGLDisplay display = m_eglTestCtx.getDisplay().getEGLDisplay();

		for (vector<EGLContext>::iterator iter = m_contexts.begin(); iter != m_contexts.end(); ++iter)
		{
			if (*iter != EGL_NO_CONTEXT)
				TCU_CHECK_EGL_CALL(eglDestroyContext(display, *iter));
			*iter = EGL_NO_CONTEXT;
		}

		m_contexts.clear();
	}
}

void MakeCurrentPerfCase::logTestInfo (void)
{
	TestLog& log = m_testCtx.getLog();

	{
		tcu::ScopedLogSection	section(log, "Test Info", "Test case information.");

		log << TestLog::Message << "Context count: "	<< m_contexts.size()											<< TestLog::EndMessage;
		log << TestLog::Message << "Surfaces count: "	<< m_surfaces.size()											<< TestLog::EndMessage;
		log << TestLog::Message << "Sample count: "	<< m_spec.sampleCount												<< TestLog::EndMessage;
		log << TestLog::Message << "Iteration count: "	<< m_spec.iterationCount										<< TestLog::EndMessage;
		log << TestLog::Message << "Window count: "	<< m_windows.size()													<< TestLog::EndMessage;
		log << TestLog::Message << "Pixmap count: "	<< m_pixmaps.size()													<< TestLog::EndMessage;
		log << TestLog::Message << "PBuffer count: "	<< (m_surfaces.size() - m_windows.size() - m_pixmaps.size())	<< TestLog::EndMessage;

		if (m_spec.release)
			log << TestLog::Message << "Context is released after each use. Both binding and releasing context are included in result time." << TestLog::EndMessage;
	}
}

void MakeCurrentPerfCase::logResults (void)
{
	TestLog& log = m_testCtx.getLog();

	log << TestLog::SampleList("Result", "Result")
		<< TestLog::SampleInfo << TestLog::ValueInfo("Time", "Time", "us", QP_SAMPLE_VALUE_TAG_RESPONSE)
		<< TestLog::EndSampleInfo;

	for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++)
		log << TestLog::Sample << deInt64(m_samples[sampleNdx]) << TestLog::EndSample;

	log << TestLog::EndSampleList;

	// Log stats
	{
		deUint64	totalTimeUs				= 0;
		deUint64	totalIterationCount		= 0;

		float		iterationTimeMeanUs		= 0.0f;
		float		iterationTimeMedianUs	= 0.0f;
		float		iterationTimeVarianceUs	= 0.0f;
		float		iterationTimeSkewnessUs	= 0.0f;
		float		iterationTimeMinUs		= std::numeric_limits<float>::max();
		float		iterationTimeMaxUs		= 0.0f;

		std::sort(m_samples.begin(), m_samples.end());

		// Calculate totals
		for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++)
		{
			totalTimeUs			+= m_samples[sampleNdx];
			totalIterationCount	+= m_spec.iterationCount;
		}

		// Calculate mean and median
		iterationTimeMeanUs		= ((float)(((double)totalTimeUs) / totalIterationCount));
		iterationTimeMedianUs	= ((float)(((double)m_samples[m_samples.size() / 2]) / m_spec.iterationCount));

		// Calculate variance
		for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++)
		{
			float iterationTimeUs	= (float)(((double)m_samples[sampleNdx]) / m_spec.iterationCount);
			iterationTimeVarianceUs	+= std::pow(iterationTimeUs - iterationTimeMedianUs, 2.0f);
		}

		// Calculate min and max
		for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++)
		{
			float iterationTimeUs	= (float)(((double)m_samples[sampleNdx]) / m_spec.iterationCount);
			iterationTimeMinUs		= std::min<float>(iterationTimeMinUs, iterationTimeUs);
			iterationTimeMaxUs		= std::max<float>(iterationTimeMaxUs, iterationTimeUs);
		}

		iterationTimeVarianceUs /= m_samples.size();

		// Calculate skewness
		for (int sampleNdx = 0; sampleNdx < (int)m_samples.size(); sampleNdx++)
		{
			float iterationTimeUs	= (float)(((double)m_samples[sampleNdx]) / m_spec.iterationCount);
			iterationTimeSkewnessUs	= std::pow((iterationTimeUs - iterationTimeMedianUs) / iterationTimeVarianceUs, 2.0f);
		}

		iterationTimeSkewnessUs /= (float)m_samples.size();

		{
			tcu::ScopedLogSection	section(log, "Result", "Statistics from results.");

			log << TestLog::Message << "Total time: "	<< totalTimeUs				<< "us" << TestLog::EndMessage;
			log << TestLog::Message << "Mean: "			<< iterationTimeMeanUs		<< "us" << TestLog::EndMessage;
			log << TestLog::Message << "Median: "		<< iterationTimeMedianUs	<< "us" << TestLog::EndMessage;
			log << TestLog::Message << "Variance: "		<< iterationTimeVarianceUs	<< "us" << TestLog::EndMessage;
			log << TestLog::Message << "Skewness: "		<< iterationTimeSkewnessUs	<< "us" << TestLog::EndMessage;
			log << TestLog::Message << "Min: "			<< iterationTimeMinUs		<< "us" << TestLog::EndMessage;
			log << TestLog::Message << "Max: "			<< iterationTimeMaxUs		<< "us" << TestLog::EndMessage;
		}

		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, de::floatToString((float)(((double)totalTimeUs)/totalIterationCount), 2).c_str());
	}
}

TestCase::IterateResult MakeCurrentPerfCase::iterate (void)
{
	if (m_samples.size() == 0)
		logTestInfo();

	{
		EGLDisplay	display		= m_eglTestCtx.getDisplay().getEGLDisplay();
		deUint64	beginTimeUs	= deGetMicroseconds();

		for (int iteration = 0; iteration < m_spec.iterationCount; iteration++)
		{
			EGLContext	context = m_contexts[m_rnd.getUint32() % m_contexts.size()];
			EGLSurface	surface	= m_surfaces[m_rnd.getUint32() % m_surfaces.size()];

			TCU_CHECK_EGL_CALL(eglMakeCurrent(display, surface, surface, context));

			if (m_spec.release)
				TCU_CHECK_EGL_CALL(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
		}

		m_samples.push_back(deGetMicroseconds() - beginTimeUs);
	}

	if ((int)m_samples.size() == m_spec.sampleCount)
	{
		logResults();
		return STOP;
	}
	else
		return CONTINUE;
}

MakeCurrentPerfTests::MakeCurrentPerfTests (EglTestContext& eglTestCtx)
	: TestCaseGroup(eglTestCtx, "make_current", "eglMakeCurrent performance tests")
{
}

void MakeCurrentPerfTests::init (void)
{
	const int iterationCount	= 100;
	const int sampleCount		= 100;

	// Add simple test group
	{
		TestCaseGroup* simple = new TestCaseGroup(m_eglTestCtx, "simple", "Simple eglMakeCurrent performance tests using single context and surface");

		const MakeCurrentPerfCase::SurfaceType types[] = {
			MakeCurrentPerfCase::SURFACETYPE_PBUFFER,
			MakeCurrentPerfCase::SURFACETYPE_PIXMAP,
			MakeCurrentPerfCase::SURFACETYPE_WINDOW
		};

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
		{
			for (int releaseNdx = 0; releaseNdx < 2; releaseNdx++)
			{
				MakeCurrentPerfCase::Spec spec;

				spec.surfaceTypes	= types[typeNdx];
				spec.contextCount	= 1;
				spec.surfaceCount	= 1;
				spec.release		= (releaseNdx == 1);
				spec.iterationCount	= iterationCount;
				spec.sampleCount	= sampleCount;

				simple->addChild(new MakeCurrentPerfCase(m_eglTestCtx, spec, spec.toName().c_str(), spec.toDescription().c_str()));
			}
		}

		addChild(simple);
	}

	// Add multi context test group
	{
		TestCaseGroup* multiContext = new TestCaseGroup(m_eglTestCtx, "multi_context", "eglMakeCurrent performance tests using multiple contexts and single surface");

		const MakeCurrentPerfCase::SurfaceType types[] = {
			MakeCurrentPerfCase::SURFACETYPE_PBUFFER,
			MakeCurrentPerfCase::SURFACETYPE_PIXMAP,
			MakeCurrentPerfCase::SURFACETYPE_WINDOW
		};

		const int contextCounts[] = {
			10, 100
		};

		for (int contextCountNdx = 0; contextCountNdx < DE_LENGTH_OF_ARRAY(contextCounts); contextCountNdx++)
		{
			for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
			{
				for (int releaseNdx = 0; releaseNdx < 2; releaseNdx++)
				{
					MakeCurrentPerfCase::Spec spec;

					spec.surfaceTypes	= types[typeNdx];
					spec.contextCount	= contextCounts[contextCountNdx];
					spec.surfaceCount	= 1;
					spec.release		= (releaseNdx == 1);
					spec.iterationCount	= iterationCount;
					spec.sampleCount	= sampleCount;

					multiContext->addChild(new MakeCurrentPerfCase(m_eglTestCtx, spec, spec.toName().c_str(), spec.toDescription().c_str()));
				}
			}
		}

		addChild(multiContext);
	}

	// Add multi surface test group
	{
		TestCaseGroup* multiSurface = new TestCaseGroup(m_eglTestCtx, "multi_surface", "eglMakeCurrent performance tests using single context and multiple surfaces");

		const MakeCurrentPerfCase::SurfaceType types[] = {
			MakeCurrentPerfCase::SURFACETYPE_PBUFFER,
			MakeCurrentPerfCase::SURFACETYPE_PIXMAP,
			MakeCurrentPerfCase::SURFACETYPE_WINDOW,

			(MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER	|MakeCurrentPerfCase::SURFACETYPE_PIXMAP),
			(MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER	|MakeCurrentPerfCase::SURFACETYPE_WINDOW),
			(MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PIXMAP	|MakeCurrentPerfCase::SURFACETYPE_WINDOW),

			(MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER|MakeCurrentPerfCase::SURFACETYPE_PIXMAP|MakeCurrentPerfCase::SURFACETYPE_WINDOW)
		};

		const int surfaceCounts[] = {
			10, 100
		};

		for (int surfaceCountNdx = 0; surfaceCountNdx < DE_LENGTH_OF_ARRAY(surfaceCounts); surfaceCountNdx++)
		{
			for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
			{
				for (int releaseNdx = 0; releaseNdx < 2; releaseNdx++)
				{
					MakeCurrentPerfCase::Spec spec;

					spec.surfaceTypes	= types[typeNdx];
					spec.surfaceCount	= surfaceCounts[surfaceCountNdx];
					spec.contextCount	= 1;
					spec.release		= (releaseNdx == 1);
					spec.iterationCount	= iterationCount;
					spec.sampleCount	= sampleCount;

					multiSurface->addChild(new MakeCurrentPerfCase(m_eglTestCtx, spec, spec.toName().c_str(), spec.toDescription().c_str()));
				}
			}
		}

		addChild(multiSurface);
	}

	// Add Complex? test group
	{
		TestCaseGroup* multi = new TestCaseGroup(m_eglTestCtx, "complex", "eglMakeCurrent performance tests using multiple contexts and multiple surfaces");

		const MakeCurrentPerfCase::SurfaceType types[] = {
			MakeCurrentPerfCase::SURFACETYPE_PBUFFER,
			MakeCurrentPerfCase::SURFACETYPE_PIXMAP,
			MakeCurrentPerfCase::SURFACETYPE_WINDOW,

			(MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER	|MakeCurrentPerfCase::SURFACETYPE_PIXMAP),
			(MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER	|MakeCurrentPerfCase::SURFACETYPE_WINDOW),
			(MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PIXMAP	|MakeCurrentPerfCase::SURFACETYPE_WINDOW),

			(MakeCurrentPerfCase::SurfaceType)(MakeCurrentPerfCase::SURFACETYPE_PBUFFER|MakeCurrentPerfCase::SURFACETYPE_PIXMAP|MakeCurrentPerfCase::SURFACETYPE_WINDOW)
		};

		const int surfaceCounts[] = {
			10, 100
		};


		const int contextCounts[] = {
			10, 100
		};

		for (int surfaceCountNdx = 0; surfaceCountNdx < DE_LENGTH_OF_ARRAY(surfaceCounts); surfaceCountNdx++)
		{
			for (int contextCountNdx = 0; contextCountNdx < DE_LENGTH_OF_ARRAY(contextCounts); contextCountNdx++)
			{
				for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(types); typeNdx++)
				{
					for (int releaseNdx = 0; releaseNdx < 2; releaseNdx++)
					{
						MakeCurrentPerfCase::Spec spec;

						spec.surfaceTypes	= types[typeNdx];
						spec.contextCount	= contextCounts[contextCountNdx];
						spec.surfaceCount	= surfaceCounts[surfaceCountNdx];
						spec.release		= (releaseNdx == 1);
						spec.iterationCount	= iterationCount;
						spec.sampleCount	= sampleCount;

						multi->addChild(new MakeCurrentPerfCase(m_eglTestCtx, spec, spec.toName().c_str(), spec.toDescription().c_str()));
					}
				}
			}
		}

		addChild(multi);
	}
}

} // egl
} // deqp