【android开发】Google RenderScript文档【一】

来源:互联网 发布:公共科目大数据贵州 编辑:程序博客网 时间:2024/06/05 02:30

这篇文章纯粹是翻译,先翻译完再说(主要是RenderScript看起来很牛逼的样子)。先粗略翻译一遍(可能会有很多问题),然后一步步学习吧。
那就开始吧。


【 概 述】
RenderScript是一款用于执行高计算量任务的框架,它在Android平台上具有良好的性能。当然,其设计初衷是用于处理大量的并行数据计算,但也不妨使用在其他场景中。其运行时(Runtime)利用设备的多CPU/GPU进行并行计算,让你可以去专注表达算法本身,而不是去在意协调任务的调用。RenderScript对于图像处理、运算摄影(computational photography)、视讯处理(计算机视觉: computer vision)有着优异的表现。
在继续往下介绍RenderScript之前,我们需要有两点概念需要理解:
● 高性能计算内核使用的是C99-derived语言编写。一个计算内核是一个 or 一组函数,你可以通过RenderScript的runtime来执行它们,进行一堆数据的并行处理。
● Java API用于管理RenderScript资源的生命周期和控制内核的执行。

一、实现RenderScript内核(Writing a RenderScript Kernel)

RenderScript内核文件保存为 .rs 类型,并且位于项目的src根目录下( /src/ ),每个 .rs 文件被称作为一个脚本,每个脚本包含了他自身的一组内核、函数、变量。一个脚本可以包含的内容如下:
● 一个编译指示申明(#pragma version(1)),一个用来申明当前RenderScript内核使用的语言版本的申明。目前只有 1 为有效的取值。
● 一个编译指示申明(#pragma rs java_package_name(com.example.app)),一个用来申明此脚本对应的Java类的包名。[注意:你的 .rs 文件必须是你应用包中的一部分,而不是放置在项目的库文件夹中]
● 0个或多个可被调用的函数(invokable functions)。一个可被调用的函数是一个单线程的RenderScript函数,你可以在Java中使用任意参数来调用他。[这些函数通常在初始化时 或者 在一个大型处理管道中处理一系列的计算时很有用]
● 0个或多个全局脚本(script globals)。一个全局脚本类似于C语言中的全局变量。你可以在Java代码中访问这些全局脚本。[全局脚本通常使用于向RenderScript内核传递参数]
● 0个或多个计算内核(compute kernels)。有两种类型的计算内核:映射内核(也叫做遍历内核 mapping kernel)、减少内核(reduction kernel)。

1)映射内核(mapping kernel)
一个映射内核是一个并行函数,它用于处理一组相同dimension的Allocation。默认情况下,他会为dimension中的每个coordinate执行一次。他主要(但不是唯一)用于对输入的一组Allocation数据进行转换,同一时间输出Allocaion的一个元素。下面是一个映射内核的例子:

uchar4 RS_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语言的函数一样滴。RS_KERNEL 主要使用来做函数的原型指定,他表示这个函数是一个”映射内核(mapping kernel)”而不是一个”可调用函数(invokable function)”。输入参数“in”是基于在内核启动时传入的Allocation参数而自动填充的,参数x 和y在后文中将会提到。内核的返回值将会自动被写入到输出参数Allocation中的适当位置。默认情况下,内核将会贯穿执行整个输入的Allocation(Allocation中的每个元素都将通过内核的方法执行一遍)。
一个映射内核可能有一个或多个输入Allocation,有一个或者两个Allocation输出。RenderScript的运行时回去检测并确保所有的输入和输出Allocation都有相同的规格(dimension),并且输入的元素类型和输出的Allocation与内核的原型匹配,如果其中一种检测失败,RenderScript将会抛出异常。【提示:在Android6.0(API23)之前,一个映射内核也许没有超过一个输入的Allocation】
如果你需要多个输入或者多个输出Allocation(超过内核所拥有的),那些对象应该绑定到全局脚本 rs_allocation 上,同时从内核中访问,或者通过可被调用函数(invokable function)的rsGetElementAt_type() / rsSetElementAt_type()。【提示:为了你的方便,RS_KERNEL是由RenderScript自动宏观定义出来的】

#define RS_KERNEL __attribute__((kernel))

2)减少内核(reduction kernel)
reduction kernel也是处理一堆相同dimension的Allocation的function家庭中的一员。默认情况下,他的累计函数(accumulator function)将会为每个dimension中的coordinate执行一遍。他主要(但不是唯一)用于“降维”一个输入的Allocation集合成一个单独的值。下面是一个reduction kernel将输入元素累加起来的例子。

#pragma rs reduce(addint) accumulator(addintAccum)static void addintAccum(int *accum, int val) {    *accum += val;}

一个reduction kernel是由一个或者多个用户写的function组成。 #pragma rs reduce是用来通过指定其名字(此例中为addint)、function(用于组成内核,此例中,addintAccum是一个accumulator function)的名字和角色来定义内核。所有这样的function都应该是static的。一个reduction kernel通常需要一个accumulator function;他也有可能拥有其他function,这取决于你想让这个kernel做什么。
一个reduction kernel的accumulator function的返回值必须是void,并且需要至少含有两个参数。第一个参数(此例中是 accum)是一个指针,指向一个accumulator data item;第二个参数(此例中是 val)基于kernel在启动时传递过来的Allocation参数而自动自动填充的。accumulator data item由RenderScript运行时创建;默认情况下,初始化为0。默认情况下,此内核或执行完整个Allocation(Allocation中的每个元素都将会通过accumulator function执行一次)。默认情况下,accumulator data item 的最终值会被视为此reduction的结果并返回给java。RenderScript的运行时回去检测并确保所有的输入和输出Allocation都有相同的规格(dimension),并且输入的元素类型和输出的Allocation与内核的原型匹配,如果其中一种检测失败,RenderScript将会抛出异常。
一个reduction kernel拥有一个或多个输入Allocation,但是没有输出Allocation。
后文的【Reduction Kernels in Depth】将会更详细的介绍到。
【reduction kernel的支持版本在android7.0(API24)以后】
mapping kernel function 或者 reduction kernel accumulator function在当前任务执行中可能会使用特殊参数x,y和z(类型必须是int或者uint32_t)访问coordinate。这些参数都是可选的。
mapping kernel function 或者 reduction kernel accumulator function可能也会获取可选的特殊参数context(rs_kernel_context类型),对于runtime API家族来说,此context是非常需要的,它用来去查询确认当前执行的任务中的属性——举个例子:rsGetDimX.( context参数适用于android6.0 ( API 23)及以上 )
● 一个可选的 init() 函数。一个init()函数是一个特殊类型的可被调用函数(invokable function),他是在脚本第一次被实例化时会被RenderScript执行的函数。这个函数允许在脚本创建时进行一些计算(初始化函数)。
● 一个或者多个静态全局script和函数。静态script脚本除了不能被java代码访问之外,其他和一般的全局script一样。静态函数是一个标准的C语言函数,他可以被所有内核调用,但是没有暴露给java API。如果一个全局script或者function不用被java API调用,那么强烈建议他申明为static的。

【设置浮点精度】
你可以控制script中要求的浮点数精确等级。特别是当我们没必要完全按照IEEE 754-2008(默认使用的标准)时,特别有用。下面将介绍如何设置不同浮点精度等级。
* #pragma rs_fp_full ( 如果没有特别指定,使用的就是此值 ):这个适用于使用IEEE 754-2008标准框架下的标准浮点精度的APP。
* #pragma rs_fp_relaxed:这个参数适用于没那么严格的基于 IEEE 754-2008标准框架的APP,他允许更低的精度。This mode enables flush-to-zero for denorms and round-towards-zero.
* #pragma rs_fp_imprecise:这个参数使用于那些不必要强制要求精度的APP。除了允许rs_fp_relaxed的特性外,还支持下面的特性:
1)将 -0.0结果使用+0.0来代替
2)将INF和NAN视为未定义。
在大多数的应用程序中,使用 rs_fp_relaxed 是没啥毛病的。由于很多架构都是只对 relax精度做附加优化(例如:SIMD CPU 架构),所以使用 rs_fp_relaxed很有好处的。

访问RenderScript API


当你使用RenderScript来开发Android应用程序时,你可以有两种方式来访问其API(兼容包和RenderScript包);
1、使用 android.renderscrip - 此包主要支持android3.0(API 11)及以上。
2、使用 android.support.v8.renderscript - 这是一个兼容包,支持android 2.3(API 9)及以上。这个包需要单独导入。
【使用RenderScript支持包API】
为了使用RenderScript的支持包,你需要确定你的开发环境可以访问他们,下面有几点要求:
** android的SDK tools版本需要在22.2或者更高
** android的SDK build-tools版本需要在18.1.0或者更高
(如果没达到版本要求,请使用SDK MANAGER更新),另外,使用支持包可能会让安装包稍大

使用支持库来访问API需要有下面几个步骤:
for android studio:
1)确保你的SDK版本和SDK build tool满足要求
2)为编译工具添加对RenderScript的支持设置。
A、在应用程序的module中打开build.gradle文件
B、添加下面的代码。

android {    compileSdkVersion 19    buildToolsVersion "19.0.3"    defaultConfig {        minSdkVersion 8        targetSdkVersion 16        renderscriptTargetApi 18        renderscriptSupportModeEnabled true    }}

for Eclipse:
1)确保你的SDK版本和SDK build tool满足要求
2)为编译工具添加对RenderScript的支持设置。
A、打开project.properties文件。
B、添加下面的代码到文件中

renderscript.target=18renderscript.support.mode=truesdk.buildtools=18.1.0

通过java代码使用RenderScript


不管是使用原生包还是支持包,大部分的应用程序遵循下面一些相同的使用模式:
1、初始化一个RenderScript实例 :创建一个RenderScript上下文(实例),通过其create( Context )方法创建。实例化RenderScript比较消耗资源,也很耗时,因此,一般情况下,我们只实例化一个对象。

2、创建至少一个Allocation对象,用于传递给Script。 一个Allocation对象是一个存储固定数量数据的RenderScript对象。内核使用Allocation对象来作为输入或者输出。Allocation对象可以在绑定为全局脚本(script global)时,在内核里通过rsGetElementAt_type() 或者 rsSetElementAt_type()来进行访问。Allocation对象允许从java代码中传递集合到RenderScript代码中,反之亦然。Allocation对象常通过createTyped( RenderScript, Type ) 或者 createFromBitmap( RenderScript, Bitmap )来创建。

3、创建Script(任意多个,根据需求而定)也很必要。当你使用RenderScript时,有两种类型的Script可用。分别是:
** ScriptC:这是一种用户定义的Script(上文:写一个RenderScript内核有介绍)。每一个Script都对应着一个java类(由RenderScript编译器映射),主要是为了更方便的在java代码中访问Script;这样的java类,一般是以ScriptC_filename作为名字,例如:有个映射内核,文件名为:invert.rs 并且假设已经创建了一个RenderScript实例对象,变量名为mRenderScript,接下来的初始化Script的代码将会是:ScriptC_invert invert = new ScriptC_invert(mRenderScript)。
** ScriptIntrinsic:这些是为了通用操作而内置在RenderScript中的一些Script。例如:高斯模糊,卷积,图像调配等。(想了解更多,请查询 android.renderscript.ScriptIntrinisic类的子类)

4、给Allocation填充数据: 除了使用createFromBitmap(),Allocation在第一次创建时,其包含的数据都为空。使用“copy”类型的方法即可为Allocation添加数据。注意:“copy”类型的方法为线程同步的。

5、创建任意多个全局脚本(Script global): 你可以在相同的 ScriptC_filename类中使用set_globalname方法来设置global。例如:我可以使用java代码 set_threshold(int) 来设置一个名为threshold的int型变量;再例如:我可以通过java代码set_lookup(Allocation)来设置一个名为lookup的rs_allocation类型的变量。这些方法也是线程异步的。

6、启动相应的内核,并执行相应的函数: forEach_mappingKernelName() 或 reduce_reductionKernelName()类型的方法可以启动一个在同一个类(ScriptC_filename)中的指定的内核。这些方法是线程异步的。取决于传递给内核的参数,方法可以拥有一个或多个Allocation,并且他们有相同的规格(dimension)。默认情况下,一个内核会执行完这些dimension中所有的coordinate;如果你要执行的内容越过一些coordinate的子集,你可以传递一个可选参数 Script.lauchOptions 作为 forEach 或 reduce 的最后一个参数。
在相同的Script_filename类中,可以使用invoke_functionName方法来启动一个可被调用函数(invokable function)。这些方法都是线程同步的。

7、从Allocation对象和JavaFutureType对象中检索数据: 如果你想通过java代码去访问Allocation中的数据,你只能通过Allocation的“copy”类型方法将那部分数据拷贝出来。如果你想获取Reduction内核的结果,你只能使用javaFutureType.get()方法。“copy”类型和get()方法都是线程同步的。

8、销毁RenderScript 实例: 你可以通过RenderScript实例的destroy()方法来销毁RenderScript实例对象,当然,也可以允许其被垃圾回收机制回收。如果此实例再次被使用,将会抛出异常。

【异步执行模型】
forEach、invoke、reduce和set方法都是异步的——即:每个方法都有可能在完成要求动作前返回给java。然而,每个独立的动作在他们启动后就是按照一定顺序序列化了的。

Allocation类提供了“copy”类型的方法去将Allocation中的数据拷贝出来。“copy”类型的方法是异步的,并且是序列化了的(遵循上述説到的异步Action方式)。

javaFutureType类提供了get()方法去获取reduction的结果。get()方法是异步的并且也是序列化的。

0 0
原创粉丝点击