您的位置:首页 > 其它

RenderScript

2015-10-11 20:44 621 查看
9.1 Overview 概览
9.2 Hellow World
9.3 Hellow Rendering
9.4 Adding Variables to Script 脚本中添加变量
9.5 HelloCompute
9.6 Native RenderScript APIs
9.7 RenderScript vs. NDK

Introduced in Honeycomb (API level 11), RenderScript is a new framework targeted at high-performance高性能 3D rendering and compute operations. While RenderScript was already used back in Android 2.1 (clair), Honeycomb is the first Android version where RenderScript is made publicly available.
After a brief overview of RenderScript, you will learn how to create a simple script and
how to use it from your Java application. Then you will learn how to perform rendering
operations from your script and how to access your script’s data from Java. We will
review one of the sample applications provided in the Android SDK, HelloCompute, and
compare two implementations of the same application, one based on Honeycomb APIs
and the other based on Ice Cream Sandwich (Android 4.0) APIs. To conclude this
chapter, we will review the RenderScript header files for both Honeycomb and Ice
Cream Sandwich, and the functions you can use in your scripts在脚本中可使用的头文件.
NOTE: This chapter is not meant to be a complete guide to 3D rendering. For example, one should acquire knowledge of the OpenGL Shading Language (GLSL) as well in order to use advanced features of RenderScript. Whole books are dedicated to OpenGL and GLSL, and mastering 3D rendering can take years of practice.

9.1 Overview 概览

There aren’t that many applications currently using RenderScript. Since a picture is
worth a thousand words一图胜千言, let’s see examples of where RenderScript is used. Perhaps the most popular example is the YouTube application and its carousel行李传送带(旋转***) view, as shown in Figure 9–1. For clarity, the screenshot was taken when the videos were not loaded yet, and you can see what each rectangle looks like (each rectangle would then contain a video thumbnail视频缩略图). Another example, Balls, is available as sample code in the Android SDK and uses sensors’ information and physic to compute the position of the balls on the screen, as shown in Figure 9–2. 9
Figure 9–1. YouTube Carousel view
Figure 9–2. Balls sample application
NOTE: To create a project from sample code (for example, Balls), create a new Android project in Eclipse and select “Create project from existing sample,” or simply create a new Android Sample project (option available with newer versions of the Eclipse ADT plugin). RenderScript samples are available only when you select 3.0 or above as the target. You can download a carousel example from
http://code.google.com/p/android-ui-utils/downloads/list.
RenderScript uses C99 as its language but is not meant to completely replace your Java
application. Instead, your application will use RenderScript for certain parts and Java for
others. When you use RenderScript, your scripts will be compiled to Low-Level Virtual
Machine (LLVM) bitcode on the host machine (for example, your Windows PC on which
you installed Eclipse), and the LLVM bitcode位码 will then be compiled to native code on the Android device itself. In that respect, it is similar to your Java code being compiled to
Dalvik bytecode字节码 on the host, which can be compiled to native code on the device by the Dalvik Just-In-Time compiler.
The native code is then cached on your device, so subsequent后续 executions of the script will be faster as the script will already be compiled to native code. As compilation of the bitcode位码的编译 to native code occurs on the device, the resulting native code can be optimized for the device, or even hardware module (for example, GPU), and you as a developer won’t have to worry about the various architectures and features. This is a major difference with the NDK, where, for example, your code has to check whether NEON is available, and should simplify your workflow significantly大大简化你的工作流程.
NOTE: For more information about LLVM, visit http://llvm.org. You can download and
install various LLVM tools from there as well. LLVM is not an Android-specific technology. As a matter of fact, it is used by Apple’s Xcode 4.
Before we talk in more detail about what you can do in RenderScript, we are going to
walk through演练 how to create a basic RenderScript script in a project. Since the emulator does not support all the features needed by RenderScript you will need an actual Android device to use RenderScript. You will often find that a RenderScript script is
simply referred to as a RenderScript.

9.2 Hello World

As always when introduced to a new framework, your first instinct反应 should be to say
“hello” to the world from a RenderScript script. To create a script, simply create a new
file in your Android project and call it helloworld.rs. Listing 9–1 shows how we can create
a simple function that will output the “Hello, World” string using a simple RenderScript
debugging function调试函数.
#pragma version(1)
#pragma rs java_package_name(com.apress.proandroid.ch9)
void hello_world() {
rsDebug("Hello, World", 0); // 0 is because we cannot send only a string to the
debug output...
}

