Renderscript

RenderScript は、Android で演算負荷の高いタスクを優れたパフォーマンスを維持しながら実行するためのフレームワークです。データ並列計算に使用することを目的としていますが、連続したワークロードにもメリットがあります。RenderScript ランタイムは、マルチコアの CPU や GPU などのデバイスで使用できるプロセッサ間で作業を並列化するため、デベロッパーが、作業のスケジュール設定ではなく、アルゴリズムの表現に専念できるようになります。RenderScript は、画像処理、コンピュータ写真処理、コンピュータ ビジョンを実行するアプリケーションに特に有用です。

Android 8.0 以降を搭載するデバイスでは、次の RenderScript フレームワークとベンダー HAL を使用します。

図 1. 内部 lib にリンクするベンダーコード

Android 7.x 以前の RenderScript との違いは次のとおりです。

  • プロセス内に RenderScript 内部 lib の 2 つのインスタンスがあります。1 つのセットは CPU の代替パス用で、/system/lib に直接由来し、もう一方のセットは GPU パス用で、/system/lib/vndk-sp に由来します。
  • /system/lib の RS 内部 lib はプラットフォームの一部として構築され、system.img がアップグレードされると更新されます。ただし、/system/lib/vndk-sp の lib はベンダー用に構築されたもので、system.img がアップグレードされても更新されません(セキュリティ上の修正のために更新されても、ABI は変わりません)。
  • ベンダーコード(RS HAL、RS ドライバ、bcc plugin)は、/system/lib/vndk-sp にある RenderScript の内部 lib に対してリンクされます。/system/lib の lib に対してはリンクできません。このディレクトリ内の lib はプラットフォーム用に構築されており、ベンダーコードと互換性がない可能性があるため、シンボルが削除される可能性があります。リンクすると、フレームワークのみの OTA が不可能になります。

設計

以降のセクションでは、Android 8.0 以降の RenderScript の設計について詳しく説明します。

ベンダーが使用できる RenderScript lib

このセクションでは、ベンダーコードで使用可能で、かつリンクできる RenderScript lib(別名: Vendor NDK for Same-Process HAL または VNDK-SP)について説明します。また、RenderScript とは無関係ですが、同様にベンダーコードに提供される追加のライブラリについても詳しく説明します。

次のライブラリのリストは Android のリリースによって異なる可能性がありますが、Android の特定のリリースでは不変です。使用可能なライブラリの最新のリストは、/system/etc/ld.config.txt を参照してください。

RenderScript Lib RenderScript 以外の Lib
  • 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 にない lib がベンダーコードで使用されないようにするリンク制限は、リンカー名前空間を使用して実行時に適用されます。詳細については、VNDK の設計プレゼンテーションをご覧ください。

