日別アーカイブ: 2011年11月1日

Android 3.x RenderScript (1)

Android 3.0 Honeycomb (API Level 11) から RenderScript が
使えるようになりました。
ハードウエア機能を用いた高速な描画を行うための仕組みです。

これまでも Java + OpenGL ES とか NDK + OpenGL ES など、ハードウエア
機能を活用できる描画手段がありました。
RenderScript は全く新しい描画のためのフレームワークとなっています。

RenderScript の特徴は、NDK のように Cコンパイラによる高速動作が
可能なこと。そして NDK と違い CPU アーキテクチャに依存しないので、
生成したバイナリの互換性が保たれることです。

また開発環境も統合されており Eclipse 上でビルド可能です。
同時に Java からアクセスするための Reflected layer class も作られます。

RenderScript から扱う描画 API は OpenGL ES の上位に位置し、
リソースの扱いなど簡略化されています。

欠点としては以下のとおり。

・Android の中でしか使えない

 NDK(C/C++) + OpenGL ES 2.0 の利点はソースコードの汎用性が非常に
 高いことです。NDK は Windows, Linux, iOS 等とソースを共有可能ですが
 RenderScript は今のところ Android 環境でしか使えません。

・描画 API

 描画 API は OpenGL ES をカプセル化したもので、OpenGL の全機能が
 そのまま使えるわけではないようです。

・ドキュメントが少ない

 サンプルソースを見ないとわからない仕様がいろいろあります。

描画なしの簡単なコードを走らせてみます。

// simple.rs
#pragma version(1)
#pragma rs java_package_name(jp.flatlib.ap02)

#include    "rs_cl.rsh"

void root( const float* vin, float* vout )
{
    *vout= *vin * 2.0f;
}


float*  istream;
float*  ostream;

rs_script   script;

void run()
{
    rsForEach( script, rsGetAllocation( istream ), rsGetAllocation( ostream ), 0 );
}

RenderScript のエントリ関数は root() です。
run() の中の rsForEach() は第一引数で与えられた script をデータの数だけ
呼び出します。与えた script の中の root() 関数が呼ばれるわけです。

ソースツリーに入れると simple.rs はバイトコードにコンパイルされ、
同時に class ScriptC_simple が作られます。

// java
ScriptC_simple  script= new ScriptC_simple( rs, res, R.raw.simple );

この class には global 変数へのアクセスや関数呼び出しのエントリも
含まれています。
上のコードの run() の呼び出しは script.invoke_run() です。

class ScriptC_ファイル名      Script そのもの
class ScriptFiled_構造体名    中で宣言した構造体
R.raw.ファイル名              バイトコードのリソース名
invoke_関数名()               関数呼び出し
set_変数()                    global 変数への書き込み
get_変数()                    前回 set した値を参照
bind_ポインタ()               データ領域の割り当て

実際に呼び出してみます。
演算するためのバッファ (Allocation) を float * 100 個分作成し、
istream/ostream に bind します。

// java
public void rstest( Context context ) {

    RenderScript    rs= RenderScript.create( context );
    ScriptC_simple  script= new ScriptC_simple( rs, context.getResources(), R.raw.simple );

    // メモリ領域の作成  float x 100
    Allocation  a_in= Allocation.createSized( rs, Element.F32(rs), 100, Allocation.USAGE_SCRIPT );
    Allocation  a_out= Allocation.createSized( rs, Element.F32(rs), 100, Allocation.USAGE_SCRIPT );

    // global 変数に書き込み
    script.bind_istream( a_in );
    script.bind_ostream( a_out );
    script.set_script( script );

    // 実行
    script.invoke_run();
}

初期値を渡して RenderScript で実行した演算結果を受け取ってみます。

// java
public void rstest( Context context ) {

    RenderScript    rs= RenderScript.create( context );
    Resources       res= context.getResources();

    // script
    ScriptC_simple  script= new ScriptC_simple( rs, res, R.raw.simple );

    // メモリ領域の作成  float x 100
    Allocation  a_in= Allocation.createSized( rs, Element.F32(rs), 100, Allocation.USAGE_SCRIPT );
    Allocation  a_out= Allocation.createSized( rs, Element.F32(rs), 100, Allocation.USAGE_SCRIPT );

    // 初期値を書き込む
    float[] srcbuf= new float[100];
    for( int i= 0 ; i< 100 ; i++ ){
        srcbuf[i]= i;
    }
    a_in.copyFrom( srcbuf );

    // global 変数に書き込み
    script.bind_istream( a_in );
    script.bind_ostream( a_out );
    script.set_script( script );

    // 実行
    script.invoke_run();

    // 結果を受け取る
    float[] destbuf= new float[100];
    a_out.copyTo( destbuf );
    for( int i= 0 ; i< 100 ; i++ ){
        Log.i( "rs", "a_out=" + destbuf[i] );
    }
}

RenderScript の問題など

先日 OS 4.0 対応 SDK r14 がリリースされたばかりですが、バグ修正のため
r15 が出ています。
API Level 13 以前の RenderScript project が動かなかった問題が修正され
たようです。これで Android 3.0 向け Sample も動くようになりました。

Download the Android SDK

*.rs をビルドすると class を生成しますが、このソース中に元ファイルの
パスを埋め込んでしまいます。
Windows の場合パスの区切りが '\' なので、u で始まるフォルダ名が
あると不正な unicode とみなされエラーとなります。
例えば C:\home\users の '\u' 等。
取り敢えず生成されたソースのエラー行を削除すればコンパイルできます。

script を更新したあと実行時にエラーが出る場合は Project を一旦
clean してリビルドした方が良いかもしれません。

続きます 「Android 3.x RenderScript (2) 描画と Allocation」