1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES Utilities
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 OpenGL ES rendering context.
22 *//*--------------------------------------------------------------------*/
23
24#include "gluRenderContext.hpp"
25#include "gluDefs.hpp"
26#include "gluRenderConfig.hpp"
27#include "gluES3PlusWrapperContext.hpp"
28#include "gluFboRenderContext.hpp"
29#include "gluPlatform.hpp"
30#include "gluStrUtil.hpp"
31#include "glwInitFunctions.hpp"
32#include "glwEnums.hpp"
33#include "tcuPlatform.hpp"
34#include "tcuCommandLine.hpp"
35#include "deStringUtil.hpp"
36
37namespace glu
38{
39
40inline bool versionGreaterOrEqual (ApiType a, ApiType b)
41{
42	return a.getMajorVersion() > b.getMajorVersion() ||
43		   (a.getMajorVersion() == b.getMajorVersion() && a.getMinorVersion() >= b.getMinorVersion());
44}
45
46bool contextSupports (ContextType ctxType, ApiType requiredApiType)
47{
48	// \todo [2014-10-06 pyry] Check exact forward-compatible restrictions.
49	const bool forwardCompatible = (ctxType.getFlags() & CONTEXT_FORWARD_COMPATIBLE) != 0;
50
51	if (isContextTypeES(ctxType))
52	{
53		DE_ASSERT(!forwardCompatible);
54		return requiredApiType.getProfile() == PROFILE_ES &&
55			   versionGreaterOrEqual(ctxType.getAPI(), requiredApiType);
56	}
57	else if (isContextTypeGLCore(ctxType))
58	{
59		if (forwardCompatible)
60			return ctxType.getAPI() == requiredApiType;
61		else
62			return requiredApiType.getProfile() == PROFILE_CORE &&
63				   versionGreaterOrEqual(ctxType.getAPI(), requiredApiType);
64	}
65	else if (isContextTypeGLCompatibility(ctxType))
66	{
67		DE_ASSERT(!forwardCompatible);
68		return (requiredApiType.getProfile() == PROFILE_CORE || requiredApiType.getProfile() == PROFILE_COMPATIBILITY) &&
69			   versionGreaterOrEqual(ctxType.getAPI(), requiredApiType);
70	}
71	else
72	{
73		DE_ASSERT(false);
74		return false;
75	}
76}
77
78static ContextFlags parseContextFlags (const std::string& flagsStr)
79{
80	const std::vector<std::string>	flagNames	= de::splitString(flagsStr, ',');
81	ContextFlags					flags		= ContextFlags(0);
82	static const struct
83	{
84		const char*		name;
85		ContextFlags	flag;
86	} s_flagMap[] =
87	{
88		{ "debug",		CONTEXT_DEBUG	},
89		{ "robust",		CONTEXT_ROBUST	}
90	};
91
92	for (std::vector<std::string>::const_iterator flagIter = flagNames.begin(); flagIter != flagNames.end(); ++flagIter)
93	{
94		int ndx;
95		for (ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_flagMap); ndx++)
96		{
97			if (*flagIter == s_flagMap[ndx].name)
98			{
99				flags = flags | s_flagMap[ndx].flag;
100				break;
101			}
102		}
103
104		if (ndx == DE_LENGTH_OF_ARRAY(s_flagMap))
105		{
106			tcu::print("ERROR: Unrecognized GL context flag '%s'\n", flagIter->c_str());
107			tcu::print("Supported GL context flags:\n");
108
109			for (ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_flagMap); ndx++)
110				tcu::print("  %s\n", s_flagMap[ndx].name);
111
112			throw tcu::NotSupportedError((std::string("Unknown GL context flag '") + *flagIter + "'").c_str(), DE_NULL, __FILE__, __LINE__);
113		}
114	}
115
116	return flags;
117}
118
119RenderContext* createDefaultRenderContext (tcu::Platform& platform, const tcu::CommandLine& cmdLine, ApiType apiType)
120{
121	const ContextFactoryRegistry&	registry		= platform.getGLPlatform().getContextFactoryRegistry();
122	RenderConfig					config;
123	const char*						factoryName		= cmdLine.getGLContextType();
124	const ContextFactory*			factory			= DE_NULL;
125	ContextFlags					ctxFlags		= ContextFlags(0);
126
127	if (registry.empty())
128		throw tcu::NotSupportedError("OpenGL is not supported", DE_NULL, __FILE__, __LINE__);
129
130	if (cmdLine.getGLContextFlags())
131		ctxFlags = parseContextFlags(cmdLine.getGLContextFlags());
132
133	config.type = glu::ContextType(apiType, ctxFlags);
134	parseRenderConfig(&config, cmdLine);
135
136	if (factoryName)
137	{
138		factory = registry.getFactoryByName(factoryName);
139
140		if (!factory)
141		{
142			tcu::print("ERROR: Unknown or unsupported GL context type '%s'\n", factoryName);
143			tcu::print("Supported GL context types:\n");
144
145			for (int factoryNdx = 0; factoryNdx < (int)registry.getFactoryCount(); factoryNdx++)
146			{
147				const ContextFactory* curFactory = registry.getFactoryByIndex(factoryNdx);
148				tcu::print("  %s: %s\n", curFactory->getName(), curFactory->getDescription());
149			}
150
151			throw tcu::NotSupportedError((std::string("Unknown GL context type '") + factoryName + "'").c_str(), DE_NULL, __FILE__, __LINE__);
152		}
153	}
154	else
155		factory = registry.getDefaultFactory();
156
157	try
158	{
159		if (cmdLine.getSurfaceType() == tcu::SURFACETYPE_FBO)
160			return new FboRenderContext(*factory, config, cmdLine);
161		else
162			return factory->createContext(config, cmdLine);
163
164	}
165	catch (const std::exception&)
166	{
167		// If ES31 context is not available, try using wrapper.
168		if (config.type.getAPI() == ApiType::es(3,1))
169		{
170			tcu::print("Warning: Unable to create native OpenGL ES 3.1 context, will use wrapper context.\n");
171			return new ES3PlusWrapperContext(*factory, config, cmdLine);
172		}
173		else
174			throw;
175	}
176}
177
178static std::vector<std::string> getExtensions (const glw::Functions& gl, ApiType apiType)
179{
180	using std::vector;
181	using std::string;
182
183	if (apiType.getProfile() == PROFILE_ES && apiType.getMajorVersion() == 2)
184	{
185		TCU_CHECK(gl.getString);
186
187		const char*	extStr	= (const char*)gl.getString(GL_EXTENSIONS);
188		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetString(GL_EXTENSIONS)");
189
190		if (extStr)
191			return de::splitString(extStr);
192		else
193			throw tcu::TestError("glGetString(GL_EXTENSIONS) returned null pointer", DE_NULL, __FILE__, __LINE__);
194	}
195	else
196	{
197		int				numExtensions	= 0;
198		vector<string>	extensions;
199
200		TCU_CHECK(gl.getIntegerv && gl.getStringi);
201
202		gl.getIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
203		GLU_EXPECT_NO_ERROR(gl.getError(), "glGetIntegerv(GL_NUM_EXTENSIONS)");
204
205		if (numExtensions > 0)
206		{
207			extensions.resize(numExtensions);
208
209			for (int ndx = 0; ndx < numExtensions; ndx++)
210			{
211				const char* const ext = (const char*)gl.getStringi(GL_EXTENSIONS, ndx);
212				GLU_EXPECT_NO_ERROR(gl.getError(), "glGetStringi(GL_EXTENSIONS)");
213
214				if (ext)
215					extensions[ndx] = ext;
216				else
217					throw tcu::TestError("glGetStringi(GL_EXTENSIONS) returned null pointer", DE_NULL, __FILE__, __LINE__);
218			}
219
220		}
221
222		return extensions;
223	}
224}
225
226void initCoreFunctions (glw::Functions* dst, const glw::FunctionLoader* loader, ApiType apiType)
227{
228	static const struct
229	{
230		ApiType		apiType;
231		void		(*initFunc)		(glw::Functions* gl, const glw::FunctionLoader* loader);
232	} s_initFuncs[] =
233	{
234		{ ApiType::es(2,0),		glw::initES20		},
235		{ ApiType::es(3,0),		glw::initES30		},
236		{ ApiType::es(3,1),		glw::initES31		},
237		{ ApiType::core(3,0),	glw::initGL30Core	},
238		{ ApiType::core(3,1),	glw::initGL31Core	},
239		{ ApiType::core(3,2),	glw::initGL32Core	},
240		{ ApiType::core(3,3),	glw::initGL33Core	},
241		{ ApiType::core(4,0),	glw::initGL40Core	},
242		{ ApiType::core(4,1),	glw::initGL41Core	},
243		{ ApiType::core(4,2),	glw::initGL42Core	},
244		{ ApiType::core(4,3),	glw::initGL43Core	},
245		{ ApiType::core(4,4),	glw::initGL44Core	},
246	};
247
248	for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(s_initFuncs); ndx++)
249	{
250		if (s_initFuncs[ndx].apiType == apiType)
251		{
252			s_initFuncs[ndx].initFunc(dst, loader);
253			return;
254		}
255	}
256
257	throw tcu::InternalError(std::string("Don't know how to load functions for ") + de::toString(apiType));
258}
259
260void initExtensionFunctions (glw::Functions* dst, const glw::FunctionLoader* loader, ApiType apiType)
261{
262	std::vector<std::string> extensions = getExtensions(*dst, apiType);
263
264	if (!extensions.empty())
265	{
266		std::vector<const char*> extStr(extensions.size());
267
268		for (size_t ndx = 0; ndx < extensions.size(); ndx++)
269			extStr[ndx] = extensions[ndx].c_str();
270
271		initExtensionFunctions(dst, loader, apiType, (int)extStr.size(), &extStr[0]);
272	}
273}
274
275void initExtensionFunctions (glw::Functions* dst, const glw::FunctionLoader* loader, ApiType apiType, int numExtensions, const char* const* extensions)
276{
277	if (apiType.getProfile() == PROFILE_ES)
278		glw::initExtensionsES(dst, loader, numExtensions, extensions);
279	else
280		glw::initExtensionsGL(dst, loader, numExtensions, extensions);
281}
282
283void initFunctions (glw::Functions* dst, const glw::FunctionLoader* loader, ApiType apiType)
284{
285	initCoreFunctions(dst, loader, apiType);
286	initExtensionFunctions(dst, loader, apiType);
287}
288
289} // glu
290