TAKING A DIVE _____________ 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. ALL 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 FUNCTION 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 all DIVE users have had to do. This system-level interface is referred to as the DIVE engine. The DIVE engine consists of a single, stand-alone DLL that exports the following functions: o Query for display capabilities o Open instance o Visible region specification o Allocation of image data buffers o Buffer-to-screen/buffer-to-buffer transfer (blitter) setup o 8-bit CLUT color palette simulation/specification o Transfer buffer to image display/transfer buffer to buffer o Utility functions useful for direct access, including window starting-address calculation o Frame buffer acquire/de-acquire o VRAM bank selection (for bank-switched displays) o 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: o CLUT 8 (256 color) - "LUT8" o 8-bit greyscale - "GREY" o RGB 16 (5-6-5, 5-5-5) - "R565", "R555", "R664" o RGB 24 (R-G-B, B-G-R) - "RGB3", "BGR3" o RGB 32 (R-G-B, B-G-R) - "RGB4", "BGR4" o YUV 9 - DVI/Indeo three-plane color subsampled - "YUV9" o YUV 411 - "YUV411" o YUV 422 - "YUV422" o YUV CCIR601 - three-plane 2x2 color subsampled (MJPEG, MPEG) - "Y2X2" o YUV CCIR601 - three-plane 4x4 color subsampled - "Y4X4" The output formats are: o CLUT 8 (256 color) o RGB 16 (5-6-5, 5-5-5, 6-6-4) o RGB 24 (R-G-B, B-G-R) o RGB 32 (R-G-B-x, B-G-R-x) o Blitter Operation 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; PPVOID 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 BUFFER _________________ 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 PALETTE ____________ 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). After 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. 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: o DiveAcquireFrameBuffer o DiveDeacquireFrameBuffer o DiveSwitchBank o 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. This method works only on even bank breaks; indirect access through Dive BlitImage is recommended on displays with bank breaks that are not aligned on scan-line boundaries. In the next 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 when ever the application window received WM_VRNDISABLED and is turned on whenever WM_VRNENABLED is received. 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 */