为Unity编程FPS限制器

时间:2019-01-11 14:41:46

标签: c# unity3d frame-rate

首先,我知道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的情况下以独家全屏模式运行构建。顺便说一句,我正在不受限制地获得更高的帧频,因此游戏的基本性能不是问题。

任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:1)

问题比我想的还要奇怪。看来DateTime的这种实现将内部存储的时间舍入为整数毫秒,因此我的帧限制器将帧定为17毫秒而不是设置为60 FPS时的16.6666毫秒。

解决方案是更改我使用的计时器的代码,只获取GetSystemTimePreciseAsFileTime()返回的原始值,而不是将其封装在DateTime对象中。

进行此更改后,如果将限制器设置为60,则RTSS会在构建中显示完美的60.0 FPS锁定,偶尔会降至59.9。

这也是帧时图的样子。我在地图上四处张望,试图锻炼一下帧限制器的一致性。可以说,我做了一个比Unity自己更好的帧限制器:)

FPS Limiter

答案 1 :(得分:0)

如果您的程序运行完美,并且spinwait完全在计算出的时间完成,您将获得60fps。

没有什么是完美的,因此它比计算的时间要多一些,因此帧速率较低。