Brian Beach
Note: This document uses the O2 VL DMbuffer API instead of the cross-platform VL DMbuffer API (see What Are the SGI Video-Related Libraries? for more info). It refers to IRIX 6.3 man pages and documentation.
DMbuffers are used to store images in various formats, which can be passed from video to graphics, graphics to video, video to programs, and programs to video.
For a general introduction to DMbuffers, see Chapter 5 of the IRIX 6.3 Digital Media Programming Guide. The short summary is that an application wanting to use DMbuffers for video and graphics first creates a pool of buffers, specifying the number of buffers in the pool. Then, buffers can be
The trick to using DMbuffers effectively is making sure that the format of the images in them is compatible with both the image producer and the image consumer.
Before getting into the details of DMbuffers and how they are used with the digital media libraries, let's take a look at image formats. There are two basic image layouts in the O2 machine: linear and graphics.
The linear layout is familiar one. It comes in two varieties: top-to-bottom and bottom-to-top. With top-to-bottom (the natural order for video), the first pixel in a image buffer is the upper-left pixel. Following it are the rest of the pixels on the top row. After the top row is the next-to-the-top row, and so on, finally ending at the bottom-right pixel. This is the scan order for a linear image:
OpenGL more naturally supports the bottom-to-top order. More about this later.
The other layout is called the graphics layout. It is the internal image layout used by the CRM graphics engine. The details of the layout are not published; SGI reserves the right to change this layout at any time. The basic idea is that the image is divided into rectangular tiles, which allow the rendering engine to be more efficient. The scan order looks something like this:
Video on O2 supports both linear and graphics layouts. glDrawPixels and glReadPixels support only the linear layout, but images in pbuffers (see below) are always in the graphics layout.
There are three pixel formats that can be used both by video and graphics. The video library (libvl), the digital media library (libdmedia), and the graphics library (libGL) all have different names for the same things. The names are:
libvl | libdmedia | libGL | |
rgba-8888 | VL_PACKING_ABGR8 | DM_IMAGE_PACKING_RGBA | GL_RGBA |
rgba-5551 | VL_PACKING_ARGB_1555 | DM_IMAGE_PACKING_XRGB5551 | GL_RGB5_A1_EXT |
rgb-332 | VL_PACKING_X444_332 | DM_IMAGE_PACKING_RGB332 | GL_RGB with GL_UNSIGNED_BYTE_3_3_2_EXT |
The man page, DMbuffer(4), has an overview of DMbuffers, what they are, and how they are used. For our purposes here, suffice it to say that a DMbuffer is a chunk of memory, obtained from dmBufferAllocate(), vlEventToDMBuffer(), or dmICReceive(), and is used to store a single image.
Before you can use DMbuffers with OpenGL or dmIC, you must first create a DMparams that describes the format of the image. To do this, you need to know the size of the image (width and height), the pixel packing, and the image layout (graphics or linear). Here is code that creates a DMparams for linear, top-to-bottom, 640x480, RGBA-8888 images:
DMparams* imageFormat; DMstatus s; s = dmParamsCreate( &imageFormat ); if ( s != DM_SUCCESS ) error(); s = dmSetImageDefaults( imageFormat, 640, 480, DM_IMAGE_PACKING_RGBA ); if ( s != DM_SUCCESS ) error(); s = dmParamsSetEnum( imageFormat, DM_IMAGE_ORIENTATION, DM_TOP_TO_BOTTOM ); if ( s != DM_SUCCESS ) error(); s = dmParamsSetEnum( imageFormat, DM_IMAGE_LAYOUT, DM_IMAGE_LAYOUT_LINEAR ); if ( s != DM_SUCCESS ) error();
For images in the graphics layout, the DM_IMAGE_ORIENTATION parameter is meaningless.
To set up a pool of DMbuffers that can be used with video and graphics, you do something like this:
DMparams* imageFormat; /* see above */ VLServer server; VLPath path; VLNode node; DMbufferpool pool; /* Set up the video path before trying to create the pool. */ { DMstatus s; int v; DMparams* poolSpec; int count = NUMBER_OF_BUFFERS_NEEDED_BY_APPLICATION; DMboolean cacheable = DM_FALSE; DMboolean mapped = DM_FALSE; s = dmParamsCreate( &poolSpec ); if ( s != DM_SUCCESS ) error(); s = dmBufferSetPoolDefaults( poolSpec, count, 0, cacheable, mapped ); if ( s != DM_SUCCESS ) error(); v = vlDMPoolGetParams( server, path, node, poolSpec ); if ( v != 0 ) error(); count += dmParamsGetInt( poolSpec, DM_BUFFER_COUNT ); dmParamsSetInt( poolSpec, DM_BUFFER_COUNT, 0 ); s = dmBufferGetGLPoolParams( imageFormat, poolSpec ); if ( s != DM_SUCCESS ) error(); dmParamsSetInt( poolSpec, DM_BUFFER_COUNT, count ); s = dmBufferCreatePool( poolSpec, &pool ); if ( s != DM_SUCCESS ) error(); dmParamsDestroy( poolSpec ); }
The count is the number of buffers in the pool. When a pool is created, it has a fixed number of buffers that never changes. To figure out the number of buffers required in the pool, you must add up all of the simultaneous requirements:
vlDMPoolGetParams will increase the BUFFER_COUNT in the parameter list to the minimum required for that one video path. To find out the actual number required by the path, BUFFER_COUNT should be 0 when it is called, and then queried afterwards.
A single buffer pool can be created once and then used for multiple purposes. To use one pool with multiple video paths, just call once for each video path before creating the pool. If the paths are to be used simultaneously, the buffer counts from each call should be added.
IMPORTANT: never call dmBufferMapData() on a buffer containing an image in the graphics layout. Cache coherency is not guaranteed if you do this.
To get video into a DMbuffer:
To take a DMbuffer that already contains an image and send it to a video output device:
The DMbuffer extension to OpenGL allows you to create a pbuffer, and then attach a DMbuffer to it to use as the color buffer. See the man page for glXAssociateDMPbufferSGIX (it's not in 6.3, but should be in 6.3.1).
Before you can associate a DMbuffer width a pbuffer, you must first create a special kind of pbuffer, called a digital media pbuffer. You do it like this:
GLXFBConfigSGIX config = ...; int width = 640; int height = 480; GLXFBconfig int attribs [] = { GLX_DIGITAL_MEDIA_PBUFFER_SGIX, True, GLX_PRESERVED_CONTENTS_SGIX, True, (int) None }; GLXPbufferSGIX pbuffer; pbuffer = glXCreateGLXPbufferSGIX( display, config, width, height, attribs ); if ( pbuffer == None ) error();
The frame buffer configuration that is used when the pbuffer is created must match the image format of the image in the DMbuffer that will be used with it. This means that the width and the height must match, the image layout must be "graphics", and the pixel packing must match the depth of the red, green, blue, and alpha planes in the frame buffer. The allowed pixel packings are: DM_IMAGE_PACKING_RGBA, and DM_IMAGE_PACKING_XRGB1555, and DM_IMAGE_PACKING_RGB332.
The code above will create a pbuffer that does not yet have an associated color buffer, but will have all of the other buffers (depth, stencil, accumulation, etc.) that are specified in the fbconfig:
Allocating a DMbuffer from a pool is easy:
DMbufferpool pool = ...; DMstatus s; DMbuffer buffer; s = dmBufferAllocate( pool, &buffer ); if ( s != DM_SUCCESS ) error();
Now you have a DMbuffer and a pbuffer:
The next step is to associate the DMbuffer with the pbuffer. This makes the DMbuffer be the color buffer.
DMparams* imageFormat = ...; DMbuffer buffer = ...; Bool ok; ok = glXAssociateDMPbufferSGIX( display, pbuffer, imageFormat, buffer ); if ( ! ok ) error();
As noted above, the imageFormat must specify that the layout is graphics, must match the width and height of the pbuffer, and must have a compatible pixel packing. This will make a link from the pbuffer to the DMbuffer, so that any rendering done to the pbuffer will be drawn into the DMbuffer.
Before you can render, of course, you must have a graphics context. This is created in the normal way, but must be created using a visual or fbconfig that is compatible with the pbuffer. Once you have a context, you can make the pbuffer current with a context:
GLXContext context = ...; Bool ok; ok = glXMakeCurrent( display, pbuffer, context );
This makes the context current, and now, any rending that happens will be done into the DMbuffer:
In addition to rendering into a DMbuffer, you can also use the contents of a DMbuffer as a texture. This is done by associating the DMbuffer with a pbuffer and then using glCopyTexSubImage2DEXT to load the contents of the DMbuffer as a texture. When certain criteria are met, the image is not actually copied, but is used as a texture directly.
For this example, say you have an image in a DMbuffer that you want to use as a texture when drawing to a window. This image may have come from a video input path, or it may have been rendered with OpenGL. Either way, the procedure for using it as a texture is the same. You start with a window and a pbuffer/DMbuffer pair:
Calling glXMakeCurrentReadSGI will attach a context so that the glCopy calls (glCopyPixels, glCopyTexImage2D, etc.) will copy pixels from the DMbuffer to the window or to texture:
At this point, any of the glCopyTexImage calls will get the image into texture memory. But, if certain criteria are met, then glCopyTexSubImage2DEXT will not actually copy. It will free the memory holding the previous texture and use the DMbuffer instead:
The conditions that enable this "copy by reference" are (see the man page for glCopyTexSubImage2DEXT):
When creating the texture to "copy" into, specifying GL_RGBA8_EXT, instead of GL_RGBA, will force the depth to be 8 bits per component. Normally, the texture will be the same depth as the current drawable.