3

Background

I'm learning how to use Renderscript, and I found this part in the docs:

In most respects, this is identical to a standard C function. The first notable feature is the attribute((kernel)) applied to the function prototype.

and they show a sample code of a kernel function:

uchar4 __attribute__((kernel)) invert(uchar4 in, uint32_t x, uint32_t y) {
  uchar4 out = in;
  out.r = 255 - in.r;
  out.g = 255 - in.g;
  out.b = 255 - in.b;
  return out;
}

The problem

It seems that some samples show that the parameters of kernel functions can be different, and not only those that appear above.

Example:

uchar4 __attribute__((kernel)) grayscale(uchar4 v_in) {
    float4 f4 = rsUnpackColor8888(v_in);
    float3 mono = dot(f4.rgb, gMonoMult);
    return rsPackColorTo8888(mono);
}

Thing is, the generated function on Java is still the same for all of those functions :

void forEach_FUNCTIONNAME(Allocation ain, Allocation aout)

where FUNCTIONNAME is the name of the function on RS.

So I assume that not every possible function can be a kernel function, and all of them need to follow some rules (besides the "attribute(kernel)" part, which needs to be added).

Yet I can't find those rules.

Only things I found is on the docs:

A kernel may have an input Allocation, an output Allocation, or both. A kernel may not have more than one input or one output Allocation. If more than one input or output is required, those objects should be bound to rs_allocation script globals and accessed from a kernel or invokable function via rsGetElementAt_type() or rsSetElementAt_type(). A kernel may access the coordinates of the current execution using the x, y, and z arguments. These arguments are optional, but the type of the coordinate arguments must be uint32_t.

The questions

  1. What are the rules for creating kernel functions, besides what's written?

  2. Which other parameters are allowed? I mean, what other parameters can I pass? Is it only those 2 "templates" of functions that I can use, or can I use other kernel-functions that have other sets of parameters?

  3. Is there a list of valid kernel functions? One that shows which parameters sets are allowed?

  4. Is it possible for me to customize those kernel functions, to have more parameters? For example, if I had a blurring function (I know we have a built in one) that I made, I could set the radius and the blurring algorithm.

Basically all of those questions are about the same

JAL
  • 41,701
  • 23
  • 172
  • 300
android developer
  • 114,585
  • 152
  • 739
  • 1,270

2 Answers2

1

There really aren't that many rules. You have to have either an input and/or an output, because kernels are executed over the range present there (i.e. you have a 2-D Allocation with x=200, y=400 - it will execute on each cell of input/output). We do support an Allocation-less launch, but it is only available in the latest Android release, and thus not usable on most devices. We also support multi-input as of Android M, but earlier target APIs won't build with that (unless you are using the compatibility library).

Parameters are usually primitive types (char, int, unsigned int, long, float, double, ...) or vector types (e.g. float4, int2, ...). You can also use structures, provided that they don't contain pointers in their definition. You cannot use pointer types unless you are using the legacy kernel API, but even then, you are limited to a single pointer to a non-pointer piece of data. https://android.googlesource.com/platform/cts/+/master/tests/tests/renderscript/src/android/renderscript/cts/kernel_all.rs has a lot of simple kernels that we use for trivial testing. It shows how to combine most of the types.

You can optionally include the rs_kernel_context parameter (which lets you look up information about the size of the launch). You can also optionally pass x, y, and/or z (with uint32_t type each) to get the actual indices on which the current execution is happening. Each x/y/z coordinate will be unique for a single launch, letting you know what cell is being operated on.

For your question 4, you can't use a radius the way that you want to. It would have to be a global variable as input, since our only kernel inputs traditionally vary as you go from cell to cell of the input/output Allocations. You can look at https://android.googlesource.com/platform/cts/+/master/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_blur.rs for an example about blur specifically.

