文章目录

  • 1 RenderScript简介
  • 2 使用RenderScript
    • 2.1 环境配置
    • 2.2 编写RenderScript文件
    • 2.3 在Android framework层调用RenderScript
    • 2.4 RenderScript工作流程
  • 3 RenderScript运行时层与反射层
    • 3.1 RenderScript运行时层
    • 3.2 反射层
  • 4 内存
    • 4.1 内存分配API
    • 4.2 静态内存
      • 4.2.1 静态内存的分配
      • 4.2.2 静态内存的读写
    • 4.3 动态内存
      • 4.3.1 动态内存的分配
      • 4.3.2 动态内存的读写
  • 5 基本应用场景
    • 5.1 RenderScript层回调Android framework层
    • 5.2 在RenderScript kernel中访问更多的元素
  • 参考资料
  • 转自:https://www.cnblogs.com/willhua/p/5778415.html

RenderScript是安卓平台上很受谷歌推荐的一个高效计算平台,它能够自动把计算任务分配到各个可用的计算核心上,包括CPU,GPU以及DSP等,提供十分高效的并行计算能力。可能是由于应用开发时的需求不够,关于RenderScript的相关文章很少,刚好我在工作中应用到此平台,做了一些笔记,因此决定整理成博文分享给大家。内容主要来源于官方文档、StackOverflow以及自己的理解,如有错误,请大家指正。本篇主要介绍RenderScript的基本概念。

1 RenderScript简介

RenderScript是安卓提供的一个高效计算平台。它显著的特点在于:

能够自动利用各种核心,包括CPU,GPU以及DSP等,来进行并行计算,能大大提高在图片处理、数学模型等领域提供高效的计算能力;
不需要针对不同的核心平台而编写不同的代码,因为RenderScript是在设备上进行运行时编译的。
使用了RenderScript的应用与一般的安卓应用在代码编写上与并没有太大区别。使用了RenderScript的应用依然像传统应用一样运行在VM中,但是你需要给你的应用编写你所需要的RenderScript代码,且这部分代码运行在native层。

RenderScript采用从属控制架构:底层RenderScript被运行在虚拟机中的上层安卓系统所控制。安卓VM负责所有内存管理并把它分配给RenderScript的内存绑定到RenderScript运行时,所以RenderScript代码能够访问这些内存。安卓框架对RenderScript进行异步调用,每个调用都放在消息队列中,并且会被尽快处理。

RenderScript工作流程需要经历三层:

**RenderScript运行时API:**提供进行运算的API
**反射层:**相当于NDK中的JNI胶水代码,它是一些由安卓编译工具自动生成的类,对你写的RenderScript代码进行包装,使得安卓层能够和RenderScript进行交互
**安卓框架:**通过调用反射层来访问RenderScript运行时

RenderScript的主要优点:

**可移植性:**对于不同架构,不同的处理器都不需要考虑代码的差异化,因为都是运行时在设备上进行编译的;
**高性能:**提供充分利用所有核心的无缝的并行化计算
**易用性:**简化编码,不需要像JNI一样写胶水代码

缺点:

**开发复杂:**需要去学习新的api

**调试可见性:**因为RenderScript可能运行在除了主cpu之外的处理器上,所以调试困难

2 使用RenderScript

使用RenderScript需要对编译或者开发环境进行一定的配置。
使用RenderScript主要分为两个步骤:编写.rs文件以及在Android framework中使用RenderScript,下面分别介绍。

2.1 环境配置

RenderScript的API可以有两种来源方式:
对于Android 3.0 (API level 11)及以上的可以在android.renderscript包中获取

通过android.support.v8.renderscript包获取,可以支持API level 8及以上的平台,官方强烈建议使用此支持包的方式来获取API

编译环境要求:

Android SDK Tools revision 需要22.2及以上
Android SDK Build-tools revision 需要18.1.0及以上

在project.properties文件中写入如下属性:

renderscript.target=18
renderscript.support.mode=true

或者在AS中的build.gradle的defaultConfig中添加

renderscriptTargetApi 18
renderscriptSupportModeEnabled true

**注意:**target的值应该为11及以上,但推荐使用18.如果在Manifest中配置的minSDK的值与target的值不相同,那么在编译的时候,将使用target的值替代Mainfest中的minSDK值。

2.2 编写RenderScript文件

RenderScript代码放在.rs或者.rsh文件中,在RenderScript代码中包含计算逻辑以及声明所有必须的变量和指针,通常一个.rs文件包含如下几个部分:

编译声明:#pragma rs java_package_name(package.name),比如#pragma rs java_package_name (com.willhua.RenderScript),用来声明本rs所在的java包。注意:.rs文件只能在应用程序包中,而不能在library项目中。
编译声明:#pragma version(1).声明RenderScript版本,现在都是1
主工作函数root().它会被RenderScript层的reForEach函数调用,实现多处理器对root工作的并行处理。Root函数必须返回void以及接受如下参数

1.分配给RenderScript的输入输出地址的指针。在Android3.2以及更低版本中,输入输出的指针都需要,在Android4.0及以后的版本中,给出其中一个或者两个都可以

2.下面两个参数是可选的,但是只要用了其中一个就必须两个都提供
a) 指向用户数据的指针。该数据会在RenderScript的计算中用到。该数据可以指向原始类型或者复杂结构类型
b) 用户数据的大小

从官方文档来看,老版本的文档中有介绍root,而新版本的则用kernel替代。官方在弱化root函数的概念,而是推荐使用kernel概念。本质上来说,root仅仅是一个写法形式上特殊的kernel而已。

可选init()函数。可以用来做任何初始化工作,比如初始化变量。它将会在每次RenderScript启动的时候,在其他任何代码之前执行一次
一些invokable函数。这些函数都是单线程函数(kernel函数的工作则是并行工作的),你可以给这些函数传递任意数量的参数。这些函数将会在反射层中生成对应的版本,可以从Android framework中调用。这些函数一般用来做一些初始化工作或者当做计算任务中的一个串行计算单元任务。注意:invokable函数不能是static的。
一些计算内核(compute kernel)。计算内核是并行执行的,它将并行处理输入Allocation中的每一个Element。一个简单的compute kernel如下:

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;}

compute kernel基本与一个C函数一样,但是有如下特征:
a) attribute((kernel))标志。该标志表示该函数是一个RenderScript kernel函数,而不是一个invokable函数
b) in参数及其类型。在RenderScript kernel中,这个参数将会基于传给kernel的输入Allocation而自动赋值,且默认情况下,对于Allocation中每一个Element都将会执行一遍kernel函数
c) 返回值及其类型。每次kernel函数执行的返回值将会自动写入到输出Allocation的正确位置。RenderScript将会对输入输出Allocation进行检查,如果他们与kernel函数声明不匹配则将抛出异常。

每个kernel都应该有一个输入Allocation或者一个输出Allocation或者二者都有,但不能有两个及以上的输入或者输出Allocation。如果需要在kernel中访问多个输入或者输出,则需要声明rs_allocation全局变量来担任多余一个的输入或者输出角色,然后再kernel函数或者invokable函数中通过rsGetElementAt_type()或者rsSetElementAt_type()来访问或者设置相应的Allocation,其中type为对应Allocation的Element类型对应的数据类型,比如uchar4。

在kernel中,可以通过可选的xyz参数来获取当前Element在整个Allocation中的坐标值,比如上面的invert中就通过xy来获取了xy坐标值。注意xyz的参数名不能设置为其他名称,且类型必须为uint32_t。

任何要在RenderScript中用到的变量,指针以及结构。这些声明也可以在.rsh文件中
所需要的script变量。就和C中的全局变量一样,这些全局变量一般用来传递参数给计算kernel。
一些静态变量以及函数。静态的变量与普通全局变量的区别在于:静态变量不会在映射在反射层,也即无法从Android framework中调用;静态函数就是一个标准的C函数,但是不会映射到反射层,也无法从Android framework中调用,但是可以在RenderScript中的kernel或者invokable中调用。如果有变量或者函数需要在RenderScript中使用但是不需要在Java中使用,强烈推荐设置为static的。
可选的精度控制配置,主要有三个等级:
a) #pragma rs_fp_full:默认的等级。表示的完全遵守IEEE 754-2008 standard的精度要求

b) #pragma rs_fp_relaxed:不严格的IEEE 754-2008 standard的精度要求

c) #pragma rs_fp_imprecise:比relaxed更低的精度要求

对于大部分应用来说,使用relaxed精度要求都可以满足要求而无任何副作用

example.rs :

#pragma version(1)#pragma rs java_package_name(com.willhua.rgbtoyuv)#pragma rs_fp_relaxedtypedef struct Point_T{    int x;    int y;}Point;//script variableuint32_t inW;uint32_t inH;uint32_t inCount;rs_allocation outYUV;struct Point point;//rootvoid root(const uchar4 *in, uint32_t x, uint32_t y){    struct myStruct my;    my.x = 0;    struct myStruct my2 = my;    int u = my.x;    uchar R,G,B;    int Y,U,V;    R = (*in).r;    G = (*in).g;    B = (*in).b;    Y = ( (  66 * R + 129 * G +  25 * B + 128) >> 8) +  16;    uint32_t yIndex = y * inW + x;    Y = ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));    rsSetElementAt_uchar(outYUV, ((uchar)Y), yIndex);    if((x & 1) == 0 && (y & 1) == 0) {        U = ( ( -38 * R -  74 * G + 112 * B + 128) >> 8) + 128;        V = ( ( 112 * R -  94 * G -  18 * B + 128) >> 8) + 128;        uint32_t index = (y >> 1) * inW + x + inCount;        U = ((U < 0) ? 0 : ((U > 255) ? 255 : U));        V = ((V < 0) ? 0 : ((V > 255) ? 255 : V));        rsSetElementAt_uchar(outYUV, ((uchar)U), index + 1);        rsSetElementAt_uchar(outYUV, ((uchar)V), index );    }}//compute kernel__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;}//invokable functionvoid setInPara(uint32_t w, uint32_t h){    inW = w;    inH = h;    inCount = w * h;}//initvoid init(){}

2.3 在Android framework层调用RenderScript

虽然各个应用使用RenderScript细节各不相同,但大体有着这样的模式:

初始化RenderScript context。通过RenderScript.create函数可以创建相应的context,有了该context才可以进行RenderScript的其他动作,并通过该context可以控制其他RenderScript对象的生命周期。因为context的创建需要在不同的硬件设备上创建资源,所以可能会比较耗时。因此,最好不要让context的创建在响应速度要求比较高的时间点上。一般来说,一个应用应该只有一个context。
至少需要创建一个Allocation。用的比较多的方法是createTyped(RenderScript, Type) 或createFromBitmap(RenderScript, Bitmap)。
创建需要的scripts。可以分为两种,一种就是通过自定义.rs文件,然后在反射层自动生成的ScriptC子类,名字为ScriptC_rsfilename;还有一种就是系统定义的一些scripts,比如高斯模糊等,他们都是ScriptIntrinsic的子类。
给Allocation填充数据。使用Allocation的copy系类方法
设置必要的script变量。在ScriptC_rsfilename会给script变量生成相应的set方法,比如int型名为num的script变量则会在反射层生成set_num(int)方法。
启动计算。.rs中定义的kernel函数都会在反射层的ScriptC_rsfilename类中生成forEach_kernalname方法。该方法的执行是异步的,且会在RenderScript中按照kernel调用顺序来执行。前面提到过,forEach_kernalname方法默认对对应Allocation中所有的Element执行计算,但是可以在forEach_kernalname参数的最后传入一个Scrpit.LunchOptions参数来指定Allocation中需要被计算的子集。.rs中定义的invokable函数则会在反射层生成invoke_functionname的方法,也可以按需调用
从Allocation中得到数据。通过Allocation的copyTo系类方法,可以把Allocation中的数据放到Java数据中。这些copyTo方法与forEach方法保持同步,forEach的计算完成之后就会自动启动copy过程。目前只支持基本类型的copyTo,暂不支持比如自定义的struct等数据。
释放资源。通过RenderScript.destory方法来释放相关资源。

2.4 RenderScript工作流程

最开始就提到,RenderScript是一个主从架构,底层的RenderScript被上层的Android framework所控制。其工作流程也正是如此。从在Android framework创建RenderScript的context开始,然后给RenderScript层分配、绑定相关内存,对script变量进行初始化,然后调用forEach函数通知启动RenderScript计算。RenderScript将会自动把它的计算任务分配到各个可用的核心上来完成计算任务(现在还只能支持CPU,以后将会支持到GPU以及DSP,且代码不需要变动)。RenderScript计算完成以后将会自动把计算结果放到相应的Allocation内存,然后在Android framework层再从Allocation中copy出数据,最后Android framework层命令RenderScript释放资源,流程介绍。下图展示了RenderScript的工作流程:

3 RenderScript运行时层与反射层

3.1 RenderScript运行时层

RenderScript运行时层是指.rs代码运行时所在的层级。当对安卓项目进行编译的时候,.rs或者.rsh中编写的代码都会被llvm编译器编译成字节码。当该安卓应用在设备上运行的时候,这些字节码将会被设备上另外一个llvm编译(just-in-time)成机器码。这些机器码是针对该设备进行了优化的,且缓存在设备上,等到下次被应用的时候就不需要重新编译了,以加快速度。虽然RenderScript运行时层很像NDK,但是由于NDK中的C函数只针对CPU设计,与RenderScript还能够在GPU和DSP上运行的目标不同,因此在RenderScript中不能使用NDK中的C函数。

RenderScript运行时库的特性包括:

请求内存分配,即其内存是由Android framework层负责分配的。
一系列针对标量与向量计算的数学函数
提供基本数据到向量/矩阵或者数据到时间的转换函数
预定义的一系列二维、三维、四维向量类型
Log功能,rsDebug函数

3.2 反射层

反射层由安卓编译工具基于开发者编写的.rs/.rsh文件自动生成的,反射层的作用就是给Android framework层提供对RenderScript运行时层操作的Java接口,包括内存分配、计算任务启动、数据交互等。

每一个.rs文件都会被映射成继承于ScriptC的类:ScriptC_RenderScript_filename,该类被生成在gen目录下与.rs文件相同的包下。该类就是.rs文件的Java版本。该类主要包含.rs中的如下内容:

非静态函数。.rs中的非kernel函数不能有返回值,因为RenderScript系统被设计成异步执行。当你从安卓层调用RenderScript函数的时候,这个调用被放在队列中,然后当轮到该调用的时候再执行。这样的话可以使RenderScript避免被经常打断以提升性能。如果想在RenderScript代码(.rs)中给安卓层返回值,则可以使用rsSendToClient()
非静态全局变量。而且会对这些变量生成get/set方法(const变量则不会生成set方法),且如果在RenderScript中对这些变量进行了初始化,那么在反射层也会进行相同的初始化。
全局指针。指针会被映射到.rs对应的类中。可以声明一个指针指向struct或者其他任何RenderScript支持的类型的指针。因为不容许在.rs中给指针分配内存,对于每个指针,都会生成一个对应的get方法以及bind_pointer_name,这个函数用于把在安卓VM中分配的内存绑定到RenderScript运行时。
定义的struct。Struct也是定义在.rs文件中,无论是单独定义struct还是和其他RenderScript代码放在一起,都会给每个单独的struct生成一个ScriptField_struct_name.java的类文件,你可以通过它来给一个或者多个该struct实例分配内存。但是注意:只有当你定义的struct在RenderScript代码中被用到了才会生成对应的类文件,若是没有使用的话则不会生成。在struct中不能含有指针或者数列。
Struct映射的详细解释

反射层生成的Struct主要包括:

构造函数:ScriptField_struct_name(RenderScript rs, int count),这个构造函数用来分配count数量的struct内存
构造函数:ScriptField_struct_name(RenderScript rs, int count, int usages)不仅通过count指定要分配的struct数量,并且通过usages指定这些内存被分配在哪个区域。主要有:
USAGE_SCRIPT:指定在脚本内存区分配内存,这也是默认的内存分配区
USAGE_GRAPHICS_TEXTURE: 在GPU的纹理内存区分配,API对其描述是:“The Allocation will be used as a texture source by one or more graphics programs.”,所以如果么有打算需要绘制这些图片就不要在TEXTURE分配了。
USAGE_GRAPHICS_VERTEX:在GPU的顶点内存区分配,API对其描述是:“The Allocation will be used as a graphics mesh. This was deprecated in API level 16.”,已经在API16废弃。
USAGE_GRAPHICS_CONSTANTS:在GPU的常量内存区分配。常量内存区被多个应用共同使用。API中对其描述是:“The Allocation will be used as the source of shader constants by one or more programs. This was deprecated in API level 16.”,同样也是在API16中就已经废弃。
可以使用或操作符来指定在多个内存区分配该内存,这样做表示向RenderScript表明:我想在多个内存区来访问该数据。

