text: /*-------------------------------------------------------------------------
   * 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 Base class for rendering tests.
   *//*--------------------------------------------------------------------*/
  
  #include "teglRenderCase.hpp"
  
  #include "teglSimpleConfigCase.hpp"
  
  #include "egluNativeDisplay.hpp"
  #include "egluNativeWindow.hpp"
  #include "egluNativePixmap.hpp"
  #include "egluUtil.hpp"
  
  #include "tcuRenderTarget.hpp"
  #include "tcuTestLog.hpp"
  #include "tcuCommandLine.hpp"
  
  #include "deStringUtil.hpp"
  #include "deUniquePtr.hpp"
  
  #include <algorithm>
  #include <iterator>
  #include <memory>
  #include <set>
  
  #include <EGL/eglext.h>
  
  #if !defined(EGL_OPENGL_ES3_BIT_KHR)
  #	define EGL_OPENGL_ES3_BIT_KHR	0x0040
  #endif
  #if !defined(EGL_CONTEXT_MAJOR_VERSION_KHR)
  #	define EGL_CONTEXT_MAJOR_VERSION_KHR EGL_CONTEXT_CLIENT_VERSION
  #endif
  
  using std::string;
  using std::vector;
  using std::set;
  
  using tcu::TestLog;
  
  namespace deqp
  {
  namespace egl
  {
  
  // \todo [2013-04-24 pyry] Should we instead store surface bit somewhere?
  template<class Derived, class Base>
  inline bool instanceOf (Base& obj)
  {
  	return dynamic_cast<Derived*>(&obj) != DE_NULL;
  }
  
  static void postSurface (tcu::egl::Surface& surface)
  {
  	const bool	isWindow	= instanceOf<tcu::egl::WindowSurface>(surface);
  	const bool	isPixmap	= instanceOf<tcu::egl::PixmapSurface>(surface);
  	const bool	isPbuffer	= instanceOf<tcu::egl::PbufferSurface>(surface);
  
  	DE_ASSERT((isWindow?1:0) + (isPixmap?1:0) + (isPbuffer?1:0) == 1);
  
  	if (isWindow)
  	{
  		tcu::egl::WindowSurface& window = static_cast<tcu::egl::WindowSurface&>(surface);
  		window.swapBuffers();
  	}
  	else if (isPixmap)
  	{
  		TCU_CHECK_EGL_CALL(eglWaitClient());
  	}
  	else
  	{
  		DE_ASSERT(isPbuffer);
  		DE_UNREF(isPbuffer);
  		TCU_CHECK_EGL_CALL(eglWaitClient());
  	}
  }
  
  // RenderCase
  
  RenderCase::RenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint apiMask, EGLint surfaceTypeMask, const vector<EGLint>& configIds)
  	: SimpleConfigCase	(eglTestCtx, name, description, configIds)
  	, m_apiMask			(apiMask)
  	, m_surfaceTypeMask	(surfaceTypeMask)
  {
  }
  
  RenderCase::~RenderCase (void)
  {
  }
  
  EGLint RenderCase::getSupportedApis (void)
  {
  	EGLint apiMask = 0;
  
  #if defined(DEQP_SUPPORT_GLES2)
  	apiMask |= EGL_OPENGL_ES2_BIT;
  #endif
  
  #if defined(DEQP_SUPPORT_GLES3)
  	apiMask |= EGL_OPENGL_ES3_BIT_KHR;
  #endif
  
  #if defined(DEQP_SUPPORT_GLES1)
  	apiMask |= EGL_OPENGL_ES_BIT;
  #endif
  
  #if defined(DEQP_SUPPORT_VG)
  	apiMask |= EGL_OPENVG_BIT;
  #endif
  
  	return apiMask;
  }
  
  void RenderCase::executeForConfig (tcu::egl::Display& defaultDisplay, EGLConfig config)
  {
  	tcu::TestLog&			log				= m_testCtx.getLog();
  	int						width			= 128;
  	int						height			= 128;
  	EGLint					configId		= defaultDisplay.getConfigAttrib(config, EGL_CONFIG_ID);
  	bool					isOk			= true;
  	string					failReason		= "";
  
  	if (m_surfaceTypeMask & EGL_WINDOW_BIT)
  	{
  		tcu::ScopedLogSection(log, (string("Config") + de::toString(configId) + "-Window").c_str(),
  										(string("Config ID ") + de::toString(configId) + ", window surface").c_str());
  
  		try
  		{
  			tcu::egl::Display&					display		= m_eglTestCtx.getDisplay();
  			de::UniquePtr<eglu::NativeWindow>	window		(m_eglTestCtx.createNativeWindow(display.getEGLDisplay(), config, DE_NULL, width, height, eglu::parseWindowVisibility(m_testCtx.getCommandLine())));
  			EGLSurface							eglSurface	= createWindowSurface(m_eglTestCtx.getNativeDisplay(), *window, display.getEGLDisplay(), config, DE_NULL);
  			tcu::egl::WindowSurface				surface		(display, eglSurface);
  
  			executeForSurface(display, surface, config);
  		}
  		catch (const tcu::TestError& e)
  		{
  			log << e;
  			isOk = false;
  			failReason = e.what();
  		}
  	}
  
  	if (m_surfaceTypeMask & EGL_PIXMAP_BIT)
  	{
  		tcu::ScopedLogSection(log, (string("Config") + de::toString(configId) + "-Pixmap").c_str(),
  										(string("Config ID ") + de::toString(configId) + ", pixmap surface").c_str());
  
  		try
  		{
  			tcu::egl::Display&					display		= m_eglTestCtx.getDisplay();
  			std::auto_ptr<eglu::NativePixmap>	pixmap		(m_eglTestCtx.createNativePixmap(display.getEGLDisplay(), config, DE_NULL, width, height));
  			EGLSurface							eglSurface	= createPixmapSurface(m_eglTestCtx.getNativeDisplay(), *pixmap, display.getEGLDisplay(), config, DE_NULL);
  			tcu::egl::PixmapSurface				surface		(display, eglSurface);
  
  			executeForSurface(display, surface, config);
  		}
  		catch (const tcu::TestError& e)
  		{
  			log << e;
  			isOk = false;
  			failReason = e.what();
  		}
  	}
  
  	if (m_surfaceTypeMask & EGL_PBUFFER_BIT)
  	{
  		tcu::ScopedLogSection(log, (string("Config") + de::toString(configId) + "-Pbuffer").c_str(),
  										(string("Config ID ") + de::toString(configId) + ", pbuffer surface").c_str());
  		try
  		{
  			EGLint surfaceAttribs[] =
  			{
  				EGL_WIDTH,	width,
  				EGL_HEIGHT,	height,
  				EGL_NONE
  			};
  
  			tcu::egl::PbufferSurface surface(defaultDisplay, config, surfaceAttribs);
  
  			executeForSurface(defaultDisplay, surface, config);
  		}
  		catch (const tcu::TestError& e)
  		{
  			log << e;
  			isOk = false;
  			failReason = e.what();
  		}
  	}
  
  	if (!isOk && m_testCtx.getTestResult() == QP_TEST_RESULT_PASS)
  		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, failReason.c_str());
  }
  
  // SingleContextRenderCase
  
  SingleContextRenderCase::SingleContextRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint apiMask, EGLint surfaceTypeMask, const std::vector<EGLint>& configIds)
  	: RenderCase(eglTestCtx, name, description, apiMask, surfaceTypeMask, configIds)
  {
  }
  
  SingleContextRenderCase::~SingleContextRenderCase (void)
  {
  }
  
  void SingleContextRenderCase::executeForSurface (tcu::egl::Display& display, tcu::egl::Surface& surface, EGLConfig config)
  {
  	EGLint				supportedApis	= getSupportedApis();
  	const EGLint		apis[]			= { EGL_OPENGL_ES2_BIT, EGL_OPENGL_ES3_BIT_KHR, EGL_OPENGL_ES_BIT, EGL_OPENVG_BIT };
  	tcu::TestLog&		log				= m_testCtx.getLog();
  
  	// Check if case is supported
  	if ((m_apiMask & supportedApis) != m_apiMask)
  		throw tcu::NotSupportedError("Client APIs not supported", "", __FILE__, __LINE__);
  
  	for (int apiNdx = 0; apiNdx < DE_LENGTH_OF_ARRAY(apis); apiNdx++)
  	{
  		EGLint apiBit = apis[apiNdx];
  
  		if ((apiBit & m_apiMask) == 0)
  			continue; // Skip this api.
  
  		EGLint			api		= EGL_NONE;
  		const char*		apiName	= DE_NULL;
  		vector<EGLint>	contextAttribs;
  
  		// Select api enum and build context attributes.
  		switch (apiBit)
  		{
  			case EGL_OPENGL_ES2_BIT:
  				api		= EGL_OPENGL_ES_API;
  				apiName	= "OpenGL ES 2.x";
  				contextAttribs.push_back(EGL_CONTEXT_CLIENT_VERSION);
  				contextAttribs.push_back(2);
  				break;
  
  			case EGL_OPENGL_ES3_BIT_KHR:
  				api		= EGL_OPENGL_ES_API;
  				apiName	= "OpenGL ES 3.x";
  				contextAttribs.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
  				contextAttribs.push_back(3);
  				break;
  
  			case EGL_OPENGL_ES_BIT:
  				api		= EGL_OPENGL_ES_API;
  				apiName	= "OpenGL ES 1.x";
  				contextAttribs.push_back(EGL_CONTEXT_CLIENT_VERSION);
  				contextAttribs.push_back(1);
  				break;
  
  			case EGL_OPENVG_BIT:
  				api		= EGL_OPENVG_API;
  				apiName	= "OpenVG";
  				break;
  
  			default:
  				DE_ASSERT(DE_FALSE);
  		}
  
  		contextAttribs.push_back(EGL_NONE);
  
  		log << TestLog::Message << apiName << TestLog::EndMessage;
  
  		tcu::egl::Context context(display, config, &contextAttribs[0], api);
  
  		context.makeCurrent(surface, surface);
  		executeForContext(display, context, surface, apiBit);
  
  		// Call SwapBuffers() / WaitClient() to finish rendering
  		postSurface(surface);
  	}
  }
  
  // MultiContextRenderCase
  
  MultiContextRenderCase::MultiContextRenderCase (EglTestContext& eglTestCtx, const char* name, const char* description, EGLint api, EGLint surfaceType, const vector<EGLint>& configIds, int numContextsPerApi)
  	: RenderCase			(eglTestCtx, name, description, api, surfaceType, configIds)
  	, m_numContextsPerApi	(numContextsPerApi)
  {
  }
  
  MultiContextRenderCase::~MultiContextRenderCase (void)
  {
  }
  
  void MultiContextRenderCase::executeForSurface (tcu::egl::Display& display, tcu::egl::Surface& surface, EGLConfig config)
  {
  	vector<std::pair<EGLint, tcu::egl::Context*> > contexts;
  	contexts.reserve(3*m_numContextsPerApi); // 3 types of contexts at maximum.
  
  	try
  	{
  		// Create contexts that will participate in rendering.
  		for (int ndx = 0; ndx < m_numContextsPerApi; ndx++)
  		{
  			if (m_apiMask & EGL_OPENGL_ES2_BIT)
  			{
  				static const EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
  				contexts.push_back(std::make_pair(EGL_OPENGL_ES2_BIT, new tcu::egl::Context(display, config, &attribs[0], EGL_OPENGL_ES_API)));
  			}
  
  			if (m_apiMask & EGL_OPENGL_ES3_BIT_KHR)
  			{
  				static const EGLint attribs[] = { EGL_CONTEXT_MAJOR_VERSION_KHR, 3, EGL_NONE };
  				contexts.push_back(std::make_pair(EGL_OPENGL_ES3_BIT_KHR, new tcu::egl::Context(display, config, &attribs[0], EGL_OPENGL_ES_API)));
  			}
  
  			if (m_apiMask & EGL_OPENGL_ES_BIT)
  			{
  				static const EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 1, EGL_NONE };
  				contexts.push_back(std::make_pair(EGL_OPENGL_ES_BIT, new tcu::egl::Context(display, config, &attribs[0], EGL_OPENGL_ES_API)));
  			}
  
  			if (m_apiMask & EGL_OPENVG_BIT)
  			{
  				static const EGLint attribs[] = { EGL_NONE };
  				contexts.push_back(std::make_pair(EGL_OPENVG_BIT, new tcu::egl::Context(display, config, &attribs[0], EGL_OPENVG_API)));
  			}
  		}
  
  		// Execute for contexts.
  		executeForContexts(display, surface, config, contexts);
  	}
  	catch (const std::exception&)
  	{
  		// Make sure all contexts have been destroyed.
  		for (vector<std::pair<EGLint, tcu::egl::Context*> >::iterator i = contexts.begin(); i != contexts.end(); i++)
  			delete i->second;
  		throw;
  	}
  
  	// Destroy contexts.
  	for (vector<std::pair<EGLint, tcu::egl::Context*> >::iterator i = contexts.begin(); i != contexts.end(); i++)
  		delete i->second;
  }
  
  // Utilities
  
  void addRenderConfigIdSet (
  	vector<RenderConfigIdSet>&			configSets,
  	const vector<eglu::ConfigInfo>&		configInfos,
  	const eglu::FilterList&				baseFilters,
  	const char*							name,
  	tcu::RGBA							colorBits,
  	EGLint								surfaceType)
  {
  	eglu::FilterList filters = baseFilters;
  	filters << (eglu::ConfigColorBits() == colorBits) << (eglu::ConfigSurfaceType() & surfaceType);
  
  	vector<EGLint> matchingConfigs;
  
  	for (vector<eglu::ConfigInfo>::const_iterator configIter = configInfos.begin(); configIter != configInfos.end(); configIter++)
  	{
  		if (!filters.match(*configIter))
  			continue;
  
  		matchingConfigs.push_back(configIter->configId);
  	}
  
  	configSets.push_back(RenderConfigIdSet(name, "", matchingConfigs, surfaceType));
  }
  
  void addRenderConfigIdSet (
  	vector<RenderConfigIdSet>&			configSets,
  	const vector<eglu::ConfigInfo>&		configInfos,
  	const eglu::FilterList&				baseFilters,
  	const char*							name,
  	tcu::RGBA							colorBits)
  {
  	addRenderConfigIdSet(configSets, configInfos, baseFilters, (string(name) + "_window").c_str(),	colorBits, EGL_WINDOW_BIT);
  	addRenderConfigIdSet(configSets, configInfos, baseFilters, (string(name) + "_pixmap").c_str(),	colorBits, EGL_PIXMAP_BIT);
  	addRenderConfigIdSet(configSets, configInfos, baseFilters, (string(name) + "_pbuffer").c_str(),	colorBits, EGL_PBUFFER_BIT);
  }
  
  void getDefaultRenderConfigIdSets (vector<RenderConfigIdSet>& configSets, const vector<eglu::ConfigInfo>& configInfos, const eglu::FilterList& baseFilters)
  {
  	using tcu::RGBA;
  
  	addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgb565",	RGBA(5, 6, 5, 0));
  	addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgb888",	RGBA(8, 8, 8, 0));
  	addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgba4444",	RGBA(4, 4, 4, 4));
  	addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgba5551",	RGBA(5, 5, 5, 1));
  	addRenderConfigIdSet(configSets, configInfos, baseFilters, "rgba8888",	RGBA(8, 8, 8, 8));
  
  	// Add other config ids to "other" set
  	{
  		set<EGLint>		usedConfigs;
  		vector<EGLint>	otherCfgSet;
  
  		for (vector<RenderConfigIdSet>::const_iterator setIter = configSets.begin(); setIter != configSets.end(); setIter++)
  		{
  			const vector<EGLint>& setCfgs = setIter->getConfigIds();
  			for (vector<EGLint>::const_iterator i = setCfgs.begin(); i != setCfgs.end(); i++)
  				usedConfigs.insert(*i);
  		}
  
  		for (vector<eglu::ConfigInfo>::const_iterator cfgIter = configInfos.begin(); cfgIter != configInfos.end(); cfgIter++)
  		{
  			if (!baseFilters.match(*cfgIter))
  				continue;
  
  			EGLint id = cfgIter->configId;
  
  			if (usedConfigs.find(id) == usedConfigs.end())
  				otherCfgSet.push_back(id);
  		}
  
  		configSets.push_back(RenderConfigIdSet("other", "", otherCfgSet, EGL_WINDOW_BIT|EGL_PIXMAP_BIT|EGL_PBUFFER_BIT));
  	}
  }
  
  } // egl
  } // deqp
  