Android 8.0 以降を搭載するデバイスでは、RenderScript 以外のすべての Same-Process HAL(SP-HAL)が、リンカー名前空間 sphal 内に読み込まれます。RenderScript は、RenderScript 固有の名前空間 rs に読み込まれ、そこでは RenderScript lib の適用を多少緩くすることが可能です。RS 実装はコンパイルされたビットコードを読み込む必要があるため、/data/*/*.sors 名前空間のパスに追加されます(他の SP-HAL が、データ パーティションから lib を読み込むことはできません)。

さらに、rs の名前空間は、他の名前空間よりも多くの lib を許可します。libRS_internal.so がこれらのライブラリに内部的に依存しているため、libmediandk.solibft2.sors の名前空間に表示されます。

図 2. リンカー名前空間の構成

ドライバの読み込み

CPU の代替パス

RS コンテキストを作成するときの RS_CONTEXT_LOW_LATENCY ビットの存在に応じて、CPU パスまたは GPU パスのいずれかを選択します。CPU パスが選択されている場合、libRS_internal.so(RS フレームワークの主要実装)は、RS lib のプラットフォームのバージョンが提供されるデフォルトのリンカー名前空間から直接 dlopen されます。

CPU 代替パスが使用された場合、ベンダーからの RS HAL 実装はまったく使用されません。RsContext オブジェクトは null mVendorDriverName で作成されます。libRSDriver.so は、デフォルトで dlopen されており、呼び出し元(libRS_internal.so)も default 名前空間で読み込まれるため、ドライバ lib は default 名前空間から読み込まれます。

図 4. CPU の代替パス

GPU パス

GPU パスの場合、libRS_internal.so は異なる読み込みを行います。 まず、libRS.soandroid.hardware.renderscript@1.0.so(およびその基盤となる libhidltransport.so)を使用して android.hardware.renderscript@1.0-impl.so(RS HAL のベンダー実装)を sphal という別のリンカー名前空間に読み込みます。その後、RS HAL は、rs という別のリンカー名前空間の libRS_internal.sodlopen します。

ベンダーは独自の RS ドライバを、RS HAL 実装(hardware/interfaces/renderscript/1.0/default/Context.cpp)に埋め込まれるビルド時間フラグ OVERRIDE_RS_DRIVER を設定することで提供できます。その後、このドライバ名は、GPU パスの RS コンテキスト用に dlopen されます。

RsContext オブジェクトの作成は、RS HAL 実装に委任されます。HAL は、引数として使用するドライバの名前とともに rsContextCreateVendor() 関数を使用する RS フレームワークにコールバックします。次に、RS フレームワークは、RsContext が初期化されるときに指定されたドライバを読み込みます。この場合、RsContext オブジェクトが rs 名前空間内に作成され、/vendor/lib が名前空間の検索パスにあるため、ドライバ ライブラリは、rs 名前空間に読み込まれます。

図 5. GPU 代替パス

default 名前空間から sphal 名前空間に移行する場合、libhidltransport.soandroid_load_sphal_library() 関数を使用してダイナミック リンカーに明示的な指示を行い、sphal 名前空間から -impl.so ライブラリを読み込ませます。

sphal 名前空間から rs 名前空間に移行する場合、/system/etc/ld.config.txt に以下の行を間接的に読み込みます。

namespace.sphal.link.rs.shared_libs = libRS_internal.so

この行は、lib が見つからない、または lib が sphal から読み込めないときに、ダイナミック リンカーが libRS_internal.sors 名前空間から読み込む必要があることを示します(sphal 名前空間は、libRS_internal.so が存在する /system/lib/vndk-sp を検索しないため、これは常に起こります)。この構成では、名前空間を移行するには libRS_internal.so へのシンプルな dlopen() 呼び出しをすれば十分です。

bcc プラグインの読み込み

bcc plugin は、bcc コンパイラに読み込まれるベンダー提供のライブラリです。bcc/system/bin ディレクトリのシステム プロセスなので、bcc plugin ライブラリは SP-HAL(つまり、バインドされることなくシステム プロセスに直接読み込むことができるベンダー HAL)と見なすことができます。SP-HAL としての bcc-plugin ライブラリは以下のようになります。

  • libLLVM.so のような、フレームワークのみのライブラリに対してリンクできません。
  • ベンダーが使用できる VNDK-SP ライブラリにのみリンクできます。

この制限は、android_sphal_load_library() 関数を使用して bcc pluginsphal 名前空間に読み込むことで適用されます。以前のバージョンの 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 ランタイム lib がリンカーへの入力として与えられます。アプリからの RS ビットコードがランタイム lib に対してリンクされ、変換されたビットコードがアプリプロセスに読み込まれると、ランタイム lib は変換されたビットコードから再び動的にリンクされます。

ランタイム lib には次のようなものがあります。

  • libcompiler_rt.so
  • libm.so
  • libc.so
  • RS ドライバ(libRSDriver.so または OVERRIDE_RS_DRIVER

コンパイルされたビットコードをアプリプロセスに読み込む場合、ld.mc で使用されたのとまったく同じライブラリを提供します。そうでない場合、コンパイルされたビットコードは、リンク時に使用可能だったシンボルを検出できません。

そのために、RS フレームワークは、RS フレームワークそのものが /system/lib または /system/lib/vndk-sp から読み込まれているかどうかによって、ld.mc を実行するときにランタイム lib の別の検索パスを使用します。これは、RS フレームワーク lib の任意のシンボルのアドレスを読み取り、dladdr() を使用してアドレスにマッピングされたファイルパスを取得することで決定できます。

SELinux ポリシー

Android 8.0 以降で SELinux ポリシーが変更されたため、vendor パーティションで追加ファイルにラベルを付ける場合は、以下の特定のルール(neverallows を通じて適用)に従う必要があります。

  • vendor パーティション内のすべてのファイルで、vendor_file をデフォルトのラベルにする必要があります。プラットフォーム ポリシーでは、これがパススルー 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 のビットコードは次の 3 つのステージで使用されます。

ステージ 詳細
コンパイル
  • bcc の入力ビットコード(.bc)は、LLVM 3.2 ビットコード形式である必要があり、bcc は既存(レガシー)アプリとの下位互換性が必要です。
  • ただし、.bc のメタデータは変更できます。たとえば、Allocation setters ∓ getters、数学関数などの新しいランタイム関数があります。ランタイム関数の一部は、libclcore.bc に存在し、その一部は LibRSDriver またはベンダーに相当する場所に存在します。
  • 新しいランタイム関数や壊れているメタデータを変更するには、bitcode 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 ビットコードは、次の 2 つの方法でコンパイルできます。

  1. /vendor/bin/ にあるベンダー固有の RenderScript コンパイラを起動します(GPU コンパイルにおすすめの方法です)。他のドライバ モジュールと同様、ベンダーのコンパイラ バイナリは、ベンダーが使用できる RenderScript lib のリストにないシステム ライブラリには依存できません。
  2. ベンダー提供の bcc plugin を使用したシステム bcc(/system/bin/bcc)を起動します。このプラグインは、ベンダーが使用できる RenderScript lib のリストにないシステム ライブラリには依存できません。

ベンダー bcc plugin が CPU コンパイルに干渉する必要があり、libLLVM.so に対するその依存関係を簡単に削除できない場合、ベンダーは bcc(および libLLVM.solibbcc.so を含む LL-NDK 以外のすべての依存関係)を /vendor パーティションにコピーする必要がああります。

さらに、ベンダーは次の変更を行う必要があります。

図 8. ベンダー ドライバの変更
  1. libclcore.bc/vendor パーティションにコピーします。これにより、libclcore.bclibLLVM.solibbcc.so が確実に同期されます。
  2. RS HAL 実装から RsdCpuScriptImpl::BCC_EXE_PATH を設定して、bcc へのパスを実行可能ファイルに変更します。

SELinux ポリシー

SELinux ポリシーは、ドライバとコンパイラ両方の実行可能ファイルに影響します。すべてのドライバ モジュールは、デバイスの file_contextssame_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 計算の今後をご覧ください。このサポート終了に関するリソース情報は次のとおりです。