DIVEing into OS/2 Games!

From EDM2
Jump to: navigation, search

by Darren Dobkin

Entertainment/education (edutainment) and games software developers all need to perform high-speed animated graphics. The OS/2 operating system has, until now, presented a dilemma for them, with the only choices being VIO APIs in a full-screen session or Presentation Manager.

Either choice came with several disadvantages. The VIO APIs permit fast screen updates, but the synchronized, device-independent, file-format independent multimedia support provided by MMPM/2 is unavailable to VIO programs. Additionally, IBM has tried to discourage use of the VIO APIs by removing them from the documentation in the OS/2 2.x Toolkits.

PM, on the other hand, preserves the integrity of the desktop session by denying frame buffer access to all applications. PM applications can use GpiBitBlt for animated graphics, but the performance is unacceptable for edutainment programs and interactive games.

In addition, many hardware manufactures such as Tseng Labs, Weitek, and S3 Inc. are preparing video acceleration chipsets for their upcoming models that will provide hardware support for the demanding tasks of color conversion, image clipping and display memory bank-switching. Ideally, the OS/2 games programmer must be able to perform high-speed blitting within the context of the PM desktop and also be in a position to exploit the new acceleration hardware when it becomes available.

The Dream Becomes a Reality

The next version of OS/2 will usher in a new era of PM programming that will solve both of these problems and enable edutainment programs and games that we could previously only dream about. These programs will use high-speed graphics comparable to the hottest sellers in the DOS market, yet they will run simultaneously with other applications, sharing audio and screen resources. These programs will combine the double-buffered, fast-blitting techniques of the DOS games market with the message-driven, window-oriented, multithreaded, high-level APIs of the PM and MMPM/2 programming world. They will benefit from new hardware improvements without any code modification.

The next generation of display hardware, through a new API set called the Direct Interface Video Extension (DIVE), will provide relief for the PM performance dilemma and compatibility. Not only will DIVE provide a new method for fast blitting of image data, but also provide support for color-depth conversion, display hardware bank-switching, visible region clipping, and stretch-blitting either to the screen or a destination image buffer. DIVE also will take advantage of stretch-blitting acceleration hardware - whenever such hardware is available. Finally, DIVE will provide the holy grail of the OS/2 game developer: a pointer to the frame buffer.

Some Practical Matters

DIVE applications gain access to DIVE functions by getting a DIVE handle as follows:

ULONG ulErrorCode;
HDIVE hDive;
BOOL fUseScreen;
PPVOID ppFrameBuffer;

ulErrorCode = DiveOpen( &hDive, fUseScreen, ppFrameBuffer);

The corresponding DiveClose( hDive ) must be called at application termination.

If DIVE is to be used for blitting to the screen, set fUseScreen to TRUE. This returns a pointer to the frame buffer in *ppFrameBuffer. If DIVE is to be used only for off-screen stretch-blitting and color-format conversion, set fUseScreen to FALSE.

DIVE Image Buffers

DIVE will use off-screen VRAM where available for acceleration of blitting operations, so the application must allocate all source and destination blitting buffers from DIVE. To allocate a buffer, the application can make the following call:

ULONG   ulBufNum;
FOURCC  fccColorSpace;
ULONG   ulWidth, ulHeight;
ulErrorCode = DiveAllocImageBuffer(
              hDive,                     /* DIVE handle            */
              &ulBufNum,                 /* buffer number (output) */
              fccColorSpace,             /* color format           */
              ulWidth, ulHeight);        /* size of maximum image  */

The corresponding DiveFreeImageBuffer(Dive, ulBufNum) call deallocates the buffer.

The color format of the image buffer is described by fccColorSpace. The DIVE API defines constants for a variety of 8-, 16-, and 24-bit color encoding schemes.

After a buffer is allocated and before it can be used for blitting, it must be accessed as follows:

PBYTE pbImageBuffer;
ULONG ulScanLineBytes;
ulErrorCode = DiveBeginImageBufferAccess(
              hDive,                /* DIVE handle                 */
              ulBufNum,             /* buffer number               */
              &pbImageBuffer,       /* ptr to image buffer(output) */
              &ulScanLineBytes);    /* scan line length (output)   */

