c# - 为 Unity 编写 FPS 限制器
问题描述
首先,我知道它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); });
}
}
}
基本上,这是一个设置为在脚本执行顺序中的任何其他脚本之前执行的单例,它会阻止执行,直到到达正确的时间。
它的工作方式是跟踪上次允许渲染帧的精确时间(它从非常精确的低级计时器获取当前时间,这里有更多详细信息)。然后在每次调用它的Update()
函数时,它会增加1.0 / FPSLimit
几秒钟以获取应该渲染下一帧的时间,然后如果当前时间小于这个时间,它会阻止执行,直到达到该时间戳。
SpinWait
基本上只是一种阻止执行的有效方法,而无需像空while
循环那样固定 CPU。但为了记录,我也尝试了一个空while
循环,并得到了相同的结果,除了 CPU 使用率更高。
因此,如果您了解此代码应该如何工作,您应该会看到理论上这应该非常精确地锁定帧速率,特别是考虑到根据 Microsoft的说法,这个计时器的精度优于 1μs (0.001ms) 。但是尽管如此,当我将其设置为 60 时,我得到了大约 58.8 FPS,我真的不明白。我在禁用垂直同步的情况下以独占全屏模式运行构建。顺便说一句,我在没有限制的情况下获得了更高的帧速率,所以游戏的基本性能不是问题。
任何帮助,将不胜感激!
解决方案
这个问题比我想象的更奇怪。似乎这种实现在DateTime
内部将存储的时间四舍五入到整数毫秒,所以我的帧限制器在设置为 60 FPS 时以 17 毫秒而不是 16.6666 毫秒来调整帧。
解决方案是更改我正在使用的计时器的代码,并仅获取返回的原始值,GetSystemTimePreciseAsFileTime()
而不是将其封装在DateTime
对象中。
通过此更改,RTSS 在构建中显示完美的 60.0 FPS 锁定,如果限制器设置为 60,则偶尔会下降到 59.9。
这也是帧时间图的样子。我在地图上四处寻找不同的东西,试图稍微锻炼一下帧限制器的一致性。可以肯定地说,我做了一个比 Unity 自己的更好的帧限制器:)
推荐阅读
- kubernetes - kubectl 错误:无法添加密钥 dashboard.yaml,该名称的另一个密钥已存在
- 3d - 根据其在 Godot 中的旋转变换空间节点
- java - 如何在 GSON 反序列化期间忽略空字符串
- r - R Shiny中的相互依赖的过滤器级联 - updatePickerInput
- java - 如何使用 Room 创建多个表?
- swift - 进入后台后如何保持在打开的 WKWebView 上创建的 WebSocket 连接一段时间
- django - 如何在 Django 模板的内循环部分访问外循环上下文值
- c# - WCF 服务 SSRS 在我的浏览器上通过 URL 工作,但在 WCF 客户端上不工作?
- r - 尝试迭代函数的输出以进行引导
- javascript - 全局变量在 PHP 中有效,但在 Javascript 中无效