blob: 1200a6603274d5bfcf0d450693251562d61134ef [file] [log] [blame]
Jason Sams423ebcb2012-08-10 15:40:53 -07001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.renderscript;
18
19import java.lang.reflect.Method;
Jason Sams08a81582012-09-18 12:32:10 -070020import java.util.ArrayList;
Jason Sams423ebcb2012-08-10 15:40:53 -070021
22/**
Tim Murrayc11e25c2013-04-09 11:01:01 -070023 * ScriptGroup creates a group of kernels that are executed
24 * together with one execution call as if they were a single kernel.
25 * The kernels may be connected internally or to an external allocation.
26 * The intermediate results for internal connections are not observable
27 * after the execution of the script.
Jason Sams08a81582012-09-18 12:32:10 -070028 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -070029 * External connections are grouped into inputs and outputs.
Jason Sams08a81582012-09-18 12:32:10 -070030 * All outputs are produced by a script kernel and placed into a
Tim Murrayc11e25c2013-04-09 11:01:01 -070031 * user-supplied allocation. Inputs provide the input of a kernel.
32 * Inputs bound to script globals are set directly upon the script.
Tim Murray2a603892012-10-10 14:21:46 -070033 * <p>
34 * A ScriptGroup must contain at least one kernel. A ScriptGroup
35 * must contain only a single directed acyclic graph (DAG) of
36 * script kernels and connections. Attempting to create a
37 * ScriptGroup with multiple DAGs or attempting to create
38 * a cycle within a ScriptGroup will throw an exception.
Tim Murrayc11e25c2013-04-09 11:01:01 -070039 * <p>
40 * Currently, all kernels in a ScriptGroup must be from separate
41 * Script objects. Attempting to use multiple kernels from the same
42 * Script object will result in an {@link android.renderscript.RSInvalidStateException}.
Jason Sams08a81582012-09-18 12:32:10 -070043 *
Jason Sams423ebcb2012-08-10 15:40:53 -070044 **/
Jason Sams08a81582012-09-18 12:32:10 -070045public final class ScriptGroup extends BaseObj {
Jason Sams423ebcb2012-08-10 15:40:53 -070046 IO mOutputs[];
47 IO mInputs[];
48
49 static class IO {
Jason Sams08a81582012-09-18 12:32:10 -070050 Script.KernelID mKID;
Jason Sams423ebcb2012-08-10 15:40:53 -070051 Allocation mAllocation;
Jason Sams423ebcb2012-08-10 15:40:53 -070052
Jason Sams08a81582012-09-18 12:32:10 -070053 IO(Script.KernelID s) {
54 mKID = s;
Jason Sams423ebcb2012-08-10 15:40:53 -070055 }
56 }
57
Jason Sams08a81582012-09-18 12:32:10 -070058 static class ConnectLine {
59 ConnectLine(Type t, Script.KernelID from, Script.KernelID to) {
60 mFrom = from;
61 mToK = to;
Jason Sams423ebcb2012-08-10 15:40:53 -070062 mAllocationType = t;
63 }
64
Jason Sams08a81582012-09-18 12:32:10 -070065 ConnectLine(Type t, Script.KernelID from, Script.FieldID to) {
66 mFrom = from;
67 mToF = to;
68 mAllocationType = t;
Jason Sams423ebcb2012-08-10 15:40:53 -070069 }
Jason Sams08a81582012-09-18 12:32:10 -070070
71 Script.FieldID mToF;
72 Script.KernelID mToK;
73 Script.KernelID mFrom;
74 Type mAllocationType;
Jason Sams423ebcb2012-08-10 15:40:53 -070075 }
76
77 static class Node {
78 Script mScript;
Jason Sams08a81582012-09-18 12:32:10 -070079 ArrayList<Script.KernelID> mKernels = new ArrayList<Script.KernelID>();
80 ArrayList<ConnectLine> mInputs = new ArrayList<ConnectLine>();
81 ArrayList<ConnectLine> mOutputs = new ArrayList<ConnectLine>();
Tim Murray2a603892012-10-10 14:21:46 -070082 int dagNumber;
Jason Sams423ebcb2012-08-10 15:40:53 -070083
84 Node mNext;
85
86 Node(Script s) {
87 mScript = s;
88 }
Jason Sams423ebcb2012-08-10 15:40:53 -070089 }
90
91
Tim Murray7a629fa2013-11-19 12:45:54 -080092 ScriptGroup(long id, RenderScript rs) {
Jason Sams423ebcb2012-08-10 15:40:53 -070093 super(id, rs);
94 }
95
Jason Sams08a81582012-09-18 12:32:10 -070096 /**
97 * Sets an input of the ScriptGroup. This specifies an
Tim Murrayc11e25c2013-04-09 11:01:01 -070098 * Allocation to be used for kernels that require an input
99 * Allocation provided from outside of the ScriptGroup.
Jason Sams08a81582012-09-18 12:32:10 -0700100 *
101 * @param s The ID of the kernel where the allocation should be
102 * connected.
103 * @param a The allocation to connect.
104 */
105 public void setInput(Script.KernelID s, Allocation a) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700106 for (int ct=0; ct < mInputs.length; ct++) {
Jason Sams08a81582012-09-18 12:32:10 -0700107 if (mInputs[ct].mKID == s) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700108 mInputs[ct].mAllocation = a;
Jason Sams08a81582012-09-18 12:32:10 -0700109 mRS.nScriptGroupSetInput(getID(mRS), s.getID(mRS), mRS.safeID(a));
Jason Sams423ebcb2012-08-10 15:40:53 -0700110 return;
111 }
112 }
113 throw new RSIllegalArgumentException("Script not found");
114 }
115
Jason Sams08a81582012-09-18 12:32:10 -0700116 /**
117 * Sets an output of the ScriptGroup. This specifies an
Tim Murrayc11e25c2013-04-09 11:01:01 -0700118 * Allocation to be used for the kernels that require an output
119 * Allocation visible after the ScriptGroup is executed.
Jason Sams08a81582012-09-18 12:32:10 -0700120 *
121 * @param s The ID of the kernel where the allocation should be
122 * connected.
123 * @param a The allocation to connect.
124 */
125 public void setOutput(Script.KernelID s, Allocation a) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700126 for (int ct=0; ct < mOutputs.length; ct++) {
Jason Sams08a81582012-09-18 12:32:10 -0700127 if (mOutputs[ct].mKID == s) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700128 mOutputs[ct].mAllocation = a;
Jason Sams08a81582012-09-18 12:32:10 -0700129 mRS.nScriptGroupSetOutput(getID(mRS), s.getID(mRS), mRS.safeID(a));
Jason Sams423ebcb2012-08-10 15:40:53 -0700130 return;
131 }
132 }
133 throw new RSIllegalArgumentException("Script not found");
134 }
135
Jason Sams08a81582012-09-18 12:32:10 -0700136 /**
137 * Execute the ScriptGroup. This will run all the kernels in
Tim Murrayc11e25c2013-04-09 11:01:01 -0700138 * the ScriptGroup. No internal connection results will be visible
139 * after execution of the ScriptGroup.
Jason Sams08a81582012-09-18 12:32:10 -0700140 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700141 public void execute() {
Jason Sams08a81582012-09-18 12:32:10 -0700142 mRS.nScriptGroupExecute(getID(mRS));
Jason Sams423ebcb2012-08-10 15:40:53 -0700143 }
144
145
Jason Sams08a81582012-09-18 12:32:10 -0700146 /**
Tim Murrayc11e25c2013-04-09 11:01:01 -0700147 * Helper class to build a ScriptGroup. A ScriptGroup is
148 * created in two steps.
Jason Sams08a81582012-09-18 12:32:10 -0700149 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -0700150 * First, all kernels to be used by the ScriptGroup should be added.
Jason Sams08a81582012-09-18 12:32:10 -0700151 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -0700152 * Second, add connections between kernels. There are two types
153 * of connections: kernel to kernel and kernel to field.
154 * Kernel to kernel allows a kernel's output to be passed to
155 * another kernel as input. Kernel to field allows the output of
156 * one kernel to be bound as a script global. Kernel to kernel is
157 * higher performance and should be used where possible.
Jason Sams08a81582012-09-18 12:32:10 -0700158 * <p>
Tim Murrayc11e25c2013-04-09 11:01:01 -0700159 * A ScriptGroup must contain a single directed acyclic graph (DAG); it
160 * cannot contain cycles. Currently, all kernels used in a ScriptGroup
161 * must come from different Script objects. Additionally, all kernels
162 * in a ScriptGroup must have at least one input, output, or internal
163 * connection.
164 * <p>
165 * Once all connections are made, a call to {@link #create} will
Jason Sams08a81582012-09-18 12:32:10 -0700166 * return the ScriptGroup object.
167 *
168 */
169 public static final class Builder {
170 private RenderScript mRS;
171 private ArrayList<Node> mNodes = new ArrayList<Node>();
172 private ArrayList<ConnectLine> mLines = new ArrayList<ConnectLine>();
173 private int mKernelCount;
Jason Sams423ebcb2012-08-10 15:40:53 -0700174
Jason Sams08a81582012-09-18 12:32:10 -0700175 /**
Tim Murrayc11e25c2013-04-09 11:01:01 -0700176 * Create a Builder for generating a ScriptGroup.
Jason Sams08a81582012-09-18 12:32:10 -0700177 *
178 *
Tim Murrayc11e25c2013-04-09 11:01:01 -0700179 * @param rs The RenderScript context.
Jason Sams08a81582012-09-18 12:32:10 -0700180 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700181 public Builder(RenderScript rs) {
182 mRS = rs;
183 }
184
Tim Murray091f7cc2012-10-12 12:02:18 -0700185 // do a DFS from original node, looking for original node
186 // any cycle that could be created must contain original node
187 private void validateCycle(Node target, Node original) {
188 for (int ct = 0; ct < target.mOutputs.size(); ct++) {
189 final ConnectLine cl = target.mOutputs.get(ct);
Jason Sams08a81582012-09-18 12:32:10 -0700190 if (cl.mToK != null) {
191 Node tn = findNode(cl.mToK.mScript);
Tim Murray091f7cc2012-10-12 12:02:18 -0700192 if (tn.equals(original)) {
Jason Sams423ebcb2012-08-10 15:40:53 -0700193 throw new RSInvalidStateException("Loops in group not allowed.");
194 }
Tim Murray091f7cc2012-10-12 12:02:18 -0700195 validateCycle(tn, original);
Jason Sams08a81582012-09-18 12:32:10 -0700196 }
197 if (cl.mToF != null) {
198 Node tn = findNode(cl.mToF.mScript);
Tim Murray091f7cc2012-10-12 12:02:18 -0700199 if (tn.equals(original)) {
Jason Sams08a81582012-09-18 12:32:10 -0700200 throw new RSInvalidStateException("Loops in group not allowed.");
201 }
Tim Murray091f7cc2012-10-12 12:02:18 -0700202 validateCycle(tn, original);
Tim Murray2a603892012-10-10 14:21:46 -0700203 }
204 }
205 }
206
207 private void mergeDAGs(int valueUsed, int valueKilled) {
208 for (int ct=0; ct < mNodes.size(); ct++) {
209 if (mNodes.get(ct).dagNumber == valueKilled)
210 mNodes.get(ct).dagNumber = valueUsed;
211 }
212 }
213
214 private void validateDAGRecurse(Node n, int dagNumber) {
215 // combine DAGs if this node has been seen already
216 if (n.dagNumber != 0 && n.dagNumber != dagNumber) {
217 mergeDAGs(n.dagNumber, dagNumber);
218 return;
219 }
220
221 n.dagNumber = dagNumber;
222 for (int ct=0; ct < n.mOutputs.size(); ct++) {
223 final ConnectLine cl = n.mOutputs.get(ct);
224 if (cl.mToK != null) {
225 Node tn = findNode(cl.mToK.mScript);
226 validateDAGRecurse(tn, dagNumber);
227 }
228 if (cl.mToF != null) {
229 Node tn = findNode(cl.mToF.mScript);
230 validateDAGRecurse(tn, dagNumber);
231 }
232 }
233 }
234
235 private void validateDAG() {
236 for (int ct=0; ct < mNodes.size(); ct++) {
237 Node n = mNodes.get(ct);
238 if (n.mInputs.size() == 0) {
239 if (n.mOutputs.size() == 0 && mNodes.size() > 1) {
240 throw new RSInvalidStateException("Groups cannot contain unconnected scripts");
241 }
242 validateDAGRecurse(n, ct+1);
243 }
244 }
245 int dagNumber = mNodes.get(0).dagNumber;
246 for (int ct=0; ct < mNodes.size(); ct++) {
247 if (mNodes.get(ct).dagNumber != dagNumber) {
248 throw new RSInvalidStateException("Multiple DAGs in group not allowed.");
Jason Sams423ebcb2012-08-10 15:40:53 -0700249 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700250 }
251 }
252
Jason Sams08a81582012-09-18 12:32:10 -0700253 private Node findNode(Script s) {
254 for (int ct=0; ct < mNodes.size(); ct++) {
255 if (s == mNodes.get(ct).mScript) {
256 return mNodes.get(ct);
Jason Sams423ebcb2012-08-10 15:40:53 -0700257 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700258 }
259 return null;
260 }
261
Jason Sams08a81582012-09-18 12:32:10 -0700262 private Node findNode(Script.KernelID k) {
263 for (int ct=0; ct < mNodes.size(); ct++) {
264 Node n = mNodes.get(ct);
265 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
266 if (k == n.mKernels.get(ct2)) {
267 return n;
Jason Sams423ebcb2012-08-10 15:40:53 -0700268 }
269 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700270 }
Jason Sams08a81582012-09-18 12:32:10 -0700271 return null;
272 }
Jason Sams423ebcb2012-08-10 15:40:53 -0700273
Jason Sams08a81582012-09-18 12:32:10 -0700274 /**
275 * Adds a Kernel to the group.
276 *
277 *
278 * @param k The kernel to add.
279 *
280 * @return Builder Returns this.
281 */
282 public Builder addKernel(Script.KernelID k) {
283 if (mLines.size() != 0) {
284 throw new RSInvalidStateException(
285 "Kernels may not be added once connections exist.");
Jason Sams423ebcb2012-08-10 15:40:53 -0700286 }
Jason Sams08a81582012-09-18 12:32:10 -0700287
288 //android.util.Log.v("RSR", "addKernel 1 k=" + k);
289 if (findNode(k) != null) {
290 return this;
291 }
292 //android.util.Log.v("RSR", "addKernel 2 ");
293 mKernelCount++;
294 Node n = findNode(k.mScript);
295 if (n == null) {
296 //android.util.Log.v("RSR", "addKernel 3 ");
297 n = new Node(k.mScript);
298 mNodes.add(n);
299 }
300 n.mKernels.add(k);
301 return this;
302 }
303
304 /**
305 * Adds a connection to the group.
306 *
307 *
308 * @param t The type of the connection. This is used to
309 * determine the kernel launch sizes on the source side
310 * of this connection.
311 * @param from The source for the connection.
312 * @param to The destination of the connection.
313 *
314 * @return Builder Returns this
315 */
316 public Builder addConnection(Type t, Script.KernelID from, Script.FieldID to) {
317 //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
318
319 Node nf = findNode(from);
320 if (nf == null) {
Tim Murray091f7cc2012-10-12 12:02:18 -0700321 throw new RSInvalidStateException("From script not found.");
Jason Sams08a81582012-09-18 12:32:10 -0700322 }
323
324 Node nt = findNode(to.mScript);
325 if (nt == null) {
326 throw new RSInvalidStateException("To script not found.");
327 }
328
329 ConnectLine cl = new ConnectLine(t, from, to);
330 mLines.add(new ConnectLine(t, from, to));
331
332 nf.mOutputs.add(cl);
333 nt.mInputs.add(cl);
Jason Sams423ebcb2012-08-10 15:40:53 -0700334
Tim Murray091f7cc2012-10-12 12:02:18 -0700335 validateCycle(nf, nf);
Jason Sams423ebcb2012-08-10 15:40:53 -0700336 return this;
337 }
338
Jason Sams08a81582012-09-18 12:32:10 -0700339 /**
340 * Adds a connection to the group.
341 *
342 *
343 * @param t The type of the connection. This is used to
344 * determine the kernel launch sizes for both sides of
345 * this connection.
346 * @param from The source for the connection.
347 * @param to The destination of the connection.
348 *
349 * @return Builder Returns this
350 */
351 public Builder addConnection(Type t, Script.KernelID from, Script.KernelID to) {
352 //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
353
354 Node nf = findNode(from);
355 if (nf == null) {
Tim Murray091f7cc2012-10-12 12:02:18 -0700356 throw new RSInvalidStateException("From script not found.");
Jason Sams08a81582012-09-18 12:32:10 -0700357 }
358
359 Node nt = findNode(to);
360 if (nt == null) {
361 throw new RSInvalidStateException("To script not found.");
362 }
363
364 ConnectLine cl = new ConnectLine(t, from, to);
365 mLines.add(new ConnectLine(t, from, to));
366
367 nf.mOutputs.add(cl);
368 nt.mInputs.add(cl);
369
Tim Murray091f7cc2012-10-12 12:02:18 -0700370 validateCycle(nf, nf);
Jason Sams08a81582012-09-18 12:32:10 -0700371 return this;
372 }
373
374
375
376 /**
377 * Creates the Script group.
378 *
379 *
380 * @return ScriptGroup The new ScriptGroup
381 */
Jason Sams423ebcb2012-08-10 15:40:53 -0700382 public ScriptGroup create() {
Tim Murray2a603892012-10-10 14:21:46 -0700383
384 if (mNodes.size() == 0) {
385 throw new RSInvalidStateException("Empty script groups are not allowed");
386 }
387
388 // reset DAG numbers in case we're building a second group
389 for (int ct=0; ct < mNodes.size(); ct++) {
390 mNodes.get(ct).dagNumber = 0;
391 }
392 validateDAG();
393
Jason Sams08a81582012-09-18 12:32:10 -0700394 ArrayList<IO> inputs = new ArrayList<IO>();
395 ArrayList<IO> outputs = new ArrayList<IO>();
Jason Sams423ebcb2012-08-10 15:40:53 -0700396
Ashok Bhat98071552014-02-12 09:54:43 +0000397 long[] kernels = new long[mKernelCount];
Jason Sams08a81582012-09-18 12:32:10 -0700398 int idx = 0;
399 for (int ct=0; ct < mNodes.size(); ct++) {
400 Node n = mNodes.get(ct);
401 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
402 final Script.KernelID kid = n.mKernels.get(ct2);
Ashok Bhat98071552014-02-12 09:54:43 +0000403 kernels[idx++] = kid.getID(mRS);
Jason Sams423ebcb2012-08-10 15:40:53 -0700404
Jason Sams08a81582012-09-18 12:32:10 -0700405 boolean hasInput = false;
406 boolean hasOutput = false;
407 for (int ct3=0; ct3 < n.mInputs.size(); ct3++) {
408 if (n.mInputs.get(ct3).mToK == kid) {
409 hasInput = true;
410 }
411 }
412 for (int ct3=0; ct3 < n.mOutputs.size(); ct3++) {
413 if (n.mOutputs.get(ct3).mFrom == kid) {
414 hasOutput = true;
415 }
416 }
417 if (!hasInput) {
418 inputs.add(new IO(kid));
419 }
420 if (!hasOutput) {
421 outputs.add(new IO(kid));
422 }
423
424 }
425 }
426 if (idx != mKernelCount) {
427 throw new RSRuntimeException("Count mismatch, should not happen.");
428 }
429
Ashok Bhat98071552014-02-12 09:54:43 +0000430 long[] src = new long[mLines.size()];
431 long[] dstk = new long[mLines.size()];
432 long[] dstf = new long[mLines.size()];
433 long[] types = new long[mLines.size()];
Jason Sams08a81582012-09-18 12:32:10 -0700434
435 for (int ct=0; ct < mLines.size(); ct++) {
436 ConnectLine cl = mLines.get(ct);
Ashok Bhat98071552014-02-12 09:54:43 +0000437 src[ct] = cl.mFrom.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700438 if (cl.mToK != null) {
Ashok Bhat98071552014-02-12 09:54:43 +0000439 dstk[ct] = cl.mToK.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700440 }
441 if (cl.mToF != null) {
Ashok Bhat98071552014-02-12 09:54:43 +0000442 dstf[ct] = cl.mToF.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700443 }
Ashok Bhat98071552014-02-12 09:54:43 +0000444 types[ct] = cl.mAllocationType.getID(mRS);
Jason Sams08a81582012-09-18 12:32:10 -0700445 }
446
Tim Murray7a629fa2013-11-19 12:45:54 -0800447 long id = mRS.nScriptGroupCreate(kernels, src, dstk, dstf, types);
Jason Sams08a81582012-09-18 12:32:10 -0700448 if (id == 0) {
449 throw new RSRuntimeException("Object creation error, should not happen.");
450 }
451
452 ScriptGroup sg = new ScriptGroup(id, mRS);
453 sg.mOutputs = new IO[outputs.size()];
454 for (int ct=0; ct < outputs.size(); ct++) {
455 sg.mOutputs[ct] = outputs.get(ct);
456 }
457
458 sg.mInputs = new IO[inputs.size()];
459 for (int ct=0; ct < inputs.size(); ct++) {
460 sg.mInputs[ct] = inputs.get(ct);
461 }
462
Jason Sams423ebcb2012-08-10 15:40:53 -0700463 return sg;
464 }
465
466 }
467
468
469}
470
471