Chromium VIZ工作流

2023-11-01 18:01:02

在 Chromium 中 viz 的核心邏輯執行在 GPU 程序中,負責接收其他程序產生的 viz::CompositorFrame(簡稱 CF),然後把這些 CF 進行合成,並將合成的結果最終渲染在視窗上。

可以將這個過程拆解成以下幾個步驟來分析:

  1. viz 的初始化;
  2. viz 的架構設計;
  3. CF 的建立;
  4. CF 的合成;
  5. CF 的渲染;

viz 的初始化只是一次性的過程,而且比較枯燥,所以放到最後再介紹。先來看一下 viz::CompositorFrame(簡稱 CF) 的建立,因為它是 viz 中的核心資料結構。

1. CF 的建立

一個 CF 物件表示一個矩形顯示區域中的一幀畫面。內部儲存了 3 類資料,分別是 CompositorFrameMetadata, TransferableResoruce 和 RenderPass/DrawQuad,如下圖所示:

 

 2. CF 的後設資料(Metadata)

viz::CompositorFrameMetadata 記錄了 CF 相關的後設資料,比如畫面的縮放級別,捲動區域,參照到的 Surface 等。需要注意的是這裡面還儲存了一個 ui::LatencyInfo 物件,該物件記錄了從 Chromium 接收到使用者的互動事件到畫面更新每個階段的延時資訊,具體都有哪些時間被記錄了下來,參見 latency_info.h。通過 ui::LatencyInfo 物件可以方便的追蹤互動到渲染各階段的延時資訊。

3. CF 參照到的資源(Resource)

viz::TransferableResource 記錄了該 CF 參照到的資源,所謂的資源可以理解為一張圖片。資源有兩種存在形式,一類是儲存在記憶體中的 Software 資源,一類是儲存在 GPU 中的 Texture(這樣表達並不嚴格,但目前為止大家都可以先這樣理解)。如果沒有開啟 Chromium 的硬體加速渲染,則只能使用 Software 資源。如果開啟了硬體加速,則只能使用硬體加速的資源。

如何建立一個資源呢? 如果是 Software 資源,只需要申請一塊記憶體(如果資源需要誇程序可以使用共用記憶體),然後將資源的畫素資料放進去就可以了。就像下面這樣:

上面的程式碼直接將 SkBitmap 中的畫素資料拷貝到共用記憶體中,然後在 viz::TransferableResource 中參照該共用記憶體的地址。

如果是 Texture 資源,就要複雜一點,因為需要先初始化 GL Context。關於如何初始化 GL Context 放在後面的 viz 初始化中解釋,這裡先忽略初始化過程直接看如何建立 GL 資源。範例程式碼如下:

上面的程式碼將 SkBitmap 中的畫素資料使用 gpu::SharedImageInterface 介面放入 SharedImage 中,然後使用它返回的 Mailbox 建立 viz::TransferableResource。就像程式碼中註釋的那樣,不一定非要使用畫素資料來建立 SharedImage,也可以使用 GPU-R 和 OOP-R 相關介面來建立 SharedImage。關於 Mailbox,SharedImage 以及 GPU-R 和 OOP-R 的相關內容可以參考Chromium GPU Resource Share (Shared Image)

哪些內容會以資源的形式存在呢? 結果可能出乎你的意料,網頁中的每一個 Tile 都參照一個資源。cc 會把網頁分成很多的 Tiles,每一個都以 viz::TileDrawQuad 的形式存在,而每一個 viz::TileDrawQuad 只是簡單參照一個資源(關於 DrawQuad 見下文)。此時,這些資源就是 cc Raster 的產物。

4. CF 包含的繪製操作(RenderPass/DrawQuad)

 

viz::RenderPass 由一系列相關的 viz::DrawQuad 構成。可以對一個 RenderPass 單獨應用特效,變換,mipmap,快取,截圖(CopyOutputRequest)等。DrawQuad 有很多種型別,分別用於不同的目的:

 

  • viz::TextureDrawQuad 內部參照一個資源。
  • viz::TileDrawQuad 表示一個 Tile 塊(Tile的概念見cc),和 TextureDrawQuad 類似,內部也參照一個資源,DisplayItemList 會被 cc Raster 為 TileDrawQuad;
  • viz::PictureDrawQuad 內部直接存放 DisplayItemList,但是目前只能用於Android WebView;
  • viz::SolidColorDrawQuad 表示一個顏色塊;
  • viz::RenderPassDrawQuad 內部參照另外一個 RenderPass 的 Id;
  • viz::SurfaceDrawQuad 內部儲存一個 viz::SurfaceId,該 Surface 的內容由其他 CompositorFrameSinkClient 建立,用於 viz 的巢狀,比如 OOPIF,OffscreenCanvas等;

 

由於 viz::RenderPassDrawQuad 的存在使得 CF 中可以儲存一個 RenderPass 樹,由於 viz::SurfaceDrawQuad 的存在使得viz可以實現UI的誇程序巢狀。

 

下面是一個典型的 CF 範例(圖片來自這裡):

 5. 總結

CF 是 viz 中的核心資料結構,它代表某塊區域中UI的一幀畫面,使用 DrawQuad 來儲存 UI 要顯示的內容。它代表了 viz 執行時的資料流。

6. 參考文獻

https://keyou.github.io/blog/2020/07/29/how-viz-works/