Common Mistakes

From OpenGL.org
Revision as of 22:23, 26 January 2010 by V-man (Talk | contribs) (Creating a Cubemap Texture)

Jump to: navigation, search

Quite a few websites show the same mistakes and the mistakes presented in their tutorials are copied and pasted by those who want to learn OpenGL.
This page has been created so that newcomers understand GL programming a little better instead of working by trial and error.
The shading language section also has its own section on common mistakes GLSL : common mistakes.

Extensions and OpenGL Versions

This part can be confusing to some so here is a definition.

An extension is a specification for a GL feature that isn't in the GL core. It is written against some specific GL version, meaning that GL version must be supported at minimum. Usually that would be GL 1.1. Extension specs are at the GL extension registry http://www.opengl.org/registry

For example, glActiveTextureARB and GL_TEXTURE0_ARB is part of the GL_ARB_multitexture extension.

If the extension is good, widely supported, and useful, a new GL version may absorb those extensions into the core of OpenGL. Sometimes this happens with no change in the functioning of the extension, but sometimes it does.

When an extension becomes a core feature, the function names lose the postfix: glActiveTextureARB becomes glActiveTexture, GL_TEXTURE0_ARB becomes GL_TEXTURE0.

In some cases, the extension API and the core API equivalents change names entirely. This was the case when changing from GL_ARB_shader_objects (GLSL support) to GL 2.0's core.

One of the possible mistakes one can make is to check for the presence of an extension, but instead using the core functions. The correct behavior is to check for the presence of the extension if you want to use the extension API, and check for the GL version if you want to use the core API.

This is complicated even more by the presence of a new form of extension: core extensions. A core extension is an ARB extension that exactly mirrors the functioning of a feature of a higher version of the OpenGL core. These extension functions and enumerators do not have a suffix, just like the core version.

The idea here is to allow the user to use the same code on prior GL versions as they do for later GL versions where the feature is core. In this case, you should check for both the version and the presence of the extension; if either is there, you can use the functionality.

The Object Oriented Language Problem

In an object-oriented language like C++, it is often useful to have a class that wraps an OpenGL object. For example, one might have a texture object that has a constructor like the following:

  MyTexture::MyTexture(const char *pfilePath)
  {
     if(LoadFile(pfilePath)==ERROR)
        return;
     textureID=0;
     glGenTextures(1, &textureID);
     //More GL code...
  }

Naturally, this would be paired with a destructor that looks something like:

  MyTexture::~MyTexture()
  {
     if(textureID)
     {
        glDeleteTextures(1, &textureID);
        textureID=0;
     }
  }

There is a large pitfall with doing this. OpenGL functions do not work unless an OpenGL context has been created and is active within that thread. Thus, glGenTextures will do nothing before context creation, and glDeleteTextures will do nothing after context destruction. The latter problem is not a significant concern since OpenGL contexts clean up after themselves, but the former is a problem.

This problem usually manifests itself when someone creates a texture object at global scope. There are several potential solutions:

  1. Do not use constructors/destructors to initialize/destroy OpenGL objects. Instead, use member functions of these classes for these purposes. This violates RAII principles, so this is not the best course of action.
  2. Have your OpenGL object constructors throw an exception if a context has not been created yet. This requires an addition to your context creation functionality that tells your code when a context has been created and is active.

Texture upload and pixel reads

You create a texture and upload the pixels with glTexImage2D (or glTexImage1D, glTexImage3D). However, the program crashes on upload, or there seems to be diagonal lines going through the resulting image. This is because the alignment of each horizontal line of your pixel array is not multiple of 4. That is, each line of your pixel data is not a multiple of 4. This typically happens to users loading an image that is of the RGB or BGR format (in other words, 24 bpp image).

Example, your image width = 401 and height = 500. The height doesn't matter. What matters is the width. If we do the math, 401 pixels x 3 bytes = 1203. Is 1203 divisible by 4? In this case, the image's data alignment is not 4. The question now is, is 1203 divisible by 1? Yes, so the alignment is 1 so you should call glPixelStorei(GL_UNPACK_ALIGNMENT, 1). The default is glPixelStorei(GL_UNPACK_ALIGNMENT, 4). Unpacking means sending data from client side (the client is you) to OpenGL.

And if you are interested, most GPUs like chunks of 4 bytes. In other words, RGBA or BGRA is prefered. RGB and BGR is considered bizarre since most GPUs, most CPUs and any other kind of chip don't handle 24 bits. This means, the driver converts your RGB or BGR to what the GPU prefers, which typically is BGRA.

