Home | History | Annotate | Download | only in renderscript
      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 public 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