| RFC-0205:Vulkan Loader | |
|---|---|
| 狀態 | 已接受 |
| 區域 |
|
| 說明 | 說明應用程式如何載入 Vulkan ICD 和層 |
| 問題 | |
| Gerrit 變更 | |
| 作者 | |
| 審查人員 | |
| 提交日期 (年-月-日) | 2023-01-12 |
| 審查日期 (年-月-日) | 2023-01-12 |
摘要
本 RFC 說明 Fuchsia 上的軟體如何載入 Vulkan ICD 和層,以執行硬體加速的算繪作業。
這份文件中的系統已大致實作完成,但文件可能包含我們日後打算進行的架構變更。
提振精神
Vulkan API 具有 C 樣式的介面,應用程式可透過這個介面編寫 GPU 程式。Vulkan 應用程式會透過幾種方式,使用 Vulkan 載入器與 Vulkan 函式介接,詳情請參閱「與 Vulkan 函式介接」。
本文中的「應用程式」是指使用 Vulkan API 的軟體元件。
Vulkan 載入器也負責載入可安裝的用戶端驅動程式 (ICD) 和 Vulkan 層,並將存取權委派給 magma 或其他 API,以便代表 ICD 執行 GPU 指令。
Vulkan ICD 是供應商專屬的共用程式庫,會載入應用程式,讓應用程式使用 GPU 進行算繪。使用 Vulkan 的應用程式需要某種機制,才能識別及載入系統中硬體的正確 ICD。
Vulkan 層是共享程式庫,可透過擴增 Vulkan API 呼叫的調度鏈,修改或觀察 Vulkan API 的行為。可用於強化 Vulkan 的功能,或代表 Vulkan 偵錯或剖析功能插入 API。
利害關係人
協助人員:
rlb@
審查者:
cstout@ costan@ jhowarth@ msandy@ rosasco@ palmer@ wittrock@
已諮詢:
社交:
Magma 團隊成員已審查過設計。本文件的早期版本已分享給元件架構團隊,並在安全團隊的辦公時間中提供。
設計
在 Fuchsia 上,Vulkan 載入器會分成兩部分:載入應用程式的 libvulkan.so 共用程式庫,以及負責載入 ICD VMO 並將其傳輸至 libvulkan.so 的載入器服務 (vulkan_loader)。兩者會使用 fuchsia.vulkan.loader.Loader 通訊協定進行通訊。
libvulkan.so
Khronos 是 Vulkan API 的標準機構。這些程式庫提供載入器共用程式庫實作,適用於 Linux、Windows、macOS 和大多數其他平台。Google 另外編寫了適用於 Android 的載入器。
Fuchsia 載入器是以 Khronos 的實作項目為基礎,程式碼位於 Fuchsia 存放區的 third_party/Vulkan-Loader,但最終會全數上傳至上游。應用程式呼叫 vkCreateInstance 或其他列舉函式時,載入器會讀取環境變數和 JSON 設定檔,判斷要使用的 ICD 和層級組合。圖層會從元件的命名空間載入,因此通常會儲存在套件中。如果載入器設定設為使用這些目錄,也可以從提供給元件的目錄功能載入。
正在載入 ICD

啟動時,libvulkan.so 會連線至 fuchsia.vulkan.loader.Loader 通訊協定。這個管道在應用程式的生命週期內都必須保持連線。如果退出,所有後續的載入器呼叫都可能會失敗。
這個長期連線可防止元件架構在執行使用 Vulkan 的用戶端時,重新載入或更新載入器。這是理想做法,因為應用程式使用載入器 API 呼叫時,可避免 Vulkan ICD 和載入器介面發生非預期的版本變更。部分用於列舉擴充功能或其他執行個體屬性的 Vulkan 進入點不會採用任何類型的內容引數,因此實作會有一些隱含的全域狀態。

