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