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