tags:

views:

286

answers:

2

I'm trying to achieve high frame-per-second on Windows GDI by using Windows Timer Queues. The relevant APIs are CreateTimerQueue, DeleteTimerQueueEx, CreateTimerQueueTimer, and DeleteTimerQueueTimer .

The timer is created using CreateTimerQueueTimer(&m_timer, m_timer_queue, TimerCallback, this, 0, 20, WT_EXECUTEINTIMERTHREAD); to achieve some 50fps of speed. GDI operations (some painting in the backstore, plus InvalidateRect) cannot be asynchronous, therefore I can't choose other flags but WT_EXECUTEINTIMERTHREAD so that no extra sync op is required on the drawing code. The idea is to achieve 50fps when possible, and when it's not, just show each frame at the maximum possible speed.

At the end of the animation (reached a total frame count), DeleteTimerQueueTimer is called to destroy the timer.

The problem is that DeleteTimerQueueTimer doesn't immediately turn off the callings of the callback function. When it's not possible to achieve the 50fps requirement, the timer pumps the call into a queue. Calling DeleteTimerQueueTimer inside the callback function doesn't destroy the queue. As a result, the callback is still being called even though it decided to shutdown the timer.

How do I deal with this problem?

- On another note, the old timeSetEvent / timeKillEvent multimedia API doesn't seem to have this problem. There are no queues and the calling of the callback function is immediately stopped when I call timeKillEvent. Is it possible to achieve the same behavior with timer queues?

A: 

If you want to run something at 50fps+, you'd probably do better to actually just have a draw loop which computes the amount of time between frames and scales the animation accordingly. Timers aren't really meant to fire so often. So (and this would probably be in your Idle handler). Like, this pseudocode (ignore lack of error handling):

static longlong last_frame;
while(1) {
    longlong current_frame = QueryPerformanceCounter();
    long delta = current_frame - last_frame;

    // Do drawing here, scale amount to move by how much time has elapsed

    last_frame = current_frame;
}
Paul Betts
Thanks, but how come the multimedia timer API doesn't have this problem? What kind of interval isn't "too often"? Wouldn't "while(1)" plus "QueryPerformanceCounter" cost much higher than a multimedia timer or a timer queue timer? So far I discovered that the timer queue timer costs virtually nothing to fire callbacks at 50fps while multimedia timer costs only a slight bit of CPU.
He Shiming
A: 

You can pass the WT_EXECUTEONLYONCE flag to the CreateTimerQueueTimer function. This will cause the timer to trigger only once and not periodically.

You can then reschedule the timer with the ChangeTimerQueueTimer method.

To cover the times where your drawing takes too long too complete in the frame, you can add a CriticalSection to the beginning of the TimerHandler method, which will cause the 2nd timer to wait until the first one completes.

Dunk