Metal 是一個和 OpenGL ES 相似的面向底層的圖形編程接口,經過使用相關的 api 能夠直接操做 GPU ,最先在 2014 年的 WWDC 的時候發佈。Metal 是 iOS 平臺獨有的,意味着它不能像 OpenGL ES 那樣支持跨平臺,可是它能最大的挖掘蘋果移動設備的 GPU 能力,進行復雜的運算,像 Unity 等遊戲引擎都經過 Metal 對 3D 能力進行了優化, App Store 還有相應的運用 Metal 技術的遊戲專題。編程
阿里巴巴淘系技術部的閒魚團隊是比較早在客戶端側選擇Flutter方案的技術團隊,當前的閒魚工程裏也是一個較爲複雜的Native-Flutter混合工程。做爲一個2C的應用,性能和用戶體驗一直是閒魚技術團隊在開發中比較關注的點。而Metal這樣的直接操做GPU的底層接口無疑會給閒魚技術團隊突破性能瓶頸提供一些新的思路。 segmentfault
下面會詳細闡述一下此次大會Metal相關的新特性,以及對於閒魚技術和整個淘系技術來講,這些新特性帶來了哪些技術啓發與思考。api
( WWDC 2020精彩內容思否專欄:https://segmentfault.com/blog... 緩存
本篇內容來自於阿里巴巴淘系技術部,無線開發工程師岑彧。
更多精彩內容可關注【淘系技術】公衆號。)session
這一章其實主要介紹的是Apple GPU的在圖形渲染上的原理和工做流,是一些比較底層的硬件原理。當咱們使用Metal進行App或者是遊戲的構建的時候,Metal會利用GPU的tile-based deferred rendering (TBDR)架構給應用和遊戲帶來很是可觀的性能提高。這一章主要就是介紹GPU的的架構和能力,以及TBDR架構進行圖像渲染的原理和流程。總之就是號召開發者們使用Metal來構建應用和遊戲。由於這個session沒有涉及到上層的軟件開發,就不對視頻的具體內容進行贅述了。詳情可見:Harness Apple GPUs with Metal架構
這一章主要介紹了Xcode中的GPU性能分析工具Instrument,這個工具如今已經支持了GPU的性能分析。而後從多個方面分析了GPU的性能瓶頸,以及性能瓶頸出現時的優化點。整體來講就是經過性能分析工具來優化咱們的App或者遊戲,讓整個畫面更加流暢。整個章節主要分爲五個部分:app
這個環節主要是快速回顧了一下Apple的GPU的架構和渲染流程。而後由於不少渲染任務都須要在不一樣的硬件單元上進行,例如ALU和TPU。他們對不一樣的吞吐量有着不一樣的度量。有不少GPU的性能指標須要被考慮,因此推出了GPU性能計數器。這個計數器可能測量到GPU的利用率,太高和太低都會形成咱們的渲染性能瓶頸。關於計數器的具體使用,參考官方的video效果會更好:Optimize Metal apps and games with GPU counters(6:37~9:57),主要使用了Instrument工具,關於工具的全面詳細的使用能夠參考WWDC19的session videoGetting Started with Instruments框架
這一章主要介紹了形成GPU性能瓶頸的各個方面以及它們的優化點。主要分爲六個方面,以下圖所示:
編程語言
GPU中一般經過ALU(Arithmetic Logic Unit)來處理各類運算,例如位操做,關係操做等。他是着色器核心的一部分。在這裏一些複雜的操做或者是高精度的浮點運算都會形成一些性能瓶頸,因此給出如下建議來進行優化:
ide
如上圖所示,咱們可使用近似或者是查找表的方式來替換複雜的運算。此外,咱們能夠將全精度的浮點數替換爲半精度的浮點數。儘可能避免隱式轉換,避免32位浮點數的輸入。以及確保全部的着色器都使用Metal的「-ffast-math」來進行編譯。
GPU經過Texture Processing Unit來處理紋理的讀寫操做。固然在讀寫的過程當中也會遇到一些性能瓶頸問題。這裏從讀和寫兩個部分分別來給出優化點:
如上圖所示,咱們能夠嘗試使用mipmaps。此外,能夠考慮更改過濾選項。例如,使用雙線性代替三線性,下降像素大小。確保使用了紋理壓縮,對Asset使用塊壓縮(如ASTC),對運行時生成的紋理使用無損紋理壓縮。
如上圖所示,咱們應該注意到像素的大小,以及每一個像素中惟一MSAA樣本的數量。此外,能夠嘗試一些優化一些邏輯寫法。
圖塊內存是一組存儲Thread Group和ImageBlock數據的高性能內存。當從ImageBlock或是Threadgroup讀取或寫入像素數據時,好比在使用Tile着色器時或者是計算分派時,能夠訪問到Tile內存。那當使用GPU性能計數器發現這個方面的性能瓶頸時,咱們能夠以下圖所示進行優化。
考慮減小threadgroup的並行,或者是SIMD/Quadgroup操做。此外,確保將線程組的內存分配和訪問對齊到16字節。最後,能夠考慮從新排序內存訪問模式。
在Metal中,緩衝區只被着色器核心訪問。在這個地方發現了性能瓶頸。咱們能夠以下圖所示進行優化:
能夠更大力度的壓縮打包數據,例如使用例如packed_half3這樣小的類型。此外,能夠嘗試向量化加載和存儲。例如使用SIMD類型。避免寄存器溢出,以及可使用紋理來平衡工做負載。
若是在這個方面,咱們的GPU性能計數器顯示一個太高的值。咱們能夠以下圖這樣優化:
若是紋理或者是緩存區也一樣顯示一個太高的值,咱們能夠把這個優化放到第一優先級。咱們能夠考慮減少工做集的大小。若是Shader正在使用Device Atomics,咱們能夠嘗試重構咱們的代碼來使用Threadgroup Atomics。
分段輸入插值。分段輸入在渲染階段由着色器核心進行插值。着色器核心有一個專用的分段輸入插值器。這個是比較固定和高精度的功能。咱們能優化的點很少,以下圖所示:
儘量的移除傳遞給分段着色器的頂點屬性。
內存帶寬也是影響咱們GPU性能的一個重要因素。若是在GPU性能計數器的內存帶寬模塊看到一個很高的值。咱們就應該以下圖所示來進行優化:
若是紋理和緩存區也一樣顯示比較高的值,那優化優先級應該排到第一位。優化方案也是較少Working Set的大小。此外,咱們應該只加載當前渲染過程須要的數據,只存儲將來渲染過程須要的數據。而後就是確保使用紋理壓縮。
若是咱們看到總體利用率比較低,這意味着Shader可能已經耗盡了一些內部資源,好比tile或者threadgroup內存。也多是線程完成執行的速度比GPU建立新線程的速度快。
咱們經過GPU計數器能夠統計到重複繪製的區域,咱們應該高校使用HSR來避免這樣的重繪。咱們能夠如圖所示的順序來進行繪製。
這一章主要給開發者們介紹了一種使用Metal的編程工做流,能夠經過優化Metal的渲染編譯模型來加強渲染管線,這個優化能夠在應用程序啓動,特別是首次啓動時大大減小PSO(管線狀態對象)的加載時間。可讓咱們的圖形渲染更加的高效。整個章節主要分爲四個部分:
衆所周知,Metal Shading Language是Apple爲開發者提供的Shader編程語言,Metal會將編程語言編譯成爲一個叫作AIR的中間產物,而後AIR會在設備上進一步編譯,生成每一個GPU所需的特定的機器碼。整個過程以下圖所示:
上述過程在每一個管線的生命週期中都會發生,當前Apple爲了加速管線的從新編譯和從新建立流程,會緩存一些Metal的方法變體,可是這個過程仍是會形成屏幕的加載耗時過長。並且在當前的這個編譯模型中,應用程序不能在不一樣的PSO(管線狀態對象)中重用以前生成的機器碼子程序。
因此咱們須要一種方法來減小這個整個管線編譯(即源代碼->AIR->GPU二進制代碼)的時間成本,還須要一種機制來支持不一樣PSO之間共享子程序和方法,這樣就不須要將相同的代碼屢次編譯或者是屢次加載到內存中。這樣開發者們就可使用這套工具來優化App首次的啓動體驗。
Metal二進制文件就是解決上述需求的方法之一,如今開發者們能夠直接使用Metal爲二進制文件來控制PSO的緩存。開發者能夠收集已編譯的PSO,而後將它們存儲到設備中,甚至能夠分發到其餘兼容的設備中(一樣的GPU和一樣的操做系統),這種二進制文件能夠看作一種Asset。下面是一些例程和示意圖:
總的來講就是這個Metal二進制文件能夠提供開發者手動管理管線緩存的方法,這樣就能夠從一個設備中獲取這些文件並部署到其餘兼容的設備上,在iOS環境下,極大地減小了第一次安裝遊戲或應用以及設備重啓後的管道建立時間。能夠優化應用的首次啓動體驗和冷啓動體驗。
動態庫將容許開發者編寫可重用的庫代碼,卻能夠減小從新編譯程序的時間和內存成本,這個特性將會容許開發者將計算着色器和程序庫動態連接。並且和二進制文件同樣,動態庫也是可序列化和可轉移的。這也是解決上述需求的方案之一。
在PSO生成的時候,每一個應用程序都須要爲程序library生成機器碼,並且使用相同的程序庫編譯多個管線會致使生成重複的機器碼。因爲大量的編譯和內存的增長,這個可能會致使更長的管線加載時間。而動態庫就能夠解決這個問題。
Metal Dynamic Library容許開發者以機器碼的形式動態連接,加載和共享工具方法。代碼能夠在多個計算管線中重用,消除了重複編譯和多個相同子程序的存儲。並且這個 MTLDynamicLibrary是可序列化的,能夠做爲應用程序的Asset使用。MTLDynamicLibrary其實就是多個計算管線調用的導出方法的集合。
大體的工做流程以下:咱們首先建立一個MTLLibrary做爲咱們指定的動態庫,這個能夠將咱們的metal代碼編譯爲AIR。而後咱們調用方法makeDynamicLibrary,這個方法須要指定一個惟一的installname,在管線建立時,linker將會使用這個名字來加載動態庫。這個方法能夠將咱們的動態庫編譯成爲機器碼。這就完成了動態庫的建立。
對於動態庫的使用來講:經過設置MTLCompileOptions裏的libraries參數,就能夠完成動態庫的加載和使用了。代碼以下:
這個部分主要介紹了構建Metal二進制文件和構建動態庫的具體工具和方法。以視頻的形式可能會更好的表現,詳情可見:Build GPU binaries with Metal (從22:51開始)
這一章主要介紹的是GPU側的bug,當前若是咱們的應用程序出現了GPU側的bug,他的錯誤日誌經常都不能讓開發者很直觀的定位到錯誤的代碼範圍和調用棧。因此在最新的Xcode中,加強了關於GPU側的debug機制。能夠像在代碼側發成的錯誤同樣不但能定位到錯誤緣由,還有錯誤的調用堆棧和各類信息均可以詳細的查看到。讓開發者能更好的修復代碼形成的GPU側的渲染錯誤。
這是當前的錯誤日誌上報,咱們能夠看到GPU側的錯誤日誌不像Api的錯誤日誌同樣可讓開發者很快的定位到錯誤緣由和錯誤的代碼位置。
而最新的Metal debugging工具就加強了這方面的能力,讓Shader的code也能夠像Api代碼同樣提供錯誤定位和分類能力。
咱們經過如下代碼即可以啓用加強版的commandbuffer錯誤機制
錯誤一共有五種狀態:
咱們也能夠經過如下代碼來打印error:
開發者能夠在開發時和測試時啓用優化版的錯誤機制
如上圖所示,這個功能能夠在GPU側發生渲染錯誤時自動定位和catch到錯誤並定位到代碼,以及獲取回溯棧幀。
咱們能夠在Xcode中按照如下流程來開啓這個功能:
1.開啓Metal中的兩個Validation選項
2.開啓issue自動斷點開關並配置類型和分類等選項
Video中用了一個demo來展現整個工做流,具體參見Debug GPU-side errors in Metal(11:25~14:45)大體流程以下圖所示:
這是一個Demo應用程序,很明顯它在渲染上出現了一些異常,可是由於是GPU側的問題,因此開發者很難定位。可是經過上述的工做流開啓Shader Validation以後。
Xcode會自動斷點到發生異常的地方,並展現出異常信息,這樣就能夠極大的提高開發者的錯誤修復效率。
這一章主要講的是Xcode12給Metal App提供了更多調試和分析的新工具。大體以下圖所示:
主要分爲兩個部分:
這個工具可讓開發者在App運行時,獲取到想分析和調試的任何一幀,而後再進入Xcode提供的各類分析界面,整體狀況,依賴狀況,內存,帶寬,GPU,Shader等各類具體的界面來對這一幀進行更加詳細的分析和調試。整個過程使用視頻的方式可能會更加高效,因此這裏不會進行詳細的贅述和分析。詳情能夠參見Gain insights into your Metal app with Xcode 12
整個工具跟以前提到過的Debugger相比,他的功能主要是讓開發者能夠隨着時間的推移來捕獲應用程序的各類信息和特徵,可讓開發者很好的調試一些例如終端,幀丟失,內存泄漏等問題。而Debugger主要是對某一幀進行調試和分析。
他提供了一個叫作編碼時間線的工具,可讓開發者查看到GPU在應用運行中的運行各類命令緩衝的狀況。而後提供了一個叫作着色器時間線的工具,可讓開發者查看到各類着色器在代碼運行期間運行的過程。而後還有GPU計數器的工具,這個工具咱們在前文進行了詳細的分析,主要是用於解決GPU的繪製性能問題的工具。而後最後一個工具就是內存分配跟蹤工具,可讓開發者查看到應用程序運行過程當中各類內存的分配和釋放,能夠幫助開發者解決內存泄漏問題或者是下降應用內存佔用。
WWDC 20關於Metal的Session中,比較重要的就是官方提供了不少可供開發者進行GPU級別的調試工具以及性能分析工具。給比較成熟龐大而複雜的工程突破性能瓶頸,提供更加優秀的用戶體驗提供了一些思路。
閒魚做爲一個電商類App,隨着功能和增多和以及工程的複雜化,在所不免的會遇到性能瓶頸,而閒魚團隊當前面對挑戰的方式是從工程級別來進行優化。從Flutter的角度來看,WWDC 20 對於Metal的調試工具和性能分析工具的完善,無疑提供了更多的優化思路。這爲將來運行在iOS上的應用的調優和突破性能瓶頸帶來了新的思路和可能性。
對於跨平臺框架,Apple有自家的SwiftUI,這也是這次大會的重點項目。不過不管是Flutter,仍是SwiftUI,你們最後對應用的性能瓶頸突破和優化必定是異曲同工的,也就是深刻到GPU級別來進行開發和調試以及性能分析。對於將來的客戶端開發人員,理解GPU和進行GPU級別的編程確定是不可或缺的技能點之一。
( WWDC 2020精彩內容思否專欄:https://segmentfault.com/blog...
本篇內容來自於阿里巴巴淘系技術部,無線開發工程師岑彧。 更多精彩內容可關注【淘系技術】公衆號。)