Подтвердить что ты не робот

Почему частота кадров в WPF нерегулярна и не ограничена для мониторинга обновления?

Я измеряю время между кадрами в простой анимации WPF. Перфоратор говорит, что приложение работает со скоростью ~ 60 кадров в секунду, поэтому я ожидал, что время между кадрами будет ~ 16,6 мс с небольшим отклонением.

    public MainWindow()
    {
    ...
        CompositionTarget.Rendering += Rendering;
    }

    List<long> FrameDurations = new List<long>();
    private long PreviousFrameTime = 0;
    private void Rendering(object o, EventArgs args)
    {
        FrameDurations.Add(DateTime.Now.Ticks - PreviousFrameTime);
        PreviousFrameTime = DateTime.Now.Ticks;
    }

Меня удивили две вещи:

  • Время между кадрами довольно нерегулярно
  • Время между кадрами составляет ~ 8 мс. Я ожидал, что частота обновления монитора установит более низкую границу времени между кадрами (т.е. 60 Гц = 16,6 мс между каждым фреймом и что-то быстрее бессмысленно).

DefaultFrameRate

Y - время между кадрами в тиках (10 000 тиков = 1 мс)
X - количество кадров

Возможные смешающие факторы

  • Неточность таймера
  • Если CompositionTarget.Rendering фактически не коррелирует с рисунком одного кадра

Проект, который я использую: SimpleWindow.zip

=== Edit

Маркус указал, что я могу использовать RenderingEventArgs.RenderingTime.Ticks вместо DateTime.Now.Ticks. Я повторил пробег и получил очень разные результаты. Единственное различие заключается в методе синхронизации:

DateTime.Now.Ticks

DefaultFrameRate

RenderingEventArgs.RenderingTime.Ticks

enter image description here

Данные из RenderingEventArgs значительно улучшают данные 16,6 мс/фрейм, и это согласовано.

  • Я не уверен, почему DateTime.Now и RenderingEventArgs будут создавать такие очень разные данные.
  • Предполагая, что RenderingEventArgs производит правильные времена, он все еще немного сбивает с толку, что эти времена не являются ожидаемыми 16.6ms.

Если дисплей обновляется каждые 16,6 мс, а WPF обновляется каждые 14,9 мс, мы можем ожидать состояние гонки, которое приведет к разрыву. То есть примерно каждый 10-й кадр WPF будет пытаться записать свое изображение, пока дисплей пытается прочитать изображение.

4b9b3361

Ответ 1

Я поднял этот вопрос с командой WPF, и вот резюме ответа, который мне дал:

Вычисление частоты кадров из пользовательского интерфейса нить сложна. Развязки WPF поток пользовательского интерфейса из потока рендеринга. Поток пользовательского интерфейса отобразит:

  • Всякий раз, когда что-то помечено как грязное, и мы спускаемся к Render приоритет. Это может случиться чаще чем частота обновления.

  • Если ожидание анимации (или если кто-то подключил Событие CompositionTarget.Rendering) мы будет отображаться в потоке пользовательского интерфейса после все присутствующие из нити рендеринга. Это предполагает ускорение выбора времени дерево, поэтому анимации вычисляют их новые значения.

Из-за этого Событие CompositionTarget.Rendering может подниматься несколько раз за "кадр". Мы сообщаем о предполагаемом "времени кадра" в RenderingEventArgs и приложения должны делать "per-frame" работает, когда сообщается изменение времени кадра.

Обратите внимание, что поток пользовательского интерфейса делает много вещей, поэтому он не является надежным предположим, что CompositionTarget.Rendering обработчик событий работает на надежном частоты вращения педалей. Используемая нами модель (развязка) два потока) означает, что пользовательский интерфейс нить может немного отстать, поскольку он вычисляет анимацию для будущее время кадра.

Особая благодарность Дуэйн Нужно объяснить мне это.

Ответ 2

Сначала - "Кристофер Беннадж-Ответ" имеет хорошее объяснение и дает подсказку:

"Выполняется только" за кадр "при изменении отображаемого времени кадра"

Это немного сложно, так как RenderingEventArgs скрыты как обычные EventArgs, и выполнение должно выполняться.

Чтобы сделать это немного легче, удобное решение можно найти в "EVAN CODE CLUNKERS" http://evanl.wordpress.com/2009/12/06/efficient-optimal-per-frame-eventing-in-wpf/

Я взял его код и немного изменил его. Теперь просто возьмите мой снимок, добавьте класс в свой проект, используйте CompositionTargetEx, вы использовали CompositionTarget, и все в порядке:)

public static class CompositionTargetEx { 
    private static TimeSpan _last = TimeSpan.Zero; 
    private static event EventHandler<RenderingEventArgs> _FrameUpdating; 
    public static event EventHandler<RenderingEventArgs> Rendering { 
        add { 
            if (_FrameUpdating == null)                 
                CompositionTarget.Rendering += CompositionTarget_Rendering;
            _FrameUpdating += value; 
        } 
        remove { 
            _FrameUpdating -= value; 
            if (_FrameUpdating == null)                
                CompositionTarget.Rendering -= CompositionTarget_Rendering; 
        } 
    } 
    static void CompositionTarget_Rendering(object sender, EventArgs e) { 
        RenderingEventArgs args = (RenderingEventArgs)e;
        if (args.RenderingTime == _last) 
            return;
        _last = args.RenderingTime; _FrameUpdating(sender, args); 
    } 
}

Ответ 3

WPF не предназначен для постоянной системы рендеринга частоты кадров. WPF отображает экран, когда элементы на экране помечены как измененные. Система рендеринга работает как цикл сообщения, поэтому нет способа гарантировать, что кадр будет отображаться через определенные интервалы.