我正在测量简单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;
}
有两件事让我感到惊讶:
Y - 以滴答为单位的帧之间的时间(10,000滴= 1ms)
X - 帧数
可能的混杂因素
我正在使用的项目:SimpleWindow.zip
===修改
Markus指出我可以使用RenderingEventArgs.RenderingTime.Ticks而不是DateTime.Now.Ticks。我重复了这次跑步并得到了截然不同的结果。唯一的区别是计时方法:
DateTime.Now.Ticks
RenderingEventArgs.RenderingTime.Ticks
来自RenderingEventArgs的数据产生的数据更接近预期的16.6ms /帧,并且它是一致的。
如果显示器每16.6ms更新一次并且WPF每14.9ms更新一次,我们可以预期会导致撕裂的竞争条件。也就是说,当显示器试图读取图像时,大约每第10帧WPF将尝试写入其图像。
答案 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将呈现屏幕。渲染系统作为消息循环运行,因此无法确保以特定间隔渲染帧。