pet*_*_sm 9 performance android systrace
我正在调查我的应用程序的性能,因为我注意到它在滚动时丢弃了一些帧.我运行systrace(在运行4.3的Nexus 4上),并在输出中注意到一个有趣的部分.
一开始一切都很好.放大左侧部分,我们可以看到绘图从每个vsync开始,完成备用时间,并等到下一个vsync.由于它是三重缓冲的,因此应该将其绘制到一个缓冲区中,该缓冲区将在完成后发布在以下vsync上.
在放大屏幕截图中的第4个vsync上,应用程序执行了一些工作,并且绘制操作没有及时完成下一个vsync.但是,我们不会删除任何帧,因为之前的抽奖正在提前一帧.
在这种情况发生之后,绘制操作不能弥补错过的vsync.相反,每个vsync只启动一个绘制操作,现在它们不再向前绘制一个帧.
放大右侧部分,该应用程序会做更多工作并错过另一个vsync.由于我们没有向前绘制一个帧,所以实际上帧丢失了.在此之后,它会回到前方画一帧.
这是预期的行为吗?我的理解是,如果你错过了一个vsync,三重缓冲允许你恢复,但是这种行为看起来像每两个你错过的vsyncs一次丢帧.
跟进问题
在此屏幕截图的右侧,应用程序实际上渲染缓冲区的速度比显示器消耗它们的速度快.在performTraversals#1(屏幕截图中标记)期间,假设正在显示缓冲区A并且正在渲染缓冲区B. #1在vsync之前完成,并将缓冲区B放入队列中.此时,应用程序是否应该能够立即开始渲染缓冲区C?相反,performTraversals#2直到下一个vsync才开始,浪费了宝贵的时间.
同样地,我对左侧对waitForever的需求感到有些困惑.假设正在显示缓冲区A,缓冲区B在队列中,并且正在渲染缓冲区C. 当缓冲区C完成渲染时,为什么不立即将它添加到队列中?相反,它会执行waitForever,直到从队列中删除缓冲区B,此时它会添加缓冲区C,这就是为什么无论应用程序渲染缓冲区有多快,队列似乎始终保持大小为1.
提供的缓冲量仅在保持缓冲区满的情况下才有意义.这意味着渲染速度比显示器消耗速度快.
标签没有出现在您的图像中,但我猜测绿色vsync行上方的紫色行是BufferQueue状态.您可以看到它通常在任何时候都有0或1个完整缓冲区.在"放大左侧"图像的最左侧,您可以看到它有两个缓冲区,但之后它只有一个,并且在屏幕的3/4处您看到一个非常短的紫色条形图表明它几乎没有及时渲染帧.
更新添加的问题......
另一篇文章中的细节几乎没有触及表面.我们必须更深入.
systrace中显示的BufferQueue计数是排队缓冲区的数量,即包含内容的缓冲区的数量.当SurfaceFlinger抓取缓冲区进行显示时,它立即释放缓冲区,将其状态更改为"free".当缓冲区显示在叠加层上时,这尤其令人兴奋,因为显示器直接从缓冲区渲染(而不是合成到暂存缓冲区并显示).
让我再说一遍:显示器主动读取数据以在屏幕上显示的缓冲区在BufferQueue中标记为"free".缓冲区具有最初"活动"的关联围栏.当它处于活动状态时,不允许任何人修改缓冲区内容.当显示器不再需要缓冲区时,它会向围栏发出信号.
因此,跟踪左侧的代码所在的原因waitForever()
是因为它正在等待围栏发出信号.当VSYNC命中时,显示屏切换到不同的缓冲区,向栅栏发出信号,您的应用程序可以立即开始使用缓冲区.这消除了如果您必须等待SurfaceFlinger唤醒,看到缓冲区不再使用,通过BufferQueue发送IPC以释放缓冲区等所导致的延迟.
请注意,调用waitForever()
只会在您不落后时显示(跟踪的左侧和右侧).当队列只有一个完整的缓冲区时,我不确定为什么会发生这种情况 - 它应该将最旧的缓冲区出列,该缓冲区应该已经发出信号.
最重要的是,你永远不会看到BufferQueue超过两个进行三重缓冲.
并非所有设备都如上所述工作.Nexus 7(2012)不使用"显式同步"机制,而前ICS设备根本没有BufferQueues.
回到编号的截图,是的,'1'和'2'之间有足够的时间,你的应用程序可以运行performTraversals().在不知道你的应用程序正在做什么的情况下很难肯定地说,但我猜你有一个Choreographer驱动的动画循环唤醒每个VSYNC并且确实有效.它不会经常运行.
如果你systrace Android Breakout,你可以看到你尽可能快地渲染时的样子("排队填充")并依靠BufferQueue背压来调节游戏速度.
将运行4.3的N4与运行4.4的N4进行比较尤其有趣.在4.3上,跟踪与你的类似,队列主要在1处徘徊,常规下降到0,偶尔会有2次上升到2.在4.4,队列几乎总是在2,偶尔下降到1.在两种情况下它都是睡在eglSwapBuffers()
; 在4.3中,跟踪通常显示waitForever()
在下面,而在4.4中显示dequeueBuffer()
.(我不知道这个随便的原因.)
更新2: 4.3和4.4之间差异的原因似乎是Nexus 4驱动程序更改.4.3驱动程序使用旧的dequeueBuffer调用,该调用变为dequeueBuffer_DEPRECATED()
(Surface.cpp第112行).旧接口不会将fence作为"out"参数,因此调用必须调用waitForever()
自身.较新的接口只是将栅栏返回到GL驱动程序,它在需要时进行等待(可能不会立即进行).
更新3:甚至更长的解释是,现在可以在这里.
归档时间: |
|
查看次数: |
2774 次 |
最近记录: |