现在的位置: 首页 > 综合 > 正文

关于精确控制FPS的程序框架

2013年01月30日 ⁄ 综合 ⁄ 共 3213字 ⁄ 字号 评论关闭

第一篇:

 

一个可以精确控制FPS的程序框架

本文转自:http://www.thecodeway.com/blog/?p=473 

 

经常看到有人讨论如何在主循环中控制帧速的问题,很多人使用Sleep之类的函数,其实Sleep本身虽然能释放CPU,但无法做到精确控制FPS的值,这里介绍一种比较巧妙的框架,对FPS可以做到比较精确的控制,而且也不会占用CPU。

这个框架用到了两个比较关键的函数:
    timeSetEvent,调用这个函数后,操作系统创建了一个后台线程,这个线程由winmm.dll控制,按照指定的频率执行指定某函数或者将某个信号设置为“有信号”。
    MsgWaitForMultipleObjects,这个函数和WaitForMultipleObjects相比,除了等待多个信号外,同时还可以同时等待指定的窗口消息。
这个框架的原理就是首先创建一个Event作为渲染信号,并且用timeSetEvent函数按照帧速频率控制它变为“有信号”

 

在主循环中,使用MsgWaitForMultipleObjects释放CPU,如果等待的结果是渲染信号,则进入渲染函数,如果是窗口消息,则进入消息处理函数

 

源代码下载: fpsFrame.rar (5.6k)

 

 

第二篇:

用Windows API编写一个精确定时的循环

本文转自:

http://gamebabyrocksun.blog.163.com/blog/static/5715346320106755833174/

 

这几天跟网友讨论一个程序优化问题时,遇到一个需要延时的循环,结合我所知道的知识,并突发灵感,想到了用Windows系统提供的定时器API写一个精确延时循环的方法。

问题原本是这个样子的,就是说,在一个循环中执行一些操作后,需要用Sleep这个函数,延迟一定时间后,在执行下一轮循环,伪代码如下:

for(int i = 0; i < MaxCount;i++)

{

     dosomething();

     Sleep(SleepTime);

}

基于Sleep延时的不稳定,不精确性,这个循环延时,效果非常的糟糕,我想到的改进办法就是使用Waitable Timer内核对象来精确延时改造循环,因为这个内核对象可以精确定时到1ms这个量级,当然改造后,可以精确定时到100纳秒这么水平,首先我们来看看,如何改造成精确1ms一次的循环:

    HANDLE hTimer = NULL;

    LARGE_INTEGER liDueTime;

    liDueTime.QuadPart = -1;

    hTimer = CreateWaitableTimer(NULL, FALSE, NULL);

    if (NULL == hTimer)

    {

        //错误处理

    }

    LONG lCycle = 1;  //1毫秒触发一次

    if (!SetWaitableTimer(hTimer, &liDueTime, lCycle, NULL, NULL, 0))

    {

        //错误处理

    }

    int i = MaxCount;

    while( (i > 0) && WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0)

    {//循环

        dosomething();

        --i;

    }

像上面这样我们就得到一个1ms执行一次的循环,当然看似程序有点复杂,但是这个复杂性只是一种表面的,实际上我们得到了一个1毫秒循环一次的循环,当然这个需要你确定你的dosomething这样的循环体函数的执行时间是1毫秒以内的,否则这个延时就没有意义了。

接着我们再来考虑如何得到一个精确到100纳秒一次的循环,这个就需要一点技巧了,首先我们不能再创建自动重置的计时器对象,而要改为手工,因为100纳秒这个精度只有再次调用SetWaitableTimer才能得到,所以必须要手工重置,每次都调用一下这个函数,重置等待100纳秒,不啰嗦了,上代码:

    HANDLE hTimer = NULL;

    LARGE_INTEGER liDueTime;

    liDueTime.QuadPart = 1;

    hTimer = CreateWaitableTimer(NULL, TRUE, NULL);

    if (NULL == hTimer)

    {

        //出错处理

    }

 

    if (!SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0))

    {

        //出错处理

    }

    int i = MaxCount;

    while( (i > 0) && WaitForSingleObject(hTimer, INFINITE) != WAIT_OBJECT_0)

    {

        dosomething();

        if (!SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0))

        {

            //出错处理

            break;

        }

        --i;

    }

这段代码和前段代码,有些细节方面的差异,仔细看代码就知道了,不再赘述。而需要提醒注意的就是,这个实际上是使每次dosomething都间隔100纳秒调用,与前一个循环逻辑上是不同的,所以这个要特别注意,当然通过把SetWaitableTimer函数调整到dosomething之前,就可以得到与前一个循环逻辑一致的循环了,这不是什么难事,当然这个就需要你的函数一定要在100纳秒以内干完活,否则,延时就没有什么意义了。

最后,还是提醒下吧,liDueTime本质是个64位的变量,在作为SetWaitableTimer函数的参数时,它的最小时间单位就是100纳秒,这样你赋值1就表示1*100纳秒,2就表示200纳秒,以此类推,但是你没法指定小数来得到1.5*100纳秒这样的数,这就是100纳秒为单位的确切含义,100纳秒就成了不可逾越的鸿沟。当然对于一般目的的程序来说,这足够了。同样的那个lCycle参数的含义也是一样的,只是单位是1毫秒最小,无法再小了,同时第二个定时循环中没有用这个参数,在这种情况下用它也是没有什么意义的。

至此精确延时循环的编写方法就介绍完了,他可以用于不用消息循环,而需要周期性操作的场合,比如周期性的发送一个数据包,周期性的轮询一个设备的状态等。

抱歉!评论已关闭.