深入解析Flutter下一代渲染引擎Impeller

来源:https://www.cnblogs.com/ClientInfra/archive/2022/08/24/16619295.html
-Advertisement-
Play Games

作者 魏國梁:位元組 Flutter Infra 工程師, Flutter Member,長期專註 Flutter 引擎技術 袁 欣:位元組 Flutter Infra 工程師, 長期關註渲染技術發展 謝昊辰:位元組 Flutter Infra 工程師,Impeller Contributor Impel ...


作者

  • 魏國梁:位元組 Flutter Infra 工程師, Flutter Member,長期專註 Flutter 引擎技術
  • 袁    欣:位元組 Flutter Infra 工程師, 長期關註渲染技術發展
  • 謝昊辰:位元組 Flutter Infra 工程師,Impeller Contributor

Impeller項目啟動背景

2022 年 6 月在 Flutter 3.0 版本中 Google 官方正式將渲染器 Impeller 從獨立倉庫中合入 Flutter Engine 主幹進行迭代,這是 2021 年 Flutter 團隊推動重新實現 Flutter 渲染後端以來,首次正式明確了 Impeller 未來代替 Skia 作為 Flutter 主渲染方案的定位。Impeller 的出現是 Flutter 團隊用以徹底解決 SkSLSkia Shading Language) 引入的 Jank 問題所做的重要嘗試。官方首次註意到 Flutter 的 Jank 問題是在 2015 年,當時推出的最重要的優化是對 Dart 代碼使用 AOT 編譯優化執行效率。在 Impeller出現之前,Flutter 對渲染性能的優化大多停留在 Skia 上層,如渲染線程優先順序的提升,在著色器編譯過久的情況下切換 CPU 繪製等策略性優化。

Jank 類型分為兩種:首次運行卡頓(Early-onset Jank)和非首次運行卡頓, Early-onset Jank 的本質是運行時著色器的編譯行為阻塞了 Flutter Raster 線程對渲染指令的提交。在 Native 應用中,開發者通常會基於 UIkit 等系統級別的 UI 框架開發應用,極少需要自定義著色器,Core Animation 等 framework 使用的著色器在 OS 啟動階段就可以完成編譯,著色器編譯產物對所有的 app 而言全局共用,所以 Native 應用極少出現著色器編譯引起的性能問題 更常見的是用戶邏輯對 UI 線程過度占用 官方為了優化 Early-onset Jank ,推出了SkSL 的 Warmup 方案,Warmup 本質是將部分性能敏感的 SkSL 生成時間前置到編譯期,仍然需要在運行時將 SkSL 轉換為 MSL 才能在 GPU 上執行。Warmup 方案需要在開發期間在真實設備上捕獲 SkSL 導出配置文件 在應用打包時通過編譯參數可以將部分 SkSL 預置在應用中。此外由於 SkSL 創建過程中捕獲了用戶設備特定的參數,不同設備 Warmup 配置文件不能相互通用,這種方案帶來的性能提升非常有限。

在 2019 年 Apple 宣佈在其生態中廢棄 OpenGL 後, Flutter 迅速完成了渲染層對 Metal 的適配。與預期不符的是, Metal 的切換使得 Early-onset Jank 的情況更加惡化,Warmup 方案的實現需要依賴 Skia 團隊對 Metal 的預編譯做支持,由於 Skia 團隊的排期問題,一度導致 Warmup 方案在 Metal 後端上不可用。與此同時社區中對 iOS 平臺 Jank 問題的反饋更加強烈,社區中一度出現屏蔽 Metal 的 Flutter Engine Build,回退到 GL 後端雖然能一定程度改善首幀性能但是在 iOS 平臺上會出現視覺效果的退化,與之相對的是,由於 Android 平臺上擁有 iOS 缺失的著色器機器碼的緩存能力, Android 平臺出現 Jank 的概率比 iOS 低很多。

除了社區中出現的通用問題外,Flutter infra 團隊也經常收到位元組內部業務方遇到的 Jank 問題的反饋,反饋較集中的有轉場動畫首次卡頓、列表滾動過程中隨機卡頓等場景:

圖片

轉場動畫觸發的著色器編譯,耗時~100ms

圖片

列表滑動過程中隨機觸發的著色器編譯,耗時~28ms

在這篇文章中,我們嘗試從 Metal 著色器編譯方案,矢量渲染器原理和 Flutter Engine 渲染層的介面設計三個維度去探究 Impeller 想要解決的問題和渲染器背後的相關技術。

Metal Shader Compilation演進

一般而言,不同的渲染後端會使用獨立的著色器語言,與 JavaScript 等常見腳本語言的執行過程類似,不同語言編寫的著色器程式為了能在 GPU 硬體上執行,需要經歷完整的 lexical analysis / syntax analysis /  Abstrat Syntax Tree (抽象語法樹,下文簡稱 AST)構建,IR 優化,binary generation 的過程。著色器的編譯處理是在廠商提供的驅動中實現,其中具體的實現對上層開發者並不可見。Mesa 是一個在 MIT 許可證下開源的三維電腦圖形庫,以開源形式實現了 OpenGL 的 api 介面。通過 Mesa 中對 GLSL 的處理可以觀察到完整的著色器處理流水線。如下圖所示,上層提供的 GLSL 源文件被 Mesa 處理為 AST 後首先會被編譯為 GLSL IR, 這是一種 High-Level  IR,經過優化後會生成另一種 Low-Level  IR :NIRNIR 結合當前 GPU 的硬體信息被處理為真正的可執行文件。不同的 IR 用來執行不同粒度的優化操作,通常底層 IR 更面向可執行文件的生成,而上層 IR 可以進行諸如 dead code elimination 等粗粒度優化。常見的高級語言(如 Swift )的編譯過程也存在 High-Level  IR (Swift IL) 到 Low-Level IR (LLVM IR)的轉換。

圖片

隨著 Vulkan 的發展, OpenGL 4.6 標準中引入了對 SPIR-V 格式的支持。SPIR-VStandard Portable Intermediate Representation)是一種標準化的 IR,統一了圖形著色器語言與並行計算(GPGPU 應用)領域。它允許不同的著色器語言轉化為標準化的中間表示,以便優化或轉化為其他高級語言,或直接傳給VulkanOpenGL 或 OpenCL 驅動執行。SPIR-V 消除了設備驅動程式中對高級語言前端編譯器的需求,大大降低了驅動程式的複雜性,使廣泛的語言和框架前端能夠在不同的硬體架構上運行。Mesa 中使用 SPIR-V 格式的著色器程式可以在編譯時直接對接到 NIR 層,縮短著色器機器碼編譯的開銷, 有助於系統渲染性能的提升。

圖片

在 Metal 應用中, 使用 Metal Shading Language(以下簡稱 MSL )編寫的著色器源碼首先被處理為 AIR (Apple IR)  格式的中間表示。如果著色器源碼是以字元形式在工程中引用,這一步會在運行時在用戶設備上進行,如果著色器被添加為工程的Target,著色器源碼會在編譯期在 Xcode 中跟隨項目構建生成 MetalLib:  一種設計用來存放 AIR 的容器格式。隨後 AIR 會在運行時,根據當前設備 GPU 的硬體信息,被 Metal Compiler Service 用 JIT 編譯為可供執行的機器碼。相比源碼形式,將著色器源碼打包為 MetalLib 有助於降低運行時生著色器機器碼的開銷。著色器機器碼的編譯會在每一次渲染管線狀態對象(P ipeline S tate O bject,下文簡稱 PSO)創建時發生,一個 PSO 持有當前渲染管線關聯的所有狀態,包含光柵化各階段的著色器機器碼,顏色混合狀態,深度信息,模版掩碼狀態,多重採樣信息等等。PSO 通常被設計為一個 imutable object(不可變對象),如果需要更改 PSO 中的狀態需要創建一個新的 PSO 拷貝。

圖片

