A couple of months ago, I had to do a lot of work with RenderScript. Unfortunately, there was not much documentation available then and there isn’t much more now. The purpose of this post is to provide the foundations for actually using RenderScript for graphical purposes. You should have already read about RenderScript and understand the overall concept (just not necessarily implementation details). I assume that you know Android and Java and aren’t scared of some basic C-style code. An understanding of computer graphics (or a graphics library such as OpenGL) definitely helps but is not a requirement.
We’ll be creating four files:
- RenderScript101.rs – The actual RenderScript code
- RenderScript101RS.java – A helper class to simplify interaction with RenderScript
- RenderScript101View.java – An RSSurfaceView that handles setting up the view and can capture touch events
- RenderScript101Activity.java – Main Activity and it’s even simpler than a typical one
When you create a new project, select a target of at least 11 and let Eclipse generate the Activity for you (ignore it for now). We’re diving right into the actual RenderScript code, so create the RenderScript101.rs
text file (in the same folder/package as the Activity). The first things we have to do are to specify the version of RenderScript we’re using (1) and specify the package (I’m using com.iangclifton.tutorials.renderscript
).
#pragma version(1) #pragma rs java_package_name(com.iangclifton.tutorials.renderscript); #include "rs_graphics.rsh" float4 bgColor; // Background color as xyzw 4-part float static void drawBackground() { rsgClearColor(bgColor.x, bgColor.y, bgColor.z, bgColor.w); } void init() { bgColor = (float4) { 0.0f, 1.0f, 0.0f, 1.0f }; rsDebug("Called init", rsUptimeMillis()); } int root() { drawBackground(); return 16; }
When your RenderScript code is first initialized, the init()
method will run once. Then the root()
method will run repeatedly. The return value for the root
method is an int specifying the number of milliseconds before root()
should run again.
Looking first at init()
, you can see we are setting the bgColor
(a 4-part float defined on line 6) to 0, 1, 0, 1. This is in RGBA format, so we’re saying no red, maximum green, no blue, and full opacity. In the next line we call rsDebug
with the message “Called init” and the current uptime in milliseconds. Think of this as an easy way to call Log.d("RenderScript", your_message_here)
. This call is not necessary, but you should definitely get used to adding debug messages into your RenderScript early on. For reference, rsDebug
is defined in rs_core.rsh
and rsUptimeMillis
is defined in rs_time.rsh
(both are available in your sdk directory under platforms/android-11/renderscript/include
and are included automatically for you).
Jumping down to the root
method, we see that it calls drawBackground()
and returns 16 (16ms for a frame is close to 60 frames per second). The drawBackground() call only does one thing right now; it clears the screen using the bgColor
we set in init()
. The rsgClearColor
method takes four floats (R, G, B, A) and is defined in rs_graphics.rsh
. Remember that RenderScript can also be used for calculations, so you have to specifically include the graphics header file (line 4). If you’re new to computer graphics, the use of the x, y, z, and w properties of bgColor
might seem strange. Without getting into the details, this is a convention in graphics where a four-part float is represented with these four letters (as you can probably guess, x, y, and z are typically coordinates. The w is usually for projection where the values are actually represented as x/w, y/w, and z/w. There’s a lot more to it than that, but the important part is knowing the letters right now).
Alright, so all that code does is define a background color from four floats (x, y, z, and w) and then it clears the screen to that color every 16-ish milliseconds. Pretty simple, but it’s a good start.
Next, you want to create the RenderScript101RS.java
file to simplify interaction with the RenderScript. It should look like this:
package com.iangclifton.tutorials.renderscript; import android.content.res.Resources; import android.renderscript.RenderScriptGL; public class RenderScript101RS { private RenderScriptGL mRS; private ScriptC_RenderScript101 mScript; public RenderScript101RS(RenderScriptGL rs, Resources res, int resId) { mRS = rs; mScript = new ScriptC_RenderScript101(rs, res, resId); mRS.bindRootScript(mScript); } }
All we are doing for now is setting a reference to the RenderScriptGL (the graphics derivative of RenderScript), instantiating the “glue” class ScriptC_RenderScript101
, and binding the script. The glue class is generated for you by Eclipse. If you have automatic builds on and saved the .rs
file we just worked on, you might have noticed the class being added to the gen folder along with your R
class. This glue class lets you interact with your RenderScript code via Java (such as if you want to set the background color from Java instead of within the RenderScript). We’ll get to it later. The bindRootScript()
method simply tells Android which script should handle rendering to the surface.
Next up, RenderScript101View.java
:
package com.iangclifton.tutorials.renderscript; import android.content.Context; import android.renderscript.RSSurfaceView; import android.renderscript.RenderScriptGL; public class RenderScript101View extends RSSurfaceView { private Context mContext; private RenderScript101RS mRenderScript; private RenderScriptGL mRS; public RenderScript101View(Context context) { super(context); mContext = context; ensureRenderScript(); } private void ensureRenderScript() { if (mRS == null) { final RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig(); mRS = createRenderScriptGL(sc); } if (mRenderScript == null) { mRenderScript = new RenderScript101RS(mRS, mContext.getResources(), R.raw.renderscript101); } } }
This is the RSSurfaceView that essentially works like other views in Android. In fact, it extends SurfaceView, so if you’ve used SurfaceView or GLSurfaceView, you have a pretty good idea of what this is.
All we’re doing here is following the convention of having an ensureRenderScript()
method that sets up the RenderScriptGL and RenderScript101RS
class instances. This method can be called when the view is first set up or when it changes. If mRS
is null, we create a SurfaceConfig (this can be used to control the bit-depth for colors, the depth buffer, etc., but we’re just using the defaults). We use the SurfaceConfig
in our call to createRenderScriptGL()
to generate the RenderScriptGL
. If the mRenderScript
reference is null, we simply construct a new one by passing the RenderScriptGL
, a reference to the Resources, and the resource identifier for our RenderScript file (this bytecode file is automatically generated for you in res/raw).
And, finally, RenderScript101Activity.java
:
package com.iangclifton.tutorials.renderscript; import android.app.Activity; import android.os.Bundle; public class RenderScript101Activity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new RenderScript101View(this)); } }
Yeah, that’s all there is to this Activity
. We’re simply constructing a new RenderScript101View
and using it as the layout. Run the app on your Honeycomb device and you should see a horrendous green screen like this:
Image may be NSFW.
Clik here to view.
If your eyes are burning, you did it right. If you don’t see a green screen, check DDMS for anything suspicious in the logs.
That concludes part one of the RenderScript 101 tutorial. Next up, RenderScript 101, Part 2: RenderScript Interaction.