浏览器结构模型
Chromium主要有Browser,Render,GPU等进程,还有插件进程没在下图表示。由于Chromium工程设计的灵活性,提供多种选择,所以这几个可以部分为独立进程,也可以不设置为独立进程。当然这种灵活性本身也增加了不少工程量。
Chroimium 跨进程模型1、 Browser进程
页面显示,tab页面管理,用户交互,下载管理等。
2、Render进程
渲染进程,根据不同情况可以为一个或者多个,
- Processpersiteinstance:每一个页面都创建一个独立的Render进程,不管这些页面是否来自于同一域。
- Processpersite:属于同一个域的页面共享同一个进程,而不同属一个域的页面则分属不同的进程。
- Processpertab:Chromium每个标签页都是独立进程
- Singleprocess:该类型的含义是,Chromium不单独为页面创建任何独立进程,所有渲染工作都在Browser进程中进行,它们是Browser进程中的多个线程。
3、GPU进程
4、NPAPI插件进程
>>>不同平台之间的跨平台问题
Chromium由Google开源维护,代码规模异常庞大,跨多个平台,包括Mac 系统,Linux系统,Windows系统,iOS系统(其中有部分和mac cocoa共用)以及Google自己的Android系统。Chomium维护了多个平台和UI的一致性,这也是导致他异常复杂的原因之一了。
我们可以参考一下Chromium跨平台的一些做法,这些做法让代码结构易于维护和保持一致性。
一、就是利用后缀命名的方式:
Mac文件用_mac ,其中 涉及UI(cocoa)部分用_cocoa。 Mac Cocoa: chrome/browser/ui/cocoa
Linux文件用_linux后缀,其中GTK专有的部分用_gtk, 其他专有部分用_x。Linux GTK: chrome/browser/ui/gtk
Windows系统用 _win后缀。
iOS用_ios后缀,不过iOS部分使用_mac。
各平台(Mac iOS Linux)用_posix 后缀share文件。
浏览器前端代码分别由各自的平台自己的目录下面。
二、 #ifdef大法
ref C++语言。 待续C++部分
三、impl大法
ref C++语言。头文件h文件中,可以用#ifdef区别不太的平台,如果比较大的实现部分可以用implenataion的方式来分离。
在base目录下就有不少的例子, 其中waitable_event.h 中,基于不同平台的情况定义通用API。其中给不同的平台定义了不同的实现,当然,如果需要定义一些通用的东西还是需要增加waitable_event.cc的实现的。
另外,单独的几乎没有共用部分的代码可以使用xx_xx_win这样的方式来命名,其中的类Xx_Xx_Win。
浏览器的启动过程代码
浏览器启动部分代码目录:
启动入口目录.png
Windows
在Windows系统上,构建一个dll 来启动WinMain 入口。。。。
Mac
在Mac上打包成一个Framework以及一个可执行文件,这二者可链接在一起。通过main函数启动ChromeMain来启动。 另外还有一个启动入口位于/src/chrome/app_shim/chrome_main_app_mode_mac.mm。这个入口同样调用ChromeMain函数
Linux
由于沙盒的因素,可以启动一个子进程,确保子进程本身不会再进main函数。 最后也同样是调用ChromeMain函数。
渲染模型
Chromium及其庞大复杂,其中webkit/blink 渲染页面主要包括如下几个步骤:
- 1、解析成DOM
---- 每一个HTML节点都可以对应到DOM树种的一个节点Node。 其中顶层根节点应该就是Document节点。
- 将DOM转成RenderObject树
----- DOM树种的每个可视节点都对应到Render树的每个节点,并且通过GraphicsContext绘制。GraphicsContext可以wrap一些skia opengl等,关于 GraphicsContext待续。
- 转成RenderLayer
---- 每一个RenderObject节点都可以通过祖先节点直接或者间接的对应到RenderLayer节点。有共同坐标系空间的RenderObject都在一个RenderLayer。
符合一些条件可以为部分的RenderObeject创建一个RenderLayer, 定义 RenderBoxModelObject::requiresLayer()
DOM树的Document节点对应的RenderView节点。DOM树中的Document的子女节点,也就是HTML节点对应的RenderBlock节点。显式的指定CSS位置的RenderObject节点。有透明效果的RenderObject节点。节点有溢出(Overflow)、alpha或者反射等效果的RenderObject节点。使用Canvas2D和3D(WebGL)技术的RenderObject节点。Video节点对应的RenderObject节点。
* 页面的根节点
* 显示的有CSS指定的位置属性(相对 绝对 or转换)。
* 透明节点
* 有溢出 alpha或者反射效果的节点
* 有一个CSS过滤器
* 可以有3D (WebGL) 或者加速2D Context 对应到<canvas> 节点
* 对应到 <video> 节点
所以RenderObject和RenderLayer不是一一对应的关系。并且RenderLayer本身也是树形结构。
- 从 RenderLayers 到 GraphicsLayers**
为了利用Compositor,部分RenderLayer有自己的BackingSurface。所有的RenderLayer都有都使用自己的graphicLayer 或者自己最近祖先的GraphicLayer。 这点和RenderObject 和 RenderLayer关系类似。
每个GraphicsLayer都有一个GraphicsContext供关联的RenderLayers绘制。 合成器最终负责在随后的合成过程中将GraphicsContexts的位图输出一起合并到最终的屏幕图像中。
从理论上讲,每个单独的RenderLayer都可以将其自身绘制到单独的支持表面上,但实际上在内存方面(尤其是VRAM)这可能非常浪费。 在当前的Blink实现中,必须满足以下条件之一才能使RenderLayer获得其自己的合成层(/third_party/blink/renderer/platform/graphics/compositing_reasons.h):
* <video>元素使用加速视频解码来使用图层
* 图层由具有3D上下文或加速2D上下文的<canvas>元素使用
* 图层用于复合插件
* 图层使用CSS动画以提高其不透明度,或使用动画的Webkit转换
* 图层使用加速的CSS过滤器
* 层具有作为合成层的后代
* 图层的z索引较低的同级对象具有复合层(换句话说,该层与复合层重叠,应在其顶部进行渲染)
使用GraphicLayer合成消耗内存和其他资源方面会比较昂贵。
- 从GraphicsLayers 到 WebLayers 再到 CC Layers
在我们开始使用Chrome的合成器实现之前,只需要再进行几层抽象即可! GraphicsLayers可以通过一个或多个Web * Layers表示其内容。 这些是WebKit端口需要实现的接口。 有关诸如WebContentsLayer.h或WebScrollbarLayer.h之类的界面,请参见Blink的public / platform目录。 Chrome的实现位于src / blink /renderer/compositor_bindings中,使用Chrome合成器层类型实现Web*Layer抽象接口。
以上各部分转换图的大致关系如图:
DOM Tree 、 RenderObject Tree 、 LayerTree 、Graphic Layer对应关系
//> * 将绘制纹理 栅格化给GPU process
Render渲染页面涉及到的流程以及重要类示意图:
render process的主要内容硬件加速 加速合成模型
Chromium合成器设计用于管理GraphicsLayer树结构以及其frame生命周期的协调关系的软件lib。目录位于src/cc 目录。
render过程分两步:
1 首先是paint。
2 然后是合成。
compsitor在合成之前会对每个层做必要的转换(CSS指定其转换属性)。所以,由于paint和compositor分离,任意一层的无效仅仅需要重绘该层即可。
- 特别需要注意区分的概念
- draw指的是一个compositor,其可以最终将合成层绘制到屏幕上。
- paint 其实是layer的支持,在CPU做软件光栅化的位图(bitmaps with software rasterization),GPU则是硬件光栅化的纹理(textures in hardware rasterization)。
回顾一下软件绘制的模型
Renderer进程(通过IPC和共享内存share memory)将有页面内容的bitmap传递到Browser进程进行显示。
在此绘制方法中,通过从后到前顺序绘制所有RenderLayers来呈现页面。从根开始递归遍历RenderLayer,大部分工作在RenderLayer::paintLayer中执行,有以下基本步骤:
- 确定该层是否与损伤rect相交以尽早取出。
- 通过调用negZOrderList中的图层的paintLayer()来递归地绘制此图层之下的图层。
- 要求与此RenderLayer关联的RenderObject进行绘制。
- 这是通过从创建该图层的RenderObject开始递归RenderObject树来完成的。只要找到与其他 RenderLayer关联的RenderObject,遍历就会停止。
- 通过调用posZOrderList中的图层的paintLayer来递归地绘制该图层之上的图层。
- 在这种模式下,RenderObjects通过向单个共享GraphicsContext中发出绘画调用,将自己绘制到目标位图中。备注 这里GraphicsContext 由skia实现。
一旦所有RenderLayers完成paint到shared bitmap上,该bitmap仍需要将其绘制到屏幕上。在Chrome中,bitmap驻留在share memory中,并通过IPC将其控制权传递给browser process。然后,browser process负责通过操作系统的window API绘制该bitmap。(例如,使用Windows上的相关HWND)
GPU加速渲染的情况
在硬件加速架构中,合成是通过调用特定于平台的3D API(Windows上为D3D;其他任何地方为GL)在GPU上进行的。 渲染器的合成器实质上是使用GPU将页面的矩形区域(即根据图层树的转换层次结构相对于视口定位的所有那些合成层)绘制为单个位图,即最终页面图像。
由于多进程架构沙箱的存在,处于安全因素的考虑,render进程无法访问调用系统的3D绘制功能API(GL / D3D)的调用。故,GPU渲染架构中,额外增加了一个GPU进程。这种方式通过client-server方式来协调工作。
Client端(render进程or Native Cient)将指令序列化发给共享内存share memory的环形缓冲区ring buffer中(ring buff的数据结构待续),Server端(也就是GPU进程,此进程可以访问系统3D绘制API)从ring buffer取出指令来最终绘制。
Client端向Shared Memory中的command (ring buffer)发送命令,Server取出命令,二者可以异步完成绘制的协调工作。另外,较大的资源数据(比如纹理等等)通过Shared Memory同步数据。
另外,也可以通过mailbox的方式来在不同的命令buffer中沟通以及管理好他们的生命周期。
Sync points也可以通过mailbox来完成command buffer之间的非阻塞同步。
GPU 多通道模型,以及其他细节问题待续。
其他问题
IPC的mojo文件,基础数据结构 异步的问题,渲染opengl skia问题
事件体系 网络优化 安全性 以及IDL编译打包的问题等等。
网友评论