Просмотр исходного кода

more robust handling of glx version and extension functions w.r.t. pbuffers and pixmaps

David Rose 16 лет назад
Родитель
Сommit
4fd129413a

+ 2 - 2
panda/src/display/graphicsEngine.cxx

@@ -408,8 +408,8 @@ make_output(GraphicsPipe *pipe,
             << "  got: " << window->get_fb_properties() << "\n";
         }
       } else {
-        display_cat.error()
-          << "Window wouldn't open; abandoning window.\n";
+        display_cat.info()
+          << window->get_type() << " wouldn't open; abandoning.\n";
       }
 
       // No good; delete the window and keep trying.

+ 1 - 1
panda/src/glxdisplay/Sources.pp

@@ -20,7 +20,7 @@
     glxGraphicsWindow.h glxGraphicsWindow.cxx \
     glxGraphicsStateGuardian.h glxGraphicsStateGuardian.I \
     glxGraphicsStateGuardian.cxx \
-    glxext.h
+    panda_glxext.h
 
   #define INSTALL_HEADERS \
     glxGraphicsBuffer.I glxGraphicsBuffer.h \

+ 16 - 3
panda/src/glxdisplay/config_glxdisplay.cxx

@@ -16,6 +16,7 @@
 #include "glxGraphicsBuffer.h"
 #include "glxGraphicsPipe.h"
 #include "glxGraphicsPixmap.h"
+#include "glxGraphicsBuffer.h"
 #include "glxGraphicsWindow.h"
 #include "glxGraphicsStateGuardian.h"
 #include "graphicsPipeSelection.h"
@@ -43,6 +44,19 @@ ConfigVariableBool glx_get_os_address
           "addresses of extension functions.  This will be done only "
           "if glxGetProcAddress() cannot be used for some reason."));
 
+ConfigVariableBool gl_support_fbo
+("gl-support-fbo", true,
+ PRC_DESC("Configure this false if your GL's implementation of "
+          "EXT_framebuffer_object is broken.  The system might still be "
+          "able to create buffers using pbuffers or the like."));
+
+ConfigVariableBool glx_support_fbconfig
+("glx-support-fbconfig", true,
+ PRC_DESC("Set this true to enable the use of the advanced FBConfig "
+          "interface (as opposed to the older XVisual interface) "
+          "if it is available, to select a graphics visual and "
+          "create an OpenGL context."));
+
 ConfigVariableBool glx_support_pbuffer
 ("glx-support-pbuffer", true,
  PRC_DESC("Set this true to enable the use of X pbuffer-based offscreen "
@@ -53,9 +67,7 @@ ConfigVariableBool glx_support_pixmap
 ("glx-support-pixmap", false,
  PRC_DESC("Set this true to enable the use of X pixmap-based offscreen "
           "buffers.  This is false by default because pixmap-based buffers "
-          "are usually slower than pbuffer-based buffers, and because at "
-          "least one driver is known to crash (crash!) when it attempts "
-          "to create a pixmap-based buffer."));
+          "are usually slower than pbuffer-based buffers."));
 
 
 ////////////////////////////////////////////////////////////////////
