通常,即使用户不做任何事情,游戏也会一直绘制新帧。这通常会发生在您调用 onIdle() 的地方。如果您的游戏仅在用户按下按钮等时或偶尔在两者之间更新窗口/屏幕,那么MSGWaitForMultipleObjects
这是一个不错的选择。
但是,在连续动画游戏中,如果可以提供帮助,您通常根本不想在渲染线程中阻塞或休眠,而是希望以最大速度渲染并依靠垂直同步作为节流阀。这样做的原因是,时间、阻塞和睡眠充其量是不精确的,最坏的情况是不可靠的,它很可能会给你的动画添加令人不安的伪影。
您通常想要做的是将属于帧的所有内容尽可能快地推送到图形 API(很可能是 OpenGL,因为您说“任何平台”),向工作线程发出信号以开始执行游戏逻辑和物理等,并且然后阻塞 SwapBuffers。
所有计时器、超时和睡眠都受调度程序的分辨率限制,在 Windows 下为 15ms(可以使用 设置为 1ms timeBeginPeriod
)。在 60fps 下,一帧是 16.666ms,所以 15ms 的阻塞是灾难性的,但即使是 1ms 仍然是相当长的时间。您无法做任何事情来获得更好的分辨率(这在 Linux 下要好得多)。
Sleep
,或任何具有超时的阻塞函数,保证您的进程至少睡眠时间与您要求的时间一样长(事实上,在 Posix 系统上,如果发生中断,它可能会睡眠更少)。它不保证您的线程将在时间一到时立即运行,或任何其他保证。
Windows 下的 Sleep(0) 更糟糕。文档说“线程将放弃其时间片的剩余部分,但仍保持就绪状态。请注意,就绪线程不能保证立即运行”。现实情况是,它在大多数情况下都可以正常工作,从“根本没有”到 5-10 毫秒的任何地方都阻塞,但有时我也看到 Sleep(0) 阻塞 100 毫秒,当它发生时这是一场灾难.
使用QueryPerformanceCounter
是自找麻烦——不要这样做。在某些系统上(具有最新 CPU 的 Windows 7)它可以正常工作,因为它使用可靠的 HPET,但在许多其他系统上,您会看到各种奇怪的“时间旅行”或“逆时针”效果,没有人可以解释或调试。那是因为在那些系统上,QPC 的结果读取取决于 CPU 频率的 TSC 计数器。CPU 频率不是恒定的,多核/多处理器系统上的 TSC 值不需要保持一致。
然后是同步问题。两个独立的计时器,即使以相同的频率滴答作响,也必然会变得不同步(因为没有“相同”之类的东西)。您的显卡中有一个计时器已经在进行垂直同步。使用这个进行同步将意味着一切都会正常工作,使用另一个将意味着事情最终将不同步。不同步可能根本没有效果,可能会画半帧,或者可能会阻塞一帧,或者其他什么。
虽然丢失一帧通常没什么大不了的(如果它只是一个并且很少发生),但在这种情况下,你可以完全避免一开始就发生这种情况。