DIVE calculates the number of bytes-per-scanline for the image buffer (based on the color format) and returns the value in ulScanLineBytes. The application can now write color data into pbImageBuffer. For example, the application can open a bitmap file and read the bitmap data directly into the image buffer. After the data has been written, the application calls DiveEndImageBufferAccess( hDive, ulBufNum) to deaccess the buffer.

DIVE Palettes

DIVE applications must inform DIVE of the state of the physical palette upon initialization and whenever the physical palette changes. At application initialization, and in response to a WM_REALIZEPALETTE message, the application calls the following sequence:

BYTE pbPal[1024];
/* Query the physical palette from PM   */
GpiQueryRealColors( hps, 0, 0, 256, (PLONG)pbPal);
/* Pass it to DIVE                      */
DiveSetDestinationPalette( hDive, (PBYTE)pbPal);

If the application itself is using palettes, these palettes also must be communicated to DIVE through the DiveSetSourcePalette API. For example, if the application is using DIVE to blit 256-color palettized images to the screen, the application must send the image palette with a call to DiveSetSourcePalette. If a sequence of images is being used for animation, and the palette remains constant through the series, you must call DiveSetSourcePalette only once before blitting the first image in the series.

DIVE provides high-speed screen updates by bypassing PM. However, to maintain the integrity of the PM desktop. DIVE applications must notify DIVE whenever the visible region of the application window changes so that output may be clipped accordingly. For the application to provide this information, a small set of new PM APIs for visible region notification will be made available.

The visible region notification APIs are public versions of a private interface originally developed to enable software motion video in MMPM/2. DIVE is based on bank-switching, color conversion, and clipping technology developed for the software motion video stream handler and the Ultimotion and Indeo CODECs. Therefore, expect that the graphics performance of DIVE applications will be comparable to the frame rates achieved by MMPM/2 software motion video.

Every DIVE application will request visible region notification at window initialization time with the following call:

WinSetVisibleRegionNotify( hwnd, TRUE);

The first parameter is the handle of the window where the graphics operations will appear; the second parameter turns on notification for that window. A corresponding WinSetVisibleRegionNotify( wnd, FALSE) call turns off notification at window termination time. Whenever the window's visible region changes, either because the window is moved or sized, or another window or icon overlaying the window is moved or sized, the window will receive a WM_VRNENABLED message. In response to this message, the DIVE application will query the new visible region using WinQueryVisibleRegion as follows:

hps = WinGetPS( hwnd );
hrgn = GpiCreateRegion( hps, 0, NULL);
WinQueryVisibleRegion( hwnd, hrgn);

Whenever the visible region, source-color format, image source, or destination size change, the DIVE application must pass the changes to DIVE with a call to DiveSetupBlitter. The application must pass the rectangles for the region in window coordinates and the position of the window in desktop coordinates.

First, get the rectangles and window coordinates:

RECTL  rctls[50];               /* rects for visible rgn  */
RGNRECT rgnCtl;                 /* region control struct  */
SETUP_BLITTER   SetupBlitter;   /* DiveSetupBlitter stuct */
POINTL  pointl;
SWP     swp;
HPS     hps;
HRGN    hrgn;
rgnCtl.ircStart = 0;            /* enumerate rectangles   */
rgnCtl.crc = 50;                /* starting with first    */
rgnCtl.ulDirection = RECTDIR_LFRT_TOPBOT;
/* get rectangles for the visible region                  */
GpiQueryRegionRects( hps, hrgn, NULL, &rgnCtl, rctls);
/* find the window position relative to its parent        */
WinQueryWindowPos( hwnd, &swp );
/* map window position to the desktop                     */
pointl.x = swp.x;
pointl.y = swp.y;
WinMapWindowPoints( WinQueryWindow( hwnd, QW_PARENT ),
                    HWND_DESKTOP, &pointl, 1);

Then, pass the information to DIVE:

/* tell DIVE about the new settings                        */
SIZEL   sizlSrcImg;                /* size of source image */
FOURCC  fccSrcColors;              /* source image format  */
SetupBlitter.ulStructLen = sizeof ( SETUP_BLITTER );
SetupBlitter.fccSrcColorFormat = fccSrcColors;
SetupBlitter.ulSrcLineSizeBytes = ulScanLineBytes;
SetupBlitter.ulSrcWidth = sizlSrcImg.cx;
SetupBlitter.ulSrcHeight = sizlSrcImg.cy;
SetupBlitter.ulSrcPosX = 0;
SetupBlitter.ulSrcPosY = 0;
SetupBlitter.fccDstColorFormat = FOURCC_SCRN;
SetupBlitter.ulDstLineSizeBytes = 0;
SetupBlitter.ulDstWidth = swp.cx;
SetupBlitter.ulDstHeight = swp.cy;
SetupBlitter.ulDstPosX = pointl.x;
SetupBlitter.ulDstPosY = pointl.y;
SetupBlitter.ulNumDstRects = rgnCtl.crcReturned;
SetupBlitter.pVisDstRects = rctls;
DiveSetupBlitter ( hDive, &SetupBlitter );

The color format of the source image is described by fccSrcColors.

In this example, the DIVE blitter is set up to blit to the screen, but that need not be the case. You can also use DIVE to perform color-conversion and stretch-blitting to a destination image. Indicate the destination color encoding format in fccDstColorFormat; set ulDstWidth and ulDstHeight to the size of the destination image; set ulNumDstRects to 1; point pVisDstRects to a single rectangle with xLeft=yBottom=0, xRight=ulDstWidth, and yTop=ulDstHeight.

A new WM_VRNDISABLED PM message serves to notify the application that visible region notification has been suspended until the next WM_VRNENABLED message; the application must call DiveSetupBlitter (Dive, 0) when it receives this message.

Direct Frame Buffer Access

The *ppFrameBuffer returned by DiveOpen gives direct addressability to the frame buffer. To write directly to the frame buffer, the DIVE application must perform its own clipping, color conversion, and bank switching. The DiveQueryCaps API will return whether or not the display subsystem is bank-switched.

DIVE will provide another API called DiveCalcFrameBufferAddress to get to a location in the frame buffer that corresponds to a rectangle in desktop coordinates:

RECTL rectlDest;                /* image rect in desktop coors      */
PVOID pDestinationAddress;      /* frame buffer address - output    */
ULONG ulBankNumber;             /* display h/w bank number - output */
ULONG ulRemlinesInAperture;     /* lines left in bank - output      */
ulErrorCode = DiveCalcFrameBufferAddress(
              hDive, rectlDest, &pDestinationAddress,
              &ulBankNumber, &ulRemlinesInAperture);

To accomplish correct clipping, the rectlDest must be in the application window's visible region. If the display hardware is bank-switched, the application must not write more than ulRemlinesInAperture lines of output before switching banks. The data written to pDestinationAddress must be in the color-encoding scheme of the screen (also provided by DiveQueryCaps). (Of course DIVE can be used to convert to the correct screen color-encoding prior to writing to the frame buffer by doing a DIVE blit to a destination buffer with the same color-encoding.) Additionally, if the screen supports only 256 colors, the data must match the current physical palette.

All write access to the frame buffer must be bracketed by calls to DiveAcquireFrameBuffer / DiveDeacquireFrameBuffer. Also, the application must not attempt to access the frame buffer between receipt of a WM_VRNDISABLED message and a WM_VRNENABLED message.

The First in a Series

Among the first programs to exploit the new DIVE interface will be an OS/2 version of the popular interactive game - DOOM. IBM is backing third-party developer AIC's efforts to port DOOM to the PM desktop using DIVE. IBM and AIC are anticipating delivery of a superior action game that will share audio and screen resources with other programs, yet perform like its DOS counterpart. Several other such efforts to port existing games to OS/2 and develop new games especially for OS/2 are underway.

Enhancements in the next OS/2 release that will enable satisfactory performance in only 4MB of RAM will expand the appeal of OS/2 in the home market. The new DIVE APIs and MMPM/2's video, audio, and animation support will make OS/2 the games platform of choice.

Look to future issues of The Developer Connection News for more information on using the OS/2 platform to develop games.

Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation