Documentation
[SDK Documentation] [Using video hardware]

 

Using video hardware

This document has been updated for use with GapiDraw 4.0 or later.
Last updated on October 5, 2008.

 

Introduction

GapiDraw 4 can store surfaces in video memory ("video surfaces") on stationary PCs or Windows Mobile devices. Used properly, the use of video surfaces may improve performance considerably. Used incorrectly however, video surfaces may decrease application performance significantly.

This document outlines the implementation of video surfaces in GapiDraw, and also outlines some general guidelines or using video surfaces.

 

Using video memory to store surfaces

A typical graphics card on a stationary PC today comes with at least 64mb of available video memory. This memory can be allocated by applications to store information such as surface data. 64mb of video memory can store one front buffer of 800x600x16bpp, one back buffer, and approximately 100 GapiDraw surfaces of 640x480 pixels. The video memory available on the card is often referred to as local video memory. Modern PCs use fast AGP buses to their graphics card, making it possible for the card to use a portion of the ordinary system memory as video memory (usually referred to as AGP aperture size as a BIOS setting). This memory is often referred to as non local video memory and is much slower to use than local video memory. Using GapiDraw you can check the amount of free video memory in total, the amount of free local video memory, and the amount of free non local video memory with the operation CGapiDisplay::GetAvailableVidMem.

Losing video memory

Each application using the video memory of a graphics card gets exclusive access to the video memory. This includes all the available video memory of the card. Switching between two applications that both use the card's video memory may result in that the contents of the data written to the video card by one application is overwritten by another application. A typical example of this is the Windows XP desktop (which by itself is an application). Switching between the desktop and another application that use video memory (such as a full screen game) most probably results in that the contents of the video memory is overwritten. The concept of data written to the video memory by one application, and then overwritten by another is referred to as losing video memory.

Reading and writing to video memory

To read and write data to the video memory of a graphics card, an application has to lock the card for exclusive access. In older versions of DirectDraw this lock was system exclusive, pausing all other threads running on the system until the video memory was unlocked. Newer versions of DirectDraw however provide a flag NOSYSLOCK (that GapiDraw uses) that avoids this.

Locking the video memory of a graphics card is a costly operation, and should be done as few times as possible. DirectDraw specifies in what pixel format the video memory should be presented to applications (e.g. 16-bit 565 RGB format), and if a graphics card uses another format it must convert/align its contents to match this format on each lock. Access to the video memory is done through a memory mapped reserved area, so that the video memory appears as ordinary system memory to the application.

Reading from video memory should be avoided at all costs since it is extremely slow. If you perform several operations each frame that read from the destination surface (e.g. using opacity or alpha blends) you should consider placing the destination surface in system memory for performance improvements.

 

Storing the back buffer in video memory

The display instance (CGapiDisplay) will always represent a surface that is stored in video memory. Drawing to the display surface will always write information directly to video memory. The back buffer can however be stored in either system memory or local video memory depending on use. The back buffer is by default stored in video memory if a graphics card is used, it is however possible to force the back buffer to be stored in system memory by using the flag GDDISPLAY_BACKBUFFERSYSMEM when opening the display (CGapiDisplay::OpenDisplay).

If the back buffer is stored in video memory, it is stored as a "true" back buffer where the display pointer (determining what is shown on the display) is toggled between the front surface and the back surface. No data is copied from the back buffer to the front buffer on each CGapiDisplay::Flip. If the back buffer is stored in system memory, GapiDraw will copy the data from the back buffer in system memory to the "true" back buffer in video memory, and then simply toggle between the back buffer and the front buffer on each flip. This imposes a slight overhead of having to copy large blocks of data to video memory on each frame, but will improve performance significantly if much data have to be read from the back buffer using non-hardware accelerated operations.

When to store the back buffer in video memory

The back buffer should be stored in video memory if most of your operations using the back buffer are hardware accelerated. If you perform several operations to the back buffer that are not hardware accelerated (such as using opacity or alpha blends) you will most probably see a significant performance increase if the back buffer is placed in system memory instead of video memory (since reading from video memory is extremely slow).

 

Storing surfaces in video memory

GapiDraw can store the surface contents of CGapiSurface and all its derivates (such as CGapiBitmapFont and CGapiMaskSurface) in video memory. In fact, GapiDraw will always try to store the contents of all surfaces in video memory if it can (i.e. the application runs in full screen on a stationary PC). If the local video memory should be full, GapiDraw will try to use non local video memory. If that fails, GapiDraw will fall back and use system memory. You can verify where a surface resides by calling CGapiSurface::GetSurfaceFlags. You can force GapiDraw to only use video memory and return with an error if memory could not be allocated with the flags GDSURFACE_VIDEOMEMORY, GDSURFACE_LOCALVIDMEM and GDSURFACE_NONLOCALVIDMEM. You send these flags to CGapiSurface::CreateSurface.

Locking and unlocking video memory

If a surface is stored in video memory and an operation that is not hardware accelerated is performed on that surface, GapiDraw will automatically lock the video memory allocated by the surface, perform the operation, and then unlock the video memory again. Locking surfaces are costly, and if several non-hardware accelerated operations are to be performed on the same video surface much overhead will be imposed by locking and unlocking the video memory.

In GapiDraw you can force a lock of a surface's video memory by calling CGapiSurface::LockVideoSurface. This will lock the video memory associated with the surface so you are free to perform a batch of non-hardware-accelerated operations on that surface. If a surface is locked in video memory the surface flag GDSURFACE_VIDEOLOCKED will be set (you can check this with CGapiSurface::GetSurfaceFlags). If a hardware-accelerated operation should be performed on a surface whose memory is locked in video memory, the video memory will be temporarily unlocked, the hardware-accelerated operation will be performed, and the video memory of the surface will then be locked again. Video memory should be locked for a limited time period only, and it is considered good practice to minimize the amount of operations performed before the video memory is unlocked with CGapiSurface::UnlockVideoSurface.

Losing surfaces

If the video memory used by a surface is destroyed by another application (e.g. switching between the desktop in Windows XP and a full screen game), all operations to that surface will fail and return GDERR_SURFACELOST. You must then reclaim the video memory used by all surface with CGapiDisplay::RestoreAllVideoSurfaces, and then re-create the contents of that surface. Instead of checking the return codes from each graphic operation for lost surfaces, you can simply call the operation CGapiDisplay::SurfacesAreLost at the end of each frame update pass. This operation will return GDERR_SURFACELOST if one or more blit operations have failed due to lost video memory. If you are using CGapiApplication, the checks for lost surfaces are handled automatically and CGapiApplication will automatically call CreateVidMemSurfaces if it needs to recreate lost video surfaces.

 

To summarize

You can use the following points as a quick guide to manage video memory in your applications:

  1. If you feel that performance is slow, try storing your backbuffer in system memory and see if application performance improves. Performance should improve if you perform many non-hardware-accelerated operations that read data back from the backbuffer.
  2. Do not perform alpha blends or use opacity on surfaces stored in video memory. Reading data from video memory is slow and should be avoided at all costs.
  3. If you do not use CGapiApplication, please verify that your application does not continuously try to restore all video surfaces while it is minimized (which will fail because the desktop have exclusive access to the video memory).
  4. Remember that the contents of the back buffer will change on each frame if you run the application in full screen mode with page flipping (it will be restored to the previous frame after each flip). It is thus not safe to assume that the contents of the back buffer is unchanged and use delta-updates in full screen mode.