This document presents information which will help to maximize OpenGL across different computer systems and within different configurations of similar systems.
Mesa, however, may potentially return any X visual type for RGB mode.
This is because some X displays on which Mesa may be used do not have
Similarly, Mesa may return a
Mesa violates the GLX specification but allows rendering on more types of
displays than OpenGL would.
Dealing with Mesa's expanded offering of visuals is mostly just a matter
of handling colormaps correctly.
The following algorithm should pick a good RGB colormap in most cases:
Basically, we use the default colormap if the visuals match. Otherwise
we look for a standard colormap. If that fails we must allocate a new,
private colormap.
If using Mesa on an 8-bit
Caveat: this algorithm may not work on Sun systems due to a bug
in the
Finally, if one intends to render into several different window with the
same RGB context those window should share the same colormap. This is
required with Mesa and helps to reduce colormap flashing with OpenGL.
Otherwise, if your GLX visual type and depth matches the default/root visual
then you can probably use the default/root colormap.
To allocate a read/write colorcell from the colormap use
If
If your application needs several color index mode windows it's a good
idea to try to share one colormap among the windows.
Finally, be sure that
After the colormap has been selected you can create your window, specifying
the colormap in the
Furthermore, you should inform the window manager if your top-level window
contains children with non-default colormaps. This is done with the
Be aware that many systems advertised as having 24-bit color, in fact,
only offer 12-bit color in double buffer mode. This is because the 24-bit
frame buffer is divided into two 12-bit buffers. Dithering may become
evident in this situation.
What if you would like both double buffering and full 24-bit color? For
example, during animation one may want double buffering but to show a
static image a full-color single buffered window would look best.
IRIS GL allowed one to reconfigure a window to single or double buffering
on the fly with
The usual steps in creating and using a GLX pixmap are:
Mesa (version 1.2.8 and later) has a GLX extension which lets the user specify
the colormap associated with a GLX pixmap. The extension provides a new
function very similar to
The proper way to use this function is:
Why would we want to do this? One, we may want our application to work on
both X and Windows platforms. Two, we may want to support both OpenGL and
IRIS GL (or PEX) during a transition period.
The basics:
On the other hand, a complex application may be so tightly integrated with
a user interface toolkit or graphics library that it's impractical to support
alternative interfaces or libraries.
OpenGL, IRIS GL and PEX code is isolated into separate source files:
The core of Vis5Ds functionality is isolated from the user interface by
an internal API. Everything "below" the API is GUI independent.
Everything "above" the API is considered user interface code. While Vis5D's
user interface code is substantial, it could be replaced by an alternative
toolkit with minimal impact on the rest of the system.
The simplest solution to this problem is to only use functionality which
is common to all libraries. This can actually be quite practical in
simple applications which don't require elaborate renderering techniques.
The other solution is to poll the graphics system to determine its
capabilities and work around those it doesn't support. Vis5D, for example,
offers volume rendering only on systems with alpha blending capability.
A small application using GLUT or Tcl/Tk is potentially portable to many
systems because GLUT and Tcl/Tk are available on most platforms.
Larger applications which use native window system toolkits will have to
be partitioned into modules which isolate window and operating system-
specific code.
The OpenGL window system interface call should be considered window
system code and not be put in the OpenGL modules. This includes the
swapbuffers operation.
Here are the highlights of PEX vs OpenGL and porting:
One may be able to continue using PEX-style data structures in your
application and render them using OpenGL commands.
SGI's
OpenGL Porting Guide is a good place to begin a porting project. Below
are the highlights of the similarities and differences in OpenGL and IRIS GL.
SGI developers also have at their disposal the toogl utility
which converts IRIS GL code to similar OpenGL code. toogl does not do all
the work for you but it can at least do much of the more tedious work.
If your IRIS GL program makes heavy use of IRIS GL's input devices,
window management, pop-up menus, etc porting will be more difficult.
One possibility is to use GLUT. GLUT provides much of the
IRIS GL functionality which OpenGL lacks.
2. GLX and Xlib and Mesa
X supports a wide variety of display devices. Dealing with X visuals and
colormaps correctly is very important for portability. If you want your
application to operate with Mesa in addition to OpenGL then there are
several special issues to be aware of.
2.1 Visuals
A GLX-based program will usually obtain an X visual with
glXChooseVisual.
Per the OpenGL GLX specification, if an RGB mode is requested,
glXChooseVisual will return either a TrueColor
or DirectColor visual.
Otherwise, a PseudoColor or StaticColor visual
will be returned for color index mode.
TrueColor or DirectColor visuals.
Mese prefers visual types in the order TrueColor,
DirectColor, PseudoColor, StaticColor,
GrayScale, and StaticGray
and visuals depths from deepest to shallowest.
There is one exception: 8-bit PseudoColor is preferred over
8-bit TrueColor. This is a convention many people prefer for
low-end displays which use an 8-bit PseudoColor visual for the
default and only have one hardware colormap.
PseudoColor,
StaticColor, GrayScale or
StaticGray visual if color index mode is requested.
2.2 Colormaps
The best way to handle X colormaps depends on whether one is rendering
in RGB or color index mode.
2.2.1 RGB mode colormaps
When rendering in RGB mode the colormap is usually never altered (using a
DirectColor visual may be an exception).
In general we
want to share read-only colormaps among windows to minimize colormap flashing.
Colormap flashing (aka the technicolor effect) occurs when the demand for
colormaps exceeds the hardware's capacity.
As the mouse is moved from window to window different colormaps may be
installed; some windows will be forced to use the wrong colormap.
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h> /* for XA_RGB_DEFAULT_MAP atom */
#if defined(__vms)
#include <X11/StdCmap.h> /* for XmuLookupStandardColormap */
#else
#include <X11/Xmu/StdCmap.h> /* for XmuLookupStandardColormap */
#endif
#include <GL/glx.h>
#include <string.h>
/*
* Return an X colormap to use for OpenGL RGB-mode rendering.
* Input: dpy - the X display
* scrnum - the X screen number
* visinfo - the XVisualInfo as returned by glXChooseVisual()
* Return: an X Colormap or 0 if there's a _serious_ error.
*/
Colormap
get_rgb_colormap( Display *dpy, int scrnum, XVisualInfo *visinfo )
{
Atom hp_cr_maps;
Status status;
int numCmaps;
int i;
XStandardColormap *standardCmaps;
Window root = RootWindow(dpy,scrnum);
int using_mesa;
/*
* First check if visinfo's visual matches the default/root visual.
*/
if (visinfo->visual==DefaultVisual(dpy,scrnum)) {
/* use the default/root colormap */
return DefaultColormap( dpy, scrnum );
}
/*
* Check if we're using Mesa.
*/
if (strstr(glXQueryServerString( dpy, scrnum, GLX_VERSION ), "Mesa")) {
using_mesa = 1;
}
else {
using_mesa = 0;
}
/*
* Next, if we're using Mesa and displaying on an HP with the "Color
* Recovery" feature and the visual is 8-bit TrueColor, search for a
* special colormap initialized for dithering. Mesa will know how to
* dither using this colormap.
*/
if (using_mesa) {
hp_cr_maps = XInternAtom( dpy, "_HP_RGB_SMOOTH_MAP_LIST", True );
if (hp_cr_maps
&& visinfo->visual->class==TrueColor
&& visinfo->depth==8) {
status = XGetRGBColormaps( dpy, root, &standardCmaps,
&numCmaps, hp_cr_maps );
if (status) {
for (i=0; i < numCmaps; i++) {
if (standardCmaps[i].visualid==visinfo->visual->visualid) {
Colormap cmap = standardCmaps[i].colormap;
XFree(standardCmaps);
return cmap;
}
}
XFree(standardCmaps);
}
}
}
/*
* Next, try to find a standard X colormap.
*/
#ifndef SOLARIS_BUG
status = XmuLookupStandardColormap( dpy, visinfo->screen,
visinfo->visualid,
visinfo->depth,
XA_RGB_DEFAULT_MAP,
/* replace */ False,
/* retain */ True);
if (status == 1) {
status = XGetRGBColormaps( dpy, root, &standardCmaps,
&numCmaps, XA_RGB_DEFAULT_MAP);
if (status == 1) {
for (i = 0; i < numCmaps; i++) {
if (standardCmaps[i].visualid == visinfo->visualid) {
Colormap cmap = standardCmaps[i].colormap;
XFree(standardCmaps);
return cmap;
}
}
XFree(standardCmaps);
}
}
#endif
/*
* If we get here, give up and just allocate a new colormap.
*/
return XCreateColormap( dpy, root, visinfo->visual, AllocNone );
}
TrueColor HP display then we
look for a special "Color Recovery" colormap which helps to produce
high-quality dithered images.
XmuLookupStandardColormap function.
2.2.2 Color index mode colormaps
When designing a color index mode application we must decide if we need
a writable colormap and/or need specific colors associated with specific
pixel values. For lighting and fog effects to work in color index mode
one has to store specific colors in consecutive colormap entries. Therefore,
a private, writable colormap is required.
It should be allocated/created with
XCreateColormap( dpy, win, visual, AllocAll ).
XAllocColorCells.
To allocate read-only cells use XAllocColor.
In both cases, X will return to you the index of a colorcell.
XAllocColor fails then you may have to search the colormap
for a close match.
The following function will search a colormap for the closest match to
your requested color:
#include <X11/Xlib.h>
#include <stdlib.h>
/* A replacement for XAllocColor.
* This function should never fail to allocate a color. When
* XAllocColor fails, we return the nearest matching color. If
* we have to allocate many colors this function isn't a great
* solution; the XQueryColors() could be done just once.
*/
static void
noFaultXAllocColor(Display * dpy, Colormap cmap, int cmapSize, XColor * color)
{
XColor *ctable, subColor;
int i, bestmatch;
double mindist; /* 3*2^16^2 exceeds long int precision. */
/* First try just using XAllocColor. */
if (XAllocColor(dpy, cmap, color))
return;
/* Retrieve color table entries. */
/* XXX alloca canidate. */
ctable = (XColor *) malloc(cmapSize * sizeof(XColor));
for (i = 0; i < cmapSize; i++)
ctable[i].pixel = i;
XQueryColors(dpy, cmap, ctable, cmapSize);
/* Find best match. */
bestmatch = -1;
mindist = 0.0;
for (i = 0; i < cmapSize; i++) {
double dr = (double) color->red - (double) ctable[i].red;
double dg = (double) color->green - (double) ctable[i].green;
double db = (double) color->blue - (double) ctable[i].blue;
double dist = dr * dr + dg * dg + db * db;
if (bestmatch < 0 || dist < mindist) {
bestmatch = i;
mindist = dist;
}
}
/* Return result. */
subColor.red = ctable[bestmatch].red;
subColor.green = ctable[bestmatch].green;
subColor.blue = ctable[bestmatch].blue;
free(ctable);
if (!XAllocColor(dpy, cmap, &subColor)) {
subColor.pixel = (unsigned long) bestmatch;
}
*color = subColor;
}
glXChooseVisual returns
a PseudoColor (or for Mesa, GrayScale) visual
if a writable colormap is needed.
XSetWindowAttributes structure passed to
XCreateWindow.
XSetWMColormapWindows function:
XSetWMColormapWindows( display, top_level_window,
&window_list, num );
2.3 Double buffering
Double buffered visuals are not required by OpenGL.
Therefore if one requests double buffering but glXChooseVisual
fails, you should try again specifying single buffering.
Similarly, OpenGL does not require single buffered visuals to be offered.
If you want a single buffered window but glXChooseVisual fails,
you should try again specifying double buffering.
Then, issue glDrawBuffer(GL_FRONT) to direct drawing to the
front color buffer.
Be sure to call glFlush to force rendering to complete at
critical points.
doublebuffer, singlebuffer and
gconfig.
This can't be done with OpenGL.
Instead, you can create two subwindows contained by
a common parent, one window single buffered and the other window double
buffered, and use XMapWidnow/XUnmapWindow to display the one
you want to use.
Remember to use separate contexts for each window since they will have
different visuals.
2.4 Alpha planes
Some systems implement the alpha buffer in client or server memory rather
than in video or frame buffer memory. This can be quite slow. Do not
not request alpha planes if you do not need them. Alpha planes are seldom
needed in practice.
2.5 GLX Pixmaps
GLX pixmaps are used for off-screen OpenGL rendering. A GLX pixmap is
basically an X Pixmap augmented with OpenGL ancillary buffers (depth,
stencil, etc).
The advantages of GLX pixmaps are they take no screen space, are never
damaged, and not constrained by the size of the screen. The disadvantage
of GLX pixmaps is that 3-D graphics hardware is often unable to render
into them; a software renderer executes the OpenGL instructions.
Notes:
glXChooseVisual
XCreatePixmap using the depth
of the visual returned by glXChooseVisual
glXCreateGLXPixmap.
glXCreateContext,
usually specifying the indirect option.
glXMakeCurrent
There is a special problem in using GLX pixmaps with Mesa in RGB mode.
Since Mesa supports RGB mode rendering into any kind of X visual it often
needs colormap information so that RGB values can be converted into logical
pixel values. The GLX pixmap facility does not provide a way to indicate
which X colormap is associated with a GLX pixmap.
glXMakeCurrent
succeeds.
glXCreateGLXPixmap:
GLXPixmap glXCreateGLXPixmapMESA( Display *dpy, XVisualInfo *visual,
Pixmap pixmap, Colormap cmap )
Strictly speaking, the colormap argument is only needed when rendering in
RGB mode into a GLX pixmap which uses a PseudoColor,
StaticColor, GrayScale or StaticGray
visual.
If the colormap is not specified but is in fact needed, the
glXMakeCurrent call will return False.
Pixmap p;
GLXPixmap q;
...
#ifdef GLX_MESA_pixmap_colormap
q = glXCreateGLXPixmapMESA( display, visual, p, colormap );
#else
q = glXCreateGLXPixmap( display, visual, p );
#endif
Since the GLX_MESA_pixmap_color extension symbol is only defined if using
Mesa's header files this technique will be portable to any GLX implementation.
3. Organization for portability
How can an OpenGL application be organized such that the code may be used
for several window systems? Or, how can an application be organized to
use both OpenGL and another graphics library?
The practicality of this depends on the nature and size of the application.
One one hand, modern window system toolkits are quite similar in that GUIs
are designed with the callback/event loop paradigm:
Furthermore, rendering can be encapsulated in wrapper functions which
present an API independent of the graphics library.
3.1 An example: Vis5D
Vis5D is a system for interactive visualization of three dimensional
atmospheric data. It can use OpenGL, IRIS GL, or PEX for 3-D rendering.
An Xlib-based GUI toolkit provides the only user interface at this time
but it's quite feasible to write a new one.
Each file performs the rendering functions defined by a single header file,
graphics.h, defining functions such as:
which graphics.ogl.c, graphics.gl.c and grahics.pex.c each implements in
its own way. The Makefile determines which source file is compiled.
3.2 Graphics library functionality
When supporting multiple graphics libraries, a difficult problem to deal
with is subsetting. While OpenGL mandates that all its features be
implemented other graphics libraries aren't as stringently defined.
PEX implementations, for example, vary greatly in terms of what features
are implemented.
3.3 Multi- window system applications
Suppose your OpenGL application must work on several window system such as
X and Microsoft Windows. How can this be accomplished? It depends on the
nature of the application.
4. Transitioning to OpenGL from other libs
As OpenGL becomes the predominate 3-D graphics library there are reasons to
port existing applications to OpenGL from older libraries:
Porting can take a lot of effort; there aren't many shortcuts. Below are
some observations and issues to be aware of.
4.1 PEX to OpenGL
PEX is a 3-D graphics extension to the X Window System. The API is
similar to Xlib in that there are many pointers, structures and complicated
function calls. OpenGL by comparison is much cleaner and simpler.
4.2 IRIS GL to OpenGL
Since OpenGL's roots are in IRIS GL one may expect porting from IRIS GL
to OpenGL to be easy. Conceptually, IRIS GL and OpenGL are very similar,
but in practice porting is not an easy job.
4.2.1 Similarities
4.2.2 Differences
These points only describe the high-level differences in the graphics
libraries. As mentioned above, the OpenGL Porting Guide goes
into much more detail.
Last edited on May 14, 1996 by Brian Paul.