ICD 是透過 fuchsia.vulkan.loader.Loader 通訊協定載入。載入器會使用 fuchsia.vulkan.loader/Loader.ConnectToManifestFs 方法存取檔案系統,其中包含描述所有相關 ICD 的資訊清單 JSON 檔案;這個檔案系統與 Linux 上的 /usr/local/share/vulkan/icd.d 檔案系統相同;如要瞭解該檔案系統的詳細資料,請參閱「檔案系統服務」。
載入器接著會使用 fuchsia.vulkan.loader/Loader.Get 方法擷取對應於 ICD 的 VMO,並載入程序中,從中取得 ICD 進入點。dlopen_vmoFuchsia 上的 Vulkan 進入點集與 Linux 上的相同,但 Fuchsia 專屬擴充功能除外,詳情請見下文。
用戶端元件也可能與軟體 ICD 實作項目 (例如 SwiftShader) 封裝在一起。如果是 SwiftShader,可以使用 VK_ICD_FILENAMES 環境變數指定 ICD 的 manifest.json 路徑。ICD 共用程式庫會從 Vulkan 用戶端元件的 /pkg/lib 載入。
由於大多數 ICD 並未儲存在套件中,且與應用程式二進位檔分開進行版本控管,因此只能對連結的應用程式 ABI 做出有限的假設。可使用的確切介面列於 Fuchsia 系統介面,但一般來說,只能使用有限的符號清單,且所有符號都必須來自 libc.so 或 libzircon.so。建構 ICD 時,系統會根據允許清單驗證匯入的符號,確保 ICD 可針對多個版本的用戶端應用程式載入。日後,隨著密封替代項目建立完成,這份許可清單可能會縮減。
ICD 必須能夠連線至外部通訊協定,尤其是與硬體通訊的基礎裝置驅動程式。他們可能也想讀取供應商專屬的設定檔,以及記錄錯誤。libc.so 會匯出多個符號來執行 I/O,但實際上,基礎作業 (例如 open) 是在 libfdio 中實作。此外,如果沒有從 libfdio 直接匯出的其他符號,就無法使用檔案系統連線至 Zircon 管道。
如要允許 ICD 執行有限的 I/O,這些定義會新增至 Vulkan ICD API:
VkResult(VKAPI_PTR* PFN_vkOpenInNamespaceAddr)(const char* pName, uint32_t handle);
VKAPI_ATTR void VKAPI_CALL vk_icdInitializeOpenInNamespaceCallback(PFN_vkOpenInNamespaceAddr
open_in_namespace_addr);
ICD 應公開 vk_icdInitializeOpenInNamespaceCallback。在呼叫任何其他驅動程式庫函式之前,系統會使用 open_in_namespace_addr 回呼呼叫此函式。ICD 可以將檔案名稱和 Zircon 管道用戶端結尾傳遞至這個回呼,以便依名稱連線至檔案系統節點。
這個函式可以存取程序的傳入命名空間,因此 ICD 可以讀取設定檔,或連線至 fuchsia.logger.LogSink 或 fuchsia.tracing.provider.Registry 等服務。Vulkan ICD 可能包含全域狀態,因此如果程序是可代管多個子項元件的執行元件 (可能使用虛擬機器或其他非程序機制來隔離元件),執行元件必須確保提供給 ICD 的服務可供任何子項元件安全使用。舉例來說,如果多個不受信任的子項元件位於程序中,執行元件就不應透過執行元件不信任的子項元件傳送 fuchsia.tracing.provider.Registry,因為該元件可能會監控所有 ICD 圖像活動。
open_in_namespace_addr 回呼會特別處理 /loader-gpu-devices 路徑的存取權。該路徑的所有存取權都會透過 fuchsia.vulkan.loader/Loader.ConnectToDeviceFs 方法,導向 vulkan_loader 提供的檔案系統;這可讓 ICD 連線至所需的任何硬體專用裝置驅動程式庫節點。ICD 可以使用 zxio 或原始 FIDL 遍歷檔案系統;如要瞭解該檔案系統的詳細資料,請參閱「檔案系統服務」。
圖層通常透過 SDK 發布,並從與應用程式相同的套件載入,因此可以依賴 SDK 中任何軟體的相同 ABI 保證。透過外部套件的目錄功能載入的層,在 ABI 方面應與 ICD 相同。
ICD 卸載/重新載入
目前無法卸載共用程式庫,因此任何 ICD 都會在程序生命週期內保持載入狀態。為避免在建立新的 Vulkan 執行個體時發生記憶體膨脹,載入器會保留所有已查看 ICD 的無效期快取 (由共用程式庫檔案名稱識別)。只要 vulkan_loader 連線有效,這個檔案名稱就是唯一的。
載入器執行環境
Vulkan API 沒有非同步執行迴圈的概念,因此從應用程式的角度來看,函式呼叫必須同步完成。載入器不會從應用程式接收 async_dispatcher_t*,且不得使用 libasync-default.so 的預設調度器。這可能會在內部建立自己的調度器和執行緒。
元件的傳出目錄是由應用程式的程式碼代管,因此載入器無法在其中放置項目。這會限制它與其他元件的互動方式。此外,平台也不會要求應用程式只載入載入器的單一副本,不過目前所有應用程式都使用 libvulkan.so 的副本,由於 soname,系統會在載入時重複資料刪除。
根據預設,載入器會在 /vulkan-loader-configuration 中搜尋設定檔,並改用 /pkg/data。這些路徑可由環境變數或覆寫層覆寫,與 Linux 相同。
vulkan_loader
vulkan_loader 服務負責判斷可用的 ICD、載入這些 ICD,並提供給應用程式。這個外掛程式位於 /core/vulkan_loader,而它公開的 fuchsia.vulkan.loader.Loader 服務會路由至工作階段、測試架構和多個應用程式。這段程式碼是以 C++ 編寫,位於 //src/graphics/bin/vulkan_loader,說明文件則位於 /src/graphics/bin/vulkan_loader/README.md。
日後這項服務可能會以 Rust 重新編寫,以降低安全風險並善用非同步程式設計功能。
辨識新裝置
vulkan_loader 服務必須能夠識別可用的 ICD。這是由正在執行的裝置驅動程式集所驅動。如果硬體的裝置驅動程式庫未執行,就無法使用相關聯的 ICD。
vulkan_loader 會使用 /dev/class/goldfish-pipe 和 /dev/class/gpu 上的目錄監看程式,判斷何時出現新的圖形裝置。
當新的圖形裝置出現時,載入器必須判斷與 ICD 相關聯的元件。確切機制視裝置類型而定:
/dev/class/gpu- 裝置上會呼叫 fuchsia.gpu.magma/Device.GetIcdList。/dev/class/goldfish-pipe:ICD 網址已硬式編碼為fuchsia-pkg://fuchsia.com/libvulkan_goldfish#meta/vulkan.cm
日後可能會支援更多類型的 GPU 硬體裝置。在某些裝置上,軟體 ICD 也可能會透過載入器通訊協定公開為備援 (由 vulkan_loader 設定選擇)。軟體 Vulkan ICD (例如 SwiftShader) 通常具有 JIT,且需要寫入可執行記憶體的功能;因此,在基於安全考量而嚴格控管這項能力的生產系統中,可能無法使用這類 ICD。
檔案系統服務
vulkan_loader 會向用戶端提供多個檔案系統,包括資訊清單檔案系統和裝置檔案系統。它會根據透過 devfs 收到的多個 ICD 套件和服務內容,建立這些檔案系統。因此,這些檔案必須使用檔案系統服務程式庫建構,且不會反映磁碟上的任何內容。
- 資訊清單 fs:說明所有相關 ICD 的所有資訊清單 JSON 檔案;這個檔案系統與 Linux 上的 /usr/local/share/vulkan/icd.d 相同,因此載入器只需要進行最少的變更。
- device fs:包含支援的 Vulkan ICD 所需的所有 GPU 裝置。如果是
/dev/<path>/<node>裝置,檔案系統會包含<path>/<node>項目。
ICD↔︎載入器介面
ICD 會以 CFv2 元件的形式提供給載入器。ICD 元件必須公開 contents 目錄,其中包含任意目錄樹 (內含共用程式庫),以及 metadata 目錄 (內含單一 metadata.json 檔案)。
ICD 通常會單獨放在獨立的封裝中。在這種情況下,contents 目錄會是套件的根目錄,而 metadata 目錄會是套件中的 meta/metadata/ 目錄。不過,載入器不會強制執行這個版面配置。
metadata.json 和 manifest.json 應盡可能儲存在套件的 meta 目錄中,因為這個目錄最適合儲存小型檔案。
ICD 共用程式庫
ICD 共用程式庫應符合 Vulkan ICD ABI。ICD 是可執行的共用程式庫,可放在套件的大部分子目錄中 (/bin 除外)。
元件資訊清單
Vulkan 載入器提供 icd_runner 執行器,可簡化從套件建立 ICD 元件的程序。ICD 套件必須包含元件資訊清單 .cml,其中匯出 contents 和 metadata 目錄功能。
icd_runner 會自動從 ICD 套件匯出 /pkg/data 和 /pkg/meta/metadata 目錄,路徑分別為 /pkg-data 和 /pkg-metadata。CML 可使用這些屬性匯出目錄功能 (使用 subdir 屬性將子目錄公開為完整能力)。
ICD 元件也可能使用 ELF 執行元件,但只能使用 fuchsia.logger.LogSink 服務。
metadata.json
metadata.json 是單一 JSON 檔案,用於向載入器說明 ICD。範例:
{
"file_path": "lib/libvulkan_example.so",
"version": 1,
"manifest_path": "meta/icd.d/libvulkan_example.json"
}
- 這個中繼資料版本的值必須為 1。
version file_path是 ICD 共用程式庫相對於公開contents目錄的位置。manifest_path是相對於公開contents目錄的 Khronos ICD 資訊清單 JSON 檔案位置。
其他客戶
可用的 Vulkan ICD 集可能會隨時間變更;系統首次啟動時,在列舉硬體之前,不會有任何 ICD 可用。之後,裝置可能會熱插拔,並顯示或消失。
也就是說,vkEnumeratePhysicalDevices 傳回的裝置清單隨時可能變更。需要 Vulkan 的應用程式可能會想在可用裝置組合變更後重試。他們可以在 fuchsia.vulkan.loader/Loader.ConnectToManifestFs 傳回的檔案系統上使用檔案系統監控程式,判斷何時重試。
實作
這項設計代表 Vulkan 載入器的現行架構,已在 Fuchsia 上實作。
效能
Vulkan 載入器在程序啟動時最為活躍。載入 Vulkan ICD 後,系統會將 Vulkan 呼叫跳轉至 ICD,或將 ICD 函式實作項目傳回應用程式,供應用程式直接呼叫。因此,只有在程序啟動期間,其效能才至關重要。
載入器的效能未經過特別考量。必須啟動元件才能連線至 ICD,並遍歷多個檔案系統路徑,才能找出 ICD 和層級設定。目前認為這不會對執行階段效能造成重大影響。
回溯相容性
libvulkan.so 與 vulkan_loader 之間的通訊會使用檔案系統、JSON 和 FIDL。這些檔案系統和 JSON 已在 Linux 上使用多年,沒有回溯相容性問題。您可以透過自然演進的方式 (分別新增路徑和鍵) 維護回溯相容性。FIDL 介面很小,可使用 FIDL 版本控管機制演進。
安全性考量
元件會載入 Vulkan 載入器提供的共用程式庫。系統的正常已驗證執行強制執行作業,可確保可執行的共用程式庫來自可信賴的位置 (例如檔案系統)。任何父項元件都可能介入 fuchsia.vulkan.loader.Loader 通訊協定,因此無法保證載入器服務元件看到的內容是由系統提供。
系統會透過解析器載入所選 ICD,並在 Magma 系統驅動程式庫 (MSD) 中依路徑參照這些 ICD。系統預設會使用完整解析器,因此可以載入臨時套件。從暫時性套件載入 ICD 對於 ICD 開發人員很有用,但對大多數使用者來說並非必要。如要停用載入暫時性套件,請停用完整解析器 (設定 auto_update_packages=false gn 參數)。我們也可以為 Vulkan 載入器建立多個核心分片,供產品擁有者選擇;工程建構可選擇使用完整解析器的分片,使用者建構則可使用基本解析器的分片。
如需特定產品,可以建立多個 vulkan_loader 服務例項,每個例項都能存取不同的解析器。根據用戶端的安全性需求,這些 fuchsia.vulkan.loader.Loader 實作項目可能會轉送至用戶端元件。目前沒有任何產品有這項規定。
載入器的設定可能會載入新圖層、防止載入其他圖層,或在這些圖層上設定選項,導致應用程式出現非預期行為。元件必須選擇從套件外部取得設定 (方法是從套件外部路由目錄能力),否則會完全控管載入器設定。
ICD 共用程式庫會在用戶端程序中執行,並可在該程序中執行任意程式碼。建構程序和一致性測試會確保只匯入允許的符號,但這並非安全保障,而且很容易遭到規避,例如查看呼叫堆疊以尋找位址,以及剖析記憶體中的可執行檔以尋找實用的小工具。應用程式不會驗證 Vulkan 傳回的大多數值,而且可能會透過仔細操控這些值,進行任意記憶體存取。
如果執行元件將多個不受信任的元件載入單一程序 (可能是使用虛擬機器或其他非程序機制來隔離元件),這些元件就不得直接發出 Vulkan 呼叫,因為目前沒有已知方法可驗證 Vulkan API 呼叫,確保應用程式不會在 Vulkan ICD 中執行未定義的行為;即使是 Vulkan 驗證層,也只能提供有限的保護。執行器程式碼本身可能會發出 Vulkan 呼叫,例如使用 Skia 或 ANGLE 代表用戶端執行經過驗證的算繪指令。提供給 ICD 的服務和裝置管道必須來自執行元件信任的來源,以免子元件彼此監聽。
隱私權注意事項
Vulkan 載入器對隱私權的影響極小。透過 FIDL 公開的唯一資訊是應用程式是否嘗試使用 Vulkan,以及嘗試使用的裝置。
測試
vulkan_loader 和 libvulkan.so 都有單元和整合測試。這些測試是密封的,不依賴裝置驅動程式或系統上安裝的實際 ICD。
此外,還有 CTF 測試,可確保 fuchsia.vulkan.loader.Loader 通訊協定的實作方式正確無誤,且 ICD 與舊版載入器相容。
Fuchsia 樹狀結構中的 Vulkan CTS 和其他 Vulkan 測試會做為端對端測試,檢查 vulkan_loader 是否與 libvulkan.so 相容。這些只能在搭載 Vulkan 硬體和裝置驅動程式的系統上執行。
說明文件
我們在 /src/graphics/bin/vulkan_loader/README.md 提供 vulkan_loader 說明文件。此外,我們也提供 Vulkan 載入器的使用者說明文件。
上游 Vulkan Loader 具有說明文件。我們應嘗試在該文件中新增 Fuchsia 專屬資訊並上傳。