Программирование ограничителя FPS для Unity - PullRequest
0 голосов
/ 11 января 2019

Прежде всего, я знаю, что Application.targetFrameRate существует, и он выполняет достаточно хорошую работу, но я хочу что-то более точное. Для меня это ограничивает частоту кадров до 60,3, когда установлено 60, и около 204, когда установлено 200. Кстати, они измеряются в сборках (не в редакторе) с использованием 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. Но только для записи, я тоже попробовал пустой цикл while и получил те же результаты, за исключением гораздо более высокой загрузки ЦП.

Итак, если вы понимаете, как этот код должен работать, вы должны увидеть, что теоретически это должно очень точно фиксировать частоту кадров, особенно учитывая, что этот таймер имеет точность лучше 1 мкс (0,001 мс) в соответствии с Microsoft . Но, несмотря на все это, я получаю около 58,8 FPS, когда я устанавливаю это значение на 60 , что я действительно не понимаю. Я запускаю сборку в эксклюзивном полноэкранном режиме с отключенной V-синхронизацией. Кстати, я получаю гораздо более высокую частоту кадров без ограничений, поэтому базовая производительность игры не проблема.

Буду признателен за любую помощь!

Ответы [ 2 ]

0 голосов
/ 11 января 2019

Проблема оказалась страннее, чем я думал. Кажется, что эта реализация DateTime округляет сохраненное время внутренне до целого числа миллисекунд, поэтому мой ограничитель кадров расставлял кадры со скоростью 17 мс вместо 16,6666 мс при установке 60 кадров в секунду.

Решением было изменить код таймера, который я использую, и просто получить необработанное значение, возвращаемое GetSystemTimePreciseAsFileTime() вместо инкапсуляции его в объекте DateTime.

С этим изменением RTSS показывает идеальную блокировку 60.0 FPS в сборке с периодическими провалами до 59.9, если ограничитель установлен на 60.

Вот как это выглядит с графиком покадрового времени. Я искал вокруг карты разные вещи, чтобы попытаться немного улучшить согласованность ограничителя кадра. Можно с уверенностью сказать, что я сделал намного лучший ограничитель кадров, чем собственный Unity:)

FPS Limiter

0 голосов
/ 11 января 2019

Если ваша программа работает отлично и спинвейт заканчивается точно в рассчитанное время, вы получаете ровно 60 кадров в секунду.

Ничто не идеально, поэтому потребуется немного больше, чем рассчитанное время, что даст вам более низкую частоту кадров.

...