2011-04-28 43 views
18

我正在测量一个简单的WPF动画帧之间的时间。穿孔器说应用程序的执行速度为〜60fps,所以我预计帧之间的时间约为16.6ms,而且几乎没有偏差。为什么WPF中的帧频不规则并不限制显示器刷新?

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; 
    } 

有两件事情让我很吃惊:帧之间

  • 时间相当不规则
  • 帧之间的时间是8ms的〜。我本来期望显示器的刷新速率会在帧之间设定一个较低的时间限制(即每帧之间60Hz = 16.6ms,而任何速度都是毫无意义的)。

DefaultFrameRate

Ÿ - 帧计数

可能的混杂因素

  • 定时误差
  • 如果CompositionTarget - 在蜱框架(10,000蜱= 1毫秒)
    X之间的时间。渲染实际上并不涉及单个框架的绘制

我使用的项目:马库斯指出,我可以用RenderingEventArgs.RenderingTime.Ticks代替DateTime.Now.Ticks SimpleWindow.zip

===编辑

。我重复了跑步,得到了非常不同的结果。唯一的区别是定时方法:

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都会尝试写入图像。

+0

秒表类是去避免计时器误差 – 2011-04-28 01:12:30

+0

这种“可能”是有益的方式:http://rhnatiuk.wordpress.com/2008/12/21/wpf-video-playback-problems/ - 我不确定这个问题有多相关,但我认为它今天仍然存在。 – 2011-04-28 07:03:53

+3

@Tristan @Martin时间测量根本不需要!你可以[将EventArgs转换为RenderingEventArgs来获取RenderTime](http://msdn.microsoft.com/en-us/library/system.windows.media.compositiontarget.shipping(VS.95).aspx) – 2011-04-28 10:32:54

回答

19

我提出与WPF队这个问题,这里是响应的总结,我给出:

计算从UI线程 帧率是困难的。 WPF从渲染线程中分离出 UI线程。 UI线程将呈现:

  • 不管什么时候被标记为脏,我们流失降至渲染 优先。这比刷新率更经常发生 。

  • 如果动画正在等待(或者,如果有人迷上 CompositionTarget.Rendering事件),我们将 每 从目前的渲染线程渲染后的UI线程。 这涉及提前计时 树,以便动画计算其新的 值。

正因为如此,该 CompositionTarget.Rendering事件可以 提高每“帧”多次。 当报告的 帧时间发生变化时,我们报告 中的预期“帧时间”RenderingEventArgs和 应用程序应该只执行“每帧” 工作。

注意,UI线程正在做许多事情 ,所以它是不可靠的 承担CompositionTarget.Rendering 事件处理程序在一个可靠的 节奏运行。我们使用的模型(将这两个线程去耦为 )意味着线程可能稍微落后,因为它正在计算未来帧时间的动画。

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

+5

太棒了。我在哪里可以学到更多这样的东西?有一本好书吗?我一直无法找到'引擎盖下'的WPF信息。一个问题是,因为WPF很容易(相对于GDI +等),所以有很多人写这篇文章的人并不真正了解他们在做什么,但仍然写下了它。似乎有一小部分质量差的信息需要过滤掉。 – Tristan 2011-06-06 18:43:47

2

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

+0

你的答案是有道理的,并且与我看到的所有内容都一致。你能提供任何数据或手段来备份你的断言吗? (此外,感谢您的时间,我非常感谢) – Tristan 2011-05-11 18:05:03

+1

您可以从WPF的一位设计人员的第9频道的WPF体系结构中检查此视频(他们谈论了很多关于Avalon的内容,它是WPF之前的代码名称发布)[链接](http://channel9.msdn.com/Shows/Going+Deep/Greg-Schechter-Windows-Presentation-FoundationWPF-Architecture) – Vicro 2011-05-12 02:38:05

+1

我还发现了一些关于[MSDN]的信息(http:// msdn .microsoft.com/en-us/library/ms748373.aspx#visual_rendering_behavior)有关渲染行为。 – Vicro 2011-05-12 02:40:33

8

首先 - “克里斯托弗Bennage's应答有一个很好的解释,并提供了一个暗示,一个解决方案:

‘只有做到‘每帧’工作的上报帧时间改变’

这是一个有点难,因为RenderingEventArgs被隐藏为正常的EventArgs并且必须完成强制转换。

为了使这个有点easyer,一个方便易解决方案可以在“埃文的CODE旧车” 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); 
    } 
}