瀏覽器之硬件加速機制

微信公衆號:愛寫bugger的阿拉斯加
如有問題或建議,請後臺留言,我會盡力解決你的問題。

前言

此文章是我最近在看的【WebKit 技術內幕】一書的一些理解和做的筆記。

而【WebKit 技術內幕】是基於 WebKit 的 Chromium 項目的講解。

書接上文 瀏覽器內核之渲染基礎

1. 硬件加速基礎

1.1 概念

硬件加速技術是指:使用 GPU 的硬件能力爲幫助渲染網頁,在爲 GPU 的作用主要是用來繪製 3D 圖形並且性能特別好。

對於 GPU 繪圖而言,當網頁分層之後,部分區域的更新可能只在網頁的一層或者幾層,而不需要將整個網頁都重新繪製。 通過重新繪製網頁的一個或者幾個層,並將它們和其他之前繪製完的層合成起來,既能使用 GPU 的能力,又能減少重繪的開銷。

每個 RenderLayer 對象都有一個後端存儲與其對應,好處是:當每一層更新的時候,WebKit 只需要更新 RenderLayer 對象包含的節點即可。所以當某一層有作保更新的時候,WebKit 重繪該層的所在內容, 這是理想的情況。現實中,由於硬件能力和資源有限。爲了節省 GPU 的內存資源,硬件加速機制在 RenderLayer 樹建立之後需要做三件事情來完成網頁的渲染。
- WebKit 決定將哪些 RenderLayer 對象組合在一起,形成一個有後端存儲的新層,這一新層不久後會用於之後的合成(Compositing),這裏面稱之爲合成層(Compositing Layer)。每個新層都有一個或者多個後端存儲,這裏的後端存儲可能是 GPU 的內存。對於一個 RenderLayer 對象,如果它沒有後端存儲的新層,那麼就使用它的父親所使用的合成層。
- 將每個合成層包含的這些 RenderLayer 內容繪製在合成層的後端存儲中,這裏的繪製可以是軟件繪製也可以是硬件繪製。
- 由合成層(Compositor)將多個合成層合成起來,形成網頁的最終可視化結果,實際就是一張圖片。 合成器是一種能夠將多個合成層按照這些層的前後順序、合成層的 3D 變形等設置而合成一個圖像結果的設施。

在 WebKit 中,只有把編譯的 C 代碼宏(macro)“ACCELERATED_COMPOSITING” 打開之後,硬件加速機制纔會被開啓,有關硬件加速的基礎設施纔會被編譯進去。

1.2 WebKit 硬件加速設施

一個 RenderLayer 對象如果需要後端存儲,它會創建一個 RenderLayerBacking 對象,該對象負責 RenderLayer 對象所需要的各種存儲。理想情況下,每個 RenderLayer 都可以創建自己的後端存儲,但事實上不是所有 RenderLayer 都有自己的 RenderLayerBacking 對象。如果一個 RenderLayer 對象被 WebKit 按照一定的規則創建了後端存儲,那麼該 RenderLayer 被稱爲合成層。

每個合成層都有一個 RenderLayerBacking, RenderLayerBacking 負責管理 RenderLayer 所需要的所有後端存儲,因爲後端存儲可能需要多個存儲空間。在 WebKit 中,存儲空間使用 GraphicsLayer 類來表示。

image.png

其中圖中的 GraphicsLayer 表示 RenderLayer 中前景層、背景層所需要的一個後端存儲。每一個 GraphicsLayer 都使用一個 GraphicsLayerClient 對象,該對象能夠收到 GraphicsLayer 的一些狀態更新信息,並且包含一個繪製該 GraphicsLayer 對象的方法,RenderLayerBacking 繼承於該類。GraphicsLayer 是 WebKit 中的基礎類,主要定義一套標準接口。

如果一個 RenderLayer 對象具有以下特徵之一,那麼它就是合成層。

  • RenderLayer 具有 CSS 3D 屬性或者 CSS 透視效果。
  • RenderLayer 包含的 RenderObject 節點表示的是使用硬件加速的視頻解碼技術的 HTML5 “video” 元素。
  • RenderLayer 包含的 RenderObject 節點表示的是使用硬件加速的 Canvas 2D 元素或者 WebGL 技術。
  • RenderLayer 使用了 CSS 透明效果的動畫或者 CSS 變換的動畫。
  • RenderLayer 使用了硬件加速的 CSS Filters 技術。
  • RenderLayer 使用了剪裁(Clip)或者反射(Reflection)屬性,並且它的後代中包括一個合成層。
  • RenderLayer 有一個 Z 座標比自己小的兄弟節點,且該節點是一個合成層。

這麼做的原因有三個:
首先當然是合併一些 RenderLayer 層,這樣可以減少內存的使用量;
其二是在合併之後,儘量減少合併帶來的重繪性能和處理上的困難;
其三對於那些使用單獨層能夠顯著提升性能的 RenderLayer 對象,可以繼續使用這些好處。

image.png

爲什麼一個 RenderLayerBacking 對象需要這麼多層?原因有很多,例如,WebKit 需要將滾動條獨立開來稱爲一個層,需要兩個容器層來表示 RenderLayer 對應的 Z座標爲正數的子女和 Z 座標爲負數的子女,需要滾動的內容建立新層,還可能需要剪裁層和反射層。

圖 8-4 中的樹狀結構描述了所有層的繪製順序,按照先根順序遍歷的結果即是繪製順序,圖中每個層就是一個 GraphicsLayer 對象。

image.png

管理這些合成層等工作的是 RenderLayerCompositor 類,可以說是個 “大管家”。

1.3 硬件渲染過程

首先,WebKit 決定哪些些是合成層併爲它們分配後端存儲。

其次,WebKit 需要遍歷和繪製每一個合成層,也就是每個合成層可能有一個或者多個 RenderLayer 對象。

最後,渲染引擎將所有繪製完的合成層合成起來,這個是由 WebKit 的移植來完成的。

1.4 3D 圖形上下文

WebKit 中的 3D 圖形上下文主要是提供一組抽象接口,這組接口能夠提供類似 OpenGLES(使用 GPU 硬件能力的 3D 圖形應用編程接口)的功能,其主要目的當然是使用 OpenGL 繪製 3D 圖形的能力。這一層抽象能夠將 WebKit 各個移植的不同部分隱藏起來,WebCore 只是使用統一的抽象接口。在 WebKit 中,3D 圖形上下文的主要用途是 WebGL,當然啓用硬件加速的 Canvas2D 等 HTML5 技術也會使用 3D 圖形技術,不過情況有些不同。

1.2 Chromium 的硬件加速機制

1.2.1 GraphicsLayer 的支持

GraphicsLayer 對象是對一個渲染後端存儲中某一層的抽象,同衆多其他 WebKit 所定義的抽象類一樣,在 WebKit 移植中,它還需要具體的實現類來支持該類所要提供的功能。

1.2.2 框架

在 Chromium 中,所以使用 GPu 硬件加速(也就是調用 OpenGL編程接口)的操作都是由一個進程(稱爲 GPU 進程)負責來完成的,這其中包括使用 GPU 硬件來進行繪圖和合成。

Chromium 是多進程架構,每個網頁的 Renderer 進程都是將之前介紹的 3D 繪圖和合成操作通過 IPC 傳遞給 GPU 進程,由它來統一調度並執行。

在 Chromium 的 Android 版本中,GPU 進程並不存在, Chromium 是將 GPU 的所有工作放在 Browser 進程中的一個線程來完成,這得益於結構設計的靈活性。但是本質上,GPU 進程和 GPU 線程並無太大區別。

image.png

上圖 描述了 Chromium 的多進程架構中 GPU 進程同其他進程之間的聯繫,事實上每個 Renderer 進程都依賴 GPU 進程來渲染網頁,當然 Browser 進程也會同 GPU 進程進行通信,其作用是創建該進程並提供網頁渲染過程最後繪製的目標存儲。

那麼 GPU 進程和 Render 進程是如何同步這些命令的呢?答案是,GPU 進程處理一些命令後,會向 Renderer 進程報告自己當前的狀態, Renderer 進程通過檢查狀態信息和自己的期望結果來確定是否滿足自己的條件。

GPU 進程最終繪製的結果不再像軟件渲染那樣通過共享內存傳遞給 Browser 進程,而是直接將頁面的內容繪製在瀏覽器的標籤窗口內。

1.2.3 命令緩衝區

命令緩衝區(Command Buffer)主要用於 GPU 進程和 GPU 的調用都進程傳遞 GL 操作命令。從接口上來講,這一設計只提供一些基本的接口來管理緩衝區,寬並沒有對緩衝區的具體方式和命令的類型進行任何限制,不過目前 Chromium 只有 GLES 一種實現方式。

1.2.4 Chromium 合成器(Chromium Compositor)

合成器的作用就是將多個合成層合成並輸出一個最終的結果,所以它的輸入是多個待合成的合成層,每個層都有一些屬性(如 3D 變形等),它的輸出就是一個後端存儲,例如一個 GPU 的紋理緩衝區。

Chromium 合成器是一個獨立並且複雜的模塊,它的作用是合成網頁劃分後的合成層。

總結

  • 硬件加速是指用GPU的硬件能力來渲染網頁,GPU的主要作用是用來繪製3D圖形並且有很好的性能,對於GPU繪圖而言,通常不像軟件渲染那樣只是計算其中更新的區域,一旦有新的更新請求,如果沒有分層,引擎可能會重新繪製所有的區域,因爲計算更新部分對GPU來說可能耗費更多的時間,當網頁分層之後,部分區域的更新可能只在一層或幾層,而不需要更新整個網頁,通過重新繪製網頁的一個或幾個層,並將它們和其他之前繪製完的層合成起來,既能使用GPU的能力,又能夠減少重繪的開銷。

最後

希望本文對你有點幫助。

下期分享 【第九章 JavaScript 引擎】 敬請期待。

送上 資源分享——Python、Java、Linux、Go、vue、react、javaScript

對 全棧開發 有興趣的朋友可以掃下方二維碼關注我的公衆號 —— 愛寫bugger的阿拉斯加

分享 web 開發相關的技術文章,熱點資源,全棧程序員的成長之路。

大家一起交流成長。

愛寫bugger的阿拉斯加

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章