高性能Web动画和渲染原理系列(4)“Compositor-Pipeline演讲PPT”学习摘要-LMLPHP

一直以来都想了解浏览器合成层的运作机制,但是相关的中文资料大多比较关注框架和开发技术,这方面的资料实在是太少了,后来在chromium官方网站的文档里找到了项目组成员malaykeshav在 2019年4月的一份关于浏览器合成流水线的演讲PPT,个人感觉里面讲的非常清楚了,由于没有找到视频,有些部分只能自行理解,本文仅对关键信息做一些笔记,对此感兴趣的读者可以在文章开头的github仓库或附件中拿到这个PPT自行学习。

摘要

1.合成流水线

合成流水线,就是指浏览器处理合成层的工作流程,其基本步骤如下:

高性能Web动画和渲染原理系列(4)“Compositor-Pipeline演讲PPT”学习摘要-LMLPHP

大致的流程就是说Paint环节会生成一个列表,列表里登记了页面元素的绘制指令,接着这个列表需要经过Raster光栅化处理,并在合成帧中处理纹理,最后的Draw环节才是将这些纹理图展示在浏览器内容区。

2. 预定义UI层

chromium中预定义了一些指定类型的UI层,大致分为:

  • Not Drawn - 为了处理透明度或滤镜效果、transform变形或者clip剪裁的非绘制层
  • Solid color layer - 固有颜色层
  • Painted texture layer - Texture纹理会在这个层执行paint渲染和后续的rasterized光栅化任务
  • Transferable resource layer - 共享资源层,可能是GPU里面的Texture纹理也可能未来会发给GPU的位图
  • Surface layer - 临时占位层,因为自顶向下遍历layer树时子树都还没处理,需要先占位最后再填充
  • Nine patch layer - 用于实现阴影的层

3. paint是什么意思

高性能Web动画和渲染原理系列(4)“Compositor-Pipeline演讲PPT”学习摘要-LMLPHP

每个层layer是由若干个views组成的,所谓paint,就是每个views将自己对应图形的绘制指令添加到层的可展示元素列表Display Item List里,这个列表会被添加到一个延迟执行的光栅化任务中,并最终生成当前层的texture纹理(可以理解为当前层的绘制结果),考虑到传输性能以及未来增量更新的需求,光栅化的结果会以tiles瓦片形式保存。在chrome中也可以看到页面瓦片化拆分的结果:

高性能Web动画和渲染原理系列(4)“Compositor-Pipeline演讲PPT”学习摘要-LMLPHP

4. 分层的优势和劣势

高性能Web动画和渲染原理系列(4)“Compositor-Pipeline演讲PPT”学习摘要-LMLPHP

分层的优势和劣势也在此进行了说明,和之前我们主动思考的答案基本一致(暗爽一下)。

5. 视图属性及其处理方式

views中支持的属性包含Clip剪裁,transform变换,effect效果(如半透明或滤镜等),mask遮罩,通常按照后序遍历的方式自底向上进行遍历处理。

clip剪裁的处理方式是在父节点和子节点之间插入一个剪裁层,用来将其子树的渲染结果剪裁到限定的范围内,然后再向上与父级进行合并;

transform变换直接作用于父节点,处理到这个节点时其子树都已经处理完毕,直接将整体应用变形即可;

effect效果一般直接作用于当前处理的节点,有时也会产生交叉依赖的场景;

PPT第40页中在介绍effect效果处理时描述了两种不同的透明度处理需求,从而引出了一个Render Surface的概念,它相当于一个临时的层,它的子树需要先绘制在这个层上,然后再向上与父节点进行合并,屏幕就是是根级的Render Surface

6. Quads

Layer遍历处理输出的结果被称为Quads(从意思上理解好像就是指输出了很多个矩形方块),每个quad都持有它被绘制到目标缓冲区所需要的资源,根据它持有的资源不同可以分为:

  • Solid Color-固定颜色型
  • Texture- 纹理型
  • Tile- 瓦片型
  • Surface- 临时绘图表面型
  • Video - 视频帧型
  • Render Pass - Render Surface类型的占位区,Render Surface子树处理完后填充到关联的Render Pass

7. Compositor Frame

合成层真正的工作要开始了,主角概念Compositor Frame(合成帧)登场,它负责将quads合并绘制在一起,胶片里59-62页非常清楚地展示了合成的过程,最终输出的结果就是根节点的纹理。

高性能Web动画和渲染原理系列(4)“Compositor-Pipeline演讲PPT”学习摘要-LMLPHP

chromium是多进程架构,Browser Process浏览器进程会对菜单栏等等容器部分的画面生成合成帧来输出,每个网页的Render Process渲染进程会对页面内容生成合成帧来输出,最终的结果都被共享给GPU ProcessGPU进程进行聚合并生成最终完整的合成表面,接着在Display Compositor环节将最后的位图展示在屏幕上。

8. 关于光栅化以及渲染方式

胶片里并没有描述具体的光栅化的处理过程,但是layer输出的quads看起来应该是光栅化以后的结果,推测应该是处理Display Item List中的绘图指令时也和WebGL类似,经过顶点着色器片元着色器的遍历式处理机制,并在过程中自动完成像素插值。

9.【重要】软件渲染和硬件渲染的区别

声明:本节内容是个人理解,仅用作技术交流,不保证对!

软件渲染和硬件渲染的区别对笔者而言一直非常抽象,只是知道基本概念。后来在【chromium开发者文档】(国内可能无法访问)中《Compositor Thread Architecture》这篇合成器线程架构的文章中找到了一些相关描述,也解开了笔者心中一直以来的疑惑,相关部分摘抄如下:

大概翻译一下,方便英语水平一般的小伙伴理解,GPU处理图片的方式是按照Texture进行贴图的,对此不熟悉的小伙伴可以查看笔者以前发的有关Three.js相关的博文。

概念比较多没有基础的读者可能理解起来有难度,我尝试用自己的话复述一下:

【软件渲染】的模式下,在paint时会直接利用Graphics Context绘图上下文将结果绘制出来,在一个SkBitmap实例中保存为位图信息;【硬件渲染】的模式下,在paint时传入一个SkPicture实例,将需要执行的绘图命令保存在里面先不执行,然后通过共享内存将它传给GPU进程,借助GPU来最终去执行绘图命令,生成多个瓦片化的位图纹理结果(OpenGL中顶点着色器向片元着色器传递数据时可以自动进行数据插值,完成光栅化的任务)。 纯软件渲染里严格说是没有合成层概念的,因为最终输出的只有一张位图,按照顺序从下往上画,和画到一个新层上再把新层贴到已有结果上其实是一样的。

不管使用哪种途径,paint动作都是得到位图数据,而最终的draw这个动作是借助OpenGL和位图数据最终把图形显示在显示器上。

11-15 09:12