Jump to content

MMAPG - Direct Interface Video Extensions (DIVE)

From EDM2
Multimedia Application Programming Guide
  1. Introduction
  2. What's New...
  3. Multimedia Application Programming Environment
  4. Media Control Interface
  5. Multimedia Logical Devices
  6. Amplifier-Mixer Device
  7. Waveform Audio Device
  8. Sequencer Device
  9. CD Audio Device
  10. CD-XA Device
  11. Videodisc Device
  12. Digital Video Device
  13. Direct Interface Video Extensions (DIVE)
  14. Captioning
  15. OS/2 Multimedia Controls
  16. Multimedia I/O File Services
  17. Resource Interchange File Format (RIFF) Services
  18. Sample Application Programs
  19. Installing a Program Using MINSTALL
  20. Caption DLL
  21. High-Level Service API
  22. Notices
  23. Glossary

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

Software motion video implementation under OS/2 has attained sizable performance advantages by enabling video decompressors to directly write to video memory. While this technique provides good performance, it has the disadvantage that each decompressor must deal with the pel format of the display in various modes, clipping the output to visible regions, and any scaling that is to be performed. Additionally, on bank-switched video displays, the decompressor must return on partial frames to enable the video stream handler to switch banks. The direct interface video extensions (DIVE) consolidate the complexities of dealing with direct video frame buffer access (sometimes referred to as 'direct access' or 'black hole') into a single API DLL that enables efficient transfer to video memory with clipping, scaling, and color space conversion. The optimized screen access functionality provided by DIVE can be used for motion video display, fast image updates for interactive games, and fast screen display by 3-D graphics libraries.

About DIVE

DIVE is a DLL that provides optimized blitting performance for motion video subsystems and applications that perform rapid screen updates in the OS/2 PM and full-screen environments. Using DIVE functions, applications can either write directly to video memory or use the DIVE blitter to get a high level of screen update performance, image scaling, color space conversion, and bank-switch display support. The DIVE blitter utilizes acceleration hardware when present and applicable to the function being performed.

DIVE Display Engine Functional Characteristics

The DIVE system-level interface abstracts the low-level device driver DIVE interface to a higher level and adds software emulation for operations that digital video CODECs were previously required to do. This system-level interface is referred to as the DIVE display engine.

The DIVE display engine consists of a single, stand-alone DLL that exports the following functions:

  • Query for display capabilities
  • Open instance
  • Visible region specification
  • Allocation of image data buffers
  • Buffer-to-screen/buffer-to-buffer transfer (blitter) setup
  • 8-bit CLUT color palette simulation/specification
  • Transfer buffer to image display/transfer buffer to buffer
  • Utility functions useful for direct access, including window starting-address calculation
  • Frame buffer acquire/deacquire
  • VRAM bank selection (for bank-switched displays)
  • Close instance

The display engine enables subsystem components (for example, video CODECs) and applications to either directly access the video buffer or to use the display engine screen transfer functions. If direct access is used, the using component or application is responsible for writing to the frame buffer format for the current display mode and correctly clipping the output. In addition, the component is responsible for acquiring and bank switching the display apertures. If display engine screen transfer functions are used, the display engine handles clipping, pel format and color space conversions, and any necessary hardware serialization. The input formats and their corresponding color encoding specification values are:

  • CLUT 8 (256 color) - 'LUT8'
  • 8-bit greyscale - 'GREY'
  • RGB 16 (5-6-5, 5-5-5) - 'R565', 'R555', 'R664'
  • RGB 24 (R-G-B, B-G-R) - 'RGB3', 'BGR3'
  • RGB 32 (R-G-B, B-G-R) - 'RGB4', 'BGR4'
  • YUV 9 - DVI/Indeo three-plane color subsampled - 'YUV9'
  • YUV 422 - 'Y422'
  • YUV CCIR601 - three-plane 2x2 color subsampled (MJPEG, MPEG) - 'Y2X2'
  • YUV CCIR601 - three-plane 4x4 color subsampled - 'Y4X4'

The output formats are:

  • CLUT 8 (256 color)
  • RGB 16 (5-6-5, 5-5-5, 6-6-4)
  • RGB 24 (R-G-B, B-G-R)
  • RGB 32 (R-G-B-x, B-G-R-x)
  • YUV 422 (Y-U-Y-V)

Using Dive

There are two main ways to use DIVE: using the DIVE blitter and using direct frame-buffer access. DIVE applications gain access to DIVE functions by obtaining a DIVE handle:

ULONG        ulErrorCode;
HDIVE        *phDiveInst;
BOOL         fNonScreenInstance;
PVOID        ppFrameBuffer;

