Synchronizing+with+video+refresh

//by Richard Russell, April 2007//

//**(Note that the code in this article requires DirectX 7 or later).**//

To achieve perfectly smooth animated graphics it is necessary to synchronize the output from your program with the video refresh rate of your graphics card and monitor. For example, if your display is running at 75 Hz your program should update its output (preferably) 75 times per second, synchronized with the start of each video frame. If it cannot run that quickly, it should update its output at a sub-multiple of the video refresh rate (e.g. 37.5 or 25 times per second).

The Windows **Graphical Device Interface** (GDI) provides no means of synchronizing with the video refresh rate, but **DirectX** (in particular its **Direct Draw** subsystem) does. It is not necessary to use DirectX to output the graphics themselves; you can use it solely for the purpose of synchronization.

To achieve this you should first incorporate the following code in the initialisation section of your program:

code format="bb4w" SYS "LoadLibrary", "DDRAW.DLL" TO ddraw% IF ddraw%=0 THEN ERROR 100, "Direct Draw not available" SYS "GetProcAddress", ddraw%, "DirectDrawCreateEx" TO `DirectDrawCreateEx` DIM IID_IDirectDraw7{a%, b%, c%, d%} IID_IDirectDraw7.a% = &15E65EC0 IID_IDirectDraw7.b% = &11D23B9C IID_IDirectDraw7.c% = &60002FB9 IID_IDirectDraw7.d% = &5BEA9797 DIM IDirectDraw{QueryInterface%, AddRef%, Release%, Compact%, CreateClipper%, \ \              CreatePalette%, CreateSurface%, DuplicateSurface%, \ \              EnumDisplayModes%, EnumSurfaces%, FlipToGDISurface%, \ \              GetCaps%, GetDisplayMode%, GetFourCCCodes%, GetGDISurface%, \ \              GetMonitorFrequency%, GetScanLine%, GetVerticalBlankStatus%, \ \              Initialize%, RestoreDisplayMode%, SetCooperativeLevel%, \ \              SetDisplayMode%, WaitForVerticalBlank%} SYS `DirectDrawCreateEx`, 0, ^IDirectDraw%, IID_IDirectDraw7{}, 0 !(^IDirectDraw{}+4) = !IDirectDraw% code Here an error results if Direct Draw isn't available. In practice you may prefer not to issue an error but to fall back to using timers or the WAIT statement to set the frame rate. The animation will not be as smooth but at least your program will still run.

The simplest way of achieving synchronization is to wait for the beginning of each video frame before updating your graphics output. To do that you can use code similar to the following (**do not use this method - see below**):

code format="bb4w" _DDWAITVB_BLOCKBEGIN = 1 REPEAT SYS IDirectDraw.WaitForVerticalBlank%, IDirectDraw%, _DDWAITVB_BLOCKBEGIN, 0 PROCpaint UNTIL FALSE code Here it is assumed that your output will be updated in **PROCpaint**.

Unfortunately there are a couple of shortcomings with this simple method. Firstly, the **WaitForVerticalBlank** function does not always work reliably. Secondly, it uses 100% CPU time while waiting for the next frame, which is undesirable (the importance of this will depend on how much time is spent 'waiting' and how much doing something useful).

An alternative and more satisfactory approach is to poll for the beginning of the video frame as follows:

code format="bb4w" SYS "timeBeginPeriod", 1 lastline% = 0 REPEAT SYS IDirectDraw.GetScanLine%, IDirectDraw%, ^thisline% IF thisline% < lastline% PROCpaint lastline% = thisline% SYS "Sleep", 1 UNTIL FALSE code Again it is assumed that your output will be updated in **PROCpaint**. The call to **timeBeginPeriod** is to ensure that the **Sleep** function waits for the shortest possible (non-zero) period.

This method may not result in as accurate synchronization as the previous one, but it avoids wasting CPU time and allows you to carry out low-priority 'background' tasks whilst waiting for the start of the next frame.

When you have finished with the Direct Draw subsystem, execute the following code (for example on exit, by including it in a **cleanup** routine called from ON ERROR and ON CLOSE statements):

code format="bb4w" SYS IDirectDraw.Release%, IDirectDraw% code