高度な RenderScript

RenderScript を使用するアプリケーションは引き続き Android VM 内で実行されるため、使い慣れたすべてのフレームワーク API にアクセスできますが、必要に応じて RenderScript を使用できます。フレームワークと RenderScript ランタイム間のこのやり取りを円滑にするために、2 つのコードレベル間の通信とメモリの管理を容易にするコードの中間層も用意されています。 このドキュメントでは、Android VM と RenderScript ランタイム間のメモリの共有方法と、異なるコードレイヤについて詳しく説明します。

RenderScript ランタイム レイヤ

RenderScript コードは、コンパクトで明確なランタイム レイヤでコンパイル、実行されます。RenderScript ランタイム API はポータビリティに優れ、プロセッサで利用できるコアの量に応じて自動的に拡張されるため、大量の計算に対応できます。

注: NDK の標準 C 関数は必ず CPU で実行する必要がある一方、RenderScript は異なるタイプのプロセッサで実行されるため、これらのライブラリにはアクセスできません。

Android プロジェクトの src/ ディレクトリの .rs.rsh ファイルで RenderScript コードを定義します。コードは、Android ビルドの一部として実行される llvm コンパイラによって、中間バイトコードにコンパイルされます。アプリケーションがデバイスで実行されると、バイトコードは別の llvm コンパイラによってマシンコードにコンパイルされます(実行時コンパイル)。マシンコードはデバイス用に最適化され、さらにキャッシュされるため、その後 RenderScript 対応アプリケーションを使用する際には、バイトコードは再コンパイルされません。

RenderScript ランタイム ライブラリの主な機能には、次のものがあります。

  • メモリ割り当てのリクエスト機能
  • 数式関数の大規模なコレクション。スカラー型とベクトル型のオーバーロードされた一般的なルーチンが多数用意されています。算術演算と比較関数の他にも加算、乗算、ドット積、クロス積などの演算を使用できます。
  • プリミティブ データ型、ベクトル、行列ルーチン、日時ルーチンを対象とする変換ルーチン
  • 2、3、または 4 つのベクトルを定義する Vector 型などの RenderScript システムをサポートするデータ型と構造。
  • ロギング関数

使用できる関数について詳しくは、RenderScript ランタイム API リファレンスをご覧ください。

リフレクション レイヤ

リフレクション レイヤは、Android フレームワークから RenderScript ランタイムへのアクセスを許可するために Android ビルドツールが生成する一連のクラスです。このレイヤには、RenderScript コードで定義されたポインタのメモリを割り当てて操作できるメソッドとコンストラクタもあります。以下のリストに、反映される主なコンポーネントを示します。

  • 作成したすべての .rs ファイルは、タイプ ScriptCproject_root/gen/package/name/ScriptC_renderscript_filename という名前のクラスに生成されます。このファイルは、.rs ファイルの .java バージョンであり、Android フレームワークから呼び出すことができます。このクラスには、.rs ファイルから反映された次のアイテムが格納されています。
    • 非静的関数
    • 非静的なグローバル RenderScript 変数。変数ごとにアクセサ メソッドが生成されるため、RenderScript 変数は Android フレームワークから読み書きできます。グローバル変数が RenderScript ランタイム レイヤで初期化される場合、これらの値は Android フレームワーク レイヤの対応する値を初期化するために使用されます。グローバル変数が const としてマークされると、set メソッドは生成されません。詳しくは、こちらをご覧ください。

    • グローバル ポインタ
  • structproject_root/gen/package/name/ScriptField_struct_name という名前の自身のクラスに反映され、このクラスが Script.FieldBase を展開します。このクラスは、この struct の 1 つ以上のインスタンスにメモリを割り当てることができる struct の配列を表します。

関数

関数は project_root/gen/package/name/ScriptC_renderscript_filename にあるスクリプト クラス自体に反映されます。たとえば、RenderScript コードで次の関数を定義するとします。

void touch(float x, float y, float pressure, int id) {
    if (id >= 10) {
        return;
    }

    touchPos[id].x = x;
    touchPos[id].y = y;
    touchPressure[id] = pressure;
}

すると、次の Java コードが生成されます。

public void invoke_touch(float x, float y, float pressure, int id) {
    FieldPacker touch_fp = new FieldPacker(16);
    touch_fp.addF32(x);
    touch_fp.addF32(y);
    touch_fp.addF32(pressure);
    touch_fp.addI32(id);
    invoke(mExportFuncIdx_touch, touch_fp);
}

RenderScript システムは非同期に設計されているため、関数には戻り値がありません。Android フレームワークのコードが RenderScript に呼び出されると、呼び出しはキューに追加され、可能な場合に実行されます。この制限により、RenderScript システムは頻繁に中断されることなく機能し、効率が向上します。関数に戻り値があるとすると、呼び出しは値が返されるまでシステムをブロックすることになってしまいます。

RenderScript コードが Android フレームワークに値を返送する場合は、rsSendToClient() 関数を使用します。

変数

サポートされている型の変数は、project_root/gen/package/name/ScriptC_renderscript_filename にあるスクリプト クラス自体に反映されます。変数ごとに一連のアクセサ メソッドが生成されます。たとえば、RenderScript コードで次の変数を定義するとします。

uint32_t unsignedInteger = 1;

すると、次の Java コードが生成されます。

private long mExportVar_unsignedInteger;
public void set_unsignedInteger(long v){
    mExportVar_unsignedInteger = v;
    setVar(mExportVarIdx_unsignedInteger, v);
}

public long get_unsignedInteger(){
    return mExportVar_unsignedInteger;
}
  

構造体

構造体は <project_root>/gen/com/example/renderscript/ScriptField_struct_name にある自身のクラスに反映されます。このクラスは、struct の配列を表し、指定された数の struct にメモリを割り当てることができます。たとえば次の構造体を定義すると、

typedef struct Point {
    float2 position;
    float size;
} Point_t;

この場合、ScriptField_Point.java に次のコードが生成されます。

package com.example.android.rs.hellocompute;

import android.renderscript.*;
import android.content.res.Resources;

  /**
  * @hide
  */
public class ScriptField_Point extends android.renderscript.Script.FieldBase {

    static public class Item {
        public static final int sizeof = 12;

        Float2 position;
        float size;

        Item() {
            position = new Float2();
        }
    }

    private Item mItemArray[];
    private FieldPacker mIOBuffer;
    public static Element createElement(RenderScript rs) {
        Element.Builder eb = new Element.Builder(rs);
        eb.add(Element.F32_2(rs), "position");
        eb.add(Element.F32(rs), "size");
        return eb.create();
    }

    public  ScriptField_Point(RenderScript rs, int count) {
        mItemArray = null;
        mIOBuffer = null;
        mElement = createElement(rs);
        init(rs, count);
    }

    public  ScriptField_Point(RenderScript rs, int count, int usages) {
        mItemArray = null;
        mIOBuffer = null;
        mElement = createElement(rs);
        init(rs, count, usages);
    }

    private void copyToArray(Item i, int index) {
        if (mIOBuffer == null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count
        */);
        mIOBuffer.reset(index * Item.sizeof);
        mIOBuffer.addF32(i.position);
        mIOBuffer.addF32(i.size);
    }

    public void set(Item i, int index, boolean copyNow) {
        if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */];
        mItemArray[index] = i;
        if (copyNow)  {
            copyToArray(i, index);
            mAllocation.setFromFieldPacker(index, mIOBuffer);
        }
    }

    public Item get(int index) {
        if (mItemArray == null) return null;
        return mItemArray[index];
    }

    public void set_position(int index, Float2 v, boolean copyNow) {
        if (mIOBuffer == null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */);
        if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */];
        if (mItemArray[index] == null) mItemArray[index] = new Item();
        mItemArray[index].position = v;
        if (copyNow) {
            mIOBuffer.reset(index * Item.sizeof);
            mIOBuffer.addF32(v);
            FieldPacker fp = new FieldPacker(8);
            fp.addF32(v);
            mAllocation.setFromFieldPacker(index, 0, fp);
        }
    }

    public void set_size(int index, float v, boolean copyNow) {
        if (mIOBuffer == null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */);
        if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */];
        if (mItemArray[index] == null) mItemArray[index] = new Item();
        mItemArray[index].size = v;
        if (copyNow)  {
            mIOBuffer.reset(index * Item.sizeof + 8);
            mIOBuffer.addF32(v);
            FieldPacker fp = new FieldPacker(4);
            fp.addF32(v);
            mAllocation.setFromFieldPacker(index, 1, fp);
        }
    }

    public Float2 get_position(int index) {
        if (mItemArray == null) return null;
        return mItemArray[index].position;
    }

    public float get_size(int index) {
        if (mItemArray == null) return 0;
        return mItemArray[index].size;
    }

    public void copyAll() {
        for (int ct = 0; ct < mItemArray.length; ct++) copyToArray(mItemArray[ct], ct);
        mAllocation.setFromFieldPacker(0, mIOBuffer);
    }

    public void resize(int newSize) {
        if (mItemArray != null)  {
            int oldSize = mItemArray.length;
            int copySize = Math.min(oldSize, newSize);
            if (newSize == oldSize) return;
            Item ni[] = new Item[newSize];
            System.arraycopy(mItemArray, 0, ni, 0, copySize);
            mItemArray = ni;
        }
        mAllocation.resize(newSize);
        if (mIOBuffer != null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */);
    }
}

生成されたコードは、RenderScript ランタイムによって要求された構造体にメモリを割り当て、メモリ内の struct を操作できるように提供されます。それぞれの struct のクラスは、次のメソッドとコンストラクタを定義します。

  • メモリを割り当てることができるようにする、オーバーロードされたコンストラクタです。ScriptField_struct_name(RenderScript rs, int count) コンストラクタを使用すると、count パラメータによってメモリを割り当てる構造体の数を定義できるようになります。ScriptField_struct_name(RenderScript rs, int count, int usages) コンストラクタは、追加のパラメータ usages を定義します。このパラメータによってメモリを割り当てる際のメモリ容量を指定できます。メモリ容量には次の 4 つが考えられます。
    • USAGE_SCRIPT: スクリプトのメモリ容量に割り当てます。これは、メモリ容量を指定しない場合のデフォルトのメモリ容量です。
    • USAGE_GRAPHICS_TEXTURE: GPU のテクスチャのメモリ容量に割り当てます。
    • USAGE_GRAPHICS_VERTEX: GPU の頂点のメモリ容量に割り当てます。
    • USAGE_GRAPHICS_CONSTANTS: さまざまなプログラム オブジェクトによって使用される GPU の定数のメモリ容量に割り当てます。

    ビット単位の OR オペレータを使用すると、複数のメモリ容量を指定できます。それによって、指定したメモリ容量のデータにアクセスすることを RenderScript ランタイムに通知します。次の例では、カスタムデータ型のメモリをスクリプト メモリ容量と頂点メモリ容量の両方に割り当てます。

    Kotlin

    val touchPoints: ScriptField_Point = ScriptField_Point(
            myRenderScript,
            2,
            Allocation.USAGE_SCRIPT or Allocation.USAGE_GRAPHICS_VERTEX
    )
    

    Java

    ScriptField_Point touchPoints = new ScriptField_Point(myRenderScript, 2,
            Allocation.USAGE_SCRIPT | Allocation.USAGE_GRAPHICS_VERTEX);
    
  • 静的なネストされたクラスである Item を使用すると、オブジェクト形式の struct のインスタンスを作成できます。このネストされたクラスは、Android コードで struct を使用することが適切な場合に有用です。オブジェクトの操作が完了したら、set(Item i, int index, boolean copyNow) を呼び出して Item を配列内の目的の位置に設定し、割り当てられたメモリにオブジェクトをプッシュできます。RenderScript ランタイムは、新しく書き込まれたメモリに自動的にアクセスできるようになります。
  • 構造体の各フィールドの値を取得および設定するためのアクセサ メソッド。各アクセサ メソッドには、読み取りまたは書き込みを行う配列の struct を指定する index パラメータが割り当てられています。各セッター メソッドには、このメモリを RenderScript ランタイムに直ちに同期するかどうかを指定する copyNow パラメータも割り当てられています。同期されていないメモリを同期するには、copyAll() を呼び出します。
  • createElement() メソッドはメモリ内に構造体の説明を作成します。この記述は、1 つまたは複数の要素で構成されるメモリを割り当てるために使用されます。
  • resize() は C の realloc() と非常に類似した機能を持ち、以前に作成された現在の値を保持しながら、以前に割り当てられたメモリを拡張できるようにします。
  • copyAll() は、フレームワーク レベルで設定されたメモリを RenderScript ランタイムに同期します。メンバーに set アクセサ メソッドを呼び出すと、オプションの copyNow boolean パラメータを指定できるようになります。true を指定すると、メソッドを呼び出したときにメモリが同期されます。false を指定した場合は、copyAll() を 1 回呼び出すことができ、まだ同期されていないすべてのプロパティのメモリが同期されます。

ポインタ

グローバル ポインタは、project_root/gen/package/name/ScriptC_renderscript_filename にあるスクリプト クラス自体に反映されます。struct またはサポートされている RenderScript タイプのいずれかに対してポインタを宣言できますが、struct にはポインタやネストされた配列を含めることはできません。たとえば、structint32_t への次のポインタを定義するとします。

typedef struct Point {
    float2 position;
    float size;
} Point_t;

Point_t *touchPoints;
int32_t *intPointer;

すると、次の Java コードが生成されます。

private ScriptField_Point mExportVar_touchPoints;
public void bind_touchPoints(ScriptField_Point v) {
    mExportVar_touchPoints = v;
    if (v == null) bindAllocation(null, mExportVarIdx_touchPoints);
    else bindAllocation(v.getAllocation(), mExportVarIdx_touchPoints);
}

public ScriptField_Point get_touchPoints() {
    return mExportVar_touchPoints;
}

private Allocation mExportVar_intPointer;
public void bind_intPointer(Allocation v) {
    mExportVar_intPointer = v;
    if (v == null) bindAllocation(null, mExportVarIdx_intPointer);
    else bindAllocation(v, mExportVarIdx_intPointer);
}

public Allocation get_intPointer() {
    return mExportVar_intPointer;
}
  

get メソッドと bind_pointer_name という名前の特別なメソッドが(set() メソッドの代わりに)生成されます。bind_pointer_name メソッドを使用すると、Android VM に割り当てられたメモリを RenderScript ランタイムにバインドできます(メモリを .rs ファイルに割り当てることはできません)。詳細については、割り当てられたメモリの使用をご覧ください。

メモリ割り当て API

RenderScript を使用するアプリケーションは、引き続き Android VM で実行されます。ただし、実際の RenderScript コードはネイティブで実行され、Android VM に割り当てられたメモリにアクセスする必要があります。これを行うには、VM に割り当てられたメモリを RenderScript ランタイムに接続する必要があります。バインディングと呼ばれるこのプロセスにより、RenderScript ランタイムはリクエストしても明示的に割り当てることができないメモリをシームレスに処理できるようになります。最終結果は、C で malloc を呼び出した場合と本質的に同じです。Android VM がガベージ コレクションを実行し、RenderScript ランタイム レイヤとメモリを共有できるというメリットもあります。バインディングは、動的に割り当てられるメモリにのみ必要です。静的に割り当てられるメモリは、コンパイル時に RenderScript コード用に自動的に作成されます。メモリ割り当ての詳細については、図 1 をご覧ください。

このメモリ割り当てシステムをサポートするために、Android VM がメモリを割り当て、malloc の呼び出しに類似した機能を提供できるようにする一連の API が存在します。これらのクラスは、基本的にメモリの割り当て方法を表していますが、実際の割り当ても行います。これらのクラスの仕組みを理解するために、次のようにシンプルな malloc の呼び出しについて考えてみます。

array = (int *)malloc(sizeof(int)*10);

malloc の呼び出しは、割り当てられるメモリのサイズ(sizeof(int))と割り当てられるメモリの数(10)の 2 つの部分に分けることができます。Android フレームワークでは、これら 2 つの部分のクラスと malloc 自体を表すクラスが用意されています。

Element クラスは、malloc の呼び出しの(sizeof(int))部分を表し、単一の浮動小数点値や構造体などのメモリ割り当ての 1 つのセルをカプセル化します。Type クラスは、Element と割り当て対象の要素の量(例では 10)をカプセル化します。TypeElement の配列とみなすことができます。Allocation クラスは、指定された Type に基づき実際のメモリの割り当てを行い、実際の割り当てメモリを表します。

ほとんどの場合、これらのメモリ割り当て API を直接呼び出す必要はありません。リフレクション レイヤクラスは、これらの API を自動的に使用するコードを生成します。メモリの割り当てに際してユーザーが行う必要があるのは、リフレクション レイヤクラスの 1 つで宣言されたコンストラクタを呼び出して、生成されたメモリ Allocation を RenderScript に渡すことだけです。 リソースからビットマップを読み込む場合や、ポインタのメモリをプリミティブ型に割り当てる場合に、これらのクラスを直接使用して自分でメモリを割り当てることもできます。この方法については、RenderScript へのメモリの割り当てとバインドのセクションをご覧ください。 次の表に、3 つのメモリ管理クラスの詳細を示します。

Android オブジェクトの種類 説明
Element

要素は、メモリ割り当ての 1 つのセルを記述し、基本と複合の 2 つの形式を持つことができます。

基本要素には、有効な RenderScript データ型のデータのコンポーネントが 1 つ含まれます。 基本要素データ型の例には、1 つの float 値、float4 ベクトル、または 1 つの RGB-565 カラーなどがあります。

複雑な要素には基本要素のリストが含まれ、RenderScript コードで宣言する struct から作成されます。たとえば、メモリに複数の struct を順番に配置できます。各構造体は、その構造体内のそれぞれのデータ型ではなく、独自の要素とみなされます。

Type

型はメモリ割り当てテンプレートであり、要素と 1 つ以上のディメンションで構成されます。メモリのレイアウト(基本的には Element の配列)を記述しますが、記述するデータに対するメモリの割り当ては行いません。

タイプは、X、Y、Z、LOD(詳細レベル)、(キューブマップの)Face の 5 つのディメンション)で構成されます。使用可能なメモリの制約内で、X、Y、Z の各ディメンションに任意の正の整数値を設定できます。単一ディメンションによる割り当ての X ディメンションはゼロより大きく、Y と Z のディメンションはゼロであり、存在しないことを示しています。たとえば、x=10、y=1 の割り当ては 2 つのディメンションとみなされ、x=10、y=0 は 1 つのディメンションとみなされます。LOD および Faces ディメンションは、存在または不存在を示すブール値です。

Allocation

割り当てによって、Type が表すメモリの説明に基づいてアプリケーション用のメモリが得られます。割り当てられたメモリは複数のメモリ空間に同時に存在できます。メモリが 1 つのスペースで変更されている場合は、メモリを明示的に同期して、存在する他のすべてのスペースで更新されるようにします。

割り当てデータは、主に「型チェックあり」と「型チェックなし」の 2 つの方法によりアップロードされます。 シンプルな配列に対しては、Android システムから配列を取得してネイティブ レイヤのメモリストアにコピーする copyFrom() 関数があります。未確認のバリアントを使用すると、構造体をサポートしていないため、構造体の配列をコピーできます。たとえば、n 個の浮動小数点の配列がある場合、浮動小数点 [n] の配列または byte[n*4] の配列に含まれるデータをコピーできます。

メモリを操作する

RenderScript で宣言する非静的なグローバル変数は、コンパイル時にメモリを割り当てられます。 これらの変数は、RenderScript コード内で直接操作でき、Android フレームワーク レベルでメモリを割り当てる必要はありません。Android フレームワーク レイヤも、リフレクション レイヤクラスで生成されるアクセサ メソッドを使用してこれらの変数にアクセスできます。上記の変数が RenderScript ランタイム レイヤで初期化される場合は、それらの値は Android フレームワーク レイヤの対応する値を初期化するために使用されます。グローバル変数が定数としてマークされると、set メソッドは生成されません。詳しくは、こちらをご覧ください。

