<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                [TOC] # 背景知識 ### CPU 與 GPU(軟件繪制和硬件繪制) 除了屏幕,UI 渲染還依賴兩個核心的硬件:CPU 與 GPU。UI 組件在繪制到屏幕之前,都需要經過 Rasterization(柵格化)操作,而柵格化操作又是一個非常耗時的操作。GPU(Graphic Processing Unit )也就是圖形處理器,它主要用于處理圖形運算,可以幫助我們加快柵格化操作。 ![](https://img.kancloud.cn/9c/56/9c568ad703ad730c22c5c06539bedfa2_1920x1167.png) 你可以從圖上看到,軟件繪制使用的是 Skia 庫,它是一款能在低端設備如手機上呈現高質量的 2D 跨平臺圖形框架,類似 Chrome、Flutter 內部使用的都是 Skia 庫。 ### OpenGL 對于硬件繪制,我們通過調用 OpenGL ES 接口利用 GPU 完成繪制。[OpenGL](https://developer.android.com/guide/topics/graphics/opengl)是一個跨平臺的圖形 API,它為 2D/3D 圖形處理硬件指定了標準軟件接口。而 OpenGL ES 是 OpenGL 的子集,專為嵌入式設備設計。 在官方[硬件加速](https://developer.android.com/guide/topics/graphics/hardware-accel)的文檔中,可以看到很多 API 都有相應的 Android API level 限制。 ![](https://img.kancloud.cn/4e/ce/4ece76ea6e9f8c934a936891b16d4c00_1726x1066.png) 這是為什么呢?其實這主要是受[OpenGL ES](https://www.khronos.org/opengles/)版本與系統支持的限制,直到最新的 Android P,有 3 個 API 是仍然沒有支持。對于不支持的 API,我們需要使用軟件繪制模式,渲染的性能將會大大降低。 ![ui_1_4](https://blog.yorek.xyz/assets/images/android/master/ui_1_4.png) Android 7.0 把 OpenGL ES 升級到最新的 3.2 版本同時,還添加了對[Vulkan](https://source.android.com/devices/graphics/arch-vulkan)的支持。 ### Vulkan Vulkan 是用于高性能 3D 圖形的低開銷、跨平臺 API。相比 OpenGL ES,Vulkan 在改善功耗、多核優化提升繪圖調用上有著非常明顯的[優勢](https://zhuanlan.zhihu.com/p/20712354)。 ## Android 渲染的演進 ### 圖形系統的 ![](https://img.kancloud.cn/6d/46/6d4614bcf315b5f746ff7a0ebe3f5b08_1168x1052.png) 我曾經在一篇文章看過一個生動的比喻,如果把應用程序圖形渲染過程當作一次繪畫過程,那么繪畫過程中 Android 的各個圖形組件的作用是: * 畫筆:Skia 或者 OpenGL。我們可以用 Skia 畫筆繪制 2D 圖形,也可以用 OpenGL 來繪制 2D/3D 圖形。正如前面所說,前者使用 CPU 繪制,后者使用 GPU 繪制。 * 畫紙:Surface。所有的元素都在 Surface 這張畫紙上進行繪制和渲染。在 Android 中,Window 是 View 的容器,每個窗口都會關聯一個 Surface。而 WindowManager 則負責管理這些窗口,并且把它們的數據傳遞給 SurfaceFlinger。 * 畫板:Graphic Buffer。Graphic Buffer 緩沖用于應用程序圖形的繪制,在 Android 4.1 之前使用的是雙緩沖機制;在 Android 4.1 之后,使用的是三緩沖機制。 * 顯示:SurfaceFlinger。它將 WindowManager 提供的所有 Surface,通過硬件合成器 Hardware Composer 合成并輸出到顯示屏。 接下來我將通過 Android 渲染演進分析的方法,幫你進一步加深對 Android 渲染的理解。 ### 1\. Android 4.0:開啟硬件加速[?](https://blog.yorek.xyz/android/paid/master/ui_1/#1-android-40 "Permanent link") 在 Android 3.0 之前,或者沒有啟用硬件加速時,系統都會使用軟件方式來渲染 UI。 ![ui_1_5](https://blog.yorek.xyz/assets/images/android/master/ui_1_6.png) 整個流程如上圖所示: * Surface。每個 View 都由某一個窗口管理,而每一個窗口都關聯有一個 Surface。 * Canvas。通過 Surface 的 lock 函數獲得一個 Canvas,Canvas 可以簡單理解為 Skia 底層接口的封裝。 * Graphic Buffer。SurfaceFlinger 會幫我們托管一個[BufferQueue](https://source.android.com/devices/graphics/arch-bq-gralloc),我們從 BufferQueue 中拿到 Graphic Buffer,然后通過 Canvas 以及 Skia 將繪制內容柵格化到上面。 * SurfaceFlinger。通過 Swap Buffer 把 Front Graphic Buffer 的內容交給 SurfaceFinger,最后硬件合成器 Hardware Composer 合成并輸出到顯示屏。 整個渲染流程是不是非常簡單?但是正如我前面所說,CPU 對于圖形處理并不是那么高效,這個過程完全沒有利用到 GPU 的高性能。 #### 硬件加速繪制[?](https://blog.yorek.xyz/android/paid/master/ui_1/#_1 "Permanent link") 所以從 Androd 3.0 開始,Android 開始支持硬件加速,到 Android 4.0 時,默認開啟硬件加速。 ![ui_1_7](https://blog.yorek.xyz/assets/images/android/master/ui_1_7.png) 硬件加速繪制與軟件繪制整個流程差異非常大,最核心就是我們通過 GPU 完成 Graphic Buffer 的內容繪制。此外硬件繪制還引入了一個 DisplayList 的概念,每個 View 內部都有一個 DisplayList,當某個 View 需要重繪時,將它標記為 Dirty。 當需要重繪時,僅僅只需要重繪一個 View 的 DisplayList,而不是像軟件繪制那樣需要向上遞歸。這樣可以大大減少繪圖的操作數量,因而提高了渲染效率。 ![ui_1_8](https://blog.yorek.xyz/assets/images/android/master/ui_1_8.png) ### 2\. Android 4.1:Project Butter[?](https://blog.yorek.xyz/android/paid/master/ui_1/#2-android-41project-butter "Permanent link") 優化是無止境的,Google 在 2012 年的 I/O 大會上宣布了 Project Butter 黃油計劃,并且在 Android 4.1 中正式開啟了這個機制。 Project Butter 主要包含兩個組成部分,一個是 VSYNC,一個是 Triple Buffering。 #### VSYNC 信號[?](https://blog.yorek.xyz/android/paid/master/ui_1/#vsync "Permanent link") 在講文件 I/O 跟網絡 I/O 的時候,我講到過中斷的概念。對于 Android 4.0,CPU 可能會因為在忙別的事情,導致沒來得及處理 UI 繪制。 為解決這個問題,Project Buffer 引入了[VSYNC](https://source.android.com/devices/graphics/implement-vsync),它類似于時鐘中斷。每收到 VSYNC 中斷,CPU 會立即準備 Buffer 數據,由于大部分顯示設備刷新頻率都是 60Hz(一秒刷新 60 次),也就是說一幀數據的準備工作都要在 16ms 內完成。 ![ui_1_9](https://blog.yorek.xyz/assets/images/android/master/ui_1_9.png) 這樣應用總是在 VSYNC 邊界上開始繪制,而 SurfaceFlinger 總是 VSYNC 邊界上進行合成。這樣可以消除卡頓,并提升圖形的視覺表現。 #### 三緩沖機制 Triple Buffering[?](https://blog.yorek.xyz/android/paid/master/ui_1/#triple-buffering "Permanent link") 在 Android 4.1 之前,Android 使用雙緩沖機制。怎么理解呢?一般來說,不同的 View 或者 Activity 它們都會共用一個 Window,也就是共用同一個 Surface。 而每個 Surface 都會有一個 BufferQueue 緩存隊列,但是這個隊列會由 SurfaceFlinger 管理,通過匿名共享內存機制與 App 應用層交互。 ![ui_1_10](https://blog.yorek.xyz/assets/images/android/master/ui_1_10.png) 整個流程如下: * 每個 Surface 對應的 BufferQueue 內部都有兩個 Graphic Buffer ,一個用于繪制一個用于顯示。我們會把內容先繪制到離屏緩沖區(OffScreen Buffer),在需要顯示時,才把離屏緩沖區的內容通過 Swap Buffer 復制到 Front Graphic Buffer 中。 * 這樣 SurfaceFlinge 就拿到了某個 Surface 最終要顯示的內容,但是同一時間我們可能會有多個 Surface。這里面可能是不同應用的 Surface,也可能是同一個應用里面類似 SurefaceView 和 TextureView,它們都會有自己單獨的 Surface。 * 這個時候 SurfaceFlinger 把所有 Surface 要顯示的內容統一交給 Hareware Composer,它會根據位置、Z-Order 順序等信息合成為最終屏幕需要顯示的內容,而這個內容會交給系統的幀緩沖區 Frame Buffer 來顯示(Frame Buffer 是非常底層的,可以理解為屏幕顯示的抽象)。 如果你理解了雙緩沖機制的原理,那就非常容易理解什么是三緩沖區了。如果只有兩個 Graphic Buffer 緩存區 A 和 B,如果 CPU/GPU 繪制過程較長,超過了一個 VSYNC 信號周期,因為緩沖區 B 中的數據還沒有準備完成,所以只能繼續展示 A 緩沖區的內容,這樣緩沖區 A 和 B 都分別被顯示設備和 GPU 占用,CPU 無法準備下一幀的數據。 ![ui_1_11](https://blog.yorek.xyz/assets/images/android/master/ui_1_11.png) 如果再提供一個緩沖區,CPU、GPU 和顯示設備都能使用各自的緩沖區工作,互不影響。簡單來說,三緩沖機制就是在雙緩沖機制基礎上增加了一個 Graphic Buffer 緩沖區,這樣可以最大限度的利用空閑時間,帶來的壞處是多使用的了一個 Graphic Buffer 所占用的內存。 ![ui_1_12](https://blog.yorek.xyz/assets/images/android/master/ui_1_12.png) 對于 VSYNC 信號和 Triple Buffering 更詳細的介紹,可以參考[《Android Project Butter 分析》](https://blog.csdn.net/innost/article/details/8272867)。 #### 數據測量[?](https://blog.yorek.xyz/android/paid/master/ui_1/#_2 "Permanent link") “工欲善其事,必先利其器”,Project Butter 在優化 UI 渲染性能的同時,也希望可以幫助我們更好地排查 UI 相關的問題。 在 Android 4.1,新增了 Systrace 性能數據采樣和分析工具。在卡頓和啟動優化中,我們已經使用過 Systrace 很多次了,也可以用它來檢測每一幀的渲染情況。 Tracer for OpenGL ES 也是 Android 4.1 新增加的工具,它可逐幀、逐函數的記錄 App 用 OpenGL ES 的繪制過程。它提供了每個 OpenGL 函數調用的消耗時間,所以很多時候用來做性能分析。但因為其強大的記錄功能,在分析渲染問題時,當 Traceview、Systrace 都顯得棘手時,還找不到渲染問題所在時,此時這個工具就會派上用場了。 在 Android 4.2,系統增加了檢測繪制過度工具,具體的使用方法可以參考[《檢查 GPU 渲染速度和繪制過度》](https://developer.android.com/studio/profile/inspect-gpu-rendering)。 ![ui_1_13](https://blog.yorek.xyz/assets/images/android/master/ui_1_13.png) ### 3\. Android 5.0:RenderThread[?](https://blog.yorek.xyz/android/paid/master/ui_1/#3-android-50renderthread "Permanent link") 經過 Project Butter 黃油計劃之后,Android 的渲染性能有了很大的改善。但是不知道你有沒有注意到一個問題,雖然我們利用了 GPU 的圖形高性能運算,但是從計算 DisplayList,到通過 GPU 繪制到 Frame Buffer,整個計算和繪制都在 UI 主線程中完成。 ![ui_1_7](https://blog.yorek.xyz/assets/images/android/master/ui_1_7.png) UI 主線程“既當爹又當媽”,任務過于繁重。如果整個渲染過程比較耗時,可能造成無法響應用戶的操作,進而出現卡頓。GPU 對圖形的繪制渲染能力更勝一籌,如果使用 GPU 并在不同線程繪制渲染圖形,那么整個流程會更加順暢。 正因如此,在 Android 5.0 引入了兩個比較大的改變。一個是引入了 RenderNode 的概念,它對 DisplayList 及一些 View 顯示屬性做了進一步封裝。另一個是引入了 RenderThread,所有的 GL 命令執行都放到這個線程上,渲染線程在 RenderNode 中存有渲染幀的所有信息,可以做一些屬性動畫,這樣即便主線程有耗時操作的時候也可以保證動畫流暢。 在官方文檔[《檢查 GPU 渲染速度和繪制過度》](https://developer.android.com/studio/profile/inspect-gpu-rendering)中,我們還可以開啟 Profile GPU Rendering 檢查。在 Android 6.0 之后,會輸出下面的計算和繪制每個階段的耗時: ![ui_1_14](https://blog.yorek.xyz/assets/images/android/master/ui_1_14.png) 如果我們把上面的步驟轉化線程模型,可以得到下面的流水線模型。CPU 將數據同步(sync)給 GPU 之后,一般不會阻塞等待 GPU 渲染完畢,而是通知結束后就返回。而 RenderThread 承擔了比較多的繪制工作,分擔了主線程很多壓力,提高了 UI 線程的響應速度。 ![ui_1_15](https://blog.yorek.xyz/assets/images/android/master/ui_1_15.png) # 參考資料 [# 20 | UI 優化(上):UI 渲染的幾個關鍵概念](https://blog.yorek.xyz/android/paid/master/ui_1/)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看