由於 PSO 可能在應用生命周期中多次創建, 為了防止著色器的重覆編譯開銷,所有編譯過的著色器機器碼會被 Metal 緩存用來加速後續 PSO 的創建過程,這個緩存稱為 Metal Shader Cache ,完全由 Metal 內部管理,不受開發者控制。應用通常會在啟動階段一次性創建大量 PSO 對象,由於此時 Metal 中沒有任何著色器的編譯緩存,PSO 的創建會觸發所有的著色器完整執行從 AIR 到機器碼的編譯過程,整個集中編譯階段是一個 CPU 密集型操作。在游戲中通常在玩家進入新關卡前利用 Loading Screen 準備好下一場景所需的 PSO,然而常規 app 中用戶的預期是能夠即點即用,一旦著色器編譯時間超過 16 ms,用戶就會感受到明顯的卡頓和掉幀。

圖片

在 Metal 2 中, Apple 首次為開發者引入了手動控制著色器緩存的能力:Metal Binary ArchiveMetal Binary Archive 的緩存層次位於 Metal Shader Cache 之上, 這意味著 Metal Binary Archive 中的緩存在 PSO 創建時會被優先使用 運行時,開發者可以通過 Metal Pipeline Manager 手動將性能敏感的著色器函數添加至 Metal Binary Archive 對象中並序列化至磁碟中。應用再次冷啟後,此時創建相同的 PSO 即是一個輕量化操作,沒有任何著色器編譯開銷。緩存的 Binary Archive 甚至可以二次分發給相同設備的用戶,如果本地 Binary Archive 中緩存的機器碼與當前設備的硬體信息不匹配,Metal 會回落至完整的編譯流水線,確保應用的正常執行。游戲堡壘之夜「Fortnite」 在啟動階段需要創建多達 1700 個 PSO 對象,通過使用 Metal Binary Archive  來加速 PSO 創建,啟動耗時從 1m26s 優化為 3s 速度提升28倍

Metal Binary Archive 通過記憶體映射的方式供 GPU 直接訪問文件系統中的著色器緩存,因此打開 Metal Binary Archive 時會占用設備寶貴的虛擬記憶體地址空間。與緩存所有的著色器函數相比,更明智的做法是根據具體的業務場景將緩存分層,在頁面退出後及時關閉對應的緩存 釋放不必要的虛擬記憶體空間。Metal Shader Cache 的黑盒管理機制無法保證著色器在使用時不會出現二次編譯 而 Metal Binary Archive 可以確保其中的緩存的著色器函數在應用生命周期內始終可用。Metal Binary Archive 雖然允許開發者手動管理著色器緩存,卻依然需要通過在運行時搜集機器碼來構建,無法保證應用初次安裝時的使用體驗。在 2022 年 WWDC 中,Metal 3 終於彌補了這個遺留的缺陷,為開發者帶來了在離線構建 Metal Binary Archive 的能力:

圖片

構建離線 Metal Binary Archive 需要使用一種全新的配置文件 Pipeline Script,Pipeline Script 其實是 Pipeline State Descriptor 的一種 JSON 表示,其中配置了 PSO 創建所需的各種狀態信息,開發者可以直接編輯生成,也可以在運行時捕獲 PSO 獲得。給定 Pipeline Script 和 MetalLib,通過 Metal 工具鏈提供的 metal 命令即可離線構建出包含著色器機器碼的 Metal Binary ArchiveMetal Binary Archive  中的機器碼可能會包含多種 GPU 架構 由於 Metal Binary Archive 需要內置在應用中提交市場 開發者可以綜合考慮包體積的因素剔除不必要的架構支持。

圖片

通過離線構建 Metal Binary Archive,著色器編譯的開銷只存在於編譯階段,應用啟動階段 PSO 的創建開銷大大降低。Metal Binary Archive 不止可以優化應用的首屏性能, 真實的業務場景下,一些 PSO 對象會遲滯到具體頁面才會被創建,觸發新的著色器編譯流程。一旦編譯耗時過長,就會影響當前 RunLoop 下 Metal 繪製指令的提交, Metal Binary Archive 可以確保在應用的生命周期內, 核心交互路徑下的著色器緩存始終為可用狀態,將節省的 CPU 時間片用來處理與用戶交互強相關的邏輯, 大大提升應用的響應性和使用體驗。

