2

我正在使用自定义视图画布绘图 (postInvalidate()) 和HardwareAcceleration进行简单的 2D 游戏。经过数周的性能分析,我决定通过 Interface Choreographer.FrameCallback将我的更新和绘图操作与 VSYNC 脉冲同步。我认为这是获得平稳运动的正确方法。

然而,我仍然经历波涛汹涌的运动。我用systrace对其进行了分析,发现这与我的 BufferQueue 有关。一旦双缓冲开始,帧时间就会超过 16 毫秒。我做了一些解释的跟踪截图:

整个draw操作等待SurfaceFlinger(消费者)的buffer释放,将自己新的空Buffer出列。

你能告诉我这是否是一种常规行为或可能是什么原因?

4

1 回答 1

1

在您的图表上,您有一条注释,“SurfaceFlinger 错过了 VSYNC”。

但是,如果您查看 BufferQueue 行,您可以看到缓冲区是在 VSYNC 截止日期之后到达的。SurfaceFlinger 醒了,但是什么也做不了。

然后,您的应用程序提供了一个额外的缓冲区,这意味着您有两个待处理的缓冲区。由于您继续在每个 VSYNC 上提供缓冲区,因此队列永远不会回到零缓冲区。当队列填满时,每次添加额外缓冲区的尝试都会导致阻塞。

FWIW,您的 BufferQueue 是三重缓冲的:两个在队列中,一个在显示器上。

您可以做几件事:

  1. 如果您错过了截止日期,请让应用丢帧。
  2. 指定帧的呈现时间,以便 SurfaceFlinger 在超过时间时丢弃它们。
  3. 每隔一段时间故意丢弃一帧以让队列为空。(不是首选方法。)

#2 仅适用于 SurfaceView 上的 GLES,因此我们可以忽略它。

#1 可能对你有用;您可以在 Grafika 中看到一个示例。它本质上说,“如果下一个 VSYNC 在不到 2 毫秒内触发,或者已经触发,则不要费心渲染当前帧。” View/invalidate 方法并没有为您提供与 GLES 相同的细粒度控制,因此我不确定它的效果如何。

在繁忙的设备上平滑动画的关键不是以 60 fps 的速度播放每一帧。关键是根据增量时间进行更新,因此即使丢一两帧,事情看起来也很流畅。

有关图形架构的更多详细信息,请参阅此文档

于 2016-07-23T04:09:25.667 回答