@@ -79,6 +91,7 @@ init_libglxdisplay() {
 #endif  // HAVE_GLXFBCONFIG
   glxGraphicsPipe::init_type();
   glxGraphicsPixmap::init_type();
+  glxGraphicsBuffer::init_type();
   glxGraphicsWindow::init_type();
   glxGraphicsStateGuardian::init_type();
 

+ 2 - 0
panda/src/glxdisplay/config_glxdisplay.h

@@ -28,6 +28,8 @@ extern EXPCL_PANDAGL void init_libglxdisplay();
 extern ConfigVariableBool glx_get_proc_address;
 extern ConfigVariableBool glx_get_os_address;
 
+extern ConfigVariableBool gl_support_fbo;
+extern ConfigVariableBool glx_support_fbconfig;
 extern ConfigVariableBool glx_support_pbuffer;
 extern ConfigVariableBool glx_support_pixmap;
 

+ 41 - 39
panda/src/glxdisplay/glxGraphicsBuffer.cxx

@@ -21,10 +21,6 @@
 #include "glgsg.h"
 #include "pStatTimer.h"
 
-#ifdef HAVE_GLXFBCONFIG
-// This whole class doesn't make sense unless we have the GLXFBConfig
-// and associated GLXPbuffer interfaces available.
-
 TypeHandle glxGraphicsBuffer::_type_handle;
 
 ////////////////////////////////////////////////////////////////////
@@ -76,7 +72,8 @@ begin_frame(FrameMode mode, Thread *current_thread) {
   PStatTimer timer(_make_current_pcollector, current_thread);
 
   begin_frame_spam(mode);
-  if (_gsg == (GraphicsStateGuardian *)NULL) {
+  if (_gsg == (GraphicsStateGuardian *)NULL ||
+      _pbuffer == None) {
     return false;
   }
 
@@ -140,15 +137,19 @@ void glxGraphicsBuffer::
 close_buffer() {
   if (_gsg != (GraphicsStateGuardian *)NULL) {
     glXMakeCurrent(_display, None, NULL);
+
+    if (_pbuffer != None) {
+      glxGraphicsStateGuardian *glxgsg;
+      DCAST_INTO_V(glxgsg, _gsg);
+      glxgsg->_glXDestroyPbuffer(_display, _pbuffer);
+      _pbuffer = None;
+    }
+    
     _gsg.clear();
     _active = false;
   }
 
-  if (_pbuffer != None) {
-    glXDestroyPbuffer(_display, _pbuffer);
-    _pbuffer = None;
-  }
-
+  _pbuffer = None;
   _is_valid = false;
 }
 
@@ -175,43 +176,47 @@ open_buffer() {
     // If the old gsg has the wrong pixel format, create a
     // new one that shares with the old gsg.
     DCAST_INTO_R(glxgsg, _gsg, false);
-    if (!glxgsg->get_fb_properties().subsumes(_fb_properties)) {
+
+    if (!glxgsg->_context_has_pbuffer || 
+        !glxgsg->get_fb_properties().subsumes(_fb_properties)) {
+      // We need a new pixel format, and hence a new GSG.
       glxgsg = new glxGraphicsStateGuardian(_engine, _pipe, glxgsg);
       glxgsg->choose_pixel_format(_fb_properties, glx_pipe->get_display(), glx_pipe->get_screen(), true, false);
       _gsg = glxgsg;
     }
   }
   
-  if (glxgsg->_fbconfig == None) {
-    // If we didn't use an fbconfig to create the GSG, we can't create
-    // a PBuffer.
+  if (glxgsg->_fbconfig == None || glxgsg->_context_has_pbuffer) {
+    // If we didn't use an fbconfig to create the GSG, or it doesn't
+    // support buffers, we can't create a PBuffer.
     return false;
   }
 
+  nassertr(glxgsg->_supports_pbuffer, false);
+
   static const int max_attrib_list = 32;
   int attrib_list[max_attrib_list];
-  int n=0;
-
-#ifdef HAVE_OFFICIAL_GLXFBCONFIG
-  // The official GLX 1.3 version passes in the size in the attrib
-  // list.
-  attrib_list[n++] = GLX_PBUFFER_WIDTH;
-  attrib_list[n++] = _x_size;
-  attrib_list[n++] = GLX_PBUFFER_HEIGHT;
-  attrib_list[n++] = _y_size;
-
-  nassertr(n < max_attrib_list, false);
-  attrib_list[n] = (int)None;
-  _pbuffer = glXCreatePbuffer(glxgsg->_display, glxgsg->_fbconfig,
-                              attrib_list);
-
-#else
-  // The SGI version passed in the size in the parameter list.
-  nassertr(n < max_attrib_list, false);
-  attrib_list[n] = (int)None;
-  _pbuffer = glXCreateGLXPbufferSGIX(glxgsg->_display, glxgsg->_fbconfig,
-                                     _x_size, _y_size, attrib_list);
-#endif
+  int n = 0;
+
+  if (glxgsg->_uses_sgix_pbuffer) {
+    // The SGI version passed in the size in the parameter list.
+    nassertr(n < max_attrib_list, false);
+    attrib_list[n] = (int)None;
+    _pbuffer = glxgsg->_glXCreateGLXPbufferSGIX(glxgsg->_display, glxgsg->_fbconfig,
+                                                _x_size, _y_size, attrib_list);
+  } else {
+    // The official GLX 1.3 version passes in the size in the attrib
+    // list.
+    attrib_list[n++] = GLX_PBUFFER_WIDTH;
+    attrib_list[n++] = _x_size;
+    attrib_list[n++] = GLX_PBUFFER_HEIGHT;
+    attrib_list[n++] = _y_size;
+    
+    nassertr(n < max_attrib_list, false);
+    attrib_list[n] = (int)None;
+    _pbuffer = glxgsg->_glXCreatePbuffer(glxgsg->_display, glxgsg->_fbconfig,
+                                         attrib_list);
+  }
 
   if (_pbuffer == None) {
     glxdisplay_cat.error()
@@ -235,6 +240,3 @@ open_buffer() {
   _is_valid = true;
   return true;
 }
-
-
-#endif  // HAVE_GLXFBCONFIG

+ 0 - 6
panda/src/glxdisplay/glxGraphicsBuffer.h

@@ -20,10 +20,6 @@
 #include "glxGraphicsPipe.h"
 #include "graphicsBuffer.h"
 
-#ifdef HAVE_GLXFBCONFIG
-// This whole class doesn't make sense unless we have the GLXFBConfig
-// and associated GLXPbuffer interfaces available.
-
 ////////////////////////////////////////////////////////////////////
 //       Class : glxGraphicsBuffer
 // Description : An offscreen buffer in the GLX environment.  This
@@ -71,6 +67,4 @@ private:
 
 #include "glxGraphicsBuffer.I"
 
-#endif  // HAVE_GLXFBCONFIG
-
 #endif

+ 23 - 24
panda/src/glxdisplay/glxGraphicsPipe.cxx

@@ -119,7 +119,7 @@ make_output(const string &name,
 
   if (retry == 1) {
     if ((host==0)||
-  //        (!gl_support_fbo)||
+        (!gl_support_fbo)||
         ((flags&BF_require_parasite)!=0)||
         ((flags&BF_require_window)!=0)) {
       return NULL;
@@ -152,34 +152,33 @@ make_output(const string &name,
                                 flags, gsg, host);
   }
 
-#ifdef HAVE_GLXFBCONFIG
   // Third thing to try: a glxGraphicsBuffer
-  
-  if (retry == 2) {
-    if (!glx_support_pbuffer) {
-      return NULL;
-    }
-
-    if (((flags&BF_require_parasite)!=0)||
-        ((flags&BF_require_window)!=0)||
-        ((flags&BF_resizeable)!=0)||
-        ((flags&BF_size_track_host)!=0)) {
-      return NULL;
-    }
-
-    if (!support_rtt) {
-      if (((flags&BF_rtt_cumulative)!=0)||
-          ((flags&BF_can_bind_every)!=0)) {
-        // If we require Render-to-Texture, but can't be sure we
-        // support it, bail.
+  if (glxgsg == NULL || glxgsg->_supports_fbconfig) {
+    if (retry == 2) {
+      if (!glx_support_pbuffer) {
+        return NULL;
+      }
+      
+      if (((flags&BF_require_parasite)!=0)||
+          ((flags&BF_require_window)!=0)||
+          ((flags&BF_resizeable)!=0)||
+          ((flags&BF_size_track_host)!=0)) {
         return NULL;
       }
+      
+      if (!support_rtt) {
+        if (((flags&BF_rtt_cumulative)!=0)||
+            ((flags&BF_can_bind_every)!=0)) {
+          // If we require Render-to-Texture, but can't be sure we
+          // support it, bail.
+          return NULL;
+        }
+      }
+      
+      return new glxGraphicsBuffer(engine, this, name, fb_prop, win_prop,
+                                   flags, gsg, host);
     }
-
-    return new glxGraphicsBuffer(engine, this, name, fb_prop, win_prop,
-                                 flags, gsg, host);
   }
-#endif  // HAVE_GLXFBCONFIG
 
   // Third thing to try: a glxGraphicsPixmap.
   if (retry == 3) {

+ 8 - 1
panda/src/glxdisplay/glxGraphicsPipe.h

@@ -27,20 +27,25 @@ class FrameBufferProperties;
 
 #ifndef CPPPARSER
 
+// Don't pick up the system glxext.h; use our own, which is better.
+#define __glxext_h_
+
 #include <GL/glx.h>
 
+/*
 #if defined(GLX_VERSION_1_3)
   // If the system glx version is at least 1.3, then we know we have
   // GLXFBConfig and GLXPbuffer.
   #define HAVE_GLXFBCONFIG
   #define HAVE_OFFICIAL_GLXFBCONFIG
 #endif
+*/
 
 // This must be included after we have included glgsg.h (which
 // includes gl.h), and after we have checked GLX_VERSION_1_3.  But we
 // must also include it before we redefine the GLXFBConfig types,
 // below.
-#include "glxext.h"
+#include "panda_glxext.h"
 
 // drose: the version of GL/glx.h that ships with Fedora Core 2 seems
 // to define GLX_VERSION_1_4, but for some reason does not define
@@ -54,6 +59,7 @@ class FrameBufferProperties;
 #endif
 
 
+/*
 #if !defined(HAVE_GLXFBCONFIG) && defined(GLX_SGIX_fbconfig) && defined(GLX_SGIX_pbuffer)
   // If the system glx version isn't 1.3, but these were defined as
   // extensions, we can work with that.
@@ -68,6 +74,7 @@ class FrameBufferProperties;
   #define HAVE_GLXFBCONFIG
   #define HAVE_SGI_GLXFBCONFIG
 #endif
+*/
 
 #endif  // CPPPARSER
 

+ 14 - 10
panda/src/glxdisplay/glxGraphicsPixmap.cxx

@@ -75,7 +75,8 @@ begin_frame(FrameMode mode, Thread *current_thread) {
   PStatTimer timer(_make_current_pcollector, current_thread);
 
   begin_frame_spam(mode);
-  if (_gsg == (GraphicsStateGuardian *)NULL) {
+  if (_gsg == (GraphicsStateGuardian *)NULL ||
+      _glx_pixmap == None) {
     return false;
   }
 
@@ -179,13 +180,19 @@ open_buffer() {
     // If the old gsg has the wrong pixel format, create a
     // new one that shares with the old gsg.
     DCAST_INTO_R(glxgsg, _gsg, false);
-    if (!glxgsg->get_fb_properties().subsumes(_fb_properties)) {
+    if (!glxgsg->_context_has_pixmap || 
+        !glxgsg->get_fb_properties().subsumes(_fb_properties)) {
       glxgsg = new glxGraphicsStateGuardian(_engine, _pipe, glxgsg);
       glxgsg->choose_pixel_format(_fb_properties, _display, glx_pipe->get_screen(), false, true);
       _gsg = glxgsg;
     }
   }
 
+  if (!glxgsg->_context_has_pixmap) {
+    // Hmm, the GSG we created won't work.
+    return false;
+  }
+
   XVisualInfo *visual_info = glxgsg->_visual;
   if (visual_info == NULL) {
     // No X visual for this fbconfig; how can we create the pixmap?
@@ -214,16 +221,13 @@ open_buffer() {
     return false;
   }
 
-#ifdef HAVE_GLXFBCONFIG
   if (glxgsg->_fbconfig) {
     // Use the FBConfig to create the pixmap.
-    _glx_pixmap = glXCreatePixmap(_display, glxgsg->_fbconfig, _x_pixmap, NULL);
-  } else
-#endif  // HAVE_GLXFBCONFIG
-    {
-      // Use the XVisual to create the pixmap.
-      _glx_pixmap = glXCreateGLXPixmap(_display, visual_info, _x_pixmap);
-    }
+    _glx_pixmap = glxgsg->_glXCreatePixmap(_display, glxgsg->_fbconfig, _x_pixmap, NULL);
+  } else {
+    // Use the XVisual to create the pixmap.
+    _glx_pixmap = glXCreateGLXPixmap(_display, visual_info, _x_pixmap);
+  }
 
   if (_glx_pixmap == None) {
     glxdisplay_cat.error()

+ 351 - 124
panda/src/glxdisplay/glxGraphicsStateGuardian.cxx

@@ -39,6 +39,14 @@ glxGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
   _visual=0;
   _visuals=0;
   _fbconfig=0;
+  _context_has_pbuffer = false;
+  _context_has_pixmap = false;
+  _slow = false;
+
+  _supports_swap_control = false;
+  _supports_fbconfig = false;
+  _supports_pbuffer = false;
+  _uses_sgix_pbuffer = false;
   
   if (share_with != (glxGraphicsStateGuardian *)NULL) {
     _prepared_objects = share_with->get_prepared_objects();
@@ -48,6 +56,7 @@ glxGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
   _libgl_handle = NULL;
   _checked_get_proc_address = false;
   _glXGetProcAddress = NULL;
+  _temp_xwindow = (Window)NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -57,6 +66,7 @@ glxGraphicsStateGuardian(GraphicsEngine *engine, GraphicsPipe *pipe,
 ////////////////////////////////////////////////////////////////////
 glxGraphicsStateGuardian::
 ~glxGraphicsStateGuardian() {
+  destroy_temp_xwindow();
   if (_visuals != (XVisualInfo *)NULL) {
     XFree(_visuals);
   }
@@ -71,7 +81,7 @@ glxGraphicsStateGuardian::
 
 ////////////////////////////////////////////////////////////////////
 //     Function: glxGraphicsStateGuardian::get_properties
-//       Access: Private
+//       Access: Public
 //  Description: Gets the FrameBufferProperties to match the
 //               indicated visual.
 ////////////////////////////////////////////////////////////////////
@@ -131,87 +141,86 @@ get_properties(FrameBufferProperties &properties, XVisualInfo *visual) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: glxGraphicsStateGuardian::get_properties_advanced
-//       Access: Private
+//       Access: Public
 //  Description: Gets the FrameBufferProperties to match the
 //               indicated GLXFBConfig
 ////////////////////////////////////////////////////////////////////
 void glxGraphicsStateGuardian::
 get_properties_advanced(FrameBufferProperties &properties, 
-                        bool &pbuffer_supported, bool &pixmap_supported,
-                        bool &slow, fbconfig config) {
-
+                        bool &context_has_pbuffer, bool &context_has_pixmap,
+                        bool &slow, GLXFBConfig config) {
   properties.clear();
 
-#ifdef HAVE_GLXFBCONFIG
-  // Now update our framebuffer_mode and bit depth appropriately.
-  int render_mode, double_buffer, stereo, red_size, green_size, blue_size,
-    alpha_size, ared_size, agreen_size, ablue_size, aalpha_size,
-    depth_size, stencil_size, samples, drawable_type, caveat;
-  
-  glXGetFBConfigAttrib(_display, config, GLX_RGBA, &render_mode);
-  glXGetFBConfigAttrib(_display, config, GLX_DOUBLEBUFFER, &double_buffer);
-  glXGetFBConfigAttrib(_display, config, GLX_STEREO, &stereo);
-  glXGetFBConfigAttrib(_display, config, GLX_RED_SIZE, &red_size);
-  glXGetFBConfigAttrib(_display, config, GLX_GREEN_SIZE, &green_size);
-  glXGetFBConfigAttrib(_display, config, GLX_BLUE_SIZE, &blue_size);
-  glXGetFBConfigAttrib(_display, config, GLX_ALPHA_SIZE, &alpha_size);
-  glXGetFBConfigAttrib(_display, config, GLX_ACCUM_RED_SIZE, &ared_size);
-  glXGetFBConfigAttrib(_display, config, GLX_ACCUM_GREEN_SIZE, &agreen_size);
-  glXGetFBConfigAttrib(_display, config, GLX_ACCUM_BLUE_SIZE, &ablue_size);
-  glXGetFBConfigAttrib(_display, config, GLX_ACCUM_ALPHA_SIZE, &aalpha_size);
-  glXGetFBConfigAttrib(_display, config, GLX_DEPTH_SIZE, &depth_size);
-  glXGetFBConfigAttrib(_display, config, GLX_STENCIL_SIZE, &stencil_size);
-  glXGetFBConfigAttrib(_display, config, GLX_SAMPLES, &samples);
-  glXGetFBConfigAttrib(_display, config, GLX_DRAWABLE_TYPE, &drawable_type);
-  glXGetFBConfigAttrib(_display, config, GLX_CONFIG_CAVEAT, &caveat);
-
-  pbuffer_supported = false;
-  if ((drawable_type & GLX_PBUFFER_BIT)!=0) {
-    pbuffer_supported = true;
-  }
-
-  pixmap_supported = false;
-  if ((drawable_type & GLX_PIXMAP_BIT)!=0) {
-    pixmap_supported = true;
-  }
-  
-  slow = false;
-  if (caveat == GLX_SLOW_CONFIG) {
-    slow = true;
-  }
-
-  if ((drawable_type & GLX_WINDOW_BIT)==0) {
-    // We insist on having a context that will support an onscreen window.
-    return;
-  }
-  
-  if (double_buffer) {
-    properties.set_back_buffers(1);
-  }
-  if (stereo) {
-    properties.set_stereo(1);
-  }
-  if (render_mode) {
-    properties.set_rgb_color(1);
-  } else {
-    properties.set_indexed_color(1);
+  if (_supports_fbconfig) {
+    // Now update our framebuffer_mode and bit depth appropriately.
+    int render_mode, double_buffer, stereo, red_size, green_size, blue_size,
+      alpha_size, ared_size, agreen_size, ablue_size, aalpha_size,
+      depth_size, stencil_size, samples, drawable_type, caveat;
+    
+    _glXGetFBConfigAttrib(_display, config, GLX_RGBA, &render_mode);
+    _glXGetFBConfigAttrib(_display, config, GLX_DOUBLEBUFFER, &double_buffer);
+    _glXGetFBConfigAttrib(_display, config, GLX_STEREO, &stereo);
+    _glXGetFBConfigAttrib(_display, config, GLX_RED_SIZE, &red_size);
+    _glXGetFBConfigAttrib(_display, config, GLX_GREEN_SIZE, &green_size);
+    _glXGetFBConfigAttrib(_display, config, GLX_BLUE_SIZE, &blue_size);
+    _glXGetFBConfigAttrib(_display, config, GLX_ALPHA_SIZE, &alpha_size);
+    _glXGetFBConfigAttrib(_display, config, GLX_ACCUM_RED_SIZE, &ared_size);
+    _glXGetFBConfigAttrib(_display, config, GLX_ACCUM_GREEN_SIZE, &agreen_size);
+    _glXGetFBConfigAttrib(_display, config, GLX_ACCUM_BLUE_SIZE, &ablue_size);
+    _glXGetFBConfigAttrib(_display, config, GLX_ACCUM_ALPHA_SIZE, &aalpha_size);
+    _glXGetFBConfigAttrib(_display, config, GLX_DEPTH_SIZE, &depth_size);
+    _glXGetFBConfigAttrib(_display, config, GLX_STENCIL_SIZE, &stencil_size);
+    _glXGetFBConfigAttrib(_display, config, GLX_SAMPLES, &samples);
+    _glXGetFBConfigAttrib(_display, config, GLX_DRAWABLE_TYPE, &drawable_type);
+    _glXGetFBConfigAttrib(_display, config, GLX_CONFIG_CAVEAT, &caveat);
+    
+    context_has_pbuffer = false;
+    if ((drawable_type & GLX_PBUFFER_BIT)!=0) {
+      context_has_pbuffer = true;
+    }
+    
+    context_has_pixmap = false;
+    if ((drawable_type & GLX_PIXMAP_BIT)!=0) {
+      context_has_pixmap = true;
+    }
+    
+    slow = false;
+    if (caveat == GLX_SLOW_CONFIG) {
+      slow = true;
+    }
+    
+    if ((drawable_type & GLX_WINDOW_BIT)==0) {
+      // We insist on having a context that will support an onscreen window.
+      return;
+    }
+    
+    if (double_buffer) {
+      properties.set_back_buffers(1);
+    }
+    if (stereo) {
+      properties.set_stereo(1);
+    }
+    if (render_mode) {
+      properties.set_rgb_color(1);
+    } else {
+      properties.set_indexed_color(1);
+    }
+    properties.set_color_bits(red_size+green_size+blue_size);
+    properties.set_stencil_bits(stencil_size);
+    properties.set_depth_bits(depth_size);
+    properties.set_alpha_bits(alpha_size);
+    properties.set_accum_bits(ared_size+agreen_size+ablue_size+aalpha_size);
+    properties.set_multisamples(samples);
+    
+    // Set both hardware and software bits, indicating not-yet-known.
+    properties.set_force_software(1);
+    properties.set_force_hardware(1);
   }
-  properties.set_color_bits(red_size+green_size+blue_size);
-  properties.set_stencil_bits(stencil_size);
-  properties.set_depth_bits(depth_size);
-  properties.set_alpha_bits(alpha_size);
-  properties.set_accum_bits(ared_size+agreen_size+ablue_size+aalpha_size);
-  properties.set_multisamples(samples);
-
-  // Set both hardware and software bits, indicating not-yet-known.
-  properties.set_force_software(1);
-  properties.set_force_hardware(1);
-#endif // HAVE_GLXFBCONFIG
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: glxGraphicsStateGuardian::choose_pixel_format
-//       Access: Private
+//       Access: Public
 //  Description: Selects a visual or fbconfig for all the windows
 //               and buffers that use this gsg.  Also creates the GL
 //               context and obtains the visual.
@@ -229,8 +238,51 @@ choose_pixel_format(const FrameBufferProperties &properties,
   _visuals = 0;
   _fbprops.clear();
 
-#ifdef HAVE_GLXFBCONFIG  
-  //// Choose best format available using GLXFBConfig
+  // First, attempt to create a context using the XVisual interface.
+  // We need this before we can query the FBConfig interface, because
+  // we need an OpenGL context to get the required extension function
+  // pointers.
+  choose_visual(properties);
+  if (_context == NULL) {
+    // No good.
+    return;
+  }
+
+  // Now we have to initialize the context so we can query its
+  // capabilities and extensions.  This also means creating a
+  // temporary window, so we have something to bind the context to and
+  // make it current.
+  init_temp_context();
+
+  if (!_supports_fbconfig) {
+    // We have a good OpenGL context, but it doesn't support the
+    // FBConfig interface, so we'll stop there.
+    if (glxdisplay_cat.is_debug()) {
+      glxdisplay_cat.debug()
+        <<" No FBConfig supported; using XVisual only.\n";
+
+      glxdisplay_cat.debug()
+        << _fbprops << "\n";
+
+      // By convention, every indirect XVisual that can render to a
+      // window can also render to a GLXPixmap.  Direct visuals we're
+      // not as sure about.
+      _context_has_pixmap = !glXIsDirect(_display, _context);
+
+      // Pbuffers aren't supported at all with the XVisual interface.
+      _context_has_pbuffer = false;
+    }
+    return;
+  }
+
+  // The OpenGL context supports the FBConfig interface, so we can use
+  // that more advanced interface to choose the actual window format
+  // we'll use.  FBConfig provides for more options than the older
+  // XVisual interface, so we'd much rather use it if it's available.
+
+  int best_quality = 0;
+  int best_result = 0;
+  FrameBufferProperties best_props;
 
   static const int max_attrib_list = 32;
   int attrib_list[max_attrib_list];
@@ -242,35 +294,38 @@ choose_pixel_format(const FrameBufferProperties &properties,
   attrib_list[n++] = GLX_DRAWABLE_TYPE;
   attrib_list[n++] = GLX_DONT_CARE;
   attrib_list[n] = (int)None;
-
+  
   int num_configs = 0;
   GLXFBConfig *configs =
-    glXChooseFBConfig(_display, _screen, attrib_list, &num_configs);
+    _glXChooseFBConfig(_display, _screen, attrib_list, &num_configs);
 
-  int best_quality = 0;
-  int best_result = 0;
-  FrameBufferProperties best_props;
+  // Now that we're done querying the first context, we don't need it
+  // anymore.  Clear it so we can create our actual OpenGL context.
+  glXDestroyContext(_display, _context);
+  _context = (GLXContext)NULL;
+  destroy_temp_xwindow();
   
   if (configs != 0) {
     for (int i = 0; i < num_configs; ++i) {
       FrameBufferProperties fbprops;
-      bool pbuffer_supported, pixmap_supported, slow;
-      get_properties_advanced(fbprops, pbuffer_supported, pixmap_supported,
+      bool context_has_pbuffer, context_has_pixmap, slow;
+      get_properties_advanced(fbprops, context_has_pbuffer, context_has_pixmap,
                               slow, configs[i]);
-      // We're not protecting this code by an is_debug() check, because if we do,
-      // some weird compiler bug appears and somehow makes the quality always 0.
-      const char *pbuffertext = pbuffer_supported ? " (pbuffer)" : "";
-      const char *pixmaptext = pixmap_supported ? " (pixmap)" : "";
+      // We're not protecting this code by an is_debug() check,
+      // because if we do, some weird compiler bug appears and somehow
+      // makes the quality always 0.
+      const char *pbuffertext = context_has_pbuffer ? " (pbuffer)" : "";
+      const char *pixmaptext = context_has_pixmap ? " (pixmap)" : "";
       const char *slowtext = slow ? " (slow)" : "";
       glxdisplay_cat.debug()
         << i << ": " << fbprops << pbuffertext << pixmaptext << slowtext << "\n";
       int quality = fbprops.get_quality(properties);
       if ((quality > 0)&&(slow)) quality -= 10000000;
-
-      if (need_pbuffer && !pbuffer_supported) {
+      
+      if (need_pbuffer && !context_has_pbuffer) {
         continue;
       }
-      if (need_pixmap && !pixmap_supported) {
+      if (need_pixmap && !context_has_pixmap) {
         continue;
       }
       
@@ -281,17 +336,31 @@ choose_pixel_format(const FrameBufferProperties &properties,
       }
     }
   }
-  
+
   if (best_quality > 0) {
     _fbconfig = configs[best_result];
     _context = 
-      glXCreateNewContext(_display, _fbconfig, GLX_RGBA_TYPE, _share_context,
-                          GL_TRUE);
+      _glXCreateNewContext(_display, _fbconfig, GLX_RGBA_TYPE, _share_context,
+                           GL_TRUE);
     if (_context) {
-      _visuals = glXGetVisualFromFBConfig(_display, _fbconfig);
+      _visuals = _glXGetVisualFromFBConfig(_display, _fbconfig);
       _visual = _visuals;
+
       if (_visual) {
-        _fbprops = best_props;
+        get_properties_advanced(_fbprops, _context_has_pbuffer, _context_has_pixmap,
+                                _slow, _fbconfig);
+
+        // hack?
+        //init_temp_context();
+
+        if (glxdisplay_cat.is_debug()) {
+          glxdisplay_cat.debug()
+            << "Selected context " << best_result << "\n";
+          glxdisplay_cat.debug()
+            << "context_has_pbuffer = " << _context_has_pbuffer
+            << ", context_has_pixmap = " << _context_has_pixmap << "\n";
+        }
+
         return;
       }
     }
@@ -303,40 +372,9 @@ choose_pixel_format(const FrameBufferProperties &properties,
     _visual = 0;
     _visuals = 0;
   }
-#endif // HAVE_GLXFBCONFIG
-  
-  if (need_pbuffer) {
-    // The xvisual interface cannot create pbuffers.
-    return;
-  }
 
-  // Scan available visuals.
-  int nvisuals=0;
-  _visuals = XGetVisualInfo(_display, 0, 0, &nvisuals);
-  if (_visuals != 0) {
-    for (int i=0; i<nvisuals; i++) {
-      FrameBufferProperties fbprops;
-      get_properties(fbprops, _visuals+i);
-      int quality = fbprops.get_quality(properties);
-      if (quality > best_quality) {
-        best_quality = quality;
-        best_result = i;
-        best_props = fbprops;
-      }
-    }
-  }
-  
-  if (best_quality > 0) {
-    _visual = _visuals+best_result;
-    _context = glXCreateContext(_display, _visual, None, GL_TRUE);    
-    if (_context) {
-      _fbprops = best_props;
-      return;
-    }
-  }
-
-  glxdisplay_cat.error() <<
-    "Could not find a usable pixel format.\n";
+  glxdisplay_cat.info()
+    << "No suitable FBConfig contexts available.\n";
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -367,6 +405,108 @@ reset() {
     _glXSwapIntervalSGI(sync_video ? 1 : 0);
   }
 
+  if (glx_support_fbconfig) {
+    if (glx_is_at_least_version(1, 3)) {
+      // If we have glx 1.3 or better, we have the FBConfig interface.
+      _supports_fbconfig = true;
+      
+      _glXChooseFBConfig = 
+        (PFNGLXCHOOSEFBCONFIGPROC)get_extension_func("glX", "ChooseFBConfig");
+      _glXCreateNewContext = 
+        (PFNGLXCREATENEWCONTEXTPROC)get_extension_func("glX", "CreateNewContext");
+      _glXGetVisualFromFBConfig = 
+        (PFNGLXGETVISUALFROMFBCONFIGPROC)get_extension_func("glX", "GetVisualFromFBConfig");
+      _glXGetFBConfigAttrib = 
+        (PFNGLXGETFBCONFIGATTRIBPROC)get_extension_func("glX", "GetFBConfigAttrib");
+      _glXCreatePixmap = 
+        (PFNGLXCREATEPIXMAPPROC)get_extension_func("glX", "CreatePixmap");
+      
+      if (_glXChooseFBConfig == NULL ||
+          _glXCreateNewContext == NULL ||
+          _glXGetVisualFromFBConfig == NULL ||
+          _glXGetFBConfigAttrib == NULL ||
+          _glXCreatePixmap == NULL) {
+        glxdisplay_cat.error()
+          << "Driver claims to support GLX_fbconfig extension, but does not define all functions.\n";
+        _supports_fbconfig = false;
+      }
+    } else if (has_extension("GLX_SGIX_fbconfig")) {
+      // Or maybe we have the old SGIX extension for FBConfig.  This is
+      // the same, but the function names are different--we just remap
+      // them to the same function pointers.
+      _supports_fbconfig = true;
+      
+      _glXChooseFBConfig = 
+        (PFNGLXCHOOSEFBCONFIGPROC)get_extension_func("glX", "ChooseFBConfigSGIX");
+      _glXCreateNewContext = 
+        (PFNGLXCREATENEWCONTEXTPROC)get_extension_func("glX", "CreateContextWithConfigSGIX");
+      _glXGetVisualFromFBConfig = 
+        (PFNGLXGETVISUALFROMFBCONFIGPROC)get_extension_func("glX", "GetVisualFromFBConfigSGIX");
+      _glXGetFBConfigAttrib = 
+        (PFNGLXGETFBCONFIGATTRIBPROC)get_extension_func("glX", "GetFBConfigAttribSGIX");
+      _glXCreatePixmap = 
+        (PFNGLXCREATEPIXMAPPROC)get_extension_func("glX", "CreateGLXPixmapWithConfigSGIX");
+      
+      if (_glXChooseFBConfig == NULL ||
+          _glXCreateNewContext == NULL ||
+          _glXGetVisualFromFBConfig == NULL ||
+          _glXGetFBConfigAttrib == NULL ||
+          _glXCreatePixmap == NULL) {
+        glxdisplay_cat.error()
+          << "Driver claims to support GLX_SGIX_fbconfig extension, but does not define all functions.\n";
+        _supports_fbconfig = false;
+      }
+    }
+    
+    if (glx_is_at_least_version(1, 3)) {
+      // If we have glx 1.3 or better, we have the PBuffer interface.
+      _supports_pbuffer = true;
+      _uses_sgix_pbuffer = false;
+      
+      _glXCreatePbuffer = 
+        (PFNGLXCREATEPBUFFERPROC)get_extension_func("glX", "CreatePbuffer");
+      _glXCreateGLXPbufferSGIX = NULL;
+      _glXDestroyPbuffer = 
+        (PFNGLXDESTROYPBUFFERPROC)get_extension_func("glX", "DestroyPbuffer");
+      if (_glXCreatePbuffer == NULL ||
+          _glXDestroyPbuffer == NULL) {
+        glxdisplay_cat.error()
+          << "Driver claims to support GLX_pbuffer extension, but does not define all functions.\n";
+        _supports_pbuffer = false;
+      }
+      
+    } else if (has_extension("GLX_SGIX_pbuffer")) {
+      // Or maybe we have the old SGIX extension for PBuffers.
+      _uses_sgix_pbuffer = true;
+      
+      // CreatePbuffer has a different form between SGIX and 1.3,
+      // however, so we must treat it specially.  But we can use the
+      // same function pointer for DestroyPbuffer.
+      _glXCreatePbuffer = NULL;
+      _glXCreateGLXPbufferSGIX = 
+        (PFNGLXCREATEGLXPBUFFERSGIXPROC)get_extension_func("glX", "CreateGLXPbufferSGIX");
+      _glXDestroyPbuffer = 
+        (PFNGLXDESTROYPBUFFERPROC)get_extension_func("glX", "DestroyGLXPbufferSGIX");
+      if (_glXCreateGLXPbufferSGIX == NULL ||
+          _glXDestroyPbuffer == NULL) {
+        glxdisplay_cat.error()
+          << "Driver claims to support GLX_SGIX_pbuffer extension, but does not define all functions.\n";
+        _supports_pbuffer = false;
+      }
+    }
+  }
+
+
+  if (glxdisplay_cat.is_debug()) {
+    glxdisplay_cat.debug()
+      << "supports_swap_control = " << _supports_swap_control << "\n";
+    glxdisplay_cat.debug()
+      << "supports_fbconfig = " << _supports_fbconfig << "\n";
+    glxdisplay_cat.debug()
+      << "supports_pbuffer = " << _supports_pbuffer
+      << " sgix = " << _uses_sgix_pbuffer << "\n";
+  }
+
   // If "Mesa" is present, assume software.  However, if "Mesa DRI" is
   // found, it's actually a Mesa-based OpenGL layer running over a
   // hardware driver.
@@ -604,3 +744,90 @@ show_glx_server_string(const string &name, int id) {
     }
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: glxGraphicsStateGuardian::choose_visual
+//       Access: Private
+//  Description: Selects a visual for this gsg.  This may be called
+//               initially, to create the first context needed in
+//               order to create the fbconfig.  On successful return,
+//               _visual and _context will be filled in with a
+//               non-NULL value.
+////////////////////////////////////////////////////////////////////
+void glxGraphicsStateGuardian::
+choose_visual(const FrameBufferProperties &properties) {
+  int best_quality = 0;
+  int best_result = 0;
+  FrameBufferProperties best_props;
+
+  // Scan available visuals.
+  int nvisuals = 0;
+  _visuals = XGetVisualInfo(_display, 0, 0, &nvisuals);
+  if (_visuals != 0) {
+    for (int i = 0; i < nvisuals; i++) {
+      FrameBufferProperties fbprops;
+      get_properties(fbprops, _visuals + i);
+      int quality = fbprops.get_quality(properties);
+      if (quality > best_quality) {
+        best_quality = quality;
+        best_result = i;
+        best_props = fbprops;
+      }
+    }
+  }
+  
+  if (best_quality > 0) {
+    _visual = _visuals + best_result;
+    _context = glXCreateContext(_display, _visual, None, GL_TRUE);    
+    if (_context) {
+      _fbprops = best_props;
+      return;
+    }
+  }
+
+  glxdisplay_cat.error() 
+    << "Could not find a usable pixel format.\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: glxGraphicsStateGuardian::init_temp_context
+//       Access: Private
+//  Description: Initializes the context created in choose_visual() by
+//               creating a temporary window and binding the context
+//               to that window.
+////////////////////////////////////////////////////////////////////
+void glxGraphicsStateGuardian::
+init_temp_context() {
+  x11GraphicsPipe *x11_pipe;
+  DCAST_INTO_V(x11_pipe, get_pipe());
+  Window root_window = x11_pipe->get_root();
+
+  destroy_temp_xwindow();
+
+  _temp_xwindow = XCreateWindow
+    (_display, root_window, 0, 0, 1, 1,
+     0, _visual->depth, InputOutput,
+     _visual->visual, 0, NULL);
+  if (_temp_xwindow == (Window)NULL) {
+    glxdisplay_cat.error()
+      << "Could not create temporary window for context\n";
+    return;
+  }
+
+  glXMakeCurrent(_display, _temp_xwindow, _context);
+  reset();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: glxGraphicsStateGuardian::destroy_temp_xwindow
+//       Access: Private
+//  Description: Destroys the temporary unmapped window created by
+//               init_temp_context().
+////////////////////////////////////////////////////////////////////
+void glxGraphicsStateGuardian::
+destroy_temp_xwindow() {
+  if (_temp_xwindow != (Window)NULL) {
+    XDestroyWindow(_display, _temp_xwindow);
+    _temp_xwindow = (Window)NULL;
+  }
+}

+ 28 - 11
panda/src/glxdisplay/glxGraphicsStateGuardian.h

@@ -20,6 +20,9 @@
 #include "glgsg.h"
 #include "glxGraphicsPipe.h"
 
+// Don't pick up the system glxext.h; use our own, which is better.
+#define __glxext_h_
+
 #include <GL/glx.h>
 
 #if defined(GLX_VERSION_1_4)
@@ -39,7 +42,7 @@ extern "C" void (*glXGetProcAddressARB(const GLubyte *procName))( void );
 
 // This must be included after we have included glgsg.h (which
 // includes gl.h).
-#include "glxext.h"
+#include "panda_glxext.h"
 
 // drose: the version of GL/glx.h that ships with Fedora Core 2 seems
 // to define GLX_VERSION_1_4, but for some reason does not define
@@ -69,17 +72,11 @@ typedef int (* PFNGLXSWAPINTERVALSGIPROC) (int interval);
 ////////////////////////////////////////////////////////////////////
 class glxGraphicsStateGuardian : public GLGraphicsStateGuardian {
 public:
-#ifdef HAVE_GLXFBCONFIG
-  typedef GLXFBConfig fbconfig;
-#else
-  typedef int         fbconfig;
-#endif
-
   INLINE const FrameBufferProperties &get_fb_properties() const;
   void get_properties(FrameBufferProperties &properties, XVisualInfo *visual);
   void get_properties_advanced(FrameBufferProperties &properties,
-                               bool &pbuffer_supported, bool &pixmap_supported,
-                               bool &slow, fbconfig config);
+                               bool &context_has_pbuffer, bool &pixmap_supported,
+                               bool &slow, GLXFBConfig config);
   void choose_pixel_format(const FrameBufferProperties &properties, 
                            Display *_display,
                            int _screen,
@@ -100,13 +97,30 @@ public:
   int _screen;
   XVisualInfo *_visual;
   XVisualInfo *_visuals;
-  fbconfig _fbconfig;
+
+  GLXFBConfig _fbconfig;
   FrameBufferProperties _fbprops;
+  bool _context_has_pbuffer;  // true if the particular fbconfig supports pbuffers
+  bool _context_has_pixmap;
+  bool _slow;
 
 public:
   bool _supports_swap_control;
   PFNGLXSWAPINTERVALSGIPROC _glXSwapIntervalSGI;
 
+  bool _supports_fbconfig;
+  PFNGLXCHOOSEFBCONFIGPROC _glXChooseFBConfig;
+  PFNGLXCREATENEWCONTEXTPROC _glXCreateNewContext;
+  PFNGLXGETVISUALFROMFBCONFIGPROC _glXGetVisualFromFBConfig;
+  PFNGLXGETFBCONFIGATTRIBPROC _glXGetFBConfigAttrib;
+  PFNGLXCREATEPIXMAPPROC _glXCreatePixmap;
+
+  bool _supports_pbuffer;  // true if the interface is available.
+  bool _uses_sgix_pbuffer;
+  PFNGLXCREATEPBUFFERPROC _glXCreatePbuffer;
+  PFNGLXCREATEGLXPBUFFERSGIXPROC _glXCreateGLXPbufferSGIX;
+  PFNGLXDESTROYPBUFFERPROC _glXDestroyPbuffer;
+
 protected:
   virtual void gl_flush() const;
   virtual GLenum gl_get_error() const;
@@ -119,13 +133,16 @@ private:
   void *get_system_func(const char *name);
   void show_glx_client_string(const string &name, int id);
   void show_glx_server_string(const string &name, int id);
-
+  void choose_visual(const FrameBufferProperties &properties);
+  void init_temp_context();
+  void destroy_temp_xwindow();
 
   int _glx_version_major, _glx_version_minor;
 
   void *_libgl_handle;
   bool _checked_get_proc_address;
   PFNGLXGETPROCADDRESSPROC _glXGetProcAddress;
+  Window _temp_xwindow;
 
 public:
   static TypeHandle get_class_type() {

+ 6 - 8
panda/src/glxdisplay/glxGraphicsWindow.cxx

@@ -188,15 +188,11 @@ open_window() {
   }
   Visual *visual = _visual_info->visual;
   
-#ifdef HAVE_GLXFBCONFIG
   if (glxgsg->_fbconfig != None) {
     setup_colormap(glxgsg->_fbconfig);
   } else {
     setup_colormap(_visual_info);
   }
-#else
-  setup_colormap(_visual_info);
-#endif  // HAVE_GLXFBCONFIG
 
   if (!x11GraphicsWindow::open_window()) {
     return false;
@@ -218,7 +214,6 @@ open_window() {
   return true;
 }
 
-#ifdef HAVE_GLXFBCONFIG
 ////////////////////////////////////////////////////////////////////
 //     Function: glxGraphicsWindow::setup_colormap
 //       Access: Private
@@ -227,7 +222,11 @@ open_window() {
 ////////////////////////////////////////////////////////////////////
 void glxGraphicsWindow::
 setup_colormap(GLXFBConfig fbconfig) {
-  XVisualInfo *visual_info = glXGetVisualFromFBConfig(_display, fbconfig);
+  glxGraphicsStateGuardian *glxgsg;
+  DCAST_INTO_V(glxgsg, _gsg);
+  nassertv(glxgsg->_supports_fbconfig);
+
+  XVisualInfo *visual_info = glxgsg->_glXGetVisualFromFBConfig(_display, fbconfig);
   if (visual_info == NULL) {
     // No X visual; no need to set up a colormap.
     return;
@@ -244,7 +243,7 @@ setup_colormap(GLXFBConfig fbconfig) {
 
   switch (visual_class) {
     case PseudoColor:
-      rc = glXGetFBConfigAttrib(_display, fbconfig, GLX_RGBA, &is_rgb);
+      rc = glxgsg->_glXGetFBConfigAttrib(_display, fbconfig, GLX_RGBA, &is_rgb);
       if (rc == 0 && is_rgb) {
         glxdisplay_cat.warning()
           << "mesa pseudocolor not supported.\n";
@@ -274,7 +273,6 @@ setup_colormap(GLXFBConfig fbconfig) {
       break;
   }
 }
-#endif  // HAVE_GLXFBCONFIG
 
 ////////////////////////////////////////////////////////////////////
 //     Function: glxGraphicsWindow::setup_colormap

+ 0 - 3
panda/src/glxdisplay/glxGraphicsWindow.h

@@ -46,10 +46,7 @@ protected:
   virtual bool open_window();
 
 private:
-
-#ifdef HAVE_GLXFBCONFIG
   virtual void setup_colormap(GLXFBConfig fbconfig);
-#endif  // HAVE_GLXFBCONFIG
   virtual void setup_colormap(XVisualInfo *visual);
   
 public:

+ 2 - 2
panda/src/glxdisplay/glxext.h → panda/src/glxdisplay/panda_glxext.h

@@ -1,5 +1,5 @@
-#ifndef __glxext_h_
-#define __glxext_h_
+#ifndef panda__glxext_h_
+#define panda__glxext_h_
 
 #ifdef __cplusplus
 extern "C" {