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)obj).longValue();
311                    size = 4;
312                } else if (obj instanceof Double) {
313                    value = ((Double)obj).longValue();
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    public void setInput(Script.KernelID s, Allocation a) {
480        for (int ct=0; ct < mInputs.length; ct++) {
481            if (mInputs[ct].mKID == s) {
482                mInputs[ct].mAllocation = a;
483                if (!mUseIncSupp) {
484                    mRS.nScriptGroupSetInput(getID(mRS), s.getID(mRS), mRS.safeID(a));
485                }
486                return;
487            }
488        }
489        throw new RSIllegalArgumentException("Script not found");
490    }
491
492    /**
493     * Sets an output of the ScriptGroup. This specifies an
494     * Allocation to be used for the kernels that require an output
495     * Allocation visible after the ScriptGroup is executed.
496     *
497     * @deprecated Use return value of {@link #execute(Object...)} instead.
498     *
499     * @param s The ID of the kernel where the allocation should be
500     *          connected.
501     * @param a The allocation to connect.
502     */
503    public void setOutput(Script.KernelID s, Allocation a) {
504        for (int ct=0; ct < mOutputs.length; ct++) {
505            if (mOutputs[ct].mKID == s) {
506                mOutputs[ct].mAllocation = a;
507                if (!mUseIncSupp) {
508                    mRS.nScriptGroupSetOutput(getID(mRS), s.getID(mRS), mRS.safeID(a));
509                }
510                return;
511            }
512        }
513        throw new RSIllegalArgumentException("Script not found");
514    }
515
516    /**
517     * Execute the ScriptGroup.  This will run all the kernels in
518     * the ScriptGroup.  No internal connection results will be visible
519     * after execution of the ScriptGroup.
520     *
521     * If Incremental Support for intrinsics is needed, the execution
522     * will take the naive path: execute kernels one by one in the
523     * correct order.
524     *
525     * @deprecated Use {@link #execute} instead.
526     */
527    public void execute() {
528        if (!mUseIncSupp) {
529            mRS.nScriptGroupExecute(getID(mRS));
530        } else {
531            // setup the allocations.
532            for (int ct=0; ct < mNodes.size(); ct++) {
533                Node n = mNodes.get(ct);
534                for (int ct2=0; ct2 < n.mOutputs.size(); ct2++) {
535                    ConnectLine l = n.mOutputs.get(ct2);
536                    if (l.mAllocation !=null) {
537                        continue;
538                    }
539
540                    //create allocation here
541                    Allocation alloc = Allocation.createTyped(mRS, l.mAllocationType,
542                                                              Allocation.MipmapControl.MIPMAP_NONE,
543                                                              Allocation.USAGE_SCRIPT);
544
545                    l.mAllocation = alloc;
546                    for (int ct3=ct2+1; ct3 < n.mOutputs.size(); ct3++) {
547                        if (n.mOutputs.get(ct3).mFrom == l.mFrom) {
548                            n.mOutputs.get(ct3).mAllocation = alloc;
549                        }
550                    }
551                }
552            }
553            for (Node node : mNodes) {
554                for (Script.KernelID kernel : node.mKernels) {
555                    Allocation ain  = null;
556                    Allocation aout = null;
557
558                    for (ConnectLine nodeInput : node.mInputs) {
559                        if (nodeInput.mToK == kernel) {
560                            ain = nodeInput.mAllocation;
561                        }
562                    }
563
564                    for (IO sgInput : mInputs) {
565                        if (sgInput.mKID == kernel) {
566                            ain = sgInput.mAllocation;
567                        }
568                    }
569
570                    for (ConnectLine nodeOutput : node.mOutputs) {
571                        if (nodeOutput.mFrom == kernel) {
572                            aout = nodeOutput.mAllocation;
573                        }
574                    }
575
576                    for (IO sgOutput : mOutputs) {
577                        if (sgOutput.mKID == kernel) {
578                            aout = sgOutput.mAllocation;
579                        }
580                    }
581
582                    kernel.mScript.forEach(kernel.mSlot, ain, aout, null);
583                }
584            }
585        }
586    }
587
588
589    /**
590     * Helper class to build a ScriptGroup. A ScriptGroup is
591     * created in two steps.
592     * <p>
593     * First, all kernels to be used by the ScriptGroup should be added.
594     * <p>
595     * Second, add connections between kernels. There are two types
596     * of connections: kernel to kernel and kernel to field.
597     * Kernel to kernel allows a kernel's output to be passed to
598     * another kernel as input. Kernel to field allows the output of
599     * one kernel to be bound as a script global. Kernel to kernel is
600     * higher performance and should be used where possible.
601     * <p>
602     * A ScriptGroup must contain a single directed acyclic graph (DAG); it
603     * cannot contain cycles. Currently, all kernels used in a ScriptGroup
604     * must come from different Script objects. Additionally, all kernels
605     * in a ScriptGroup must have at least one input, output, or internal
606     * connection.
607     * <p>
608     * Once all connections are made, a call to {@link #create} will
609     * return the ScriptGroup object.
610     *
611     * @deprecated Use {@link Builder2} instead.
612     *
613     */
614    public static final class Builder {
615        private RenderScript mRS;
616        private ArrayList<Node> mNodes = new ArrayList<Node>();
617        private ArrayList<ConnectLine> mLines = new ArrayList<ConnectLine>();
618        private int mKernelCount;
619        private boolean mUseIncSupp = false;
620
621        /**
622         * Create a Builder for generating a ScriptGroup.
623         *
624         *
625         * @param rs The RenderScript context.
626         */
627        public Builder(RenderScript rs) {
628            mRS = rs;
629        }
630
631        // do a DFS from original node, looking for original node
632        // any cycle that could be created must contain original node
633        private void validateCycle(Node target, Node original) {
634            for (int ct = 0; ct < target.mOutputs.size(); ct++) {
635                final ConnectLine cl = target.mOutputs.get(ct);
636                if (cl.mToK != null) {
637                    Node tn = findNode(cl.mToK.mScript);
638                    if (tn.equals(original)) {
639                        throw new RSInvalidStateException("Loops in group not allowed.");
640                    }
641                    validateCycle(tn, original);
642                }
643                if (cl.mToF != null) {
644                    Node tn = findNode(cl.mToF.mScript);
645                    if (tn.equals(original)) {
646                        throw new RSInvalidStateException("Loops in group not allowed.");
647                    }
648                    validateCycle(tn, original);
649                }
650            }
651        }
652
653        private void mergeDAGs(int valueUsed, int valueKilled) {
654            for (int ct=0; ct < mNodes.size(); ct++) {
655                if (mNodes.get(ct).dagNumber == valueKilled)
656                    mNodes.get(ct).dagNumber = valueUsed;
657            }
658        }
659
660        private void validateDAGRecurse(Node n, int dagNumber) {
661            // combine DAGs if this node has been seen already
662            if (n.dagNumber != 0 && n.dagNumber != dagNumber) {
663                mergeDAGs(n.dagNumber, dagNumber);
664                return;
665            }
666
667            n.dagNumber = dagNumber;
668            for (int ct=0; ct < n.mOutputs.size(); ct++) {
669                final ConnectLine cl = n.mOutputs.get(ct);
670                if (cl.mToK != null) {
671                    Node tn = findNode(cl.mToK.mScript);
672                    validateDAGRecurse(tn, dagNumber);
673                }
674                if (cl.mToF != null) {
675                    Node tn = findNode(cl.mToF.mScript);
676                    validateDAGRecurse(tn, dagNumber);
677                }
678            }
679        }
680
681        private void validateDAG() {
682            for (int ct=0; ct < mNodes.size(); ct++) {
683                Node n = mNodes.get(ct);
684                if (n.mInputs.size() == 0) {
685                    if (n.mOutputs.size() == 0 && mNodes.size() > 1) {
686                        String msg = "Groups cannot contain unconnected scripts";
687                        throw new RSInvalidStateException(msg);
688                    }
689                    validateDAGRecurse(n, ct+1);
690                }
691            }
692            int dagNumber = mNodes.get(0).dagNumber;
693            for (int ct=0; ct < mNodes.size(); ct++) {
694                if (mNodes.get(ct).dagNumber != dagNumber) {
695                    throw new RSInvalidStateException("Multiple DAGs in group not allowed.");
696                }
697            }
698        }
699
700        private Node findNode(Script s) {
701            for (int ct=0; ct < mNodes.size(); ct++) {
702                if (s == mNodes.get(ct).mScript) {
703                    return mNodes.get(ct);
704                }
705            }
706            return null;
707        }
708
709        private Node findNode(Script.KernelID k) {
710            for (int ct=0; ct < mNodes.size(); ct++) {
711                Node n = mNodes.get(ct);
712                for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
713                    if (k == n.mKernels.get(ct2)) {
714                        return n;
715                    }
716                }
717            }
718            return null;
719        }
720
721        /**
722         * Adds a Kernel to the group.
723         *
724         *
725         * @param k The kernel to add.
726         *
727         * @return Builder Returns this.
728         */
729        public Builder addKernel(Script.KernelID k) {
730            if (mLines.size() != 0) {
731                throw new RSInvalidStateException(
732                    "Kernels may not be added once connections exist.");
733            }
734            if (k.mScript.isIncSupp()) {
735                mUseIncSupp = true;
736            }
737            //android.util.Log.v("RSR", "addKernel 1 k=" + k);
738            if (findNode(k) != null) {
739                return this;
740            }
741            //android.util.Log.v("RSR", "addKernel 2 ");
742            mKernelCount++;
743            Node n = findNode(k.mScript);
744            if (n == null) {
745                //android.util.Log.v("RSR", "addKernel 3 ");
746                n = new Node(k.mScript);
747                mNodes.add(n);
748            }
749            n.mKernels.add(k);
750            return this;
751        }
752
753        /**
754         * Adds a connection to the group.
755         *
756         *
757         * @param t The type of the connection. This is used to
758         *          determine the kernel launch sizes on the source side
759         *          of this connection.
760         * @param from The source for the connection.
761         * @param to The destination of the connection.
762         *
763         * @return Builder Returns this
764         */
765        public Builder addConnection(Type t, Script.KernelID from, Script.FieldID to) {
766            //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
767            Node nf = findNode(from);
768            if (nf == null) {
769                throw new RSInvalidStateException("From script not found.");
770            }
771
772            Node nt = findNode(to.mScript);
773            if (nt == null) {
774                throw new RSInvalidStateException("To script not found.");
775            }
776
777            ConnectLine cl = new ConnectLine(t, from, to);
778            mLines.add(new ConnectLine(t, from, to));
779
780            nf.mOutputs.add(cl);
781            nt.mInputs.add(cl);
782
783            validateCycle(nf, nf);
784            return this;
785        }
786
787        /**
788         * Adds a connection to the group.
789         *
790         *
791         * @param t The type of the connection. This is used to
792         *          determine the kernel launch sizes for both sides of
793         *          this connection.
794         * @param from The source for the connection.
795         * @param to The destination of the connection.
796         *
797         * @return Builder Returns this
798         */
799        public Builder addConnection(Type t, Script.KernelID from, Script.KernelID to) {
800            //android.util.Log.v("RSR", "addConnection " + t +", " + from + ", " + to);
801            Node nf = findNode(from);
802            if (nf == null) {
803                throw new RSInvalidStateException("From script not found.");
804            }
805
806            Node nt = findNode(to);
807            if (nt == null) {
808                throw new RSInvalidStateException("To script not found.");
809            }
810
811            ConnectLine cl = new ConnectLine(t, from, to);
812            mLines.add(new ConnectLine(t, from, to));
813
814            nf.mOutputs.add(cl);
815            nt.mInputs.add(cl);
816
817            validateCycle(nf, nf);
818            return this;
819        }
820
821        /**
822         * Calculate the order of each node.
823         *
824         *
825         * @return Success or Fail
826         */
827        private boolean calcOrderRecurse(Node node0, int depth) {
828            node0.mSeen = true;
829            if (node0.mOrder < depth) {
830                node0.mOrder = depth;
831            }
832            boolean ret = true;
833
834            for (ConnectLine link : node0.mOutputs) {
835                Node node1 = null;
836                if (link.mToF != null) {
837                    node1 = findNode(link.mToF.mScript);
838                } else {
839                    node1 = findNode(link.mToK.mScript);
840                }
841                if (node1.mSeen) {
842                    return false;
843                }
844                ret &= calcOrderRecurse(node1, node0.mOrder + 1);
845            }
846
847            return ret;
848        }
849
850        private boolean calcOrder() {
851            boolean ret = true;
852            for (Node n0 : mNodes) {
853                if (n0.mInputs.size() == 0) {
854                    for (Node n1 : mNodes) {
855                        n1.mSeen = false;
856                    }
857                    ret &= calcOrderRecurse(n0, 1);
858                }
859            }
860
861            Collections.sort(mNodes, new Comparator<Node>() {
862                public int compare(Node n1, Node n2) {
863                    return n1.mOrder - n2.mOrder;
864                }
865            });
866
867            return ret;
868        }
869
870        /**
871         * Creates the Script group.
872         *
873         *
874         * @return ScriptGroup The new ScriptGroup
875         */
876        public ScriptGroup create() {
877
878            if (mNodes.size() == 0) {
879                throw new RSInvalidStateException("Empty script groups are not allowed");
880            }
881
882            // reset DAG numbers in case we're building a second group
883            for (int ct=0; ct < mNodes.size(); ct++) {
884                mNodes.get(ct).dagNumber = 0;
885            }
886            validateDAG();
887
888            ArrayList<IO> inputs = new ArrayList<IO>();
889            ArrayList<IO> outputs = new ArrayList<IO>();
890
891            long[] kernels = new long[mKernelCount];
892            int idx = 0;
893            for (int ct=0; ct < mNodes.size(); ct++) {
894                Node n = mNodes.get(ct);
895                for (int ct2=0; ct2 < n.mKernels.size(); ct2++) {
896                    final Script.KernelID kid = n.mKernels.get(ct2);
897                    kernels[idx++] = kid.getID(mRS);
898
899                    boolean hasInput = false;
900                    boolean hasOutput = false;
901                    for (int ct3=0; ct3 < n.mInputs.size(); ct3++) {
902                        if (n.mInputs.get(ct3).mToK == kid) {
903                            hasInput = true;
904                        }
905                    }
906                    for (int ct3=0; ct3 < n.mOutputs.size(); ct3++) {
907                        if (n.mOutputs.get(ct3).mFrom == kid) {
908                            hasOutput = true;
909                        }
910                    }
911                    if (!hasInput) {
912                        inputs.add(new IO(kid));
913                    }
914                    if (!hasOutput) {
915                        outputs.add(new IO(kid));
916                    }
917                }
918            }
919            if (idx != mKernelCount) {
920                throw new RSRuntimeException("Count mismatch, should not happen.");
921            }
922
923            long id = 0;
924            if (!mUseIncSupp) {
925                long[] src = new long[mLines.size()];
926                long[] dstk = new long[mLines.size()];
927                long[] dstf = new long[mLines.size()];
928                long[] types = new long[mLines.size()];
929
930                for (int ct=0; ct < mLines.size(); ct++) {
931                    ConnectLine cl = mLines.get(ct);
932                    src[ct] = cl.mFrom.getID(mRS);
933                    if (cl.mToK != null) {
934                        dstk[ct] = cl.mToK.getID(mRS);
935                    }
936                    if (cl.mToF != null) {
937                        dstf[ct] = cl.mToF.getID(mRS);
938                    }
939                    types[ct] = cl.mAllocationType.getID(mRS);
940                }
941                id = mRS.nScriptGroupCreate(kernels, src, dstk, dstf, types);
942                if (id == 0) {
943                    throw new RSRuntimeException("Object creation error, should not happen.");
944                }
945            } else {
946                //Calculate the order of the DAG so that script can run one after another.
947                calcOrder();
948            }
949
950            ScriptGroup sg = new ScriptGroup(id, mRS);
951            sg.mOutputs = new IO[outputs.size()];
952            for (int ct=0; ct < outputs.size(); ct++) {
953                sg.mOutputs[ct] = outputs.get(ct);
954            }
955
956            sg.mInputs = new IO[inputs.size()];
957            for (int ct=0; ct < inputs.size(); ct++) {
958                sg.mInputs[ct] = inputs.get(ct);
959            }
960            sg.mNodes = mNodes;
961            sg.mUseIncSupp = mUseIncSupp;
962            return sg;
963        }
964
965    }
966
967    /**
968     * Represents a binding of a value to a global variable in a
969     * kernel or invocable function. Used in closure creation.
970     */
971
972    public static final class Binding {
973        private final Script.FieldID mField;
974        private final Object mValue;
975
976        /**
977         * Returns a Binding object that binds value to field
978         *
979         * @param field the Script.FieldID of the global variable
980         * @param value the value
981         */
982
983        public Binding(Script.FieldID field, Object value) {
984            mField = field;
985            mValue = value;
986        }
987
988        /**
989         * Returns the field ID
990         */
991
992        public Script.FieldID getField() { return mField; }
993
994        /**
995         * Returns the value
996         */
997
998        public Object getValue() { return mValue; }
999    }
1000
1001    /**
1002     * The builder class for creating script groups
1003     * <p>
1004     * A script group is created using closures (see class {@link Closure}).
1005     * A closure is a function call to a kernel or
1006     * invocable function. Each function argument or global variable accessed inside
1007     * the function is bound to 1) a known value, 2) a script group input
1008     * (see class {@link Input}), or 3) a
1009     * future (see class {@link Future}).
1010     * A future is the output of a closure, either the return value of the
1011     * function or a global variable written by that function.
1012     * <p>
1013     * Closures are created using the {@link #addKernel} or {@link #addInvoke}
1014     * methods.
1015     * When a closure is created, futures from previously created closures
1016     * can be used as its inputs.
1017     * External script group inputs can be used as inputs to individual closures as well.
1018     * An external script group input is created using the {@link #addInput} method.
1019     * A script group is created by a call to the {@link #create} method, which
1020     * accepts an array of futures as the outputs for the script group.
1021     * <p>
1022     * Closures in a script group can be evaluated in any order as long as the
1023     * following conditions are met:
1024     * 1) a closure must be evaluated before any other closures that take its
1025     * futures as inputs;
1026     * 2) all closures added before an invoke closure must be evaluated
1027     * before it;
1028     * and 3) all closures added after an invoke closure must be evaluated after
1029     * it.
1030     * As a special case, the order that the closures are added is a legal
1031     * evaluation order. However, other evaluation orders are possible, including
1032     * concurrently evaluating independent closures.
1033     */
1034
1035    public static final class Builder2 {
1036        RenderScript mRS;
1037        List<Closure> mClosures;
1038        List<Input> mInputs;
1039        private static final String TAG = "ScriptGroup.Builder2";
1040
1041        /**
1042         * Returns a Builder object
1043         *
1044         * @param rs the RenderScript context
1045         */
1046        public Builder2(RenderScript rs) {
1047            mRS = rs;
1048            mClosures = new ArrayList<Closure>();
1049            mInputs = new ArrayList<Input>();
1050        }
1051
1052        /**
1053         * Adds a closure for a kernel
1054         *
1055         * @param k Kernel ID for the kernel function
1056         * @param returnType Allocation type for the return value
1057         * @param args arguments to the kernel function
1058         * @param globalBindings bindings for global variables
1059         * @return a closure
1060         */
1061
1062        private Closure addKernelInternal(Script.KernelID k, Type returnType, Object[] args,
1063                                          Map<Script.FieldID, Object> globalBindings) {
1064            Closure c = new Closure(mRS, k, returnType, args, globalBindings);
1065            mClosures.add(c);
1066            return c;
1067        }
1068
1069        /**
1070         * Adds a closure for an invocable function
1071         *
1072         * @param invoke Invoke ID for the invocable function
1073         * @param args arguments to the invocable function
1074         * @param globalBindings bindings for global variables
1075         * @return a closure
1076         */
1077
1078        private Closure addInvokeInternal(Script.InvokeID invoke, Object[] args,
1079                                          Map<Script.FieldID, Object> globalBindings) {
1080            Closure c = new Closure(mRS, invoke, args, globalBindings);
1081            mClosures.add(c);
1082            return c;
1083        }
1084
1085        /**
1086         * Adds a script group input
1087         *
1088         * @return a script group input, which can be used as an argument or a value to
1089         *     a global variable for creating closures
1090         */
1091
1092        public Input addInput() {
1093            Input unbound = new Input();
1094            mInputs.add(unbound);
1095            return unbound;
1096        }
1097
1098        /**
1099         * Adds a closure for a kernel
1100         *
1101         * @param k Kernel ID for the kernel function
1102         * @param argsAndBindings arguments followed by bindings for global variables
1103         * @return a closure
1104         */
1105
1106        public Closure addKernel(Script.KernelID k, Type returnType, Object... argsAndBindings) {
1107            ArrayList<Object> args = new ArrayList<Object>();
1108            Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
1109            if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
1110                return null;
1111            }
1112            return addKernelInternal(k, returnType, args.toArray(), bindingMap);
1113        }
1114
1115        /**
1116         * Adds a closure for an invocable function
1117         *
1118         * @param invoke Invoke ID for the invocable function
1119         * @param argsAndBindings arguments followed by bindings for global variables
1120         * @return a closure
1121         */
1122
1123        public Closure addInvoke(Script.InvokeID invoke, Object... argsAndBindings) {
1124            ArrayList<Object> args = new ArrayList<Object>();
1125            Map<Script.FieldID, Object> bindingMap = new HashMap<Script.FieldID, Object>();
1126            if (!seperateArgsAndBindings(argsAndBindings, args, bindingMap)) {
1127                return null;
1128            }
1129            return addInvokeInternal(invoke, args.toArray(), bindingMap);
1130        }
1131
1132        /**
1133         * Creates a script group
1134         *
1135         * @param name name for the script group. Legal names can only contain letters, digits,
1136         *        '-', or '_'. The name can be no longer than 100 characters.
1137         * @param outputs futures intended as outputs of the script group
1138         * @return a script group
1139         */
1140
1141        public ScriptGroup create(String name, Future... outputs) {
1142            if (name == null || name.isEmpty() || name.length() > 100 ||
1143                !name.equals(name.replaceAll("[^a-zA-Z0-9-]", "_"))) {
1144                throw new RSIllegalArgumentException("invalid script group name");
1145            }
1146            ScriptGroup ret = new ScriptGroup(mRS, name, mClosures, mInputs, outputs);
1147            return ret;
1148        }
1149
1150        private boolean seperateArgsAndBindings(Object[] argsAndBindings,
1151                                                ArrayList<Object> args,
1152                                                Map<Script.FieldID, Object> bindingMap) {
1153            int i;
1154            for (i = 0; i < argsAndBindings.length; i++) {
1155                if (argsAndBindings[i] instanceof Binding) {
1156                    break;
1157                }
1158                args.add(argsAndBindings[i]);
1159            }
1160
1161            for (; i < argsAndBindings.length; i++) {
1162                if (!(argsAndBindings[i] instanceof Binding)) {
1163                    return false;
1164                }
1165                Binding b = (Binding)argsAndBindings[i];
1166                bindingMap.put(b.getField(), b.getValue());
1167            }
1168
1169            return true;
1170        }
1171
1172    }
1173
1174}
1175
1176
1177