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.

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 texture source (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 three-dimensional.

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.

Listing 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:wsHelloComputesrccomexampleandroid shellocomputemono.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.

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.44.94