一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情。
VSync信号
vsync是有两个信号的,
一个是vsync-app用于生成当前帧的数据;(CPU计算和GPU渲染)
一个用于消费数据(合成图像到Display上,vsync-surface) 。
一,vsync信号来源
vsync可以由底层HardWare提供经由Display发送,当底层Hardware不能提供时也会发送vsync信号到Display。vsync屏蔽了底层Hal,使得没有Vsync的硬件也可以使用。
二,发送流程
HardWare到达Display之后,Display会将vsync信号分成两个一个用于生成一个用于消费的vsync信号。
一个是vsync-app唤醒Chrographer做App的绘制操作(生成当前帧数据)
一个是vsync-sf是SurfaceFliger使用,当vsync信号来临时进行合成操作(要满足消费完上一帧数据的条件下)
三,偏移量
vsync每隔16ms发送一个。vsync会分成两个信号发送。这就意味着只要这两个信号在16ms之内处理完数据就可以。 也就是说我们可以打乱顺序是先合成消费帧数据绘制到屏幕上还是先生成帧数据。
比如先发送vsync-app在0-13ms做完处理,接着13-16ms在发送vsync-surface合成数据 或者颠倒,但是事件一定保证只要在16ms之内处理完这两个信号即可
四,整个处理过程:
1.vsync-app:UI Thread准备好绘制指令,提交给Render Thread渲染线程去调用OpenGl的函数(Android5.1之前渲染操作比如Opengl函数的调用也是在UI线程去做的)去生成buffer并放到BufferQuene中
2.vsync-surface:SurfaceFliger进程去BufferQuene中去取出buffer合成图像显示到屏幕Display中。
五,vsync-app 解释
唤醒Chorgrapher去做处理生成当前这一帧的数据。 注意:有两个线程共同合作完成绘制动作:UIThread生成指令和RenderThread调用OpenGl库生成Buffer放入到BufferQuene缓冲队列中。 UIThread:Choreographer.doFrame()RenderThread:DrawFrame
首先来讲UIThread的Choreographer.doFrame方法:
1.按顺序发送INPUT,ANIMATION,TRASVEL并处理他们各自的doFrame方法 先处理输入事件在处理动画,最后的TRASVEL会进行调用到ViewRootImpl中的doTrasvel回调,这个回调里面会进行measure,layout和draw。
这里讲下draw方法,进行performDraw方法调用时会调用全局Surface(也就是activity)的lockCanvas方法。这个方法会在native层的Surface对象中锁定一块内存区域返回值为canvas也就是这片在native层的Surface内存空间中。接下来调用draw方法把这个canvas传入到参数中,也就是我们在draw方法中对canvas进行的修改实质上都是对这块内存区域的修改。这块内存区域其实就是缓冲队列bufferQuene;Surface对应一块画布canvas,其内部有多个缓冲区,行成一个BufferQuene缓冲队列,通过缓冲队列作为载体。
发送给SurfaceFlinger的数据是通过WindowManager将当前所有Window一起发送(Window其实就是Surface),SurfaceFlinger获取到Layer(就是Surface)后进行合成
draw方法其实并没有进行真正的绘制,而是把绘制的操作放入到了DisplayList中进一步封装为了RenderNode节点。
绘制最终会调用到View.invalidate方法
2.RenderThread执行的时候UIThread就可以释放掉去做其他处理,接着RenerThread去取出DisplayList中的数据进行处理生成RenderNode。 具体流程: RenderThread会执行一个DrawFrameTask的Task,里面核心方法是DrawFrame。RenderThread中RenderThread通过dequeue()
拿到graphic buffer(surfaceFlinger的缓冲区),根据绘制指令直接操作OpenGL的绘制接口,最终通过GPU设备把绘制指令渲染到了缓冲区graphic buffer。完成渲染后,把缓冲区交还给SurfaceFlinger的BufferQueue。SurfaceFlinger会通过硬件设备进行layer的合成,最终展示到屏幕。
六,vsync-sf:
App端中RenderThread产生的FrameBuffer数据会在SurfaceFliger中进行消费。也就是取出阻塞队列中的渲染数据。SurfaceFliger接收到WindowManager提供的所有Surface进行合成Surface
大致流程
APP通过WindowManager统一提供所有Surface的缓冲区【不管是SurfaceView还是普通的布局流程都会将数据提交到Surface的BufferQuene中】
Java中的Surface是null,最终都是由Native层的Surface处理。Native中的Surface持有的一个接口用于和bufferQuene交互,渲染到Surface上,其实是渲染到了BufferQuene中的GraphicBuffer,通过接口将GraphicBuffer提交到BufferQuene中
通过对canvas的操作系统进程接收到BufferQuene之后SurfaceFlinger统一合成多个Surface也就是Layer。通过OpenGl生成图层放到缓冲区frameBuffer中,hwcomposer取出frameBuffer进行合成图层显示到屏幕
推荐阅读:
Android 渲染系列-App整个渲染流程全解析