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