Similarly, if you read a buffer with glReadPixels, you might get similar problems. There is a GL_PACK_ALIGNMENT just like the GL_UNPACK_ALIGNMENT. The default GL_PACK_ALIGNMENT is 4 which means each horizontal line must be a multiple of 4 in size. If you read the buffer with a format such as BGRA or RGBA you won't have any problems since the line is already a multiple of 4. If you read it in a format such as BGR or RGB then you risk running into this problem.

The GL_PACK_ALIGNMENT can only be 1, 2, 4, or 8. So an alignment of 3 is not allowed. You could just change the GL_PACK_ALIGNMENT to 1. Or you can pad your buffer out so that each line, even with only 3 values per pixel, is a multiple of 4.

Image precision

You call glTexImage2D(GL_TEXTURE_2D, 0, X, width, height, 0, format, type, pixels) and you set X to 1, 2, 3, 4. The X refers to the number of components (from RED at 1 to RGBA at 4).

It is preferred to actually give a real image format, preferably one with a specific internal precision.

If the OpenGL implementation does not support the particular format and precision you choose, the driver will internally convert it into something it does support. If you want the:

 int value;
 glGetTexLevelParameteriv(GL_TEXTURE_2D, GL_INTERNAL_FORMAT, &value)

To get the actual format. OpenGL versions 3.x and above have a set of required image formats that all conformant implementations must implement.

Creating a Texture

What's wrong with this code?

  glGenTextures(1, &textureID);
  glBindTexture(GL_TEXTURE_2D, textureID);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);

The texture won't work because it is incomplete. The default GL_TEXTURE_MIN_FILTER state is GL_NEAREST_MIPMAP_LINEAR so GL will consider the texture incomplete as long as you don't create the mipmaps.

This code is better because it sets up some of the important texture object states:

  glGenTextures(1, &textureID);
  glBindTexture(GL_TEXTURE_2D, textureID);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);

If you want to use mipmaps with this texture, you should use either generate them automatically (see below) or upload the mipmap levels individually.

Automatic mipmap generation

OpenGL 1.4 is required for support for automatic mipmap generation. GL_GENERATE_MIPMAP is part of the texture object state and it is a flag (GL_TRUE or GL_FALSE). If it is set to GL_TRUE, then whenever texture level 0 is updated, the mipmaps will all be regenerated.

  glGenTextures(1, &textureID);
  glBindTexture(GL_TEXTURE_2D, textureID);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 
  glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); 
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);

When GL_EXT_framebuffer_object is present, instead of using the GL_GENERATE_MIPMAP flag, you can use glGenerateMipmapEXT. This causes the generation of mipmaps at a specific time (when you call the function).

  glGenTextures(1, &textureID);
  glBindTexture(GL_TEXTURE_2D, textureID);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
  glGenerateMipmapEXT(GL_TEXTURE_2D);  //Generate mipmaps now!!!

If you were to use GL_GENERATE_MIPMAP with render targets, then mipmaps would have to be generated after every draw call. This allows you to generate mipmaps after all drawing is done, rather than after each draw call.

Warning: It has been reported that on some ATI drivers, glGenerateMipmapEXT(GL_TEXTURE_2D) has no effect unless you proceed it with a call to glEnable(GL_TEXTURE_2D) in this particular case. Once again, to be clear, bind the texture, glEnable, then glGenerateMipmapEXT. This is a bug and has been in the ATI drivers for a while. Perhaps by the time you read this, it has been corrected.

In order to not cause problems for your users, we suggest you continue to use GL_GENERATE_MIPMAP for your GL 2.1 program when making a standard texture and use glGenerateMipmapEXT for your RTTs.

In GL 3.x, GL_GENERATE_MIPMAP is deprecated. You must use glGenerateMipmap.

Creating a Texture #2, glTexEnvi

Since a lot of tutorials call glTexEnvi when they create a texture, quite a few people end up thinking that the texture environment state is part of the texture object.

  glGenTextures(1, &textureID);
  glBindTexture(GL_TEXTURE_2D, textureID);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

States such as GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_TEXTURE_MAG_FILTER, GL_TEXTURE_MIN_FILTER are part of the texture object.
glTexEnv is part of the texture image unit (TIU).
When you set this it will effect any texture attached to the TIU and it only has affect during rendering.
You can select a TIU with glActiveTexture(GL_TEXTURE0+i).
Also keep in mind that glTexEnvi has no effect when a fragment shader is bound.

And in the end, cleanup

  glDeleteTextures(1, &textureID);

gluBuild2DMipmaps

Never use this. Use either GL_GENERATE_MIPMAP or the glGenerateMipmap function.

Creating a Cubemap Texture

