Vsync
垂直同步,扫描一帧的同步时间,如屏幕的刷新率为60Hz,那么周期为1/60s,也就是16.66ms一次的时间间隔;
在了解安卓的显示流程的时候,网上一般都会贴个图说是通过VSYNC驱动下的绘制,合成,显示的流程线方式;
实际上由于app和sf都存在着存在一个相位偏移,所以并不是安全按照Vsync的时序来完成这些操作的;
那什么是VSYNC呢?为了有最基本的认识,让我们从底层代码开始讲起,借助VSYNC我们也会大致了解和体会到显示相关的代码框架;
1 | ./drivers/video/fbdev/sunxi/disp2/disp/dev_disp.c |
如果我们使用ftrace抓取workqueue的使用情况的话,就能看到有如下的打印
1 | $ echo workqueue:workqueue_queue_work > /sys/kernel/debug/tracing/set_event |
对于底层的disp,除了提供VSYNC信号,另外,他还提供了disp_ioctl这套系统调用接口,太多的话就不做阐述,如果后面有哪个接口比较重要再做描述;
HWC
代码目录在hareware/aw/hwc2中
hwc提供了安卓标准的hwc的相关接口实现(hwc2_function_pointer_t);安卓通过HIDL调用到hwc的相关function;然后再通过这些接口实现了对kernel/driver的调用;在hwc中有两个主要的线程,负责监听底层的事件,还有送显;
1 | threadResouce/hwc_event_thread.cpp |
HIDL
那么HIDL怎么玩的?
如上所示就是一个HIDL的玩法;以vsync的callback注册流程为例;
SF_BE:SurfaceFlinger采用的是前后端设计,与HWC相关的逻辑都会放到SurfaceFlingeBE中
1 | 代码路径:framwwork/nativeservices/surfaceflinger/ |
HWC2_Client:属于surfaceflinge进程,通过Binder和HWC2的HAL Server交互,命名空间是HWC2
1 | 代码路径:services/surfaceflinger/DisplayHardware/ |
HWC SERVER:
client调用到server中标准的接口
1 |
|
HWC VENDER:
提供hwc2_function_pointer_t的实现;
1 | 代码路径:hardware/aw/hwc2 |
总结:
1.安卓提供了一套标准的hwc2_function_pointer_t给厂家去实现,然后自己就可以通过HIDL接口进行调用;
2.在Vsync时间来临时,会通过回调调到SurfaceFlinger的实现函数onVsyncReceived,然后又会通过唤醒DispSync这个线程,通过postEvent传递VSYNC事件,然后MessageQueue收到这个消息后,就会调用对应的hanlder函数,最终调用到SurfaceFlinger中onMessageReceiver处理,这里就会去决策是否要进行合成了;细节后面会讲到,我们现在只需要知道有VSYNC会来的时候会调到SurfaceFlinge的回调就行;
MessageQueue
surfaceflinger启动
1 | system/core/rootdir/init.rc |
先将创建的线程给打出来,后面有涉及到的线程作用也会提及
MessageQueue创建
类介绍
1 | class SurfaceFlinger : public BnSurfaceComposer, 实现SurfaceComposer接口 |
onFirstRef
1 | flinger的数据类型为sp强指针类型,当首次被强指针引用的时候则执行onFirstRef |
简单介绍一下就是looper不断从MessageQueue中取出一个Message,然后交给其对应的Hanlder;handler通过Looper sendMessgae 到队列中,也负责处理相关的消息,处理的消息为INVALIDATE,REFRESH当Vsync来的时候会调到callback,onVsyncReceived;接下来就是消息的收发是怎么驱动起来的了~
线程创建
1 | void SurfaceFlinger::init() { |
线程流程图
对于我们而言需要知道的就是onVsyncReceive后会经过addResyncSample->updateModel 来到DispSyncThread线程的一直走到onVsyncEvent然后走到mSFEventThread的sendEvent,这个时候我们的MessageQueue就会收到消息,派发INVALID的消息然后决策是否需要刷新;
虽然说系统每秒有60个HW Vsync,但不代表APP和Vsync在每个Vsync都要更新画面,因为在Android里面,是根据software VSYN(Vsync-sf和Vsync-app)来更新画面,Software Vsync根据HwVsync过去发生的时间推测未来发生的时间,因此当APP或SF利用requestNextVsync的时候才会触发Vsync-sf或VSYNC-app;当SW Vsync和硬件Vsync误差无法接受的时候,就会重新打开硬件Vsync,来重新调节SW vsync
消息处理
INVALIDATE消息:用于处理Layer或者Display属性的变化以及Layer对应的buffer的更新
1 | 1.Layer或者Display属性的更新通过调用handleMessageTransaction()处理 |
当做完这一步后会去决策是否要进行刷新;
REFRESH消息:表示SurfaceFlinger需要进行一次合成操作(Refresh),通过handleMessageRefresh()实现;主要有三种情况:
1 | 1.Layer属性的变化导致window state发生变化 |
如果这三种情况之一发生,则置refreshNeeded为true,调用signalRresh发出MessgaeQueue:REFRESH消息
当Vsync信号来之前,Layer或者Display属性的变化会做本地保存,只有当Vsyn信号到来时,SurfaceFlinger才会通过INVALIDATE和REFRESH消息来做统一的合并渲染和输出的处理工作;
代码流程如下:
1 | void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS { |
总结
以下是一帧的大概显示流程,后续有机会在详细讲下技术细节:
事件驱动:vsync-app驱动应用层绘图完毕后会回调onFrameAvaliable到SF的layer里面更新layer状态,然后调用requestVsync把mSFEventThread唤醒(应用若长期不送帧,会进入休眠状态),在下一个Vsync信号来时会往MessageQueue(MQ)队列添加invalidata消息。MQ是SF的主线程,它主要处理invalidate和refresh消息。当MQ收到Refresh消息后,就会真正的进入合成和送显的流程;关键流程就是以上所说的rebuildLayerStacks, calculateWorkingSet, prepareFrame, doComposition, postComposition
SF进行Refresh时,通过VrModeSwitch()判断是否处于虚拟桌面,调用prepareFrame(对应HWC的de2TryToAssignLayer)询问HWC各个图层的合成方式。
接着调用drawMesh()进行重绘合成(OPENGL)和调用postFrameBuffer()提交合成的画布或这一帧。SF提交是不代表GPU已经处理完成,所以不直接送显;而是通过submitLayerToDisplay()把这一帧放入链表中,就直接返回接着处理下一帧。
HWC::DisplayOpr有一个线程,submitThreadLoop()循环通过从链表中拿出一帧数据,通过commitToDisplay()异步向display下发,下发前等GPU完成即fence释放;
其他
如何强制GPU合成:
1 | service call SurfaceFlinger 1008 i32 1 |
配置关闭硬件VSYNC
1 | 方案目录的mk文件中添加如下: |