我正在调查我的应用程序的性能,因为我注意到它在滚动时丢了一些帧。我运行了 systrace(在运行 4.3 的 Nexus 4 上)并注意到输出中有一个有趣的部分。
一开始一切都很好。放大左侧部分,我们可以看到绘图在每个 vsync 上开始,并在剩余时间结束时完成,并等待下一个 vsync。由于它是三重缓冲的,它应该被绘制到一个缓冲区中,该缓冲区将在完成后发布到以下 vsync 上。
在放大的屏幕截图中的第 4 个 vsync 上,应用程序做了一些工作,并且绘制操作没有及时完成下一个 vsync。但是,我们不会丢弃任何帧,因为之前的抽签是在前一帧进行的。
但是,在这种情况发生之后,绘图操作并不能弥补错过的垂直同步。相反,每个 vsync 只启动一个绘制操作,现在它们不再向前绘制一帧。
放大右侧部分,该应用程序做了更多的工作并且错过了另一个垂直同步。因为我们没有在前面画一个框架,所以实际上这里丢掉了一个框架。在此之后,它会回到前面绘制一帧。
这是预期的行为吗?我的理解是,如果您错过了 vsync,三重缓冲可以让您恢复,但这种行为看起来就像每错过两次 vsync 就会丢帧一次。
跟进问题
在此屏幕截图的右侧,应用程序实际上渲染缓冲区的速度比显示器消耗缓冲区的速度要快。在 performTraversals #1 期间(在屏幕截图中标记),假设正在显示缓冲区 A,正在渲染缓冲区 B。#1 在 vsync 之前很久就完成了,并将缓冲区 B 放入队列中。此时,应用程序不应该能够立即开始渲染缓冲区C吗?相反,performTraversals #2 直到下一个 vsync 才开始,这浪费了其间的宝贵时间。
同样,我对此处左侧是否需要 waitForever 感到有些困惑。假设缓冲区 A 正在显示,缓冲区 B 在队列中,缓冲区 C 正在渲染。当缓冲区 C 完成渲染时,为什么不立即将其添加到队列中?相反,它会一直等待直到缓冲区 B 从队列中删除,此时它会添加缓冲区 C,这就是为什么无论应用程序渲染缓冲区的速度有多快,队列似乎总是保持大小为 1。