【问题标题】:Double buffering slows frame rendering | systrace analysis双缓冲减慢帧渲染 |系统跟踪分析
【发布时间】:2016-11-11 04:27:09
【问题描述】:

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

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

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

您能告诉我这是否是正常行为,或者可能是什么原因?

【问题讨论】:

    标签: android graphics double-buffering systrace


    【解决方案1】:

    在您的图表上,您有一条注释“SurfaceFlinger 未命中 VSYNC”。

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

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

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

    你可以做一些事情:

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

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

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

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

    有关图形架构的更多详细信息,请参阅this doc

    【讨论】:

    • 感谢您的详细解答!我决定使用 GLSurfaceView 实现 OpenGL ES 1。我现在在 onDrawFrame() 方法中测量每一帧的增量时间。在过去的最后一帧时间上进行额外的插值(以消除大的“口吃跳跃”)是一个很大的改进。现在我的游戏运行非常流畅!
    猜你喜欢
    • 2014-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-03
    • 1970-01-01
    相关资源
    最近更新 更多