It's best to set the wrap mode to GL_CLAMP_TO_EDGE and not the other formats. Don't forget to define all 6 faces else the texture is considered incomplete. Don't forget to setup GL_TEXTURE_WRAP_R because cubemaps require 3D texture coordinates.

Example:

  glGenTextures(1, &textureID);
  glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
  //Define all 6 faces
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+0, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels_face0);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+1, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels_face1);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+2, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels_face2);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+3, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels_face3);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+4, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels_face4);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X+5, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels_face5);

If you want auto-generated mipmaps, you can use any of the aforementioned mechanisms.

Example 2: Notice that in this one GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X and the other tokens are used instead.

  glGenTextures(1, &textureID);
  glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
  //Define all 6 faces
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels_face0);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels_face1);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels_face2);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels_face3);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels_face4);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels_face5);

Texture edge color problem

When using a clamp of the texture wrap mpde, use GL_CLAMP_TO_EDGE, not GL_CLAMP. GL_CLAMP_TO_EDGE means that the colors outside of the texture range are the color of the nearest texel in the texture. Whereas GL_CLAMP means that the colors outside of the texture range are the border color. This is usually not what you want, and can lead to black borders around your texture (since the border color is black).

Updating A Texture

In case you don't want to use Render_To_Texture, you will be just refreshing the texels either from main memory or from the framebuffer.

To change texels in an already existing 2d texture, use glTexSubImage2D:

  glBindTexture(GL_TEXTURE_2D, textureID);    //A texture you have already created with glTexImage2D
  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGRA, GL_UNSIGNED_BYTE, pixels);

Notice that glTexSubImage2D was used and not glTexImage2D. glTexImage2D respecifies the entire texture, changing its size, deleting the previous data, and so forth. glTexSubImage2D only modifies pixel data within the texture.

glTexSubImage2D can be used to update all the texels, or simply a portion of them.

To copy texels from the framebuffer, use glCopyTexSubImage2D.

  glBindTexture(GL_TEXTURE_2D, textureID);    //A texture you have already created with glTexImage2D
  glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height);   //Copy back buffer to texture

Just like the case where you should use glTexSubImage2D instead of glTexImage2D, use glCopyTexSubImage2D instead of glCopyTexImage2D.

Render To Texture

To render directly to a texture, without doing a copy as above, use Framebuffer Objects.

Warning: NVIDIA's OpenGL driver has a known issue with using incomplete textures. If the texture is not texture complete, the FBO itself will be considered GL_FRAMEBUFFER_UNSUPPORTED, or will have GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT. This is a driver bug, as the OpenGL specification does not allow implementations to return either of these values simply because a texture is not yet complete. Until this is resolved in NVIDIA's drivers, it is advised to make sure that all textures have mipmap levels, and that all glTexParameteri values are properly set up for the format of the texture. For example, integral textures are not complete if the mag and min filters have any LINEAR fields.

Depth Testing Doesn't Work

You probably did not ask for a depth buffer. If you are using GLUT, glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL) GLUT_DEPTH asks for a depth buffer. Be sure to enable the depth testing with glEnable(GL_DEPTH_TEST) and call glDepthFunc(GL_LEQUAL).

No Alpha in the Framebuffer

Be sure you create a double buffered context and make sure you ask for a alpha component. With GLUT, you can call glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL) in which GL_RGBA asks for a alpha component.

glFinish and glFlush

Use glFlush if you are rendering directly to your window. It is better to have a double buffered window but if you have a case where you want to render to the window directly, then go ahead.

There are a lot of tutorial website that suggest you do this:

 glFlush();
 SwapBuffers();

This is unnecessary. The SwapBuffer command takes care of flushing and command processing.

What does glFlush do? It tells the driver to send all pending commands to the GPU immediately.

What does glFinish do? It tells the driver to send all pending commands to the GPU immediately and waits until all the commands have completed. This can take a lot of time.

The OpenGL specification never requires that you send a glFlush or a glFinish; all operations will execute in the order in which they were given. This even goes to accessing Buffer Objects; if the buffer object is being updated by OpenGL, the the specification requires that OpenGL automatically halt until this update is complete. You do not need to manually do a glFinish before accessing the buffer.

Therefore, you should only use glFinish when you are doing something that the specification specifically states will not be synchronous.

glDrawPixels

For good performance, use a format that is directly supported by the GPU. Use a format that causes the driver to basically to a memcpy to the GPU. Most graphics cards support GL_BGRA. Example:

  glDrawPixels(width, height, GL_BGRA, GL_UNSIGNED_BYTE, pixels);

However, it is recommened that you use a texture instead and just update the texture with glTexSubImage2D.