The first line declares which version of RenderScript your script uses. The second line declares the package name of the Java reflection of this script. As you build your Android project, many things will happen under the hood头巾, and the build tools will create
the following three files:
ScriptC_helloworld.java (in gen/ directory)
helloworld.d (in gen/ directory)
helloworld.bc (in res/raw/ directory)
Listing 9–2 shows the content of the auto-generated ScriptC_helloworld.java file. You can find that file in your project’s gen directory, which is the same directory R.java is generated. This file is part of the reflected layer of your script and contains the definition of the class that will allow you to invoke调用 functions from your script and communicate with your script in general. In that same directory you will also find helloworld.d, which simply lists the dependencies for the helloworld.bc file (that is, helloworld.bc would have to be generated again if any of the listed dependencies in helloworld.d is modified).
Listing 9–2. ScriptC_helloworld.java
/*
* Copyright (C) 2011 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: E:\ws\Chapter
9\src\com\apress\proandroid\ch9\helloworld.rs
*/
package com.apress.proandroid.ch9;
import android.renderscript.*;
import android.content.res.Resources;
/**
* @hide
*/
public class ScriptC_helloworld extends ScriptC {
// Constructor
public ScriptC_helloworld(RenderScript rs, Resources resources, int id) {
super(rs, resources, id);
}
private final static int mExportFuncIdx_hello_world = 0;
public void invoke_hello_world() {
invoke(mExportFuncIdx_hello_world);
}
}
Three things stand out in this auto-generated file:
The package name is the one defined in the script with #pragma
The public constructor公共构造函数
The invoke_hello_world function
Parts of this auto-generated file, even though they are important, are not relevant to you不需要你操心. For example, the private mExportFuncIdx_hello_world constant in Listing 9–2 refers to the index of a function in the script, but its actual value is of no importance to you or your application.
To use the script, you will have to create an instance of ScriptC_helloworld in your Java
code, which will then allow you to call the script’s hello_world function via the reflected
invoke_hello_world method, as shown in Listing 9–3.
Listing 9–3. Calling RenderScript Function
public class Chapter9Activity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
HelloWorldRenderScript();
}
private void HelloWorldRenderScript() {
RenderScript rs = RenderScript.create(this); // needs a Context as parameter
// script created using the helloworld bitcode in res/raw/helloworld.bc
ScriptC_helloworld helloworldScript = new ScriptC_helloworld(rs, getResources(),
R.raw.helloworld);
// now we can call the script’s hello_world function using the reflected method
helloworldScript.invoke_hello_world();
}
}

Executing this code will result in “Hello, World 0 0x0” being displayed in logcat with the
tag “RenderScript”.
NOTE: Use DDMS perspective in Eclipse to observe that RenderScript-related threads are now running in the application (with the name RSMessageThread). You can also see the following output in logcat: “RS Launching thread(s), reported CPU count 2” (if your device has 2 cores). As you learn about RenderScript, observe the outputs generated with the tag “RenderScript”.
While the helloworld.bc file is critical as it contains the script bitcode, its actual content
is not of great value to you. All you should care about is the fact that the LLVM bitcode
is the platform-independent representation of your script. That being said, it is possible
to disassemble this file and convert it to human-readable LLVM assembly language
using the LLVM disassembler llvm-dis (available as part of the LLVM suite). Listing 9–4
shows the LLVM assembly language version of helloworld.bc.
Listing 9–4. LLVM Assembly Language (helloworld.bc)
; ModuleID = '/home/herve/helloworld.bc'
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-
f64:64:64-v64:64:64-v128:128:128-a0:0:64-n32"
target triple = "armv7-none-linux-gnueabi"
@.str = private constant [13 x i8] c"Hello, World\00"
define void @hello_world() nounwind {
tail call void @_Z7rsDebugPKci(i8* getelementptr inbounds ([13 x i8]* @.str, i32 0,
i32 0), i32 0) nounwind
ret void
}
declare void @_Z7rsDebugPKci(i8*, i32)
!#pragma = !{!0, !1}
!#rs_export_func = !{!2}
!0 = metadata !{metadata !"version", metadata !"1"}
!1 = metadata !{metadata !"java_package_name", metadata !"com.apress.proandroid.ch9"}
!2 = metadata !{metadata !"hello_world"}
This example did not perform any rendering using RenderScript. In fact, the activity’s
layout was still defined using XML and the activity content was still set from a layout
resource which was inflated when setContentView() was called. Now that you know
how to create a basic script, it is time to see how rendering can be performed with
RenderScript.

9.3 Hello Rendering

Rendering with RenderScript is a little bit more complicated than simply invoking a script
function, and requires a little more work before you can see anything on the screen. You
will need to:
Create a script that does the rendering.
Create a RenderScriptGL context object上下文对象.
Create a class that extends RSSurfaceView.
Set your RSSurfaceView as the content view for your activity.

9.3.1 Creating a Rendering Script

Our first rendering script will be extremely simple as it will only change the background
color to some random color. The script is shown in Listing 9–5 and is in a file called
hellorendering.rs. The names of the RenderScript files are important as they define the
resource name (in that case R.raw.hellorendering, which is simply defined as an integer
in R.java).
Listing 9–5. Changing Background Color
#pragma version(1)
#pragma rs java_package_name(com.apress.proandroid.ch9)
#include "rs_graphics.rsh"
// invoked automatically when the script is created
void init() {
// do whatever you need to do here...
}
int root() {
float red = rsRand(1.0f);
float green = rsRand(1.0f);
float blue = rsRand(1.0f);
// clear the background color with random color
rsgClearColor(red, green, blue, 1.0f); // alpha is 1.0f, i.e. opaque
// 50 frames per second = 20 milliseconds per frame
return 20;
}
The first two lines of the files are the same as in Listing 9–1. The following line is to
include the rs_graphics.rsh header file, where rsgClearColor() is defined. Header files
are automatically included except for rs_graphics.rsh, so you will have to explicitly
include that file when you use graphics RenderScript functions such as rsgClearColor()
or rsgBindFont().
The init() function here is for informational purpose提示作用 only as its implementation is empty. The init() function is optional and, if present, will be called automatically when
the script is created. This allows you to perform any initialization before other routines
are executed. A script that does no rendering, like the one shown in Listing 9–1, could
also have an init() function.
The root() function is where the rendering actually happens. The implementation here is
trivial as all it does is clear the background with a random color. What is interesting
though is the return value of the function as it specifies how often rendering will occur. In this particular case we return 20, which means the frame should be updated every 20
milliseconds (that is, frame rate of 50 frames per second).
NOTE: init() and root() are reserved functions and do not result in invoke_init() and
invoke_root() methods being created in the reflected layer (ScriptC_hellorendering.java in
this case).

9.3.2 Creating a RenderScriptGL Context

Now that the rendering script is complete, you will need a RenderScriptGL context
object. Here the RenderScriptGL context object is created when the RSSurfaceView’s
surfaceChanged() method is called. The HelloRenderingView implementation is shown in
Listing 9–6.
Listing 9–6. HelloRenderingView.java
public class HelloRenderingView extends RSSurfaceView {
public HelloRenderingView(Context context) {
super(context);
}
private RenderScriptGL mRS;
private HelloRenderingRS mRender; // helper class
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
super.surfaceChanged(holder, format, w, h);
if (mRS == null) {
RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig();
mRS = createRenderScriptGL(sc);
mRender = new HelloRenderingRS();
mRender.init(mRS, getResources());
}
mRS.setSurface(holder, w, h);
}
@Override
protected void onDetachedFromWindow() {
if(mRS != null) {
mRS = null;
destroyRenderScriptGL();
}
}
}
As you can see in Listing 9–6, a HelloRenderingRS helper class is used as well.

9.3.3 Extending RSSurfaceView

The implementation of this class is shown in Listing 9–7.
Listing 9–7. HelloRenderingRS.java
public class HelloRenderingRS {
public HelloRenderingRS() {
}
private Resources mRes;
private RenderScriptGL mRS;
private ScriptC_hellorendering mScript;
public void init(RenderScriptGL rs, Resources res) {
mRS = rs;
mRes = res;
mScript = new ScriptC_hellorendering(mRS, mRes, R.raw.hellorendering);
mRS.bindRootScript(mScript);
}
}
In this class, we create the actual ScriptC_hellorendering script and bind it to the
RenderScriptGL object. Without the call to bindRootScript(), nothing would actually get
rendered as the script’s root() function would not be called. If you find yourself staring
at a black screen when you were expecting some rendering to happen, first check you
did not forget to call bindRootScript().

9.3.4 Setting the Content View
Now that all these files exist, you can create an instance of HelloRenderingView and set
the activity’s content to this instance, as shown in Listing 9–8.
Listing 9–8. Activity
public class Chapter9Activity extends Activity {
static final String TAG = "Chapter9Activity";
private HelloRenderingView mHelloRenderingView;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.main); // we don’t use the XML layout anymore here so
we comment it out
HelloWorldRenderScript();
mHelloRenderingView = new HelloRenderingView(this);
setContentView(mHelloRenderingView);
240 CHAPTER 9: RenderScript
}
@Override
protected void onPause() {
super.onPause();
mHelloRenderingView.pause(); // to pause the rendering thread
}
@Override
protected void onResume() {
super.onResume();
mHelloRenderingView.resume(); // to resume the rendering thread
}
private void HelloWorldRenderScript() {
// see Listing 9–3 for implementation
}
}
While most of this file should be trivial to you now, there is one thing in this file worth noting: the RSSurfaceView must be told when the activity is paused and resumed. Without the calls to RSSurfaceView.pause() and RSSurfaceView.resume(), the rendering thread would not be aware of the activity’s state and would carry on rendering things even when the activity is not necessarily visible.
In Android 4.0 two new important features are added:
Your application can use an allocation as an off-screen buffer by setting the Allocation.USAGE_GRAPHICS_RENDER_TARGET flag
when creating the allocation object.
You can use an RSTextureView in your layout (together with other views). While an RSSurfaceView creates a separate window, an RSTextureView does not.

9.4 Adding Variables to Script 脚本中添加变量

Let’s modify Listing 9–5 a little bit to learn more about the reflected layer. So far, we only saw how the hello_world routine from the script was reflected in Listing 9–2. Here we are going to add more variables in the script, which we will call hellorendering2.rs, as
shown in Listing 9–9.
Listing 9–9. Changing Background Color (Second Version)
#pragma version(1)
#pragma rs java_package_name(com.apress.proandroid.ch9)
#include "rs_graphics.rsh"
// we removed init() as it was empty anyway
float red = 0.0f; // initialized to 1.0f
float green; // purposely not initialized
static float blue; // purposely not initialized, static
const float alpha = 1.0f; // constant
int root() {
// clear the background color
blue = rsRand(1.0f);
rsgClearColor(red, green, blue, alpha);
// 50 frames per second = 20 milliseconds per frame
return 20;
}
As you can see, the four variables—red, green, blue, and alpha—are all defined
differently. While this does not necessarily make sense for the actual script, it allows us to see how the reflected layer is created. Listing 9–10 shows the
ScriptC_hellorendering2.java class.
Listing 9–10. ScriptC_hellorendering2.java
/*
* Copyright (C) 2011 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: E:\ws\Chapter
9\src\com\apress\proandroid\ch9\hellorendering2.rs
*/
package com.apress.proandroid.ch9;
import android.renderscript.*;
import android.content.res.Resources;
/**
* @hide
*/
public class ScriptC_hellorendering2 extends ScriptC {
// Constructor
public ScriptC_hellorendering2(RenderScript rs, Resources resources, int id) {
super(rs, resources, id);
mExportVar_red = 0f;
mExportVar_alpha = 1f;
}
private final static int mExportVarIdx_red = 0;
private float mExportVar_red;
public void set_red(float v) {
mExportVar_red = v;
setVar(mExportVarIdx_red, v);
}
public float get_red() {
return mExportVar_red;
}
private final static int mExportVarIdx_green = 1;
private float mExportVar_green;
public void set_green(float v) {
mExportVar_green = v;
setVar(mExportVarIdx_green, v);
}
public float get_green() {
return mExportVar_green;
}
private final static int mExportVarIdx_alpha = 2;
private float mExportVar_alpha;
public float get_alpha() {
return mExportVar_alpha;
}
}
The global red variable was defined in the script and initialized to zero. Consequently,
the reflected layer defines a private mExportVar_red floating-point member, initialized to
zero in the constructor, as well as two methods to set and get the red value: set_red()
and get_red().
The global green variable in the script was very similar to the red one, except for the fact
that it was not initialized. The reflected layer therefore also defines a private
mExportVar_green floating-point member as well as two methods, set_green() and
get_green(). However, the mExportVar_green member is not initialized in the
constructor.
The blue variable was defined as static, which means it is not exported outside the
script. As a consequence, the reflected layer does not define any member or method
related to the script’s blue component.
Finally, the alpha component was defined as a constant, and therefore the reflected
layer contains the initialization of the mExportVar_alpha member in the constructor and
only defines one method, get_alpha(), to access the value of the member. Since the
alpha component is constant固定不变, there is indeed no need to define any set_alpha() method in Java.
NOTE: Global pointers defined in the script would result in a bind_pointer_name() method in the reflected layer instead of a set_pointer_name() method.

As you can see, a get() method in the reflected layer returns the last value set in Java.
For example, get_green() simply returns mExportVar_green, which can only be modified
by a call to set_green(). This basically means that if the script modifies the value of the
global green variable, the change won’t propagate通知 to the Java layer. This is a very
important detail to be aware of.
We will now review one of the simple examples provided by Android as sample code to
learn more about one of the most important RenderScript functions.

9.5 HelloCompute

The Android HelloCompute sample application simply converts a color picture to black
and white. Listing 9–11 shows the script implementation (build target is Honeycomb).
Listing 9–11. RenderScript mono.rs
/*
* Copyright (C) 2011 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.
*/
#pragma version(1)
#pragma rs java_package_name(com.android.example.hellocompute)
rs_allocation gIn;
rs_allocation gOut;
rs_script gScript;
const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
void root(const uchar4 *v_in, uchar4 *v_out, const void *usrData, uint32_t x, uint32_t
y) {
float4 f4 = rsUnpackColor8888(*v_in);
float3 mono = dot(f4.rgb, gMonoMult);
*v_out = rsPackColorTo8888(mono);
}
void filter() {
rsForEach(gScript, gIn, gOut, 0); // first: script; second: input allocation; third:
output allocation
}
The root() function converts a single pixel to black and white. It does so in four steps:
1. The ARGB value (*v_in) is converted to a floating-point vector.
2. root() performs the dot product of f4.rgb and gMonoMult, which returns a single floating-point value (the luminance亮度).
3. All three entries分量 in the mono vector are given the same value (the result of the dot product, that is, the luminance).
4. The mono vector is converted to an ARGB value that is copied into
*v_out.
NOTE: The gMonoMult values are defined by the NTSC standard.
In order for the script to convert a whole picture to black and white, it has to be told to execute the root() function on each pixel. This is what the filter() function is doing by calling the rsForEach() function. The rsForEach() function
executes the script (first parameter) for each pair of elements in the input and
output allocations (second and third parameters). The fourth parameter,
usrData, is set to zero here and is ignored by the root() function.

9.5.1 Allocations

