Difference between revisions of "Tutorial: OpenGL 3.0 Context Creation (GLX)"

From OpenGL.org
Jump to: navigation, search
(changed the program to be compatible with C pre-99 and removed outcommented code to make the source more transparent.)
(5 intermediate revisions by 3 users not shown)
Line 7: Line 7:
 
== The Code ==
 
== The Code ==
  
<pre>
+
<source lang="c">
 
#include <stdio.h>
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <stdlib.h>
 
#include <string.h>
 
#include <string.h>
 
#include <unistd.h>
 
#include <unistd.h>
#define GL_GLEXT_PROTOTYPES 1
 
#define GLX_GLXEXT_PROTOTYPES 1
 
 
#include <X11/Xlib.h>
 
#include <X11/Xlib.h>
 
#include <X11/Xutil.h>
 
#include <X11/Xutil.h>
Line 22: Line 20:
 
#define GLX_CONTEXT_MINOR_VERSION_ARB      0x2092
 
#define GLX_CONTEXT_MINOR_VERSION_ARB      0x2092
 
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
 
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
 +
 +
// Helper to check for extension string presence.  Adapted from:
 +
//  http://www.opengl.org/resources/features/OGLextensions/
 +
static bool isExtensionSupported(const char *extList, const char *extension)
 +
 +
{
 +
 +
  const char *start;
 +
  const char *where, *terminator;
 +
 
 +
  /* Extension names should not have spaces. */
 +
  where = strchr(extension, ' ');
 +
  if ( where || *extension == '\0' )
 +
    return false;
 +
 +
  /* It takes a bit of care to be fool-proof about parsing the
 +
    OpenGL extensions string. Don't be fooled by sub-strings,
 +
    etc. */
 +
  for ( start = extList; ; ) {
 +
    where = strstr( start, extension );
 +
 +
    if ( !where )
 +
      break;
 +
 +
    terminator = where + strlen( extension );
 +
 +
    if ( where == start || *(where - 1) == ' ' )
 +
      if ( *terminator == ' ' || *terminator == '\0' )
 +
        return true;
 +
 +
    start = terminator;
 +
  }
 +
 +
  return false;
 +
}
 +
 +
static bool ctxErrorOccurred = false;
 +
static int ctxErrorHandler( Display *dpy, XErrorEvent *ev )
 +
{
 +
    ctxErrorOccurred = true;
 +
    return 0;
 +
}
  
 
int main (int argc, char ** argv)
 
int main (int argc, char ** argv)
Line 51: Line 91:
 
       None
 
       None
 
     };
 
     };
 +
 +
  int glx_major, glx_minor;
 +
 +
  // FBConfigs were added in GLX version 1.3.
 +
  if ( !glXQueryVersion( display, &glx_major, &glx_minor ) ||
 +
      ( ( glx_major == 1 ) && ( glx_minor < 3 ) ) || ( glx_major < 1 ) )
 +
  {
 +
    printf( "Invalid GLX version" );
 +
    exit(1);
 +
  }
  
 
   printf( "Getting matching framebuffer configs\n" );
 
   printf( "Getting matching framebuffer configs\n" );
Line 89: Line 139:
 
   }
 
   }
  
   // Get a visual
+
   GLXFBConfig bestFbc = fbc[ best_fbc ];
  int fbc_id = best_fbc;
+
  //int fbc_id = worst_fbc;
+
  
   XVisualInfo *vi = glXGetVisualFromFBConfig( display, fbc[ fbc_id ]  );
+
  // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
 +
  XFree( fbc );
 +
 
 +
  // Get a visual
 +
   XVisualInfo *vi = glXGetVisualFromFBConfig( display, bestFbc );
 
   printf( "Chosen visual ID = 0x%x\n", vi->visualid );
 
   printf( "Chosen visual ID = 0x%x\n", vi->visualid );
  
 
   printf( "Creating colormap\n" );
 
   printf( "Creating colormap\n" );
 
   XSetWindowAttributes swa;
 
   XSetWindowAttributes swa;
   swa.colormap = XCreateColormap( display, RootWindow( display, vi->screen ),  
+
  Colormap cmap;
                                  vi->visual, AllocNone );