注: rs_program_fragmentrs_allocation などのポインタを含む特定の RenderScript 構造体を使用している場合は、まず対応する Android フレームワーク クラスのオブジェクトを取得してから、その構造体の set メソッドを呼び出してメモリを RenderScript ランタイムにバインドする必要があります。RenderScript ランタイム レイヤでは、これらの構造体を直接操作することはできません。この制限は、ポインタを含むユーザー定義の構造体には該当しません。これは、そもそもリフレクション レイヤクラスへのエクスポートが行えないためです。ポインタを含む静的ではないグローバル構造体を宣言しようとすると、コンパイラ エラーが発生します。

RenderScript ではポインタもサポートされていますが、Android フレームワークのコードに明示的にメモリを割り当てる必要があります。.rs ファイルでグローバル ポインタを宣言したら、適切なリフレクション レイヤクラスを使用してメモリを割り当て、そのメモリをネイティブ RenderScript レイヤにバインドします。このメモリは、RenderScript レイヤの他にも、Android フレームワーク レイヤから操作でき、それによって最適なレイヤで変数を柔軟に変更できます。

動的メモリを RenderScript に割り当ててバインドする

動的メモリを割り当てるには、Script.FieldBase のコンストラクタを呼び出す必要があります。これが最も一般的な方法です。Allocation を手動で作成する方法もあります。これはプリミティブ型ポインタなどの場合に必要です。簡素化のために、できる限り Script.FieldBase クラスのコンストラクタを使用します。 メモリ割り当てを取得したら、反映済みのポインタの bind メソッドを呼び出し、割り当てられたメモリを RenderScript ランタイムにバインドします。

以下の例では、プリミティブ型ポインタ intPointer とポインタの両方のメモリを構造体 touchPoints に割り当てています。この例ではメモリを RenderScript にもバインドします。

Kotlin

private lateinit var myRenderScript: RenderScript
private lateinit var script: ScriptC_example
private lateinit var resources: Resources

public fun init(rs: RenderScript, res: Resources) {
    myRenderScript = rs
    resources = res

    // allocate memory for the struct pointer, calling the constructor
    val touchPoints = ScriptField_Point(myRenderScript, 2)

    // Create an element manually and allocate memory for the int pointer
    val intPointer: Allocation = Allocation.createSized(myRenderScript, Element.I32(myRenderScript), 2)

    // create an instance of the RenderScript, pointing it to the bytecode resource
    script = ScriptC_point(myRenderScript/*, resources, R.raw.example*/)

    // bind the struct and int pointers to the RenderScript
    script.bind_touchPoints(touchPoints)
    script.bind_intPointer(intPointer)

   ...
}

Java

private RenderScript myRenderScript;
private ScriptC_example script;
private Resources resources;

public void init(RenderScript rs, Resources res) {
    myRenderScript = rs;
    resources = res;

    // allocate memory for the struct pointer, calling the constructor
    ScriptField_Point touchPoints = new ScriptField_Point(myRenderScript, 2);

    // Create an element manually and allocate memory for the int pointer
    intPointer = Allocation.createSized(myRenderScript, Element.I32(myRenderScript), 2);

    // create an instance of the RenderScript, pointing it to the bytecode resource
    script = new ScriptC_example(myRenderScript, resources, R.raw.example);

    // bind the struct and int pointers to the RenderScript
    script.bind_touchPoints(touchPoints);
    script.bind_intPointer(intPointer);

   ...
}

メモリに対して読み取りと書き込みを行う

静的に割り当てられるメモリと動的に割り当てられるメモリは、RenderScript ランタイムと Android フレームワーク レイヤの両方で読み書きできます。

静的に割り当てられるメモリには、RenderScript ランタイム レベルでの一方通行の通信制限があります。RenderScript コードが変数の値を変更した場合、効率性を目的として Android フレームレイヤに返されることはありません。Android フレームワークから設定された最後の値は、常に get メソッドの呼び出し中に返されます。ただし、Android フレームワークのコードが変数を変更した場合、その変更内容は RenderScript ランタイムに自動的に送信するか、後で同期できます。RenderScript ランタイムから Android フレームワーク レイヤにデータを送信する必要がある場合は、rsSendToClient() 関数を使用してこの制限を克服できます。

動的に割り当てられたメモリを操作した場合、RenderScript ランタイム レイヤの変更は、関連付けられたポインタを使用してメモリの割り当てを変更した場合に、Android フレームワーク レイヤに反映されます。 Android フレームワーク レイヤでオブジェクトを変更すると、すぐにその変更が RenderScript ランタイム レイヤに反映されます。

グローバル変数に対して読み取りと書き込みを行う

グローバル変数への読み書きは簡単なプロセスです。アクセサ メソッドは、Android フレームワーク レベルで使用することも、RenderScript コードで直接設定することもできます。RenderScript コードで行った変更は、Android フレームワーク レイヤには反映されない点に注意してください(詳しくはこちらをご覧ください)。

たとえば、次の構造体が rsfile.rs という名前のファイルで宣言されたとします。

typedef struct Point {
    int x;
    int y;
} Point_t;

Point_t point;

rsfile.rs の構造体に次のように値を直接代入できます。これらの値は、Android フレームワーク レベルには反映されません。

point.x = 1;
point.y = 1;

構造体には、このように Android フレームワーク レイヤで値を割り当てることができます。これらの値は、RenderScript ランタイム レベルに非同期に反映されます。

Kotlin

val script: ScriptC_rsfile = ...

...

script._point = ScriptField_Point.Item().apply {
    x = 1
    y = 1
}

Java

ScriptC_rsfile script;

...

Item i = new ScriptField_Point.Item();
i.x = 1;
i.y = 1;
script.set_point(i);

次のように、RenderScript コードの値を読み取ることができます。

rsDebug("Printing out a Point", point.x, point.y);

Android フレームワーク レイヤの値は、次のコードで読み取ることができます。このコードは、Android フレームワーク レベルで設定された場合にのみ値を返す点に注意してください。RenderScript ランタイム レベルでのみ値を設定すると、null ポインタ例外が発生します。

Kotlin

Log.i("TAGNAME", "Printing out a Point: ${mScript._point.x} ${mScript._point.y}")
println("${point.x} ${point.y}")

Java

Log.i("TAGNAME", "Printing out a Point: " + script.get_point().x + " " + script.get_point().y);
System.out.println(point.get_x() + " " + point.get_y());

グローバル ポインタの読み取りと書き込みを行う

メモリが Android フレームワーク レベルで割り当てられ、RenderScript ランタイムにバインドされていると仮定して、そのポインタに対して getset メソッドを使用して Android フレームワーク レベルからメモリを読み書きできます。 RenderScript ランタイム レイヤでは、ポインタを使用して通常どおりにメモリに読み書きでき、静的に割り当てられたメモリとは異なり、変更内容が Android フレームワーク レイヤに反映されます。

たとえば、rsfile.rs という名前のファイルの struct に次のポインタがあるとします。

typedef struct Point {
    int x;
    int y;
} Point_t;

Point_t *point;

Android フレームワーク レイヤにすでにメモリを割り当てていると仮定すると、struct の値に通常どおりアクセスできます。ポインタ変数を介して構造体に加えた変更は、Android フレームワーク レイヤで自動的に使用できます。

Kotlin

point[index].apply {
    x = 1
    y = 1
}

Java

point[index].x = 1;
point[index].y = 1;

ポインタの値は、Android フレームワーク レイヤでも読み書きできます。

Kotlin

val i = ScriptField_Point.Item().apply {
    x = 100
    y = 100
}
val p = ScriptField_Point(rs, 1).apply {
    set(i, 0, true)
}
script.bind_point(p)

p.get_x(0)            //read x and y from index 0
p.get_y(0)

Java

ScriptField_Point p = new ScriptField_Point(rs, 1);
Item i = new ScriptField_Point.Item();
i.x=100;
i.y = 100;
p.set(i, 0, true);
script.bind_point(p);

p.get_x(0);            //read x and y from index 0
p.get_y(0);

すでにメモリがバインドされている場合は、値を変更するたびに RenderScript ランタイムにメモリを再バインドする必要はありません。