glEnableClientState(GL_INDEX_ARRAY)

What's wrong with this code?

  glBindBuffer(GL_ARRAY_BUFFER, vboid);
  glVertexPointer(3, GL_FLOAT, sizeof(vertex_format), 0);
  glNormalPointer(GL_FLOAT, sizeof(vertex_format), 20);
  glEnableClientState(GL_VERTEX_ARRAY);
  glEnableClientState(GL_NORMAL_ARRAY);
  glEnableClientState(GL_INDEX_ARRAY);
  glBindBuffer(GL_ELEMENT_ARRAY, iboid);
  glDrawRangeElements(....);

The problem is that GL_INDEX_ARRAY does not mean what this programmer thinks it does. GL_INDEX_ARRAY has nothing to do with indices for your glDrawRangeElements. This is for color index arrays.

Never use these. Just use a color array, as follows.

  glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex_format), X);
  glEnableClientState(GL_COLOR_ARRAY);

glInterleavedArrays

This call is for automatic interleaving of vertex data. Never do this. It is always preferable to use manual interleaving (by setting the stride parameter on the gl*Pointer calls appropriately).

An example of proper stride usage:

  struct MyVertex
  {
      float x, y, z;       //Vertex
      float nx, ny, nz;    //Normal
      float s0, t0;        //Texcoord0
      float s1, s2;        //Texcoord1
  };
  //-----------------
  glVertexPointer(3, GL_FLOAT, sizeof(MyVertex), offset);
  glNormalPointer(GL_FLOAT, sizeof(MyVertex), offset+sizeof(float)*3);
  glClientActiveTexture(GL_TEXTURE0);
  glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), offset+sizeof(float)*6);
  glClientActiveTexture(GL_TEXTURE1);
  glTexCoordPointer(2, GL_FLOAT, sizeof(MyVertex), offset+sizeof(float)*8);

GL_DOUBLE

glLoadMatrixd, glRotated and any other function that have to do with the double type. Most GPUs don't support GL_DOUBLE (double) so the driver will convert the data to GL_FLOAT (float) and send to the GPU. If you put GL_DOUBLE data in a VBO, the performance might even be much worst than immediate mode (immediate mode means glBegin, glVertex, glEnd). GL doesn't offer any better way to know what the GPU prefers.

Note that GL_DOUBLE may be useful in future hardware.

Misaligned vertex formats

During vertex specification, it is generally best if all of the components of a vertex format are aligned to 4 bytes. So if you do something like this:

  glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(vertex_format), X);

The next component of the format should be padded out from the end of the color by 1 byte. Thus, you should have:

  glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(vertex_format), 0);
  glVertexPointer(GL_FLOAT, sizeof(vertex_format), 4);

Instead of this:

  glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(vertex_format), 0);
  glVertexPointer(GL_FLOAT, sizeof(vertex_format), 3);

There is one extra byte of wasted space in the first one, but it is preferable to the misaligned float value.

Unsupported formats #3

  glTexImage2D(GL_TEXTURE2D, 0, GL_RGB8, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, pixels);

Although plenty of image formats like bmp, png, jpg are by default saved as 24 bit and this can save disk space, this is not what the GPU prefers. GPUs prefer multiple of 4 bytes. The driver will convert your data to GL_RGBA8 and it will set alpha to 255. GL doesn't offer any better way to know what the GPU prefers.