As rsForEach() uses allocations as parameters, allocations have to be created
in Java and must then be passed down to the script. This is what
createScript() in HelloCompute.java does among other things, as shown in
Listing 9–12.
Listing 9–12. Allocations
private RenderScript mRS;
private Allocation mInAllocation; // input allocation
private Allocation mOutAllocation; // output allocation
private ScriptC_mono mScript;
...
private void createScript() {
mRS = RenderScript.create(this);
mInAllocation = Allocation.createFromBitmap(mRS, mBitmapIn,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
mOutAllocation = Allocation.createTyped(mRS, mInAllocation.getType());
mScript = new ScriptC_mono(mRS, getResources(), R.raw.mono);

mScript.set_gIn(mInAllocation);
mScript.set_gOut(mOutAllocation);
mScript.set_gScript(mScript);
mScript.invoke_filter();
mOutAllocation.copyTo(mBitmapOutRS);
In this example, the input memory allocation is created from a bitmap, and the allocation will be used by the script. Other possible uses include textu resource 原纹理 (USAGE_GRAPHICS_TEXTURE) and graphics mesh
(USAGE_GRAPHICS_VERTEX). You can review the various methods defined in
the Allocation class to create allocations. Allocations can be one dimensional
(for example, array), two-dimensional (for example, bitmap), or threedimensional.

9.5.2 rsForEach

The rsForEach function exists in three different flavors:
rsForEach(rs_script script, rs_allocation input, rs_allocation output)
(only in Android 4.0 and above)
rsForEach(rs_script script, rs_allocation input, rs_allocation output,
const void * usrData)
rsForEach(rs_script script, rs_allocation input, rs_allocation output,
const void * usrData, const rs_script_call_t*)
As we saw in Listing 9–11, the first parameter is a script. It will define which root() function to execute. The second and third parameters specify the input and output allocations. The fourth parameter, if present, is some private user data that can be used by the script. Android will not use this value and it can be set to zero or any other value when not used by the root() function. The fifth parameter, when present, will specify how the root() function will be executed. Listing 9–13 shows the definitions of the rs_script_call_t
structure.
Listing 9–13. rs_script_call_t
enum rs_for_each_strategy {
RS_FOR_EACH_STRATEGY_SERIAL,
RS_FOR_EACH_STRATEGY_DONT_CARE,
RS_FOR_EACH_STRATEGY_DST_LINEAR,
RS_FOR_EACH_STRATEGY_TILE_SMALL,
RS_FOR_EACH_STRATEGY_TILE_MEDIUM,
RS_FOR_EACH_STRATEGY_TILE_LARGE
};
typedef struct rs_script_call {
enum rs_for_each_strategy strategy;
uint32_t xStart;

uint32_t xEnd;
uint32_t yStart;
uint32_t yEnd;
uint32_t zStart;
uint32_t zEnd;
uint32_t arrayStart;
uint32_t arrayEnd;
} rs_script_call_t;
You can configure the strategy as well as the start and end parameters (for all
dimensions of the allocations适用于所有维). The strategy is only a hint and there is no guarantee implementations will obey the order.
For convenience, Android 4.0 (Ice Cream Sandwich) defines the Script.forEach()
method. While you should not be calling this method in your code directly, it is being
used in the reflected code. Thanks to this new method, the mono.rs script can be
simplified, and Listing 9–14 shows the implementation of the script when the build target
is set to Android 4.0.
Listing 9–14. RenderScript mono.rs (Android 4.0 Version)
/*
* Copyright (C) 2011 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.
*/
#pragma version(1)
#pragma rs java_package_name(com.example.android.rs.hellocompute)
const static float3 gMonoMult = {0.299f, 0.587f, 0.114f};
void root(const uchar4 *v_in, uchar4 *v_out) {
float4 f4 = rsUnpackColor8888(*v_in);
float3 mono = dot(f4.rgb, gMonoMult);
*v_out = rsPackColorTo8888(mono);
}
As you can see, the filter() function has been removed and so were the gIn, gOut, and
gScript variables. To understand why it was possible to remove this function in the
Android 4.0 sample application, one has to look at the reflected layer that is generated
when the project is built. Listing 9–15 shows the new reflected layer.

9–15. ScriptC_mono.java (Android 4.0 Version)
/*
* Copyright (C) 2011 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:
E:\ws\HelloCompute\src\com\example\android\rs\hellocompute\mono.rs
*/
package com.example.android.rs.hellocompute;
import android.renderscript.*;
import android.content.res.Resources;
/**
* @hide
*/
public class ScriptC_mono extends ScriptC {
// Constructor
public ScriptC_mono(RenderScript rs, Resources resources, int id) {
super(rs, resources, id);
__U8_4 = Element.U8_4(rs);
}
private Element __U8_4;
private final static int mExportForEachIdx_root = 0;
public void forEach_root(Allocation ain, Allocation aout) {
// 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!");
}
// Verify dimensions
Type tIn = ain.getType();
Type tOut = aout.getType();
if ((tIn.getCount() != tOut.getCount()) ||
(tIn.getX() != tOut.getX()) ||
(tIn.getY() != tOut.getY()) ||
(tIn.getZ() != tOut.getZ()) ||
(tIn.hasFaces() != tOut.hasFaces()) ||
(tIn.hasMipmaps() != tOut.hasMipmaps())) {

throw new RSRuntimeException("Dimension mismatch between input and output
parameters!");
}
forEach(mExportForEachIdx_root, ain, aout, null);
}
}
Obviously the invoke_filter() method does not appear in the reflected layer as
filter() is not defined in the script anymore. Because the gIn, gOut, and gScript
variables were removed, all the set/get methods in the reflected layer are also gone. The
key method now is forEach_root(), which is called in createScript() in
HelloCompute.java, as shown in Listing 9–16 (the Honeycomb version is shown in
Listing 9–12).
Listing 9–16. createScript() Method (Android 4.0 Version)
private void createScript() {
mRS = RenderScript.create(this);
mInAllocation = Allocation.createFromBitmap(mRS, mBitmapIn,
Allocation.MipmapControl.MIPMAP_NONE,
Allocation.USAGE_SCRIPT);
mOutAllocation = Allocation.createTyped(mRS, mInAllocation.getType());
mScript = new ScriptC_mono(mRS, getResources(), R.raw.mono);
mScript.forEach_root(mInAllocation, mOutAllocation);
mOutAllocation.copyTo(mBitmapOut);
}
NOTE: Allocations配置 have to be compatible相互兼容 (for example, same sizes in all dimensions).
As you can see, createScript() now simply calls the forEach_root() method from the
reflected layer. Because the Script.forEach() method does not exist in Honeycomb,
you may want to avoid using that method and instead use the original Honeycomb way
of doing things. Although it is more complicated, it also allows your application to be
compatible with Android 3.x devices.

9.5.3 Performance

Since RenderScript is targeted at high-performance 3D rendering and compute
operations, it is important to compare the performance of a script to its Java counterpart
to give you an idea of how much gain you can achieve with RenderScript. For this
purpose, a simple Java implementation of the black-and-white filter is provided in Listing
9–17.

Listing 9–17. Java Filter
// mBitmapIn is the input bitmap, mBitmapOut is the output bitmap
int w = mBitmapIn.getWidth();
int h = mBitmapIn.getHeight();
int size = w * h;
int[] pixels = new int[size];
mBitmapIn.getPixels(pixels, 0, w, 0, 0, w, h); // we get all the pixels (as 32-bit
integer values)
for (int i = 0; i < size; i++) {
int c = pixels[i]; // 0xAARRGGBB
// we extract red, green and blue components (each one in [0, 255] range)
int r = (c >> 16) & 0xFF;
int g = (c >> 8) & 0xFF;
int b = c & 0xFF;
// approximation of the formula using integer arithmetic
r *= 76;
g *= 151;
b *= 29;
int y = (r + g + b) >> 8; // luminance
pixels[i] = y | (y << 8) | (y << 16) | ( c & 0xFF000000);
}
mBitmapOut.setPixels(pixels, 0, w, 0, 0, w, h); // we set the output bitmap’s pixels
While the RenderScript version of the filter took about 115 milliseconds to complete on a
Galaxy Tab 10.1 (using the bitmap resource provided in the sample application as input),
the Java version took only 48 milliseconds! The no-JIT version
(android:vmSafeMode=”true” in the application’s manifest) took about 130 milliseconds
to complete.The results may sound surprising but one has to consider the overhead of
creating the script, allocations, and floating-point operations. A more complicated script
may perform better than its Java counterpart副本, and may benefit in the future from more
powerful processing units. For example, in the future a script could run on the GPU
instead of the CPU, possibly taking advantage of better support for parallel平行线 execution.
Not all scripts will be slower, but this example shows that using RenderScript may not
always make your code faster.

9.6 Native RenderScript APIs

Your RenderScript code has access to a limited set of APIs as it cannot simply call APIs
from the NDK or C library. The RenderScript APIs are defined in six header files, located
in the SDK’s platform/android-xx/renderscript/include (where xx is the API level, for
example, 13):
rs_types.rsh
rs_core.rsh
rs_cl.rsh
rs_math.rsh
rs_graphics.rsh
rs_time.rsh
In Ice Cream Sandwich, the RenderScript APIs are defined in 12 header files. In addition
to the six header files above, the following six files were added:
rs_allocation.rsh
rs_atomic.rsh
rs_debug.rsh
rs_matrix.rsh
rs_object.rsh
rs_quaternion.rsh
Again, as online documentation for RenderScript is currently quite scant缺乏, it is
recommended you review these files. One of the big differences between Honeycomb
and Ice Cream Sandwich is the addition of comments in these header files. You should
therefore refer to the Android 4.0 RenderScript header files whenever possible since you
will find more information in those than in the Honeycomb header files.

9.6.1rs_types.rsh

This header file defines the basic types used in RenderScript (in addition to the C99 standard types char, short, int, long, and float):
int8_t
int16_t
int32_t
int64_t
uint8_t
uint16_t
uint32_t
uint64_t
uchar (defined as uint8_t)
ushort (defined as uint16_t)
uint (defined as uint32_t)
ulong (defined as uint64_t)
In addition to these basic types, this file defines vector types:
float2
float3
float4
double2 (Android 4.0 and above)
double3 (Android 4.0 and above)
double4 (Android 4.0 and above)
char2
char3
char4
uchar2
uchar3
uchar4
short2
short3
short4
ushort2
ushort3
ushort4
int2
int3
int4
uint2
uint3
uint4
Since a 3D framework would not be complete without matrices矩阵, several matrix types are also defined:
rs_matrix2x2 (square matrix of order 2)
rs_matrix3x3 (square matrix of order 3)
rs_matrix4x4 (square matrix of order 4)

NOTE: While vector矢量 types are defined for both floating-point and integer vectors (for example,float2, ushort3, and int4), matrix types use float only.
Honeycomb MR1 (API level 12) introduced the quaternion type rs_quaternion (defined
as float4). Additional APIs were also introduced to manipulate this new type.
As you can see, the number of types is relatively high, however each of these types can
be seen as a scalar标量, a vector向量, or a matrix.
The following types are also defined in rs_types.rsh and are opaque handles to their
Java counterparts:
rs_element
rs_type
rs_allocation
rs_sampler
rs_script
rs_mesh
rs_program_fragment
rs_program_vertex
rs_program_raster
rs_program_store
rs_font
All these types are also exposed in the Java layer in the android.renderscript package:
Byte2
Byte3
Byte4
Double2 (Android 4.0 and above)
Double3 (Android 4.0 and above)
Double4 (Android 4.0 and above)
Float2
Float3
Float4
Int2
Int3
Int4
Long2
Long3
Long4
Matrix2f
Matrix3f
Matrix4f
Short2
Short3
Short4
Element
Type
Allocation
Sampler
Script
Mesh
ProgramFragment
ProgramVertex
ProgramRaster
ProgramStore
Font

9.6.2 rs_core.rsh

This header file defines the following functions in Honeycomb:
rsDebug
rsPackColorTo8888
rsUnpackColor8888
rsMatrixSet
rsMatrixGet
rsMatrixLoadIdentity
rsMatrixLoad
rsMatrixLoadRotate
rsMatrixLoadScale
rsMatrixLoadTranslate
rsMatrixLoadMultiply
rsMatrixMultiply
rsMatrixRotate
rsMatrixScale
rsMatrixTranslate
rsMatrixLoadOrtho
rsMatrixLoadFrustum
rsMatrixLoadPerspective
rsMatrixInverse
rsMatrixInverseTranspose
rsMatrixTranspose
rsClamp
The matrix functions were moved to a dedicated header file in Android 4.0:
rs_matrix.rsh.
As the quaternion type was introduced in Honeycomb MR1 (Android 3.1), new functions
were also added to rs_core.rsh:
rsQuaternionSet
rsQuaternionMultiply
rsQuaternionAdd
rsQuaternionLoadRotateUnit
rsQuaternionLoadRotate
rsQuaternionConjugate
rsQuaternionDot
rsQuaternionNormalize
rsQuaternionSlerp
rsQuaternionGetMatrixUnit
This header file went through a metamorphosis in Android 4.0. As a matter of fact, the
Honeycomb version of this file was a little bit all over the place, containing matrix,
quaternion, debugging, and some math functions. The file was dramatically reorganized
in Android 4.0 to define only the following functions:

rsSendToClient
rsSendToClientBlocking
rsForEach

The matrix, quaternion, and debugging functions were moved to their new respective
files (rs_matrix.rsh, rs_quaternion.rsh, and rs_debug.rsh) while the math functions
were naturally moved to rs_math.rsh. Since header files are automatically included, the
fact that functions were moved from one header file to another should not affect your
script.

9.6.3 rs_cl.rsh

This header file is actually somewhat hard to read as it uses many macros宏 to define
functions. It contains the main “compute” APIs, and if you played hooky逃学 instead of
attending math classes, you are in for a shock看起来有点难懂.
This header file defines the following math functions:
acos arc cosine
acosh inverse hyperbolic cosine
acospi acos(x) / pi
asin arc sine
asinh inverse hyperbolic sine
asinpi asin(x) / pi
atan arc tangent
atan2 arc tangent with 2 parameters
atanh hyperbolic arc tangent
atanpi atan(x) / pi
atan2pi atan2(x,y) / pi
cbrt cubic root
ceil round up value
copysign x with its sign changed to sign of y
cos cosine
cosh hyperbolic cosine
cospi cos(pi * x)
erf error function encountered in integrating the normal distribution
erfc complimentary error function

exp ex
exp2 2x
exp10 10x
expm1 ex – 1.0
fabs absolute value of floating-point value
fdim positive difference between x and y
floor largest integer less than or equal to x
fma (x * y) + z, rounded
fmax maximum of 2 floating-point values
fmin minimum of 2 floating-point values
fmod modulus
fract fractional value in x (floor(x) also returned)
frexp extract mantissa and exponent
hypot square root of x2 + y2
ilogb exponent as integer value
ldexp x * 2y
lgamma natural logarithm of the absolute value of the gamma
log natural logarithm (base e)
log10 common logarithm (base 10)
log2 logarithm base 2
log1p natural logarithm of 1+x
logb exponent of x, which is the integral part of logr|x|
mad approximation of (x * y) + z (when speed is preferred
over accuracy)
modf decompose floating-point number into integral and
fractional parts
nextafter next representable floating-point value following x in
the direction of y
pow xy
pown xy (y is integer)
powr xy (x greater than or equal to zero)
remainder remainder

remquo remainder and quotient
rint round to nearest integer (in floating-point format)
rootn x1/y
round nearest integer value (in floating-point format)
sqrt square root
rsqrt reciprocal of square root (i.e. 1.0 / sqrt(x))
sin sine
sincos sine and cosine
sinh hyperbolic sine
sinpi sin(pi * x)
tan tangent
tanh hyperbolic tangent
tanpi tan(pi * x)
tgamma gamma function
trunc truncate floating-point value
It also defines these few integer functions:
abs absolute value
clz number of leading 0-bits
min minimum of two integers
max maximum of two integers
Some other common functions are also defined:
clamp clamp amount to range given by low and high
degrees convert radians to degrees
mix start + (stop – start)*amount (linear blend)
radians convert degrees to radians
step 0.0 if v is less than edge, else 1.0
smoothstep 0.0 if v edge0, 1.0 if v edge1, else smooth Hermite
interpolation
sign sign (-1.0, -0.0, +0.0 or 1.0)
Last but not least, this header file defines some geometric functions:
cross cross product

dot dot product
length vector length
distance distance between 2 points
normalize normalize vector (length will be 1.0)

If you are familiar with OpenCL, you should recognize these APIs. As a matter of fact,
the Honeycomb rs_cl.rsh header file itself refers to the OpenCL documentation as it
contains comments referring to sections 6.11.2, 6.11.3, 6.11.4, and 6.11.5, which are the
sections in the OpenCL 1.1 specifications covering the math, integer, common, and
geometric functions respectively. Listing 9–18 shows these comments from rs_cl.rsh.
You can also refer to the OpenCL documentation for more information, which is freely
available on the Khronos website (
www.khronos.org/opencl).
Listing 9–18. Comments Referring To OpenCL In rs_cl.rsh
#ifndef __RS_CL_RSH__
#define __RS_CL_RSH__
...
// Float ops, 6.11.2
...
extern float __attribute__((overloadable)) trunc(float);
FN_FUNC_FN(trunc)
// Int ops (partial), 6.11.3
...
IN_FUNC_IN_IN_BODY(max, (v1 > v2 ? v1 : v2))
FN_FUNC_FN_F(max)
// 6.11.4
_RS_RUNTIME float __attribute__((overloadable)) clamp(float amount, float low, float
high);
...
// 6.11.5
_RS_RUNTIME float3 __attribute__((overloadable)) cross(float3 lhs, float3 rhs);
...
#endif
This header file also defines some conversion functions such as convert_int3, which
converts a vector of three elements to a vector of three integers.

9.6.4 rs_math.rsh

This header file defines the following functions in Honeycomb:
rsSetObject
rsClearObject
rsIsObject
rsGetAllocation
rsAllocationGetDimX
rsAllocationGetDimY
rsAllocationGetDimZ
rsAllocationGetDimLOD
rsAllocationGetDimFaces
rsGetElementAt
rsRand
rsFrac
rsSendToClient
rsSendToClientBlocking
rsForEach
As we saw earlier, rsForEach() is one of the most important functions.
The rsSendToClient() and rsSendToClientBlocking() functions (moved to rs_core.rsh
in Android 4.0) are worth mentioning as they allow a script to send data to the Java
layer. For the Java layer to be able to receive data, your application will have to register
a message handler, as shown in Listing 9–19.
Listing 9–19. Message Handler
RenderScript rs = RenderScript.create(this); // needs a Context as parameter
rs.setMessageHandler(new RenderScript.RSMessageHandler() {
@Override
public void run() {
super.run();
Log.d(TAG, String.valueOf(this.mID) + " " + mData + ", length:" + mLength);
}
});
The handler’s mID, mData, and mLength fields will contain the information passed by the script. If your script sends data to the Java layer but no handler is registered, an
exception will be thrown.
In Android 4.0, the rs_math.rsh was also modified to focus exclusively on mathematical
functions:
rsRand
rsFrac
rsClamp
rsExtractFrustumPlanes (new in Android 4.0)
rsIsSphereInFrustum (new in Android 4.0)
rsPackColorTo8888
rsUnpackColor8888
The allocation functions were moved to a new file, rs_allocation.rsh, which, in addition
to the functions introduced in Honeycomb, defines the following two new functions:
rsAllocationCopy1DRange
rsAllocationCopy2DRange
The object functions were also moved to a new header file: rs_object.rsh.

9.6.5 rs_graphics.rsh

This header files defines the following functions:
rsgBindProgramFragment
rsgBindProgramStore
rsgBindProgramVertex
rsgBindProgramRaster
rsgBindSampler
rsgBindTexture
rsgProgramVertexLoadProjectionMatrix
rsgProgramVertexLoadModelMatrix
rsgProgramVertexLoadTextureMatrix
rsgProgramVertexGetProjectionMatrix
rsgProgramFragmentConstantColor
rsgGetWidth
rsgGetHeight
rsgAllocationsSyncAll (new overloaded function added in Android 4.0)
rsgDrawRect
rsgDrawQuad
rsgDrawQuadTexCoords
rsgDrawSpriteScreenspace
rsgDrawMesh
rsgClearColor
rsgClearDepth
rsgDrawText
rsgBindFont
rsgFontColor
rsgMeasureText
rsgMeshComputeBoundingBox
In Android 4.0 the following functions were added to rs_graphics.rsh:
rsgBindColorTarget
rsgClearColorTarget
rsgBindDepthTarget
rsgClearDepthTarget
rsgClearAllRenderTargets
rsgFinish
Since the focus of this chapter is not on making you an expert in 3D rendering,
we won’t review these functions in detail. If you are already familiar with
OpenGL, then you should easily be able to use these functions. If you are not,
then it is recommended you first get familiar with 3D terms like fragment,
vertex顶点, and mesh网格 before you dive into RenderScript.

9.6.6 rs_time.rsh

This file defines the following:
rsTime
rsLocalTime
rsUptimeMillis
rsUptimeNanos
rsGetDt

Of particular interest is the rsGetDt() function, which returns the number of seconds (as a floating-point value) since it was last called. Listing 9–20 shows how this would typically be used in a script.
Listing 9–20. Calling rsGetDt()
#pragma version(1)
#pragma rs java_package_name(com.yourpackagehere)
typedef struct __attribute__((packed, aligned(4))) MyObject {
float x;
float other_property;
} MyObject_t;
MyObject_t *object; // Java layer would have to call bind_object
int root() {
float dt = rsGetDt();
float dx = dt * 10.f; // 10 pixels per second
object->x += dx; // new x position of object
// do something else here, for example to draw the object
return 20;
}

NOTE: While the root() function returns 20 (that is, 20 milliseconds between frames, or 50
frames per second), the frame rate is not guaranteed. This is why your code should use
rsGetDt() instead of assuming the duration between two frames is the value root() returns.

9.6.7 rs_atomic.rsh

New in Android 4.0, this file defines functions that were not defined in Honeycomb:
rsAtomicInc
rsAtomicDec
rsAtomicAdd
rsAtomicSub
rsAtomicAnd
rsAtomicOr
rsAtomicXor
rsAtomicMin
rsAtomicMax
rsAtomicCas

As their names indicate, these functions perform various operations atomically这些函数的操作都是原子的.

9.7 RenderScript vs. NDK

Both RenderScript and NDK are here to improve the performance of applications. While they share a common goal, you should think carefully about using one versus the other. Each has its own advantages and drawbacks, and not thinking things through could result in you being stuck during development or finding out the hard way that something is extremely difficult to implement.
The advantages of RenderScript can be summarized as follows:
Platform-independent (ARM, MIPS, Intel, for example)
Easy parallel execution 易于并行执行
Can use CPU, GPU, or other processing units
On the other hand, RenderScript does have some drawbacks:
Android-only (scripts cannot be used with other platforms like iOS)
Learning curve
Limited number of APIs数量有限
Actual RenderScript performance results are hard to obtain as they will heavily depend
on the Android version and the hardware device. Some scripts may run much faster than
their Java or NDK counterpart while others may not. Because of legacy code遗留代码 or because
it simply makes sense in your application, you can actually use both RenderScript and
the NDK in the same application.

Summary

While it still appears to be a somewhat immature略显稚嫩 technology in Honeycomb,
RenderScript is already improving in Android 4.0 and is going to take advantage of more
hardware features in the future. Even though ARM is clearly the dominant architecture in
Android portable devices nowadays and using the NDK can therefore be justified, the
fact that RenderScript is platform-independent can be a huge benefit as it can ultimately
reduce your maintenance cost significantly. All in all, RenderScript shows some
promises and is likely to be an important part of your applications in the future should
you need to reach performance levels not attainable with Java alone.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  RenderScript