1 /*
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 
17 package android.renderscript;
18 
19 import android.util.Log;
20 import android.util.Pair;
21 import java.util.ArrayList;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25 
26 /**
27  * A group of kernels that are executed
28  * together with one execution call as if they were a single kernel
29  * <p>
30  * In addition to kernels, a script group may contain invocable functions as well.
31  * A script group may take inputs and generate outputs, which are consumed and
32  * produced by its member kernels.
33  * Inside a script group, outputs from one kernel can be passed to another kernel as inputs.
34  * The API disallows cyclic dependencies among kernels in a script group,
35  * effectively making it a directed acyclic graph (DAG) of kernels.
36  * <p>
37  * Grouping kernels together allows for more efficient execution. For example,
38  * runtime and compiler optimization can be applied to reduce computation and
39  * communication overhead, and to make better use of the CPU and the GPU.
40  *
41  * @deprecated Renderscript has been deprecated in API level 31. Please refer to the <a
42  * href="https://developer.android.com/guide/topics/renderscript/migration-guide">migration
43  * guide</a> for the proposed alternatives.
44  **/
45 @Deprecated
46 public final class ScriptGroup extends BaseObj {
47     private static final String TAG = "ScriptGroup";
48     IO mOutputs[];
49     IO mInputs[];
50 
51     static class IO {
52         Script.KernelID mKID;
53         Allocation mAllocation;
54 
IO(Script.KernelID s)55         IO(Script.KernelID s) {
56             mKID = s;
57         }
58     }
59 
60     static class ConnectLine {
ConnectLine(Type t, Script.KernelID from, Script.KernelID to)61         ConnectLine(Type t, Script.KernelID from, Script.KernelID to) {
62             mFrom = from;
63             mToK = to;
64             mAllocationType = t;
65         }
66 
ConnectLine(Type t, Script.KernelID from, Script.FieldID to)67         ConnectLine(Type t, Script.KernelID from, Script.FieldID to) {
68             mFrom = from;
69             mToF = to;
70             mAllocationType = t;
71         }
72 
73         Script.FieldID mToF;
74         Script.KernelID mToK;
75         Script.KernelID mFrom;
76         Type mAllocationType;
77     }
78 
79     static class Node {
80         Script mScript;
81         ArrayList<Script.KernelID> mKernels = new ArrayList<Script.KernelID>();
82         ArrayList<ConnectLine> mInputs = new ArrayList<ConnectLine>();
83         ArrayList<ConnectLine> mOutputs = new ArrayList<ConnectLine>();
84         int dagNumber;
85 
86         Node mNext;
87 
Node(Script s)88         Node(Script s) {
89             mScript = s;
90         }
91     }
92 
93 
94     /**
95      * An opaque class for closures
96      * <p>
97      * A closure represents a function call to a kernel or invocable function,
98      * combined with arguments and values for global variables. A closure is
99      * created using the {@link android.renderscript.ScriptGroup.Builder2#addKernel} or
100      * {@link android.renderscript.ScriptGroup.Builder2#addInvoke}
101      * method.
102      */
103 
104     public static final class Closure extends BaseObj {
105         private Object[] mArgs;
106         private Allocation mReturnValue;
107         private Map<Script.FieldID, Object> mBindings;
108 
109         private Future mReturnFuture;
110         private Map<Script.FieldID, Future> mGlobalFuture;
111 
112         private FieldPacker mFP;
113 
114         private static final String TAG = "Closure";
115 
Closure(long id, RenderScript rs)116         Closure(long id, RenderScript rs) {
117             super(id, rs);
118         }
119 
Closure(RenderScript rs, Script.KernelID kernelID, Type returnType, Object[] args, Map<Script.FieldID, Object> globals)120         Closure(RenderScript rs, Script.KernelID kernelID, Type returnType,
121                        Object[] args, Map<Script.FieldID, Object> globals) {
122             super(0, rs);
123 
124             mArgs = args;
125             mReturnValue = Allocation.createTyped(rs, returnType);
126             mBindings = globals;
127             mGlobalFuture = new HashMap<Script.FieldID, Future>();
128 
129             int numValues = args.length + globals.size();
130 
131             long[] fieldIDs = new long[numValues];
132             long[] values = new long[numValues];
133             int[] sizes = new int[numValues];
134             long[] depClosures = new long[numValues];
135             long[] depFieldIDs = new long[numValues];
136 
137             int i;
138             for (i = 0; i < args.length; i++) {
139                 fieldIDs[i] = 0;
140                 retrieveValueAndDependenceInfo(rs, i, null, args[i],
141                                                values, sizes, depClosures, depFieldIDs);
142             }
143             for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
144                 Object obj = entry.getValue();
145                 Script.FieldID fieldID = entry.getKey();
146                 fieldIDs[i] = fieldID.getID(rs);
147                 retrieveValueAndDependenceInfo(rs, i, fieldID, obj,
148                                                values, sizes, depClosures, depFieldIDs);
149                 i++;
150             }
151 
152             long id = rs.nClosureCreate(kernelID.getID(rs), mReturnValue.getID(rs),
153                                         fieldIDs, values, sizes, depClosures, depFieldIDs);
154 
155             setID(id);
156 
157             guard.open("destroy");
158         }
159 
Closure(RenderScript rs, Script.InvokeID invokeID, Object[] args, Map<Script.FieldID, Object> globals)160         Closure(RenderScript rs, Script.InvokeID invokeID,
161                        Object[] args, Map<Script.FieldID, Object> globals) {
162             super(0, rs);
163             mFP = FieldPacker.createFromArray(args);
164 
165             mArgs = args;
166             mBindings = globals;
167             mGlobalFuture = new HashMap<Script.FieldID, Future>();
168 
169             int numValues = globals.size();
170 
171             long[] fieldIDs = new long[numValues];
172             long[] values = new long[numValues];
173             int[] sizes = new int[numValues];
174             long[] depClosures = new long[numValues];
175             long[] depFieldIDs = new long[numValues];
176 
177             int i = 0;
178             for (Map.Entry<Script.FieldID, Object> entry : globals.entrySet()) {
179                 Object obj = entry.getValue();
180                 Script.FieldID fieldID = entry.getKey();
181                 fieldIDs[i] = fieldID.getID(rs);
182                 retrieveValueAndDependenceInfo(rs, i, fieldID, obj, values,
183                                                sizes, depClosures, depFieldIDs);
184                 i++;
185             }
186 
187             long id = rs.nInvokeClosureCreate(invokeID.getID(rs), mFP.getData(), fieldIDs,
188                                               values, sizes);
189 
190             setID(id);
191 
192             guard.open("destroy");
193         }
194 
195         /**
196          * Destroys this Closure and the Allocation for its return value
197          */
destroy()198         public void destroy() {
199             super.destroy();
200             if (mReturnValue != null) {
201                 mReturnValue.destroy();
202             }
203         }
204 
finalize()205         protected void finalize() throws Throwable {
206             // Set null mReturnValue to avoid double-destroying it, in case its
207             // finalizer races ahead.
208             mReturnValue = null;
209             super.finalize();
210         }
211 
retrieveValueAndDependenceInfo(RenderScript rs, int index, Script.FieldID fid, Object obj, long[] values, int[] sizes, long[] depClosures, long[] depFieldIDs)212         private void retrieveValueAndDependenceInfo(RenderScript rs,
213                                                     int index, Script.FieldID fid, Object obj,
214                                                     long[] values, int[] sizes,
215                                                     long[] depClosures,
216                                                     long[] depFieldIDs) {
217 
218             if (obj instanceof Future) {
219                 Future f = (Future)obj;
220                 obj = f.getValue();
221                 depClosures[index] = f.getClosure().getID(rs);
222                 Script.FieldID fieldID = f.getFieldID();
223                 depFieldIDs[index] = fieldID != null ? fieldID.getID(rs) : 0;
224             } else {
225                 depClosures[index] = 0;
226                 depFieldIDs[index] = 0;
227             }
228 
229             if (obj instanceof Input) {
230                 Input unbound = (Input)obj;
231                 if (index < mArgs.length) {
232                     unbound.addReference(this, index);
233                 } else {
234                     unbound.addReference(this, fid);
235                 }
236                 values[index] = 0;
237                 sizes[index] = 0;
238             } else {
239                 ValueAndSize vs = new ValueAndSize(rs, obj);
240                 values[index] = vs.value;
241                 sizes[index] = vs.size;
242             }
243         }
244 
245         /**
246          * Returns the future for the return value
247          *
248          * @return a future
249          */
250 
getReturn()251         public Future getReturn() {
252             if (mReturnFuture == null) {
253                 mReturnFuture = new Future(this, null, mReturnValue);
254             }
255 
256             return mReturnFuture;
257         }
258 
259         /**
260          * Returns the future for a global variable
261          *
262          * @param field the field ID for the global variable
263          * @return a future
264          */
265 
getGlobal(Script.FieldID field)266         public Future getGlobal(Script.FieldID field) {
267             Future f = mGlobalFuture.get(field);
268 
269             if (f == null) {
270                 // If the field is not bound to this closure, this will return a future
271                 // without an associated value (reference). So this is not working for
272                 // cross-module (cross-script) linking in this case where a field not
273                 // explicitly bound.
274                 Object obj = mBindings.get(field);
275                 if (obj instanceof Future) {
276                     obj = ((Future)obj).getValue();
277                 }
278                 f = new Future(this, field, obj);
279                 mGlobalFuture.put(field, f);
280             }
281 
282             return f;
283         }
284 
setArg(int index, Object obj)285         void setArg(int index, Object obj) {
286             if (obj instanceof Future) {
287                 obj = ((Future)obj).getValue();
288             }
289             mArgs[index] = obj;
290             ValueAndSize vs = new ValueAndSize(mRS, obj);
291             mRS.nClosureSetArg(getID(mRS), index, vs.value, vs.size);
292         }
293 
setGlobal(Script.FieldID fieldID, Object obj)294         void setGlobal(Script.FieldID fieldID, Object obj) {
295             if (obj instanceof Future) {
296                 obj = ((Future)obj).getValue();
297             }
298             mBindings.put(fieldID, obj);
299             ValueAndSize vs = new ValueAndSize(mRS, obj);
300             mRS.nClosureSetGlobal(getID(mRS), fieldID.getID(mRS), vs.value, vs.size);
301         }
302 
303         private static final class ValueAndSize {
ValueAndSize(RenderScript rs, Object obj)304             public ValueAndSize(RenderScript rs, Object obj) {
305                 if (obj instanceof Allocation) {
306                     value = ((Allocation)obj).getID(rs);
307                     // Special value for size to tell the runtime and driver that
308                     // the value is an Allocation
309                     size = -1;
310                 } else if (obj instanceof Boolean) {
311                     value = ((Boolean)obj).booleanValue() ? 1 : 0;
312                     size = 4;
313                 } else if (obj instanceof Integer) {
314                     value = ((Integer)obj).longValue();
315                     size = 4;
316                 } else if (obj instanceof Long) {
317                     value = ((Long)obj).longValue();
318                     size = 8;
319                 } else if (obj instanceof Float) {
320                     value = Float.floatToRawIntBits(((Float)obj).floatValue());
321                     size = 4;
322                 } else if (obj instanceof Double) {
323                     value = Double.doubleToRawLongBits(((Double)obj).doubleValue());
324                     size = 8;
325                 }
326             }
327             public long value;
328             public int size;
329         }
330     }
331 
332     /**
333      * An opaque class for futures
334      * <p>
335      * A future represents an output of a closure, either the return value of
336      * the function, or the value of a global variable written by the function.
337      * A future is created by calling the {@link Closure#getReturn}  or
338      * {@link Closure#getGlobal} method.
339      */
340 
341     public static final class Future {
342         Closure mClosure;
343         Script.FieldID mFieldID;
344         Object mValue;
345 
Future(Closure closure, Script.FieldID fieldID, Object value)346         Future(Closure closure, Script.FieldID fieldID, Object value) {
347             mClosure = closure;
348             mFieldID = fieldID;
349             mValue = value;
350         }
351 
getClosure()352         Closure getClosure() { return mClosure; }
getFieldID()353         Script.FieldID getFieldID() { return mFieldID; }
getValue()354         Object getValue() { return mValue; }
355     }
356 
357     /**
358      * An opaque class for script group inputs
359      * <p>
360      * Created by calling the {@link Builder2#addInput} method. The value
361      * is assigned in {@link ScriptGroup#execute(Object...)} method as
362      * one of its arguments. Arguments to the execute method should be in
363      * the same order as intputs are added using the addInput method.
364      */
365 
366     public static final class Input {
367         // Either mFieldID or mArgIndex should be set but not both.
368         List<Pair<Closure, Script.FieldID>> mFieldID;
369         // -1 means unset. Legal values are 0 .. n-1, where n is the number of
370         // arguments for the referencing closure.
371         List<Pair<Closure, Integer>> mArgIndex;
372         Object mValue;
373 
Input()374         Input() {
375             mFieldID = new ArrayList<Pair<Closure, Script.FieldID>>();
376             mArgIndex = new ArrayList<Pair<Closure, Integer>>();
377         }
378 
addReference(Closure closure, int index)379         void addReference(Closure closure, int index) {
380             mArgIndex.add(Pair.create(closure, Integer.valueOf(index)));
381         }
382 
addReference(Closure closure, Script.FieldID fieldID)383         void addReference(Closure closure, Script.FieldID fieldID) {
384             mFieldID.add(Pair.create(closure, fieldID));
385         }
386 
set(Object value)387         void set(Object value) {
388             mValue = value;
389             for (Pair<Closure, Integer> p : mArgIndex) {
390                 Closure closure = p.first;
391                 int index = p.second.intValue();
392                 closure.setArg(index, value);
393             }
394             for (Pair<Closure, Script.FieldID> p : mFieldID) {
395                 Closure closure = p.first;
396                 Script.FieldID fieldID = p.second;
397                 closure.setGlobal(fieldID, value);
398             }
399         }
400 
get()401         Object get() { return mValue; }
402     }
403 
404     private String mName;
405     private List<Closure> mClosures;
406     private List<Input> mInputs2;
407     private Future[] mOutputs2;
408 
ScriptGroup(long id, RenderScript rs)409     ScriptGroup(long id, RenderScript rs) {
410         super(id, rs);
411         guard.open("destroy");
412     }
413 
ScriptGroup(RenderScript rs, String name, List<Closure> closures, List<Input> inputs, Future[] outputs)414     ScriptGroup(RenderScript rs, String name, List<Closure> closures,
415                  List<Input> inputs, Future[] outputs) {
416         super(0, rs);
417         mName = name;
418         mClosures = closures;
419         mInputs2 = inputs;
420         mOutputs2 = outputs;
421 
422         long[] closureIDs = new long[closures.size()];
423         for (int i = 0; i < closureIDs.length; i++) {
424             closureIDs[i] = closures.get(i).getID(rs);
425         }
426         long id = rs.nScriptGroup2Create(name, RenderScript.getCachePath(), closureIDs);
427         setID(id);
428         guard.open("destroy");
429     }
430 
431     /**
432      * Executes a script group
433      *
434      * @param inputs Values for inputs to the script group, in the order as the
435      *        inputs are added via {@link Builder2#addInput}.
436      * @return Outputs of the script group as an array of objects, in the order
437      *         as futures are passed to {@link Builder2#create}.
438      */
439 
execute(Object... inputs)440     public Object[] execute(Object... inputs) {
441         if (inputs.length < mInputs2.size()) {
442             Log.e(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
443                   "less than expected " + mInputs2.size());
444             return null;
445         }
446 
447         if (inputs.length > mInputs2.size()) {
448             Log.i(TAG, this.toString() + " receives " + inputs.length + " inputs, " +
449                   "more than expected " + mInputs2.size());
450         }
451 
452         for (int i = 0; i < mInputs2.size(); i++) {
453             Object obj = inputs[i];
454             if (obj instanceof Future || obj instanceof Input) {
455                 Log.e(TAG, this.toString() + ": input " + i +
456                       " is a future or unbound value");
457                 return null;
458             }
459             Input unbound = mInputs2.get(i);
460             unbound.set(obj);
461         }
462 
463         mRS.nScriptGroup2Execute(getID(mRS));
464 
465         Object[] outputObjs = new Object[mOutputs2.length];
466         int i = 0;
467         for (Future f : mOutputs2) {
468             Object output = f.getValue();
469             if (output instanceof Input) {
470                 output = ((Input)output).get();
471             }
472             outputObjs[i++] = output;
473         }
474         return outputObjs;
475     }
476 
477     /**
478      * Sets an input of the ScriptGroup. This specifies an
479      * Allocation to be used for kernels that require an input
480      * Allocation provided from outside of the ScriptGroup.
481      *
482      * @deprecated Set arguments to {@link #execute(Object...)} instead.
483      *
484      * @param s The ID of the kernel where the allocation should be
485      *          connected.
486      * @param a The allocation to connect.
487      */
setInput(Script.KernelID s, Allocation a)488     public void setInput(Script.KernelID s, Allocation a) {
489         for (int ct=0; ct < mInputs.length; ct++) {
490             if (mInputs[ct].mKID == s) {
491                 mInputs[ct].mAllocation = a;
492                 mRS.nScriptGroupSetInput(getID(mRS), s.getID(mRS), mRS.safeID(a));
493                 return;
494             }
495         }
496         throw new RSIllegalArgumentException("Script not found");
497     }
498 
499     /**
500      * Sets an output of the ScriptGroup. This specifies an
501      * Allocation to be used for the kernels that require an output
502      * Allocation visible after the ScriptGroup is executed.
503      *
504      * @deprecated Use return value of {@link #execute(Object...)} instead.
505      *
506      * @param s The ID of the kernel where the allocation should be
507      *          connected.
508      * @param a The allocation to connect.
509      */
setOutput(Script.KernelID s, Allocation a)510     public void setOutput(Script.KernelID s, Allocation a) {
511         for (int ct=0; ct < mOutputs.length; ct++) {
512             if (mOutputs[ct].mKID == s) {
513                 mOutputs[ct].mAllocation = a;
514                 mRS.nScriptGroupSetOutput(getID(mRS), s.getID(mRS), mRS.safeID(a));
515                 return;
516             }
517         }
518         throw new RSIllegalArgumentException("Script not found");
519     }
520 
521     /**
522      * Execute the ScriptGroup.  This will run all the kernels in
523      * the ScriptGroup.  No internal connection results will be visible
524      * after execution of the ScriptGroup.
525      *
526      * @deprecated Use {@link #execute} instead.
527      *
528      */
execute()529     public void execute() {
530         mRS.nScriptGroupExecute(getID(mRS));
531     }
532 
533 
534     /**
535      * Helper class to build a ScriptGroup. A ScriptGroup is
536      * created in two steps.
537      * <p>
538      * First, all kernels to be used by the ScriptGroup should be added.
539      * <p>
540      * Second, add connections between kernels. There are two types
541      * of connections: kernel to kernel and kernel to field.
542      * Kernel to kernel allows a kernel's output to be passed to
543      * another kernel as input. Kernel to field allows the output of
544      * one kernel to be bound as a script global. Kernel to kernel is
545      * higher performance and should be used where possible.
546      * <p>
547      * A ScriptGroup must contain a single directed acyclic graph (DAG); it
548      * cannot contain cycles. Currently, all kernels used in a ScriptGroup
549      * must come from different Script objects. Additionally, all kernels
550      * in a ScriptGroup must have at least one input, output, or internal
551      * connection.
552      * <p>
553      * Once all connections are made, a call to {@link #create} will
554      * return the ScriptGroup object.
555      *
556      * @deprecated Use {@link Builder2} instead.
557      *
558      */
559     public static final class Builder {
560         private RenderScript mRS;
561         private ArrayList<Node> mNodes = new ArrayList<Node>();
562         private ArrayList<ConnectLine> mLines = new ArrayList<ConnectLine>();
563         private int mKernelCount;
564 
565         /**
566          * Create a Builder for generating a ScriptGroup.
567          *
568          *
569          * @param rs The RenderScript context.
570          */
Builder(RenderScript rs)571         public Builder(RenderScript rs) {
572             mRS = rs;
573         }
574 
575         // do a DFS from original node, looking for original node
576         // any cycle that could be created must contain original node
validateCycle(Node target, Node original)577         private void validateCycle(Node target, Node original) {
578             for (int ct = 0; ct < target.mOutputs.size(); ct++) {
579                 final ConnectLine cl = target.mOutputs.get(ct);
580                 if (cl.mToK != null) {
581                     Node tn = findNode(cl.mToK.mScript);
582                     if (tn.equals(original)) {
583                         throw new RSInvalidStateException("Loops in group not allowed.");
584                     }
585                     validateCycle(tn, original);
586                 }
587                 if (cl.mToF != null) {
588                     Node tn = findNode(cl.mToF.mScript);
589                     if (tn.equals(original)) {
590                         throw new RSInvalidStateException("Loops in group not allowed.");
591                     }
592                     validateCycle(tn, original);
593                 }
594             }
595         }
596 
mergeDAGs(int valueUsed, int valueKilled)597         private void mergeDAGs(int valueUsed, int valueKilled) {
598             for (int ct=0; ct < mNodes.size(); ct++) {
599                 if (mNodes.get(ct).dagNumber == valueKilled)
600                     mNodes.get(ct).dagNumber = valueUsed;
601             }
602         }
603 
validateDAGRecurse(Node n, int dagNumber)604         private void validateDAGRecurse(Node n, int dagNumber) {
605             // combine DAGs if this node has been seen already
606             if (n.dagNumber != 0 && n.dagNumber != dagNumber) {
607                 mergeDAGs(n.dagNumber, dagNumber);
608                 return;
609             }
610 
611             n.dagNumber = dagNumber;
612             for (int ct=0; ct < n.mOutputs.size(); ct++) {
613                 final ConnectLine cl = n.mOutputs.get(ct);
614                 if (cl.mToK != null) {
615                     Node tn = findNode(cl.mToK.mScript);
616                     validateDAGRecurse(tn, dagNumber);
617                 }
618                 if (cl.mToF != null) {
619                     Node tn = findNode(cl.mToF.mScript);
620                     validateDAGRecurse(tn, dagNumber);
621                 }
622             }
623         }
624 
validateDAG()625         private void validateDAG() {
626             for (int ct=0; ct < mNodes.size(); ct++) {
627                 Node n = mNodes.get(ct);
628                 if (n.mInputs.size() == 0) {
629                     if (n.mOutputs.size() == 0 && mNodes.size() > 1) {
630                         String msg = "Groups cannot contain unconnected scripts";
631                         throw new RSInvalidStateException(msg);
632                     }
633                     validateDAGRecurse(n, ct+1);
634                 }
635             }
636             int dagNumber = mNodes.get(0).dagNumber;
637             for (int ct=0; ct < mNodes.size(); ct++) {
638                 if (mNodes.get(ct).dagNumber != dagNumber) {
639                     throw new RSInvalidStateException("Multiple DAGs in group not allowed.");
640                 }
641             }
642         }
643 
findNode(Script s)644         private Node findNode(Script s) {
645             for (int ct=0; ct < mNodes.size(); ct++) {
646                 if (s == mNodes.get(ct).mScript) {
647                     return mNodes.get(ct);
648                 }
649             }
650             return null;
651         }
652 
findNode(Script.KernelID k)653         private Node findNode(Script.KernelID k) {
654             for (int ct=0; ct < mNodes.size(); ct++) {
655                 Node n = mNodes.get(ct);
656                 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
657                     if (k == n.mKernels.get(ct2)) {
658                         return n;
659                     }
660                 }
661             }
662             return null;
663         }
664 
665         /**
666          * Adds a Kernel to the group.
667          *
668          *
669          * @param k The kernel to add.
670          *
671          * @return Builder Returns this.
672          */
addKernel(Script.KernelID k)673         public Builder addKernel(Script.KernelID k) {
674             if (mLines.size() != 0) {
675                 throw new RSInvalidStateException(
676                     "Kernels may not be added once connections exist.");
677             }
678 
679             //android.util.Log.v("RSR", "addKernel 1 k=" + k);
680             if (findNode(k) != null) {
681                 return this;
682             }
683             //android.util.Log.v("RSR", "addKernel 2 ");
684             mKernelCount++;
685             Node n = findNode(k.mScript);
686             if (n == null) {
687                 //android.util.Log.v("RSR", "addKernel 3 ");
688                 n = new Node(k.mScript);
689                 mNodes.add(n);
690             }
691             n.mKernels.add(k);
692             return this;
693         }
694 
695         /**
696          * Adds a connection to the group.
697          *
698          *
699          * @param t The type of the connection. This is used to
700          *          determine the kernel launch sizes on the source side
701          *          of this connection.
702          * @param from The source for the connection.
703          * @param to The destination of the connection.
704          *
705          * @return Builder Returns this
706          */
addConnection(Type t, Script.KernelID from, Script.FieldID to)707         public Builder addConnection(Type t, Script.KernelID from, Script.FieldID to) {
708             //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
709 
710             Node nf = findNode(from);
711             if (nf == null) {
712                 throw new RSInvalidStateException("From script not found.");
713             }
714 
715             Node nt = findNode(to.mScript);
716             if (nt == null) {
717                 throw new RSInvalidStateException("To script not found.");
718             }
719 
720             ConnectLine cl = new ConnectLine(t, from, to);
721             mLines.add(new ConnectLine(t, from, to));
722 
723             nf.mOutputs.add(cl);
724             nt.mInputs.add(cl);
725 
726             validateCycle(nf, nf);
727             return this;
728         }
729 
730         /**
731          * Adds a connection to the group.
732          *
733          *
734          * @param t The type of the connection. This is used to
735          *          determine the kernel launch sizes for both sides of
736          *          this connection.
737          * @param from The source for the connection.
738          * @param to The destination of the connection.
739          *
740          * @return Builder Returns this
741          */
addConnection(Type t, Script.KernelID from, Script.KernelID to)742         public Builder addConnection(Type t, Script.KernelID from, Script.KernelID to) {
743             //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
744 
745             Node nf = findNode(from);
746             if (nf == null) {
747                 throw new RSInvalidStateException("From script not found.");
748             }
749 
750             Node nt = findNode(to);
751             if (nt == null) {
752                 throw new RSInvalidStateException("To script not found.");
753             }
754 
755             ConnectLine cl = new ConnectLine(t, from, to);
756             mLines.add(new ConnectLine(t, from, to));
757 
758             nf.mOutputs.add(cl);
759             nt.mInputs.add(cl);
760 
761             validateCycle(nf, nf);
762             return this;
763         }
764 
765 
766 
767         /**
768          * Creates the Script group.
769          *
770          *
771          * @return ScriptGroup The new ScriptGroup
772          */
create()773         public ScriptGroup create() {
774 
775             if (mNodes.size() == 0) {
776                 throw new RSInvalidStateException("Empty script groups are not allowed");
777             }
778 
779             // reset DAG numbers in case we're building a second group
780             for (int ct=0; ct < mNodes.size(); ct++) {
781                 mNodes.get(ct).dagNumber = 0;
782             }
783             validateDAG();
784 
785             ArrayList<IO> inputs = new ArrayList<IO>();
786             ArrayList<IO> outputs = new ArrayList<IO>();
787 
788             long[] kernels = new long[mKernelCount];
789             int idx = 0;
790             for (int ct=0; ct < mNodes.size(); ct++) {
791                 Node n = mNodes.get(ct);
792                 for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
793                     final Script.KernelID kid = n.mKernels.get(ct2);
794                     kernels[idx++] = kid.getID(mRS);
795 
796                     boolean hasInput = false;
797                     boolean hasOutput = false;
798                     for (int ct3=0; ct3 < n.mInputs.size(); ct3++) {
799                         if (n.mInputs.get(ct3).mToK == kid) {
800                             hasInput = true;
801                         }
802                     }
803                     for (int ct3=0; ct3 < n.mOutputs.size(); ct3++) {
804                         if (n.mOutputs.get(ct3).mFrom == kid) {
805                             hasOutput = true;
806                         }
807                     }
808                     if (!hasInput) {
809                         inputs.add(new IO(kid));
810                     }
811                     if (!hasOutput) {
812                         outputs.add(new IO(kid));
813                     }
814 
815                 }
816             }
817             if (idx != mKernelCount) {
818                 throw new RSRuntimeException("Count mismatch, should not happen.");
819             }
820 
821             long[] src = new long[mLines.size()];
822             long[] dstk = new long[mLines.size()];
823             long[] dstf = new long[mLines.size()];
824             long[] types = new long[mLines.size()];
825 
826             for (int ct=0; ct < mLines.size(); ct++) {
827                 ConnectLine cl = mLines.get(ct);
828                 src[ct] = cl.mFrom.getID(mRS);
829                 if (cl.mToK != null) {
830                     dstk[ct] = cl.mToK.getID(mRS);
831                 }
832                 if (cl.mToF != null) {
833                     dstf[ct] = cl.mToF.getID(mRS);
834                 }
835                 types[ct] = cl.mAllocationType.getID(mRS);
836             }
837 
838             long id = mRS.nScriptGroupCreate(kernels, src, dstk, dstf, types);
839             if (id == 0) {
840                 throw new RSRuntimeException("Object creation error, should not happen.");
841             }
842 
843             ScriptGroup sg = new ScriptGroup(id, mRS);
844             sg.mOutputs = new IO[outputs.size()];
845             for (int ct=0; ct < outputs.size(); ct++) {
846                 sg.mOutputs[ct] = outputs.get(ct);
847             }
848 
849             sg.mInputs = new IO[inputs.size()];
850             for (int ct=0; ct < inputs.size(); ct++) {
851                 sg.mInputs[ct] = inputs.get(ct);
852             }
853 
854             return sg;
855         }
856 
857     }
858 
859     /**
860      * Represents a binding of a value to a global variable in a
861      * kernel or invocable function. Used in closure creation.
862      */
863 
864     public static final class Binding {
865         private final Script.FieldID mField;
866         private final Object mValue;
867 
868         /**
869          * Returns a Binding object that binds value to field
870          *
871          * @param field the Script.FieldID of the global variable
872          * @param value the value
873          */
874 
Binding(Script.FieldID field, Object value)875         public Binding(Script.FieldID field, Object value) {
876             mField = field;
877             mValue = value;
878         }
879 
880         /**
881          * Returns the field ID
882          */
883 
getField()884         Script.FieldID getField() { return mField; }
885 
886         /**
887          * Returns the value
888          */
889 
getValue()890         Object getValue() { return mValue; }
891     }
892 
893     /**
894      * The builder class for creating script groups
895      * <p>
896      * A script group is created using closures (see class {@link Closure}).
897      * A closure is a function call to a kernel or
898      * invocable function. Each function argument or global variable accessed inside
899      * the function is bound to 1) a known value, 2) a script group input
900      * (see class {@link Input}), or 3) a
901      * future (see class {@link Future}).
902      * A future is the output of a closure, either the return value of the
903      * function or a global variable written by that function.
904      * <p>
905      * Closures are created using the {@link #addKernel} or {@link #addInvoke}
906      * methods.
907      * When a closure is created, futures from previously created closures
908      * can be used as its inputs.
909      * External script group inputs can be used as inputs to individual closures as well.
910      * An external script group input is created using the {@link #addInput} method.
911      * A script group is created by a call to the {@link #create} method, which
912      * accepts an array of futures as the outputs for the script group.
913      * <p>
914      * Closures in a script group can be evaluated in any order as long as the
915      * following conditions are met:
916      * 1) a closure must be evaluated before any other closures that take its
917      * futures as inputs;
918      * 2) all closures added before an invoke closure must be evaluated
919      * before it;
920      * and 3) all closures added after an invoke closure must be evaluated after
921      * it.
922      * As a special case, the order that the closures are added is a legal
923      * evaluation order. However, other evaluation orders are possible, including
924      * concurrently evaluating independent closures.
925      */
926 
927     public static final class Builder2 {
928         RenderScript mRS;
929         List<Closure> mClosures;
930         List<Input> mInputs;
931         private static final String TAG = "ScriptGroup.Builder2";
932 
933         /**
934          * Returns a Builder object
935          *
936          * @param rs the RenderScript context
937          */
Builder2(RenderScript rs)938         public Builder2(RenderScript rs) {
939             mRS = rs;
940             mClosures = new ArrayList<Closure>();
941             mInputs = new ArrayList<Input>();
942         }
943 
944         /**
945          * Adds a closure for a kernel
946          *
947          * @param k Kernel ID for the kernel function
948          * @param returnType Allocation type for the return value
949          * @param args arguments to the kernel function
950          * @param globalBindings bindings for global variables
951          * @return a closure
952          */
953 
addKernelInternal(Script.KernelID k, Type returnType, Object[] args, Map<Script.FieldID, Object> globalBindings)954         private Closure addKernelInternal(Script.KernelID k, Type returnType, Object[] args,
955                                           Map<Script.FieldID, Object> globalBindings) {
956             Closure c = new Closure(mRS, k, returnType, args, globalBindings);
957             mClosures.add(c);
958             return c;
959         }
960 
961         /**
962          * Adds a closure for an invocable function
963          *
964          * @param invoke Invoke ID for the invocable function
965          * @param args arguments to the invocable function
966          * @param globalBindings bindings for global variables
967          * @return a closure
968          */
969 
addInvokeInternal(Script.InvokeID invoke, Object[] args, Map<Script.FieldID, Object> globalBindings)970         private Closure addInvokeInternal(Script.InvokeID invoke, Object[] args,
971                                           Map<Script.FieldID, Object> globalBindings) {
972             Closure c = new Closure(mRS, invoke, args, globalBindings);
973             mClosures.add(c);
974             return c;
975         }
976 
977         /**
978          * Adds a script group input
979          *
980          * @return a script group input, which can be used as an argument or a value to
981          *     a global variable for creating closures
982          */
addInput()983         public Input addInput() {
984             Input unbound = new Input();
985             mInputs.add(unbound);
986             return unbound;
987         }
988 
989         /**
990          * Adds a closure for a kernel
991          *
992          * @param k Kernel ID for the kernel function
993          * @param argsAndBindings arguments followed by bindings for global variables
994          * @return a closure
995          */
996 
addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings)997         public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) {
998             ArrayList<Object> args = new ArrayList<Object>();
999             Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
1000             if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
1001                 return null;
1002             }
1003             return addKernelInternal(k, returnType, args.toArray(), bindingMap);
1004         }
1005 
1006         /**
1007          * Adds a closure for an invocable function
1008          *
1009          * @param invoke Invoke ID for the invocable function
1010          * @param argsAndBindings arguments followed by bindings for global variables
1011          * @return a closure
1012          */
1013 
addInvoke(Script.InvokeID invoke, Object... argsAndBindings)1014         public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) {
1015             ArrayList<Object> args = new ArrayList<Object>();
1016             Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
1017             if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
1018                 return null;
1019             }
1020             return addInvokeInternal(invoke, args.toArray(), bindingMap);
1021         }
1022 
1023         /**
1024          * Creates a script group
1025          *
1026          * @param name name for the script group. Legal names can only contain letters, digits,
1027          *        '-', or '_'. The name can be no longer than 100 characters.
1028          *        Try to use unique names, to avoid name conflicts and reduce
1029          *        the cost of group creation.
1030          * @param outputs futures intended as outputs of the script group
1031          * @return a script group
1032          */
1033 
create(String name, Future... outputs)1034         public ScriptGroup create(String name, Future... outputs) {
1035             if (name == null || name.isEmpty() || name.length() > 100 ||
1036                 !name.equals(name.replaceAll("[^a-zA-Z0-9-]", "_"))) {
1037                 throw new RSIllegalArgumentException("invalid script group name");
1038             }
1039             ScriptGroup ret = new ScriptGroup(mRS, name, mClosures, mInputs, outputs);
1040             mClosures = new ArrayList<Closure>();
1041             mInputs = new ArrayList<Input>();
1042             return ret;
1043         }
1044 
seperateArgsAndBindings(Object[] argsAndBindings, ArrayList<Object> args, Map<Script.FieldID, Object> bindingMap)1045         private boolean seperateArgsAndBindings(Object[] argsAndBindings,
1046                                                 ArrayList<Object> args,
1047                                                 Map<Script.FieldID, Object> bindingMap) {
1048             int i;
1049             for (i = 0; i < argsAndBindings.length; i++) {
1050                 if (argsAndBindings[i] instanceof Binding) {
1051                     break;
1052                 }
1053                 args.add(argsAndBindings[i]);
1054             }
1055 
1056             for (; i < argsAndBindings.length; i++) {
1057                 if (!(argsAndBindings[i] instanceof Binding)) {
1058                     return false;
1059                 }
1060                 Binding b = (Binding)argsAndBindings[i];
1061                 bindingMap.put(b.getField(), b.getValue());
1062             }
1063 
1064             return true;
1065         }
1066 
1067     }
1068 
1069     /**
1070      * Destroy this ScriptGroup and all Closures in it
1071      */
destroy()1072     public void destroy() {
1073         super.destroy();
1074         // ScriptGroup created using the old Builder class does not
1075         // initialize the field mClosures
1076         if (mClosures != null) {
1077             for (Closure c : mClosures) {
1078                 c.destroy();
1079             }
1080         }
1081     }
1082 }
1083