C++程序  |  684行  |  21.21 KB

/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES Utilities
 * ------------------------------------------------
 *
 * 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 OpenGL ES 3plus wrapper context.
 *//*--------------------------------------------------------------------*/

#include "gluES3PlusWrapperContext.hpp"
#include "gluRenderContext.hpp"
#include "gluRenderConfig.hpp"
#include "glwInitFunctions.hpp"
#include "glwFunctionLoader.hpp"
#include "gluContextFactory.hpp"
#include "gluContextInfo.hpp"
#include "gluShaderUtil.hpp"
#include "deThreadLocal.hpp"
#include "deSTLUtil.hpp"
#include "deUniquePtr.hpp"
#include "glwEnums.hpp"

#include <sstream>
#include <vector>
#include <string>
#include <cstring>
#include <algorithm>
#include <map>

namespace glu
{

namespace es3plus
{

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

class Context
{
public:
								Context			(const glu::RenderContext& ctx);
								~Context		(void);

	void						addExtension	(const char* name);

	const glw::Functions&		gl;			//!< GL 4.3 core context functions.

	// Wrapper state.
	string						vendor;
	string						version;
	string						renderer;
	string						shadingLanguageVersion;
	string						extensions;
	vector<string>				extensionList;
	bool						primitiveRestartEnabled;

	deUint32					defaultVAO;
	bool						defaultVAOBound;

	const glu::GLSLVersion		nativeGLSLVersion;
};

Context::Context (const glu::RenderContext& ctx)
	: gl						(ctx.getFunctions())
	, vendor					("drawElements")
	, version					("OpenGL ES 3.2")
	, renderer					((const char*)gl.getString(GL_RENDERER))
	, shadingLanguageVersion	("OpenGL ES GLSL ES 3.2")
	, primitiveRestartEnabled	(false)
	, defaultVAO				(0)
	, defaultVAOBound			(false)
	, nativeGLSLVersion			(glu::getContextTypeGLSLVersion(ctx.getType()))
{
	const de::UniquePtr<glu::ContextInfo> ctxInfo(glu::ContextInfo::create(ctx));

	gl.genVertexArrays(1, &defaultVAO);
	if (gl.getError() != GL_NO_ERROR || defaultVAO == 0)
		throw tcu::InternalError("Failed to allocate VAO for emulation");

	gl.bindVertexArray(defaultVAO);
	if (gl.getError() != GL_NO_ERROR)
		throw tcu::InternalError("Failed to bind default VAO");
	defaultVAOBound = true;

	gl.enable(GL_PROGRAM_POINT_SIZE);
	gl.getError(); // supress potential errors, feature is not critical

	gl.enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
	gl.getError(); // suppress

	// Extensions
	addExtension("GL_OES_texture_stencil8");
	addExtension("GL_OES_sample_shading");
	addExtension("GL_OES_sample_variables");
	addExtension("GL_OES_shader_multisample_interpolation");
	addExtension("GL_OES_shader_image_atomic");
	addExtension("GL_OES_texture_storage_multisample_2d_array");

	// Enable only if base ctx supports these or compatible GL_NV_blend_equation_advanced ext
	if (ctxInfo->isExtensionSupported("GL_NV_blend_equation_advanced") ||
		ctxInfo->isExtensionSupported("GL_KHR_blend_equation_advanced"))
	{
		addExtension("GL_KHR_blend_equation_advanced");
	}
	if (ctxInfo->isExtensionSupported("GL_NV_blend_equation_advanced_coherent") ||
		ctxInfo->isExtensionSupported("GL_KHR_blend_equation_advanced_coherent"))
	{
		addExtension("GL_KHR_blend_equation_advanced_coherent");
	}

	addExtension("GL_EXT_shader_io_blocks");
	addExtension("GL_EXT_geometry_shader");
	addExtension("GL_EXT_geometry_point_size");
	addExtension("GL_EXT_tessellation_shader");
	addExtension("GL_EXT_tessellation_point_size");
	addExtension("GL_EXT_gpu_shader5");
	addExtension("GL_KHR_debug");
	addExtension("GL_EXT_texture_cube_map_array");
	addExtension("GL_EXT_shader_implicit_conversions");
	addExtension("GL_EXT_primitive_bounding_box");
	addExtension("GL_EXT_texture_sRGB_decode");
	addExtension("GL_EXT_texture_border_clamp");
	addExtension("GL_EXT_texture_buffer");
	addExtension("GL_EXT_draw_buffers_indexed");
}

Context::~Context (void)
{
	if (defaultVAO)
		gl.deleteVertexArrays(1, &defaultVAO);
}

void Context::addExtension (const char* name)
{
	if (!extensions.empty())
		extensions += " ";
	extensions += name;

	extensionList.push_back(name);
}

static de::ThreadLocal tls_context;

void setCurrentContext (Context* context)
{
	tls_context.set(context);
}

inline Context* getCurrentContext (void)
{
	return (Context*)tls_context.get();
}

static GLW_APICALL void GLW_APIENTRY getIntegerv (deUint32 pname, deInt32* params)
{
	Context* context = getCurrentContext();

	if (context)
	{
		if (pname == GL_NUM_EXTENSIONS && params)
			*params = (deInt32)context->extensionList.size();
		else
			context->gl.getIntegerv(pname, params);
	}
}

static GLW_APICALL const glw::GLubyte* GLW_APIENTRY getString (deUint32 name)
{
	Context* context = getCurrentContext();

	if (context)
	{
		switch (name)
		{
			case GL_VENDOR:						return (const glw::GLubyte*)context->vendor.c_str();
			case GL_VERSION:					return (const glw::GLubyte*)context->version.c_str();
			case GL_RENDERER:					return (const glw::GLubyte*)context->renderer.c_str();
			case GL_SHADING_LANGUAGE_VERSION:	return (const glw::GLubyte*)context->shadingLanguageVersion.c_str();
			case GL_EXTENSIONS:					return (const glw::GLubyte*)context->extensions.c_str();
			default:							return context->gl.getString(name);
		}
	}
	else
		return DE_NULL;
}

static GLW_APICALL const glw::GLubyte* GLW_APIENTRY getStringi (deUint32 name, deUint32 index)
{
	Context* context = getCurrentContext();

	if (context)
	{
		if (name == GL_EXTENSIONS)
		{
			if ((size_t)index < context->extensionList.size())
				return (const glw::GLubyte*)context->extensionList[index].c_str();
			else
				return context->gl.getStringi(name, ~0u);
		}
		else
			return context->gl.getStringi(name, index);
	}
	else
		return DE_NULL;
}

static GLW_APICALL void GLW_APIENTRY enable (deUint32 cap)
{
	Context* context = getCurrentContext();

	if (context)
	{
		if (cap == GL_PRIMITIVE_RESTART_FIXED_INDEX)
		{
			context->primitiveRestartEnabled = true;
			// \todo [2013-09-30 pyry] Call to glPrimitiveRestartIndex() is required prior to all draw calls!
		}
		else
			context->gl.enable(cap);
	}
}

static GLW_APICALL void GLW_APIENTRY disable (deUint32 cap)
{
	Context* context = getCurrentContext();

	if (context)
	{
		if (cap == GL_PRIMITIVE_RESTART_FIXED_INDEX)
			context->primitiveRestartEnabled = false;
		else
			context->gl.disable(cap);
	}
}

static GLW_APICALL void GLW_APIENTRY bindVertexArray (deUint32 array)
{
	Context* context = getCurrentContext();

	if (context)
	{
		context->gl.bindVertexArray(array == 0 ? context->defaultVAO : array);
		context->defaultVAOBound = (array == 0);
	}
}

static GLW_APICALL void GLW_APIENTRY hint (deUint32 target, deUint32 mode)
{
	Context* context = getCurrentContext();

	if (context)
	{
		if (target != GL_GENERATE_MIPMAP_HINT)
			context->gl.hint(target, mode);
		// \todo [2013-09-30 pyry] Verify mode.
	}
}

static void translateShaderSource (deUint32 shaderType, std::ostream& dst, const std::string& src, const std::vector<std::string>& filteredExtensions, GLSLVersion version)
{
	bool				foundVersion		= false;
	std::istringstream	istr				(src);
	std::string			line;
	int					srcLineNdx			= 1;
	bool				preprocessorSection	= true;

	while (std::getline(istr, line, '\n'))
	{
		if (preprocessorSection && !line.empty() && line[0] != '#')
		{
			preprocessorSection = false;

			// ARB_separate_shader_objects requires gl_PerVertex to be explicitly declared
			if (shaderType == GL_VERTEX_SHADER)
			{
				dst << "out gl_PerVertex {\n"
					<< "    vec4 gl_Position;\n"
					<< "    float gl_PointSize;\n"
					<< "    float gl_ClipDistance[];\n"
					<< "};\n"
					<< "#line " << (srcLineNdx + 1) << "\n";
			}
			else if (shaderType == GL_TESS_CONTROL_SHADER)
			{
				dst << "#extension GL_ARB_tessellation_shader : enable\n"
					<< "in gl_PerVertex {\n"
					<< "	highp vec4 gl_Position;\n"
					<< "	highp float gl_PointSize;\n"
					<< "} gl_in[gl_MaxPatchVertices];\n"
					<< "out gl_PerVertex {\n"
					<< "	highp vec4 gl_Position;\n"
					<< "	highp float gl_PointSize;\n"
					<< "} gl_out[];\n"
					<< "#line " << (srcLineNdx + 1) << "\n";
			}
			else if (shaderType == GL_TESS_EVALUATION_SHADER)
			{
				dst << "#extension GL_ARB_tessellation_shader : enable\n"
					<< "in gl_PerVertex {\n"
					<< "	highp vec4 gl_Position;\n"
					<< "	highp float gl_PointSize;\n"
					<< "} gl_in[gl_MaxPatchVertices];\n"
					<< "out gl_PerVertex {\n"
					<< "	highp vec4 gl_Position;\n"
					<< "	highp float gl_PointSize;\n"
					<< "};\n"
					<< "#line " << (srcLineNdx + 1) << "\n";
			}
			else if (shaderType == GL_GEOMETRY_SHADER)
			{
				dst << "in gl_PerVertex {\n"
					<< "	highp vec4 gl_Position;\n"
					<< "	highp float gl_PointSize;\n"
					<< "} gl_in[];\n"
					<< "out gl_PerVertex {\n"
					<< "	highp vec4 gl_Position;\n"
					<< "	highp float gl_PointSize;\n"
					<< "};\n"
					<< "#line " << (srcLineNdx + 1) << "\n";
			}

			// GL_EXT_primitive_bounding_box tessellation no-op fallback
			if (shaderType == GL_TESS_CONTROL_SHADER)
			{
				dst << "#define gl_BoundingBoxEXT _dummy_unused_output_for_primitive_bbox\n"
					<< "patch out vec4 _dummy_unused_output_for_primitive_bbox[2];\n"
					<< "#line " << (srcLineNdx + 1) << "\n";
			}
		}

		if (line == "#version 310 es" || line == "#version 320 es")
		{
			foundVersion = true;
			dst << glu::getGLSLVersionDeclaration(version) << "\n";
		}
		else if (line == "#version 300 es")
		{
			foundVersion = true;
			dst << "#version 330\n";
		}
		else if (line.substr(0, 10) == "precision ")
		{
			const size_t	precPos		= 10;
			const size_t	precEndPos	= line.find(' ', precPos);
			const size_t	endPos		= line.find(';');

			if (precEndPos != std::string::npos && endPos != std::string::npos && endPos > precEndPos+1)
			{
				const size_t		typePos		= precEndPos+1;
				const std::string	precision	= line.substr(precPos, precEndPos-precPos);
				const std::string	type		= line.substr(typePos, endPos-typePos);
				const bool			precOk		= precision == "lowp" || precision == "mediump" || precision == "highp";

				if (precOk &&
					(type == "image2D" || type == "uimage2D" || type == "iimage2D" ||
					 type == "imageCube" || type == "uimageCube" || type == "iimageCube" ||
					 type == "image3D" || type == "iimage3D" || type == "uimage3D" ||
					 type == "image2DArray" || type == "iimage2DArray" || type == "uimage2DArray" ||
					 type == "imageCubeArray" || type == "iimageCubeArray" || type == "uimageCubeArray"))
					dst << "// "; // Filter out statement
			}

			dst << line << "\n";
		}
		else if (line.substr(0, 11) == "#extension ")
		{
			const size_t	extNamePos		= 11;
			const size_t	extNameEndPos	= line.find_first_of(" :", extNamePos);
			const size_t	behaviorPos		= line.find_first_not_of(" :", extNameEndPos);

			if (extNameEndPos != std::string::npos && behaviorPos != std::string::npos)
			{
				const std::string	extName				= line.substr(extNamePos, extNameEndPos-extNamePos);
				const std::string	behavior			= line.substr(behaviorPos);
				const bool			filteredExtension	= de::contains(filteredExtensions.begin(), filteredExtensions.end(), extName);
				const bool			validBehavior		= behavior == "require" || behavior == "enable" || behavior == "warn" || behavior == "disable";

				if (filteredExtension && validBehavior)
					dst << "// "; // Filter out extension
			}
			dst << line << "\n";
		}
		else if (line.substr(0, 21) == "layout(blend_support_")
			dst << "// " << line << "\n";
		else
			dst << line << "\n";

		srcLineNdx += 1;
	}

	DE_ASSERT(foundVersion);
	DE_UNREF(foundVersion);
}

static std::string translateShaderSources (deUint32 shaderType, deInt32 count, const char* const* strings, const int* length, const std::vector<std::string>& filteredExtensions, GLSLVersion version)
{
	std::ostringstream	srcIn;
	std::ostringstream	srcOut;

	for (int ndx = 0; ndx < count; ndx++)
	{
		const int len = length && length[ndx] >= 0 ? length[ndx] : (int)strlen(strings[ndx]);
		srcIn << std::string(strings[ndx], strings[ndx] + len);
	}

	translateShaderSource(shaderType, srcOut, srcIn.str(), filteredExtensions, version);

	return srcOut.str();
}

static GLW_APICALL void GLW_APIENTRY shaderSource (deUint32 shader, deInt32 count, const char* const* strings, const int* length)
{
	Context* context = getCurrentContext();

	if (context)
	{
		if (count > 0 && strings)
		{
			deInt32				shaderType = GL_NONE;
			context->gl.getShaderiv(shader, GL_SHADER_TYPE, &shaderType);
			{
				const std::string	translatedSrc	= translateShaderSources(shaderType, count, strings, length, context->extensionList, context->nativeGLSLVersion);
				const char*			srcPtr			= translatedSrc.c_str();
				context->gl.shaderSource(shader, 1, &srcPtr, DE_NULL);
			}
		}
		else
			context->gl.shaderSource(shader, count, strings, length);
	}
}

static GLW_APICALL void GLW_APIENTRY bindFramebuffer (deUint32 target, deUint32 framebuffer)
{
	Context* context = getCurrentContext();

	if (context)
	{
		context->gl.bindFramebuffer(target, framebuffer);

		// Emulate ES behavior where sRGB conversion is only controlled by color buffer format.
		if (target == GL_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER || target == GL_READ_FRAMEBUFFER)
			((framebuffer != 0) ? context->gl.enable : context->gl.disable)(GL_FRAMEBUFFER_SRGB);
	}
}

static GLW_APICALL void GLW_APIENTRY blendBarrierKHR (void)
{
	Context* context = getCurrentContext();

	if (context)
	{
		// \todo [2014-03-18 pyry] Use BlendBarrierNV() if supported
		context->gl.finish();
	}
}

static GLW_APICALL deUint32 GLW_APIENTRY createShaderProgramv (deUint32 type, deInt32 count, const char* const* strings)
{
	Context* context = getCurrentContext();

	if (context)
	{
		if (count > 0 && strings)
		{
			const std::string	translatedSrc	= translateShaderSources(type, count, strings, DE_NULL, context->extensionList, context->nativeGLSLVersion);
			const char*			srcPtr			= translatedSrc.c_str();
			return context->gl.createShaderProgramv(type, 1, &srcPtr);
		}
		else
			return context->gl.createShaderProgramv(type, count, strings);
	}
	return 0;
}

static GLW_APICALL void GLW_APIENTRY dummyPrimitiveBoundingBox (float minX, float minY, float minZ, float minW, float maxX, float maxY, float maxZ, float maxW)
{
	// dummy no-op. No-op is a valid implementation. States queries are not emulated.
	DE_UNREF(minX);
	DE_UNREF(minY);
	DE_UNREF(minZ);
	DE_UNREF(minW);
	DE_UNREF(maxX);
	DE_UNREF(maxY);
	DE_UNREF(maxZ);
	DE_UNREF(maxW);
}

static void initFunctions (glw::Functions* dst, const glw::Functions& src)
{
	// Functions directly passed to GL context.
#include "gluES3PlusWrapperFuncs.inl"

	// Wrapped functions.
	dst->bindVertexArray		= bindVertexArray;
	dst->disable				= disable;
	dst->enable					= enable;
	dst->getIntegerv			= getIntegerv;
	dst->getString				= getString;
	dst->getStringi				= getStringi;
	dst->hint					= hint;
	dst->shaderSource			= shaderSource;
	dst->createShaderProgramv	= createShaderProgramv;
	dst->bindFramebuffer		= bindFramebuffer;

	// Extension functions
	{
		using std::map;

		class ExtFuncLoader : public glw::FunctionLoader
		{
		public:
			ExtFuncLoader (const map<string, glw::GenericFuncType>& extFuncs)
				: m_extFuncs(extFuncs)
			{
			}

			glw::GenericFuncType get (const char* name) const
			{
				map<string, glw::GenericFuncType>::const_iterator pos = m_extFuncs.find(name);
				return pos != m_extFuncs.end() ? pos->second : DE_NULL;
			}

		private:
			const map<string, glw::GenericFuncType>& m_extFuncs;
		};

		map<string, glw::GenericFuncType>	extFuncMap;
		const ExtFuncLoader					extFuncLoader	(extFuncMap);

		// OES_sample_shading
		extFuncMap["glMinSampleShadingOES"]			= (glw::GenericFuncType)src.minSampleShading;

		// OES_texture_storage_multisample_2d_array
		extFuncMap["glTexStorage3DMultisampleOES"]	= (glw::GenericFuncType)src.texStorage3DMultisample;

		// KHR_blend_equation_advanced
		extFuncMap["glBlendBarrierKHR"]				= (glw::GenericFuncType)blendBarrierKHR;

		// EXT_tessellation_shader
		extFuncMap["glPatchParameteriEXT"]			= (glw::GenericFuncType)src.patchParameteri;

		// EXT_geometry_shader
		extFuncMap["glFramebufferTextureEXT"]		= (glw::GenericFuncType)src.framebufferTexture;

		// KHR_debug
		extFuncMap["glDebugMessageControlKHR"]		= (glw::GenericFuncType)src.debugMessageControl;
		extFuncMap["glDebugMessageInsertKHR"]		= (glw::GenericFuncType)src.debugMessageInsert;
		extFuncMap["glDebugMessageCallbackKHR"]		= (glw::GenericFuncType)src.debugMessageCallback;
		extFuncMap["glGetDebugMessageLogKHR"]		= (glw::GenericFuncType)src.getDebugMessageLog;
		extFuncMap["glGetPointervKHR"]				= (glw::GenericFuncType)src.getPointerv;
		extFuncMap["glPushDebugGroupKHR"]			= (glw::GenericFuncType)src.pushDebugGroup;
		extFuncMap["glPopDebugGroupKHR"]			= (glw::GenericFuncType)src.popDebugGroup;
		extFuncMap["glObjectLabelKHR"]				= (glw::GenericFuncType)src.objectLabel;
		extFuncMap["glGetObjectLabelKHR"]			= (glw::GenericFuncType)src.getObjectLabel;
		extFuncMap["glObjectPtrLabelKHR"]			= (glw::GenericFuncType)src.objectPtrLabel;
		extFuncMap["glGetObjectPtrLabelKHR"]		= (glw::GenericFuncType)src.getObjectPtrLabel;

		// GL_EXT_primitive_bounding_box (dummy no-op)
		extFuncMap["glPrimitiveBoundingBoxEXT"]		= (glw::GenericFuncType)dummyPrimitiveBoundingBox;

		// GL_EXT_texture_border_clamp
		extFuncMap["glTexParameterIivEXT"]			= (glw::GenericFuncType)src.texParameterIiv;
		extFuncMap["glTexParameterIuivEXT"]			= (glw::GenericFuncType)src.texParameterIuiv;
		extFuncMap["glGetTexParameterIivEXT"]		= (glw::GenericFuncType)src.getTexParameterIiv;
		extFuncMap["glGetTexParameterIuivEXT"]		= (glw::GenericFuncType)src.getTexParameterIuiv;
		extFuncMap["glSamplerParameterIivEXT"]		= (glw::GenericFuncType)src.samplerParameterIiv;
		extFuncMap["glSamplerParameterIuivEXT"]		= (glw::GenericFuncType)src.samplerParameterIuiv;
		extFuncMap["glGetSamplerParameterIivEXT"]	= (glw::GenericFuncType)src.getSamplerParameterIiv;
		extFuncMap["glGetSamplerParameterIuivEXT"]	= (glw::GenericFuncType)src.getSamplerParameterIuiv;

		// GL_EXT_texture_buffer
		extFuncMap["glTexBufferEXT"]				= (glw::GenericFuncType)src.texBuffer;
		extFuncMap["glTexBufferRangeEXT"]			= (glw::GenericFuncType)src.texBufferRange;

		// GL_EXT_draw_buffers_indexed
		extFuncMap["glEnableiEXT"]					= (glw::GenericFuncType)src.enablei;
		extFuncMap["glDisableiEXT"]					= (glw::GenericFuncType)src.disablei;
		extFuncMap["glBlendEquationiEXT"]			= (glw::GenericFuncType)src.blendEquationi;
		extFuncMap["glBlendEquationSeparateiEXT"]	= (glw::GenericFuncType)src.blendEquationSeparatei;
		extFuncMap["glBlendFunciEXT"]				= (glw::GenericFuncType)src.blendFunci;
		extFuncMap["glBlendFuncSeparateiEXT"]		= (glw::GenericFuncType)src.blendFuncSeparatei;
		extFuncMap["glColorMaskiEXT"]				= (glw::GenericFuncType)src.colorMaski;
		extFuncMap["glIsEnablediEXT"]				= (glw::GenericFuncType)src.isEnabledi;

		{
			int	numExts	= 0;
			dst->getIntegerv(GL_NUM_EXTENSIONS, &numExts);

			if (numExts > 0)
			{
				vector<const char*> extStr(numExts);

				for (int ndx = 0; ndx < numExts; ndx++)
					extStr[ndx] = (const char*)dst->getStringi(GL_EXTENSIONS, ndx);

				glw::initExtensionsES(dst, &extFuncLoader, (int)extStr.size(), &extStr[0]);
			}
		}
	}
}

} // es3plus

ES3PlusWrapperContext::ES3PlusWrapperContext (const ContextFactory& factory, const RenderConfig& config, const tcu::CommandLine& cmdLine)
	: m_context		(DE_NULL)
	, m_wrapperCtx	(DE_NULL)
{
	// Flags that are valid for both core & es context. Currently only excludes CONTEXT_FORWARD_COMPATIBLE
	const ContextFlags validContextFlags = CONTEXT_ROBUST | CONTEXT_DEBUG;

	static const ContextType wrappableNativeTypes[] =
	{
		ContextType(ApiType::core(4,4), config.type.getFlags() & validContextFlags),	// !< higher in the list, preferred
		ContextType(ApiType::core(4,3), config.type.getFlags() & validContextFlags),
	};

	if (config.type.getAPI() != ApiType::es(3,2))
		throw tcu::NotSupportedError("Unsupported context type (ES3.2 wrapper supports only ES3.2)");

	// try to create any wrappable context

	for (int nativeCtxNdx = 0; nativeCtxNdx < DE_LENGTH_OF_ARRAY(wrappableNativeTypes); ++nativeCtxNdx)
	{
		glu::ContextType nativeContext = wrappableNativeTypes[nativeCtxNdx];

		try
		{
			glu::RenderConfig nativeConfig = config;
			nativeConfig.type = nativeContext;

			m_context		= factory.createContext(nativeConfig, cmdLine);
			m_wrapperCtx	= new es3plus::Context(*m_context);

			es3plus::setCurrentContext(m_wrapperCtx);
			es3plus::initFunctions(&m_functions, m_context->getFunctions());
			break;
		}
		catch (...)
		{
			es3plus::setCurrentContext(DE_NULL);

			delete m_wrapperCtx;
			delete m_context;

			m_wrapperCtx = DE_NULL;
			m_context = DE_NULL;

			// throw only if all tries failed (that is, this was the last potential target)
			if (nativeCtxNdx + 1 == DE_LENGTH_OF_ARRAY(wrappableNativeTypes))
				throw;
			else
				continue;
		}
	}
}

ES3PlusWrapperContext::~ES3PlusWrapperContext (void)
{
	delete m_wrapperCtx;
	delete m_context;
}

ContextType ES3PlusWrapperContext::getType (void) const
{
	return ContextType(ApiType::es(3,2), m_context->getType().getFlags());
}

} // glu