一个可以精确控制FPS的程序框架
经常看到有人讨论如何在主循环中控制帧速的问题,很多人使用Sleep之类的函数,其实Sleep本身虽然能释放CPU,但无法做到精确控制FPS的值,这里介绍一种比较巧妙的框架,对FPS可以做到比较精确的控制,而且也不会占用CPU。
这个框架用到了两个比较关键的函数:
timeSetEvent,调用这个函数后,操作系统创建了一个后台线程,这个线程由winmm.dll控制,按照指定的频率执行指定某函数或者将某个信号设置为“有信号”。
MsgWaitForMultipleObjects,这个函数和WaitForMultipleObjects相比,除了等待多个信号外,同时还可以同时等待指定的窗口消息。
这个框架的原理就是首先创建一个Event作为渲染信号,并且用timeSetEvent函数按照帧速频率控制它变为“有信号”
// Create render signal event
HANDLE g_hTickEvent = :: CreateEvent ( NULL , FALSE , FALSE , NULL ) ;
ResetEvent ( g_hTickEvent ) ;
// Create time event
MMRESULT g_hEventTimer = :: timeSetEvent (
( INT )( 1000.0 f / MAX_FPS ) ,
1 ,
( LPTIMECALLBACK ) g_hTickEvent ,
0 ,
TIME_PERIODIC | TIME_CALLBACK_EVENT_SET ) ;
HANDLE g_hTickEvent = :: CreateEvent ( NULL , FALSE , FALSE , NULL ) ;
ResetEvent ( g_hTickEvent ) ;
// Create time event
MMRESULT g_hEventTimer = :: timeSetEvent (
( INT )( 1000.0 f / MAX_FPS ) ,
1 ,
( LPTIMECALLBACK ) g_hTickEvent ,
0 ,
TIME_PERIODIC | TIME_CALLBACK_EVENT_SET ) ;
在主循环中,使用MsgWaitForMultipleObjects释放CPU,如果等待的结果是渲染信号,则进入渲染函数,如果是窗口消息,则进入消息处理函数
MSG
msg
;
ZeroMemory ( & msg , sizeof ( msg ) ) ;
while ( msg . message != WM_QUIT )
{
if ( WAIT_OBJECT_0 == MsgWaitForMultipleObjects ( 1 , & g_hTickEvent , FALSE , 1000 , QS_ALLINPUT ))
{
Render () ;
}
else
{
while ( PeekMessage ( & msg , NULL , 0 , 0 , PM_REMOVE ))
{
if ( msg . message == WM_QUIT ) break ;
TranslateMessage ( & msg ) ;
DispatchMessage ( & msg ) ;
} ;
}
}
ZeroMemory ( & msg , sizeof ( msg ) ) ;
while ( msg . message != WM_QUIT )
{
if ( WAIT_OBJECT_0 == MsgWaitForMultipleObjects ( 1 , & g_hTickEvent , FALSE , 1000 , QS_ALLINPUT ))
{
Render () ;
}
else
{
while ( PeekMessage ( & msg , NULL , 0 , 0 , PM_REMOVE ))
{
if ( msg . message == WM_QUIT ) break ;
TranslateMessage ( & msg ) ;
DispatchMessage ( & msg ) ;
} ;
}
}