高效能Web動畫和渲染原理系列(4)“Compositor-Pipeline演講PPT”學習摘要
目錄
- 摘要
- 1.合成流水線
- 2. 預定義UI層
- 3. paint是什麼意思
- 4. 分層的優勢和劣勢
- 5. 檢視屬性及其處理方式
- 6. Quads
- 7. Compositor Frame
- 8. 關於光柵化以及渲染方式
- 9.【重要】軟體渲染和硬體渲染的區別
示例程式碼託管在:http://www.github.com/dashnowords/blogs
部落格園地址:《大史住在大前端》原創博文目錄
華為雲社群地址:【你要的前端打怪升級指南】
附件PPT來自chromium官方網站開發文件。術語裡的
cc
指的是Chromium Compositor
一直以來都想了解瀏覽器合成層的運作機制,但是相關的中文資料大多比較關注框架和開發技術,這方面的資料實在是太少了,後來在chromium
官方網站的文件裡找到了專案組成員malaykeshav在 2019年4月的一份關於瀏覽器合成流水線的演講PPT,個人感覺裡面講的非常清楚了,由於沒有找到視訊,有些部分只能自行理解,本文僅對關鍵資訊做一些筆記,對此感興趣的讀者可以在文章開頭的github
倉庫或附件中拿到這個PPT自行學習。
摘要
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中也可以看到頁面瓦片化拆分的結果:
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 Process
GPU程序進行聚合並生成最終完整的合成表面,接著在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之間的協作任務),最後再傳遞給底層的Direct3D
或OpenGL
(也就是圖形學的底層技術),如果只是按照常規流程來處理,就會需要一次又一次來複制生成的紋理資料,這顯然不是我們想要的。
我們現在使用了兩個小方法來使這個流程變得快一點。它們分別作用於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讓它去幹活。