矢量渲染基礎概念

矢量渲染泛指在平面坐標系內通過組裝幾何圖元來生成圖像信息的手段,通過定義一套完整的繪製指令,可以在不同的終端上還原出不失真的圖形, 任何前端的視窗都可以被看作一個 2D 平面的矢量渲染畫布,Chrome 與 Android 渲染系統就是基於 Google 的 2D 圖形庫 Skia 構建。對應用開發而言,矢量渲染技術也扮演重要角色,如文本 / 圖表 / 地圖 / SVG / Lottie 等都依賴矢量渲染能力來提供高品質的視覺效果。

圖片

矢量渲染的基礎單元是 Path(路徑),Path 可以包含單個或多個 Contour(輪廓),Contour在一些渲染器中也稱為 SubPathContour 由連續的 Segment(直線/高階貝塞爾曲線)組成,標準的幾何構型(圓形/矩形)均可被視為一種特殊的 Path,一些特殊的 Path 可以包含坑洞或者自交叉(如五角星⭐️),這類 Path 的處理需要一些特殊的方案。圍繞 Path 可以構造出各種複雜的圖形,著名的老虎

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1. CS架構 CS架構其實在我們身邊比比皆是,手機里的app大多都是CS架構,比如騰訊作為服務端為你提供視頻,你得下個騰訊視頻客戶端才能看它的視頻。 這裡的騰訊視頻是客服端client,騰訊也有一個自己的服務端server 這種自己開創一個客服端的方式叫做CS架構。 CS架構的好處是:可以自定義發 ...
  • 所謂的延時任務給大家舉個例子:你買了一張火車票,必須在30分鐘之內付款,否則該訂單被自動取消。訂單30分鐘不付款自動取消,這個任務就是一個延時任務。 我之前已經寫過2篇關於延時任務的文章: 《完整實現-通過DelayQueue實現延時任務》 《延時任務(二)-基於netty時間輪演算法實戰》 這兩種方 ...
  • 作者:小牛呼嚕嚕 | https://xiaoniuhululu.com 電腦內功、JAVA底層、面試相關資料等更多精彩文章在公眾號「小牛呼嚕嚕 」 引用拷貝 引用拷貝: 引用拷貝不會在堆上創建一個新的對象,只 會在棧上生成一個新的引用地址,最終指向依然是堆上的同一個對象。 //實體類 publi ...
  • 測試用例 public class Example01 { private String name; private int age; public Example01(){} public void sayHello(){ System.out.println("Hell,World!"); } ...
  • 一、前言 在我們一般的web系統中必不可少的就是許可權的配置,也有經典的RBAC許可權模型,是基於角色的許可權控制。這是目前最常被開發者使用也是相對易用、通用許可權模型。當然SpringSecurity已經實現了許可權的校驗,但是不夠靈活,我們可以自己寫一下校驗條件,從而更加的靈活! 很多開源框架中也是用的比 ...
  • 數組實戰,程式員的基本功。 實戰需求: 輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得所有奇數位於數組的前半部分,所有偶數位於數組的後半部分。 實戰思路: 1、先聲明兩個數組,分別用於存儲奇數和偶數, 2、然後遍歷待排序的數組,根據是否可以被 2 整除,將數據分發到偶數和奇數數組, 3 ...
  • 大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是IAR下調試信息輸出機制之半主機(Semihosting)。 在嵌入式世界里,輸出列印信息是一種非常常用的輔助調試手段,藉助列印信息,我們可以比較容易地定位和分析程式問題。在嵌入式應用設計里實現列印信息輸出的方式有很多,本系列將以 IA ...
  • Android逆向之脫殼 脫殼一般指去除加固包。 已知脫殼有三種手段: Xposed:例反射大師 VM:例blackdex Frida 每個手段都有不同的用法。 一般步驟 去除簽名驗證(大部分加殼都有驗證,推薦用np的modex3.0,推薦選精簡包) 脫殼 反射大師:需要xp框架。點擊反射大師,選擇 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...