RFC-0205:Vulkan 載入器 | |
---|---|
狀態 | 已接受 |
區域 |
|
說明 | 說明應用程式如何載入 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 VMOs 並將其轉移至 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 和層級組合。圖層會從元件的命名空間載入,因此通常會儲存在套件中。只要載入器設定設為使用這些目錄,這些資源也可能會從提供給元件的目錄功能載入。
植入式去顫器載入
在啟動時,libvulkan.so
會連線至 fuchsia.vulkan.loader.Loader 通訊協定。這個管道必須在應用程式生命週期內保持連線狀態。如果退出,所有日後的載入器呼叫都可能失敗。
這個長效連線可避免元件架構在使用 Vulkan 的用戶端執行時重新載入或更新載入器。這麼做是因為這可避免在應用程式使用載入器 API 呼叫時,意外變更 Vulkan ICD 和載入器介面的版本。用於列舉擴充功能或其他例項屬性的某些 Vulkan 進入點不會採用任何類型的context 引數;因此,實作會具有某些隱含的全域狀態。
系統會使用 fuchsia.vulkan.loader.Loader
通訊協定載入 ICD。載入器會使用 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 專屬的擴充功能,如下所述。
用戶端元件也可以與 SwiftShader 等軟體 ICD 實作項目一起封裝。以 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 圖形活動,因為該元件可能會窺探所有 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
會為用戶端提供多個檔案系統,包括 manifest fs 和 device fs。它會根據透過 devfs 接收的多個 ICD 套件和服務內容建立這些檔案系統。因此,必須使用檔案系統服務程式庫建構,且不會反映磁碟上的任何內容。
- manifest fs:所有資訊清單 JSON 檔案,說明所有相關的 ICD;這個檔案系統與 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
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"
}
- 對於這個中繼資料版本,
version
必須為 1。 file_path
是相對於公開contents
目錄的 ICD 共用資料庫位置。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
通訊協定上插入內容,因此系統無法保證載入器服務元件所看到的內容是由系統提供。
系統會根據 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 驗證層也只能提供有限的保護。Runner 程式碼可能會讓 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 硬體和裝置驅動程式的系統上執行。
說明文件
vulkan_loader
說明文件位於 /src/graphics/bin/vulkan_loader/README.md。使用者說明文件說明如何使用 Vulkan 載入器。
上游 Vulkan 載入器有說明文件。我們應該嘗試在該文件中新增並上游傳輸 Fuchsia 專屬資訊。