我正在尝试使用Windows计时器队列在Windows GDI上实现高每秒帧数。相关API为CreateTimerQueue, DeleteTimerQueueEx, CreateTimerQueueTimer,
和DeleteTimerQueueTimer
。
使用CreateTimerQueueTimer(&m_timer, m_timer_queue, TimerCallback, this, 0, 20, WT_EXECUTEINTIMERTHREAD);
创建计时器以达到约50fps的速度。 GDI操作(在backstore中的一些绘画,加上InvalidateRect)不能是异步的,因此我不能选择其他标志但是WT_EXECUTEINTIMERTHREAD,因此绘图代码上不需要额外的同步操作。我们的想法是在可能的情况下达到50fps,如果不是,只需以最大可能速度显示每个帧。
在动画结束时(达到总帧数),调用DeleteTimerQueueTimer
来销毁计时器。
问题是DeleteTimerQueueTimer
没有立即关闭回调函数的调用。当无法达到50fps的要求时,定时器将呼叫泵入队列。在回调函数内调用DeleteTimerQueueTimer
不会破坏队列。因此,即使它决定关闭计时器,仍会调用回调。
我该如何处理这个问题?
-
另一方面,旧的timeSetEvent / timeKillEvent
多媒体API似乎没有此问题。没有队列,当我调用timeKillEvent时立即停止调用回调函数。是否可以使用计时器队列实现相同的行为?
答案 0 :(得分:2)
您可以将WT_EXECUTEONLYONCE标志传递给CreateTimerQueueTimer函数。这将导致计时器仅触发一次,而不是定期触发。
然后,您可以使用ChangeTimerQueueTimer方法重新安排计时器。
要覆盖绘图在帧中花费太长时间的时间,可以在TimerHandler方法的开头添加CriticalSection,这将导致第二个计时器等到第一个计时器完成。
答案 1 :(得分:1)
如果你想以50fps +运行某些东西,你可能会更好地实际上只有一个绘制循环来计算帧之间的时间量并相应地缩放动画。定时器并不是真的经常发射。所以(这可能在你的空闲处理程序中)。比如,这个伪代码(忽略缺少错误处理):
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;
}
答案 2 :(得分:1)
如果计时器尚未安排,DeleteTimerQueueTimer将取消计时器。 (当你使用WT_EXECUTEINTIMERTHREAD时,我相信它们在来自定时器队列和工作线程共享的线程池的线程上排队为APC。)如果它已经被调度(不仅仅是运行) - 它将被运行并且DeleteTimerQueueTimer调用将阻止直到完成。
如果我理解您的问题,我可以建议以下内容吗? 1.在调用DeleteTimerQueueTimer之前 - 设置一个标志,表示abortAllTimers为true。 2.在每个计时器回调函数中检查abortAllTimers是否为真。如果是真的 - 那么立即返回而不进行任何绘图。
最后 - 不应该从计时器回调中调用DeleteTimerQueueTimer。相反,我建议你应该从任何其他线程调用它 - 比如你用来启动计时器的线程。
希望这有帮助。