ulErrorCode = DiveOpen( *phDiveInst, fNonScreenInstance, ppFrameBuffer );

A corresponding DiveClose function must be called at application termination. If DIVE is to be used for blitting to the screen, set fNonScreenInstance to FALSE. Otherwise, if DIVE is to be used only for off-screen sizing or color format conversion, set fNonScreenInstance to TRUE. If fNonScreenInstance is FALSE, a pointer to the frame buffer is returned in ppFrameBuffer.

DIVE Image Buffers

Because DIVE will use off-screen VRAM where available for acceleration of blitting operations, the application should allocate all source blitting buffers from DIVE whenever possible. To allocate a buffer, the application would make the following call:

ULONG   ulBufNum;
FOURCC  fccColorSpace;
ULONG   ulWidth, ulHeight, ulLineSizeBytes;
PBYTE   pbImageBuffer;

ulErrorCode = DiveAllocImageBuffer(
        hDive,          /* DIVE handle  */
        &ulBufNum,     /* Buffer number (output)       */
        fccColorSpace,  /* Color format */
        ulWidth, ulHeight,     /* Size of maximum image        */
        ulLineSizeBytes,
        &pbImageBuffer);

A corresponding DiveFreeImageBuffer function call is used to deallocate the buffer when it is no longer needed. The color format of the image buffer is described by fccColorSpace. The DIVE interface 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 shown in the following example:

PBYTE   pbImageBuffer;
ULONG   ulBufferScanLineBytes, ulBufferScanLines;

ulErrorCode = DiveBeginImageBufferAccess(
        hDiveInst,          /* DIVE handle   */
        ulBufferNumber,       /* Buffer number */
        &pbImageBuffer,    /* Ptr to image buffer (output) */
        &ulBufferScanLineBytes);  /* Scan line length (output)    */
        &ulBufferScanLines);  /* Scan lines (output)    */

DIVE calculates the number of bytes per scan line for the image buffer (based on the color format) and returns the value in ulBufferScanLineBytes. The application can now write color data into pbImageBuffer. For example, the application could open a bit-map file and read the bit-map data directly into the image buffer. After the data has been written, the application calls DiveEndImageBufferAccess to deaccess the buffer. Be sure to use scan line bytes (you might have to read a line at a time).

DIVE Palettes

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 must also be communicated to DIVE through the DiveSetSourcePalette function. 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, it is necessary to call DiveSetSourcePalette only once before blitting the first image in the series.

DIVE provides high-speed screen updates by bypassing PM. In order 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.

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, and the second parameter turns on notification for that window. (A corresponding WinSetVisibleRegionNotify(hwnd, FALSE) call should be made to turn notification off at window termination time.)

Whenever the window's visible region begins to change, either because the window is being moved or sized or another window or icon overlaying the window is being moved or sized, the window will receive a WM_VRNDISABLED message. In response to this message, the DIVE application will call DiveSetupBlitter (hDiveInst, 0). Once the movement is complete, 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, or image source or destination size changes, the DIVE application must pass the changes to DIVE with a call to DiveSetupBlitter. Note that it is necessary to 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];      /* Rectangles for visible rgn   */
RGNRECT rgnCtl;           /* Region control struct         */
SETUP_BLITTER   SetupBlitter;   /* DiveSetupBlitter struct       */
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.fInvert = 0;
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 = 0;
SetupBlitter.ulDstPosY = 0;
SetupBlitter.lScreenPosX = pointl.x;
SetupBlitter.lScreenPosY = pointl.y;
SetupBlitter.ulNumDstRects = rgnCtl.crcReturned;
SetupBlitter.pVisDstRects = rctls;
DiveSetupBlitter ( hDive, &SetupBlitter );

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

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

Blitter Operation

The following illustrates buffer-to-buffer transfer using DiveBlitImage.

The following illustrates buffer-to-screen transfer using DiveBlitImage.

Note: The screen cannot be used as a source for blitting using DIVE.

Transparent Blitting

DIVE transparent blitting functions enable an interactive game or imaging application to create composites of graphics and image data using a transparency key color.

To set up for transparent blitting, the application calls DiveSetTransparentBlitMode before it calls DiveSetupBlitter.

ULONG APIENTRY DiveSetTransparentBlitMode (HDIVE hDiveInst,
                                           ULONG ulTransBlitMode,
                                           ULONG ulValue1,
                                           ULONG ulValue2);

The following transparent blitting modes can be specified for ulTransBlitMode:

  • DIVE_TBM_NONE
  • DIVE_TBM_EXCLUDE_SOURCE_VALUE
  • DIVE_TBM_EXCLUDE_SOURCE_RGB_RANGE
  • DIVE_TBM_INCLUDE_SOURCE_RGB_RANGE
  • DIVE_TBM_EXCLUDE_SOURCE_YUV_RANGE
  • DIVE_TBM_INCLUDE_SOURCE_YUV_RANGE

