RFC-0205:Vulkan 載入器

RFC-0205:Vulkan 載入器
狀態已接受
區域
  • 圖像
說明

說明應用程式如何載入 Vulkan ICD 和層

問題
變更
作者
審查人員
提交日期 (年/月)2023-01-12
審查日期 (年/月)2023-01-12

摘要

這個 RFC 說明 Fuchsia 上的軟體如何載入 Vulkan ICD,以執行硬體加速轉譯作業。

本文件中的系統大部分均已實作完成,但本文件可能會包含我們打算在未來進行的架構變更。

提振精神

Vulkan API 有 C 式介面,應用程式會使用這個介面設計 GPU。使用 Vulkan 載入器的 Vulkan 應用程式,可以透過多種方式使用 Vulkan 函式,如以下所述:與 Vulkan 函式互動

在本文件中,「application」是用來表示使用 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 載入

Vulkan 載入器啟動流程

在啟動時,libvulkan.so 會連線至 fuchsia.vulkan.loader.Loader 通訊協定。在應用程式的生命週期內,此管道必須保持連結狀態。如果結束,日後所有的載入器呼叫都可能會失敗。

這種長期連線會防止元件架構在執行 Vulkan 的用戶端時,無法重新載入或更新載入器。之所以建議這麼做,是因為在應用程式使用載入器 API 呼叫時,可避免 Vulkan ICD 和載入器介面版本意外變更。部分用於列舉擴充功能或其他執行個體屬性的 Vulkan 進入點,不接受任何類型的 context 引數,因此實作將會有某些隱含的全域狀態。

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,以便 dlopen_vmo 載入程序並從中取得 ICD 進入點。Fuchsia 上的 Vulkan 進入點集與 Linux 相同,但下列是 Fuchsia 專用擴充功能除外。

用戶端元件也可以連同軟體 ICD 實作一起封裝,例如 SwiftShader。如果是 SwiftShader,VK_ICD_FILENAMES 環境變數可用來指定 ICD 的 manifest.json 路徑。系統將從 Vulkan 用戶端元件的 /pkg/lib 載入 ICD 共用資料庫。

由於大部分的 ICD 並未儲存在套件中,且會與應用程式二進位檔分開建立版本,因此對其連結的應用程式 ABI 只能做出有限的假設。這些系統可以使用的確切介面列在 Fuchsia 系統介面中,但通常只能使用有限的符號清單,這些符號都必須來自 libc.solibzircon.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.LogSinkfuchsia.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 的副本,但由於目前所有應用程式都會因其因此而刪除重複的副本。

載入器預設會在 /vulkan-loader-configuration 搜尋設定檔,並改回使用 /pkg/data。這些路徑可由環境變數或「覆寫層」覆寫,與 Linux 相同。

vulkan_loader

vulkan_loader 服務負責判斷可用的 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,並需要寫入可執行記憶體的能力,因此可能無法在必須基於安全因素嚴格控管的實際工作環境系統上使用。

檔案系統服務

vulkan_loader 為用戶端提供多個檔案系統,包括資訊清單 f裝置 f。此程式會根據透過 devf 接收的多個 ICD 套件和服務內容來建立這些檔案系統。因此,您必須使用檔案系統供應程式庫來建構,而且不會反映磁碟上的任何內容。

  • manifest fs:所有描述所有相關 ICD 的資訊清單 JSON 檔案;這個檔案系統和 Linux 中的 /usr/local/share/vulkan/icd.d 相同,因此需要對載入器進行最低的變更。
  • device fs:包含支援的 Vulkan ICD 所需的所有 GPU 裝置。如果是 /dev/<path>/<node> 裝置,檔案系統會包含 <path>/<node> 項目。

ICD️++ 載入器介面

CFv2 元件的形式提供 ICD 資訊。ICD 元件必須公開一個包含共用程式庫的任意目錄樹狀結構,以及包含單一 metadata.json 檔案的 metadata 目錄的 contents 目錄。

ICD 通常包含在獨立的套件中。在這種情況下,contents 目錄會是套件的根目錄,metadata 目錄則是套件中的 meta/metadata/ 目錄。不過,載入器不會強制執行這個版面配置。

在理想情況下,metadata.jsonmanifest.json 應儲存在套件中的 meta 目錄底下,因為該目錄在儲存小型檔案時最有效率。

ICD 共用資料庫

ICD 共用程式庫應與 Vulkan ICD ABI 相符。ICD 是可執行的共用程式庫,可放置在套件的大部分子目錄 (而非 /bin) 中。

元件資訊清單

Vulkan 載入器提供 icd_runner 執行器,簡化從套件建立 ICD 元件的過程。ICD 套件必須包含可匯出 contentsmetadata 目錄功能的元件資訊清單 .cml

