Effect of Hyperthreading on a Thread Object for Windows Animation

Paul Milenkovic

Copyright 2003

Skip to Part 2

Animation is the presentation of a sequence of visual images for creating the illusion of motion. While animation is an important element of most computer games, it is also important in the visualization of many kinds of scientific data. Animation can also be applied to a computer user interface to give feedback and show progress of lengthy processing steps.

A key element of computer animation is a means of pacing the animation sequence and controlling the time interval between video images in that sequence. Windows offers several means of controlling that pacing. We will present a thread object that is the most capable of several competing ways of pacing an animation.

Microsoft Windows application programs are event-driven. Every Windows application program has at its core an event loop that 1) inputs an event from the operating system, 2) performs any optional filtering of that event, and 3) returns that event back to the operating system for the operating system to route that event to the window-handle object that event was meant for. Windows events are called messages, and a message is a 16-bit numeric code along with a data payload of a pair of 32-bit numeric values (the WParam and LParam). Some messages are system-defined numeric codes indicating keystrokes and mouse clicks; other messages are user-defined numeric codes outside the range of the system-defined codes. Every Windows program that has a main window also has an event loop – also called a message pump – although that loop may be hidden way inside the class framework used to simplify writing your application program.

The first technique for pacing animation is the Windows timer. A timer is created and destroyed with the SetTimer() and KillTimer() Windows applications interface (API) calls, and can be directed to send WM_TIMER messages to a specified window-handle object in an application program at regular intervals – the program can respond to each WM_TIMER message to plot an image in an animation sequence.

The Windows timer has the advantage that it generates events --Windows messages -- that integrate well with the processing of all of the other events that a Windows program must handle. Since a Windows program needs to process Windows messages anyway, adding processing for the WM_TIMER message is a natural way of extending a Windows program to support animation. There are also more subtle advantages to a Windows timer. Windows stores messages in a queue, and it gives other messages higher priority by moving WM_TIMER to the back of that queue. Windows also purges the queue of pending timer messages if the target window handle is closed, leaving one less worry for the application programmer.

One likes to think that the key to having a responsive user interface is the ability to give designated actions high priority – responding to a mouse click should be high priority. The only way to give an event high priority is to give competing events low priority, and that WM_TIMER is given the lowest priority means that a timer-based animation won't cause congestion of the event queue, even if the animation is requiring too much processor time. Animation code should be written to complete in the available time, but a program may be run on too slow a processor or other Windows programs or operating system services could preempt processor cycles. Ideally animation should proceed at the desired speed, but if the processor cannot handle the workload, it is better to let the animation slow down, at minor annoyance to the user, than let the response to mouse clicks to slow down, at major annoyance to the user. There is nothing more frustrating than a user interface that appears to lock up by not responding to mouse or key click input.

The disadvantage of the Windows timer is that the millisecond value specified for the timer interval is more of a suggestion, and the timer interval is rounded perhaps to the nearest 15 milliseconds depending on the hardware configuration. The Windows timer is only suited for very lazy or sluggish animations.

The second technique for pacing Windows animation is the multimedia timer API – timeBeginPeriod() and timeKillEvent(). A multimedia timer offers millisecond resolution, subject to the constraint that Windows itself does not time-slice at the millisecond level so millisecond time response is attempted but not guaranteed. Windows is not a real-time operating system, and an application program can occasionally be blocked for tens of milliseconds to allow a disk access or other operating system function to complete. Windows does not attempt to be real-time because its goal is to maximize throughput subject to reasonable response to the user interface, and sub millisecond time slicing was believed to use too much processor time in context switching. The irony is that processors are getting so fast that Windows is responding at the millisecond level – Windows could have been millisecond-responsive a couple processor generations ago if Microsoft had tuned their kernel differently.

Multimedia timer intervals are signaled through a callback function, and the Windows API help file cryptically warns against doing anything from that callback apart from a PostMessage() call. In that sense, a multimedia timer is not as convenient to program as a Windows timer, but the coding requirements are not insurmountable. The more serious disadvantage of the multimedia timer is that it is not integrated with the Windows event queue, and issues with message priority are the responsibility of the application programmer. Suppose PostMessage() is used to send WM_USER (user-defined message base value) messages to an application window? It turns out that WM_USER is given higher queue priority than WM_PAINT and even the WM_xxMOUSE messages, and the multimedia timer callback will keep feeding the event queue with WM_USER messages in Sorcerer's Apprentice fashion whether or not the application program is able to keep up. If too much workload is placed on the processor, WM_USER messages can block paint and mouse messages and overload the event queue, locking up an application.

A third technique of pacing animation is to use event loop idle time – this is a favored technique of game program developers (E. R. Lyons, 1995, Black Art of Windows Game Programming, Waite Group Press, Corte Madera, California). The PeekMessage() function is a non-blocking means of polling the event queue. If PeekMessage() finds a message in the queue it dispatches it; if the queue is empty, the event loop invokes an “idle time” function that performs the animation sequence update. The event loop idle time technique has the advantage that it gives all Windows messages priority over the animation, and it allows the animation to be self-paced. If there is a lot of message traffic, the animation response will degrade but the application will remain responsive to the user, a desired outcome.

There are some details to the proper use of this technique. Once the animation sequence is complete, it is important to revert to the blocking GetMessage() call to process the message loop. If the program is “spinning” on the non-blocking PeekMessage() call and doing nothing during idle time, the program is consuming processor cycles and degrading the performance of other running applications. While GetMessage() processes WM_QUIT, the message to terminate an application, an event loop using PeekMessage() needs to handle WM_QUIT explicitly. That the idle time function is a single, global function invoked from inside the message loop is a minor inconvenience to programs that need to animate multiple windows. One possible solution is to have the idle time function be a method of a global singleton-pattern class, and to have classes that need idle-time updating to register as observers of that singleton class.

One means of pacing the animation is to block updating the screen until the time of the video vertical retrace interval -- this has the advantage of giving smooth animations free of flicker and tear video artifact on video-tube displays. Under Windows, this may be accomplished using the IDirectDraw::WaitForVerticalBlank() call of the DirectX API. Most other Windows APIs call functions from a DLL; DirectX uses COM for its API. What that means is that WaitForVerticalBlank() is not a DLL function as is API function DestroyWindow(). Instead, an API function is invoked to return a pointer that in C++ can be cast to an abstract class. IDirectDraw::WaitForVerticalBlank() means WaitForVerticalBlank() is a virtual member function of that abstract class, which will be invoked from the pointer according to ((IDirectDraw) ddraw_ptr)->WaitForVerticalBlank().

WaitForVerticalBlank() is a Windows-specific feature that came out of the DirectX initiative from Microsoft to encourage game programmers to develop Windows versions of their DOS games. DOS allows and even encourages access to hardware registers, and DirectX was an effort to make low-level hardware features accessible through Windows. While the Macintosh also supports vertical retrace detection, this feature is absent from the many cross-platform systems: Java, wxWindows, Trolltech Qt+, and others.

The WaitForVerticalBlank() provides for signaling an event when a vertical retrace occurs, but that feature is not supported by most graphics cards. I have also found that IDirectDraw::GetScanLine() is not supported either. These features may be provisions for more capable graphics card, but the lowest common denominator of VGA-type cards is that they set a bit during vertical retrace and clear that bit at other times. The only way to detect vertical retrace is to use a spin loop that checks for that bit to be set. As a result, WaitForVerticalBlank() blocks without yielding to other threads, and it can even skip a vertical retrace and block until the next retrace occurs if the processor was preempted during the short time when the vertical retrace bit is set. That the event loop idle time technique is self pacing is an important advantage given that behavior; while spin blocking during idle time can degrade performance, it won't lock up the user interface.

Event loop idle time has the disadvantage that it requires access to the event loop. That an application program has a single event loop is a recommended programming practice; scattering event loops throughout a program to handle various kinds program modes is considered bad design. Microsoft doesn't always do as they say, and Windows has its own event loops scattered about that preempt the application event loop: examples include MessageBox() modal alert windows and mouse selection of a main window menu and a main window title bar. When the user selects a menu or triggers a MessageBox() alert window, another message loop is in play, one that cannot be modified by the application programmer, and event loop idle time processing ceases for the duration. For a game program, having the game animation freeze at those times may be an acceptable outcome. For a multimedia player, however, it may be desirable to continue the animation – some other technique for pacing the animation is required.

Go to Part 2