背景:我在F#/ .net中编写了一个gameboy模拟器。在游戏手中,vblank(~ffs)约为60赫兹并且与游戏速度相关,因此模拟器尽可能接近60 fps的重要性。
由于现代计算机可以比60 fps更快地运行我的模拟器,因此我需要减慢模拟器的速度。我目前的方法是计算两个VBlanks之间的时间,然后等待该vblank周期的剩余时间。
问题在于如何在没有繁忙的循环cpu的情况下等待。由于我通常需要等待几毫秒(有时更多,有时更少),内置的Thread.sleep函数不是一个好的选择,因为除非你指定0等待时间,它将至少睡眠〜15毫秒,这是长期(和不准确)的方式。我目前的方法是使用sleep(0),它实际上只是一个花哨的自旋锁(其他线程可以运行,但你仍然最大化cpu)。
解决这个问题的正确方法是什么?我正在考虑等待从计时器中释放的信号量,但是计时器能否提供所需的时间分辨率?并不仅仅是一种奇特的睡眠?
编辑:这被标记为What Thread sleep method is most precise: Monitor.Wait vs System.Timer vs DispatchTimer vs Threading.Timer的副本,但我认为这不是精确度问题,更多的是为紧密的游戏循环找到合适的解决方案。
答案 0 :(得分:1)
正如您所指出的那样,默认的系统定时器不够快 - 15.6ms能够达到60 FPS,但不是“每帧的恒定时间”或接近该值的任何地方。
一个解决方案是使用繁忙的循环,但是,对于如此漫长的等待时间,这是一个巨大的浪费(有趣的是,一种方法“太短”而另一种方式“太长”:))。
另一种选择是使用timeBeginPeriod
(https://msdn.microsoft.com/en-us/library/windows/apps/dd757624(v=vs.85).aspx)更改系统计时器 - 您需要使用P / Invokes来访问此API,但它可以让您以更高的准确度睡眠。或者更好的是,使用Timer
设置为~16.7ms。
如果你不想弄乱它,只需制作一个计时器(System.Threading.Timer
,而不是windows形成一个)并将其设置为15ms。虽然这不会给你正好60 FPS,但它应该平均每秒约64次更新,这应该足够接近,不容易察觉。由于定时器回调与“休眠”分离(没有真正的睡眠,但是你在等待计时器触发时你没有做CPU工作),它不会像Windows窗体那样跳过或双帧。请注意,这隐含涉及多线程,因此请确保正确使用同步。