首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >控制帧速率

控制帧速率
EN

Stack Overflow用户
提问于 2014-08-04 10:48:18
回答 3查看 2.3K关注 0票数 4

我正在创建一组需要在帧周期中工作的线程。我想控制在一秒钟内完成了多少帧。我将我的代码简化为这个,这样我就可以向您展示我所写的内容。

代码语言:javascript
复制
// setup the frame timer
std::chrono::time_point<std::chrono::system_clock> start = std::chrono::system_clock::now();
std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::now();

while(running == true)
{
    // update timer
    start = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsed_seconds = start - end;
    double frameTime = elapsed_seconds.count();

    this->Work(frameTime);

    // update timer
    std::chrono::time_point<std::chrono::system_clock> afterWork = std::chrono::system_clock::now();
    std::chrono::duration<double> elapsedWorkTime = afterWork - end ;

    const double minWorkTime = 1000 / this->timer.NumberOfFramePerSeconds;
    if(elapsedWorkTime.count() < minWorkTime)
    {
        double timeToSleep = minWorkTime - elapsedWorkTime.count();
        std::this_thread::sleep_for(std::chrono::milliseconds((int)timeToSleep));
    }

    // update fps
    end = start;
    timer.FrameCount += 1;
}

并不是所有线程都有相同的工作量,有些线程比其他线程有更多的工作,因此如果没有睡眠,我就会得到围绕这个Thread 1 : 150fps, Thread 2: 5000fps, Thread 3: 5000fps, Thread 4: 5000fps的结果。

我想要的是能够将帧每秒设置为60,所以使用上面的代码,我会将this->timer.NumberOfFramePerSeconds设置为60。我的问题是,当我这样做的时候,我得到了一个像Thread 1 : 30fps, Thread 2: 60fps, Thread 3: 60fps, Thread 4: 60fps这样的结果

这表明我的代码有问题,因为当我注释掉睡眠时,线程1将很高兴地完成超过150帧,但是当睡眠在那里时,它将工作在我试图实现的60以下。

我的代码/算法正确吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-08-04 15:16:01

在我看来,您可能有一个单位转换错误。这通常是由于在chrono类型系统之外处理时间单位和引入显式转换因素造成的。例如:

代码语言:javascript
复制
double frameTime = elapsed_seconds.count();
this->Work(frameTime);

Work可以修改为采用duration<double>而不是double吗?这将有助于确保double不会被重新解释为Work内部的秒以外的其他内容。如果由于某些原因不能,则至少可以减少以下错误的暴露:

代码语言:javascript
复制
this->Work(elapsed_seconds.count());

以下情况看上去很可疑:

代码语言:javascript
复制
std::chrono::duration<double> elapsedWorkTime = afterWork - end ;

const double minWorkTime = 1000 / this->timer.NumberOfFramePerSeconds;
if(elapsedWorkTime.count() < minWorkTime)

elapsedWorkTime显然有秒的单位。minWorkTime似乎有毫秒的单位。if扔掉所有单位的信息。这看起来应该是:

代码语言:javascript
复制
std::chrono::duration<double> minWorkTime(1./this->timer.NumberOfFramePerSeconds);
if(elapsedWorkTime < minWorkTime)

或者如果您真的希望minWorkTime表示毫秒(这不是必要的):

代码语言:javascript
复制
std::chrono::duration<double, std::milli> minWorkTime(1000./this->timer.NumberOfFramePerSeconds);
if(elapsedWorkTime < minWorkTime)

我推荐前者,因为1000的引入只是错误出现的另一个机会。

代码语言:javascript
复制
    double timeToSleep = minWorkTime - elapsedWorkTime.count();
    std::this_thread::sleep_for(std::chrono::milliseconds((int)timeToSleep));

在这里,您将不必要地离开chrono的安全网,手动执行操作。相反,这应该是:

代码语言:javascript
复制
    std::this_thread::sleep_for(minWorkTime - elapsedWorkTime);

或者如果你想变得更详细:

代码语言:javascript
复制
    auto timeToSleep = minWorkTime - elapsedWorkTime;
    std::this_thread::sleep_for(timeToSleep);

.count()的使用应该是罕见的。目前在打印duration时需要使用它,也可能需要处理无法使用chrono的遗留代码。如果您发现自己使用的.count()更多,请后退一步,尝试删除它。chrono有助于防止单元转换错误,当您通过.count()退出系统时,会破坏chrono存在的原因。

更新

VS2013有一个错误,它不会在双基式持续时间上睡觉。所以作为解决办法..。

代码语言:javascript
复制
std::this_thread::sleep_for(std::chrono::duration_cast
                <std::chrono::milliseconds>(minWorkTime - elapsedWorkTime));

这很难看,但仍然把所有的转换因素都留给chrono

感谢Caesar为我测试了这个解决方案。

票数 4
EN

Stack Overflow用户

发布于 2014-08-04 14:13:03

简单的答案是你没有。参见this SO question

与其控制易出错且不能很好地处理慢速线程的framerate,您应该尽可能地设置代码来处理当前的框架。

让线程做他们合理能做的工作。如果由于某种原因不得不暂停,请使用消息队列,并在消息队列为空时休眠。

票数 2
EN

Stack Overflow用户

发布于 2014-08-04 16:53:56

睡眠通常不是很精确,因为时钟的分辨率可能很低。

例如,在Windows上,典型的定时器分辨率大约为15.6ms。如果你只要求睡眠1毫秒,它就会睡到下一个15.6毫秒的间隔,在未来至少是1毫秒。平均而言,这比你预期的要长6.8毫秒。一旦时间流逝,您就无法保证处理器会立即安排您的线程。

因此,我怀疑您的线程需要睡眠一段时间,但它的睡眠时间要长得多,并且错过了下一个框架。(其他线程的运行速度正好是60 fps,这意味着您还拥有其他的速率限制器,比如垂直同步。)

一个可能的解决方案,尽管效率低下,是做一个繁忙-等待,而不是睡眠的任何时间比时钟分辨率短。例如,如果您必须延迟3.2*clock_resolution,那么您可以为3*clock_resolutions睡觉,然后在循环中旋转,直到实际时间到来。

另一种解决方案是让线程等待一个同步原语,该原语以所需的速率发出信号。根据可用的原语类型,当您试图同步多个线程时,这可能会很棘手。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25120825

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档