Transparent blitting functions are based on source pixel values. If the pixel value in the source image buffer is the key color, the value of the corresponding pixel in the destination buffer is not modified. Interpretation of color values specified in ulValue1 and ulValue2 parameters is dependent on the source image color format specified in the fccSrcColorFormat field of the SetupBlitter structure and the transparent blitting mode.

Direct Frame-Buffer Access

As mentioned earlier, *ppFrameBuffer returned by DiveOpen gives direct addressability to the frame buffer. In order to write directly to the frame buffer, the DIVE application must perform its own clipping, color conversion, and bank switching. The following functions are provided for direct frame-buffer access: DiveAcquireFrameBuffer, DiveDeacquireFrameBuffer, DiveSwitchBank, and DiveCalcFrameBufferAddress.

The DiveQueryCaps function returns whether the display subsystem is bank-switched. DIVE provides another function called DiveCalcFrameBufferAddress to get to a location in the frame buffer that corresponds to a rectangle in desktop coordinates:

PRECTL prectlDest;            /* Image rectangle in desktop coors  */
PVOID pDestinationAddress;    /* Frame buffer address - output     */
PULONG pulBankNumber;           /* Display h/w bank number - output  */
PULONG pulRemlinesInBank;       /* Lines left in bank - output        */

ulErrorCode = DiveCalcFrameBufferAddress(
        hDiveInst, &prectlDest, &pDestinationAddress,
        &pulBankNumber, &pulRemlinesInBank);

To accomplish correct clipping, prectlDest must be in the application window's visible region. If the display hardware is bank-switched, then the application must not write more than pulRemlinesInBank 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 DiveBlitImage 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 and DiveDeacquireFrameBuffer. Also, the application must not attempt to access the frame buffer between receipt of a WM_VRNDISABLED message and a WM_VRNENABLED message.

A typical application would do the following:

BOOL  fKeepBlitting = TRUE;
BOOL  fFBAccessOK;
RECTL rectlOutput;      /* Image rectangle in desktop coors  */
RECTL rectlDest;        /* Portion to blit in this bank */
ULONG ulMoreLines;      /* Lines in image left to blit  */
LONG  lBlitTop;         /* Top of next blit     */
PVOID pDestinationAddress;   /* Frame buffer address - output */
ULONG ulBankNumber;           /* Display h/w bank number - output */
ULONG ulRemlinesInAperature; /* Lines left in bank - output  */
BOOL  fAcquired = FALSE;     /* Acquired frame buffer yet    */

while (fKeepBlitting)
  {
  /* ... Call DiveSetupBlitter as before ...    */

  /********************************************************/
  /* Calculate total number of lines to blit.  Then blit  */
  /* only those lines that will fit in the current bank.  */
  /* Switch to successive banks until the entire image is */
  /* blitted.                                             */
  /********************************************************/
  ulMoreLines = rectlDest.yTop - rectlDest.yBottom;
  memcpy( &rectlDest, &rectlOutput, sizeof(RECTL));
  while (ulMoreLines && fFBAccessOK)
    {
    ulErrorCode = DiveCalcFrameBufferAddress(
        hDive, rectlDest, &pDestinationAddress,
        &ulBankNumber, &ulRemlinesInAperture);
    if (!fAcquired)
      if (!DiveAcquireFrameBuffer( hDive, ulBankNumber))
        fAcquired = TRUE;
      else break;
    DiveSwitchBank( hDive, ulBankNumber);
      {
      /* ... write data for (ulRemlinesInAperture) top lines of */
      /* rectlDest to pDestinationAddress ...   */
      if (ulRemlinesInAperture < ulMoreLines)
        {               /* if need next bank    */
        rectlDest.yTop -= ulRemlinesInAperture;
        ulMoreLines -= ulRemlinesInAperture;
        }
      else ulMoreLines = 0;
      }
    if (fAcquired)
      DiveDeacquireFrameBuffer( hDive);
    }           /* end: while more lines to blit */
  }             /* end: blitter loop    */

Note: This method works only on even bank breaks; indirect access through DiveBlitImage is recommended on displays with bank breaks that are not aligned on scan-line boundaries.

In the previous example, the application spins off a separate thread to perform blitting. The procedure for this thread contains a nested loop that switches display banks for each image blitted as long as blitting is needed. The flag fFBAccessOK is turned off whenever the application window received WM_VRNDISABLED and is turned on whenever WM_VRNENABLED is received.