雖然網路上有許多 Renderscript 處理繪圖運算的範例,但它不只可以作繪圖加速還可以作平行運算,在 PandaBoard ES Rev B 板子上,發現雙核心 CPU 的架構下,似乎是沒問題的。然而,網路上場看得範例都是處理圖檔,讀檔的資料結構很簡單,就是最基本的 Bitmap 而已,於是花了不少功夫推敲,才找到如何定義自己的資料結構進行平行運算!
作法很簡單,先把你想要的資料結構定義在 Renderscript (myscript.rs) 檔案內,如 C 語言的 structure:
#pragma version(1)
#pragma rs java_package_name(com.example.android.rs.MyParallelCompute)
#include "rs_graphics.rsh"
struct MyDataIn {
int a;
int b;
};
//struct MyDataOut {
// int sum;
//};
//void root(const struct MyDataIn *in, struct MyDataOut* out) {
void root(const struct MyDataIn *in, int* out) {
//out->sum = (int)a+b;
*out = in->a + in->b;
rsDebug("myscript a=", (int) in->a);
rsDebug("myscript b=", (int) in->b);
}
如此一來,編譯後自動產生 ScriptField_MyDataIn.java (gen/com.example.android.rs.MyParallelCompute裡),之後在 Java 端撰寫程式時,使用這個 class 來包裝你的資料:
private void createScript() {
mRS = RenderScript.create(this);
int myDataCount = 5;
ScriptField_MyDataIn in = new ScriptField_MyDataIn(mRS,myDataCount);
//ScriptField_MyDataOut out = new ScriptField_MyDataOut(mRS,myDataCount,myDataCount);
// initial data
for( int i=0 ; i<myDataCount ; ++i) {
in.set_a(i, i, false);
in.set_b(i, i*2, false);
}
in.copyAll();
//Allocation dataOut = Allocation.createTyped(mRS, out.getType());
Allocation dataOut = Allocation.createSized(mRS, Element.I32(mRS), myDataCount);
// call renderscript
mScript = new ScriptC_myscript(mRS, getResources(), R.raw.myscript);
mScript.forEach_root(in.getAllocation(),dataOut);
//byte []x = new byte[ ScriptField_MyDataOut.Item.sizeof * myDataCount ];
//dataOut.copyTo(x);
int[] x = new int[myDataCount];
dataOut.copyTo(x);
// result data
for( int i=0 ; i<myDataCount ; ++i) {
//Log.e("MyParalle","(a,b,sum)=("+in.get_a(i)+"+"+in.get_b(i)+"="+out.get_sum(i)+")");
Log.e("MyParalle","(a,b,sum)=("+in.get_a(i)+"+"+in.get_b(i)+"="+x[i]+")");
}
}
此例很簡單,使用平行運算把輸入的資料(struct MyDataIn)進行相加後輸出(int)。紅色是把資料轉成 RS 的輸入,綠色則是用來儲存運算結果,而藍色則是把綠色取得的資料,轉成在 Java 常用的結構。
幾個小筆記:
- 用 forEach_root 進行平行運算,輸入的資料個數要跟輸出個數一樣才能使用,在此資料個數就是 myDataCount,而從 Java 傳遞給 forEach_root 的參數輸入跟輸出必須是 Allocation 型態
- 想要自訂結構,請在 *.rs 先定義(如 struct MyDataIn),接著編譯後,則可以再 Java 中使用(如 ScriptField_MyDataIn)
- 在 Java 中使用 ScriptField_* 進行資料初始化後,想要把它變成有效的 Allocation 時,記得先做 copyAll(),不然則是每次設值時,最後一個 copyNow 參數設定成 true,詳情起看 ScriptField_*.java 查閱實作
- 運算後的資料也是 Allocation,必須把它轉成自己的資料結構,由於 Allocation 目前只支援輸出(copyTo)成 int [], short [], float [], byte[] 和 Bitmap 等,所以建議先以這些結構來設計,不然輸出還要花心力去做轉換,希望之後的架構可以支援直接輸出成自訂結構
- Allocation 初始化有不少 Element.XXX 函式可以使用,例如 rs 用 void root( uchar4 *in ) 的話,那就用 Element.U8_4(mRS) 來對應
很好的例子~ 非常感谢博主大神!!!!!
回覆刪除我也从网上找了很多不实用的例子,你这个是现实国内最好的入门文章
千万不要用sdk.buildtools 19.0.0,编译出来的东西错的~ 运行不了
刪除换回17.0.0就好了
原來如此 Orz 這篇筆記已經接近一年半前的了,我也非常久沒用了。有機會再研究 sdk.buildtools 19 版吧,大部分架構的更新通常就會一直更新下去,舊版的容易被捨棄掉。
刪除或許 Renderscript 還沒蓬勃發展,所以留一下的文件不多。
祝你研究順利 :)
想不到昨天发的留言,今天就有人回复了!我是刚研究RenderScript,上两天android sdk升级到4.4,然后buildtools升级到19就不行了....蛋疼得要命....幸好在未升级前运行过sdk sample那个computer demo,知道是编译问题。不然我还一直以为是我的.rs有语法问题,自问C语言也学得不烂....
刪除作者已經移除這則留言。
回覆刪除