高精度睡眠或如何产生CPU并保持精确的帧速率

Blu*_*kMN 5 .net c# sleep

我在2D游戏引擎上工作,该引擎具有一个名为LimitFrameRate的功能,以确保游戏运行速度不快,用户无法玩游戏.在这个游戏引擎中,游戏的速度与帧速率有关.因此通常人们希望将帧速率限制为大约60 fps.这个函数的代码相对简单:计算我们应该在下一帧开始工作之前剩余的时间量,将其转换为毫秒,睡眠该毫秒数(可能是0),重复直到它恰好是正确的时间,然后退出.这是代码:

public virtual void LimitFrameRate(int fps)
{
  long freq;
  long frame;
  freq = System.Diagnostics.Stopwatch.Frequency;
  frame = System.Diagnostics.Stopwatch.GetTimestamp();
  while ((frame - previousFrame) * fps < freq)
  {
     int sleepTime = (int)((previousFrame * fps + freq - frame * fps) * 1000 / (freq * fps));
     System.Threading.Thread.Sleep(sleepTime);
     frame = System.Diagnostics.Stopwatch.GetTimestamp();
  }
  previousFrame = frame;
}
Run Code Online (Sandbox Code Playgroud)

当然,我发现由于某些系统上睡眠功能的不精确性,帧速率与预期完全不同.睡眠功能的精度只有大约15毫秒,所以你不能等待.奇怪的是,有些系统使用此代码可以实现完美的帧速率,并且可以完美地实现一系列帧速率.但其他系统则没有.我可以删除睡眠功能,然后其他系统将实现帧速率,但随后它们占用了CPU.

我读过有关睡眠功能的其他文章:

编码器是做什么的?我不是要求保证帧速率(换句话说,保证睡眠时间),只是一般行为.我希望能够睡眠(例如)7毫秒来为操作系统提供一些CPU,并且通常在7毫秒或更短的时间内返回控制(只要它恢复一些CPU时间),如果需要更有时候,那没关系.所以我的问题如下:

  1. 为什么睡眠在某些Windows环境中完美而精确地工作而不在其他环境中?(有没有办法在所有环境中获得相同的行为?)
  2. 如何在不占用C#代码的CPU的情况下实现通常精确的帧速率?

Cod*_*aos 5

您可以使用timeBeginPeriod来提高计时器/睡眠精度.请注意,这会对系统产生全局影响,并可能会增加功耗.
你可以timeBeginPeriod(1)在程序的开头打电话.在您观察到更高计时器精度的系统上,另一个正在运行的程序可能会这样做.
我不打算计算睡眠时间,只是sleep(1)在循环中使用.

但即使只有16ms的精度,您也可以编写代码,以便随着时间的推移平均误差.这就是我要做的.编写代码并不难,并且应该很少适应当前的代码.

或者您可以切换到使移动与经过时间成比例的代码.但即使在这种情况下,您也应该实现帧速率限制器,这样您就不会获得无用的高帧率和不必要的功耗.

编辑:根据这个答案中的想法和评论,制定了接受的答案.


Blu*_*kMN 0

根据对 CodeInChaos 答案的想法和评论,这是我得出的最终代码。我最初编辑了该答案,但 CodeInChaos 建议这是一个单独的答案。

public virtual void LimitFrameRate(int fps)
{
   long freq;
   long frame;
   freq = System.Diagnostics.Stopwatch.Frequency;
   frame = System.Diagnostics.Stopwatch.GetTimestamp();
   while ((frame - fpsStartTime) * fps < freq * fpsFrameCount)
   {
      int sleepTime = (int)((fpsStartTime * fps + freq * fpsFrameCount - frame * fps) * 1000 / (freq * fps));
      if (sleepTime > 0) System.Threading.Thread.Sleep(sleepTime);
      frame = System.Diagnostics.Stopwatch.GetTimestamp();
   }
   if (++fpsFrameCount > fps)
   {
      fpsFrameCount = 0;
      fpsStartTime = frame;
   }
}
Run Code Online (Sandbox Code Playgroud)