RenderScript からの移行

Android 12 以降、RenderScript API は非推奨となります。デバイス メーカーとコンポーネント メーカーはすでにハードウェア アクセラレーションのサポートの提供を終了しており、RenderScript のサポートは今後のリリースで完全に終了する予定です。

多くのユースケースでは、C / C++ のパフォーマンスで十分です。組み込み関数について RenderScript のみに依存している場合は、これらの使用を RenderScript 組み込み関数置換ツールキットで置き換えることができます。ツールキットは使いやすく、パフォーマンスを 2 倍向上させる可能性があります。

GPU アクセラレーションを最大限に活用する必要がある場合は、スクリプトを Vulkan に移行することをおすすめします。その他の高速化オプションとしては、スクリプトを OpenGL に移行する方法、キャンバス ベースの画像オペレーション、または Android グラフィック シェーディング言語(AGSL)の利用などがあります。

Android プラットフォームの RenderScript のサポート終了に伴い、Android Gradle プラグインの RenderScript のサポートも終了します。Android Gradle プラグイン 7.2 以降、RenderScript API のサポートは終了しています。RenderScript API は引き続き機能しますが、警告がトリガーされます。AGP の今後のバージョンでは、RenderScript のサポートが含まれなくなります。このガイドでは、RenderScript からの移行方法について説明します。

組み込み関数からの移行

RenderScript の組み込み関数は RenderScript のサポート終了後も引き続き機能しますが、実行できるのは GPU ではなく CPU に対してのみの可能性があります。

一部のオペレーションについては、より効率的なオプションがプラットフォームまたは Jetpack ライブラリに組み込まれました。

組み込みの高速画像オペレーション

Android プラットフォームは、RenderScript 組み込み関数とは無関係に、画像に適用できる高速画像処理オペレーションをサポートしています。次に例を示します。

  • ブレンド
  • ぼかし
  • カラー マトリックス
  • サイズ変更

Android 12 以降のビューへの画像ぼかし

Android 12(API レベル 31)では、ぼかしをサポートする RenderEffect が追加され、RenderNode にぼかしを入れることができるようになりました。RenderNode は、Android がプラットフォーム グラフィックを高速化する際に使用するディスプレイ リストの構成です。

Android には、View に関連付けられた RenderNode に効果を適用するショートカットが用意されています。View にぼかしを入れるには、View.setRenderEffect() を呼び出します。

val blurRenderEffect = RenderEffect.createBlurEffect(radius, radius,
        Shader.TileMode.MIRROR
    )
view.setRenderEffect(blurRenderEffect)

Android 12 以降のビットマップにレンダリングされた画像ぼかし

ぼかしを入れた画像を Bitmap にレンダリングする場合、フレームワークは HardwareBuffer に基づく HardwareRenderer での高速レンダリングをサポートしています。次のコードは、ぼかしの HardwareRendererRenderNodeRenderEffect を作成します。

val imageReader = ImageReader.newInstance(
    bitmap.width, bitmap.height,
    PixelFormat.RGBA_8888, numberOfOutputImages,
    HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT
)
val renderNode = RenderNode("BlurEffect")
val hardwareRenderer = HardwareRenderer()

hardwareRenderer.setSurface(imageReader.surface)
hardwareRenderer.setContentRoot(renderNode)
renderNode.setPosition(0, 0, imageReader.width, imageReader.height)
val blurRenderEffect = RenderEffect.createBlurEffect(
    radius, radius,
    Shader.TileMode.MIRROR
)
renderNode.setRenderEffect(blurRenderEffect)

効果を適用するには、RenderNode の内部 RecordingCanvas を使用します。次のコードは、図形描画を記録してレンダリング リクエストを作成し、リクエストが完了するまで待ちます。

val renderCanvas = it.renderNode.beginRecording()
renderCanvas.drawBitmap(it.bitmap, 0f, 0f, null)
renderNode.endRecording()
hardwareRenderer.createRenderRequest()
    .setWaitForPresent(true)
    .syncAndDraw()

レンダリングされた画像は、ImageReader に関連付けられた HardwareBuffer 内にあります。次のコードは、Image を取得し、その HardwareBuffer をラップする Bitmap を返します。

val image = imageReader.acquireNextImage() ?: throw RuntimeException("No Image")
val hardwareBuffer = image.hardwareBuffer ?: throw RuntimeException("No HardwareBuffer")
val bitmap = Bitmap.wrapHardwareBuffer(hardwareBuffer, null)
    ?: throw RuntimeException("Create Bitmap Failed")

次のコードは、画像のレンダリング後にクリーンアップします。ImageReaderRenderNodeRenderEffectHardwareRenderer を使用して複数の画像を処理できます。

hardwareBuffer.close()
image.close()
imageReader.close()
renderNode.discardDisplayList()
hardwareRenderer.destroy()

画像処理のための AGSL

Android 13 以降では、Android グラフィック シェーディング言語(AGSL)を使用して、プログラム可能な RuntimeShader オブジェクトの動作を定義します。AGSL の構文の多くは GLSL フラグメント シェーダーと同じですが、Android グラフィック レンダリング システム内で動作して、Canvas 内の描画のカスタマイズと View コンテンツのフィルタリングを行います。これを使用して図形描画オペレーション中にカスタム画像処理を追加できます。また、RenderNode を直接使用して画像を Bitmap キャンバスにレンダリングすることもできます。次の例は、カスタム シェーダーを適用して画像のぼかし効果を置き換える方法を示しています。

まず、RuntimeShader を作成し、AGSL シェーダー コードでインスタンス化します。次のシェーダーは、色相回転のカラー マトリックスを適用するために使用されます。

val hueShader = RuntimeShader("""
    uniform float2 iResolution;       // Viewport resolution (pixels)
    uniform float2 iImageResolution;  // iImage1 resolution (pixels)
    uniform float iRadian;            // radian to rotate things around
    uniform shader iImage1;           // An input image
    half4 main(float2 fragCoord) {
    float cosR = cos(iRadian);
    float sinR = sin(iRadian);
        mat4 hueRotation =
        mat4 (
                0.299 + 0.701 * cosR + 0.168 * sinR, //0
                0.587 - 0.587 * cosR + 0.330 * sinR, //1
                0.114 - 0.114 * cosR - 0.497 * sinR, //2
                0.0,                                 //3
                0.299 - 0.299 * cosR - 0.328 * sinR, //4
                0.587 + 0.413 * cosR + 0.035 * sinR, //5
                0.114 - 0.114 * cosR + 0.292 * sinR, //6
                0.0,                                 //7
                0.299 - 0.300 * cosR + 1.25 * sinR,  //8
                0.587 - 0.588 * cosR - 1.05 * sinR,  //9
                0.114 + 0.886 * cosR - 0.203 * sinR, //10
                0.0,                                 //11
                0.0, 0.0, 0.0, 1.0 );                //12,13,14,15
        float2 scale = iImageResolution.xy / iResolution.xy;
        return iImage1.eval(fragCoord * scale)*hueRotation;
    }
""")

シェーダーは、他の RenderEffect と同様に RenderNode に適用できます。次の例は、hueShader でユニフォームを設定する方法を示しています。

hueShader.setFloatUniform("iImageResolution", bitmap.width.toFloat(),
    bitmap.height.toFloat())
hueShader.setFloatUniform("iResolution", bitmap.width.toFloat(),
    bitmap.height.toFloat())
hueShader.setFloatUniform("iRadian", radian)
hueShader.setInputShader( "iImage1", BitmapShader(bitmap, Shader.TileMode.MIRROR,
    Shader.TileMode.MIRROR))
val colorFilterEffect = RenderEffect.createShaderEffect(it.hueShader)
renderNode.setRenderEffect(colorFilterEffect)

