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