1/*-------------------------------------------------------------------------
2 * drawElements Quality Program Tester Core
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 EGL utilities
22 *//*--------------------------------------------------------------------*/
23
24#include "egluUtil.hpp"
25#include "egluDefs.hpp"
26#include "egluNativeDisplay.hpp"
27#include "egluConfigFilter.hpp"
28#include "eglwLibrary.hpp"
29#include "eglwEnums.hpp"
30#include "tcuCommandLine.hpp"
31#include "deSTLUtil.hpp"
32#include "deStringUtil.hpp"
33#include "glwEnums.hpp"
34
35#include <algorithm>
36#include <sstream>
37
38using std::string;
39using std::vector;
40
41namespace eglu
42{
43
44using namespace eglw;
45
46vector<EGLint> attribMapToList (const AttribMap& attribs)
47{
48	vector<EGLint> attribList;
49
50	for (AttribMap::const_iterator it = attribs.begin(); it != attribs.end(); ++it)
51	{
52		attribList.push_back(it->first);
53		attribList.push_back(it->second);
54	}
55
56	attribList.push_back(EGL_NONE);
57
58	return attribList;
59}
60
61Version getVersion (const Library& egl, EGLDisplay display)
62{
63	EGLint major, minor;
64
65	// eglInitialize on already initialized displays just returns the version.
66	EGLU_CHECK_CALL(egl, initialize(display, &major, &minor));
67
68	return Version(major, minor);
69}
70
71vector<string> getExtensions (const Library& egl, EGLDisplay display)
72{
73	const char*	const extensionStr = egl.queryString(display, EGL_EXTENSIONS);
74
75	EGLU_CHECK_MSG(egl, "Querying extensions failed");
76
77	return de::splitString(extensionStr, ' ');
78}
79
80bool hasExtension (const Library& egl, EGLDisplay display, const string& str)
81{
82	const vector<string> extensions = getExtensions(egl, display);
83	return de::contains(extensions.begin(), extensions.end(), str);
84}
85
86vector<string> getClientExtensions (const Library& egl)
87{
88	return getExtensions(egl, EGL_NO_DISPLAY);
89}
90
91vector<string> getDisplayExtensions (const Library& egl, EGLDisplay display)
92{
93	DE_ASSERT(display != EGL_NO_DISPLAY);
94
95	return getExtensions(egl, display);
96}
97
98vector<EGLConfig> getConfigs (const Library& egl, EGLDisplay display)
99{
100	vector<EGLConfig>	configs;
101	EGLint				configCount	= 0;
102	EGLU_CHECK_CALL(egl, getConfigs(display, DE_NULL, 0, &configCount));
103
104	if (configCount > 0)
105	{
106		configs.resize(configCount);
107		EGLU_CHECK_CALL(egl, getConfigs(display, &(configs[0]), (EGLint)configs.size(), &configCount));
108	}
109
110	return configs;
111}
112
113vector<EGLConfig> chooseConfigs (const Library& egl, EGLDisplay display, const EGLint* attribList)
114{
115	EGLint	numConfigs	= 0;
116
117	EGLU_CHECK_CALL(egl, chooseConfig(display, attribList, DE_NULL, 0, &numConfigs));
118
119	{
120		vector<EGLConfig> configs(numConfigs);
121
122		if (numConfigs > 0)
123			EGLU_CHECK_CALL(egl, chooseConfig(display, attribList, &configs.front(), numConfigs, &numConfigs));
124
125		return configs;
126	}
127}
128
129vector<EGLConfig> chooseConfigs (const Library& egl, EGLDisplay display, const FilterList& filters)
130{
131	const vector<EGLConfig>	allConfigs		(getConfigs(egl, display));
132	vector<EGLConfig>		matchingConfigs;
133
134	for (vector<EGLConfig>::const_iterator cfg = allConfigs.begin(); cfg != allConfigs.end(); ++cfg)
135	{
136		if (filters.match(egl, display, *cfg))
137			matchingConfigs.push_back(*cfg);
138	}
139
140	return matchingConfigs;
141}
142
143EGLConfig chooseSingleConfig (const Library& egl, EGLDisplay display, const FilterList& filters)
144{
145	const vector<EGLConfig>	allConfigs	(getConfigs(egl, display));
146
147	for (vector<EGLConfig>::const_iterator cfg = allConfigs.begin(); cfg != allConfigs.end(); ++cfg)
148	{
149		if (filters.match(egl, display, *cfg))
150			return *cfg;
151	}
152
153	TCU_THROW(NotSupportedError, "No matching EGL config found");
154}
155
156EGLConfig chooseSingleConfig (const Library& egl, EGLDisplay display, const EGLint* attribList)
157{
158	const vector<EGLConfig> configs (chooseConfigs(egl, display, attribList));
159	if (configs.empty())
160		TCU_THROW(NotSupportedError, "No matching EGL config found");
161
162	return configs.front();
163}
164
165vector<EGLConfig> chooseConfigs (const Library& egl, EGLDisplay display, const AttribMap& attribs)
166{
167	const vector<EGLint>	attribList	= attribMapToList(attribs);
168	return chooseConfigs(egl, display, &attribList.front());
169}
170
171EGLConfig chooseSingleConfig (const Library& egl, EGLDisplay display, const AttribMap& attribs)
172{
173	const vector<EGLint>	attribList	= attribMapToList(attribs);
174	return chooseSingleConfig(egl, display, &attribList.front());
175}
176
177EGLConfig chooseConfigByID (const Library& egl, EGLDisplay display, EGLint id)
178{
179	AttribMap attribs;
180
181	attribs[EGL_CONFIG_ID]			= id;
182	attribs[EGL_TRANSPARENT_TYPE]	= EGL_DONT_CARE;
183	attribs[EGL_COLOR_BUFFER_TYPE]	= EGL_DONT_CARE;
184	attribs[EGL_RENDERABLE_TYPE]	= EGL_DONT_CARE;
185	attribs[EGL_SURFACE_TYPE]		= EGL_DONT_CARE;
186
187	return chooseSingleConfig(egl, display, attribs);
188}
189
190EGLint getConfigAttribInt (const Library& egl, EGLDisplay display, EGLConfig config, EGLint attrib)
191{
192	EGLint value = 0;
193	EGLU_CHECK_CALL(egl, getConfigAttrib(display, config, attrib, &value));
194	return value;
195}
196
197EGLint getConfigID (const Library& egl, EGLDisplay display, EGLConfig config)
198{
199	return getConfigAttribInt(egl, display, config, EGL_CONFIG_ID);
200}
201
202EGLint querySurfaceInt (const Library& egl, EGLDisplay display, EGLSurface surface, EGLint attrib)
203{
204	EGLint value = 0;
205	EGLU_CHECK_CALL(egl, querySurface(display, surface, attrib, &value));
206	return value;
207}
208
209tcu::IVec2 getSurfaceSize (const Library& egl, EGLDisplay display, EGLSurface surface)
210{
211	const EGLint width	= querySurfaceInt(egl, display, surface, EGL_WIDTH);
212	const EGLint height	= querySurfaceInt(egl, display, surface, EGL_HEIGHT);
213	return tcu::IVec2(width, height);
214}
215
216tcu::IVec2 getSurfaceResolution (const Library& egl, EGLDisplay display, EGLSurface surface)
217{
218	const EGLint hRes	= querySurfaceInt(egl, display, surface, EGL_HORIZONTAL_RESOLUTION);
219	const EGLint vRes	= querySurfaceInt(egl, display, surface, EGL_VERTICAL_RESOLUTION);
220
221	if (hRes == EGL_UNKNOWN || vRes == EGL_UNKNOWN)
222		TCU_THROW(NotSupportedError, "Surface doesn't support pixel density queries");
223	return tcu::IVec2(hRes, vRes);
224}
225
226//! Get EGLdisplay using eglGetDisplay() or eglGetPlatformDisplayEXT()
227EGLDisplay getDisplay (NativeDisplay& nativeDisplay)
228{
229	const Library&	egl								= nativeDisplay.getLibrary();
230	const bool		supportsLegacyGetDisplay		= (nativeDisplay.getCapabilities() & NativeDisplay::CAPABILITY_GET_DISPLAY_LEGACY) != 0;
231	const bool		supportsPlatformGetDisplay		= (nativeDisplay.getCapabilities() & NativeDisplay::CAPABILITY_GET_DISPLAY_PLATFORM) != 0;
232	bool			usePlatformExt					= false;
233	EGLDisplay		display							= EGL_NO_DISPLAY;
234
235	TCU_CHECK_INTERNAL(supportsLegacyGetDisplay || supportsPlatformGetDisplay);
236
237	if (supportsPlatformGetDisplay)
238	{
239		const vector<string> platformExts = getClientExtensions(egl);
240		usePlatformExt = de::contains(platformExts.begin(), platformExts.end(), string("EGL_EXT_platform_base")) &&
241						 de::contains(platformExts.begin(), platformExts.end(), string(nativeDisplay.getPlatformExtensionName()));
242	}
243
244	if (usePlatformExt)
245	{
246		const vector<EGLint>	legacyAttribs	= toLegacyAttribList(nativeDisplay.getPlatformAttributes());
247
248		display = egl.getPlatformDisplayEXT(nativeDisplay.getPlatformType(), nativeDisplay.getPlatformNative(), &legacyAttribs[0]);
249		EGLU_CHECK_MSG(egl, "eglGetPlatformDisplayEXT()");
250		TCU_CHECK(display != EGL_NO_DISPLAY);
251	}
252	else if (supportsLegacyGetDisplay)
253	{
254		display = egl.getDisplay(nativeDisplay.getLegacyNative());
255		EGLU_CHECK_MSG(egl, "eglGetDisplay()");
256		TCU_CHECK(display != EGL_NO_DISPLAY);
257	}
258	else
259		throw tcu::InternalError("No supported way to get EGL display", DE_NULL, __FILE__, __LINE__);
260
261	DE_ASSERT(display != EGL_NO_DISPLAY);
262	return display;
263}
264
265EGLDisplay getAndInitDisplay (NativeDisplay& nativeDisplay, Version* version)
266{
267	const Library&	egl		= nativeDisplay.getLibrary();
268	EGLDisplay		display	= getDisplay(nativeDisplay);
269	int				major, minor;
270
271	EGLU_CHECK_CALL(egl, initialize(display, &major, &minor));
272
273	if (version)
274		*version = Version(major, minor);
275
276	return display;
277}
278
279//! Create EGL window surface using eglCreateWindowSurface() or eglCreatePlatformWindowSurfaceEXT()
280EGLSurface createWindowSurface (NativeDisplay& nativeDisplay, NativeWindow& window, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList)
281{
282	const Library&	egl							= nativeDisplay.getLibrary();
283	const bool		supportsLegacyCreate		= (window.getCapabilities() & NativeWindow::CAPABILITY_CREATE_SURFACE_LEGACY) != 0;
284	const bool		supportsPlatformCreate		= (window.getCapabilities() & NativeWindow::CAPABILITY_CREATE_SURFACE_PLATFORM) != 0;
285	bool			usePlatformExt				= false;
286	EGLSurface		surface						= EGL_NO_SURFACE;
287
288	TCU_CHECK_INTERNAL(supportsLegacyCreate || supportsPlatformCreate);
289
290	if (supportsPlatformCreate)
291	{
292		const vector<string> platformExts = getClientExtensions(egl);
293		usePlatformExt = de::contains(platformExts.begin(), platformExts.end(), string("EGL_EXT_platform_base")) &&
294						 de::contains(platformExts.begin(), platformExts.end(), string(nativeDisplay.getPlatformExtensionName()));
295	}
296
297	// \todo [2014-03-13 pyry] EGL 1.5 core support
298	if (usePlatformExt)
299	{
300		const vector<EGLint>	legacyAttribs	= toLegacyAttribList(attribList);
301
302		surface = egl.createPlatformWindowSurfaceEXT(display, config, window.getPlatformNative(), &legacyAttribs[0]);
303		EGLU_CHECK_MSG(egl, "eglCreatePlatformWindowSurfaceEXT()");
304		TCU_CHECK(surface != EGL_NO_SURFACE);
305	}
306	else if (supportsLegacyCreate)
307	{
308		const vector<EGLint> legacyAttribs = toLegacyAttribList(attribList);
309		surface = egl.createWindowSurface(display, config, window.getLegacyNative(), &legacyAttribs[0]);
310		EGLU_CHECK_MSG(egl, "eglCreateWindowSurface()");
311		TCU_CHECK(surface != EGL_NO_SURFACE);
312	}
313	else
314		throw tcu::InternalError("No supported way to create EGL window surface", DE_NULL, __FILE__, __LINE__);
315
316	DE_ASSERT(surface != EGL_NO_SURFACE);
317	return surface;
318}
319
320//! Create EGL pixmap surface using eglCreatePixmapSurface() or eglCreatePlatformPixmapSurfaceEXT()
321EGLSurface createPixmapSurface (NativeDisplay& nativeDisplay, NativePixmap& pixmap, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList)
322{
323	const Library&	egl							= nativeDisplay.getLibrary();
324	const bool		supportsLegacyCreate		= (pixmap.getCapabilities() & NativePixmap::CAPABILITY_CREATE_SURFACE_LEGACY) != 0;
325	const bool		supportsPlatformCreate		= (pixmap.getCapabilities() & NativePixmap::CAPABILITY_CREATE_SURFACE_PLATFORM) != 0;
326	bool			usePlatformExt				= false;
327	EGLSurface		surface						= EGL_NO_SURFACE;
328
329	TCU_CHECK_INTERNAL(supportsLegacyCreate || supportsPlatformCreate);
330
331	if (supportsPlatformCreate)
332	{
333		const vector<string> platformExts = getClientExtensions(egl);
334		usePlatformExt = de::contains(platformExts.begin(), platformExts.end(), string("EGL_EXT_platform_base")) &&
335						 de::contains(platformExts.begin(), platformExts.end(), string(nativeDisplay.getPlatformExtensionName()));
336	}
337
338	if (usePlatformExt)
339	{
340		const vector<EGLint>	legacyAttribs	= toLegacyAttribList(attribList);
341
342		surface = egl.createPlatformPixmapSurfaceEXT(display, config, pixmap.getPlatformNative(), &legacyAttribs[0]);
343		EGLU_CHECK_MSG(egl, "eglCreatePlatformPixmapSurfaceEXT()");
344		TCU_CHECK(surface != EGL_NO_SURFACE);
345	}
346	else if (supportsLegacyCreate)
347	{
348		const vector<EGLint> legacyAttribs = toLegacyAttribList(attribList);
349		surface = egl.createPixmapSurface(display, config, pixmap.getLegacyNative(), &legacyAttribs[0]);
350		EGLU_CHECK_MSG(egl, "eglCreatePixmapSurface()");
351		TCU_CHECK(surface != EGL_NO_SURFACE);
352	}
353	else
354		throw tcu::InternalError("No supported way to create EGL pixmap surface", DE_NULL, __FILE__, __LINE__);
355
356	DE_ASSERT(surface != EGL_NO_SURFACE);
357	return surface;
358}
359
360static WindowParams::Visibility getWindowVisibility (tcu::WindowVisibility visibility)
361{
362	switch (visibility)
363	{
364		case tcu::WINDOWVISIBILITY_WINDOWED:	return WindowParams::VISIBILITY_VISIBLE;
365		case tcu::WINDOWVISIBILITY_FULLSCREEN:	return WindowParams::VISIBILITY_FULLSCREEN;
366		case tcu::WINDOWVISIBILITY_HIDDEN:		return WindowParams::VISIBILITY_HIDDEN;
367
368		default:
369			DE_ASSERT(false);
370			return WindowParams::VISIBILITY_DONT_CARE;
371	}
372}
373
374WindowParams::Visibility parseWindowVisibility (const tcu::CommandLine& commandLine)
375{
376	return getWindowVisibility(commandLine.getVisibility());
377}
378
379EGLenum parseClientAPI (const std::string& api)
380{
381	if (api == "OpenGL")
382		return EGL_OPENGL_API;
383	else if (api == "OpenGL_ES")
384		return EGL_OPENGL_ES_API;
385	else if (api == "OpenVG")
386		return EGL_OPENVG_API;
387	else
388		throw tcu::InternalError("Unknown EGL client API '" + api + "'");
389}
390
391vector<EGLenum> parseClientAPIs (const std::string& apiList)
392{
393	const vector<string>	apiStrs	= de::splitString(apiList, ' ');
394	vector<EGLenum>			apis;
395
396	for (vector<string>::const_iterator api = apiStrs.begin(); api != apiStrs.end(); ++api)
397		apis.push_back(parseClientAPI(*api));
398
399	return apis;
400}
401
402vector<EGLenum> getClientAPIs (const eglw::Library& egl, eglw::EGLDisplay display)
403{
404	return parseClientAPIs(egl.queryString(display, EGL_CLIENT_APIS));
405}
406
407EGLint getRenderableAPIsMask (const eglw::Library& egl, eglw::EGLDisplay display)
408{
409	const vector<EGLConfig>	configs	= getConfigs(egl, display);
410	EGLint					allAPIs	= 0;
411
412	for (vector<EGLConfig>::const_iterator i = configs.begin(); i != configs.end(); ++i)
413		allAPIs |= getConfigAttribInt(egl, display, *i, EGL_RENDERABLE_TYPE);
414
415	return allAPIs;
416}
417
418vector<EGLint> toLegacyAttribList (const EGLAttrib* attribs)
419{
420	const deUint64	attribMask		= 0xffffffffull;	//!< Max bits that can be used
421	vector<EGLint>	legacyAttribs;
422
423	if (attribs)
424	{
425		for (const EGLAttrib* attrib = attribs; *attrib != EGL_NONE; attrib += 2)
426		{
427			if ((attrib[0] & ~attribMask) || (attrib[1] & ~attribMask))
428				throw tcu::InternalError("Failed to translate EGLAttrib to EGLint", DE_NULL, __FILE__, __LINE__);
429
430			legacyAttribs.push_back((EGLint)attrib[0]);
431			legacyAttribs.push_back((EGLint)attrib[1]);
432		}
433	}
434
435	legacyAttribs.push_back(EGL_NONE);
436
437	return legacyAttribs;
438}
439
440template<typename Factory>
441static const Factory& selectFactory (const tcu::FactoryRegistry<Factory>& registry, const char* objectTypeName, const char* cmdLineArg)
442{
443	if (cmdLineArg)
444	{
445		const Factory* factory = registry.getFactoryByName(cmdLineArg);
446
447		if (factory)
448			return *factory;
449		else
450		{
451			tcu::print("ERROR: Unknown or unsupported EGL %s type '%s'", objectTypeName, cmdLineArg);
452			tcu::print("Available EGL %s types:\n", objectTypeName);
453			for (size_t ndx = 0; ndx < registry.getFactoryCount(); ndx++)
454				tcu::print("  %s: %s\n", registry.getFactoryByIndex(ndx)->getName(), registry.getFactoryByIndex(ndx)->getDescription());
455
456			TCU_THROW(NotSupportedError, (string("Unsupported or unknown EGL ") + objectTypeName + " type '" + cmdLineArg + "'").c_str());
457		}
458	}
459	else if (!registry.empty())
460		return *registry.getDefaultFactory();
461	else
462		TCU_THROW(NotSupportedError, (string("No factory supporting EGL '") + objectTypeName + "' type").c_str());
463}
464
465const NativeDisplayFactory& selectNativeDisplayFactory (const NativeDisplayFactoryRegistry& registry, const tcu::CommandLine& cmdLine)
466{
467	return selectFactory(registry, "display", cmdLine.getEGLDisplayType());
468}
469
470const NativeWindowFactory& selectNativeWindowFactory (const NativeDisplayFactory& factory, const tcu::CommandLine& cmdLine)
471{
472	return selectFactory(factory.getNativeWindowRegistry(), "window", cmdLine.getEGLWindowType());
473}
474
475const NativePixmapFactory& selectNativePixmapFactory (const NativeDisplayFactory& factory, const tcu::CommandLine& cmdLine)
476{
477	return selectFactory(factory.getNativePixmapRegistry(), "pixmap", cmdLine.getEGLPixmapType());
478}
479
480} // eglu
481