Почему частота кадров в WPF нерегулярна и не ограничивается мониторингом обновления? - PullRequest
20 голосов
/ 28 апреля 2011

Я измеряю время между кадрами в простой анимации WPF.Perforator говорит, что приложение работает со скоростью ~ 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 - время между кадрами в тиках (10000 тиков = 1 мс)X - Количество кадров

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

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

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

=== Редактировать

Маркус указал, что я мог бы использовать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,6 мс.

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

Ответы [ 3 ]

20 голосов
/ 14 мая 2011

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

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

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

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

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

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

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

8 голосов
/ 02 мая 2013

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

«Работает« на кадр »только при изменении сообщенного времени кадра»

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

Чтобы сделать это немного проще, удобное решение можно найти в "EVAN'S 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); 
    } 
}
2 голосов
/ 11 мая 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...