【性能优化】RenderThread工作流程梳理

一、为什么要梳理RenderThread各工作阶段的逻辑?

对于Android原声的UI绘制流程,主线程我们再熟悉不过了,因此分析丢帧问题也比较好定位原因,从而进行优化,比如:常见的卡binder调用、布局负责层级过于负责导致measure/ layout /draw过程耗时,都能从trace明显看到。
1、RenderThread线程渲染耗时无明确指向性**
RenderThread一般出现耗时,要么系统层面逻辑引起,要么就是绘制工作过大,导致GPU渲染时间超时。

这两个问题对于应用来说,只能尝试优化后者,无非就是减少过度绘制层数、精简动画效果、精简一些模糊、渐变、阴影的效果。优化后代码,再进行测试验证,整个优化链路来说,并没有像主线程那么清晰有明确指向。比如:绘制超时了,具体是啥效果比较耗费渲染性能导致的?或者说具体是对应到应用层的哪个方法调用引起的?这都是没有明确指向的。
2、RenderThread渲染的逻辑代码,并不是在java层实现,而是转到了native层需和底层渲染接口打交道,阅读起来难度较大

这个其实也好理解,毕竟要和比较底层的渲染接口通信了,应用层的渲染指令,转成android图形渲染底层的指令,这些都是比较底层的实现了,用java来实现,性能完全不够看,甚至都没法直接访问到。

3、需要和sf进程关联分析,而应用层不了解sf工作原理

图像数据流

图片引用自:
Android Systrace 基础知识 - Vsync 解读

二、RenderThread工作的5个阶段

RenderThread工作的5个阶段

三、sf合成buffer时如何保证GPU已经将其内容渲染完成?

RenderThread执行渲染的几步中flush commands是把GPU指令传递给GPU,通知GPU需要开始绘制buffer内容,注意这里GPU的 pipeline 真正何时开始,是由gpu自己的command队列决定的,并不是同步就一定开始的,可能会在传递指令后延迟一定时间开始。

但是这个步骤并不是同步阻塞式的等GPU绘制指令完成的,把指令传递给GPU后,就继续执行buffer交换(eglSwapBuffersWithDamageKHR)、重新把buffer推到sf的BufferQueue中了。所以从这个逻辑中,就能发现有个问题,就是sf在收到vsync-sf信号时,acquireBuffer操作需要做合并时,还需要等待这个buffer上的gpu绘制工作完成(***注意:这里实际上android系统为了保持vsync-sf信号的节奏不被打乱,是才采用了跳帧的方式,也就是等下一个vsync-sf信号达到时执行合成再看buffer有没有ready再决定是否逻辑合成逻辑***),如果遇到要做合并时还未绘制完成,就会在trace的应用进程的GPU tid轨道上的上产生“waiting for GPU completion id”这样的trace tag:  

GPU线程的waiting for GPU completion id的trace截图

注意:这里GPU 15043这个线程,代表的并不是GPU的执行绘制线程,仅代表的是 CPU 等GPU绘制逻辑完成的过程。

sf合成时需要等GPU完成的过程->也就是sf的fence同步机制。

sf合成时需要等GPU完成的过程

分析以上trace的过程:

VSYNC-sf 到达
↓
buffer 还没 ready(GPU 未完成)
↓
SF 放弃 latch
↓
这一帧不合成
↓
sleep ~16ms
↓
等下一帧 VSYNC

sf在发现buffer未ready时的决策问题,此时 SF 有两个选择:

  • 方案A:阻塞等 fence,等 GPU 完成 → 再合成
  • 方案B:直接跳过这一帧(实际采用),不等 → skip frame
    绝大多数情况下选择 B(非阻塞),选择是不卡 VSYNC 节奏

于是发生了截图的trace中看到的现象:

VSYNC-sf 到达
↓
buffer 还没 ready(GPU 未完成)
↓
SF 放弃 latch
↓
这一帧不合成
↓
sleep ~16ms
↓
等下一帧 VSYNC
fence机制的理解
在 Android 中, App 提交(Queue)Buffer 给 SF 时,通常会携带一个 Acquire Fence。这个 Fence 代表了 GPU 对该 Buffer 的渲染进度。当 VSYNC-sf 到达时,SF 必须检查这个 Fence 是否已经 signal(即 GPU 是否已经真正把画面画完了)。  

重新放大了看上面的trace截图,可以看到sf的 commit 385807这次任务,从trace打印也能看到fence 是unsignaled状态!

sf的commit 385807这次任务,从trace打印也能看到fence 是unsignaled状态

对应的源码路径
源码捷选如下:frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferCheck(
        const TransactionHandler::TransactionFlushState& flushState) {
     const bool fenceSignaled = !acquireFenceAvailable ||
                        s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
     if (!fenceSignaled) {
        // check fence status
                    const bool allowLatchUnsignaled =
                            shouldLatchUnsignaled(s, transaction.states.size(),
                                                  flushState.firstTransaction) &&
                            layer->isSimpleBufferUpdate(s);
                    if (allowLatchUnsignaled) {
                        SFTRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s",
                                       layer->name.c_str());
                        ready = TransactionReadiness::NotReadyUnsignaled;
                    } else {
                           ready = TransactionReadiness::NotReady;
                           auto& listener = s.bufferData->releaseBufferListener;
                           if (listener &&
                            (flushState.queueProcessTime - transaction.postTime) >
                                    std::chrono::nanoseconds(4s).count()) {
                            mTransactionHandler
                                    .onTransactionQueueStalled(transaction.id,
                                                               {.pid = layer->ownerPid.val(),
                                                                .layerId = layer->id,
                                                                .layerName = layer->name,
                                                                .bufferId = s.bufferData->getId(),
                                                                .frameNumber =
                                                                        s.bufferData->frameNumber});
                        }
                        // trace中打印fence是非signaled就是从这里打印出来的!!
                        SFTRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
                        return TraverseBuffersReturnValues::STOP_TRAVERSAL;
                    }
     }
}
``
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容