Panda3D
glxGraphicsStateGuardian.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file glxGraphicsStateGuardian.cxx
10  * @author drose
11  * @date 2003-01-27
12  */
13 
15 #include "config_glxdisplay.h"
16 #include "config_glgsg.h"
17 #include "lightReMutexHolder.h"
18 
19 #include <dlfcn.h>
20 
21 using std::string;
22 
23 
24 TypeHandle glxGraphicsStateGuardian::_type_handle;
25 
26 /**
27  *
28  */
29 glxGraphicsStateGuardian::
30 glxGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
31  glxGraphicsStateGuardian *share_with) :
32  PosixGraphicsStateGuardian(engine, pipe)
33 {
34  _share_context=nullptr;
35  _context=nullptr;
36  _display=nullptr;
37  _screen=0;
38  _visual=nullptr;
39  _visuals=nullptr;
40  _fbconfig=nullptr;
41  _context_has_pbuffer = false;
42  _context_has_pixmap = false;
43  _slow = false;
44 
45  _supports_swap_control = false;
46  _supports_fbconfig = false;
47  _supports_pbuffer = false;
48  _uses_sgix_pbuffer = false;
49 
50  if (share_with != nullptr) {
51  _prepared_objects = share_with->get_prepared_objects();
52  _share_context = share_with->_context;
53  }
54 
55  _checked_get_proc_address = false;
56  _glXGetProcAddress = nullptr;
57  _temp_context = (GLXContext)nullptr;
58  _temp_xwindow = (X11_Window)nullptr;
59  _temp_colormap = (Colormap)nullptr;
60 }
61 
62 /**
63  *
64  */
65 glxGraphicsStateGuardian::
66 ~glxGraphicsStateGuardian() {
67  // Actually, the lock might have already destructed, so we can't reliably
68  // grab the X11 lock here.
69  //LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
70  destroy_temp_xwindow();
71  if (_visuals != nullptr) {
72  XFree(_visuals);
73  }
74  if (_context != (GLXContext)nullptr) {
75  glXDestroyContext(_display, _context);
76  _context = (GLXContext)nullptr;
77  }
78 }
79 
80 /**
81  * Gets the FrameBufferProperties to match the indicated visual.
82  */
84 get_properties(FrameBufferProperties &properties, XVisualInfo *visual) {
85 
86  int use_gl, render_mode, double_buffer, stereo,
87  red_size, green_size, blue_size,
88  alpha_size, ared_size, agreen_size, ablue_size, aalpha_size,
89  depth_size, stencil_size;
90 
91  glXGetConfig(_display, visual, GLX_USE_GL, &use_gl);
92  glXGetConfig(_display, visual, GLX_RGBA, &render_mode);
93  glXGetConfig(_display, visual, GLX_DOUBLEBUFFER, &double_buffer);
94  glXGetConfig(_display, visual, GLX_STEREO, &stereo);
95  glXGetConfig(_display, visual, GLX_RED_SIZE, &red_size);
96  glXGetConfig(_display, visual, GLX_GREEN_SIZE, &green_size);
97  glXGetConfig(_display, visual, GLX_BLUE_SIZE, &blue_size);
98  glXGetConfig(_display, visual, GLX_ALPHA_SIZE, &alpha_size);
99  glXGetConfig(_display, visual, GLX_ACCUM_RED_SIZE, &ared_size);
100  glXGetConfig(_display, visual, GLX_ACCUM_GREEN_SIZE, &agreen_size);
101  glXGetConfig(_display, visual, GLX_ACCUM_BLUE_SIZE, &ablue_size);
102  glXGetConfig(_display, visual, GLX_ACCUM_ALPHA_SIZE, &aalpha_size);
103  glXGetConfig(_display, visual, GLX_DEPTH_SIZE, &depth_size);
104  glXGetConfig(_display, visual, GLX_STENCIL_SIZE, &stencil_size);
105 
106  properties.clear();
107 
108  if (use_gl == 0) {
109  // If we return a set of properties without setting either rgb_color or
110  // indexed_color, then this indicates a visual that's no good for any kind
111  // of rendering.
112  return;
113  }
114 
115  if (double_buffer) {
116  properties.set_back_buffers(1);
117  }
118  if (stereo) {
119  properties.set_stereo(1);
120  }
121  if (render_mode) {
122  properties.set_rgb_color(1);
123  } else {
124  properties.set_indexed_color(1);
125  }
126 
127  properties.set_rgba_bits(red_size, green_size, blue_size, alpha_size);
128  properties.set_stencil_bits(stencil_size);
129  properties.set_depth_bits(depth_size);
130  properties.set_accum_bits(ared_size+agreen_size+ablue_size+aalpha_size);
131 
132  // Set both hardware and software bits, indicating not-yet-known.
133  properties.set_force_software(1);
134  properties.set_force_hardware(1);
135 }
136 
137 /**
138  * Gets the FrameBufferProperties to match the indicated GLXFBConfig
139  */
142  bool &context_has_pbuffer, bool &context_has_pixmap,
143  bool &slow, GLXFBConfig config) {
144  properties.clear();
145 
146  if (_supports_fbconfig) {
147  // Now update our framebuffer_mode and bit depth appropriately.
148  int render_type, double_buffer, stereo, red_size, green_size, blue_size,
149  alpha_size, ared_size, agreen_size, ablue_size, aalpha_size,
150  depth_size, stencil_size, samples, drawable_type, caveat, srgb_capable;
151 
152  _glXGetFBConfigAttrib(_display, config, GLX_RENDER_TYPE, &render_type);
153  _glXGetFBConfigAttrib(_display, config, GLX_DOUBLEBUFFER, &double_buffer);
154  _glXGetFBConfigAttrib(_display, config, GLX_STEREO, &stereo);
155  _glXGetFBConfigAttrib(_display, config, GLX_RED_SIZE, &red_size);
156  _glXGetFBConfigAttrib(_display, config, GLX_GREEN_SIZE, &green_size);
157  _glXGetFBConfigAttrib(_display, config, GLX_BLUE_SIZE, &blue_size);
158  _glXGetFBConfigAttrib(_display, config, GLX_ALPHA_SIZE, &alpha_size);
159  _glXGetFBConfigAttrib(_display, config, GLX_ACCUM_RED_SIZE, &ared_size);
160  _glXGetFBConfigAttrib(_display, config, GLX_ACCUM_GREEN_SIZE, &agreen_size);
161  _glXGetFBConfigAttrib(_display, config, GLX_ACCUM_BLUE_SIZE, &ablue_size);
162  _glXGetFBConfigAttrib(_display, config, GLX_ACCUM_ALPHA_SIZE, &aalpha_size);
163  _glXGetFBConfigAttrib(_display, config, GLX_DEPTH_SIZE, &depth_size);
164  _glXGetFBConfigAttrib(_display, config, GLX_STENCIL_SIZE, &stencil_size);
165  _glXGetFBConfigAttrib(_display, config, GLX_SAMPLES, &samples);
166  _glXGetFBConfigAttrib(_display, config, GLX_DRAWABLE_TYPE, &drawable_type);
167  _glXGetFBConfigAttrib(_display, config, GLX_CONFIG_CAVEAT, &caveat);
168  _glXGetFBConfigAttrib(_display, config, GLX_FRAMEBUFFER_SRGB_CAPABLE_EXT, &srgb_capable);
169 
170  context_has_pbuffer = false;
171  if ((drawable_type & GLX_PBUFFER_BIT)!=0) {
172  context_has_pbuffer = true;
173  }
174 
175  context_has_pixmap = false;
176  if ((drawable_type & GLX_PIXMAP_BIT)!=0) {
177  context_has_pixmap = true;
178  }
179 
180  slow = false;
181  if (caveat == GLX_SLOW_CONFIG) {
182  slow = true;
183  }
184 
185  if ((drawable_type & GLX_WINDOW_BIT)==0) {
186  // We insist on having a context that will support an onscreen window.
187  return;
188  }
189 
190  if (double_buffer) {
191  properties.set_back_buffers(1);
192  }
193 
194  if (stereo) {
195  properties.set_stereo(true);
196  }
197 
198  if (srgb_capable) {
199  properties.set_srgb_color(true);
200  }
201 
202  if ((render_type & GLX_RGBA_BIT)!=0) {
203  properties.set_rgb_color(true);
204  }
205  if ((render_type & GLX_COLOR_INDEX_BIT)!=0) {
206  properties.set_indexed_color(true);
207  }
208 
209  properties.set_rgba_bits(red_size, green_size, blue_size, alpha_size);
210  properties.set_stencil_bits(stencil_size);
211  properties.set_depth_bits(depth_size);
212  properties.set_accum_bits(ared_size+agreen_size+ablue_size+aalpha_size);
213  properties.set_multisamples(samples);
214 
215  // Set both hardware and software bits, indicating not-yet-known.
216  properties.set_force_software(1);
217  properties.set_force_hardware(1);
218  }
219 }
220 
221 /**
222  * Selects a visual or fbconfig for all the windows and buffers that use this
223  * gsg. Also creates the GL context and obtains the visual.
224  */
227  X11_Display *display,
228  int screen, bool need_pbuffer, bool need_pixmap) {
229 
230  LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
231  _display = display;
232  _screen = screen;
233  _context = nullptr;
234  _fbconfig = nullptr;
235  _visual = nullptr;
236  if (_visuals != nullptr) {
237  XFree(_visuals);
238  _visuals = nullptr;
239  }
240 
241  _fbprops.clear();
242 
243  // First, attempt to create a context using the XVisual interface. We need
244  // this before we can query the FBConfig interface, because we need an
245  // OpenGL context to get the required extension function pointers.
246  destroy_temp_xwindow();
247  choose_temp_visual(properties);
248  if (_temp_context == nullptr) {
249  // No good.
250  return;
251  }
252 
253  // Now we have to initialize the context so we can query its capabilities
254  // and extensions. This also means creating a temporary window, so we have
255  // something to bind the context to and make it current.
256  init_temp_context();
257 
258  if (!_supports_fbconfig) {
259  // We have a good OpenGL context, but it doesn't support the FBConfig
260  // interface, so we'll stop there.
261  glxdisplay_cat.debug()
262  <<" No FBConfig supported; using XVisual only.\n"
263  << _fbprops << "\n";
264 
265  _context = _temp_context;
266  _temp_context = (GLXContext)nullptr;
267 
268  // By convention, every indirect XVisual that can render to a window can
269  // also render to a GLXPixmap. Direct visuals we're not as sure about.
270  _context_has_pixmap = !glXIsDirect(_display, _context);
271 
272  // Pbuffers aren't supported at all with the XVisual interface.
273  _context_has_pbuffer = false;
274  return;
275  }
276 
277  // The OpenGL context supports the FBConfig interface, so we can use that
278  // more advanced interface to choose the actual window format we'll use.
279  // FBConfig provides for more options than the older XVisual interface, so
280  // we'd much rather use it if it's available.
281 
282  int best_quality = 0;
283  int best_result = 0;
284  FrameBufferProperties best_props;
285 
286  int render_type = GLX_RGBA_TYPE;
287  if (properties.get_indexed_color()) {
288  render_type = GLX_COLOR_INDEX_TYPE;
289  }
290 
291  static const int max_attrib_list = 32;
292  int attrib_list[max_attrib_list];
293  int n = 0;
294  attrib_list[n++] = GLX_STEREO;
295  attrib_list[n++] = GLX_DONT_CARE;
296  attrib_list[n++] = GLX_RENDER_TYPE;
297  attrib_list[n++] = GLX_DONT_CARE;
298  attrib_list[n++] = GLX_DRAWABLE_TYPE;
299  attrib_list[n++] = GLX_DONT_CARE;
300  attrib_list[n] = (int)None;
301 
302  int num_configs = 0;
303  GLXFBConfig *configs =
304  _glXChooseFBConfig(_display, _screen, attrib_list, &num_configs);
305 
306  if (configs != nullptr) {
307  bool context_has_pbuffer, context_has_pixmap, slow;
308  int quality, i;
309  for (i = 0; i < num_configs; ++i) {
310  FrameBufferProperties fbprops;
311  get_properties_advanced(fbprops, context_has_pbuffer, context_has_pixmap,
312  slow, configs[i]);
313  quality = fbprops.get_quality(properties);
314  if ((quality > 0)&&(slow)) quality -= 10000000;
315  if (glxdisplay_cat.is_debug()) {
316  const char *pbuffertext = context_has_pbuffer ? " (pbuffer)" : "";
317  const char *pixmaptext = context_has_pixmap ? " (pixmap)" : "";
318  const char *slowtext = slow ? " (slow)" : "";
319  glxdisplay_cat.debug()
320  << i << ": " << fbprops << " quality=" << quality << pbuffertext
321  << pixmaptext << slowtext << "\n";
322  }
323  if (need_pbuffer && !context_has_pbuffer) {
324  continue;
325  }
326  if (need_pixmap && !context_has_pixmap) {
327  continue;
328  }
329 
330  if (quality > best_quality) {
331  best_quality = quality;
332  best_result = i;
333  best_props = fbprops;
334  }
335  }
336  }
337 
338  if (best_quality > 0) {
339  _fbconfig = configs[best_result];
340 
341  if (_glXCreateContextAttribs != nullptr) {
342  // NB. This is a wholly different type of attrib list than below, the
343  // same values are not used!
344  n = 0;
345  attrib_list[n++] = GLX_RENDER_TYPE;
346  attrib_list[n++] = render_type;
347  if (gl_version.get_num_words() > 0) {
348  attrib_list[n++] = GLX_CONTEXT_MAJOR_VERSION_ARB;
349  attrib_list[n++] = gl_version[0];
350  if (gl_version.get_num_words() > 1) {
351  attrib_list[n++] = GLX_CONTEXT_MINOR_VERSION_ARB;
352  attrib_list[n++] = gl_version[1];
353  }
354  }
355  if (gl_debug) {
356  attrib_list[n++] = GLX_CONTEXT_FLAGS_ARB;
357  attrib_list[n++] = GLX_CONTEXT_DEBUG_BIT_ARB;
358  }
359  attrib_list[n] = None;
360  _context = _glXCreateContextAttribs(_display, _fbconfig, _share_context,
361  GL_TRUE, attrib_list);
362  } else {
363  _context =
364  _glXCreateNewContext(_display, _fbconfig, render_type, _share_context,
365  GL_TRUE);
366  }
367 
368  if (_context) {
369  mark_new();
370 
371  if (_visuals != nullptr) {
372  XFree(_visuals);
373  _visuals = nullptr;
374  }
375  _visuals = _glXGetVisualFromFBConfig(_display, _fbconfig);
376  _visual = _visuals;
377 
378  if (_visual) {
379  get_properties_advanced(_fbprops, _context_has_pbuffer, _context_has_pixmap,
380  _slow, _fbconfig);
381 
382  if (!properties.get_srgb_color()) {
383  _fbprops.set_srgb_color(false);
384  }
385 
386  if (glxdisplay_cat.is_debug()) {
387  glxdisplay_cat.debug()
388  << "Selected context " << best_result << ": " << _fbprops << "\n";
389  glxdisplay_cat.debug()
390  << "context_has_pbuffer = " << _context_has_pbuffer
391  << ", context_has_pixmap = " << _context_has_pixmap << "\n";
392  }
393 
394  return;
395  }
396  }
397  // This really shouldn't happen, so I'm not too careful about cleanup.
398  glxdisplay_cat.error()
399  << "Could not create FBConfig context!\n";
400  _fbconfig = nullptr;
401  _context = nullptr;
402  _visual = nullptr;
403  _visuals = nullptr;
404  }
405 
406  glxdisplay_cat.warning()
407  << "No suitable FBConfig contexts available; using XVisual only.\n"
408  << _fbprops << "\n";
409 
410  _context = _temp_context;
411  _temp_context = (GLXContext)nullptr;
412 
413  // By convention, every indirect XVisual that can render to a window can
414  // also render to a GLXPixmap. Direct visuals we're not as sure about.
415  _context_has_pixmap = !glXIsDirect(_display, _context);
416 
417  // Pbuffers aren't supported at all with the XVisual interface.
418  _context_has_pbuffer = false;
419 }
420 
421 /**
422  * Returns true if the runtime GLX version number is at least the indicated
423  * value, false otherwise.
424  */
426 glx_is_at_least_version(int major_version, int minor_version) const {
427  if (_glx_version_major < major_version) {
428  return false;
429  }
430  if (_glx_version_major > major_version) {
431  return true;
432  }
433  if (_glx_version_minor < minor_version) {
434  return false;
435  }
436  return true;
437 }
438 
439 /**
440  * Calls glFlush().
441  */
442 void glxGraphicsStateGuardian::
443 gl_flush() const {
444  // This call requires synchronization with X.
445  LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
446  PosixGraphicsStateGuardian::gl_flush();
447 }
448 
449 /**
450  * Returns the result of glGetError().
451  */
452 GLenum glxGraphicsStateGuardian::
453 gl_get_error() const {
454  // This call requires synchronization with X.
455  LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
456  return PosixGraphicsStateGuardian::gl_get_error();
457 }
458 
459 /**
460  * Queries the runtime version of OpenGL in use.
461  */
462 void glxGraphicsStateGuardian::
463 query_gl_version() {
464  LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
465  PosixGraphicsStateGuardian::query_gl_version();
466 
467  show_glx_client_string("GLX_VENDOR", GLX_VENDOR);
468  show_glx_client_string("GLX_VERSION", GLX_VERSION);
469  show_glx_server_string("GLX_VENDOR", GLX_VENDOR);
470  show_glx_server_string("GLX_VERSION", GLX_VERSION);
471 
472  glXQueryVersion(_display, &_glx_version_major, &_glx_version_minor);
473 
474  // We output to glgsg_cat instead of glxdisplay_cat, since this is where the
475  // GL version has been output, and it's nice to see the two of these
476  // together.
477  if (glgsg_cat.is_debug()) {
478  glgsg_cat.debug()
479  << "GLX_VERSION = " << _glx_version_major << "." << _glx_version_minor
480  << "\n";
481  }
482 }
483 
484 /**
485  * This may be redefined by a derived class (e.g. glx or wgl) to get whatever
486  * further extensions strings may be appropriate to that interface, in
487  * addition to the GL extension strings return by glGetString().
488  */
489 void glxGraphicsStateGuardian::
490 get_extra_extensions() {
491  LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
492  save_extensions(glXQueryExtensionsString(_display, _screen));
493 }
494 
495 /**
496  * Returns the pointer to the GL extension function with the indicated name.
497  * It is the responsibility of the caller to ensure that the required
498  * extension is defined in the OpenGL runtime prior to calling this; it is an
499  * error to call this for a function that is not defined.
500  */
501 void *glxGraphicsStateGuardian::
502 do_get_extension_func(const char *name) {
503  nassertr(name != nullptr, nullptr);
504 
505  if (glx_get_proc_address) {
506  LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
507 
508  // First, check if we have glXGetProcAddress available. This will be
509  // superior if we can get it.
510 
511 #if defined(LINK_IN_GLXGETPROCADDRESS) && defined(HAVE_GLXGETPROCADDRESS)
512  // If we are confident the system headers defined it, we can call it
513  // directly. This is more reliable than trying to determine its address
514  // dynamically, but it may make libpandagl.so fail to load if the symbol
515  // isn't in the runtime library.
516  return (void *)glXGetProcAddress((const GLubyte *)name);
517 
518 #elif defined(LINK_IN_GLXGETPROCADDRESS) && defined(HAVE_GLXGETPROCADDRESSARB)
519  // The ARB extension version is OK too. Sometimes the prototype isn't
520  // supplied for some reason.
521  return (void *)glXGetProcAddressARB((const GLubyte *)name);
522 
523 #else
524  // Otherwise, we have to fiddle around with the dynamic runtime.
525 
526  if (!_checked_get_proc_address) {
527  const char *funcName = nullptr;
528 
529  if (glx_is_at_least_version(1, 4)) {
530  funcName = "glXGetProcAddress";
531 
532  } else if (has_extension("GLX_ARB_get_proc_address")) {
533  funcName = "glXGetProcAddressARB";
534  }
535 
536  if (funcName != nullptr) {
537  _glXGetProcAddress = (PFNGLXGETPROCADDRESSPROC)get_system_func(funcName);
538  if (_glXGetProcAddress == nullptr) {
539  glxdisplay_cat.warning()
540  << "Couldn't load function " << funcName
541  << ", GL extensions may be unavailable.\n";
542  }
543  }
544 
545  _checked_get_proc_address = true;
546  }
547 
548  // Use glxGetProcAddress() if we've got it; it should be more robust.
549  if (_glXGetProcAddress != nullptr) {
550  return (void *)_glXGetProcAddress((const GLubyte *)name);
551  }
552 #endif // HAVE_GLXGETPROCADDRESS
553  }
554 
555  // Otherwise, fall back to the OS-provided calls.
556  return PosixGraphicsStateGuardian::do_get_extension_func(name);
557 }
558 
559 /**
560  * Queries the GLX extension pointers.
561  */
562 void glxGraphicsStateGuardian::
563 query_glx_extensions() {
564  _supports_swap_control = has_extension("GLX_SGI_swap_control");
565 
566  if (_supports_swap_control) {
567  _glXSwapIntervalSGI =
568  (PFNGLXSWAPINTERVALSGIPROC)get_extension_func("glXSwapIntervalSGI");
569  if (_glXSwapIntervalSGI == nullptr) {
570  glxdisplay_cat.error()
571  << "Driver claims to support GLX_SGI_swap_control extension, but does not define all functions.\n";
572  _supports_swap_control = false;
573  }
574  }
575 
576  if (_supports_swap_control) {
577  // Set the video-sync setting up front, if we have the extension that
578  // supports it.
579  _glXSwapIntervalSGI(sync_video ? 1 : 0);
580  }
581 
582  if (glx_support_fbconfig) {
583  if (glx_is_at_least_version(1, 3)) {
584  // If we have glx 1.3 or better, we have the FBConfig interface.
585  _supports_fbconfig = true;
586 
587  _glXChooseFBConfig =
588  (PFNGLXCHOOSEFBCONFIGPROC)get_extension_func("glXChooseFBConfig");
589  _glXCreateNewContext =
590  (PFNGLXCREATENEWCONTEXTPROC)get_extension_func("glXCreateNewContext");
591  _glXGetVisualFromFBConfig =
592  (PFNGLXGETVISUALFROMFBCONFIGPROC)get_extension_func("glXGetVisualFromFBConfig");
593  _glXGetFBConfigAttrib =
594  (PFNGLXGETFBCONFIGATTRIBPROC)get_extension_func("glXGetFBConfigAttrib");
595  _glXCreatePixmap =
596  (PFNGLXCREATEPIXMAPPROC)get_extension_func("glXCreatePixmap");
597 
598  if (_glXChooseFBConfig == nullptr ||
599  _glXCreateNewContext == nullptr ||
600  _glXGetVisualFromFBConfig == nullptr ||
601  _glXGetFBConfigAttrib == nullptr ||
602  _glXCreatePixmap == nullptr) {
603  glxdisplay_cat.error()
604  << "Driver claims to support GLX_fbconfig extension, but does not define all functions.\n";
605  _supports_fbconfig = false;
606  }
607  } else if (has_extension("GLX_SGIX_fbconfig")) {
608  // Or maybe we have the old SGIX extension for FBConfig. This is the
609  // same, but the function names are different--we just remap them to the
610  // same function pointers.
611  _supports_fbconfig = true;
612 
613  _glXChooseFBConfig =
614  (PFNGLXCHOOSEFBCONFIGPROC)get_extension_func("glXChooseFBConfigSGIX");
615  _glXCreateNewContext =
616  (PFNGLXCREATENEWCONTEXTPROC)get_extension_func("glXCreateContextWithConfigSGIX");
617  _glXGetVisualFromFBConfig =
618  (PFNGLXGETVISUALFROMFBCONFIGPROC)get_extension_func("glXGetVisualFromFBConfigSGIX");
619  _glXGetFBConfigAttrib =
620  (PFNGLXGETFBCONFIGATTRIBPROC)get_extension_func("glXGetFBConfigAttribSGIX");
621  _glXCreatePixmap =
622  (PFNGLXCREATEPIXMAPPROC)get_extension_func("glXCreateGLXPixmapWithConfigSGIX");
623 
624  if (_glXChooseFBConfig == nullptr ||
625  _glXCreateNewContext == nullptr ||
626  _glXGetVisualFromFBConfig == nullptr ||
627  _glXGetFBConfigAttrib == nullptr ||
628  _glXCreatePixmap == nullptr) {
629  glxdisplay_cat.error()
630  << "Driver claims to support GLX_SGIX_fbconfig extension, but does not define all functions.\n";
631  _supports_fbconfig = false;
632  }
633  }
634 
635  if (glx_is_at_least_version(1, 3)) {
636  // If we have glx 1.3 or better, we have the PBuffer interface.
637  _supports_pbuffer = true;
638  _uses_sgix_pbuffer = false;
639 
640  _glXCreatePbuffer =
641  (PFNGLXCREATEPBUFFERPROC)get_extension_func("glXCreatePbuffer");
642  _glXCreateGLXPbufferSGIX = nullptr;
643  _glXDestroyPbuffer =
644  (PFNGLXDESTROYPBUFFERPROC)get_extension_func("glXDestroyPbuffer");
645  if (_glXCreatePbuffer == nullptr ||
646  _glXDestroyPbuffer == nullptr) {
647  glxdisplay_cat.error()
648  << "Driver claims to support GLX_pbuffer extension, but does not define all functions.\n";
649  _supports_pbuffer = false;
650  }
651 
652  } else if (has_extension("GLX_SGIX_pbuffer")) {
653  // Or maybe we have the old SGIX extension for PBuffers.
654  _uses_sgix_pbuffer = true;
655 
656  // CreatePbuffer has a different form between SGIX and 1.3, however, so
657  // we must treat it specially. But we can use the same function pointer
658  // for DestroyPbuffer.
659  _glXCreatePbuffer = nullptr;
660  _glXCreateGLXPbufferSGIX =
661  (PFNGLXCREATEGLXPBUFFERSGIXPROC)get_extension_func("glXCreateGLXPbufferSGIX");
662  _glXDestroyPbuffer =
663  (PFNGLXDESTROYPBUFFERPROC)get_extension_func("glXDestroyGLXPbufferSGIX");
664  if (_glXCreateGLXPbufferSGIX == nullptr ||
665  _glXDestroyPbuffer == nullptr) {
666  glxdisplay_cat.error()
667  << "Driver claims to support GLX_SGIX_pbuffer extension, but does not define all functions.\n";
668  _supports_pbuffer = false;
669  }
670  }
671 
672  if (has_extension("GLX_ARB_create_context")) {
673  _glXCreateContextAttribs =
674  (PFNGLXCREATECONTEXTATTRIBSARBPROC)get_extension_func("glXCreateContextAttribsARB");
675  } else {
676  _glXCreateContextAttribs = nullptr;
677  }
678  }
679 
680  if (glxdisplay_cat.is_debug()) {
681  glxdisplay_cat.debug()
682  << "supports_swap_control = " << _supports_swap_control << "\n";
683  glxdisplay_cat.debug()
684  << "supports_fbconfig = " << _supports_fbconfig << "\n";
685  glxdisplay_cat.debug()
686  << "supports_pbuffer = " << _supports_pbuffer
687  << " sgix = " << _uses_sgix_pbuffer << "\n";
688  }
689 
690  // If "Mesa" is present, assume software. However, if "Mesa DRI" is found,
691  // it's actually a Mesa-based OpenGL layer running over a hardware driver.
692  if (_gl_renderer.find("Mesa") != string::npos &&
693  _gl_renderer.find("Mesa DRI") == string::npos) {
694  // It's Mesa, therefore probably a software context.
695  _fbprops.set_force_software(1);
696  _fbprops.set_force_hardware(0);
697  } else {
698  _fbprops.set_force_hardware(1);
699  _fbprops.set_force_software(0);
700  }
701 }
702 
703 /**
704  * Outputs the result of glxGetClientString() on the indicated tag.
705  */
706 void glxGraphicsStateGuardian::
707 show_glx_client_string(const string &name, int id) {
708  if (glgsg_cat.is_debug()) {
709  const char *text = glXGetClientString(_display, id);
710  if (text == nullptr) {
711  glgsg_cat.debug()
712  << "Unable to query " << name << " (client)\n";
713  } else {
714  glgsg_cat.debug()
715  << name << " (client) = " << (const char *)text << "\n";
716  }
717  }
718 }
719 
720 /**
721  * Outputs the result of glxQueryServerString() on the indicated tag.
722  */
723 void glxGraphicsStateGuardian::
724 show_glx_server_string(const string &name, int id) {
725  if (glgsg_cat.is_debug()) {
726  const char *text = glXQueryServerString(_display, _screen, id);
727  if (text == nullptr) {
728  glgsg_cat.debug()
729  << "Unable to query " << name << " (server)\n";
730  } else {
731  glgsg_cat.debug()
732  << name << " (server) = " << (const char *)text << "\n";
733  }
734  }
735 }
736 
737 /**
738  * Selects an XVisual for an initial OpenGL context. This may be called
739  * initially, to create the first context needed in order to create the
740  * fbconfig. On successful return, _visual and _temp_context will be filled
741  * in with a non-NULL value.
742  */
743 void glxGraphicsStateGuardian::
744 choose_temp_visual(const FrameBufferProperties &properties) {
745  nassertv(_temp_context == (GLXContext)nullptr);
746 
747  int best_quality = 0;
748  int best_result = 0;
749  FrameBufferProperties best_props;
750 
751  // Scan available visuals.
752  if (_visuals != nullptr) {
753  XFree(_visuals);
754  _visuals = nullptr;
755  }
756  int nvisuals = 0;
757  _visuals = XGetVisualInfo(_display, 0, nullptr, &nvisuals);
758  if (_visuals != nullptr) {
759  for (int i = 0; i < nvisuals; i++) {
760  FrameBufferProperties fbprops;
761  get_properties(fbprops, _visuals + i);
762  int quality = fbprops.get_quality(properties);
763  if (quality > best_quality) {
764  best_quality = quality;
765  best_result = i;
766  best_props = fbprops;
767  }
768  }
769  }
770 
771  if (best_quality > 0) {
772  _visual = _visuals + best_result;
773  _temp_context = glXCreateContext(_display, _visual, None, GL_TRUE);
774  if (_temp_context) {
775  _fbprops = best_props;
776  return;
777  }
778  }
779 
780  glxdisplay_cat.error()
781  << "Could not find a usable pixel format.\n";
782 }
783 
784 /**
785  * Initializes the context created in choose_temp_visual() by creating a
786  * temporary window and binding the context to that window.
787  */
788 void glxGraphicsStateGuardian::
789 init_temp_context() {
790  x11GraphicsPipe *x11_pipe;
791  DCAST_INTO_V(x11_pipe, get_pipe());
792  X11_Window root_window = x11_pipe->get_root();
793 
794  // Assume everyone uses TrueColor or DirectColor these days.
795  Visual *visual = _visual->visual;
796  nassertv(visual->c_class == DirectColor || visual->c_class == TrueColor);
797  _temp_colormap = XCreateColormap(_display, root_window,
798  visual, AllocNone);
799  XSetWindowAttributes wa;
800  wa.colormap = _temp_colormap;
801  unsigned long attrib_mask = CWColormap;
802 
803  _temp_xwindow = XCreateWindow
804  (_display, root_window, 0, 0, 100, 100,
805  0, _visual->depth, InputOutput,
806  visual, attrib_mask, &wa);
807  if (_temp_xwindow == (X11_Window)nullptr) {
808  glxdisplay_cat.error()
809  << "Could not create temporary window for context\n";
810  return;
811  }
812 
813  // Now use it to query the available GLX features.
814  glXMakeCurrent(_display, _temp_xwindow, _temp_context);
815  query_gl_version();
816  get_extra_extensions();
817  query_glx_extensions();
818 }
819 
820 /**
821  * Destroys the temporary unmapped window created by init_temp_context().
822  */
823 void glxGraphicsStateGuardian::
824 destroy_temp_xwindow() {
825  glXMakeCurrent(_display, None, nullptr);
826 
827  if (_temp_colormap != (Colormap)nullptr) {
828  XFreeColormap(_display, _temp_colormap);
829  _temp_colormap = (Colormap)nullptr;
830  }
831  if (_temp_xwindow != (X11_Window)nullptr) {
832  XDestroyWindow(_display, _temp_xwindow);
833  _temp_xwindow = (X11_Window)nullptr;
834  }
835 
836  if (_temp_context != (GLXContext)nullptr) {
837  glXDestroyContext(_display, _temp_context);
838  _temp_context = (GLXContext)nullptr;
839  }
840 }
This graphics pipe represents the interface for creating graphics windows on an X-based client.
A tiny specialization on GLGraphicsStateGuardian to add some glx-specific information.
void clear()
Unsets all properties that have been specified so far, and resets the FrameBufferProperties structure...
void choose_pixel_format(const FrameBufferProperties &properties, X11_Display *_display, int _screen, bool need_pbuffer, bool need_pixmap)
Selects a visual or fbconfig for all the windows and buffers that use this gsg.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
X11_Window get_root() const
Returns the handle to the root window on the pipe's display.
This GSG is used only for CallbackGraphicsWindow (which might not be using the glx interfaces),...
bool glx_is_at_least_version(int major_version, int minor_version) const
Returns true if the runtime GLX version number is at least the indicated value, false otherwise.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
void get_properties(FrameBufferProperties &properties, XVisualInfo *visual)
Gets the FrameBufferProperties to match the indicated visual.
Similar to MutexHolder, but for a light reentrant mutex.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void get_properties_advanced(FrameBufferProperties &properties, bool &context_has_pbuffer, bool &pixmap_supported, bool &slow, GLXFBConfig config)
Gets the FrameBufferProperties to match the indicated GLXFBConfig.
void set_rgba_bits(int r, int g, int b, int a)
Convenience method for setting the red, green, blue and alpha bits in one go.
This class is the main interface to controlling the render process.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
int get_quality(const FrameBufferProperties &reqs) const
Assumes that these properties are a description of a window.