icd_runner 會自動從 /pkg-data/pkg-metadata 路徑的 ICD 套件中匯出 /pkg/data/pkg/meta/metadata 目錄。CML 可使用這些 API 匯出這兩種目錄功能 (使用 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"
}
  • 這個中繼資料版本的「version」必須為 1。
  • file_path 是 ICD 共用資料庫相對於公開 contents 目錄的位置。
  • manifest_pathKhronos ICD 資訊清單 JSON 檔案 (相對於公開的 contents 目錄) 的位置。

其他用戶端

可用的 Vulkan ICD 組合可能會隨著時間變更;當系統首次啟動時,沒有 ICD 可供使用,直到硬體列舉為止。之後裝置可能會接上電源,出現或消失了。

也就是說,vkEnumeratePhysicalDevices 傳回的裝置清單隨時可能變更。部分需要使用 Vulkan 的應用程式,可能會在一組可用裝置變更後重試。並且可以使用從 fuchsia.vulkan.loader/Loader.ConnectToManifestFs 傳回的檔案系統上的檔案系統監控工具,判斷重試的時間。

實作

這項設計代表 Fuchsia 上已實作的 Vulkan 載入器目前的架構。

效能

Vulkan 載入器在程序啟動時最常使用。Vulkan ICD 載入後,會透過 Trampolines 的 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 的開發人員非常實用,但對多數使用者而言不需要。您可以停用完整解析器 (設定 auto_update_packages=false gnarg),停用載入臨時套件。我們也可以為產品擁有者選擇的 Vulkan 載入器建立多個核心資料分割;工程版本可以選擇使用完整解析器的資料分割,使用者建構作業則可將資料分割與基本解析器搭配使用。

如果需要特定產品,可以建立多個 vulkan_loader 服務的執行個體,每個執行個體都具備不同解析器的存取權。其 fuchsia.vulkan.loader.Loader 實作項目可以根據用戶端的安全性需求轉送至用戶端元件。目前並沒有產品設有這項規定。

設定載入器的設定可能會載入新圖層、阻止載入其他圖層,或設定這些圖層上的選項,可能造成應用程式出現非預期行為。元件必須選擇從套件外將設定納入設定 (透過將目錄能力從套件外部轉送),但在其他情況下可完全控制載入器設定。

ICD 共用程式庫會在用戶端程序中執行,且可在該程序中執行任意程式碼。建構程序和一致性測試可確保這些符號只會匯入許可清單中的符號,但無法保證安全,且可能會輕易規避,例如透過呼叫堆疊找出地址,以及剖析記憶體中的執行檔,找出有用的小工具。應用程式不會驗證 Vulkan 傳回的大部分值,也可能藉由謹慎操控這些值來操縱 Vulkan 的記憶體存取行為。

如果執行元件會將其他不信任的元件載入到單一程序 (也許使用虛擬機器或其他非程序機制來隔離元件),這些元件將無法直接發出 Vulkan 呼叫,因為沒有已知的方法驗證 Vulkan API 呼叫,保證應用程式不會在 Vulkan ICD 中執行未定義的行為,而 Vulkan ICD 也只提供有限的驗證層。Runner 程式碼可能會自行發出 Vulkan 呼叫,例如使用 Skia 或 ANGLE ,代表用戶端執行經過驗證的算繪指令。提供給 ICD 的服務和裝置管道必須來自執行元件所信任的來源,以免子項元件彼此無法相互通訊。

隱私權注意事項

Vulkan 載入器幾乎不會影響隱私權。只有應用程式嘗試使用 Vulkan,以及嘗試使用哪些裝置,才會透過 FIDL 公開的資訊。

測試

vulkan_loader 和 libvulkan.so 有單元和整合測試。這些測試十分密封,不需要依賴裝置驅動程式或安裝在系統上的真實 ICD。

此外,我們還進行 CTF 測試,確保 fuchsia.vulkan.loader.Loader 通訊協定的實作正確無誤,且提供的 ICD 與舊版載入器版本相容。

紅杉樹中的 Vulkan CTS 和其他 Vulkan 測試將做為端對端測試,可檢查 vulkan_loader 是否與 libvulkan.so 相容。只能在內含 Vulkan 硬體和裝置驅動程式的系統上執行。

說明文件

我們在 /src/graphics/bin/vulkan_loader/README.md 中加入 vulkan_loader 說明文件。請參閱一些使用者說明文件,瞭解如何使用 Vulkan 載入器。

上游 Vulkan 載入器提供說明文件。我們應嘗試在該文件中加入及上游 Fuchsia 專屬資訊。

先前的圖片和參考資料

Linux/Windows/MacOS 載入器