渲染腳本

RenderScript是一個框架,用於在 Android 上以高性能運行計算密集型任務。它設計用於數據並行計算,儘管串行工作負載也可以從中受益。 RenderScript 運行時跨設備上可用的處理器(例如多核 CPU 和 GPU)並行工作,使開發人員能夠專注於表達算法而不是調度工作。 RenderScript 對於執行圖像處理、計算攝影或計算機視覺的應用程序特別有用。

運行 Android 8.0 及更高版本的設備使用以下 RenderScript 框架和供應商 HAL:

圖 1.鏈接到內部庫的供應商代碼

與 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 庫
  • android.hardware.graphics.renderscript@1.0.so
  • libRS_internal.so
  • libRSCpuRef.so
  • libblas.so
  • libbcinfo.so
  • libcompiler_rt.so
  • libRSDriver.so
  • libc.so
  • libm.so
  • libdl.so
  • libstdc++.so
  • liblog.so
  • libnativewindow.so
  • libsync.so
  • libvndksupport.so
  • libbase.so
  • libc++.so
  • libcutils.so
  • libutils.so
  • libhardware.so
  • libhidlbase.so
  • libhidltransport.so
  • libhwbinder.so
  • liblzma.so
  • libz.so
  • libEGL.so
  • libGLESv1_CM.so
  • libGLESv2.so

鏈接器命名空間配置

阻止不在 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.solibft2.so暴露於rs命名空間,因為libRS_internal.so對這些庫具有內部依賴關係。

圖 2.鏈接器的命名空間配置

加載驅動程序

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命名空間中加載。

圖 4. CPU 回退路徑

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位於命名空間的搜索路徑中。

圖 5. GPU 回退路徑

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 特定路徑。

圖 6.加載 bcc 插件,Android 7.x 及更低版本


圖 7.加載 bcc 插件,Android 8.0 及更高版本

ld.mc 的搜索路徑

執行ld.mc時,一些 RS 運行時庫作為鏈接器的輸入提供。來自應用程序的 RS 位碼與運行時庫鏈接,當轉換後的位碼加載到應用程序進程中時,運行時庫再次與轉換後的位碼動態鏈接。

運行時庫包括:

  • libcompiler_rt.so
  • libm.so
  • libc.so
  • RS 驅動程序( libRSDriver.soOVERRIDE_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 位碼分為三個階段:

階段細節
編譯
  • bcc的輸入位碼 (.bc) 必須採用LLVM 3.2位碼格式,並且bcc必須向後兼容現有(舊版)應用程序。
  • 但是,.bc 中的元數據可能會發生變化(可能會有新的運行時函數,例如分配設置器∓ 獲取器、數學函數等)。部分運行時函數位於libclcore.bc中,其中一部分位於 LibRSDriver 或供應商等效項中。
  • 新的運行時函數或破壞元數據更改需要增加位碼 API 級別。因為供應商驅動程序無法使用它,所以也必須增加 HAL 版本。
  • 供應商可能有自己的編譯器,但bcc的結論/要求也適用於這些編譯器。
關聯
  • 編譯後的 .o 將與供應商驅動程序鏈接,例如libRSDriver_foo.solibcompiler_rt.so 。 CPU 路徑將與libRSDriver.so鏈接。
  • 如果 .o 需要來自libRSDriver_foo的新運行時 API,則必須更新供應商驅動程序以支持它。
  • 某些供應商可能有自己的鏈接器,但ld.mc的論點也適用於它們。
加載
  • libRSCpuRef加載共享對象。如果此接口有更改,則需要 HAL 版本凸點。
  • 供應商要么依賴libRSCpuRef加載共享對象,要么實現自己的。

除了 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 位代碼:

  1. /vendor/bin/中調用供應商特定的 RenderScript 編譯器(GPU 編譯的首選方法)。與其他驅動程序模塊類似,供應商編譯器二進製文件不能依賴任何不在供應商可用的 RenderScript 庫列表中的系統庫。
  2. 使用供應商提供的bcc plugin調用系統密件抄送: /system/bin/bcc ;此插件不能依賴任何不在供應商可用的 RenderScript 庫列表中的系統庫。

如果供應商bcc plugin需要干擾 CPU 編譯並且無法輕鬆刪除其對libLLVM.so的依賴,則供應商應將bcc (以及所有非 LL-NDK 依賴項,包括libLLVM.solibbcc.so )複製到/vendor分區。

此外,供應商需要進行以下更改:

圖 8.對供應商驅動程序的更改
  1. libclcore.bc複製到/vendor分區。這可確保libclcore.bclibLLVM.solibbcc.so同步。
  2. 通過從 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

舊設備

傳統設備是那些滿足以下條件的設備:

  1. PRODUCT_SHIPPING_API_LEVEL低於 26。
  2. 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 。此棄用的資源信息包括以下內容: