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  int flags = 0;
356  if (gl_debug) {
357  flags |= GLX_CONTEXT_DEBUG_BIT_ARB;
358  }
359  if (gl_forward_compatible) {
360  flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
361  if (gl_version.get_num_words() == 0 || gl_version[0] < 2) {
362  glxdisplay_cat.error()
363  << "gl-forward-compatible requires gl-version >= 3 0\n";
364  }
365  }
366  if (flags != 0) {
367  attrib_list[n++] = GLX_CONTEXT_FLAGS_ARB;
368  attrib_list[n++] = flags;
369  }
370  attrib_list[n] = None;
371  _context = _glXCreateContextAttribs(_display, _fbconfig, _share_context,
372  GL_TRUE, attrib_list);
373  } else {
374  _context =
375  _glXCreateNewContext(_display, _fbconfig, render_type, _share_context,
376  GL_TRUE);
377  }
378 
379  if (_context) {
380  mark_new();
381 
382  if (_visuals != nullptr) {
383  XFree(_visuals);
384  _visuals = nullptr;
385  }
386  _visuals = _glXGetVisualFromFBConfig(_display, _fbconfig);
387  _visual = _visuals;
388 
389  if (_visual) {
390  get_properties_advanced(_fbprops, _context_has_pbuffer, _context_has_pixmap,
391  _slow, _fbconfig);
392 
393  if (!properties.get_srgb_color()) {
394  _fbprops.set_srgb_color(false);
395  }
396 
397  if (glxdisplay_cat.is_debug()) {
398  glxdisplay_cat.debug()
399  << "Selected context " << best_result << ": " << _fbprops << "\n";
400  glxdisplay_cat.debug()
401  << "context_has_pbuffer = " << _context_has_pbuffer
402  << ", context_has_pixmap = " << _context_has_pixmap << "\n";
403  }
404 
405  return;
406  }
407  }
408  // This really shouldn't happen, so I'm not too careful about cleanup.
409  glxdisplay_cat.error()
410  << "Could not create FBConfig context!\n";
411  _fbconfig = nullptr;
412  _context = nullptr;
413  _visual = nullptr;
414  _visuals = nullptr;
415  }
416 
417  glxdisplay_cat.warning()
418  << "No suitable FBConfig contexts available; using XVisual only.\n"
419  << _fbprops << "\n";
420 
421  _context = _temp_context;
422  _temp_context = (GLXContext)nullptr;
423 
424  // By convention, every indirect XVisual that can render to a window can
425  // also render to a GLXPixmap. Direct visuals we're not as sure about.
426  _context_has_pixmap = !glXIsDirect(_display, _context);
427 
428  // Pbuffers aren't supported at all with the XVisual interface.
429  _context_has_pbuffer = false;
430 }
431 
432 /**
433  * Returns true if the runtime GLX version number is at least the indicated
434  * value, false otherwise.
435  */
437 glx_is_at_least_version(int major_version, int minor_version) const {
438  if (_glx_version_major < major_version) {
439  return false;
440  }
441  if (_glx_version_major > major_version) {
442  return true;
443  }
444  if (_glx_version_minor < minor_version) {
445  return false;
446  }
447  return true;
448 }
449 
450 /**
451  * Calls glFlush().
452  */
453 void glxGraphicsStateGuardian::
454 gl_flush() const {
455  // This call requires synchronization with X.
456  LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
457  PosixGraphicsStateGuardian::gl_flush();
458 }
459 
460 /**
461  * Returns the result of glGetError().
462  */
463 GLenum glxGraphicsStateGuardian::
464 gl_get_error() const {
465  // This call requires synchronization with X.
466  LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
467  return PosixGraphicsStateGuardian::gl_get_error();
468 }
469 
470 /**
471  * Queries the runtime version of OpenGL in use.
472  */
473 void glxGraphicsStateGuardian::
474 query_gl_version() {
475  LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
476  PosixGraphicsStateGuardian::query_gl_version();
477 
478  show_glx_client_string("GLX_VENDOR", GLX_VENDOR);
479  show_glx_client_string("GLX_VERSION", GLX_VERSION);
480  show_glx_server_string("GLX_VENDOR", GLX_VENDOR);
481  show_glx_server_string("GLX_VERSION", GLX_VERSION);
482 
483  glXQueryVersion(_display, &_glx_version_major, &_glx_version_minor);
484 
485  // We output to glgsg_cat instead of glxdisplay_cat, since this is where the
486  // GL version has been output, and it's nice to see the two of these
487  // together.
488  if (glgsg_cat.is_debug()) {
489  glgsg_cat.debug()
490  << "GLX_VERSION = " << _glx_version_major << "." << _glx_version_minor
491  << "\n";
492  }
493 }
494 
495 /**
496  * This may be redefined by a derived class (e.g. glx or wgl) to get whatever
497  * further extensions strings may be appropriate to that interface, in
498  * addition to the GL extension strings return by glGetString().
499  */
500 void glxGraphicsStateGuardian::
501 get_extra_extensions() {
502  LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
503  save_extensions(glXQueryExtensionsString(_display, _screen));
504 }
505 
506 /**
507  * Returns the pointer to the GL extension function with the indicated name.
508  * It is the responsibility of the caller to ensure that the required
509  * extension is defined in the OpenGL runtime prior to calling this; it is an
510  * error to call this for a function that is not defined.
511  */
512 void *glxGraphicsStateGuardian::
513 do_get_extension_func(const char *name) {
514  nassertr(name != nullptr, nullptr);
515 
516  if (glx_get_proc_address) {
517  LightReMutexHolder holder(glxGraphicsPipe::_x_mutex);
518 
519  // First, check if we have glXGetProcAddress available. This will be
520  // superior if we can get it.
521 
522 #if defined(LINK_IN_GLXGETPROCADDRESS) && defined(HAVE_GLXGETPROCADDRESS)
523  // If we are confident the system headers defined it, we can call it
524  // directly. This is more reliable than trying to determine its address
525  // dynamically, but it may make libpandagl.so fail to load if the symbol
526  // isn't in the runtime library.
527  return (void *)glXGetProcAddress((const GLubyte *)name);
528 
529 #elif defined(LINK_IN_GLXGETPROCADDRESS) && defined(HAVE_GLXGETPROCADDRESSARB)
530  // The ARB extension version is OK too. Sometimes the prototype isn't
531  // supplied for some reason.
532  return (void *)glXGetProcAddressARB((const GLubyte *)name);
533 
534 #else
535  // Otherwise, we have to fiddle around with the dynamic runtime.
536 
537  if (!_checked_get_proc_address) {
538  const char *funcName = nullptr;
539 
540  if (glx_is_at_least_version(1, 4)) {
541  funcName = "glXGetProcAddress";
542 
543  } else if (has_extension("GLX_ARB_get_proc_address")) {
544  funcName = "glXGetProcAddressARB";
545  }
546 
547  if (funcName != nullptr) {
548  _glXGetProcAddress = (PFNGLXGETPROCADDRESSPROC)get_system_func(funcName);
549  if (_glXGetProcAddress == nullptr) {
550  glxdisplay_cat.warning()
551  << "Couldn't load function " << funcName
552  << ", GL extensions may be unavailable.\n";
553  }
554  }
555 
556  _checked_get_proc_address = true;
557  }
558 
559  // Use glxGetProcAddress() if we've got it; it should be more robust.
560  if (_glXGetProcAddress != nullptr) {
561  return (void *)_glXGetProcAddress((const GLubyte *)name);
562  }
563 #endif // HAVE_GLXGETPROCADDRESS
564  }
565 
566  // Otherwise, fall back to the OS-provided calls.
567  return PosixGraphicsStateGuardian::do_get_extension_func(name);
568 }
569 
570 /**
571  * Queries the GLX extension pointers.
572  */
573 void glxGraphicsStateGuardian::
574 query_glx_extensions() {
575  _supports_swap_control = has_extension("GLX_SGI_swap_control");
576 
577  if (_supports_swap_control) {
578  _glXSwapIntervalSGI =
579  (PFNGLXSWAPINTERVALSGIPROC)get_extension_func("glXSwapIntervalSGI");
580  if (_glXSwapIntervalSGI == nullptr) {
581  glxdisplay_cat.error()
582  << "Driver claims to support GLX_SGI_swap_control extension, but does not define all functions.\n";
583  _supports_swap_control = false;
584  }
585  }
586 
587  if (_supports_swap_control) {
588  // Set the video-sync setting up front, if we have the extension that
589  // supports it.
590  _glXSwapIntervalSGI(sync_video ? 1 : 0);
591  }
592 
593  if (glx_support_fbconfig) {
594  if (glx_is_at_least_version(1, 3)) {
595  // If we have glx 1.3 or better, we have the FBConfig interface.
596  _supports_fbconfig = true;
597 
598  _glXChooseFBConfig =
599  (PFNGLXCHOOSEFBCONFIGPROC)get_extension_func("glXChooseFBConfig");
600  _glXCreateNewContext =
601  (PFNGLXCREATENEWCONTEXTPROC)get_extension_func("glXCreateNewContext");
602  _glXGetVisualFromFBConfig =
603  (PFNGLXGETVISUALFROMFBCONFIGPROC)get_extension_func("glXGetVisualFromFBConfig");
604  _glXGetFBConfigAttrib =
605  (PFNGLXGETFBCONFIGATTRIBPROC)get_extension_func("glXGetFBConfigAttrib");
606  _glXCreatePixmap =
607  (PFNGLXCREATEPIXMAPPROC)get_extension_func("glXCreatePixmap");
608 
609  if (_glXChooseFBConfig == nullptr ||
610  _glXCreateNewContext == nullptr ||
611  _glXGetVisualFromFBConfig == nullptr ||
612  _glXGetFBConfigAttrib == nullptr ||
613  _glXCreatePixmap == nullptr) {
614  glxdisplay_cat.error()
615  << "Driver claims to support GLX_fbconfig extension, but does not define all functions.\n";
616  _supports_fbconfig = false;
617  }
618  } else if (has_extension("GLX_SGIX_fbconfig")) {
619  // Or maybe we have the old SGIX extension for FBConfig. This is the
620  // same, but the function names are different--we just remap them to the
621  // same function pointers.
622  _supports_fbconfig = true;
623 
624  _glXChooseFBConfig =
625  (PFNGLXCHOOSEFBCONFIGPROC)get_extension_func("glXChooseFBConfigSGIX");
626  _glXCreateNewContext =
627  (PFNGLXCREATENEWCONTEXTPROC)get_extension_func("glXCreateContextWithConfigSGIX");
628  _glXGetVisualFromFBConfig =
629  (PFNGLXGETVISUALFROMFBCONFIGPROC)get_extension_func("glXGetVisualFromFBConfigSGIX");
630  _glXGetFBConfigAttrib =
631  (PFNGLXGETFBCONFIGATTRIBPROC)get_extension_func("glXGetFBConfigAttribSGIX");
632  _glXCreatePixmap =
633  (PFNGLXCREATEPIXMAPPROC)get_extension_func("glXCreateGLXPixmapWithConfigSGIX");
634 
635  if (_glXChooseFBConfig == nullptr ||
636  _glXCreateNewContext == nullptr ||
637  _glXGetVisualFromFBConfig == nullptr ||
638  _glXGetFBConfigAttrib == nullptr ||
639  _glXCreatePixmap == nullptr) {
640  glxdisplay_cat.error()
641  << "Driver claims to support GLX_SGIX_fbconfig extension, but does not define all functions.\n";
642  _supports_fbconfig = false;
643  }
644  }
645 
646  if (glx_is_at_least_version(1, 3)) {
647  // If we have glx 1.3 or better, we have the PBuffer interface.
648  _supports_pbuffer = true;
649  _uses_sgix_pbuffer = false;
650 
651  _glXCreatePbuffer =
652  (PFNGLXCREATEPBUFFERPROC)get_extension_func("glXCreatePbuffer");
653  _glXCreateGLXPbufferSGIX = nullptr;
654  _glXDestroyPbuffer =
655  (PFNGLXDESTROYPBUFFERPROC)get_extension_func("glXDestroyPbuffer");
656  if (_glXCreatePbuffer == nullptr ||
657  _glXDestroyPbuffer == nullptr) {
658  glxdisplay_cat.error()
659  << "Driver claims to support GLX_pbuffer extension, but does not define all functions.\n";
660  _supports_pbuffer = false;
661  }
662 
663  } else if (has_extension("GLX_SGIX_pbuffer")) {
664  // Or maybe we have the old SGIX extension for PBuffers.
665  _uses_sgix_pbuffer = true;
666 
667  // CreatePbuffer has a different form between SGIX and 1.3, however, so
668  // we must treat it specially. But we can use the same function pointer
669  // for DestroyPbuffer.
670  _glXCreatePbuffer = nullptr;
671  _glXCreateGLXPbufferSGIX =
672  (PFNGLXCREATEGLXPBUFFERSGIXPROC)get_extension_func("glXCreateGLXPbufferSGIX");
673  _glXDestroyPbuffer =
674  (PFNGLXDESTROYPBUFFERPROC)get_extension_func("glXDestroyGLXPbufferSGIX");
675  if (_glXCreateGLXPbufferSGIX == nullptr ||
676  _glXDestroyPbuffer == nullptr) {
677  glxdisplay_cat.error()
678  << "Driver claims to support GLX_SGIX_pbuffer extension, but does not define all functions.\n";
679  _supports_pbuffer = false;
680  }
681  }
682 
683  if (has_extension("GLX_ARB_create_context")) {
684  _glXCreateContextAttribs =
685  (PFNGLXCREATECONTEXTATTRIBSARBPROC)get_extension_func("glXCreateContextAttribsARB");
686  } else {
687  _glXCreateContextAttribs = nullptr;
688  }
689  }
690 
691  if (glxdisplay_cat.is_debug()) {
692  glxdisplay_cat.debug()
693  << "supports_swap_control = " << _supports_swap_control << "\n";
694  glxdisplay_cat.debug()
695  << "supports_fbconfig = " << _supports_fbconfig << "\n";
696  glxdisplay_cat.debug()
697  << "supports_pbuffer = " << _supports_pbuffer
698  << " sgix = " << _uses_sgix_pbuffer << "\n";
699  }
700 
701  // If "Mesa" is present, assume software. However, if "Mesa DRI" is found,
702  // it's actually a Mesa-based OpenGL layer running over a hardware driver.
703  if (_gl_renderer.find("Mesa") != string::npos &&
704  _gl_renderer.find("Mesa DRI") == string::npos) {
705  // It's Mesa, therefore probably a software context.
706  _fbprops.set_force_software(1);
707  _fbprops.set_force_hardware(0);
708  } else {
709  _fbprops.set_force_hardware(1);
710  _fbprops.set_force_software(0);
711  }
712 }
713 
714 /**
715  * Outputs the result of glxGetClientString() on the indicated tag.
716  */
717 void glxGraphicsStateGuardian::
718 show_glx_client_string(const string &name, int id) {
719  if (glgsg_cat.is_debug()) {
720  const char *text = glXGetClientString(_display, id);
721  if (text == nullptr) {
722  glgsg_cat.debug()
723  << "Unable to query " << name << " (client)\n";
724  } else {
725  glgsg_cat.debug()
726  << name << " (client) = " << (const char *)text << "\n";
727  }
728  }
729 }
730 
731 /**
732  * Outputs the result of glxQueryServerString() on the indicated tag.
733  */
734 void glxGraphicsStateGuardian::
735 show_glx_server_string(const string &name, int id) {
736  if (glgsg_cat.is_debug()) {
737  const char *text = glXQueryServerString(_display, _screen, id);
738  if (text == nullptr) {
739  glgsg_cat.debug()
740  << "Unable to query " << name << " (server)\n";
741  } else {
742  glgsg_cat.debug()
743  << name << " (server) = " << (const char *)text << "\n";
744  }
745  }
746 }
747 
748 /**
749  * Selects an XVisual for an initial OpenGL context. This may be called
750  * initially, to create the first context needed in order to create the
751  * fbconfig. On successful return, _visual and _temp_context will be filled
752  * in with a non-NULL value.
753  */
754 void glxGraphicsStateGuardian::
755 choose_temp_visual(const FrameBufferProperties &properties) {
756  nassertv(_temp_context == (GLXContext)nullptr);
757 
758  int best_quality = 0;
759  int best_result = 0;
760  FrameBufferProperties best_props;
761 
762  // Scan available visuals.
763  if (_visuals != nullptr) {
764  XFree(_visuals);
765  _visuals = nullptr;
766  }
767  int nvisuals = 0;
768  _visuals = XGetVisualInfo(_display, 0, nullptr, &nvisuals);
769  if (_visuals != nullptr) {
770  for (int i = 0; i < nvisuals; i++) {
771  FrameBufferProperties fbprops;
772  get_properties(fbprops, _visuals + i);
773  int quality = fbprops.get_quality(properties);
774  if (quality > best_quality) {
775  best_quality = quality;
776  best_result = i;
777  best_props = fbprops;
778  }
779  }
780  }
781 
782  if (best_quality > 0) {
783  _visual = _visuals + best_result;
784  _temp_context = glXCreateContext(_display, _visual, None, GL_TRUE);
785  if (_temp_context) {
786  _fbprops = best_props;
787  return;
788  }
789  }
790 
791  glxdisplay_cat.error()
792  << "Could not find a usable pixel format.\n";
793 }
794 
795 /**
796  * Initializes the context created in choose_temp_visual() by creating a
797  * temporary window and binding the context to that window.
798  */
799 void glxGraphicsStateGuardian::
800 init_temp_context() {
801  x11GraphicsPipe *x11_pipe;
802  DCAST_INTO_V(x11_pipe, get_pipe());
803  X11_Window root_window = x11_pipe->get_root();
804 
805  // Assume everyone uses TrueColor or DirectColor these days.
806  Visual *visual = _visual->visual;
807  nassertv(visual->c_class == DirectColor || visual->c_class == TrueColor);
808  _temp_colormap = XCreateColormap(_display, root_window,
809  visual, AllocNone);
810  XSetWindowAttributes wa;
811  wa.colormap = _temp_colormap;
812  unsigned long attrib_mask = CWColormap;
813 
814  _temp_xwindow = XCreateWindow
815  (_display, root_window, 0, 0, 100, 100,
816  0, _visual->depth, InputOutput,
817  visual, attrib_mask, &wa);
818  if (_temp_xwindow == (X11_Window)nullptr) {
819  glxdisplay_cat.error()
820  << "Could not create temporary window for context\n";
821  return;
822  }
823 
824  // Now use it to query the available GLX features.
825  glXMakeCurrent(_display, _temp_xwindow, _temp_context);
826  query_gl_version();
827  get_extra_extensions();
828  query_glx_extensions();
829 }
830 
831 /**
832  * Destroys the temporary unmapped window created by init_temp_context().
833  */
834 void glxGraphicsStateGuardian::
835 destroy_temp_xwindow() {
836  glXMakeCurrent(_display, None, nullptr);
837 
838  if (_temp_colormap != (Colormap)nullptr) {
839  XFreeColormap(_display, _temp_colormap);
840  _temp_colormap = (Colormap)nullptr;
841  }
842  if (_temp_xwindow != (X11_Window)nullptr) {
843  XDestroyWindow(_display, _temp_xwindow);
844  _temp_xwindow = (X11_Window)nullptr;
845  }
846 
847  if (_temp_context != (GLXContext)nullptr) {
848  glXDestroyContext(_display, _temp_context);
849  _temp_context = (GLXContext)nullptr;
850  }
851 }
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
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.
void clear()
Unsets all properties that have been specified so far, and resets the FrameBufferProperties structure...
int get_quality(const FrameBufferProperties &reqs) const
Assumes that these properties are a description of a window.
This class is the main interface to controlling the render process.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
Similar to MutexHolder, but for a light reentrant mutex.
This GSG is used only for CallbackGraphicsWindow (which might not be using the glx interfaces),...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
A tiny specialization on GLGraphicsStateGuardian to add some glx-specific information.
void get_properties(FrameBufferProperties &properties, XVisualInfo *visual)
Gets the FrameBufferProperties to match the indicated visual.
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.
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.
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.
This graphics pipe represents the interface for creating graphics windows on an X-based client.
X11_Window get_root() const
Returns the handle to the root window on the pipe's display.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.