腾讯自主研发动画组件PAG开源
PAG (Portable Animated Graphics) 是一套完整的動畫工作流。它提供從AE導出插件,到桌面預覽工具,再到各端的跨平臺渲染SDK,助力于將AE動畫方便快捷的應用于各平臺終端。PAG目前是公司AVGenerator OTeam開源協同小組的核心組件之一,廣泛應用于公司內外40余款主流APP或業務,涵蓋UI動畫、視頻編輯、特效模板、服務端特效渲染等多個場景,于2022年1月開源至GitHub。
PAG(Portable Animated Graphics)是騰訊自主研發的一套完整的動畫工作流解決方案,助力于將 AE 動畫方便快捷的應用于各平臺終端。和 Lottie、SVGA 相比,支持的 AE 特性更多,支持的平臺更廣(增加了 mac OS、Windows 和 Linux),性能方面也做了深層次的優化,支持圖層編輯,可以與視頻編輯場景緊密結合。目前已經廣泛應用于公司內外幾十款 APP,包括國民級 APP 微信、QQ、騰訊視頻、QQ 音樂、QQ 空間等。
本文將會對 PAG 與 Lottie、SVGA 的工作流程、實現方案、性能等進行對比,并且將會介紹 PAG 特有的一些能力,為一些想要了解或接入 PAG 的開發同學提供一些參考。
1、 工作流程
下面將對 Lottie、SVGA、PAG 的工作流程進行對比。
1.1 Lottie 的工作流程
下面是 Lottie 的實現流程圖,設計師使用 AE 設計好動畫, 通過 bodymovin 插件將 AE 工程文件導出為 json 文件,在客戶端(使用 Lottie SDK)解析,最后通過各平臺原生渲染方案進行渲染,其中在 Android 平臺上通過 Canvas 進行繪制,在 iOS 上通過 CALayer 進行繪制,在 web 端支持 SVG、Canvas 和 HTML 繪制。
圖1 Lottie工作流程圖rLottie 與 lottie 工作流一致,在 SDK 上實現不一樣,rLottie 沒有使用平臺特定實現,是統一 C++實現。
1.2 SVGA 工作流程
SVGA 流程如圖 2 所示,大體流程與 Lottie 類似,使用 SVGAConverter 插件導出,文件是 PB 序列化以后 zip 壓縮的格式,在具體實現上通過設置幀率來生成一個配置文件,使得每一幀一個配置,每一幀都是關鍵幀,從而在繪制的過程中不用解析高階插值。SVGA 在 Lottie 方案基礎上進行了優化和完善,但是不支持復雜的矢量形狀圖層和特效。
圖2 SVGA工作流程圖1.3 PAG 的工作流程
PAG 的流程類似 Lottie,設計師使用 AE 設計好動畫以后,通過 PAGExporter 插件讀取 AE 工程文件,根據具體需求選擇矢量導出、BMP 預合成、混合導出方式中的一種導出一個 PAG 二進制文件,客戶端對該 PAG 二進制文件進行解碼、渲染,各端共享一套 C++實現,平臺端只做接口封裝。(導出插件:PAGExporter;桌面預覽工具:PAGViewer;客戶端渲染 SDK:PAG SDK)
圖3 PAG工作流程圖以上 3 個庫的工作流程大體相似,不同之處在于導出和渲染。
2、 實現方案對比
下面對 Lottie、SVGA 和 PAG 的實現方案進行對比。
2.1 誕生背景
Lottie 最早從UI動畫場景出發解決矢量動畫渲染的問題,從官方社區來看,我們能容易發現 Lottie 的矢量基因,社區作品大多是矢量圖形類動畫。SVGA 是 YY 直播的開發工程師 2017 年發布的一套跨平臺動畫解決方案,誕生于直播場景,SVGA 不支持復雜矢量圖形動畫,對位圖動畫的支持超過 Lottie,其最初的目標是為了改善和彌補Lottie。不可否認,兩者都是業界優化的動畫解決方案。PAG誕生于2016年,最初的原因是為了解決更為復雜的視頻編輯場景下動畫渲染問題,同時又完美覆蓋了UI動畫和直播場景。
一個有意思的共同點,以上三種方案的作者都有比較豐富的 Flash 相關背景,都在把Flash完善動畫工作流的實現方式帶到移動端,三者出發的場景不同,因此實現的方式也會存在一些差異。
2.2 導出插件
Lottie 和 SVGA 都使用 AE Script SDK 來導出 AE 工程,但是 AE Script SDK 本身存在一定限制,不能訪問 AE 文件中的所有屬性,PAG 則使用 AE C++ SDK,能訪問 AE 文件中所有屬性和一些高級 API,能夠實現對 AE 文件的完整導出。
2.3 SDK 實現
渲染層面
Lottie 和 SVGA 渲染層面的實現依賴平臺端接口,因此不同平臺會存在支持的 AE 特性有所差異、渲染效果不一致等問題。PAG 渲染層面使用 C++實現,所有平臺共享同一套實現,平臺端只是封裝接口調用,提供渲染環境,因此 PAG 所有平臺支持特性一致,渲染效果一致。rLottie 跟 PAG 類似,底層共享一套 C++實現,素材支持 lottie 的 json 文件,矢量渲染性能還不錯,但缺少各平臺封裝,支持的 AE 特性不全,也不支持文本、序列幀等。
渲染緩存層面
Lottie 和 SVGA 依賴平臺端的接口繪制,只能依賴平臺側接口的渲染緩存,PAG 內部有三級緩存機制,從素材結構到渲染結構都有緩存,實現了非常高性能的繪制效率。
文件格式方面
Lottie 導出素材格式是 json 文本,可讀性高,但是承載 AE 特性能力差,文件體積大,解碼速度慢。SVGA 使用 ProtoBuffer 序列化,解碼速度快,最終生成的文件直接使用 zip 壓縮。PAG 采用二進制的編碼方法,配套自研編解碼器,動態比特位壓縮,冗余信息極少,文件體積最小,解碼速度最快,且支持圖片和音頻信息編碼。
平臺端支持方面
目前 Lottie 僅支持 Android、iOS、web、mac OS,SVGA 支持 Android、iOS 和 web 端,PAG 可以支持到 Android、iOS、web、mac OS、windows、Linux,涵蓋到所有平臺。
3、 動畫文件及性能對比
3.1 矢量動畫文件對比
表1 動畫文件對比如上表所示,PAG 采用了動態比特位的壓縮技術,動畫文件可以做到足夠小。相同的 AE 工程,PAG 導出的動畫文件大小是 Lottie 動畫文件的 51%,SVGA 動畫文件的 22%。
3.2 矢量動畫渲染性能
表2 矢量動畫渲染性能對比如表所示,在矢量圖形渲染方面,PAG 優化 Lottie 和 SVGA,內存占用方面會偏大一些。
4、 PAG 版本迭代與技術演進
PAG 從第一行代碼寫下到現在已經經歷了 5 年,期間經歷了多個版本迭代:在 PAG 1.0 版本中,我們重點設計了高壓縮率的文件格式,以及游戲引擎級別的跨平臺的渲染架構。雖然還支持了帶動畫的文本編輯能力,但 1.0 版本跟 Lottie 一樣僅覆蓋了 AE 的純矢量導出能力,很多復雜動畫效果無法被完整還原。
于是在 PAG 2.0 版本中,我們引入了 BMP 預合成的混合導出能力,同時解決了 AE 全特性的支持和可編輯性的問題。2.0 版本還引入了占位圖替換的能力,為照片模板和視頻模板的生產帶來了工業化量產的能力。
到 3.0 版本時,固定時間軸的模板已經越發沒法滿足需求,PAG 在編輯性上又進行了一步探索突破,開放了圖層級別的原子編輯組合能力,支持了從原子特效組件動態構建模板,很好的支撐了游戲戰報和一鍵出片等動態模板的需求。
截止到本月,PAG 4.0 版本的開發也接近收尾。這個版本耗時了近一年時間完成了在渲染架構上最大的一次升級,徹底脫離了谷歌的 Skia 2D 繪圖庫,PAG SDK 包體也直線下降了約 60%,并完成了包括 Web 平臺在內的全平臺覆蓋。
下面詳細介紹一下各個版本迭代過程中的重點技術演進細節:
4.1 跨平臺渲染架構
PAG 方案最早就是誕生在視頻編輯的場景下,要讓動畫能夠在視頻編輯場景下無縫整合使用,需要解決兩個問題:支持離屏渲染繪制、子線程渲染。Lottie 的動畫方案之所以無法應用在視頻合成中,主要是因為依賴了平臺相關的 UI 框架,開發成本較低,但也導致了它只能渲染到 UI 視圖上,并且無法在子線程中使用。
PAG 的整套動畫方案就是基于 C++跨平臺架構研發的,一直從最底層的動畫插值器,還原到上層的時間軸和圖層渲染樹系統,雖然開發成本較高,但是所有端共享同一套代碼,天然的能保障跨端渲染一致性。最重要的是能直接渲染到離屏紋理上,并完美支持子線程動畫渲染。
圖4 PAG與視頻渲染相結合在解決完整合視頻渲染的問題后,還需要考慮怎么優化動畫的性能。視頻編輯的場景本身資源耗費比較高,每幀并行地存在多個視頻解碼以及各種特效處理,此時留給 PAG 的渲染時間就不太多。我們需要把 PAG 的渲染性能優化到極致,來滿足視頻編輯場景的實時預覽需求。
時間靜態區間
分析動畫文件的特效,我們發現大部分的動畫素材實際上并不是整個時間軸上都在變化,或多或少會存在一些畫面靜止的區間。而 PAG 在刷新時,如果遇到這些靜態區間,會直接返回上一幀的動畫內容,自動跳過任何重復的繪制。極限情況下,假設有一個一分鐘的動畫素材,但實際上全程都是靜止的,它對 PAG 來說就相當于一張靜態圖片,整個刷新的過程中都是 0 開銷。而在 Lottie 方案中,整個刷新過程都是全量的開銷,因為它每幀都會清空屏幕重新刷新。
三級緩存結構
這里的解決思路是用空間來換時間。
第一個層面是文件緩存,主要解決 PAG 文件從文件解碼到內存過程的耗時,同一個動畫文件只需要解碼一次,就可以放在多個動畫實例中渲染,避免多個相同動畫的重復解碼。
第二個層面是繪制緩存,解碼后的文件有多個時間軸屬性,我們將生成的繪制數據緩存到共享文件中,一個文件的任何一幀,只要繪制過一次,第二次繪制就可以得到加速。同時還利用了靜態區間的特點來優化內存,將每個圖層拆分成多個屬性組,每個屬性組計算出靜態區間的列表后,只緩存每個靜態區間第一幀數據。
第三個層面是內容緩存,這個層級的加速效果是最明顯的。通常情況下,圖層的內容繪制是最耗時的,因為要經歷柵格化等操作。但是內容一般不會隨著時間軸變化,反而是輕量的矩陣參數會頻繁的變化。根據這個原理,如果一個圖層內容是靜止的,我們會把他的內容緩存成一張紋理。這樣整個時間軸上,只會經歷一次柵格化的過程,后續每幀的繪制都可以復用第一幀的紋理,快速套用矩陣變換,接近零成本地渲染出動畫效果。這里的內容緩存我們同樣考慮了內存優化問題。例如一個動畫文件預設的大小是 500x500,但是實際使用中,整體被縮放到了 50x50 的大小,那么內部創建的內容緩存,會對應的縮小相應的面積倍數。這樣可以做到在保證清晰度的前提下,只緩存最小的面積。
4.2 BMP 預合成
在純矢量的導出模式下,無論是那種實現方案,在眾多的 AE 特性面前,都只支持將有限的 AE 特性導出渲染。因為在有桌面顯卡的情況下,有部分 AE 特性都還需要跑進度條才能完成預覽,在移動端根本沒可能做到實時。另外還存在第三方 AE 插件的效果無法導出的問題。這在一定程度上限制了設計師的創造力。另一方面,由于相同的動畫在 AE 中有很多實現方式,但性能卻千差萬別。于是我們思考如何解決眾多 AE 特性支持的問題,通過分析 AE 提供的 SDK 的能力,我們發現 AE SDK 可以直接截圖,可以導出任何效果,且包含第三方 AE 插件的效果,但缺點也很明顯,圖片無法進行編輯,如果通過截圖的方式,文件會比較大。
圖5 BMP預合成導出實現文件大問題解決
針對截圖后文件比較大的問題(動畫一般不低于 24 幀),我們首先想到了視頻編碼的極限幀間壓縮能力,相對于原始的圖片序列幀,可以壓縮到百分之一點幾的大小,另外視頻格式還可以使用硬件解碼,從而獲得比較高的渲染性能。但這里遇到的一個問題是:動畫一般都是透明的,而視頻格式卻不支持透明通道。于是我們視頻編碼的同時,擴展了透明通道,如上圖所示,左邊為 RGB 的視頻內容,右邊為 Alpha 通道的灰度圖,最終渲染的時候再合并回 RGBA 的圖片,從而實現對透明通道的支持。渲染的過程中,由于啟用了硬件加速解碼,可以直接得到一個 YUV 的紋理。我們在這里的優化點主要是不使用常見的 FFmpeg 來執行 YUV 到 RGB 轉換,從而避免紋理在 CPU 和 GPU 之間來回拷貝,而是自定義了一個 Shader 腳本,利用硬件加速在一次繪制過程中,同時完成 YUV 轉換和 Alpha 通道合并。這里平均就能夠提高 10%的渲染性能。
可編輯性問題解決
針對 BMP 預合成無法編輯的特點,我們將 BMP 預合成支持的粒度由文件延伸到合成,支持矢量和 BMP 預合成混合導出,從而實現了支持所有的 AE 特性又能保持運行時的可編輯性。
4.3 圖層編輯能力
在照片模板和視頻模板不斷地量產過程中,固定時間軸和尺寸的模板已經逐漸出現了在應用上的瓶頸。特別是當一鍵出片、王者戰報等智能模板需求的出現,整個模板不是由固定的時間軸組成,而是可能由多個原子特效組件拼裝而成,設計師即使投入非常高的人力,也無法針對每一種情況進行排列組合輸出。這里對 PAG 的編輯能力也提出了進一步的挑戰:就是要能對多個 PAG 文件,同時具有空間位置和時間軸的組合能力。由業務方去控制組合的規則。基于這個需求,我們引入了圖層渲染樹的編輯架構,不僅支持文本和占位圖比編輯,還支持圖層級別的編輯。一個文件就是一棵渲染樹,支持圖層級別的任意修改位置甚至增刪圖層,也可以把別的 PAG 文件添加到這棵渲染樹中作為子樹。能在空間維度上進行自由的排列擺放。而在時間軸的組合上,我們提供了 PAG 時間伸縮的能力,包含循環,變速,定格等多種自適應模式。每個圖層又提供了起始時間的調整能力,能夠自由設置在時間軸上的相對位置,能夠靈活適配用戶視頻的時長。
圖6 PAG圖層編輯經過這些改造,新的接口不僅滿足了智能模板的編輯性需求,也簡化了原有業務調用的復雜度。例如原先業務上除了要構建外部的視頻時間軸,還需要在渲染的過程中不斷手動更新每個視頻片段和 PAG 進度的對應關系。現在無論哪種使用場景,都可以簡化為兩個步驟:利用空間和時間的組合能力構建一個渲染樹,然后播放或者導出即可。
4.4 全新渲染引擎升級
在 PAG 的前 3 個大版本的迭代過程中,大部分的業務痛點問題都已經得到了很好的解決和覆蓋。但是接入過程中始終一直還存在一個難以回避的痛點:SDK 包體能否進一步壓縮?例如在某些頭部的 App 對接過程中,甚至要求接入后包體 0 增量。對大部分應用來說,包體直接影響增長拉新的數據,因此包體優化確實是個剛需。在之前的版本里,我們的渲染架構由于依賴了谷歌的 Skia 2D 繪圖庫。我們也已經針對性做了非常多的定制和裁剪,但是 Skia 依然占據了 PAG SDK 75%左右的包體,無法在進一步進行裁剪。
而在性能方面,3.0 版本上層的 PAG 渲染架構已經做了游戲引擎幾乎所有能做的優化策略。但是由于 Skia 需要兼容歷史遺留的 CPU 繪制模式,在 API 上暴露會比較保守,很多針對現代 GPU 繪制管線可以進一步優化性能的接口都沒暴露出來。另外由于 Skia 是針對 UI 這種隨機繪制設計的引擎,內部做了大量的緩存來確保隨機渲染的性能。而對于動畫這種可預測的渲染模式沒有很好的優化,如果針對性優化可以有效降低平均的內存占用。整體上由于渲染對 Skia 的依賴,導致我們在性能上想要進一步突破也遇到了瓶頸。
為了徹底打破包體和性能的限制,我們花了將近一整年時間自研實現了一套輕量的純 GPU 繪圖引擎。在包體方面,我們最大化利用了平臺端提供的所有可用能力,例如復雜矢量圖形的柵格化, iOS 直接使用平臺自帶的 CoreGraphics,文本方面利用起 CoreText ,Android 端圖片解碼直接利用 Java 反射等。最終實現以 500K 左右的包體覆蓋了 Skia 絕大部分功能。
而在接口設計上,我們充分暴露了針對 GPU 渲染的優化能力給到調用層,例如提交紋理后統一不再重復緩存一份 CPU 圖片,暴露傳入紋理遮罩緩存的能力實現一次性上屏,并在移動端全面開啟了 HardwareBuffer 接口的使用來加速紋理提交。在減小包體和內存占用的同時進一步提升了渲染性能的天花板。在接口易用性方面也自帶線程安全的設計,所有 GPU 資源統一管理,外部任意線程釋放引用都可以確保正確銷毀,降低了使用 Skia 的 GPU 繪制模式時,容易出錯并需要大量封裝平臺相關上下文代碼的門檻。
表3 PAG 4.0 包體優化數據另外在 PAG 4.0 版本中,我們也提供了對 Web 平臺的支持。之前遲遲未覆蓋這最后一個平臺,部分原因也是在等待新的渲染引擎升級完成后,可以減少 Web 端的包體加載壓力。在 Web 端,Lottie 和 SVGA 使用 Web 的 HTML、CSS 和 Javascript 重新實現了一遍。而 PAG 依然保持了全平臺共享一套 C++ 代碼的架構。通過 WebAssembly 將全新的渲染引擎直接綁定到 WebGL 接口上進行渲染,僅在文本和柵格化等模塊上對 Web 平臺做了針對性的優化適配。
目前這個新的繪圖引擎仍然內置在 PAG 4.0 版本內,未來有可能會進一步抽離成獨立的 2D 繪圖庫,應用到動畫工作流以外更多的渲染場景中。目標實現成針對現代 GPU 渲染優化的,包體和性能達到最佳平衡的 2D 繪圖引擎。
5、 結束語
除了本文描述的 PAG 技術能力,相對于 Lottie 和 SVGA,PAG 的輔助工具也非常完善,如果需要了解,大家可以訪問 PAG 官網:https://pag.io/
如果大家對改進 PAG 項目有任何的想法或建議,歡迎訪問 Github 主頁:
https://github.com/Tencent/libpag?
提交 issue 或 pull request,一起參與到開源項目建設中,幫助 PAG 動畫方案做到更好。
官方交流QQ 群: 893379574
總結
以上是生活随笔為你收集整理的腾讯自主研发动画组件PAG开源的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 云原生背景运维转型之 SRE 实践
- 下一篇: 新一代消息队列 Pulsar