为什么WPF中的帧率不规则且不限于监视刷新?

时间:2011-04-28 00:50:13

标签: wpf animation

我正在测量简单WPF动画中帧之间的时间。穿孔器说该应用程序的执行速度约为60fps,因此我预计帧之间的时间约为16.6ms,偏差很小。

    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毫秒。我原本预计显示器的刷新率会设置帧之间的时间下限(即每帧之间60Hz = 16.6ms,更快的是没有意义)。

DefaultFrameRate

Y - 以滴答为单位的帧之间的时间(10,000滴= 1ms)
X - 帧数

可能的混杂因素

  • 计时器不准确
  • 如果CompositionTarget.Rendering实际上与单帧的绘制无关

我正在使用的项目:SimpleWindow.zip

===修改

Markus指出我可以使用RenderingEventArgs.RenderingTime.Ticks而不是DateTime.Now.Ticks。我重复了这次跑步并得到了截然不同的结果。唯一的区别是计时方法:

DateTime.Now.Ticks

DefaultFrameRate

RenderingEventArgs.RenderingTime.Ticks

enter image description here

来自RenderingEventArgs的数据产生的数据更接近预期的16.6ms /帧,并且它是一致的。

  • 我不确定为什么DateTime.Now和RenderingEventArgs会产生如此不同的数据。
  • 假设RenderingEventArgs正在产生正确的时间,那么这些时间不是预期的16.6ms仍然有点令人不安。

如果显示器每16.6ms更新一次并且WPF每14.9ms更新一次,我们可以预期会导致撕裂的竞争条件。也就是说,当显示器试图读取图像时,大约每第10帧WPF将尝试写入其图像。

3 个答案:

答案 0 :(得分:20)

我向WPF团队提出了这个问题,这里是我给出的回复摘要:

  

从UI计算帧率   线程很难。 WPF解耦   来自渲染线程的UI线程。   UI线程将呈现:

     
      
  • 每当某些东西被标记为脏,我们就会流失到渲染   优先。这种情况可能更频繁发生   比刷新率。

  •   
  • 如果动画处于待处理状态(或者有人挂钩了   CompositionTarget.Rendering事件)我们   将在UI线程之后呈现   每个来自渲染线程的礼物。   这涉及推进时机   树所以动画计算他们的新   值。

  •   
     

因此,   CompositionTarget.Rendering事件可以   每个“框架”被提升多次。   我们报告了预期的“帧时间”   RenderingEventArgs,和   应用程序应该只做   报告时“每帧”工作   帧时间变化。

     

请注意,UI线程正在执行许多操作   事情,所以它是不可靠的   假设CompositionTarget.Rendering   事件处理程序运行可靠   韵律。我们使用的模型(解耦   两个线程)意味着UI   线程可能有点落后,因为   它正在计算a的动画   未来的帧时间。

特别感谢Dwayne Need向我解释这一点。

答案 1 :(得分:8)

首先 - 'Christopher Bennage的答案有一个很好的解释并提供解决方案的提示:

当报告的帧时间发生变化时,“仅执行”每帧“工作”

这有点难,因为RenderingEventArgs被隐藏为正常的EventArgs并且必须进行强制转换。

为了使这更容易一些,可以在“EVAN'S CODE CLUNKERS”中找到一个方便的解决方案 http://evanl.wordpress.com/2009/12/06/efficient-optimal-per-frame-eventing-in-wpf/

我拿了他的代码并修改了一下。现在只需把我的剪辑,将类添加到你的Project,使用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 :(得分:2)

WPF并非旨在成为一个恒定的帧速率渲染系统。当屏幕中的元素标记为已更改时,WPF将呈现屏幕。渲染系统作为消息循环运行,因此无法确保以特定间隔渲染帧。