+
   swa.colormap = cmap = XCreateColormap( display,
 +
                                        RootWindow( display, vi->screen ),  
 +
                                        vi->visual, AllocNone );
 
   swa.background_pixmap = None ;
 
   swa.background_pixmap = None ;
 
   swa.border_pixel      = 0;
 
   swa.border_pixel      = 0;
Line 115: Line 169:
 
   }
 
   }
  
   XStoreName( display, win, "GL 3.0 Window");
+
  // Done with the visual info data
 +
  XFree( vi );
 +
 
 +
   XStoreName( display, win, "GL 3.0 Window" );
  
 
   printf( "Mapping window\n" );
 
   printf( "Mapping window\n" );
 
   XMapWindow( display, win );
 
   XMapWindow( display, win );
  
   // See if GL driver supports glXCreateContextAttribsARB()
+
   // Get the default screen's GLX extension list
  //  Create an old-style GLX context first, to get the correct function ptr.
+
   const char *glxExts = glXQueryExtensionsString( display,
   glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
+
                                                  DefaultScreen( display ) );
  
   GLXContext ctx_old = glXCreateContext( display, vi, 0, True );
+
   // NOTE: It is not necessary to create or make current to a context before
 +
  // calling glXGetProcAddressARB
 +
  glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
 
   glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
 
   glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
           glXGetProcAddress( (const GLubyte *) "glXCreateContextAttribsARB" );
+
           glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" );
  
 
   GLXContext ctx = 0;
 
   GLXContext ctx = 0;
  
   // If it doesn't, just use the old-style 2.x GLX context
+
   // Install an X error handler so the application won't exit if GL 3.0
   if ( !glXCreateContextAttribsARB )
+
  // context allocation fails.
 +
  //
 +
  // Note this error handler is global.  All display connections in all threads
 +
  // of a process use the same error handler, so be sure to guard against other
 +
  // threads issuing X commands while this code is running.
 +
  ctxErrorOccurred = false;
 +
  int (*oldHandler)(Display*, XErrorEvent*) =
 +
      XSetErrorHandler(&ctxErrorHandler);
 +
 
 +
  // Check for the GLX_ARB_create_context extension string and the function.
 +
  // If either is not present, use GLX 1.3 context creation method.
 +
   if ( !isExtensionSupported( glxExts, "GLX_ARB_create_context" ) ||
 +
      !glXCreateContextAttribsARB )
 
   {
 
   {
 
     printf( "glXCreateContextAttribsARB() not found"
 
     printf( "glXCreateContextAttribsARB() not found"
 
             " ... using old-style GLX context\n" );
 
             " ... using old-style GLX context\n" );
     ctx = ctx_old;
+
     ctx = glXCreateNewContext( display, bestFbc, GLX_RGBA_TYPE, 0, True );
 
   }
 
   }
  
   // If it "does", try to get a GL 3.0 context!