Unsupported formats #4

  glTexImage2D(GL_TEXTURE2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

The above is almost OK. The problem is the GL_RGBA. On certain platforms, the GPU prefers that red and blue be swapped (GL_BGRA).
If you supply GL_RGBA, then the driver will do the swapping for you which is slow.
On which platforms? Making a list would be too long but one example is x86+Windows and x64+Windows.

Swap Buffers

A modern OpenGL program should always use double buffering. A modern OpenGL program should also have a depth buffer.

Render sequence should be like this:

 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
 RenderScene();
 SwapBuffers(hdc);  //For Windows

In some programs, the programmer does not want to rerender the scene since the scene is heavy. He might simply call SwapBuffers (Windows) without clearing the buffer. This is risky since it might give unreliable results between different GPU/driver combination.

There are 2 options:

  1. You can set the rendering context correctly so that SwapBuffers copies the back buffer rather than switches it. In Windows, this means setting the dwFlags in the PIXELFORMATDESCRIPTOR to PFD_SWAP_COPY.
  2. Render to a framebuffer object and blit to the back buffer, then SwapBuffers.

The Pixel Ownership Problem

If your windows is covered or if it is partially covered or if window is outside the desktop area, the GPU might not render to those portions.

This is explained in the OpenGL specification. It is called undefined behavior since on some platforms/GPU/driver combinations it will work just fine and on others it will not.

The solution is to make an offscreen buffer (FBO) and render to the FBO.

glAreTexturesResident and Video Memory

glAreTexturesResident doesn't necessarily return the value that you think it should return. On some implementations, it would return always TRUE and on others, it returns TRUE when it's loaded into video memory. A modern OpenGL program should not use this function.

If you need to find out how much video memory your video card has, you need to ask the OS. GL doesn't provide a function since GL is intended to be multiplatform and on some systems, there is no such thing as dedicated video memory.

Even if your OS tells you how much VRAM there is, it's difficult for an application to predict what it should do. It is better to offer the user a feature in your program that let's him controls "quality".

ATI/AMD created GL_ATI_meminfo. This extension is very easy to use. You basically need to call glGetIntegerv with the appropriate token values.
http://www.opengl.org/registry/specs/ATI/meminfo.txt

Selection and Picking and Feedback Mode

A modern OpenGL program should not use the selection buffer or feedback mode. These are not 3D graphics rendering features yet they have been added to GL since version 1.0. Selection and feedback runs in software (CPU side). On some implementations, when used along with VBOs, it has been reported that performance is lousy.

A modern OpenGL program should do color picking (render each object with some unique color and glReadPixels to find out what object your mouse was on) or do the picking with some 3rd party mathematics library.

GL_POINTS and GL_LINES

This will be about the problems related to GL_POINTS and GL_LINES.

Users notice that on some implementation points or lines are rendered a little different then on others. This is because the GL spec allows some flexibility. On some implementation, when you call:

 glPointSize(5.0);
 glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 glEnable(GL_BLEND);
 glEnable(GL_POINT_SMOOTH);
 RenderMyPoints();

the points will look nice and round, on other GPU/drivers it would look like squares.

On some implementations, when you call glEnable(GL_POINT_SMOOTH) or glEnable(GL_LINE_SMOOTH) and you use shaders at the same time, your rendering speed goes down to 0.1 FPS. This is because the driver does software rendering. This would happen on AMD/ATI GPUs/drivers.

Color Index, The imaging subset

Section 3.6.2 of the GL specification talks about the imaging subset. glColorTable and related operations are part of this subset. They are typically not supported by common GPUs and are software emulated. It is recommended that you avoid it.

If you find that your texture memory consumption is too high, use texture compression. If you really want to use paletted color indexed textures, you can implement this yourself a texture and a shader.

Bitfield enumerators

Some OpenGL enumerators represent bits in a particular bitfield. All of these end in _BIT (before any extension suffix). Thus, if you find yourself doing:

 glPushAttrib(GL_BLEND | GL_DRAW_BUFFER);

You know this is wrong. Because neither of these enumerators ends in _BIT, they are not bitfields and thus cannot be or'd together. By contrast:

 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

This is perfectly fine. All of these end in _BIT, so this makes sense.

Triple Buffering

You cannot control whether a driver does triple buffering. You could try to implement it yourself using a FBO. But if the driver is already doing triple buffering, your code will only turn it into quadruple buffering. Which is usually overkill.

Paletted textures

Do not use the GL_EXT_paletted_texture extension. Support for it has been dropped by the major GL vendors.

If you really need paletted textures, you may use shaders to achieve that effect. For example:

 //Fragment shader
 uniform sampler2D ColorTable;
 uniform sampler2D MyIndexTexture;
 varying vec2 TexCoord0;
 void main()
 {
   //What color do we want to index?
   vec4 myindex = texture2D(MyIndexTexture, TexCoord0);
   //Do a dependency texture read
   vec4 texel = texture2D(ColorTable, myindex.xy);
   gl_FragColor = texel;   //Output the color
 }

ColorTable might be in a format of your choice such as GL_RGBA8. ColorTable could be a 1D texture 256 elements in size.

MyIndexTexture can be in any format such as GL_R8UI (GL_R8UI is available in GL 3.0). MyIndexTexture could be of any dimension such as 64 x 32.

We read MyIndexTexture and we use this result as a texcoord to read ColorTable. If you wish to perform palette animation, or simply update the colors in the color table, you can submit new values to ColorTable with glTexSubImage1D. Assuming that the color table is in GL_RGBA format:

 glBindTexture(GL_TEXTURE_1D, myColorTableID);
 glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_BGRA, GL_UNSIGNED_BYTE, mypixels);

Notice that the format is GL_BGRA. As explain before, most GPUs prefer the BGRA format; using RGB, BGR and RGBA results in lower performance.