RenderScript

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 之外的所有同進程 HAL (SP-HAL) 均載入到連結器命名空間sphal內。 RenderScript 被載入到 RenderScript 特定的命名空間rs中,該位置可以稍微寬鬆地執行 RenderScript 函式庫。由於RS實作需要載入編譯後的bitcode,因此將/data/*/*.so加入到rs命名空間的路徑中(其他SP-HAL不允許從data分區載入lib)。

此外, 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編輯的,且驅動程式庫是從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選項指定的,並且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 中的安全增強型 Linux

位元碼的 ABI 相容性

如果沒有新增新的 API,這表示沒有 HAL 版本提升,RS 框架將繼續使用現有的 GPU (HAL 1.0) 驅動程式。

對於不影響位元碼的較小 HAL 變更 (HAL 1.1),框架應回退到 CPU 來處理這些新新增的 API,並在其他地方繼續使用 GPU (HAL 1.0) 驅動程式。

對於影響位碼編譯/連結的主要 HAL 變更 (HAL 2.0),RS 框架應選擇不載入供應商提供的 GPU 驅動程序,而是使用 CPU 或 Vulkan 路徑進行加速。

使用 RenderScript 位元碼分三個階段進行:

階段細節
編譯
  • bcc的輸入位碼 (.bc) 必須採用LLVM 3.2位碼格式,且bcc必須向後相容現有(舊版)應用程式。
  • 然而,.bc 中的元資料可能會發生變化(可能會有新的運行時函數,例如分配 setters ∓ getters、數學函數等)。部分運行時函數位於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呼叫系統 bcc: /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 運算的發展。此棄用的資源資訊包括以下內容: