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 Platform that uses X11 via GLX.
22 *//*--------------------------------------------------------------------*/
23
24#include "tcuLnxX11GlxPlatform.hpp"
25
26#include "tcuRenderTarget.hpp"
27#include "glwInitFunctions.hpp"
28#include "deUniquePtr.hpp"
29#include "glwEnums.hpp"
30
31#include <sstream>
32#include <iterator>
33#include <set>
34
35#define GLX_GLXEXT_PROTOTYPES
36#include <GL/glx.h>
37
38#ifndef GLX_CONTEXT_OPENGL_NO_ERROR_ARB
39#define GLX_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3
40#endif
41
42namespace tcu
43{
44namespace lnx
45{
46namespace x11
47{
48namespace glx
49{
50
51using de::UniquePtr;
52using de::MovePtr;
53using glu::ApiType;
54using glu::ContextFactory;
55using glu::ContextType;
56using glu::RenderConfig;
57using glu::RenderContext;
58using tcu::CommandLine;
59using tcu::RenderTarget;
60using std::string;
61using std::set;
62using std::istringstream;
63using std::ostringstream;
64using std::istream_iterator;
65
66typedef RenderConfig::Visibility Visibility;
67
68
69template<typename T>
70static inline T checkGLX(T value, const char* expr, const char* file, int line)
71{
72	if (!value)
73		throw tcu::TestError("GLX call failed", expr, file, line);
74	return value;
75}
76
77#define TCU_CHECK_GLX(EXPR) checkGLX(EXPR, #EXPR, __FILE__, __LINE__)
78#define TCU_CHECK_GLX_CONFIG(EXPR) checkGLX((EXPR) == Success, #EXPR, __FILE__, __LINE__)
79
80class GlxContextFactory : public glu::ContextFactory
81{
82public:
83							GlxContextFactory	(EventState& eventState);
84							~GlxContextFactory	(void);
85	RenderContext*			createContext		(const RenderConfig&	config,
86												 const CommandLine&		cmdLine) const;
87
88	EventState&				getEventState		(void) const { return m_eventState;}
89
90	const PFNGLXCREATECONTEXTATTRIBSARBPROC
91							m_glXCreateContextAttribsARB;
92
93private:
94	EventState&				m_eventState;
95};
96
97class GlxDisplay : public XlibDisplay
98{
99public:
100							GlxDisplay				(EventState&	eventState,
101													 const char*	name);
102	int						getGlxMajorVersion		(void) const { return m_majorVersion; }
103	int						getGlxMinorVersion		(void) const { return m_minorVersion; }
104	bool					isGlxExtensionSupported (const char* extName) const;
105
106private:
107	int						m_errorBase;
108	int						m_eventBase;
109	int						m_majorVersion;
110	int						m_minorVersion;
111	set<string>				m_extensions;
112};
113
114class GlxVisual
115{
116public:
117							GlxVisual			(GlxDisplay& display, GLXFBConfig fbConfig);
118	int						getAttrib			(int attribute);
119	Visual*					getXVisual			(void) { return m_visual; }
120	GLXContext				createContext		(const GlxContextFactory&		factory,
121												 const ContextType&				contextType,
122												 glu::ResetNotificationStrategy	resetNotificationStrategy);
123	GLXWindow				createWindow		(::Window xWindow);
124	GlxDisplay&				getGlxDisplay		(void) { return m_display; }
125	::Display*				getXDisplay			(void) { return m_display.getXDisplay(); }
126
127private:
128	GlxDisplay&				m_display;
129	::Visual*				m_visual;
130	const GLXFBConfig		m_fbConfig;
131};
132
133class GlxDrawable
134{
135public:
136	virtual					~GlxDrawable		(void) {}
137
138	virtual void			processEvents		(void) {}
139	virtual void			getDimensions		(int* width, int* height) = 0;
140	int						getWidth			(void);
141	int						getHeight			(void);
142	void					swapBuffers			(void) { glXSwapBuffers(getXDisplay(), getGLXDrawable()); }
143
144	virtual ::Display*		getXDisplay			(void) = 0;
145	virtual GLXDrawable		getGLXDrawable		(void) = 0;
146
147protected:
148							GlxDrawable			() {}
149	unsigned int			getAttrib			(int attribute);
150};
151
152class GlxWindow : public GlxDrawable
153{
154public:
155							GlxWindow			(GlxVisual& visual, const RenderConfig& cfg);
156							~GlxWindow			(void);
157	void					processEvents		(void) { m_x11Window.processEvents(); }
158	::Display*				getXDisplay			(void) { return m_x11Display.getXDisplay(); }
159	void					getDimensions		(int* width, int* height);
160
161protected:
162	GLXDrawable				getGLXDrawable		() { return m_GLXDrawable; }
163
164private:
165	XlibDisplay&			m_x11Display;
166	XlibWindow				m_x11Window;
167	const GLXDrawable		m_GLXDrawable;
168};
169
170class GlxRenderContext : public RenderContext
171{
172public:
173										GlxRenderContext	(const GlxContextFactory&	factory,
174															 const RenderConfig&		config);
175										~GlxRenderContext	(void);
176	virtual ContextType					getType				(void) const;
177	virtual void						postIterate			(void);
178	virtual void						makeCurrent			(void);
179	void								clearCurrent		(void);
180	virtual const glw::Functions&		getFunctions		(void) const;
181	virtual const tcu::RenderTarget&	getRenderTarget		(void) const;
182
183private:
184	GlxDisplay							m_glxDisplay;
185	GlxVisual							m_glxVisual;
186	ContextType							m_type;
187	GLXContext							m_GLXContext;
188	UniquePtr<GlxDrawable>				m_glxDrawable;
189	RenderTarget						m_renderTarget;
190	glw::Functions						m_functions;
191};
192
193extern "C"
194{
195	static int tcuLnxX11GlxErrorHandler (::Display* display, XErrorEvent* event)
196	{
197		char buf[80];
198		XGetErrorText(display, event->error_code, buf, sizeof(buf));
199		tcu::print("X operation %u:%u failed: %s\n",
200				   event->request_code, event->minor_code, buf);
201		return 0;
202	}
203}
204
205GlxContextFactory::GlxContextFactory (EventState& eventState)
206	: glu::ContextFactory			("glx", "X11 GLX OpenGL Context")
207	, m_glXCreateContextAttribsARB	(
208		reinterpret_cast<PFNGLXCREATECONTEXTATTRIBSARBPROC>(
209			TCU_CHECK_GLX(
210				glXGetProcAddress(
211					reinterpret_cast<const GLubyte*>("glXCreateContextAttribsARB")))))
212	, m_eventState					(eventState)
213{
214	XSetErrorHandler(tcuLnxX11GlxErrorHandler);
215}
216
217RenderContext* GlxContextFactory::createContext (const RenderConfig&	config,
218												 const CommandLine&		cmdLine) const
219{
220	DE_UNREF(cmdLine);
221	GlxRenderContext* const renderContext = new GlxRenderContext(*this, config);
222	return renderContext;
223}
224
225GlxContextFactory::~GlxContextFactory (void)
226{
227}
228
229GlxDisplay::GlxDisplay (EventState& eventState, const char* name)
230	: XlibDisplay	(eventState, name)
231{
232	const Bool supported = glXQueryExtension(m_display, &m_errorBase, &m_eventBase);
233	if (!supported)
234		TCU_THROW(NotSupportedError, "GLX protocol not supported by X server");
235
236	TCU_CHECK_GLX(glXQueryVersion(m_display, &m_majorVersion, &m_minorVersion));
237
238	{
239		const int screen = XDefaultScreen(m_display);
240		// nVidia doesn't seem to report client-side extensions correctly,
241		// so only use server side
242		const char* const extensions =
243			TCU_CHECK_GLX(glXQueryServerString(m_display, screen, GLX_EXTENSIONS));
244		istringstream extStream(extensions);
245		m_extensions = set<string>(istream_iterator<string>(extStream),
246								   istream_iterator<string>());
247	}
248}
249
250
251bool GlxDisplay::isGlxExtensionSupported (const char* extName) const
252{
253	return m_extensions.find(extName) != m_extensions.end();
254}
255
256//! Throw `tcu::NotSupportedError` if `dpy` is not compatible with GLX
257//! version `major`.`minor`.
258static void checkGlxVersion (const GlxDisplay& dpy, int major, int minor)
259{
260	const int dpyMajor = dpy.getGlxMajorVersion();
261	const int dpyMinor = dpy.getGlxMinorVersion();
262	if (!(dpyMajor == major && dpyMinor >= minor))
263	{
264		ostringstream oss;
265		oss << "Server GLX version "
266			<< dpyMajor << "." << dpyMinor
267			<< " not compatible with required version "
268			<< major << "." << minor;
269		TCU_THROW(NotSupportedError, oss.str().c_str());
270	}
271}
272
273//! Throw `tcu::NotSupportedError` if `dpy` does not support extension `extName`.
274static void checkGlxExtension (const GlxDisplay& dpy, const char* extName)
275{
276	if (!dpy.isGlxExtensionSupported(extName))
277	{
278		ostringstream oss;
279		oss << "GLX extension \"" << extName << "\" not supported";
280		TCU_THROW(NotSupportedError, oss.str().c_str());
281	}
282}
283
284GlxVisual::GlxVisual (GlxDisplay& display, GLXFBConfig fbConfig)
285	: m_display		(display)
286	, m_visual		(DE_NULL)
287	, m_fbConfig	(fbConfig)
288{
289	XVisualInfo* visualInfo = glXGetVisualFromFBConfig(getXDisplay(), fbConfig);
290
291	if (!visualInfo)
292		TCU_THROW(ResourceError, "glXGetVisualFromFBConfig() returned NULL");
293
294	m_visual = visualInfo->visual;
295	XFree(visualInfo);
296}
297
298int GlxVisual::getAttrib (int attribute)
299{
300	int fbvalue;
301	TCU_CHECK_GLX_CONFIG(glXGetFBConfigAttrib(getXDisplay(), m_fbConfig, attribute, &fbvalue));
302	return fbvalue;
303}
304
305GLXContext GlxVisual::createContext (const GlxContextFactory&		factory,
306									 const ContextType&				contextType,
307									 glu::ResetNotificationStrategy	resetNotificationStrategy)
308{
309	std::vector<int>	attribs;
310
311	checkGlxVersion(m_display, 1, 4);
312	checkGlxExtension(m_display, "GLX_ARB_create_context");
313	checkGlxExtension(m_display, "GLX_ARB_create_context_profile");
314
315	{
316		const ApiType	apiType		= contextType.getAPI();
317		int				profileMask	= 0;
318
319		switch (apiType.getProfile())
320		{
321			case glu::PROFILE_ES:
322				checkGlxExtension(m_display, "GLX_EXT_create_context_es2_profile");
323				profileMask = GLX_CONTEXT_ES2_PROFILE_BIT_EXT;
324				break;
325			case glu::PROFILE_CORE:
326				profileMask = GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
327				break;
328			case glu::PROFILE_COMPATIBILITY:
329				profileMask = GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
330				break;
331			default:
332				DE_FATAL("Impossible context profile");
333		}
334
335		attribs.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB);
336		attribs.push_back(apiType.getMajorVersion());
337		attribs.push_back(GLX_CONTEXT_MINOR_VERSION_ARB);
338		attribs.push_back(apiType.getMinorVersion());
339		attribs.push_back(GLX_CONTEXT_PROFILE_MASK_ARB);
340		attribs.push_back(profileMask);
341	}
342
343	// Context flags
344	{
345		int		flags	= 0;
346
347		if ((contextType.getFlags() & glu::CONTEXT_FORWARD_COMPATIBLE) != 0)
348		{
349			if (glu::isContextTypeES(contextType))
350				TCU_THROW(InternalError, "Only OpenGL core contexts can be forward-compatible");
351
352			flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
353		}
354
355		if ((contextType.getFlags() & glu::CONTEXT_DEBUG) != 0)
356			flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
357
358		if ((contextType.getFlags() & glu::CONTEXT_ROBUST) != 0)
359			flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB;
360
361		if ((contextType.getFlags() & glu::CONTEXT_NO_ERROR) != 0)
362		{
363			if (m_display.isGlxExtensionSupported("GLX_ARB_create_context_no_error"))
364			{
365				attribs.push_back(GLX_CONTEXT_OPENGL_NO_ERROR_ARB);
366				attribs.push_back(True);
367			}
368			else
369				TCU_THROW(NotSupportedError, "GLX_ARB_create_context_no_error is required for creating no-error contexts");
370		}
371
372		if (flags != 0)
373		{
374			attribs.push_back(GLX_CONTEXT_FLAGS_ARB);
375			attribs.push_back(flags);
376		}
377	}
378
379	if (resetNotificationStrategy != glu::RESET_NOTIFICATION_STRATEGY_NOT_SPECIFIED)
380	{
381		attribs.push_back(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
382
383		if (resetNotificationStrategy == glu::RESET_NOTIFICATION_STRATEGY_NO_RESET_NOTIFICATION)
384			attribs.push_back(GLX_NO_RESET_NOTIFICATION_ARB);
385		else if (resetNotificationStrategy == glu::RESET_NOTIFICATION_STRATEGY_LOSE_CONTEXT_ON_RESET)
386			attribs.push_back(GLX_LOSE_CONTEXT_ON_RESET_ARB);
387		else
388			TCU_THROW(InternalError, "Unknown reset notification strategy");
389	}
390
391	// Terminate attrib list
392	attribs.push_back(None);
393
394	return TCU_CHECK_GLX(factory.m_glXCreateContextAttribsARB(
395							 getXDisplay(), m_fbConfig, DE_NULL, True, &attribs[0]));
396}
397
398GLXWindow GlxVisual::createWindow (::Window xWindow)
399{
400	return TCU_CHECK_GLX(glXCreateWindow(getXDisplay(), m_fbConfig, xWindow, NULL));
401}
402
403unsigned GlxDrawable::getAttrib (int attrib)
404{
405	unsigned int value = 0;
406	glXQueryDrawable(getXDisplay(), getGLXDrawable(), attrib, &value);
407	return value;
408}
409
410int GlxDrawable::getWidth (void)
411{
412	int width = 0;
413	getDimensions(&width, DE_NULL);
414	return width;
415}
416
417int GlxDrawable::getHeight (void)
418{
419	int height = 0;
420	getDimensions(DE_NULL, &height);
421	return height;
422}
423
424GlxWindow::GlxWindow (GlxVisual& visual, const RenderConfig& cfg)
425	: m_x11Display	(visual.getGlxDisplay())
426	, m_x11Window	(m_x11Display, cfg.width, cfg.height,
427					 visual.getXVisual())
428	, m_GLXDrawable	(visual.createWindow(m_x11Window.getXID()))
429{
430	m_x11Window.setVisibility(cfg.windowVisibility != RenderConfig::VISIBILITY_HIDDEN);
431}
432
433void GlxWindow::getDimensions (int* width, int* height)
434{
435	if (width != DE_NULL)
436		*width = getAttrib(GLX_WIDTH);
437	if (height != DE_NULL)
438		*height = getAttrib(GLX_HEIGHT);
439
440	// glXQueryDrawable may be buggy, so fall back to X geometry if needed
441	if ((width != DE_NULL && *width == 0) || (height != DE_NULL && *height == 0))
442		m_x11Window.getDimensions(width, height);
443}
444
445GlxWindow::~GlxWindow (void)
446{
447	glXDestroyWindow(m_x11Display.getXDisplay(), m_GLXDrawable);
448}
449
450static const struct Attribute
451{
452	int						glxAttribute;
453	int	RenderConfig::*		cfgMember;
454} s_attribs[] =
455{
456	{ GLX_RED_SIZE,		&RenderConfig::redBits		},
457	{ GLX_GREEN_SIZE,	&RenderConfig::greenBits	},
458	{ GLX_BLUE_SIZE,	&RenderConfig::blueBits		},
459	{ GLX_ALPHA_SIZE,	&RenderConfig::alphaBits	},
460	{ GLX_DEPTH_SIZE,	&RenderConfig::depthBits	},
461	{ GLX_STENCIL_SIZE,	&RenderConfig::stencilBits	},
462	{ GLX_SAMPLES,		&RenderConfig::numSamples	},
463	{ GLX_FBCONFIG_ID,	&RenderConfig::id			},
464};
465
466static deUint32 surfaceTypeToDrawableBits (RenderConfig::SurfaceType type)
467{
468	switch (type)
469	{
470		case RenderConfig::SURFACETYPE_WINDOW:
471			return GLX_WINDOW_BIT;
472		case RenderConfig::SURFACETYPE_OFFSCREEN_NATIVE:
473			return GLX_PIXMAP_BIT;
474		case RenderConfig::SURFACETYPE_OFFSCREEN_GENERIC:
475			return GLX_PBUFFER_BIT;
476		case RenderConfig::SURFACETYPE_DONT_CARE:
477			return GLX_WINDOW_BIT | GLX_PIXMAP_BIT | GLX_PBUFFER_BIT;
478		default:
479			DE_FATAL("Impossible case");
480	}
481	return 0;
482}
483
484static bool configMatches (GlxVisual& visual, const RenderConfig& renderCfg)
485{
486	if (renderCfg.id != RenderConfig::DONT_CARE)
487		return visual.getAttrib(GLX_FBCONFIG_ID) == renderCfg.id;
488
489	for (const Attribute* it = DE_ARRAY_BEGIN(s_attribs); it != DE_ARRAY_END(s_attribs); it++)
490	{
491		const int requested = renderCfg.*it->cfgMember;
492		if (requested != RenderConfig::DONT_CARE &&
493			requested != visual.getAttrib(it->glxAttribute))
494			return false;
495	}
496
497	{
498		deUint32 bits = surfaceTypeToDrawableBits(renderCfg.surfaceType);
499
500		if ((visual.getAttrib(GLX_DRAWABLE_TYPE) & bits) == 0)
501			return false;
502
503		// It shouldn't be possible to have GLX_WINDOW_BIT set without a visual,
504		// but let's make sure.
505		if (renderCfg.surfaceType == RenderConfig::SURFACETYPE_WINDOW &&
506			visual.getXVisual() == DE_NULL)
507			return false;
508	}
509
510	return true;
511}
512
513class Rank
514{
515public:
516				Rank		(void) : m_value(0), m_bitsLeft(64) {}
517	void		add			(size_t bits, deUint32 value);
518	void		sub			(size_t bits, deUint32 value);
519	deUint64	getValue	(void) { return m_value; }
520
521private:
522	deUint64	m_value;
523	size_t		m_bitsLeft;
524};
525
526void Rank::add (size_t bits, deUint32 value)
527{
528	TCU_CHECK_INTERNAL(m_bitsLeft >= bits);
529	m_bitsLeft -= bits;
530	m_value = m_value << bits | de::min((1U << bits) - 1, value);
531}
532
533void Rank::sub (size_t bits, deUint32 value)
534{
535	TCU_CHECK_INTERNAL(m_bitsLeft >= bits);
536	m_bitsLeft -= bits;
537	m_value = m_value << bits | ((1U << bits) - 1 - de::min((1U << bits) - 1U, value));
538}
539
540static deUint64 configRank (GlxVisual& visual)
541{
542	// Sanity checks.
543	if (visual.getAttrib(GLX_DOUBLEBUFFER)					== False	||
544		(visual.getAttrib(GLX_RENDER_TYPE) & GLX_RGBA_BIT)	== 0)
545		return 0;
546
547	Rank rank;
548	int caveat		= visual.getAttrib(GLX_CONFIG_CAVEAT);
549	int redSize		= visual.getAttrib(GLX_RED_SIZE);
550	int greenSize	= visual.getAttrib(GLX_GREEN_SIZE);
551	int blueSize	= visual.getAttrib(GLX_BLUE_SIZE);
552	int alphaSize	= visual.getAttrib(GLX_ALPHA_SIZE);
553	int depthSize	= visual.getAttrib(GLX_DEPTH_SIZE);
554	int stencilSize	= visual.getAttrib(GLX_STENCIL_SIZE);
555	int minRGB		= de::min(redSize, de::min(greenSize, blueSize));
556
557	// Prefer conformant configurations.
558	rank.add(1, (caveat != GLX_NON_CONFORMANT_CONFIG));
559
560	// Prefer non-transparent configurations.
561	rank.add(1, visual.getAttrib(GLX_TRANSPARENT_TYPE) == GLX_NONE);
562
563	// Avoid stereo
564	rank.add(1, visual.getAttrib(GLX_STEREO) == False);
565
566	// Avoid overlays
567	rank.add(1, visual.getAttrib(GLX_LEVEL) == 0);
568
569	// Prefer to have some alpha.
570	rank.add(1, alphaSize > 0);
571
572	// Prefer to have a depth buffer.
573	rank.add(1, depthSize > 0);
574
575	// Prefer to have a stencil buffer.
576	rank.add(1, stencilSize > 0);
577
578	// Avoid slow configurations.
579	rank.add(1, (caveat != GLX_SLOW_CONFIG));
580
581	// Prefer larger, evenly distributed color depths
582	rank.add(4, de::min(minRGB, alphaSize));
583
584	// If alpha is low, choose best RGB
585	rank.add(4, minRGB);
586
587	// Prefer larger depth and stencil buffers
588	rank.add(6, deUint32(depthSize + stencilSize));
589
590	// Avoid excessive sampling
591	rank.sub(5, visual.getAttrib(GLX_SAMPLES));
592
593	// Prefer True/DirectColor
594	int visualType = visual.getAttrib(GLX_X_VISUAL_TYPE);
595	rank.add(1, visualType == GLX_TRUE_COLOR || visualType == GLX_DIRECT_COLOR);
596
597	return rank.getValue();
598}
599
600static GlxVisual chooseVisual (GlxDisplay& display, const RenderConfig& cfg)
601{
602	::Display*	dpy			= display.getXDisplay();
603	deUint64	maxRank		= 0;
604	GLXFBConfig	maxConfig	= DE_NULL;
605	int			numElems	= 0;
606
607	GLXFBConfig* const fbConfigs = glXGetFBConfigs(dpy, DefaultScreen(dpy), &numElems);
608	TCU_CHECK_MSG(fbConfigs != DE_NULL, "Couldn't query framebuffer configurations");
609
610	for (int i = 0; i < numElems; i++)
611	{
612		try
613		{
614			GlxVisual visual(display, fbConfigs[i]);
615
616			if (!configMatches(visual, cfg))
617				continue;
618
619			deUint64 cfgRank = configRank(visual);
620
621			if (cfgRank > maxRank)
622			{
623				maxRank		= cfgRank;
624				maxConfig	= fbConfigs[i];
625			}
626		}
627		catch (const tcu::ResourceError&)
628		{
629			// Some drivers report invalid visuals. Ignore them.
630		}
631	}
632	XFree(fbConfigs);
633
634	if (maxRank == 0)
635		TCU_THROW(NotSupportedError, "Requested GLX configuration not found or unusable");
636
637	return GlxVisual(display, maxConfig);
638}
639
640GlxDrawable* createDrawable (GlxVisual& visual, const RenderConfig& config)
641{
642	RenderConfig::SurfaceType surfaceType = config.surfaceType;
643
644	if (surfaceType == RenderConfig::SURFACETYPE_DONT_CARE)
645	{
646		if (visual.getXVisual() == DE_NULL)
647			// No visual, cannot create X window
648			surfaceType = RenderConfig::SURFACETYPE_OFFSCREEN_NATIVE;
649		else
650			surfaceType = RenderConfig::SURFACETYPE_WINDOW;
651	}
652
653	switch (surfaceType)
654	{
655		case RenderConfig::SURFACETYPE_DONT_CARE:
656			DE_FATAL("Impossible case");
657
658		case RenderConfig::SURFACETYPE_WINDOW:
659			return new GlxWindow(visual, config);
660			break;
661
662		case RenderConfig::SURFACETYPE_OFFSCREEN_NATIVE:
663			// \todo [2013-11-28 lauri] Pixmaps
664
665		case RenderConfig::SURFACETYPE_OFFSCREEN_GENERIC:
666			// \todo [2013-11-28 lauri] Pbuffers
667
668		default:
669			TCU_THROW(NotSupportedError, "Unsupported surface type");
670	}
671
672	return DE_NULL;
673}
674
675struct GlxFunctionLoader : public glw::FunctionLoader
676{
677							GlxFunctionLoader	(void) {}
678
679	glw::GenericFuncType	get					(const char* name) const
680	{
681		return glXGetProcAddress(reinterpret_cast<const GLubyte*>(name));
682	}
683};
684
685GlxRenderContext::GlxRenderContext (const GlxContextFactory&	factory,
686									const RenderConfig&			config)
687	: m_glxDisplay		(factory.getEventState(), DE_NULL)
688	, m_glxVisual		(chooseVisual(m_glxDisplay, config))
689	, m_type			(config.type)
690	, m_GLXContext		(m_glxVisual.createContext(factory, config.type, config.resetNotificationStrategy))
691	, m_glxDrawable		(createDrawable(m_glxVisual, config))
692	, m_renderTarget	(m_glxDrawable->getWidth(), m_glxDrawable->getHeight(),
693						 PixelFormat(m_glxVisual.getAttrib(GLX_RED_SIZE),
694									 m_glxVisual.getAttrib(GLX_GREEN_SIZE),
695									 m_glxVisual.getAttrib(GLX_BLUE_SIZE),
696									 m_glxVisual.getAttrib(GLX_ALPHA_SIZE)),
697						 m_glxVisual.getAttrib(GLX_DEPTH_SIZE),
698						 m_glxVisual.getAttrib(GLX_STENCIL_SIZE),
699						 m_glxVisual.getAttrib(GLX_SAMPLES))
700{
701	const GlxFunctionLoader loader;
702	makeCurrent();
703	glu::initFunctions(&m_functions, &loader, config.type.getAPI());
704}
705
706GlxRenderContext::~GlxRenderContext (void)
707{
708	clearCurrent();
709	if (m_GLXContext != DE_NULL)
710		glXDestroyContext(m_glxDisplay.getXDisplay(), m_GLXContext);
711}
712
713void GlxRenderContext::makeCurrent (void)
714{
715	const GLXDrawable drawRead = m_glxDrawable->getGLXDrawable();
716	TCU_CHECK_GLX(glXMakeContextCurrent(m_glxDisplay.getXDisplay(),
717										drawRead, drawRead, m_GLXContext));
718}
719
720void GlxRenderContext::clearCurrent (void)
721{
722	TCU_CHECK_GLX(glXMakeContextCurrent(m_glxDisplay.getXDisplay(),
723										None, None, DE_NULL));
724}
725
726ContextType GlxRenderContext::getType (void) const
727{
728	return m_type;
729}
730
731void GlxRenderContext::postIterate (void)
732{
733	m_glxDrawable->swapBuffers();
734	m_glxDrawable->processEvents();
735	m_glxDisplay.processEvents();
736}
737
738const RenderTarget& GlxRenderContext::getRenderTarget (void) const
739{
740	return m_renderTarget;
741}
742
743const glw::Functions& GlxRenderContext::getFunctions (void) const
744{
745	return m_functions;
746}
747
748MovePtr<ContextFactory> createContextFactory (EventState& eventState)
749{
750	return MovePtr<ContextFactory>(new GlxContextFactory(eventState));
751}
752
753} // glx
754} // x11
755} // lnx
756} // tcu
757