高性能Web動畫和渲染原理系列(4)「Compositor-Pipeline演講PPT」學習摘要

示例代碼託管在:http://www.github.com/dashnowords/blogs前端

博客園地址:《大史住在大前端》原創博文目錄git

華爲雲社區地址:【你要的前端打怪升級指南】github

附件PPT來自chromium官方網站開發文檔。術語裏的cc指的是Chromium Compositorweb

一直以來都想了解瀏覽器合成層的運做機制,可是相關的中文資料大多比較關注框架和開發技術,這方面的資料實在是太少了,後來在chromium官方網站的文檔裏找到了項目組成員malaykeshav在 2019年4月的一份關於瀏覽器合成流水線的演講PPT,我的感受裏面講的很是清楚了,因爲沒有找到視頻,有些部分只能自行理解,本文僅對關鍵信息作一些筆記,對此感興趣的讀者能夠在文章開頭的github倉庫或附件中拿到這個PPT自行學習。chrome

摘要

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中也能夠看到頁面瓦片化拆分的結果:ide

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讓它去幹活。

相關文章
相關標籤/搜索