【摘要】 学习chromium对合成层的处理

示例代码托管在:http://www.github.com/dashnowords/blogs

博客园地址:《大史住在大前端》原创博文目录

附件PPT来自chromium官方网站开发文档。术语里的cc指的是Chromium Compositor

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

摘要

1.合成流水线

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

大致的流程就是说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是什么意思

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

4. 分层的优势和劣势

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

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页非常清楚地展示了合成的过程,最终输出的结果就是根节点的纹理。

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

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

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

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

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

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

Texture Upload

One challenge with all these textures is that we rasterize them on the main thread of the renderer process, but need to actually get them into the GPU memory. This requires handing information about these textures (and their contents) to the impl thread, then to the GPU process, and once there, into the GL/D3D driver. Done naively, this causes us to copy a single texture over and over again, something we definitely don't want to do.

We have two tricks that we use right now to make this a bit faster. To understand them, an aside on “painting” versus “rasterization.”

  • Painting is the word we use for telling webkit to dump a part of its RenderObject tree to a GraphicsContext. We can pass the painting routine a GraphicsContext implementation that executes the commands as it receives them, or we can pass it a recording context that simply writes down the commands as it receives them.

  • Rasterization is the word we use for actually executing graphics context commands. We typically execute the rasterization commands with the CPU (software rendering) but could also execute them directly with the GPU using Ganesh.

  • Upload: this is us actually taking the contents of a rasterized bitmap in main memory and sending it to the GPU as a texture.With these definitions in mind, we deal with texture upload with the following tricks:

  • Per-tile painting: we pass WebKit paint a recording context that simply records the GraphicsContext operations into an SkPicture data structure. We can then rasterize several texture tiles from that one picture.

  • SHM upload: instead of rasterizing into a void* from the renderer heap, we allocate a shared memory buffer and upload into that instead. The GPU process then issues its glTex* operations using that shared memory, avoiding one texture copy.The holy grail of texture upload is “zero copy” upload. With such a scheme, we manage to get a raw pointer inside the renderer process’ sandbox to GPU memory, which we software-rasterize directly into. We can’t yet do this anywhere, but it is something we fantasize about.

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

纹理上传:处理纹理的挑战之一就是它是在渲染进程(可以理解为单个Tab网页的进程)的主线程里进行的,但是最终需要将其放入GPU内存。这就需要将纹理数据递交给合成器线程,然后再交给GPU进程(Chromium架构里有专门的GPU进程用来专门处理和GPU之间的协作任务),最后再传递给底层的Direct3DOpenGL(也就是图形学的底层技术),如果只是按照常规流程来处理,就会需要一次又一次来复制生成的纹理数据,这显然不是我们想要的。我们现在使用了两个小方法来使这个流程变得快一点。它们分别作用于painting(绘制)和rasterization(光栅化)两个阶段。

  • 1号知识点!!!Painting我们用来告诉webkit为RenderObject Tree的来生成对应的GraphicsContext。通过给painting routine(绘制流程)传递一个GraphicsContext的具体实现来执行这些已经编排好的绘制命令,也可以传递一个record context(记录上下文)只是简单地把绘图命令都记录下来。

  • 2号知识点!!!Rasterization(光栅化)是指Graphics context关联的绘图命令实际被执行的过程。通常我们使用CPU(也就是软件渲染的方式)来执行光栅化任务,也可以直接使用GPU来渲染(也就是硬件渲染的方式)。

  • 上传:指在主线程存储区获取到光栅化以后的位图内容然后将它作为纹理上传给GPU的过程,考虑到上述已经提及的定义,上传过程是如下来处理的:

    • 瓦片绘制:我们在webkit中使用recording context来简单地记录Graphics Context的操作指令,将它存储为SkPicture类型(直接使用软件光栅化时生成的是SkBitmap类型),随后可以从一张picture里面光栅化处理得到多个纹理瓦片

    • 共享内存:在软件渲染的方式中,光栅化的结果会被存储在renderer进程的堆内存里,现在不这样搞了,我们重新分配了一块共享缓冲区,然后通过它来传递相关对象,GPU进程随后在获取纹理时直接从共享内存中获取就行了,这样就避免了数据的拷贝。总的来说,纹理上传的过程几乎是零拷贝的。利用这样的结构,我们在renderer进程(也就是网页的渲染进程)的沙箱环境内也可以获取到指向GPU 内存的指针,而在软件光栅化的过程中,是直接将位图结果放在这里的。

  • Painting: this is the process of asking Layers for their content. This is where we ask webkit to tell us what is on a layer. We might then rasterize that content into a bitmap using software, or we might do something fancier. Painting is a main thread operation.

  • Drawing: this is the process of taking the layer tree and smashing it together with OpenGL onto the screen. Drawing is an impl-thread operation.

  • painting:表示的过程是向Layers对象查询层内容,也就是让webkit告诉我们每一层上面到底有什么。接下来我们就可以使用软件光栅化的方式将这些内容处理为位图,也可以做一些更牛的事情,painting是一个主线程行为。

  • drawing:是指将Layer中的内容用OpenGL绘制在屏幕上的过程,它是另一个线程中的操作。

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

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

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

所以【硬件渲染】就是渲染进程把要做的事情和需要的数据都写好,然后打包递给GPU让它去干活。

完整的PPT可以在附件或开头的github仓库中获取。

Chromium OS - Compositor Pipeline.rar

md原文.rar

作者:华为云云享专家大史不说话

高性能Web动画和渲染原理系列(4)“Compositor-Pipeline演讲PPT”学习摘要【华为云技术分享】相关推荐

  1. 高性能Web动画和渲染原理系列(3)——transform和opacity为什么高性能

    [摘要] 研究Web高性能动画及原理 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目录 关于opa ...

  2. 高性能Web动画和渲染原理系列(2)——渲染管线和CPU渲染

    [摘要] 介绍浏览器渲染原理及CPU渲染流程 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目录 一 ...

  3. 高性能Web动画和渲染原理系列(5)合成层的生成条件和陷阱

    [摘要] 关于合成层的知识和动画编写的建议 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文目录 一. ...

  4. 【华为云技术分享】前端工程师必备:从浏览器的渲染到性能优化

    摘要:本文主要讲谈及浏览器的渲染原理.流程以及相关的性能问题. 问题前瞻 1. 为什么css需要放在头部?2. js为什么要放在body后面?3. 图片的加载和渲染会阻塞页面DOM构建吗?4. dom ...

  5. 【华为云技术分享】全WEB化开发体验,开发者新利器华为云CloudIDE即将揭秘

    随着公有云的不断普及,无论是企业办公.社交网络,还是线上娱乐都越来越依赖云上提供的各种服务,微服务.DevOps.持续交付.容器化等云原生技术和理念也在企业上云大背景下不断对现有的开发活动以及开发工具 ...

  6. 【华为云技术分享】KubeFlow-Pipeline及Argo实现原理速析

    [摘要] KubeFlow的Pipeline流程引擎,Argo Workflow流程引擎,2者实现原理分析,及2者间的关系.为你云原生流程引擎商用选型提供参考. Argo Workflow流程引擎,可 ...

  7. 【华为云技术分享】玩转华为物联网IoTDA服务系列三-自动售货机销售分析场景示例

    [摘要] 通过收集自动售货机系统的销售数据,EI数据分析售货销量状况. 场景简介 通过收集自动售货机系统的销售数据,EI数据分析售货销量状况. 该场景主要描述的是设备可以通过MQTT协议与物联网平台进 ...

  8. 【华为云技术分享】MongoDB经典故障系列五:sharding集群执行sh.stopBalancer()命令被卡住怎么办?

    [摘要] MongoDB sharding集群执行sh.stopBalancer()命令时被卡住怎么办?别慌,华为云数据库来给您支招,收下这份方案指南,让您分分钟远离被自建MongoDB数据库支配的恐 ...

  9. 【华为云技术分享】MongoDB经典故障系列四:调整oplog大小,引起从库宕机怎么办?

    一不小心调整了自建MongoDB数据库的oplog大小,从而引起从库宕机怎么办?别急,华为云数据库给您支招:一是取消延迟配置,先扩容延时从库的oplog大小,再扩容主库的oplog:二是对主库先降级再 ...

最新文章

  1. 《Mysql数据库及应用》_MySQL数据库及应用
  2. 克莱姆V(克莱姆相关系数、克莱姆关联系数、独立系数)
  3. CentOS7 安装 Gitlab
  4. oracle数据库转sqlite,Sqlite导入Oracle软件(SqliteToOracle)
  5. 汉化:Termius for Mac(SSH客户端)
  6. 基于FaceNet人脸识别的人脸对比认证(一) -- 环境部署、认证实验
  7. 远程控制电脑的实现方法
  8. php 精准定位到街道,ip地址查询精确到街道_ip查询详细地址带地图
  9. 大数据思维的十大原理:当样本数量足够大时,你会发现其实每个人都是一模一样的
  10. 超级详细的IDEA设置Java类和方法的注释模板
  11. 利用Python进行博客图片压缩
  12. 5GC 网元AMF、SMF、AUSF、UPF、PCF、UDM、NRF、NSSF、NEF介绍
  13. 解决阿里巴巴JSONObject工具 com.alibaba.fastjson.JSONObject cannot be cast to 的问题
  14. android 代码 lut,如何将颜色LUT应用于位图图像以获取android中的滤镜效果?
  15. 图像处理之混合空间增强——(Java:拉普拉斯锐化、Sobel边缘检测、均值滤波、伽马变换)
  16. 使用搜狐云景部署Ghost博客
  17. python和cpa哪个好_为什么CPA的价值下降了?谈谈我的看法
  18. GAN网络的模型坍塌和不稳定的分析
  19. elementui 合并单元格
  20. 小小滑块可笑可笑-安卓滑块验证码通杀方案研究(3)

热门文章

  1. 在c语言中利用链表常见问题,C语言,链表中遇到棘手有关问题
  2. python作业不会做怎么办_不学点编程,将来怎么给孩子辅导作业——Python新手入门教程...
  3. darknet框架_【通知】有三AI发布150页深度学习开源框架指导手册与GitHub项目,欢迎加入我们的开源团队...
  4. linux iphone 同步时间,与iPad/iPhone同步
  5. web安全之XSS基础-常见编码科普
  6. cisco路由器 三层交换机简单环境配置实例(图)
  7. 博客迁移到github
  8. oracle 写declare例子
  9. Application Request Routing (ARR) TIME OUT 502
  10. leetcode 1185 python