每秒只运行代码60次

时间:2010-03-25 08:53:49

标签: c++

我正在创建一个依赖于系统时间的directx应用程序(因为它必须准确), 我需要在后台运行代码行60次(在boost :: thread创建的一个线程中)。这等于60 FPS(每秒帧数),但不依赖于主应用帧速率。

//.................
void frameThread()
{
    // I want to run codes inside this loop for *exactly* 60 times in a second.
    // In other words, every 16.67 (1000/60) milliseconds
    for(;;)
    {
      DoWork();
      //.........
    }
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
   initialize();
   //.....stuffs
   boost::thread framethread(frameThread);
   //......
}

有办法做到这一点吗?

任何形式的帮助将不胜感激:)

6 个答案:

答案 0 :(得分:5)

由于60 Hz是一种常见的显示器刷新率,听起来你想要启用V-Sync,而不是乱搞定时器和睡眠。

您提到您使用的是DirectX - 如果您使用的是DirectX 9,请在创建设备时指定D3DPRESENT_INTERVAL_ONE,然后应用程序将同步到显示器刷新率。通常这是60赫兹 - 但它理论上也可以是任何东西,例如50Hz,85Hz或120Hz。你会得到一个非常好的无泪显示器 - 如果你不使用V-sync,你可能会在滚动时撕掉人工制品。

这引发了一个问题:如果帧速率变化如此之大,如何让游戏以相同的速度前进?答案很简单 - 使用高分辨率计时器(如Windows上的QueryPerformanceCounter),并测量每帧之间的时间。然后,您需要根据此“增量时间”值进行任何运动或移动。无论如何,即使使用固定的帧速率,这也是一件好事 - 如果计算机速度很慢且播放器只能达到20fps,它会阻止你的游戏突然以慢动作运行。

以下是以每秒100像素水平移动对象的示例,无论帧速率如何:

object.x += 100.0f * delta_time; // same speed no matter the framerate

这种IMO是一种更好的方法,特别是考虑到它是无撕裂的,与帧率无关的,并且与用户的监视器同步,而不是你自己设置的任意帧率。

答案 1 :(得分:1)

在DoWork()之前获取系统时间,将其添加到1/60秒并存储以供以后使用。在DoWork()(必须运行不到1/60秒)后,您必须睡眠,直到系统时间等于先前存储的时间。这应该可以解决问题。

答案 2 :(得分:1)

使用waitable timer作为同步对象。

HANDLE hWaitTimer;

void frameThread()
{
    while ( WaitForSingleObject( hWaitTimer, INFINITE ) == WAIT_OBJECT_0 )
    {
      // reactivate timer
      LARGE_INTEGER due_time = {0};
      due_time.LowPart = 10000/60; // in 100 nanosecond intervals
      ::SetWaitableTimer( hWaitTimer,  &due_time, 0, NULL, NULL, FALSE );

      DoWork();
      //.........
    }
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    hWaitTimer = ::CreateWaitableTimer( NULL, FALSE, NULL );
    LARGE_INTEGER due_time = {0};
    due_time.LowPart = 10000/60; // in 100 nanosecond intervals
    ::SetWaitableTimer( hWaitTimer,  &due_time, 0, NULL, NULL, FALSE );

    // create trhead etc.


    // before exit
    CloseHandle( hWaitTimer );
}

答案 3 :(得分:1)

“准确”是什么意思? Windows不是RTOS,因此无法从中获得真正的准确性。你可以非常接近,但你不能保证你的线程会在你想要的时候安排。

无论如何,我会选择一个等待计时器,一个线程池和RegisterWaitForSingleObject。

答案 4 :(得分:0)

如果您确实需要高精度,请使用特定于平台的多媒体计时器,请参阅timeSetEvent函数http://msdn.microsoft.com/en-us/library/aa448195.aspx。将TIME_PERIODIC与TIME_CALLBACK_FUNCTION标志一起使用,uResolution = 0。在任何情况下,DirectX都不可移植。

我不认为您可以使用跨平台的Boost线程获得所需的精度。

答案 5 :(得分:0)

如果您是100%,那么您将不会运行超过1/60秒,那么解决方案就是使用semaphore和单独的计时器,每1/60秒计时。

在计时器线程中你semaphore.V()和你的工作线程你semaphore.P()。

Windows有native semaphores

如果您的工作线程可能需要超过1/60,那么您应该通过某种“worker_lock”实例变量来补偿它。永远不要锁定定时器螺纹,它必须尽可能快。同时提高定时器接收线程的优先级。