Stephen Hines
  • 2,612
  • 1
  • 13
  • 12
  • I don't understand. So I do have only 2 options for kernel functions, or not? And how does it know if what I give it is an input or output? And what can it do if it's only input ? Maybe modify the input? Can I, or can't I, put extra parameters to the kernel function? If I can't, which functions can I use? If I can, how does it know which ones are used for the for-each-pixel mechanism of the bitmap (or other allocations) ? – android developer Oct 05 '15 at 21:17
  • You can either use the legacy API (that uses pointers for parameters) or you can use RS_KERNEL that uses pass-by-value. It is an error to const-cast away "const" on legacy kernels and write to inputs. Similarly for the RS_KERNEL version, you can't actually write to the input and have it reflect that update. Please look at some sample code before asking more questions, as I think it might be easier if you understand how the per-pixel (i.e. cell) based behavior of kernels works in all compute languages. – Stephen Hines Oct 05 '15 at 22:12
  • But the code I've found is from samples. I don't get how it works. How can it know what each parameter is? What other parameters can I use that will be auto-filled? – android developer Oct 07 '15 at 21:58
  • The only auto-filled parameters are x, y, (and maybe z in Android M). The other parameters (before any x, y, z) are converted to Allocations in the Java reflection. The same thing happens to the return type if it isn't void. – Stephen Hines Oct 07 '15 at 23:24
  • 1
    How does it know which parameter is which, and fill it? By the type and name? By the order ? Can I put weird things, like "x, someInput, y" , for example? – android developer Oct 09 '15 at 07:23
  • No, x and y have to come at the end. You can't mix these up. Please look at our sample code for good habits and it will be much easier for you. – Stephen Hines Oct 09 '15 at 20:12
  • Does it say anywhere on the docs about the need to be at the end? This is what I asked for. I wanted clear rules , to know what I can and can't do. – android developer Oct 09 '15 at 20:56
  • The docs are bad. Yes we know this. Just try to use it and the compiler will tell you when you make a mistake. :) – Stephen Hines Oct 11 '15 at 18:03
  • ok, can you please gather all the rules that you've found and write them on the answer itself ? Also, seeing that you know about RS, can you please check this question too: http://stackoverflow.com/q/33056776/878126 ? – android developer Oct 11 '15 at 19:13
  • I got to echo "android developer" here. The rules for parameters don't seem to be well defined nor documented. Thanks to Stephen for the patient responses though. – HRJ Jan 03 '17 at 10:12
1

Just some keypoints with which I was struggling, when I started to learn RS. Basically the yellow texts above include all RS wisdom, but in a "too compact" way to understand. In order to answer your questions 1 and 2 you have to differentiate between two types of allocations. The first type of allocations I call the "formal" allocations. In the kernel expression

      uchar4 __attribute__((kernel)) invert(uchar4 in, uint32_t x, uint32_t y) {

this are the Input allocation in (of type uchar4, i.e. 8 bit unsigned integer) and the Output allocation out which is also uchar4 - this is the type you can see on the left hand side of the kernel expression. The output is what will be given back via "return", same as in Java functions. You need at least one formal allocation (i.e. one Input OR one Output OR both of them).

The other type of allocations I call "side Allocation". This is what you handle via script globals, and these can be as well input or output allocations. If you use them as input, you will pass the input from Java side via copyTo(). If If you use them as output, you will get the output to Java side via copyFrom().

Now, the point is that, although you need at least one formal allocation, there is no qualitative difference between the formal and the side allocations, the only thing you need to care is that you use at least one formal allocation.

All allocations in the kernel (whether "formal" or "side") have the same dimensions in terms of width and height.

Question 3 is implicitely answered by 1 and 2.

  1. only formal Input allocation,
  2. only formal Output allocation,
  3. both formal Input and formal Output allocations
  4. 1.-3. can each have any number of additional "side" allocations.

Question 4: Yes. In your Gauss example, if you want to pass the radius of blur (e.g. 1-100) or the blurring algorithm (e.g. types 1,2 and 3) you would simply use one global variable for each of these, so that they can be applied within the kernel. Here I would not speak about "allocation" in the above sense since those are always of the same dimension as the grid spanned by the kernel (typically x width times y height). Nevertheless you still need to pass these Parameters via script_setxxx(yyy).

Hope this helps a bit.

Settembrini
  • 1,366
  • 3
  • 20
  • 32
  • My question was about kernel functions. Can I use other forms, other than what I used? What can I use, and what can't I use? – android developer Oct 05 '15 at 21:18
  • The only difference beween your first and second kernel above are the additional aguments uint32_t x, uint32_t y in the first kernel. However these are optional and implicit, i.e. you will add these, if you need to access the x and y coordinates during a kernel pass. A typical application would be the Gaussfilter, where you need to access the neighbor pixels of each pixel in order to build some average (access neighbours via rsGetElementAt_type() ). The forEach_FUNCTIONNAME(Allocation ain, Allocation aout) is needed both times because you have an In- AND Out-Allocation here. – Settembrini Oct 06 '15 at 12:51
  • So you say what I've found are optional? What other things are optional, and how can it tell what each parameter is supposed to be? by type&name ? – android developer Oct 07 '15 at 21:57