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 GL context factory using EGL.
22 *//*--------------------------------------------------------------------*/
23
24#include "egluGLContextFactory.hpp"
25
26#include "tcuRenderTarget.hpp"
27#include "tcuPlatform.hpp"
28#include "tcuCommandLine.hpp"
29
30#include "gluDefs.hpp"
31
32#include "egluDefs.hpp"
33#include "egluUtil.hpp"
34#include "egluGLUtil.hpp"
35#include "egluNativeWindow.hpp"
36#include "egluNativePixmap.hpp"
37#include "egluStrUtil.hpp"
38
39#include "eglwLibrary.hpp"
40#include "eglwEnums.hpp"
41
42#include "glwInitFunctions.hpp"
43#include "glwInitES20Direct.hpp"
44#include "glwInitES30Direct.hpp"
45
46#include "deDynamicLibrary.hpp"
47#include "deSTLUtil.hpp"
48
49#include <string>
50#include <string>
51#include <sstream>
52
53using std::string;
54using std::vector;
55
56// \todo [2014-03-12 pyry] Use command line arguments for libraries?
57
58// Default library names
59#if !defined(DEQP_GLES2_LIBRARY_PATH)
60#	if (DE_OS == DE_OS_WIN32)
61#		define DEQP_GLES2_LIBRARY_PATH "libGLESv2.dll"
62#	else
63#		define DEQP_GLES2_LIBRARY_PATH "libGLESv2.so"
64#	endif
65#endif
66
67#if !defined(DEQP_GLES3_LIBRARY_PATH)
68#	define DEQP_GLES3_LIBRARY_PATH DEQP_GLES2_LIBRARY_PATH
69#endif
70
71#if !defined(DEQP_OPENGL_LIBRARY_PATH)
72#	if (DE_OS == DE_OS_WIN32)
73#		define DEQP_OPENGL_LIBRARY_PATH "opengl32.dll"
74#	else
75#		define DEQP_OPENGL_LIBRARY_PATH "libGL.so"
76#	endif
77#endif
78
79namespace eglu
80{
81
82using namespace eglw;
83
84namespace
85{
86
87enum
88{
89	DEFAULT_OFFSCREEN_WIDTH		= 512,
90	DEFAULT_OFFSCREEN_HEIGHT	= 512
91};
92
93class GetProcFuncLoader : public glw::FunctionLoader
94{
95public:
96	GetProcFuncLoader (const Library& egl)
97		: m_egl(egl)
98	{
99	}
100
101	glw::GenericFuncType get (const char* name) const
102	{
103		return (glw::GenericFuncType)m_egl.getProcAddress(name);
104	}
105
106protected:
107	const Library& m_egl;
108};
109
110class DynamicFuncLoader : public glw::FunctionLoader
111{
112public:
113	DynamicFuncLoader	(de::DynamicLibrary* library)
114		: m_library(library)
115	{
116	}
117
118	glw::GenericFuncType get (const char* name) const
119	{
120		return (glw::GenericFuncType)m_library->getFunction(name);
121	}
122
123private:
124	de::DynamicLibrary*	m_library;
125};
126
127class RenderContext : public GLRenderContext
128{
129public:
130										RenderContext			(const NativeDisplayFactory* displayFactory, const NativeWindowFactory* windowFactory, const NativePixmapFactory* pixmapFactory, const glu::RenderConfig& config);
131	virtual								~RenderContext			(void);
132
133	virtual glu::ContextType			getType					(void) const { return m_renderConfig.type;	}
134	virtual const glw::Functions&		getFunctions			(void) const { return m_glFunctions;		}
135	virtual const tcu::RenderTarget&	getRenderTarget			(void) const { return m_glRenderTarget;		}
136	virtual void						postIterate				(void);
137
138	virtual EGLDisplay					getEGLDisplay			(void) const { return m_eglDisplay;			}
139	virtual EGLContext					getEGLContext			(void) const { return m_eglContext;			}
140
141private:
142	void								create					(const NativeDisplayFactory* displayFactory, const NativeWindowFactory* windowFactory, const NativePixmapFactory* pixmapFactory, const glu::RenderConfig& config);
143	void								destroy					(void);
144
145	const glu::RenderConfig				m_renderConfig;
146	const NativeWindowFactory* const	m_nativeWindowFactory;	// Stored in case window must be re-created
147
148	NativeDisplay*						m_display;
149	NativeWindow*						m_window;
150	NativePixmap*						m_pixmap;
151
152	EGLDisplay							m_eglDisplay;
153	EGLConfig							m_eglConfig;
154	EGLSurface							m_eglSurface;
155	EGLContext							m_eglContext;
156
157	tcu::RenderTarget					m_glRenderTarget;
158	de::DynamicLibrary*					m_dynamicGLLibrary;
159	glw::Functions						m_glFunctions;
160};
161
162RenderContext::RenderContext (const NativeDisplayFactory* displayFactory, const NativeWindowFactory* windowFactory, const NativePixmapFactory* pixmapFactory, const glu::RenderConfig& config)
163	: m_renderConfig		(config)
164	, m_nativeWindowFactory	(windowFactory)
165	, m_display				(DE_NULL)
166	, m_window				(DE_NULL)
167	, m_pixmap				(DE_NULL)
168
169	, m_eglDisplay			(EGL_NO_DISPLAY)
170	, m_eglSurface			(EGL_NO_SURFACE)
171	, m_eglContext			(EGL_NO_CONTEXT)
172
173	, m_dynamicGLLibrary	(DE_NULL)
174{
175	DE_ASSERT(displayFactory);
176
177	try
178	{
179		create(displayFactory, windowFactory, pixmapFactory, config);
180	}
181	catch (...)
182	{
183		destroy();
184		throw;
185	}
186}
187
188RenderContext::~RenderContext(void)
189{
190	try
191	{
192		destroy();
193	}
194	catch (...)
195	{
196		// destroy() calls EGL functions that are checked and may throw exceptions
197	}
198
199	delete m_window;
200	delete m_pixmap;
201	delete m_display;
202	delete m_dynamicGLLibrary;
203}
204
205static WindowParams::Visibility getNativeWindowVisibility (glu::RenderConfig::Visibility visibility)
206{
207	using glu::RenderConfig;
208
209	switch (visibility)
210	{
211		case RenderConfig::VISIBILITY_HIDDEN:		return WindowParams::VISIBILITY_HIDDEN;
212		case RenderConfig::VISIBILITY_VISIBLE:		return WindowParams::VISIBILITY_VISIBLE;
213		case RenderConfig::VISIBILITY_FULLSCREEN:	return WindowParams::VISIBILITY_FULLSCREEN;
214		default:
215			DE_ASSERT(visibility == (RenderConfig::Visibility)RenderConfig::DONT_CARE);
216			return WindowParams::VISIBILITY_DONT_CARE;
217	}
218}
219
220typedef std::pair<NativeWindow*, EGLSurface> WindowSurfacePair;
221typedef std::pair<NativePixmap*, EGLSurface> PixmapSurfacePair;
222
223WindowSurfacePair createWindow (NativeDisplay* nativeDisplay, const NativeWindowFactory* windowFactory, EGLDisplay eglDisplay, EGLConfig eglConfig, const glu::RenderConfig& config)
224{
225	const int						width			= (config.width		== glu::RenderConfig::DONT_CARE ? WindowParams::SIZE_DONT_CARE	: config.width);
226	const int						height			= (config.height	== glu::RenderConfig::DONT_CARE ? WindowParams::SIZE_DONT_CARE	: config.height);
227	const WindowParams::Visibility	visibility		= getNativeWindowVisibility(config.windowVisibility);
228	NativeWindow*					nativeWindow	= DE_NULL;
229	EGLSurface						surface			= EGL_NO_SURFACE;
230	const EGLAttrib					attribList[]	= { EGL_NONE };
231
232	nativeWindow = windowFactory->createWindow(nativeDisplay, eglDisplay, eglConfig, &attribList[0], WindowParams(width, height, visibility));
233
234	try
235	{
236		surface = eglu::createWindowSurface(*nativeDisplay, *nativeWindow, eglDisplay, eglConfig, attribList);
237	}
238	catch (...)
239	{
240		delete nativeWindow;
241		throw;
242	}
243
244	return WindowSurfacePair(nativeWindow, surface);
245}
246
247PixmapSurfacePair createPixmap (NativeDisplay* nativeDisplay, const NativePixmapFactory* pixmapFactory, EGLDisplay eglDisplay, EGLConfig eglConfig, const glu::RenderConfig& config)
248{
249	const int			width			= (config.width		== glu::RenderConfig::DONT_CARE ? DEFAULT_OFFSCREEN_WIDTH	: config.width);
250	const int			height			= (config.height	== glu::RenderConfig::DONT_CARE ? DEFAULT_OFFSCREEN_HEIGHT	: config.height);
251	NativePixmap*		nativePixmap	= DE_NULL;
252	EGLSurface			surface			= EGL_NO_SURFACE;
253	const EGLAttrib		attribList[]	= { EGL_NONE };
254
255	nativePixmap = pixmapFactory->createPixmap(nativeDisplay, eglDisplay, eglConfig, &attribList[0], width, height);
256
257	try
258	{
259		surface = eglu::createPixmapSurface(*nativeDisplay, *nativePixmap, eglDisplay, eglConfig, attribList);
260	}
261	catch (...)
262	{
263		delete nativePixmap;
264		throw;
265	}
266
267	return PixmapSurfacePair(nativePixmap, surface);
268}
269
270EGLSurface createPBuffer (const Library& egl, EGLDisplay display, EGLConfig eglConfig, const glu::RenderConfig& config)
271{
272	const int		width			= (config.width		== glu::RenderConfig::DONT_CARE ? DEFAULT_OFFSCREEN_WIDTH	: config.width);
273	const int		height			= (config.height	== glu::RenderConfig::DONT_CARE ? DEFAULT_OFFSCREEN_HEIGHT	: config.height);
274	EGLSurface		surface;
275	const EGLint	attribList[]	=
276	{
277		EGL_WIDTH,	width,
278		EGL_HEIGHT,	height,
279		EGL_NONE
280	};
281
282	surface = egl.createPbufferSurface(display, eglConfig, &(attribList[0]));
283	EGLU_CHECK_MSG(egl, "eglCreatePbufferSurface()");
284
285	return surface;
286}
287
288void RenderContext::create (const NativeDisplayFactory* displayFactory, const NativeWindowFactory* windowFactory, const NativePixmapFactory* pixmapFactory, const glu::RenderConfig& config)
289{
290	glu::RenderConfig::SurfaceType	surfaceType	= config.surfaceType;
291
292	DE_ASSERT(displayFactory);
293
294	m_display		= displayFactory->createDisplay();
295	m_eglDisplay	= eglu::getDisplay(*m_display);
296
297	const Library& egl = m_display->getLibrary();
298
299	{
300		EGLint major = 0;
301		EGLint minor = 0;
302		EGLU_CHECK_CALL(egl, initialize(m_eglDisplay, &major, &minor));
303	}
304
305	m_eglConfig	= chooseConfig(egl, m_eglDisplay, config);
306
307	if (surfaceType == glu::RenderConfig::SURFACETYPE_DONT_CARE)
308	{
309		// Choose based on what selected configuration supports
310		const EGLint supportedTypes = eglu::getConfigAttribInt(egl, m_eglDisplay, m_eglConfig, EGL_SURFACE_TYPE);
311
312		if ((supportedTypes & EGL_WINDOW_BIT) != 0)
313			surfaceType = glu::RenderConfig::SURFACETYPE_WINDOW;
314		else if ((supportedTypes & EGL_PBUFFER_BIT) != 0)
315			surfaceType = glu::RenderConfig::SURFACETYPE_OFFSCREEN_GENERIC;
316		else if ((supportedTypes & EGL_PIXMAP_BIT) != 0)
317			surfaceType = glu::RenderConfig::SURFACETYPE_OFFSCREEN_NATIVE;
318		else
319			throw tcu::NotSupportedError("Selected EGL config doesn't support any surface types", DE_NULL, __FILE__, __LINE__);
320	}
321
322	switch (surfaceType)
323	{
324		case glu::RenderConfig::SURFACETYPE_WINDOW:
325		{
326			if (windowFactory)
327			{
328				const WindowSurfacePair windowSurface = createWindow(m_display, windowFactory, m_eglDisplay, m_eglConfig, config);
329				m_window		= windowSurface.first;
330				m_eglSurface	= windowSurface.second;
331			}
332			else
333				throw tcu::NotSupportedError("EGL platform doesn't support windows", DE_NULL, __FILE__, __LINE__);
334			break;
335		}
336
337		case glu::RenderConfig::SURFACETYPE_OFFSCREEN_NATIVE:
338		{
339			if (pixmapFactory)
340			{
341				const PixmapSurfacePair pixmapSurface = createPixmap(m_display, pixmapFactory, m_eglDisplay, m_eglConfig, config);
342				m_pixmap		= pixmapSurface.first;
343				m_eglSurface	= pixmapSurface.second;
344			}
345			else
346				throw tcu::NotSupportedError("EGL platform doesn't support pixmaps", DE_NULL, __FILE__, __LINE__);
347			break;
348		}
349
350		case glu::RenderConfig::SURFACETYPE_OFFSCREEN_GENERIC:
351			m_eglSurface = createPBuffer(egl, m_eglDisplay, m_eglConfig, config);
352			break;
353
354		default:
355			throw tcu::InternalError("Invalid surface type");
356	}
357
358	m_eglContext = createGLContext(egl, m_eglDisplay, m_eglConfig, config.type);
359
360	EGLU_CHECK_CALL(egl, makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext));
361
362	// Init core functions
363
364	if (hasExtension(egl, m_eglDisplay, "EGL_KHR_get_all_proc_addresses"))
365	{
366		// Use eglGetProcAddress() for core functions
367		GetProcFuncLoader funcLoader(egl);
368		glu::initCoreFunctions(&m_glFunctions, &funcLoader, config.type.getAPI());
369	}
370#if !defined(DEQP_GLES2_RUNTIME_LOAD)
371	else if (config.type.getAPI() == glu::ApiType::es(2,0))
372	{
373		glw::initES20Direct(&m_glFunctions);
374	}
375#endif
376#if !defined(DEQP_GLES3_RUNTIME_LOAD)
377	else if (config.type.getAPI() == glu::ApiType::es(3,0))
378	{
379		glw::initES30Direct(&m_glFunctions);
380	}
381#endif
382	else
383	{
384		const char* libraryPath = DE_NULL;
385
386		if (glu::isContextTypeES(config.type))
387		{
388			if (config.type.getMinorVersion() <= 2)
389				libraryPath = DEQP_GLES2_LIBRARY_PATH;
390			else
391				libraryPath = DEQP_GLES3_LIBRARY_PATH;
392		}
393		else
394			libraryPath = DEQP_OPENGL_LIBRARY_PATH;
395
396		m_dynamicGLLibrary = new de::DynamicLibrary(libraryPath);
397
398		DynamicFuncLoader funcLoader(m_dynamicGLLibrary);
399		glu::initCoreFunctions(&m_glFunctions, &funcLoader, config.type.getAPI());
400	}
401
402	// Init extension functions
403	{
404		GetProcFuncLoader extLoader(egl);
405		glu::initExtensionFunctions(&m_glFunctions, &extLoader, config.type.getAPI());
406	}
407
408	{
409		EGLint				width, height, depthBits, stencilBits, numSamples;
410		tcu::PixelFormat	pixelFmt;
411
412		egl.querySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH,		&width);
413		egl.querySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT,	&height);
414
415		egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_RED_SIZE,		&pixelFmt.redBits);
416		egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_GREEN_SIZE,		&pixelFmt.greenBits);
417		egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_BLUE_SIZE,		&pixelFmt.blueBits);
418		egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_ALPHA_SIZE,		&pixelFmt.alphaBits);
419
420		egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_DEPTH_SIZE,		&depthBits);
421		egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_STENCIL_SIZE,	&stencilBits);
422		egl.getConfigAttrib(m_eglDisplay, m_eglConfig, EGL_SAMPLES,			&numSamples);
423
424		EGLU_CHECK_MSG(egl, "Failed to query config attributes");
425
426		m_glRenderTarget = tcu::RenderTarget(width, height, pixelFmt, depthBits, stencilBits, numSamples);
427	}
428}
429
430void RenderContext::destroy (void)
431{
432	const Library& egl = m_display->getLibrary();
433
434	if (m_eglDisplay != EGL_NO_DISPLAY)
435	{
436		EGLU_CHECK_CALL(egl, makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
437
438		if (m_eglSurface != EGL_NO_SURFACE)
439			EGLU_CHECK_CALL(egl, destroySurface(m_eglDisplay, m_eglSurface));
440
441		if (m_eglContext != EGL_NO_CONTEXT)
442			EGLU_CHECK_CALL(egl, destroyContext(m_eglDisplay, m_eglContext));
443
444		EGLU_CHECK_CALL(egl, terminate(m_eglDisplay));
445
446		m_eglDisplay	= EGL_NO_DISPLAY;
447		m_eglSurface	= EGL_NO_SURFACE;
448		m_eglContext	= EGL_NO_CONTEXT;
449	}
450
451	delete m_window;
452	delete m_pixmap;
453	delete m_display;
454	delete m_dynamicGLLibrary;
455
456	m_window			= DE_NULL;
457	m_pixmap			= DE_NULL;
458	m_display			= DE_NULL;
459	m_dynamicGLLibrary	= DE_NULL;
460}
461
462void RenderContext::postIterate (void)
463{
464	const Library& egl = m_display->getLibrary();
465
466	if (m_window)
467	{
468		EGLBoolean	swapOk		= egl.swapBuffers(m_eglDisplay, m_eglSurface);
469		EGLint		error		= egl.getError();
470		const bool	badWindow	= error == EGL_BAD_SURFACE || error == EGL_BAD_NATIVE_WINDOW;
471
472		if (!swapOk && !badWindow)
473			throw tcu::ResourceError(string("eglSwapBuffers() failed: ") + getErrorStr(error).toString());
474
475		try
476		{
477			m_window->processEvents();
478		}
479		catch (const WindowDestroyedError&)
480		{
481			tcu::print("Warning: Window destroyed, recreating...\n");
482
483			EGLU_CHECK_CALL(egl, makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
484			EGLU_CHECK_CALL(egl, destroySurface(m_eglDisplay, m_eglSurface));
485			m_eglSurface = EGL_NO_SURFACE;
486
487			delete m_window;
488			m_window = DE_NULL;
489
490			try
491			{
492				WindowSurfacePair windowSurface = createWindow(m_display, m_nativeWindowFactory, m_eglDisplay, m_eglConfig, m_renderConfig);
493				m_window		= windowSurface.first;
494				m_eglSurface	= windowSurface.second;
495
496				EGLU_CHECK_CALL(egl, makeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext));
497
498				swapOk	= EGL_TRUE;
499				error	= EGL_SUCCESS;
500			}
501			catch (const std::exception& e)
502			{
503				if (m_eglSurface)
504				{
505					egl.makeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
506					egl.destroySurface(m_eglDisplay, m_eglSurface);
507					m_eglSurface = EGL_NO_SURFACE;
508				}
509
510				delete m_window;
511				m_window = DE_NULL;
512
513				throw tcu::ResourceError(string("Failed to re-create window: ") + e.what());
514			}
515		}
516
517		if (!swapOk)
518		{
519			DE_ASSERT(badWindow);
520			throw tcu::ResourceError(string("eglSwapBuffers() failed: ") + getErrorStr(error).toString());
521		}
522
523		// Refresh dimensions
524		{
525			int	newWidth	= 0;
526			int	newHeight	= 0;
527
528			egl.querySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH,		&newWidth);
529			egl.querySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT,	&newHeight);
530			EGLU_CHECK_MSG(egl, "Failed to query window size");
531
532			if (newWidth	!= m_glRenderTarget.getWidth() ||
533				newHeight	!= m_glRenderTarget.getHeight())
534			{
535				tcu::print("Warning: Window size changed (%dx%d -> %dx%d), test results might be invalid!\n",
536						   m_glRenderTarget.getWidth(), m_glRenderTarget.getHeight(), newWidth, newHeight);
537
538				m_glRenderTarget = tcu::RenderTarget(newWidth, newHeight,
539													 m_glRenderTarget.getPixelFormat(),
540													 m_glRenderTarget.getDepthBits(),
541													 m_glRenderTarget.getStencilBits(),
542													 m_glRenderTarget.getNumSamples());
543			}
544		}
545	}
546	else
547		m_glFunctions.flush();
548}
549
550} // anonymous
551
552GLContextFactory::GLContextFactory (const NativeDisplayFactoryRegistry& displayFactoryRegistry)
553	: glu::ContextFactory		("egl", "EGL OpenGL Context")
554	, m_displayFactoryRegistry	(displayFactoryRegistry)
555{
556}
557
558glu::RenderContext* GLContextFactory::createContext (const glu::RenderConfig& config, const tcu::CommandLine& cmdLine) const
559{
560	const NativeDisplayFactory& displayFactory = selectNativeDisplayFactory(m_displayFactoryRegistry, cmdLine);
561
562	const NativeWindowFactory*	windowFactory;
563	const NativePixmapFactory*	pixmapFactory;
564
565	try
566	{
567		windowFactory = &selectNativeWindowFactory(displayFactory, cmdLine);
568	}
569	catch (const tcu::NotSupportedError&)
570	{
571		windowFactory = DE_NULL;
572	}
573
574	try
575	{
576		pixmapFactory = &selectNativePixmapFactory(displayFactory, cmdLine);
577	}
578	catch (const tcu::NotSupportedError&)
579	{
580		pixmapFactory = DE_NULL;
581	}
582
583	return new RenderContext(&displayFactory, windowFactory, pixmapFactory, config);
584}
585
586} // eglu
587