什么是Renderscript?
Renderscript是android平台上进行高性能计算的框架。Renderscript主要面向并行计算,虽然它对计算密集型工作也是有益的。Renderscript在运行时将在设备上可用的处理器间平衡负载,比如多核CPU,GPU或者DSP,它让你专注于算法而不是平衡负载。RenderScript对图像处理,计算摄影学,计算机视觉方面的应用非常有用。
在开始学习Renderscript前,你应该理解两个主要概念:
1 高性能的计算核心由支持C99协议的语言书写。(也就是C语言。译者注)
2 一套java API 用来管理Renderscript资源,并控制核心计算的执行。
写Renderscript核心
一个Renderscript核心一般写在.rs文件中,在[工程目录]/src/ 目录下;每一个.rs文件成为一个脚本。每一个脚本包含一系列的内核,函数,变量。通常包含
1 一个编译声明Renderscript核心语言的版本(#pragma version(1))。
目前可用值只有1.
2 一个编译声明这个脚本对应的java类的包名
(#pragma rs java_package_name(com.example.app)).
3 一些可调用的函数。
一个可调用的函数是一个单线程的Renderscript函数,你可以再java代码中调用它并传递任意多的参数。它通常用在较大的处理管道里初始化设置或者一系列计算。
4 一些全局变量。
一个Renderscript全局变量等价于C语言中的一个全局变量。你可以再java代码中访问Renderscript全局变量,它们通常被用来向Renderscript核心传递参数。
5 一些计算核心。
一个计算核心是在Allocation里的Element间执行的平行的函数。一个简单的核心如下:
uchar4 __attribute__((kernel)) invert(uchar4 in, uint32_t x, uint32_t y) {
uchar4 out = in;
out.r = 255 - in.r;
out.g = 255 - in.g;
out.b = 255 - in.b;
return out;
}
它在很多方面跟标准C函数一致。第一个值得注意的是__attribute__((kernel)), 这表明这个函数式Renderscript核心而不是可调用函数。第二个特性是 in参数和它的类型,在Renderscript核心里第一个参数会根据传入内核平台的Allocation被自动填入。默认的,核心将在每一个Element执行一次时在整个Allocation中运行(???不懂 译者注)。 第三个注意点是核心的返回类型,这个返回值将被自动的写入到输出Allocation的适当的位置。Renderscript会在运行时确保输入和输出的Allocation的Element类型互相匹配;如果不匹配,会抛出异常。
一个核心可能有一个输入Allocation或/和一个输出Allocation。也可能有多个输入输出Allocation。如果需要多个输入输出Allocation,这些对象应该同re_allocations脚本全局变量绑定,并且通过rsGetElementAt_type() / rsSetElementAt_type() 在核心或可调用函数中访问。
一个核心可以使用x,y和z参数来访问当前执行的坐标。这些参数是可选的,但是类型必须是uint32_t。
6 可选的 init() 函数。
Init() 韩式是一个特殊的可调用函数,它在脚本初始化时执行。
7 一些静态全局变量和函数。
静态的全局变量与普通的全局变量不同的地方是它不能从java代码中设定。静态函数是一个标准的C函数,可以再核心和脚本中的可调用方法中调用,但是不能被java API访问。如果全局变量或者函数不需要在java代码中使用,强烈建议声明成静态的。
设置浮点精度
你可以在脚本里控制浮点数的精度。这样做在不要求使用IEEE 754-2008 标准(默认使用)时是很有用的。下面的编译指示可以设置不同的浮点精度:
1 #pragma rs_fp_full (不指定时默认的设置):使用IEEE 754-2008 标准的精度。
2 #pragma rs_fp_relaxed 不严格的使用IEEE 754-2008 的标准,允许少量的精度误差。
3 #pragma rs_fp_imprecise 不需要严格的精度要求,允许#pragma rs_fp_relaxed下的所有情况,还包括:
-0.0会返回+0.0
INF 和 NAN的操作部明确
….(不懂。。。译者注)
大多数应用可以使用#pragma rs_fp_relaxed。
访问Renderscript API
在android应用里使用Renderscript有两种方式
1 android.renderscript 这个包在运行Android3.0(API level 11)的设备上可用,这是原始的API,目前没有被更新。
2 android.support.v8.renderscript 这个包可以通过共享库在运行Android2.2(API level 8)或以上版本的设备上使用。
强烈建议使用第二种方式,因为它包含了最新的更新,并且支持更多的设备。
使用Renderscript共享库API
为了使用共享库的Renderscript API,你需要安装你的开发环境。你需要下面的SDK工具:
Android SDK Tools revision 22.2 or higher
Android SDK Build-tools revision 18.1.0 or higher
在eclipse工程中使用共享库,你需要:
1 安装需要的SDK和编译工具
2打开工程根目录下的project.properties文件
3 添加下面的内容
renderscript.target=18
renderscript.support.mode=true
sdk.buildtools=18.1.0
4 在类中导入共享库的类
import android.support.v8.renderscript.*;
在java代码中使用Renderscript
在java代码中使用Renderscript依赖android.renderscript 或者android.support.v8.renderscript中的API。大部分程序遵循如下的使用方式:
1 初始化Renderscript context。
Renderscript context通过create(Context)创建,它能保证Renderscript能被使用,并且提供了一个对象来控制所有后面创建的Renderscript对象的生命周期。创建这个context可能是一个耗时的操作,因为可能要在硬件的不同位置创建资源;通常,一个程序同时只能有一个Renderscript context。
2 创建至少一个Allocation来传递给脚本。
一个Allocation是一个Renderscript对象,它存储了固定数量的数据。脚本里的核心将Allocation对象作为输入和输出,核心可以在绑定脚本全局变量时通过rsGetElementAt_type() 和rsSetElementType_type()来访问Allocation对象。Allocation对象可以通过数组从java代码传递给Renderscript代码,反之亦然。Allocation对象通常用createTyped(RenderScript, Type) 或者 createFromBitmap(RenderScript,
Bitmap)创建。
3 创建你所需的任何脚本。有两种脚本可被创建:
ScriptC : 这是上面”写Renderscript脚本”部分介绍的用户自定义脚本。Renderscript编译器会为每一个脚本创建一个java类,可以方便从java代码中访问脚本;这个类会被命名成ScriptC_filename。比如:如果上面的核心位于inver.rs中,并且一个Renderscript context已经被创建,在java代码中初始化脚本的代码如下:
ScriptC_invert invert = new ScriptC_invert(mRenderScript);
ScriptIntrinsic : 这是嵌入在Renderscript核心中的通用操作,比如高斯模糊,卷积,图像混合。更多信息请参看 ScriptInstrinsic.
4 在Allocation中的数据。除了android.renderscript创建的Allocation以外,一个空的Allocation将被创建,填充一个Allocation,请使用Allocation的copy方法。
5 设置必要的脚步全局变量。
全局变量可以使用ScriptC_filename里的set_globalname来设置。例如,要想设置一个叫elements的整形变量,使用java方法set_elements(int)。Renderscript对象也可以在核心中设置,例如,叫做lookup的rs_allocation变量可以通过set_lookup(Allocation)函数设置。
6加载适当的核心。
使用ScriptC_filename中的forEach_kernelname()函数可以加载一个核心。加载过程是异步的,加载的顺序和启动的顺序是一致的。根据传入内核的参数的不同,这个函数会携带一个或两个Allocation。默认的,一个核心会在整个输入输出Allocation中执行;为了在一系列Allocation中执行,给forEach函数的最后一个参数传递恰当的Script.LaunchOptions。可调用函数可以使用ScriptC_filename中的invoke_functionname调用。
7 从Allocation对象中拷贝数据。
为了在java代码中访问Allocation中的数据,需要使用Allocation的copy方法拷贝到java缓冲区中。这些方法会必要的时候在异步内核和函数调用间做同步。
8 销毁Renderscript context。
可以使用destroy()方法销毁Renderscript context,或者允许Renderscript context对象被垃圾回收。这样使用这个context中的任何对象将抛出异常。