Bitmap を取得するには、前述の画像ぼかしのサンプルと同じ手法を使用します。

  • RenderNode の内部 RecordingCanvas でシェーダーを適用します。
  • Image が取得され、その HardwareBuffer をラップする Bitmap が返されます。

CameraX を使用した平面 YUV から RGB への変換

画像処理で使用するための平面 YUV から RGB への変換は、Jetpack の CameraX 内の ImageAnalysis ユースケースの一部としてサポートされています。

ImageAnalysis の使用に関するリソースは、CameraX のスタートガイド Codelab の一部として、および Android カメラのサンプル リポジトリにあります。

RenderScript 組み込み関数置換ツールキット

アプリケーションで組み込み関数を使用する場合、スタンドアロンの代替ライブラリを使用できます。既存の RenderScript CPU 実装を使用するより高速であることがテストで確認されています。

ツールキットには次の関数が含まれています。

  • ブレンド
  • ぼかし
  • カラー マトリックス
  • 積和演算
  • ヒストグラムと histogramDot
  • ルックアップ テーブル(LUT)と LUT 3D
  • サイズ変更
  • YUV から RGB

詳細と制限事項については、ツールキットの README.md ファイルと Toolkit.kt. ファイルをご覧ください。

ライブラリをダウンロード、追加、使用するには、次の手順を実施します。

  1. GitHub からプロジェクトをダウンロードします。

  2. renderscript-toolkit module を見つけてビルドします。

  3. アプリの build.gradle ファイルを変更して、Android Studio プロジェクトにライブラリを追加します。

  4. ツールキットの適切なメソッドを呼び出します。

例: ScriptIntrinsicBlur 関数から移行する

ScriptIntrinsicBlur 関数を置き換える手順は次のとおりです。

  • ビットマップのぼかし処理のために Toolkit.blur を呼び出します。

    var blurredBitmap = Toolkit.blur(myBitmap, radius)
    
  • バイトの配列で表現された画像のぼかし処理では、幅、高さ、ピクセルあたりのバイト数を指定します。

    val outArray = Toolkit.blur(inputArray, bytesPerPixel, width, height, radius)
    

スクリプトからの移行

以下の方法でユースケースを解決できない場合:

GPU アクセラレーションを利用できるユースケースの場合は、Android は、クロス プラットフォームの Vulkan API と OpenGL ES(GLES)API での GPU コンピューティングをサポートしています。ただし、ほとんどのデバイスでは、スクリプトは GPU ではなく CPU ですでに実行されているため、移行は不要かもしれません。ユースケースによっては、C / C++ は RenderScript、GLES、Vulkan による処理よりも速くなります(少なくともユースケースに十分な速度)。

移行方法を理解するには、サンプルアプリをご覧ください。このサンプルでは、RenderScript でビットマップのぼかし処理とカラー マトリックス変換を行う方法と、Vulkan と OpenGL を使用して同等のコードを作成する方法を説明します。

アプリでさまざまなリリースをサポートする必要がある場合は、Android 6(API レベル 23)以前を搭載したデバイスには RenderScript を使用し、Android 7(API レベル 24)以降を搭載したサポート デバイスでは Vulkan または GLES を使用します。minSdkVersion が 24 以上の場合は、RenderScript を使用する必要はありません。GPU コンピューティングのサポートが必要な場合はいつでも Vulkan または GLES 3.1 を使用できます。

Android は GLES API 用の SDK バインディングを提供しているため、OpenGL ES で作業する場合は NDK を使用する必要はありません。

Vulkan は SDK バインディングを提供していないため、RenderScript から Vulkan へは直接マッピングされません。NDK を使用して Vulkan コードを記述し、Kotlin または Java からこのコードにアクセスする JNI 関数を作成します。

以降のページでは、RenderScript からの移行のさまざまな側面について説明します。サンプルアプリでは、これらの考慮事項をほぼすべてカバーしています。これらの考慮事項を理解するには、RenderScript と Vulkan の対応するコードを比較します。