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 
42 namespace tcu
43 {
44 namespace lnx
45 {
46 namespace x11
47 {
48 namespace glx
49 {
50 
51 using de::UniquePtr;
52 using de::MovePtr;
53 using glu::ApiType;
54 using glu::ContextFactory;
55 using glu::ContextType;
56 using glu::RenderConfig;
57 using glu::RenderContext;
58 using tcu::CommandLine;
59 using tcu::RenderTarget;
60 using std::string;
61 using std::set;
62 using std::istringstream;
63 using std::ostringstream;
64 using std::istream_iterator;
65 
66 typedef RenderConfig::Visibility Visibility;
67 
68 
69 template<typename T>
checkGLX(T value,const char * expr,const char * file,int line)70 static 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 
80 class GlxContextFactory : public glu::ContextFactory
81 {
82 public:
83 							GlxContextFactory	(EventState& eventState);
84 							~GlxContextFactory	(void);
85 	RenderContext*			createContext		(const RenderConfig&	config,
86 												 const CommandLine&		cmdLine) const;
87 
getEventState(void) const88 	EventState&				getEventState		(void) const { return m_eventState;}
89 
90 	const PFNGLXCREATECONTEXTATTRIBSARBPROC
91 							m_glXCreateContextAttribsARB;
92 
93 private:
94 	EventState&				m_eventState;
95 };
96 
97 class GlxDisplay : public XlibDisplay
98 {
99 public:
100 							GlxDisplay				(EventState&	eventState,
101 													 const char*	name);
getGlxMajorVersion(void) const102 	int						getGlxMajorVersion		(void) const { return m_majorVersion; }
getGlxMinorVersion(void) const103 	int						getGlxMinorVersion		(void) const { return m_minorVersion; }
104 	bool					isGlxExtensionSupported (const char* extName) const;
105 
106 private:
107 	int						m_errorBase;
108 	int						m_eventBase;
109 	int						m_majorVersion;
110 	int						m_minorVersion;
111 	set<string>				m_extensions;
112 };
113 
114 class GlxVisual
115 {
116 public:
117 							GlxVisual			(GlxDisplay& display, GLXFBConfig fbConfig);
118 	int						getAttrib			(int attribute);
getXVisual(void)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);
getGlxDisplay(void)124 	GlxDisplay&				getGlxDisplay		(void) { return m_display; }
getXDisplay(void)125 	::Display*				getXDisplay			(void) { return m_display.getXDisplay(); }
126 
127 private:
128 	GlxDisplay&				m_display;
129 	::Visual*				m_visual;
130 	const GLXFBConfig		m_fbConfig;
131 };
132 
133 class GlxDrawable
134 {
135 public:
~GlxDrawable(void)136 	virtual					~GlxDrawable		(void) {}
137 
processEvents(void)138 	virtual void			processEvents		(void) {}
139 	virtual void			getDimensions		(int* width, int* height) = 0;
140 	int						getWidth			(void);
141 	int						getHeight			(void);
swapBuffers(void)142 	void					swapBuffers			(void) { glXSwapBuffers(getXDisplay(), getGLXDrawable()); }
143 
144 	virtual ::Display*		getXDisplay			(void) = 0;
145 	virtual GLXDrawable		getGLXDrawable		(void) = 0;
146 
147 protected:
GlxDrawable()148 							GlxDrawable			() {}
149 	unsigned int			getAttrib			(int attribute);
150 };
151 
152 class GlxWindow : public GlxDrawable
153 {
154 public:
155 							GlxWindow			(GlxVisual& visual, const RenderConfig& cfg);
156 							~GlxWindow			(void);
processEvents(void)157 	void					processEvents		(void) { m_x11Window.processEvents(); }
getXDisplay(void)158 	::Display*				getXDisplay			(void) { return m_x11Display.getXDisplay(); }
159 	void					getDimensions		(int* width, int* height);
160 
161 protected:
getGLXDrawable()162 	GLXDrawable				getGLXDrawable		() { return m_GLXDrawable; }
163 
164 private:
165 	XlibDisplay&			m_x11Display;
166 	XlibWindow				m_x11Window;
167 	const GLXDrawable		m_GLXDrawable;
168 };
169 
170 class GlxRenderContext : public RenderContext
171 {
172 public:
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 
183 private:
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 
193 extern "C"
194 {
tcuLnxX11GlxErrorHandler(::Display * display,XErrorEvent * event)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 
GlxContextFactory(EventState & eventState)205 GlxContextFactory::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 
createContext(const RenderConfig & config,const CommandLine & cmdLine) const217 RenderContext* 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 
~GlxContextFactory(void)225 GlxContextFactory::~GlxContextFactory (void)
226 {
227 }
228 
GlxDisplay(EventState & eventState,const char * name)229 GlxDisplay::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 
isGlxExtensionSupported(const char * extName) const251 bool 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`.
checkGlxVersion(const GlxDisplay & dpy,int major,int minor)258 static 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`.
checkGlxExtension(const GlxDisplay & dpy,const char * extName)274 static 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 
GlxVisual(GlxDisplay & display,GLXFBConfig fbConfig)284 GlxVisual::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 
getAttrib(int attribute)298 int GlxVisual::getAttrib (int attribute)
299 {
300 	int fbvalue;
301 	TCU_CHECK_GLX_CONFIG(glXGetFBConfigAttrib(getXDisplay(), m_fbConfig, attribute, &fbvalue));
302 	return fbvalue;
303 }
304 
createContext(const GlxContextFactory & factory,const ContextType & contextType,glu::ResetNotificationStrategy resetNotificationStrategy)305 GLXContext 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 
createWindow(::Window xWindow)398 GLXWindow GlxVisual::createWindow (::Window xWindow)
399 {
400 	return TCU_CHECK_GLX(glXCreateWindow(getXDisplay(), m_fbConfig, xWindow, NULL));
401 }
402 
getAttrib(int attrib)403 unsigned GlxDrawable::getAttrib (int attrib)
404 {
405 	unsigned int value = 0;
406 	glXQueryDrawable(getXDisplay(), getGLXDrawable(), attrib, &value);
407 	return value;
408 }
409 
getWidth(void)410 int GlxDrawable::getWidth (void)
411 {
412 	int width = 0;
413 	getDimensions(&width, DE_NULL);
414 	return width;
415 }
416 
getHeight(void)417 int GlxDrawable::getHeight (void)
418 {
419 	int height = 0;
420 	getDimensions(DE_NULL, &height);
421 	return height;
422 }
423 
GlxWindow(GlxVisual & visual,const RenderConfig & cfg)424 GlxWindow::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 
getDimensions(int * width,int * height)433 void 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 
~GlxWindow(void)445 GlxWindow::~GlxWindow (void)
446 {
447 	glXDestroyWindow(m_x11Display.getXDisplay(), m_GLXDrawable);
448 }
449 
450 static 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 
surfaceTypeToDrawableBits(RenderConfig::SurfaceType type)466 static 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 
configMatches(GlxVisual & visual,const RenderConfig & renderCfg)484 static 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 
513 class Rank
514 {
515 public:
Rank(void)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);
getValue(void)519 	deUint64	getValue	(void) { return m_value; }
520 
521 private:
522 	deUint64	m_value;
523 	size_t		m_bitsLeft;
524 };
525 
add(size_t bits,deUint32 value)526 void 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 
sub(size_t bits,deUint32 value)533 void 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 
configRank(GlxVisual & visual)540 static 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 
chooseVisual(GlxDisplay & display,const RenderConfig & cfg)600 static 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 
createDrawable(GlxVisual & visual,const RenderConfig & config)640 GlxDrawable* 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 
675 struct GlxFunctionLoader : public glw::FunctionLoader
676 {
GlxFunctionLoadertcu::lnx::x11::glx::GlxFunctionLoader677 							GlxFunctionLoader	(void) {}
678 
gettcu::lnx::x11::glx::GlxFunctionLoader679 	glw::GenericFuncType	get					(const char* name) const
680 	{
681 		return glXGetProcAddress(reinterpret_cast<const GLubyte*>(name));
682 	}
683 };
684 
GlxRenderContext(const GlxContextFactory & factory,const RenderConfig & config)685 GlxRenderContext::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 
~GlxRenderContext(void)706 GlxRenderContext::~GlxRenderContext (void)
707 {
708 	clearCurrent();
709 	if (m_GLXContext != DE_NULL)
710 		glXDestroyContext(m_glxDisplay.getXDisplay(), m_GLXContext);
711 }
712 
makeCurrent(void)713 void 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 
clearCurrent(void)720 void GlxRenderContext::clearCurrent (void)
721 {
722 	TCU_CHECK_GLX(glXMakeContextCurrent(m_glxDisplay.getXDisplay(),
723 										None, None, DE_NULL));
724 }
725 
getType(void) const726 ContextType GlxRenderContext::getType (void) const
727 {
728 	return m_type;
729 }
730 
postIterate(void)731 void GlxRenderContext::postIterate (void)
732 {
733 	m_glxDrawable->swapBuffers();
734 	m_glxDrawable->processEvents();
735 	m_glxDisplay.processEvents();
736 }
737 
getRenderTarget(void) const738 const RenderTarget& GlxRenderContext::getRenderTarget (void) const
739 {
740 	return m_renderTarget;
741 }
742 
getFunctions(void) const743 const glw::Functions& GlxRenderContext::getFunctions (void) const
744 {
745 	return m_functions;
746 }
747 
createContextFactory(EventState & eventState)748 MovePtr<ContextFactory> createContextFactory (EventState& eventState)
749 {
750 	return MovePtr<ContextFactory>(new GlxContextFactory(eventState));
751 }
752 
753 } // glx
754 } // x11
755 } // lnx
756 } // tcu
757