Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/r/79.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么WPF中的帧速率不规则,并且不限于监视器刷新?_Wpf_Animation - Fatal编程技术网

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

为什么WPF中的帧速率不规则,并且不限于监视器刷新?,wpf,animation,Wpf,Animation,我在一个简单的WPF动画中测量帧间的时间。Perforator说该应用程序的执行速度约为60fps,因此我预计帧间时间约为16.6ms,偏差很小 public MainWindow() { ... CompositionTarget.Rendering += Rendering; } List<long> FrameDurations = new List<long>(); private long Pre

我在一个简单的WPF动画中测量帧间的时间。Perforator说该应用程序的执行速度约为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;
    }
public主窗口()
{
...
CompositionTarget.Rendering+=呈现;
}
List FrameDurations=新列表();
private long PreviousFrameTime=0;
私有void呈现(对象o、事件args args)
{
添加(DateTime.Now.Ticks-PreviousFrameTime);
PreviousFrameTime=DateTime.Now.Ticks;
}
有两件事让我吃惊:

  • 两帧之间的时间相当不规则
  • 帧间时间约为8毫秒。我原以为显示器的刷新率会设置帧间时间的下限(即每帧之间的时间为60Hz=16.6ms,任何更快的都是毫无意义的)

Y-帧间时间,单位为滴答(10000滴答=1ms)
X帧计数

可能的混杂因素

  • 定时器误差
  • 如果CompositionTarget.Rendering实际上与单个帧的绘图不相关
我正在使用的项目:

==编辑

Markus指出我可以使用RenderingEventArgs.RenderingTime.Ticks而不是DateTime.Now.Ticks。我重复了一遍,得到了完全不同的结果。唯一的区别是计时方法:

DateTime.Now.Ticks

RenderingEventArgs.RenderingTime.Ticks

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

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

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

WPF不是设计为恒定帧速率渲染系统的。当屏幕中的元素标记为已更改时,WPF将渲染到屏幕。渲染系统作为消息循环运行,因此无法确保以特定的间隔渲染帧。

我向WPF团队提出了这个问题,下面是我得到的答复摘要:

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

  • 每当有东西被标记为脏的时候,我们就把水排干进行渲染 优先。这种情况可能更经常发生 比刷新率高

  • 如果动画处于挂起状态(或者如果有人钩住了 CompositionTarget.Rendering事件)我们 之后将在UI线程上呈现 渲染线程中的每个存在。 这包括提前时间安排 树,以便动画计算新的 价值观

正因为如此,这个 CompositionTarget.Rendering事件可以 每“帧”可提升多次。 我们在报告中报告了预期的“帧时间” RenderingEventArgs,以及 应用程序只应 报告时的“每帧”工作 帧时间改变

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


特别感谢德韦恩需要向我解释这一点。

首先-”克里斯托弗·本纳奇的回答有很好的解释,并提供了解决方案的提示:

仅在报告的帧时间更改时执行“每帧”工作

这有点困难,因为RenderingEventArgs作为普通EventArgs隐藏,必须进行转换

为了使这一点更容易,可以在“EVAN的代码破烂”中找到一个方便的解决方案

我把他的代码修改了一下。现在只需将我的截图添加到您的项目中,使用CompositionTargetEx您是否使用了CompositionTarget,您很好:)

公共静态类CompositionTargetEx{
私有静态时间跨度_last=TimeSpan.0;
私有静态事件事件处理程序\u帧更新;
公共静态事件事件处理程序呈现{
添加{
如果(_frameupdateing==null)
CompositionTarget.Rendering+=CompositionTarget\u Rendering;
_帧更新+=值;
} 
删除{
_帧更新-=值;
如果(_frameupdateing==null)
CompositionTarget.Rendering-=CompositionTarget\u Rendering;
} 
} 
静态void CompositionTarget_呈现(对象发送方,事件参数e){
RenderingEventArgs args=(RenderingEventArgs)e;
如果(args.RenderingTime==\u last)
返回;
_last=args.RenderingTime;\u帧更新(发送方,args);
} 
}

Stopwatch类是避免计时器不准确的方法这“可能”很有用:-我不确定这个问题有多重要,但我认为它今天仍然存在。@Tristan@Martin时间测量根本不需要!你可以@Patrick-我怀疑Roman有什么发现。我希望他的文章包括数据。没有数据,他的文章读起来像是一种观点(受过教育的观点,但仍然不是‘事实’)。@Markus-当然!我觉得自己很笨。谢谢:)你的回答很有道理,而且与我所看到的一切都是一致的。您能否提供任何数据或方法来备份您的断言?(同时,感谢您的时间。我非常感谢)您可以在WPF A上查看此视频
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); 
    } 
}