Chromium渲染框架总结

渲染优化

chromium为了优化渲染效率做了很多优化,这些不仅可以用在web渲染,也可以用于一些native图像界面的渲染;这些优化包括:

  1. 分成paint和compositing

  2. paint分为recording和光栅化

  3. 跨线程渲染,避免和网络请求影响帧率,同时paint和光栅化可以并行,充分利用了CPU多核特性

  4. Tiling

  5. 跨进程渲染,提高稳定性

  6. 先渲染0.5比例大小的内容,减少texture上传的大小加快渲染速度,同时离屏幕渲染正常比例的内容,提高滑动和缩放体验

  7. 特点策略决定cpu/gpu渲染

  8. delegated renderer/direct renderer,将渲染放到最后,减少中间texture

 

为什么要用硬件渲染(GPU)?

  1. gpu设计用来处理大量图形处理,效率比cpu更快,可能还更省电

  2. 有些图像比如视频,一些3d图像本身就在gpu内存,ui框架使用硬件渲染可以避免这些gpu内存写回到主存

  3. cpu和gpu可以并行处理,比如cpu可以做光珊化的同时,gpu完成绘制到屏幕,形成一个流水线,效率更快

 

线程化渲染

impl线程和main线程(webkit线程)

main线程负责网页渲染和执行JS,包括css样式计算,会同步等待主文档的网络请求,依赖于网页内容复杂程度,可能比较耗时,chromium通过impl/main线程机制,把网页的绘制和上面的操作分离,实现动画,滑动的流畅性,实现方式是:在main线程保存了一颗Layer树,Layer是一个有内容和大小的绘图元数据,main线程会把Layer绘制到一个SkPicture上去,没有做实际上的光栅化操作,有些内容比如canvas需要保留绘制状态的就会光栅化到一个texture;impl线程会通过一个commit操作,把main线程的这颗Layer树同步到impl线程,这里是同步操作,但是耗时比较小,同时把layer相关的texture上传到gpu,impl线程就可以直接把内容绘制到屏幕;

commit的步骤

main线程发现网页有变动,异步通知impl我需要一次commit,impl线程收到通知后,根据当前的状态,比如下一次vsync是否已经到了,告诉main线程可以开始beginframe和commit了,beginframe就是把网页内容paint到layer,这里一般是记录绘制命令到SkPicture,有部分需要光栅化比如Canvas,commit是main通知impl来做的,main线程阻塞直到impl线程完成两个Layer树的同步;commit还负责把impl线程用户滑动的offset同步给main线程的layer树

上述两个线程需要一个调度器和一个状态机管理;

在多进程架构中,impl/main线程都运行在Render进程

 

Compositing

什么是Compositing,网页内容分成很多层,分层的好处一个是避免重复渲染,另一个就是利用gpu加速compositing;

在chromium里面渲染分成两个步骤,paint和compositing,paint就是把绘制命令记录到SkPicutre然后光栅化成像素数据,compositing就是把这些数据叠在一起显示到屏幕;

相比于绘制,gpu的compositing是很轻量的操作;

chromium compositor的作用就是做compositing操作,还有管理每一帧的生命周期

 

部分刷新

PicturePile

 

Tiling

一般一个Layer并不会对应一个texture,这样会导致Layer有部分更新时,整个texture都要重新光栅化,chromium使用tiling技术,把一个layer分成一些方块,按照方块为单位进行光栅化和分配texture;像一些video/webgl因为内容已经在gpu内存并且一般都会全部刷新的不需要分块

 

光栅化

分为软件光栅化和gpu光栅化:

  1. 软件光栅化,通过skia画到共享内存的bitmap中,再上传到gpu texture,需要单独开一个raster线程,因为光栅化比较耗时;对于频繁刷洗的页面效率比较低

光栅化时需要的图片也需要单独线程上传;

 

  1. 硬件光栅化,通过skia的ganesh,直接通过gl命令画到texture;有点是节约了内存,对于频繁更新的页面渲染效率更快,但是不适合渲染文字,skia绘制文字的算法不是和gpu的并行计算架构;

gpu光栅化有个优化,就是可以直接光栅化到buffer上,而不是texture,节省了一个texture的内存,缺点是因为没有中间存储的texture,每一帧都要全部重绘(flutter就是这么干的)

  1. 如何选择光栅化方法, 一般是文字比较多用cpu,变动频繁用gpu,代码中skia有一个api控制:suitableForGpuRasterization

  2.  

 

DelegatedRenderer

Render端的impl线程不会将内容渲染到texture,而是把绘制信息(Compositor Frame)传递给Browser端,Browser端有自己的UI和Layer树结构,Render传递过来的绘制信息会作为其中一个Layer和BrowserUI一起渲染,这样减少了一个中间texture,提高效率

 

一帧的生命周期

Layer Tree创建 => Output Surface创建 => 网页绘制 => Layer Tree与Pending Layer Tree同步 => Tile光栅化 => Pending Layer Tree激活为Active Layer Tree

Layer树

Dom->RenderObject->RenderLayer->GraphicsLayer->Layer Tree->PendingLayerTree->ActiveLayerTree

GraphicsLayer->Layer Tree

Layer Tree与WebKit中的Graphics Layer Tree在结构上是完全同步的,并且这个同步过程是由WebKit控制的

Layer Tree->PendingLayerTree

没当每当WebKit修改了Render Object时,都会请求执行一次commit操作,scheduler会判断上一次commit由没由执行,没有的化就忽略这一次,避免commit操作请求得太过频繁,commit的任务就是同步树结构;同步完成后就要对PendingLayer分块,再对分块光栅化

PendingLayerTree->ActiveLayerTree

PendingLayerTree光栅化完成后和ActivieLayerTree交换

 

降级

  1. 设备不支持cpu,使用软件渲染

  2. 线程化渲染效率更低?使用单线程渲染,impl/main合并为一个线程,去掉recording,把内容直接绘制到缓冲中

 

GPU进程

impl线程光栅化和绘制到屏幕,使用的是标准的GLES命令,他是通过宏定义的方式把GL命令替换成了和GPU进程间的通信,通过commandbuffer机制把gl命令发给GPU命令发送给GPU进程执行;

commandbuffer是一块共享内存,存储gl命令和参数,以及关联的bitmap,顶点数组;commandbuffer有一个配套的mailbox和syncpoint机制用来共享和同步不同commandbuffer之间的texture使用

你可能感兴趣的:(平台)