瀏覽器結(jié)構(gòu)模型
Chromium主要有Browser,Render税迷,GPU等進(jìn)程,還有插件進(jìn)程沒(méi)在下圖表示锹漱。由于Chromium工程設(shè)計(jì)的靈活性箭养,提供多種選擇,所以這幾個(gè)可以部分為獨(dú)立進(jìn)程凌蔬,也可以不設(shè)置為獨(dú)立進(jìn)程露懒。當(dāng)然這種靈活性本身也增加了不少工程量。
1砂心、 Browser進(jìn)程
頁(yè)面顯示懈词,tab頁(yè)面管理,用戶交互辩诞,下載管理等坎弯。
2、Render進(jìn)程
渲染進(jìn)程,根據(jù)不同情況可以為一個(gè)或者多個(gè)抠忘,
- Processpersiteinstance:每一個(gè)頁(yè)面都創(chuàng)建一個(gè)獨(dú)立的Render進(jìn)程撩炊,不管這些頁(yè)面是否來(lái)自于同一域。
- Processpersite:屬于同一個(gè)域的頁(yè)面共享同一個(gè)進(jìn)程崎脉,而不同屬一個(gè)域的頁(yè)面則分屬不同的進(jìn)程拧咳。
- Processpertab:Chromium每個(gè)標(biāo)簽頁(yè)都是獨(dú)立進(jìn)程
- Singleprocess:該類型的含義是,Chromium不單獨(dú)為頁(yè)面創(chuàng)建任何獨(dú)立進(jìn)程囚灼,所有渲染工作都在Browser進(jìn)程中進(jìn)行骆膝,它們是Browser進(jìn)程中的多個(gè)線程。
3灶体、GPU進(jìn)程
4阅签、NPAPI插件進(jìn)程
>>>不同平臺(tái)之間的跨平臺(tái)問(wèn)題
Chromium由Google開(kāi)源維護(hù),代碼規(guī)模異常龐大蝎抽,跨多個(gè)平臺(tái)政钟,包括Mac 系統(tǒng),Linux系統(tǒng)樟结,Windows系統(tǒng)养交,iOS系統(tǒng)(其中有部分和mac cocoa共用)以及Google自己的Android系統(tǒng)。Chomium維護(hù)了多個(gè)平臺(tái)和UI的一致性狭吼,這也是導(dǎo)致他異常復(fù)雜的原因之一了层坠。
我們可以參考一下Chromium跨平臺(tái)的一些做法,這些做法讓代碼結(jié)構(gòu)易于維護(hù)和保持一致性刁笙。
一破花、就是利用后綴命名的方式:
Mac文件用_mac ,其中 涉及UI(cocoa)部分用_cocoa疲吸。 Mac Cocoa: chrome/browser/ui/cocoa
Linux文件用_linux后綴座每,其中GTK專有的部分用_gtk, 其他專有部分用_x摘悴。Linux GTK: chrome/browser/ui/gtk
Windows系統(tǒng)用 _win后綴峭梳。
iOS用_ios后綴,不過(guò)iOS部分使用_mac蹂喻。
各平臺(tái)(Mac iOS Linux)用_posix 后綴share文件葱椭。
瀏覽器前端代碼分別由各自的平臺(tái)自己的目錄下面。
二口四、 #ifdef大法
ref C++語(yǔ)言孵运。 待續(xù)C++部分
三、impl大法
ref C++語(yǔ)言蔓彩。頭文件h文件中治笨,可以用#ifdef區(qū)別不太的平臺(tái)驳概,如果比較大的實(shí)現(xiàn)部分可以用implenataion的方式來(lái)分離。
在base目錄下就有不少的例子旷赖, 其中waitable_event.h 中顺又,基于不同平臺(tái)的情況定義通用API。其中給不同的平臺(tái)定義了不同的實(shí)現(xiàn)等孵,當(dāng)然稚照,如果需要定義一些通用的東西還是需要增加waitable_event.cc的實(shí)現(xiàn)的。
另外俯萌,單獨(dú)的幾乎沒(méi)有共用部分的代碼可以使用xx_xx_win這樣的方式來(lái)命名锐锣,其中的類Xx_Xx_Win。
瀏覽器的啟動(dòng)過(guò)程代碼
瀏覽器啟動(dòng)部分代碼目錄:
Windows
在Windows系統(tǒng)上绳瘟,構(gòu)建一個(gè)dll 來(lái)啟動(dòng)WinMain 入口。姿骏。糖声。。
Mac
在Mac上打包成一個(gè)Framework以及一個(gè)可執(zhí)行文件分瘦,這二者可鏈接在一起蘸泻。通過(guò)main函數(shù)啟動(dòng)ChromeMain來(lái)啟動(dòng)。 另外還有一個(gè)啟動(dòng)入口位于/src/chrome/app_shim/chrome_main_app_mode_mac.mm嘲玫。這個(gè)入口同樣調(diào)用ChromeMain函數(shù)
Linux
由于沙盒的因素悦施,可以啟動(dòng)一個(gè)子進(jìn)程,確保子進(jìn)程本身不會(huì)再進(jìn)main函數(shù)去团。 最后也同樣是調(diào)用ChromeMain函數(shù)抡诞。
渲染模型
Chromium及其龐大復(fù)雜,其中webkit/blink 渲染頁(yè)面主要包括如下幾個(gè)步驟:
- 1土陪、解析成DOM
---- 每一個(gè)HTML節(jié)點(diǎn)都可以對(duì)應(yīng)到DOM樹(shù)種的一個(gè)節(jié)點(diǎn)Node昼汗。 其中頂層根節(jié)點(diǎn)應(yīng)該就是Document節(jié)點(diǎn)。
- 將DOM轉(zhuǎn)成RenderObject樹(shù)
----- DOM樹(shù)種的每個(gè)可視節(jié)點(diǎn)都對(duì)應(yīng)到Render樹(shù)的每個(gè)節(jié)點(diǎn)鬼雀,并且通過(guò)GraphicsContext繪制顷窒。GraphicsContext可以wrap一些skia opengl等,關(guān)于 GraphicsContext待續(xù)源哩。
- 轉(zhuǎn)成RenderLayer
---- 每一個(gè)RenderObject節(jié)點(diǎn)都可以通過(guò)祖先節(jié)點(diǎn)直接或者間接的對(duì)應(yīng)到RenderLayer節(jié)點(diǎn)鞋吉。有共同坐標(biāo)系空間的RenderObject都在一個(gè)RenderLayer。
符合一些條件可以為部分的RenderObeject創(chuàng)建一個(gè)RenderLayer励烦, 定義 RenderBoxModelObject::requiresLayer()
DOM樹(shù)的Document節(jié)點(diǎn)對(duì)應(yīng)的RenderView節(jié)點(diǎn)谓着。DOM樹(shù)中的Document的子女節(jié)點(diǎn),也就是HTML節(jié)點(diǎn)對(duì)應(yīng)的RenderBlock節(jié)點(diǎn)崩侠。顯式的指定CSS位置的RenderObject節(jié)點(diǎn)漆魔。有透明效果的RenderObject節(jié)點(diǎn)坷檩。節(jié)點(diǎn)有溢出(Overflow)、alpha或者反射等效果的RenderObject節(jié)點(diǎn)改抡。使用Canvas2D和3D(WebGL)技術(shù)的RenderObject節(jié)點(diǎn)矢炼。Video節(jié)點(diǎn)對(duì)應(yīng)的RenderObject節(jié)點(diǎn)。
* 頁(yè)面的根節(jié)點(diǎn)
* 顯示的有CSS指定的位置屬性(相對(duì) 絕對(duì) or轉(zhuǎn)換)阿纤。
* 透明節(jié)點(diǎn)
* 有溢出 alpha或者反射效果的節(jié)點(diǎn)
* 有一個(gè)CSS過(guò)濾器
* 可以有3D (WebGL) 或者加速2D Context 對(duì)應(yīng)到<canvas> 節(jié)點(diǎn)
* 對(duì)應(yīng)到 <video> 節(jié)點(diǎn)
所以RenderObject和RenderLayer不是一一對(duì)應(yīng)的關(guān)系句灌。并且RenderLayer本身也是樹(shù)形結(jié)構(gòu)。
- 從 RenderLayers 到 GraphicsLayers**
為了利用Compositor欠拾,部分RenderLayer有自己的BackingSurface胰锌。所有的RenderLayer都有都使用自己的graphicLayer 或者自己最近祖先的GraphicLayer。 這點(diǎn)和RenderObject 和 RenderLayer關(guān)系類似藐窄。
每個(gè)GraphicsLayer都有一個(gè)GraphicsContext供關(guān)聯(lián)的RenderLayers繪制资昧。 合成器最終負(fù)責(zé)在隨后的合成過(guò)程中將GraphicsContexts的位圖輸出一起合并到最終的屏幕圖像中。
從理論上講荆忍,每個(gè)單獨(dú)的RenderLayer都可以將其自身繪制到單獨(dú)的支持表面上格带,但實(shí)際上在內(nèi)存方面(尤其是VRAM)這可能非常浪費(fèi)。 在當(dāng)前的Blink實(shí)現(xiàn)中刹枉,必須滿足以下條件之一才能使RenderLayer獲得其自己的合成層(/third_party/blink/renderer/platform/graphics/compositing_reasons.h):
* <video>元素使用加速視頻解碼來(lái)使用圖層
* 圖層由具有3D上下文或加速2D上下文的<canvas>元素使用
* 圖層用于復(fù)合插件
* 圖層使用CSS動(dòng)畫以提高其不透明度叽唱,或使用動(dòng)畫的Webkit轉(zhuǎn)換
* 圖層使用加速的CSS過(guò)濾器
* 層具有作為合成層的后代
* 圖層的z索引較低的同級(jí)對(duì)象具有復(fù)合層(換句話說(shuō),該層與復(fù)合層重疊微宝,應(yīng)在其頂部進(jìn)行渲染)
使用GraphicLayer合成消耗內(nèi)存和其他資源方面會(huì)比較昂貴棺亭。
- 從GraphicsLayers 到 WebLayers 再到 CC Layers
在我們開(kāi)始使用Chrome的合成器實(shí)現(xiàn)之前,只需要再進(jìn)行幾層抽象即可蟋软! GraphicsLayers可以通過(guò)一個(gè)或多個(gè)Web * Layers表示其內(nèi)容镶摘。 這些是WebKit端口需要實(shí)現(xiàn)的接口。 有關(guān)諸如WebContentsLayer.h或WebScrollbarLayer.h之類的界面钟鸵,請(qǐng)參見(jiàn)Blink的public / platform目錄钉稍。 Chrome的實(shí)現(xiàn)位于src / blink /renderer/compositor_bindings中,使用Chrome合成器層類型實(shí)現(xiàn)Web*Layer抽象接口棺耍。
以上各部分轉(zhuǎn)換圖的大致關(guān)系如圖:
//> * 將繪制紋理 柵格化給GPU process
Render渲染頁(yè)面涉及到的流程以及重要類示意圖:
硬件加速 加速合成模型
Chromium合成器設(shè)計(jì)用于管理GraphicsLayer樹(shù)結(jié)構(gòu)以及其frame生命周期的協(xié)調(diào)關(guān)系的軟件lib俊卤。目錄位于src/cc 目錄。
render過(guò)程分兩步:
1 首先是paint害幅。
2 然后是合成消恍。
compsitor在合成之前會(huì)對(duì)每個(gè)層做必要的轉(zhuǎn)換(CSS指定其轉(zhuǎn)換屬性)。所以以现,由于paint和compositor分離狠怨,任意一層的無(wú)效僅僅需要重繪該層即可约啊。
- 特別需要注意區(qū)分的概念
- draw指的是一個(gè)compositor,其可以最終將合成層繪制到屏幕上佣赖。
- paint 其實(shí)是layer的支持恰矩,在CPU做軟件光柵化的位圖(bitmaps with software rasterization),GPU則是硬件光柵化的紋理(textures in hardware rasterization)憎蛤。
回顧一下軟件繪制的模型
Renderer進(jìn)程(通過(guò)IPC和共享內(nèi)存share memory)將有頁(yè)面內(nèi)容的bitmap傳遞到Browser進(jìn)程進(jìn)行顯示外傅。
在此繪制方法中,通過(guò)從后到前順序繪制所有RenderLayers來(lái)呈現(xiàn)頁(yè)面俩檬。從根開(kāi)始遞歸遍歷RenderLayer萎胰,大部分工作在RenderLayer::paintLayer中執(zhí)行,有以下基本步驟:
- 確定該層是否與損傷rect相交以盡早取出棚辽。
- 通過(guò)調(diào)用negZOrderList中的圖層的paintLayer()來(lái)遞歸地繪制此圖層之下的圖層技竟。
- 要求與此RenderLayer關(guān)聯(lián)的RenderObject進(jìn)行繪制。
- 這是通過(guò)從創(chuàng)建該圖層的RenderObject開(kāi)始遞歸RenderObject樹(shù)來(lái)完成的屈藐。只要找到與其他 RenderLayer關(guān)聯(lián)的RenderObject灵奖,遍歷就會(huì)停止。
- 通過(guò)調(diào)用posZOrderList中的圖層的paintLayer來(lái)遞歸地繪制該圖層之上的圖層估盘。
- 在這種模式下,RenderObjects通過(guò)向單個(gè)共享GraphicsContext中發(fā)出繪畫調(diào)用骡尽,將自己繪制到目標(biāo)位圖中遣妥。備注 這里GraphicsContext 由skia實(shí)現(xiàn)。
一旦所有RenderLayers完成paint到shared bitmap上攀细,該bitmap仍需要將其繪制到屏幕上箫踩。在Chrome中,bitmap駐留在share memory中谭贪,并通過(guò)IPC將其控制權(quán)傳遞給browser process境钟。然后,browser process負(fù)責(zé)通過(guò)操作系統(tǒng)的window API繪制該bitmap俭识。(例如慨削,使用Windows上的相關(guān)HWND)
GPU加速渲染的情況
在硬件加速架構(gòu)中,合成是通過(guò)調(diào)用特定于平臺(tái)的3D API(Windows上為D3D套媚;其他任何地方為GL)在GPU上進(jìn)行的缚态。 渲染器的合成器實(shí)質(zhì)上是使用GPU將頁(yè)面的矩形區(qū)域(即根據(jù)圖層樹(shù)的轉(zhuǎn)換層次結(jié)構(gòu)相對(duì)于視口定位的所有那些合成層)繪制為單個(gè)位圖,即最終頁(yè)面圖像堤瘤。
由于多進(jìn)程架構(gòu)沙箱的存在玫芦,處于安全因素的考慮,render進(jìn)程無(wú)法訪問(wèn)調(diào)用系統(tǒng)的3D繪制功能API(GL / D3D)的調(diào)用本辐。故桥帆,GPU渲染架構(gòu)中医增,額外增加了一個(gè)GPU進(jìn)程。這種方式通過(guò)client-server方式來(lái)協(xié)調(diào)工作老虫。
Client端(render進(jìn)程or Native Cient)將指令序列化發(fā)給共享內(nèi)存share memory的環(huán)形緩沖區(qū)ring buffer中(ring buff的數(shù)據(jù)結(jié)構(gòu)待續(xù))熄驼,Server端(也就是GPU進(jìn)程,此進(jìn)程可以訪問(wèn)系統(tǒng)3D繪制API)從ring buffer取出指令來(lái)最終繪制寻狂。
Client端向Shared Memory中的command (ring buffer)發(fā)送命令蠕嫁,Server取出命令,二者可以異步完成繪制的協(xié)調(diào)工作菊卷。另外缔恳,較大的資源數(shù)據(jù)(比如紋理等等)通過(guò)Shared Memory同步數(shù)據(jù)。
另外洁闰,也可以通過(guò)mailbox的方式來(lái)在不同的命令buffer中溝通以及管理好他們的生命周期歉甚。
Sync points也可以通過(guò)mailbox來(lái)完成command buffer之間的非阻塞同步。
GPU 多通道模型扑眉,以及其他細(xì)節(jié)問(wèn)題待續(xù)纸泄。
其他問(wèn)題
IPC的mojo文件,基礎(chǔ)數(shù)據(jù)結(jié)構(gòu) 異步的問(wèn)題腰素,渲染opengl skia問(wèn)題
事件體系 網(wǎng)絡(luò)優(yōu)化 安全性 以及IDL編譯打包的問(wèn)題等等聘裁。