综上,能用的或者建议使用的就只有SCRIPT和TEXTURE两个,只需要记住:后面一个当你有使用GL绘制你分配的Allocation的时候才用即可。

一个Item内部类,通过该内部类你可以创建该结构的实例,这对于如果需要在安卓层中使用结构实例就非常有用。可以使用set(Item i, int index, boolean copyNow)方法来把某个Item实例插入到已经分配好的内存的指定位置。
结构中的每个字段都会有一个对应的set/get方法,且这些方法中都有一个index参数来指定要设置/读取内存区的哪个对象。每一个set方法都有一个copyNow参数来说明是否立即同步该内存到RenderScript运行时。通过调用copyAll方法可以同步所有还没有同步的内存。
创建该结构在内存中的描述Element,通过该Element可以分配由一个或者多个该结构对应的Element组成的内存。
resize()函数。就像C中的realloc()一样,可以扩展之前分配的内存,并保持之前创建的对象的值。
copyAll()用来同步在framework层设置的值到RenderScript运行时层。当调用set方法时,如果给copyNow设置的false,则将会在调用copyNow时同步到RenderScript运行时层。
代码举例:

.rs文件,文件名:script.rs

#pragma version(1)#pragma rs java_package_name(com.example.renderscripttest)#pragma rs_fp_relaxeduint32_t width;uint32_t height;rs_allocation inBitmap;rs_allocation rgbBitmap;rs_allocation yuvBitmap;//multipliers to convert a RGB colors to black and whiteconst static float3 gMonoMult = {0.299f, 0.587f, 0.114f};typedef struct Point_2D{    int x;    int y;}Point;static Point *spPoint;static Point sPoint;Point point;Point *pPoint;//google samplevoid root(const uchar4 *v_in, uchar4 *v_out) {  //unpack a color to a float4  float4 f4 = rsUnpackColor8888(*v_in);  //take the dot product of the color and the multiplier  float3 mono = dot(f4.rgb, gMonoMult);  //repack the float to a color  *v_out = rsPackColorTo8888(mono);}void __attribute((kernel)) setPoint(const uint2 in, uint32_t x, uint32_t y){rsDebug("lyh", point.x);    point.x = 9; //struct is used    point.y = 12;    rsSendToClient(0, &point, 1);    rsDebug("willhua", point.x);}uchar4 __attribute__((kernel)) halveBitmap(uchar4 in){    uchar4 out = in;    out.r = in.r / 2;    out.r = in.r / 2;    out.r = in.r / 2;    return out;}uchar4 __attribute__((kernel)) averageBitmap(uchar4 in, uint32_t x, uint32_t y){    uchar4 out = in;        uchar4 left = in;    uchar4 top = in;    uchar4 right = in;    uchar4 bottom = in;        if(x - 1 > -1){ //access other element        left = rsGetElementAt_uchar4(inBitmap, x - 1, y);    }    if(y - 1 > -1){        top = rsGetElementAt_uchar4(inBitmap, x , y - 1);    }    if(x + 1 < width){        right = rsGetElementAt_uchar4(inBitmap, x + 1, y);    }    if(y + 1 < height){        bottom = rsGetElementAt_uchar4(inBitmap, x, y + 1);    }        out.r = (left.r + top.r + right.r + bottom.r) / 4;    out.g = (left.g + top.g + right.g + bottom.g) / 4;    out.b = (left.b + top.b + right.b + bottom.b) / 4;        return out;}    

反射层生成的ScriptC子类:

/* * Copyright (C) 2011-2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* * This file is auto-generated. DO NOT MODIFY! * The source Renderscript file: G:\\Files\\EclipseWorkSpace\\RenderScriptTest\\src\\com\\example\\renderscripttest\\script.rs */package com.example.renderscripttest;import android.support.v8.renderscript.*;import android.content.res.Resources;/** * @hide */public class ScriptC_script extends ScriptC {    private static final String __rs_resource_name = "script";    // Constructor    public  ScriptC_script(RenderScript rs) {        this(rs,             rs.getApplicationContext().getResources(),             rs.getApplicationContext().getResources().getIdentifier(                 __rs_resource_name, "raw",                 rs.getApplicationContext().getPackageName()));    }    public  ScriptC_script(RenderScript rs, Resources resources, int id) {        super(rs, resources, id);        __U32 = Element.U32(rs);        __ALLOCATION = Element.ALLOCATION(rs);        __ScriptField_Point_2D = ScriptField_Point_2D.createElement(rs);        __U8_4 = Element.U8_4(rs);        __U32_2 = Element.U32_2(rs);    }    private Element __ALLOCATION;    private Element __ScriptField_Point_2D;    private Element __U32;    private Element __U32_2;    private Element __U8_4;    private FieldPacker __rs_fp_ALLOCATION;    private FieldPacker __rs_fp_ScriptField_Point_2D;    private FieldPacker __rs_fp_U32;    private final static int mExportVarIdx_width = 0;    private long mExportVar_width;    public synchronized void set_width(long v) {        if (__rs_fp_U32!= null) {            __rs_fp_U32.reset();        } else {            __rs_fp_U32 = new FieldPacker(4);        }        __rs_fp_U32.addU32(v);        setVar(mExportVarIdx_width, __rs_fp_U32);        mExportVar_width = v;    }    public long get_width() {        return mExportVar_width;    }    public Script.FieldID getFieldID_width() {        return createFieldID(mExportVarIdx_width, null);    }    private final static int mExportVarIdx_height = 1;    private long mExportVar_height;    public synchronized void set_height(long v) {        if (__rs_fp_U32!= null) {            __rs_fp_U32.reset();        } else {            __rs_fp_U32 = new FieldPacker(4);        }        __rs_fp_U32.addU32(v);        setVar(mExportVarIdx_height, __rs_fp_U32);        mExportVar_height = v;    }    public long get_height() {        return mExportVar_height;    }    public Script.FieldID getFieldID_height() {        return createFieldID(mExportVarIdx_height, null);    }    private final static int mExportVarIdx_inBitmap = 2;    private Allocation mExportVar_inBitmap;    public synchronized void set_inBitmap(Allocation v) {        setVar(mExportVarIdx_inBitmap, v);        mExportVar_inBitmap = v;    }    public Allocation get_inBitmap() {        return mExportVar_inBitmap;    }    public Script.FieldID getFieldID_inBitmap() {        return createFieldID(mExportVarIdx_inBitmap, null);    }    private final static int mExportVarIdx_rgbBitmap = 3;    private Allocation mExportVar_rgbBitmap;    public synchronized void set_rgbBitmap(Allocation v) {        setVar(mExportVarIdx_rgbBitmap, v);        mExportVar_rgbBitmap = v;    }    public Allocation get_rgbBitmap() {        return mExportVar_rgbBitmap;    }    public Script.FieldID getFieldID_rgbBitmap() {        return createFieldID(mExportVarIdx_rgbBitmap, null);    }    private final static int mExportVarIdx_yuvBitmap = 4;    private Allocation mExportVar_yuvBitmap;    public synchronized void set_yuvBitmap(Allocation v) {        setVar(mExportVarIdx_yuvBitmap, v);        mExportVar_yuvBitmap = v;    }    public Allocation get_yuvBitmap() {        return mExportVar_yuvBitmap;    }    public Script.FieldID getFieldID_yuvBitmap() {        return createFieldID(mExportVarIdx_yuvBitmap, null);    }    private final static int mExportVarIdx_point = 5;    private ScriptField_Point_2D.Item mExportVar_point;    public synchronized void set_point(ScriptField_Point_2D.Item v) {        mExportVar_point = v;        FieldPacker fp = new FieldPacker(8);        fp.addI32(v.x);        fp.addI32(v.y);        int []__dimArr = new int[1];        __dimArr[0] = 1;        setVar(mExportVarIdx_point, fp, __ScriptField_Point_2D, __dimArr);    }    public ScriptField_Point_2D.Item get_point() {        return mExportVar_point;    }    public Script.FieldID getFieldID_point() {        return createFieldID(mExportVarIdx_point, null);    }    private final static int mExportVarIdx_pPoint = 6;    private ScriptField_Point_2D mExportVar_pPoint;    public void bind_pPoint(ScriptField_Point_2D v) {        mExportVar_pPoint = v;        if (v == null) bindAllocation(null, mExportVarIdx_pPoint);        else bindAllocation(v.getAllocation(), mExportVarIdx_pPoint);    }    public ScriptField_Point_2D get_pPoint() {        return mExportVar_pPoint;    }    private final static int mExportForEachIdx_root = 0;    public Script.KernelID getKernelID_root() {        return createKernelID(mExportForEachIdx_root, 3, null, null);    }    public void forEach_root(Allocation ain, Allocation aout) {        forEach_root(ain, aout, null);    }    public void forEach_root(Allocation ain, Allocation aout, Script.LaunchOptions sc) {        // check ain        if (!ain.getType().getElement().isCompatible(__U8_4)) {            throw new RSRuntimeException("Type mismatch with U8_4!");        }        // check aout        if (!aout.getType().getElement().isCompatible(__U8_4)) {            throw new RSRuntimeException("Type mismatch with U8_4!");        }        Type t0, t1;        // Verify dimensions        t0 = ain.getType();        t1 = aout.getType();        if ((t0.getCount() != t1.getCount()) ||            (t0.getX() != t1.getX()) ||            (t0.getY() != t1.getY()) ||            (t0.getZ() != t1.getZ()) ||            (t0.hasFaces()   != t1.hasFaces()) ||            (t0.hasMipmaps() != t1.hasMipmaps())) {            throw new RSRuntimeException("Dimension mismatch between parameters ain and aout!");        }        forEach(mExportForEachIdx_root, ain, aout, null, sc);    }    private final static int mExportForEachIdx_setPoint = 1;    public Script.KernelID getKernelID_setPoint() {        return createKernelID(mExportForEachIdx_setPoint, 57, null, null);    }    public void forEach_setPoint(Allocation ain) {        forEach_setPoint(ain, null);    }    public void forEach_setPoint(Allocation ain, Script.LaunchOptions sc) {        // check ain        if (!ain.getType().getElement().isCompatible(__U32_2)) {            throw new RSRuntimeException("Type mismatch with U32_2!");        }        forEach(mExportForEachIdx_setPoint, ain, null, null, sc);    }    private final static int mExportForEachIdx_halveBitmap = 2;    public Script.KernelID getKernelID_halveBitmap() {        return createKernelID(mExportForEachIdx_halveBitmap, 35, null, null);    }    public void forEach_halveBitmap(Allocation ain, Allocation aout) {        forEach_halveBitmap(ain, aout, null);    }    public void forEach_halveBitmap(Allocation ain, Allocation aout, Script.LaunchOptions sc) {        // check ain        if (!ain.getType().getElement().isCompatible(__U8_4)) {            throw new RSRuntimeException("Type mismatch with U8_4!");        }        // check aout        if (!aout.getType().getElement().isCompatible(__U8_4)) {            throw new RSRuntimeException("Type mismatch with U8_4!");        }        Type t0, t1;        // Verify dimensions        t0 = ain.getType();        t1 = aout.getType();        if ((t0.getCount() != t1.getCount()) ||            (t0.getX() != t1.getX()) ||            (t0.getY() != t1.getY()) ||            (t0.getZ() != t1.getZ()) ||            (t0.hasFaces()   != t1.hasFaces()) ||            (t0.hasMipmaps() != t1.hasMipmaps())) {            throw new RSRuntimeException("Dimension mismatch between parameters ain and aout!");        }        forEach(mExportForEachIdx_halveBitmap, ain, aout, null, sc);    }    private final static int mExportForEachIdx_averageBitmap = 3;    public Script.KernelID getKernelID_averageBitmap() {        return createKernelID(mExportForEachIdx_averageBitmap, 59, null, null);    }    public void forEach_averageBitmap(Allocation ain, Allocation aout) {        forEach_averageBitmap(ain, aout, null);    }    public void forEach_averageBitmap(Allocation ain, Allocation aout, Script.LaunchOptions sc) {        // check ain        if (!ain.getType().getElement().isCompatible(__U8_4)) {            throw new RSRuntimeException("Type mismatch with U8_4!");        }        // check aout        if (!aout.getType().getElement().isCompatible(__U8_4)) {            throw new RSRuntimeException("Type mismatch with U8_4!");        }        Type t0, t1;        // Verify dimensions        t0 = ain.getType();        t1 = aout.getType();        if ((t0.getCount() != t1.getCount()) ||            (t0.getX() != t1.getX()) ||            (t0.getY() != t1.getY()) ||            (t0.getZ() != t1.getZ()) ||            (t0.hasFaces()   != t1.hasFaces()) ||            (t0.hasMipmaps() != t1.hasMipmaps())) {            throw new RSRuntimeException("Dimension mismatch between parameters ain and aout!");        }        forEach(mExportForEachIdx_averageBitmap, ain, aout, null, sc);    }}

反射层生成的struct,Point_2D, 对应的类:

/* * Copyright (C) 2011-2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* * This file is auto-generated. DO NOT MODIFY! * The source Renderscript file: G:\\Files\\EclipseWorkSpace\\RenderScriptTest\\src\\com\\example\\renderscripttest\\script.rs */package com.example.renderscripttest;import android.support.v8.renderscript.*;import android.content.res.Resources;/** * @hide */public class ScriptField_Point_2D extends android.support.v8.renderscript.Script.FieldBase {    static public class Item {        public static final int sizeof = 8;        int x;        int y;        Item() {        }    }    private Item mItemArray[];    private FieldPacker mIOBuffer;    private static java.lang.ref.WeakReference mElementCache = new java.lang.ref.WeakReference(null);    public static Element createElement(RenderScript rs) {        Element.Builder eb = new Element.Builder(rs);        eb.add(Element.I32(rs), "x");        eb.add(Element.I32(rs), "y");        return eb.create();    }    private  ScriptField_Point_2D(RenderScript rs) {        mItemArray = null;        mIOBuffer = null;        mElement = createElement(rs);    }    public  ScriptField_Point_2D(RenderScript rs, int count) {        mItemArray = null;        mIOBuffer = null;        mElement = createElement(rs);        init(rs, count);    }    public  ScriptField_Point_2D(RenderScript rs, int count, int usages) {        mItemArray = null;        mIOBuffer = null;        mElement = createElement(rs);        init(rs, count, usages);    }    public static ScriptField_Point_2D create1D(RenderScript rs, int dimX, int usages) {        ScriptField_Point_2D obj = new ScriptField_Point_2D(rs);        obj.mAllocation = Allocation.createSized(rs, obj.mElement, dimX, usages);        return obj;    }    public static ScriptField_Point_2D create1D(RenderScript rs, int dimX) {        return create1D(rs, dimX, Allocation.USAGE_SCRIPT);    }    public static ScriptField_Point_2D create2D(RenderScript rs, int dimX, int dimY) {        return create2D(rs, dimX, dimY, Allocation.USAGE_SCRIPT);    }    public static ScriptField_Point_2D create2D(RenderScript rs, int dimX, int dimY, int usages) {        ScriptField_Point_2D obj = new ScriptField_Point_2D(rs);        Type.Builder b = new Type.Builder(rs, obj.mElement);        b.setX(dimX);        b.setY(dimY);        Type t = b.create();        obj.mAllocation = Allocation.createTyped(rs, t, usages);        return obj;    }    public static Type.Builder createTypeBuilder(RenderScript rs) {        Element e = createElement(rs);        return new Type.Builder(rs, e);    }    public static ScriptField_Point_2D createCustom(RenderScript rs, Type.Builder tb, int usages) {        ScriptField_Point_2D obj = new ScriptField_Point_2D(rs);        Type t = tb.create();        if (t.getElement() != obj.mElement) {            throw new RSIllegalArgumentException("Type.Builder did not match expected element type.");        }        obj.mAllocation = Allocation.createTyped(rs, t, usages);        return obj;    }    private void copyToArrayLocal(Item i, FieldPacker fp) {        fp.addI32(i.x);        fp.addI32(i.y);    }    private void copyToArray(Item i, int index) {        if (mIOBuffer == null) mIOBuffer = new FieldPacker(mElement.getBytesSize() * getType().getX()/* count */);        mIOBuffer.reset(index * mElement.getBytesSize());        copyToArrayLocal(i, mIOBuffer);    }    public synchronized 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);            FieldPacker fp = new FieldPacker(mElement.getBytesSize());            copyToArrayLocal(i, fp);            mAllocation.setFromFieldPacker(index, fp);        }    }    public synchronized Item get(int index) {        if (mItemArray == null) return null;        return mItemArray[index];    }    public synchronized void set_x(int index, int v, boolean copyNow) {        if (mIOBuffer == null) mIOBuffer = new FieldPacker(mElement.getBytesSize() * getType().getX()/* count */);        if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */];        if (mItemArray[index] == null) mItemArray[index] = new Item();        mItemArray[index].x = v;        if (copyNow)  {            mIOBuffer.reset(index * mElement.getBytesSize());            mIOBuffer.addI32(v);            FieldPacker fp = new FieldPacker(4);            fp.addI32(v);            mAllocation.setFromFieldPacker(index, 0, fp);        }    }    public synchronized void set_y(int index, int v, boolean copyNow) {        if (mIOBuffer == null) mIOBuffer = new FieldPacker(mElement.getBytesSize() * getType().getX()/* count */);        if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */];        if (mItemArray[index] == null) mItemArray[index] = new Item();        mItemArray[index].y = v;        if (copyNow)  {            mIOBuffer.reset(index * mElement.getBytesSize() + 4);            mIOBuffer.addI32(v);            FieldPacker fp = new FieldPacker(4);            fp.addI32(v);            mAllocation.setFromFieldPacker(index, 1, fp);        }    }    public synchronized int get_x(int index) {        if (mItemArray == null) return 0;        return mItemArray[index].x;    }    public synchronized int get_y(int index) {        if (mItemArray == null) return 0;        return mItemArray[index].y;    }    public synchronized void copyAll() {        for (int ct = 0; ct < mItemArray.length; ct++) copyToArray(mItemArray[ct], ct);        mAllocation.setFromFieldPacker(0, mIOBuffer);    }}

4 内存

我们已经知道,应用自身运行在安卓VM中,而RenderScript部分代码则运行在本地,且其内存是由上层的应用分配的。

4.1 内存分配API

内存API主要包含三个类:Element,Type与Allocation。他们三者对应的关系能用malloc函数的使用来很好的说明,例如:

int *array = (int )malloc(sizeof(int) * 10);
malloc函数的参数可以分成两个部分:第一个就是sizeof(int),该过程指定每个内存单元需要多个内存;第二个就是
10,该过程指定需要分配多个这样的内存单元。对应的内存分配APIs就是Element类,表示的是一个内存单元,就像一个float或者一个struct所表示的内存。而Type表示的就是“*10”部分,就像一个Element序列一样。Allocation则用来执行由Type所描述的内存分配,且最终代表分配得到的内存。

大部分情况下都不需要直接使用这些API,因为系统在自动生成反射层的时候已经做好了封装,需要做的就是调用对应类的构造函数并把分配的内存绑定到RenderScript而已。但是比如当你加载一张图片到RenderScript层或者给一个指针分配内存时,就需要应用到这些API。

4.2 静态内存

4.2.1 静态内存的分配

这里的静态内存指的是在RenderScript中声明的非静态的全局变量(静态的或者局部的变量就无法在Android framework层访问,也就不讨论),他们在编译时就分配了内存,在RenderScript代码中可以直接使用它们而不需要在Android Framework层给他们分配内存。在Android Framework层也可以通过反射层生成的函数来访问他们。如果这些变量在RenderScript中被初始化,那么他们也将在Android Framework层中被进行同样的初始化。

注意:如果在RenderScript中使用到了RenderScript中预定义了的一些含有指针的结构体,比如rs_program_fragment和rs_allocation,那么就需要先在Android Framework层构造一个该结构对应类的实例,然后调用set方法把内存绑定到RenderScript运行时,而不能直接在RenderScript层操作。但是这个对于用户自定义的包含指针的结果无效,因为根本就无法自定义包含指针的结构。

4.2.2 静态内存的读写

在RenderScript层对静态分配的内存的写操作是单向的。当你在RenderScript层修改了某个变量的值,出于性能方面的考虑,这个变化不会反应给安卓层。在安卓层调用get方法获得的是安卓层最后一次通过set方法设置的值,除非通过rsSendToClient()等手段,否则安卓层是永远获取不到RenderScript对静态分配的内存的修改的。但是,当安卓层修改了某个变量的值后,该值的变化随后就会自动同步到RenderScript层。

下面是读写示例。假如在rsfile.rs中定义了全局变量point:

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

那么在RenderScript中可以如下直接给变量赋值:

point.x = 1;
point.y = 1;
在Android framework层中可以这样修改该变量的值,且修改的值会通知到RenderScript层:

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

在RenderScript中读取变量的值:

rsDebug(“Printing out a Point”, point.x, point.y);
在Android framework中读取变量的值。再一次强调:读取到的永远是Android framework层最后一次调用set方法给该变量赋的值,而如果在Android framework层没有用set方法给变量赋过值的话,那么读取的将是null,下面代码也会报空指针错误

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

4.3 动态内存

4.3.1 动态内存的分配

对于动态内存,比如指针,就必须在Android Framework层中给它分配内存。需要两个过程:分配内存与绑定内存。这样做的好处在于:安卓VM能够完全掌握RenderScript内存的分配与回收。

无论在Android Framework层还是RenderScript中,都可以通过该指针来访问分配的内存。

为了给RenderScript动态分配内存,最常见的做法是调用Script.FieldBase的构造函数,当然好也可以手动创建Allocation来实现,为了简单起见,应该使用Script.FieldBase.当获取分配的内存对象后,就可以通过反射层的bind方法把该内存绑定到RenderScript。下面代码是两种实现方式的例子:

private RenderScript myRenderScript;private ScriptC_example mScript;private Resources resources;public void init(RenderScript rs, Resources res) {    myRenderScript = rs;    resources = res;     //使用反射层生成的类分配内存    ScriptField_Point touchPoints = new ScriptField_Point(myRenderScript, 2);    //自己使用API分配内存    intPointer = Allocation.createSized(myRenderScript, Element.I32(myRenderScript), 2);    mScript = new ScriptC_example(myRenderScript, resources, R.raw.example);     //绑定内存    mScript.bind_touchPoints(touchPoints);    mScript.bind_intPointer(intPointer);   ...}

4.3.2 动态内存的读写

对于动态内存,在Android framework层可以通过反射层的set/get方法来读/写内存,在RenderScript中也可以像往常一样读/写,且任意一方的写操作都会通知到另外一方。

下面是示例。假设在rsfile.rs定义了如下全局指针:

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

只要你已经在Android framework层给分配了内存,那么就可以像往常一样使用它,且任何的修改都会通知到Android framework层。

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

在Android framework 层通过反射层提供的方法读写:

ScriptField_Point p = new ScriptField_Point(mRS, 1);    Item i = new ScriptField_Point.Item();    i.x=100;    i.y = 100;    p.set(i, 0, true);    mScript.bind_point(p);     points.get_x(0);            //read x and y from index 0    points.get_x(0);

内存只需要绑定一次就可以了,不需要每次修改值的时候再次绑定

5 基本应用场景

5.1 RenderScript层回调Android framework层

前面我们提到过,RenderScript中的invokable不能有返回值,以及对于静态内存,RenderScript层对其修改不会通知到Android framework层。对于这两种情况,配套使用RSMessageHandler和rsSendToClient是很好的解决方案。对于二者的关系,从命名就可以看出一二,RSMessageHandler相当于常用的handleMessage函数,而rsSendToClient则相当于Handler.sendMessage,只是RSMessageHandler运行在Android framework层,而rsSendToClient运行在RenderScript层。

RenderScript.RSMessageHandler它implements Runnable,在使用过程中只需要重写run函数即可。其有三个重要的字段:

mData:int[],对应rsSendToClient中的data,表示从RenderScript发送过来的数据的地址
mID:消息标志,对应rsSendToClient中的cmdID,类似使用Handler发送Message时的what
mLength:对应rsSendToClient时的len,mData中数据的个数
使用范例:

在RenderScript(.rs文件)中调用回调函数rsSendToClicent,reSendToClient函数有四个: rsSendToClient (int cmdID) rsSendToClient (int cmdID, const void *data, uint len) rsSendToClientBlocking (int cmdID) rsSendToClientBlocking (int cmdID, const void *data, uint len),其中cmdID即相当于message中的what参数。
在安卓层中,设定RenderScript中消息的Handler。即继承RenderScript.RSMessageHandler写一个类RSHandler,重写其中的run函数。在RenderScript.RSMessageHandler中有一个mID参数,即若与reSendToClient中的cmdID相等则表示是该reSendToClient发送过来的消息。

        mRenderScript.setMessageHandler(new RSMessageHandler(){            @Override            public void run(){                switch (mID) {                case type:                    //do something                    break;                default:                    break;                }            }        });

注意:run函数不是运行在主线程,所以在run函数中不能直接做操作主界面UI的操作。

5.2 在RenderScript kernel中访问更多的元素

我们提到过,对于一个kernel,最多只能有一个输入Allocation,假如需要在kernel中访问更多的Allocation,那怎么办呢?

在kernel中,仅容许对当前元素进行操作,即当前坐标(x,y)表示的元素,如果想访问其他元素,则需要定义一个全局的输入allocation,然后使用rsGetElementAt_type()来获取其他元素,比如:下面的averageBitmap就访问了全局变量inBitmap的数据:

rs_allocation inBitmap;uchar4 __attribute__((kernel)) averageBitmap(uchar4 in, uint32_t x, uint32_t y){    uchar4 out = in;        uchar4 left = in;    uchar4 top = in;    uchar4 right = in;    uchar4 bottom = in;        if(x - 1 > -1){ //access other element        left = rsGetElementAt_uchar4(inBitmap, x - 1, y);    }    if(y - 1 > -1){        top = rsGetElementAt_uchar4(inBitmap, x , y - 1);    }    if(x + 1 < width){        right = rsGetElementAt_uchar4(inBitmap, x + 1, y);    }    if(y + 1 < height){        bottom = rsGetElementAt_uchar4(inBitmap, x, y + 1);    }        out.r = (left.r + top.r + right.r + bottom.r) / 4;    out.g = (left.g + top.g + right.g + bottom.g) / 4;    out.b = (left.b + top.b + right.b + bottom.b) / 4;        return out;}    

参考资料

官方demo

转自:https://www.cnblogs.com/willhua/p/5778415.html

更多相关文章

  1. Android(安卓)Intent 使用 Parcel 反序列化出错.
  2. Android(安卓)Cursor源码笔记(2)
  3. Cocos2d-x 3.0的启动流程
  4. 使用javah和javap得到JNI接口的签名
  5. 【Frida Hook 学习记录】Frida Hook Android(安卓)常用方法
  6. android binder 讲解之权限管理
  7. 硬件访问服务2之让Android应用程序访问C库
  8. Mms模块ConversationList流程分析(1)
  9. Android(安卓)ICS创建StagefrightPlayer播放器的过程

随机推荐

  1. 利用Handler定时更新Android UI
  2. adb android设备无法连上eclipse
  3. Android-两种方式实现走马灯效果
  4. Android 中文 SDK (47) —— Filter
  5. Android 启动过程(2)
  6. Android使用TabHost程序异常终止could no
  7. Android的OpenGL学习笔记(5)
  8. Android 中数据库查询方法 query() 中的
  9. Android之实现textvew跑马灯效果
  10. unity内置浏览器插件UniWebView的使用(支