首先,我知道Application.targetFrameRate
存在,并且做得很好,但是我想要更准确的信息。对我而言,将帧速率设置为60时限制为60.3左右,设置为200时将帧速率限制为204左右。顺便说一句,这些是使用RTSS 7.2在构建中(而不是在编辑器中)进行测量。
因此,我开始使用低级计时器在Unity中创建我的自定义帧限制器,但是由于某种原因它不能正常工作。这是我的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using System;
public class FrameLimiter : MonoBehaviour
{
private FrameLimiter m_Instance;
public FrameLimiter Instance { get { return m_Instance; } }
public double FPSLimit = 300.0;
private long lastTime = HighResolutionDateTime.UtcNow.Ticks;
void Awake()
{
m_Instance = this;
}
void OnDestroy()
{
m_Instance = null;
}
void Update()
{
if (FPSLimit == 0.0) return;
lastTime += TimeSpan.FromSeconds(1.0 / FPSLimit).Ticks;
var now = HighResolutionDateTime.UtcNow.Ticks;
if (now >= lastTime)
{
lastTime = now;
return;
}
else
{
SpinWait.SpinUntil(() => { return (HighResolutionDateTime.UtcNow.Ticks >= lastTime); });
}
}
}
基本上,这是一个单例,设置为在脚本执行顺序中的任何其他脚本之前执行,并且它会阻塞执行,直到达到正确的时间为止。
它的工作方式是跟踪上一次允许渲染帧的准确时间(它从非常精确的低电平计时器here's more detail about that获取当前时间)。然后,在每次对其Update()
函数的调用中,它会向其添加1.0 / FPSLimit
秒,以获取应渲染下一帧的时间,然后,如果当前时间小于此时间,它将阻止执行直到该时间戳已到。
SpinWait
基本上只是阻止执行而无需像空的while
循环那样固定CPU的有效方法。但是,仅作记录,我也尝试了一个空的while
循环,并获得了相同的结果,只是CPU使用率更高。
因此,如果您了解该代码的工作原理,那么应该看到从理论上讲应该非常精确地锁定帧速率,尤其是考虑到该计时器的精度优于1μs(0.001ms)according to Microsoft 。 但是尽管如此,当我将其设置为60时,我还是获得了约58.8 FPS ,我真的不明白。 我正在禁用V-sync的情况下以独家全屏模式运行构建。顺便说一句,我正在不受限制地获得更高的帧频,因此游戏的基本性能不是问题。
任何帮助将不胜感激!
答案 0 :(得分:1)
问题比我想的还要奇怪。看来DateTime
的这种实现将内部存储的时间舍入为整数毫秒,因此我的帧限制器将帧定为17毫秒而不是设置为60 FPS时的16.6666毫秒。
解决方案是更改我使用的计时器的代码,只获取GetSystemTimePreciseAsFileTime()
返回的原始值,而不是将其封装在DateTime
对象中。
进行此更改后,如果将限制器设置为60,则RTSS会在构建中显示完美的60.0 FPS锁定,偶尔会降至59.9。
这也是帧时图的样子。我在地图上四处张望,试图锻炼一下帧限制器的一致性。可以说,我做了一个比Unity自己更好的帧限制器:)
答案 1 :(得分:0)
如果您的程序运行完美,并且spinwait完全在计算出的时间完成,您将获得60fps。
没有什么是完美的,因此它比计算的时间要多一些,因此帧速率较低。