RenderScript是一個框架,用於在 Android 上以高性能運行計算密集型任務。它設計用於數據並行計算,儘管串行工作負載也可以從中受益。 RenderScript 運行時跨設備上可用的處理器(例如多核 CPU 和 GPU)並行工作,使開發人員能夠專注於表達算法而不是調度工作。 RenderScript 對於執行圖像處理、計算攝影或計算機視覺的應用程序特別有用。
運行 Android 8.0 及更高版本的設備使用以下 RenderScript 框架和供應商 HAL:
與 Android 7.x 及更低版本中的 RenderScript 的區別包括:
- 一個進程中的兩個 RenderScript 內部庫實例。一組用於 CPU 回退路徑,直接來自
/system/lib
;另一組用於 GPU 路徑,來自/system/lib/vndk-sp
。 -
/system/lib
中的 RS 內部庫是作為平台的一部分構建的,並隨著system.img
的升級而更新。但是,/system/lib/vndk-sp
中的庫是為供應商構建的,並且在升級system.img
時不會更新(雖然可以更新它們以進行安全修復,但它們的 ABI 保持不變)。 - 供應商代碼(RS HAL、RS 驅動程序和
bcc plugin
)與位於/system/lib/vndk-sp
的 RenderScript 內部庫鏈接。它們無法鏈接到/system/lib
中的庫,因為該目錄中的庫是為平台構建的,因此可能與供應商代碼不兼容(即,可能會刪除符號)。這樣做會使僅框架的 OTA 成為不可能。
設計
以下部分詳細介紹了 Android 8.0 及更高版本中的 RenderScript 設計。
供應商可用的 RenderScript 庫
本部分列出了可用於供應商代碼並且可以鏈接的 RenderScript 庫(稱為相同進程 HAL 的供應商 NDK 或 VNDK-SP)。它還詳細介紹了與 RenderScript 無關但也提供給供應商代碼的其他庫。
雖然以下庫列表可能因 Android 版本而異,但對於特定的 Android 版本是不可變的;有關可用庫的最新列表,請參閱/system/etc/ld.config.txt
。
渲染腳本庫 | 非 RenderScript 庫 |
---|---|
|
|
鏈接器命名空間配置
阻止不在 VNDK-SP 中的庫被供應商代碼使用的鏈接限制在運行時使用鏈接器命名空間強制執行。 (有關詳細信息,請參閱VNDK 設計演示。)
在運行 Android 8.0 及更高版本的設備上,除 RenderScript 之外的所有 Same-Process HAL (SP-HAL) 都加載到鏈接器命名空間sphal
中。 RenderScript 被加載到特定於 RenderScript 的命名空間rs
中,該位置可以稍微寬鬆地執行 RenderScript 庫。因為 RS 實現需要加載編譯後的 bitcode, /data/*/*.so
被添加到rs
命名空間的路徑中(其他 SP-HAL 不允許從數據分區加載 libs)。
此外, rs
命名空間允許的庫比其他命名空間提供的要多。 libmediandk.so
和libft2.so
暴露於rs
命名空間,因為libRS_internal.so
對這些庫具有內部依賴關係。
加載驅動程序
CPU 回退路徑
根據創建 RS 上下文時是否存在RS_CONTEXT_LOW_LATENCY
位,選擇 CPU 或 GPU 路徑。選擇 CPU 路徑時, libRS_internal.so
(RS 框架的主要實現)直接從提供平台版本 RS 庫的默認鏈接器命名空間dlopen
編輯。
當採用 CPU 回退路徑時,根本不使用供應商提供的 RS HAL 實現,並使用 null mVendorDriverName
創建一個RsContext
對象。 libRSDriver.so
(默認情況下)是dlopen
ed,並且驅動程序庫是從default
命名空間加載的,因為調用者( libRS_internal.so
)也在default
命名空間中加載。
GPU 路徑
對於 GPU 路徑, libRS_internal.so
的加載方式不同。首先, libRS.so
使用android.hardware.renderscript@1.0.so
(及其底層libhidltransport.so
)將android.hardware.renderscript@1.0-impl.so
(RS HAL 的供應商實現)加載到名為sphal
然後 RS HAL 在另一個名為rs
的鏈接器命名空間中dlopen
s libRS_internal.so
。
供應商可以通過設置構建時間標誌OVERRIDE_RS_DRIVER
來提供自己的 RS 驅動程序,該標誌嵌入到 RS HAL 實現中( hardware/interfaces/renderscript/1.0/default/Context.cpp
)。然後,此驅動程序名稱會被dlopen
用於 GPU 路徑的 RS 上下文。
RsContext
對象的創建委託給 RS HAL 實現。 HAL 使用rsContextCreateVendor()
函數回調 RS 框架,並將驅動程序的名稱用作參數。然後,RS 框架會在RsContext
初始化時加載指定的驅動程序。在這種情況下,驅動程序庫被加載到rs
命名空間中,因為RsContext
對像是在rs
命名空間內創建的,並且/vendor/lib
位於命名空間的搜索路徑中。
從default
命名空間轉換到sphal
命名空間時, libhidltransport.so
使用android_load_sphal_library()
函數顯式命令動態鏈接器從sphal
命名空間加載-impl.so
庫。
從sphal
命名空間轉換到rs
命名空間時,加載是通過/system/etc/ld.config.txt
中的以下行間接完成的:
namespace.sphal.link.rs.shared_libs = libRS_internal.so
此行指定當無法從sphal
命名空間找到/加載 lib 時,動態鏈接器應從rs
命名空間加載libRS_internal.so
(總是如此,因為sphal
命名空間不搜索/system/lib/vndk-sp
其中libRS_internal.so
駐留)。使用此配置,對libRS_internal.so
的簡單dlopen()
調用就足以進行命名空間轉換。
加載密件抄送插件
bcc plugin
是加載到bcc
編譯器中的供應商提供的庫。因為bcc
是/system/bin
目錄下的系統進程,所以bcc plugin
庫可以被認為是一個 SP-HAL(即無需綁定即可直接加載到系統進程中的供應商 HAL)。作為 SP-HAL, bcc-plugin
庫:
- 無法鏈接到僅限框架的庫,例如
libLLVM.so
。 - 只能鏈接到供應商可用的 VNDK-SP 庫。
通過使用android_sphal_load_library()
函數將bcc plugin
加載到sphal
命名空間來強制執行此限制。在以前的 Android 版本中,插件名稱是使用-load
選項指定的,而 lib 是使用libLLVM.so
的簡單dlopen()
加載的。在 Android 8.0 及更高版本中,這是在-plugin
選項中指定的,lib 由bcc
本身直接加載。此選項啟用開源 LLVM 項目的非 Android 特定路徑。
ld.mc 的搜索路徑
執行ld.mc
時,一些 RS 運行時庫作為鏈接器的輸入提供。來自應用程序的 RS 位碼與運行時庫鏈接,當轉換後的位碼加載到應用程序進程中時,運行時庫再次與轉換後的位碼動態鏈接。
運行時庫包括:
-
libcompiler_rt.so
-
libm.so
-
libc.so
- RS 驅動程序(
libRSDriver.so
或OVERRIDE_RS_DRIVER
)
將編譯後的位碼加載到應用程序進程中時,提供與ld.mc
使用的完全相同的庫。否則,編譯的位碼可能找不到鏈接時可用的符號。
為此,RS 框架在執行ld.mc
時對運行時庫使用不同的搜索路徑,具體取決於 RS 框架本身是從/system/lib
還是從/system/lib/vndk-sp
。這可以通過讀取 RS 框架庫的任意符號的地址並使用dladdr()
來獲取映射到該地址的文件路徑來確定。
SELinux 政策
由於 Android 8.0 及更高版本中的 SELinux 策略更改,在vendor
分區中標記其他文件時,您必須遵循特定規則(通過neverallows
強制執行):
-
vendor_file
必須是vendor
分區中所有文件的默認標籤。平台策略要求它訪問直通 HAL 實現。 - 通過供應商 SEPolicy 在
vendor
分區中添加的所有新exec_types
都必須具有vendor_file_type
屬性。這是通過neverallows
強制執行的。 - 為避免與未來的平台/框架更新發生衝突,請避免在
vendor
分區中標記除exec_types
以外的文件。 - AOSP 識別的相同進程 HAL 的所有庫依賴項必須標記為
same_process_hal_file
。
有關 SELinux 策略的詳細信息,請參閱Android 中的 Security-Enhanced Linux 。
位碼的 ABI 兼容性
如果沒有添加新的 API,這意味著沒有 HAL 版本增加,RS 框架將繼續使用現有的 GPU (HAL 1.0) 驅動程序。
對於不影響位碼的微小 HAL 更改 (HAL 1.1),框架應為這些新添加的 API 回退到 CPU,並在其他地方繼續使用 GPU (HAL 1.0) 驅動程序。
對於影響位碼編譯/鏈接的主要 HAL 更改 (HAL 2.0),RS 框架應選擇不加載供應商提供的 GPU 驅動程序,而是使用 CPU 或 Vulkan 路徑進行加速。
使用 RenderScript 位碼分為三個階段:
階段 | 細節 |
---|---|
編譯 |
|
關聯 |
|
加載 |
|
除了 HAL,運行時 API 和導出的符號也是接口。自 Android 7.0 (API 24) 以來,這兩個接口都沒有更改,並且沒有立即計劃在 Android 8.0 及更高版本中更改它。但是,如果接口發生變化,HAL 版本也會增加。
供應商實施
Android 8.0 及更高版本需要對 GPU 驅動程序進行一些更改才能使 GPU 驅動程序正常工作。
驅動模塊
- 驅動程序模塊不能依賴任何不在列表中的系統庫。
- 驅動程序必須提供自己的
android.hardware.renderscript@1.0-impl_{NAME}
,或者聲明默認實現android.hardware.renderscript@1.0-impl
作為其依賴項。 - CPU 實現
libRSDriver.so
是一個很好的示例,說明瞭如何刪除非 VNDK-SP 依賴項。
位碼編譯器
您可以通過兩種方式為供應商驅動程序編譯 RenderScript 位代碼:
- 在
/vendor/bin/
中調用供應商特定的 RenderScript 編譯器(GPU 編譯的首選方法)。與其他驅動程序模塊類似,供應商編譯器二進製文件不能依賴任何不在供應商可用的 RenderScript 庫列表中的系統庫。 - 使用供應商提供的
bcc plugin
調用系統密件抄送:/system/bin/bcc
;此插件不能依賴任何不在供應商可用的 RenderScript 庫列表中的系統庫。
如果供應商bcc plugin
需要干擾 CPU 編譯並且無法輕鬆刪除其對libLLVM.so
的依賴,則供應商應將bcc
(以及所有非 LL-NDK 依賴項,包括libLLVM.so
、 libbcc.so
)複製到/vendor
分區。
此外,供應商需要進行以下更改:
- 將
libclcore.bc
複製到/vendor
分區。這可確保libclcore.bc
、libLLVM.so
和libbcc.so
同步。 - 通過從 RS HAL 實現設置
RsdCpuScriptImpl::BCC_EXE_PATH
來更改bcc
可執行文件的路徑。
SELinux 政策
SELinux 策略同時影響驅動程序和編譯器可執行文件。所有驅動程序模塊必須在設備的file_contexts
中標記為same_process_hal_file
。例如:
/vendor/lib(64)?/libRSDriver_EXAMPLE\.so u:object_r:same_process_hal_file:s0
編譯器可執行文件必須能夠被應用程序進程調用,bcc 的供應商副本( /vendor/bin/bcc
)也是如此。例如:
device/vendor_foo/device_bar/sepolicy/file_contexts: /vendor/bin/bcc u:object_r:same_process_hal_file:s0
舊設備
傳統設備是那些滿足以下條件的設備:
- PRODUCT_SHIPPING_API_LEVEL低於 26。
- PRODUCT_FULL_TREBLE_OVERRIDE未定義。
對於舊設備,升級到 Android 8.0 及更高版本時不會強制執行這些限制,這意味著驅動程序可以繼續鏈接到/system/lib[64]
中的庫。但是,由於與OVERRIDE_RS_DRIVER
相關的架構更改,必須將android.hardware.renderscript@1.0-impl
安裝到/vendor
分區;不這樣做會強制 RenderScript 運行時回退到 CPU 路徑。
有關 Renderscript 棄用動機的信息,請參閱 Android 開發者博客: Android GPU Compute Going Forward 。此棄用的資源信息包括以下內容: