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指定这些内存被分配在哪个区域。主要有:
  1. USAGE_SCRIPT:指定在脚本内存区分配内存,这也是默认的内存分配区
  2. USAGE_GRAPHICS_TEXTURE: 在GPU的纹理内存区分配,API对其描述是:"The Allocation will be used as a texture source by one or more graphics programs.",所以如果么有打算需要绘制这些图片就不要在TEXTURE分配了。
  3. USAGE_GRAPHICS_VERTEX:在GPU的顶点内存区分配,API对其描述是:"The Allocation will be used as a graphics mesh. This was deprecated in API level 16.",已经在API16废弃。
  4. 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_relaxed

uint32_t width;
uint32_t height;

rs_allocation inBitmap;
rs_allocation rgbBitmap;
rs_allocation yuvBitmap;

//multipliers to convert a RGB colors to black and white
const 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 sample
void 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;
}    
View Code

相关文章: