6

I have a Renderscript which processes an image that is given in output to an Allocation. I want to use this Allocation as a texture in my OpenGL program but I don't know how to get a texture ID from the Allocation.

On the other hand, I know I could use a graphic Renderscript, but since it has been deprecated, I guess there must be some other way to achieve the same result.

Mario Demontis
  • 469
  • 3
  • 11

2 Answers2

7

Specify USAGE_IO_OUTPUT when you create the Allocation. Assuming you are generating the texture data in a script you would also add USAGE_SCRIPT. You can then call

Allocation.setSurface(theGLSurface)

to link the allocation to a texture. Each time you want to update the texture with the contents of the script you need to call.

Allocation.ioSend()

This will move the data without making extra copies.

R. Jason Sams
  • 1,469
  • 9
  • 10
  • 2
    So, I guess I have to do a `Allocation.setSurface(new Surface(new SurfaceTexture(textureID)))` in order to bind the allocation of the output to the texture ID. I'm running the script and then rendering the texture to the screen, but I cannot see any change in the rendering, as the script was never run – Mario Demontis Mar 08 '13 at 14:34
  • I tried this but it would cause a crash when the Renderscript context was released, see http://stackoverflow.com/questions/13842609/how-to-tidy-up-a-surface-and-surfacetexture-when-used-with-renderscript – Massycat Mar 13 '13 at 13:40
2

You have phrased the question in terms of transforming an Allocation into a texture, but it's easier to think of creating a texture then granting the Allocation ownership of that texture. This way, you would know the texture ID before passing it to the Allocation. Beware: the Allocation has the power to radically change properties of the texture.

Step by step:

  1. Create a GL texture

  2. Create a SurfaceTexture using its ID

  3. Create an Allocation with usage Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT

  4. Pass the SurfaceTexture to the Allocation with setSurface()

  5. Put data in the Allocation

  6. Call ioSend() on the Allocation to update the texture

  7. Repeat steps 5 and 6 as often as you want to update the texture

I am very far from a GL expert, so step 2 is frankly conjecture. Below is an adaptation of the HelloCompute sample in which I replace displayout with a TextureView, which is a View that handily creates a texture and a SurfaceTexture for me. From then on I follow the steps above.

package com.example.android.rs.hellocompute;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.Bundle;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.Type;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.TextureView;
import android.widget.ImageView;

public class HelloCompute extends Activity {
    private Bitmap mBitmapIn;

    private RenderScript mRS;
    private ScriptC_mono mScript;
    private Allocation mSurfaceAllocation;
    private Allocation mCanvasAllocation;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mBitmapIn = loadBitmap(R.drawable.data);
        int x = mBitmapIn.getWidth();
        int y = mBitmapIn.getHeight();

        ImageView in = (ImageView) findViewById(R.id.displayin);
        in.setImageBitmap(mBitmapIn);

        mRS = RenderScript.create(this);
        mScript = new ScriptC_mono(mRS, getResources(), R.raw.mono);

        mCanvasAllocation = Allocation.createTyped(mRS, 
                new Type.Builder(mRS, Element.RGBA_8888(mRS))
                    .setX(x).setY(y).create(), 
                Allocation.USAGE_IO_INPUT | Allocation.USAGE_SCRIPT);

        mSurfaceAllocation = Allocation.createTyped(mRS, 
                new Type.Builder(mRS, Element.RGBA_8888(mRS))
                    .setX(x).setY(y).create(), 
                Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT);

        TextureView mTextureView = (TextureView) findViewById(R.id.displayout);
        mTextureView.setSurfaceTextureListener(
          new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture s, 
                    int w, int h) {
                if(s != null) mSurfaceAllocation.setSurface(new Surface(s));
                mScript.forEach_root(mCanvasAllocation, mSurfaceAllocation);
                mSurfaceAllocation.ioSend();
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture s) {
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture s, 
                    int w, int h) {
                if(s != null) mSurfaceAllocation.setSurface(new Surface(s));
            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture s) {
                mSurfaceAllocation.setSurface(null);
                return true;
            }
        });

        try {
            Surface surface = mCanvasAllocation.getSurface();
            Canvas canvas = surface.lockCanvas(new Rect(0,0,100,100));
            canvas.drawBitmap(mBitmapIn, 0, 0, new Paint());
            surface.unlockCanvasAndPost(canvas);
            mCanvasAllocation.ioReceive();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (OutOfResourcesException e) {
            e.printStackTrace();
        }
    }

    private Bitmap loadBitmap(int resource) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        return BitmapFactory.decodeResource(getResources(), resource, options);
    }
}
Kietz
  • 1,186
  • 11
  • 19