+
   // If it does, try to get a GL 3.0 context!
 
   else
 
   else
 
   {
 
   {
     glXMakeCurrent( display, 0, 0 );
+
     int context_attribs[] =
    glXDestroyContext( display, ctx_old );
+
 
+
    static int context_attribs[] =
+
 
       {
 
       {
 
         GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
 
         GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
Line 153: Line 221:
  
 
     printf( "Creating context\n" );
 
     printf( "Creating context\n" );
     ctx = glXCreateContextAttribsARB( display, fbc[ fbc_id ], 0,  
+
     ctx = glXCreateContextAttribsARB( display, bestFbc, 0,
 
                                       True, context_attribs );
 
                                       True, context_attribs );
     if ( ctx )
+
 
 +
    // Sync to ensure any errors generated are processed.
 +
    XSync( display, False );
 +
     if ( !ctxErrorOccurred && ctx )
 
       printf( "Created GL 3.0 context\n" );
 
       printf( "Created GL 3.0 context\n" );
 
     else
 
     else
 
     {
 
     {
 
       // Couldn't create GL 3.0 context.  Fall back to old-style 2.x context.
 
       // Couldn't create GL 3.0 context.  Fall back to old-style 2.x context.
 +
      // When a context version below 3.0 is requested, implementations will
 +
      // return the newest context version compatible with OpenGL versions less
 +
      // than version 3.0.
 +
      // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
 +
      context_attribs[1] = 1;
 +
      // GLX_CONTEXT_MINOR_VERSION_ARB = 0
 +
      context_attribs[3] = 0;
 +
 +
      ctxErrorOccurred = false;
 +
 
       printf( "Failed to create GL 3.0 context"
 
       printf( "Failed to create GL 3.0 context"
 
               " ... using old-style GLX context\n" );
 
               " ... using old-style GLX context\n" );
       ctx = glXCreateContext( display, vi, 0, True );
+
       ctx = glXCreateContextAttribsARB( display, bestFbc, 0,  
 +
                                        True, context_attribs );
 
     }
 
     }
 
   }
 
   }
  
   XFree( fbc );
+
   // Sync to ensure any errors generated are processed.
 +
  XSync( display, False );
 +
 
 +
  // Restore the original error handler
 +
  XSetErrorHandler( oldHandler );
 +
 
 +
  if ( ctxErrorOccurred || !ctx )
 +
  {
 +
    printf( "Failed to create an OpenGL context\n" );
 +
    exit(1);
 +
  }
  
 
   // Verifying that context is a direct context
 
   // Verifying that context is a direct context
  printf( "Verifying that context is direct\n" );
 
 
   if ( ! glXIsDirect ( display, ctx ) )
 
   if ( ! glXIsDirect ( display, ctx ) )
 
   {
 
   {
     printf( "Indirect GLX rendering context obtained" );
+
     printf( "Indirect GLX rendering context obtained\n" );
     exit(1);
+
  }
 +
  else
 +
  {
 +
     printf( "Direct GLX rendering context obtained\n" );
 
   }
 
   }
  
Line 191: Line 285:
 
   sleep( 1 );
 
   sleep( 1 );
  
  ctx = glXGetCurrentContext(  );
 
 
   glXMakeCurrent( display, 0, 0 );
 
   glXMakeCurrent( display, 0, 0 );
 
   glXDestroyContext( display, ctx );
 
   glXDestroyContext( display, ctx );
 +
 +
  XDestroyWindow( display, win );
 +
  XFreeColormap( display, cmap );
 +
  XCloseDisplay( display );
 
}
 
}
</pre>
+
</source>
  
 
== Compilation ==
 
== Compilation ==
  
  g++ -o gl3 gl3.cxx -lGL
+
  c++ -o gl3 gl3.cxx -lGL -lX11
  
 
== Execution ==
 
== Execution ==
Line 216: Line 313:
 
   Creating context
 
   Creating context
 
   Created GL 3.0 context
 
   Created GL 3.0 context
   Verifying that context is direct
+
   Direct GLX rendering context obtained
 
   Making context current
 
   Making context current
 +
 +
[[Category:Tutorials]]

Revision as of 17:24, 13 February 2011

Overview

This tutorial illustrates (with a working program) how to create an OpenGL 3.0 context using the new glXCreateContextAttribsARB() API. In the event that this API is unsupported or fails, we fall back and create a standard (old-style) GL 2.1 context.

It also shows how to choose the X visual / framebuffer format that has the highest number of multisample subsamples in it.

The Code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/gl.h>
#include <GL/glx.h>
 
#define GLX_CONTEXT_MAJOR_VERSION_ARB       0x2091
#define GLX_CONTEXT_MINOR_VERSION_ARB       0x2092
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
 
// Helper to check for extension string presence.  Adapted from:
//   http://www.opengl.org/resources/features/OGLextensions/
static bool isExtensionSupported(const char *extList, const char *extension)
 
{
 
  const char *start;
  const char *where, *terminator;
 
  /* Extension names should not have spaces. */
  where = strchr(extension, ' ');
  if ( where || *extension == '\0' )
    return false;
 
  /* It takes a bit of care to be fool-proof about parsing the
     OpenGL extensions string. Don't be fooled by sub-strings,
     etc. */
  for ( start = extList; ; ) {
    where = strstr( start, extension );
 
    if ( !where )
      break;
 
    terminator = where + strlen( extension );
 
    if ( where == start || *(where - 1) == ' ' )
      if ( *terminator == ' ' || *terminator == '\0' )
        return true;
 
    start = terminator;
  }
 
  return false;
}
 
static bool ctxErrorOccurred = false;
static int ctxErrorHandler( Display *dpy, XErrorEvent *ev )
{
    ctxErrorOccurred = true;
    return 0;
}
 
int main (int argc, char ** argv)
{
  Display *display = XOpenDisplay(0);
 
  if ( !display )
  {
    printf( "Failed to open X display\n" );
    exit(1);
  }
 
  // Get a matching FB config
  static int visual_attribs[] =
    {
      GLX_X_RENDERABLE    , True,
      GLX_DRAWABLE_TYPE   , GLX_WINDOW_BIT,
      GLX_RENDER_TYPE     , GLX_RGBA_BIT,
      GLX_X_VISUAL_TYPE   , GLX_TRUE_COLOR,
      GLX_RED_SIZE        , 8,
      GLX_GREEN_SIZE      , 8,
      GLX_BLUE_SIZE       , 8,
      GLX_ALPHA_SIZE      , 8,
      GLX_DEPTH_SIZE      , 24,
      GLX_STENCIL_SIZE    , 8,
      GLX_DOUBLEBUFFER    , True,
      //GLX_SAMPLE_BUFFERS  , 1,
      //GLX_SAMPLES         , 4,
      None
    };
 
  int glx_major, glx_minor;
 
  // FBConfigs were added in GLX version 1.3.
  if ( !glXQueryVersion( display, &glx_major, &glx_minor ) || 
       ( ( glx_major == 1 ) && ( glx_minor < 3 ) ) || ( glx_major < 1 ) )
  {
    printf( "Invalid GLX version" );
    exit(1);
  }
 
  printf( "Getting matching framebuffer configs\n" );
  int fbcount;
  GLXFBConfig *fbc = glXChooseFBConfig( display, DefaultScreen( display ), 
                                        visual_attribs, &fbcount );
  if ( !fbc )
  {
    printf( "Failed to retrieve a framebuffer config\n" );
    exit(1);
  }
  printf( "Found %d matching FB configs.\n", fbcount );
 
  // Pick the FB config/visual with the most samples per pixel
  printf( "Getting XVisualInfos\n" );
  int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
 
  int i;
  for ( i = 0; i < fbcount; i++ )
  {
    XVisualInfo *vi = glXGetVisualFromFBConfig( display, fbc[i] );
    if ( vi )
    {
      int samp_buf, samples;
      glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf );
      glXGetFBConfigAttrib( display, fbc[i], GLX_SAMPLES       , &samples  );
 
      printf( "  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
              " SAMPLES = %d\n", 
              i, vi -> visualid, samp_buf, samples );
 
      if ( best_fbc < 0 || samp_buf && samples > best_num_samp )
        best_fbc = i, best_num_samp = samples;
      if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp )
        worst_fbc = i, worst_num_samp = samples;
    }
    XFree( vi );
  }
 
  GLXFBConfig bestFbc = fbc[ best_fbc ];
 
  // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
  XFree( fbc );
 
  // Get a visual
  XVisualInfo *vi = glXGetVisualFromFBConfig( display, bestFbc );
  printf( "Chosen visual ID = 0x%x\n", vi->visualid );
 
  printf( "Creating colormap\n" );
  XSetWindowAttributes swa;
  Colormap cmap;
  swa.colormap = cmap = XCreateColormap( display,
                                         RootWindow( display, vi->screen ), 
                                         vi->visual, AllocNone );
  swa.background_pixmap = None ;
  swa.border_pixel      = 0;
  swa.event_mask        = StructureNotifyMask;
 
  printf( "Creating window\n" );
  Window win = XCreateWindow( display, RootWindow( display, vi->screen ), 
                              0, 0, 100, 100, 0, vi->depth, InputOutput, 
                              vi->visual, 
                              CWBorderPixel|CWColormap|CWEventMask, &swa );
  if ( !win )
  {
    printf( "Failed to create window.\n" );
    exit(1);
  }
 
  // Done with the visual info data
  XFree( vi );
 
  XStoreName( display, win, "GL 3.0 Window" );
 
  printf( "Mapping window\n" );
  XMapWindow( display, win );
 
  // Get the default screen's GLX extension list
  const char *glxExts = glXQueryExtensionsString( display,
                                                  DefaultScreen( display ) );
 
  // NOTE: It is not necessary to create or make current to a context before
  // calling glXGetProcAddressARB
  glXCreateContextAttribsARBProc glXCreateContextAttribsARB = 0;
  glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)
           glXGetProcAddressARB( (const GLubyte *) "glXCreateContextAttribsARB" );
 
  GLXContext ctx = 0;
 
  // Install an X error handler so the application won't exit if GL 3.0
  // context allocation fails.
  //
  // Note this error handler is global.  All display connections in all threads
  // of a process use the same error handler, so be sure to guard against other
  // threads issuing X commands while this code is running.
  ctxErrorOccurred = false;
  int (*oldHandler)(Display*, XErrorEvent*) =
      XSetErrorHandler(&ctxErrorHandler);
 
  // Check for the GLX_ARB_create_context extension string and the function.
  // If either is not present, use GLX 1.3 context creation method.
  if ( !isExtensionSupported( glxExts, "GLX_ARB_create_context" ) ||
       !glXCreateContextAttribsARB )
  {
    printf( "glXCreateContextAttribsARB() not found"
            " ... using old-style GLX context\n" );
    ctx = glXCreateNewContext( display, bestFbc, GLX_RGBA_TYPE, 0, True );
  }
 
  // If it does, try to get a GL 3.0 context!
  else
  {
    int context_attribs[] =
      {
        GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
        GLX_CONTEXT_MINOR_VERSION_ARB, 0,
        //GLX_CONTEXT_FLAGS_ARB        , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
        None
      };
 
    printf( "Creating context\n" );
    ctx = glXCreateContextAttribsARB( display, bestFbc, 0,
                                      True, context_attribs );
 
    // Sync to ensure any errors generated are processed.
    XSync( display, False );
    if ( !ctxErrorOccurred && ctx )
      printf( "Created GL 3.0 context\n" );
    else
    {
      // Couldn't create GL 3.0 context.  Fall back to old-style 2.x context.
      // When a context version below 3.0 is requested, implementations will
      // return the newest context version compatible with OpenGL versions less
      // than version 3.0.
      // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
      context_attribs[1] = 1;
      // GLX_CONTEXT_MINOR_VERSION_ARB = 0
      context_attribs[3] = 0;
 
      ctxErrorOccurred = false;
 
      printf( "Failed to create GL 3.0 context"
              " ... using old-style GLX context\n" );
      ctx = glXCreateContextAttribsARB( display, bestFbc, 0, 
                                        True, context_attribs );
    }
  }
 
  // Sync to ensure any errors generated are processed.
  XSync( display, False );
 
  // Restore the original error handler
  XSetErrorHandler( oldHandler );
 
  if ( ctxErrorOccurred || !ctx )
  {
    printf( "Failed to create an OpenGL context\n" );
    exit(1);
  }
 
  // Verifying that context is a direct context
  if ( ! glXIsDirect ( display, ctx ) )
  {
    printf( "Indirect GLX rendering context obtained\n" );
  }
  else
  {
    printf( "Direct GLX rendering context obtained\n" );
  }
 
  printf( "Making context current\n" );
  glXMakeCurrent( display, win, ctx );
 
  glClearColor ( 0, 0.5, 1, 1 );
  glClear ( GL_COLOR_BUFFER_BIT );
  glXSwapBuffers ( display, win );
 
  sleep( 1 );
 
  glClearColor ( 1, 0.5, 0, 1 );
  glClear ( GL_COLOR_BUFFER_BIT );
  glXSwapBuffers ( display, win );
 
  sleep( 1 );
 
  glXMakeCurrent( display, 0, 0 );
  glXDestroyContext( display, ctx );
 
  XDestroyWindow( display, win );
  XFreeColormap( display, cmap );
  XCloseDisplay( display );
}

Compilation

  c++ -o gl3 gl3.cxx -lGL -lX11

Execution

 > gl3
 Getting matching framebuffer configs
 Found 3 matching FB configs.
 Getting XVisualInfos
   Matching fbconfig 0, visual ID 0x23: SAMPLE_BUFFERS = 0, SAMPLES = 0
   Matching fbconfig 1, visual ID 0x37: SAMPLE_BUFFERS = 1, SAMPLES = 2
   Matching fbconfig 2, visual ID 0x39: SAMPLE_BUFFERS = 1, SAMPLES = 4
 Chosen visual ID = 0x39
 Creating colormap
 Creating window
 Mapping window
 Creating context
 Created GL 3.0 context
 Direct GLX rendering context obtained
 Making context current